2 * Copyright (C) Volition, Inc. 1999. 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
10 * $Logfile: /Freespace2/code/Network/MultiUI.cpp $
15 * C file for all the UI controls of the mulitiplayer screens
18 * Revision 1.12 2005/10/02 09:27:49 taylor
19 * fix interface problems with MultiJoinWait screen in FS1
21 * Revision 1.11 2005/03/29 02:18:47 taylor
22 * Various 64-bit platform fixes
23 * Fix compiler errors with MAKE_FS1 and fix gr_set_bitmap() too
24 * Make sure that turrets can fire at asteroids for FS1 (needed for a couple missions)
25 * Streaming audio support (big thanks to Pierre Willenbrock!!)
26 * Removed dependance on strings.tbl for FS1 since we don't actually need it now
28 * Revision 1.10 2004/09/20 01:31:44 theoddone33
31 * Revision 1.9 2004/07/04 11:39:06 taylor
32 * fix missing debrief text, crash on exit, path separator's, warning fixes, no GR_SOFT
34 * Revision 1.8 2003/05/25 02:30:43 taylor
37 * Revision 1.7 2002/06/09 04:41:24 relnev
38 * added copyright header
40 * Revision 1.6 2002/06/02 06:02:59 relnev
43 * Revision 1.5 2002/06/02 00:31:35 relnev
44 * implemented osregistry
46 * Revision 1.4 2002/06/01 07:12:33 relnev
47 * a few NDEBUG updates.
49 * removed a few warnings.
51 * Revision 1.3 2002/05/26 20:49:54 theoddone33
54 * Revision 1.2 2002/05/07 03:16:47 theoddone33
55 * The Great Newline Fix
57 * Revision 1.1.1.1 2002/05/03 03:28:10 root
61 * 94 6/16/00 3:16p Jefff
62 * sim of the year dvd version changes, a few german soty localization
65 * 93 10/14/99 2:51p Jefff
68 * 92 10/13/99 3:50p Jefff
69 * fixed unnumbered XSTRs
71 * 91 9/15/99 1:45a Dave
72 * Don't init joystick on standalone. Fixed campaign mode on standalone.
73 * Fixed no-score-report problem in TvT
75 * 90 9/14/99 12:51a Jefff
78 * 89 9/13/99 4:52p Dave
81 * 88 9/13/99 11:30a Dave
82 * Added checkboxes and functionality for disabling PXO banners as well as
83 * disabling d3d zbuffer biasing.
85 * 87 9/12/99 10:06p Jefff
86 * changed instances of "Squad War" to "SquadWar"
88 * 86 9/03/99 1:32a Dave
89 * CD checking by act. Added support to play 2 cutscenes in a row
90 * seamlessly. Fixed super low level cfile bug related to files in the
91 * root directory of a CD. Added cheat code to set campaign mission # in
94 * 85 9/01/99 10:49p Dave
95 * Added nice SquadWar checkbox to the client join wait screen.
97 * 84 8/30/99 2:49p Jefff
99 * 83 8/26/99 8:49p Jefff
100 * Updated medals screen and about everything that ever touches medals in
101 * one way or another. Sheesh.
103 * 82 8/25/99 4:38p Dave
104 * Updated PXO stuff. Make squad war report stuff much more nicely.
106 * 81 8/20/99 2:09p Dave
107 * PXO banner cycling.
109 * 80 8/20/99 10:06a Jefff
110 * removed closed/rstricted buttons from multi start screen
112 * 79 8/18/99 11:30a Jefff
114 * 78 8/18/99 10:38a Jefff
116 * 77 8/16/99 4:06p Dave
117 * Big honking checkin.
119 * 76 8/16/99 1:08p Jefff
120 * added sounds to a few controls, made input boxes lose focus on ENTER
122 * 75 8/16/99 9:52a Jefff
123 * fixed bitmap loading on buttons in multi-sync screen
125 * 74 8/11/99 5:54p Dave
126 * Fixed collision problem. Fixed standalone ghost problem.
128 * 73 8/10/99 4:35p Jefff
129 * fixed hi-res coords
131 * 72 8/06/99 12:29a Dave
132 * Multiple bug fixes.
134 * 71 8/05/99 3:13p Jasenw
135 * tweaked some text placement coords.
137 * 70 8/04/99 1:38p Jefff
138 * moved some text in multi join wait
140 * 69 8/03/99 12:45p Dave
143 * 68 7/25/99 5:17p Jefff
144 * campaign descriptions show up on multicreate screen
146 * 67 7/20/99 1:49p Dave
147 * Peter Drake build. Fixed some release build warnings.
149 * 66 7/19/99 2:13p Dave
150 * Added some new strings for Heiko.
152 * 65 7/15/99 9:20a Andsager
153 * FS2_DEMO initial checkin
155 * 64 7/08/99 10:53a Dave
156 * New multiplayer interpolation scheme. Not 100% done yet, but still
157 * better than the old way.
159 * 63 6/30/99 10:49a Jasenw
160 * Fixed coords for new launch countdown ani
162 * 62 6/29/99 7:39p Dave
163 * Lots of small bug fixes.
165 * 61 6/25/99 11:59a Dave
166 * Multi options screen.
168 * 60 6/09/99 2:17p Dave
169 * Fixed up pleasewait bitmap rendering.
171 * 59 6/05/99 3:42p Dave
172 * New multi sync screen.
174 * 58 6/01/99 6:07p Dave
175 * New loading/pause/please wait bar.
177 * 57 5/21/99 6:45p Dave
178 * Sped up ui loading a bit. Sped up localization disk access stuff. Multi
179 * start game screen, multi password, and multi pxo-help screen.
181 * 56 5/06/99 11:10a Dave
182 * Fixed coord on multi create screen.
184 * 55 5/04/99 6:38p Dave
185 * Finished multi join-wait screen.
187 * 54 5/04/99 5:20p Dave
188 * Fixed up multiplayer join screen and host options screen. Should both
191 * 53 5/03/99 11:04p Dave
192 * Most of the way done with the multi join screen.
194 * 52 5/03/99 8:32p Dave
195 * New version of multi host options screen.
197 * 51 4/29/99 2:15p Neilk
198 * slider2 code got modified; changed parameters for create
200 * 50 4/25/99 3:02p Dave
201 * Build defines for the E3 build.
203 * 49 4/21/99 6:15p Dave
204 * Did some serious housecleaning in the beam code. Made it ready to go
205 * for anti-fighter "pulse" weapons. Fixed collision pair creation. Added
206 * a handy macro for recalculating collision pairs for a given object.
208 * 48 4/16/99 5:27p Neilk
209 * added slider support and hir res for multi_create
211 * 47 4/14/99 6:37p Dave
212 * Fixed scroll button bug on host create screen.
214 * 46 4/14/99 5:28p Dave
217 * 45 4/12/99 10:07p Dave
218 * Made network startup more forgiving. Added checkmarks to dogfight
219 * screen for players who hit commit.
221 * 44 4/09/99 2:21p Dave
222 * Multiplayer beta stuff. CD checking.
224 * 43 4/08/99 1:28p Dave
225 * Small bug fixes for refresh button on the multi create screen.
227 * 42 4/08/99 11:55a Neilk
228 * Converted Multi_Create to new artwork (just lowres)
230 * 41 4/08/99 2:10a Dave
231 * Numerous bug fixes for the beta. Added builtin mission info for the
234 * 40 3/20/99 3:48p Andsager
235 * Do mission_loop stuff for PXO
237 * 39 3/10/99 6:50p Dave
238 * Changed the way we buffer packets for all clients. Optimized turret
239 * fired packets. Did some weapon firing optimizations.
241 * 38 3/09/99 6:24p Dave
242 * More work on object update revamping. Identified several sources of
243 * unnecessary bandwidth.
245 * 37 3/08/99 7:03p Dave
246 * First run of new object update system. Looks very promising.
248 * 36 2/25/99 4:19p Dave
249 * Added multiplayer_beta defines. Added cd_check define. Fixed a few
250 * release build warnings. Added more data to the squad war request and
253 * 35 2/24/99 3:26p Anoop
254 * Make sure the host is the only guy who bashes skill level for TvT.
256 * 34 2/24/99 2:25p Dave
257 * Fixed up chatbox bugs. Made squad war reporting better. Fixed a respawn
258 * bug for dogfight more.
260 * 33 2/23/99 2:29p Dave
261 * First run of oldschool dogfight mode.
263 * 32 2/17/99 2:11p Dave
264 * First full run of squad war. All freespace and tracker side stuff
267 * 31 2/12/99 6:16p Dave
268 * Pre-mission Squad War code is 95% done.
270 * 30 2/11/99 3:08p Dave
271 * PXO refresh button. Very preliminary squad war support.
273 * 29 2/08/99 5:07p Dave
274 * FS2 chat server support. FS2 specific validated missions.
276 * 28 2/04/99 6:29p Dave
277 * First full working rev of FS2 PXO support. Fixed Glide lighting
280 * 27 1/30/99 5:08p Dave
281 * More new hi-res stuff.Support for nice D3D textures.
283 * 26 1/29/99 2:08a Dave
284 * Fixed beam weapon collisions with players. Reduced size of scoring
285 * struct for multiplayer. Disabled PXO.
287 * 25 1/15/99 2:36p Neilk
288 * fixed multi_jw coordinates
290 * 24 1/13/99 7:19p Neilk
291 * Converted Mission Brief, Barracks, Synch to high res support
293 * 23 1/12/99 7:17p Neilk
295 * 22 1/12/99 5:45p Dave
296 * Moved weapon pipeline in multiplayer to almost exclusively client side.
297 * Very good results. Bandwidth goes down, playability goes up for crappy
298 * connections. Fixed object update problem for ship subsystems.
300 * 21 1/12/99 4:07a Dave
301 * Put in barracks code support for selecting squad logos. Properly
302 * distribute squad logos in a multiplayer game.
304 * 20 1/11/99 7:19p Neilk
305 * Converted multi_join interface to support multiple resolutions
307 * 19 12/18/98 1:13a Dave
308 * Rough 1024x768 support for Direct3D. Proper detection and usage through
311 * 18 12/17/98 4:50p Andsager
312 * Added debrief_assemble_optional_mission_popup_text() for single and
315 * 17 12/14/98 12:13p Dave
316 * Spiffed up xfer system a bit. Put in support for squad logo file xfer.
319 * 16 12/10/98 10:19a Andsager
320 * Fix mission loop assert
322 * 15 12/10/98 9:59a Andsager
323 * Fix some bugs with mission loops
325 * 14 12/09/98 1:56p Andsager
326 * Initial checkin of mission loop
328 * 13 12/03/98 5:22p Dave
329 * Ported over Freespace 1 multiplayer ships.tbl and weapons.tbl
332 * 12 11/30/98 1:07p Dave
333 * 16 bit conversion, first run.
335 * 11 11/20/98 11:16a Dave
336 * Fixed up IPX support a bit. Making sure that switching modes and
337 * loading/saving pilot files maintains proper state.
339 * 10 11/19/98 4:57p Dave
340 * Ignore PXO option if IPX is selected.
342 * 9 11/19/98 4:19p Dave
343 * Put IPX sockets back in psnet. Consolidated all multiplayer config
346 * 8 11/19/98 8:04a Dave
347 * Full support for D3-style reliable sockets. Revamped packet lag/loss
348 * system, made it receiver side and at the lowest possible level.
350 * 7 11/17/98 11:12a Dave
351 * Removed player identification by address. Now assign explicit id #'s.
353 * 6 10/19/98 11:15a Dave
354 * Changed requirements for stats storing in PXO mode.
356 * 5 10/16/98 9:40a Andsager
357 * Remove ".h" files from model.h
359 * 4 10/13/98 9:29a Dave
360 * Started neatening up freespace.h. Many variables renamed and
361 * reorganized. Added AlphaColors.[h,cpp]
363 * 3 10/07/98 6:27p Dave
364 * Globalized mission and campaign file extensions. Removed Silent Threat
365 * special code. Moved \cache \players and \multidata into the \data
368 * 2 10/07/98 10:53a Dave
371 * 1 10/07/98 10:50a Dave
373 * 333 10/02/98 3:22p Allender
374 * fix up the -connect option and fix the -port option
376 * 332 9/17/98 9:26p Dave
377 * Externalized new string.
379 * 331 9/17/98 3:08p Dave
380 * PXO to non-pxo game warning popup. Player icon stuff in create and join
381 * game screens. Upped server count refresh time in PXO to 35 secs (from
384 * 330 9/17/98 9:43a Allender
385 * removed an SDL_assert that Dave called bogus.
387 * 329 9/16/98 6:54p Dave
388 * Upped max sexpression nodes to 1800 (from 1600). Changed FRED to sort
389 * the ship list box. Added code so that tracker stats are not stored with
392 * 328 9/15/98 7:24p Dave
393 * Minor UI changes. Localized bunch of new text.
395 * 327 9/15/98 4:03p Dave
396 * Changed readyroom and multi screens to display "st" icon for all
397 * missions with mission disk content (not necessarily just those that
398 * come with Silent Threat).
400 * 326 9/15/98 11:44a Dave
401 * Renamed builtin ships and wepaons appropriately in FRED. Put in scoring
402 * scale factors. Fixed standalone filtering of MD missions to non-MD
405 * 325 9/13/98 9:36p Dave
406 * Support for new info icons for multiplayer missions (from-volition,
407 * valid, mission disk, etc).
409 * 324 9/11/98 4:14p Dave
410 * Fixed file checksumming of < file_size. Put in more verbose kicking and
411 * PXO stats store reporting.
413 * 323 9/10/98 1:17p Dave
414 * Put in code to flag missions and campaigns as being MD or not in Fred
415 * and Freespace. Put in multiplayer support for filtering out MD
416 * missions. Put in multiplayer popups for warning of non-valid missions.
418 * 322 9/04/98 3:51p Dave
419 * Put in validated mission updating and application during stats
422 * 321 8/31/98 2:06p Dave
423 * Make cfile sort the ordering or vp files. Added support/checks for
424 * recognizing "mission disk" players.
426 * 320 8/21/98 1:15p Dave
427 * Put in log system hooks in useful places.
429 * 319 8/20/98 5:31p Dave
430 * Put in handy multiplayer logfile system. Now need to put in useful
431 * applications of it all over the code.
433 * 318 8/12/98 4:53p Dave
434 * Put in 32 bit checksumming for PXO missions. No validation on the
435 * actual tracker yet, though.
437 * 317 8/07/98 10:40a Allender
438 * new command line flags for starting netgames. Only starting currently
439 * works, and PXO isn't implemented yet
441 * 316 7/24/98 9:27a Dave
442 * Tidied up endgame sequencing by removing several old flags and
443 * standardizing _all_ endgame stuff with a single function call.
445 * 315 7/14/98 10:04a Allender
446 * fixed the countdown code to not be reliant on timer_get_fixed_seconds
448 * 314 7/10/98 5:04p Dave
449 * Fix connection speed bug on standalone server.
451 * 313 7/09/98 6:01p Dave
452 * Firsts full version of PXO updater. Put in stub for displaying
455 * 312 7/07/98 2:49p Dave
458 * 311 6/30/98 2:17p Dave
459 * Revised object update system. Removed updates for all weapons. Put
460 * button info back into control info packet.
462 * 310 6/13/98 9:32p Mike
463 * Kill last character in file which caused "Find in Files" to report the
464 * file as "not a text file."
471 #include <winsock.h> // for inet_addr()
473 #include <sys/types.h>
474 #include <sys/socket.h>
475 #include <netinet/in.h>
476 #include <arpa/inet.h>
481 #include "multiutil.h"
482 #include "multimsgs.h"
488 #include "gamesequence.h"
489 #include "freespace.h"
490 #include "contexthelp.h"
495 #include "missionshipchoice.h"
496 #include "multi_xfer.h"
498 #include "stand_server.h"
499 #include "linklist.h"
500 #include "multiteamselect.h"
501 #include "missioncampaign.h"
508 #include "missiondebrief.h"
509 #include "multi_ingame.h"
510 #include "multi_kick.h"
511 #include "multi_data.h"
512 #include "multi_campaign.h"
513 #include "multi_team.h"
514 #include "multi_pinfo.h"
515 #include "multi_observer.h"
516 #include "multi_voice.h"
517 #include "multi_endgame.h"
518 #include "managepilot.h"
521 #include "objcollide.h"
523 #include "multi_pmsg.h"
524 #include "multi_obj.h"
525 #include "multi_log.h"
526 #include "alphacolors.h"
527 #include "animplay.h"
528 #include "multi_dogfight.h"
529 #include "missionpause.h"
530 #include "multi_fstracker.h"
531 #include "multi_sw.h"
533 // -------------------------------------------------------------------------------------------------------------
535 // MULTIPLAYER COMMON interface controls
538 // the common text info box stuff. This is lifted almost directly from Alans briefing code (minus the spiffy colored, scrolling
540 int Multi_common_text_coords[GR_NUM_RESOLUTIONS][4] = {
553 int Multi_common_text_max_display[GR_NUM_RESOLUTIONS] = {
562 #define MULTI_COMMON_TEXT_META_CHAR '$'
563 #define MULTI_COMMON_TEXT_MAX_LINE_LENGTH 100
564 #define MULTI_COMMON_TEXT_MAX_LINES 20
565 #define MULTI_COMMON_MAX_TEXT (MULTI_COMMON_TEXT_MAX_LINES * MULTI_COMMON_TEXT_MAX_LINE_LENGTH)
567 char Multi_common_all_text[MULTI_COMMON_MAX_TEXT];
568 char Multi_common_text[MULTI_COMMON_TEXT_MAX_LINES][MULTI_COMMON_TEXT_MAX_LINE_LENGTH];
570 int Multi_common_top_text_line = -1; // where to start displaying from
571 int Multi_common_num_text_lines = 0; // how many lines we have
573 void multi_common_scroll_text_up();
574 void multi_common_scroll_text_down();
575 void multi_common_move_to_bottom();
576 void multi_common_render_text();
577 void multi_common_split_text();
579 #define MAX_IP_STRING 255 // maximum length for ip string
581 void multi_common_scroll_text_up()
583 Multi_common_top_text_line--;
584 if ( Multi_common_top_text_line < 0 ) {
585 Multi_common_top_text_line = 0;
586 if ( !mouse_down(MOUSE_LEFT_BUTTON) )
587 gamesnd_play_iface(SND_GENERAL_FAIL);
590 gamesnd_play_iface(SND_SCROLL);
594 void multi_common_scroll_text_down()
596 Multi_common_top_text_line++;
597 if ( (Multi_common_num_text_lines - Multi_common_top_text_line) < Multi_common_text_max_display[gr_screen.res] ) {
598 Multi_common_top_text_line--;
599 if ( !mouse_down(MOUSE_LEFT_BUTTON) ){
600 gamesnd_play_iface(SND_GENERAL_FAIL);
603 gamesnd_play_iface(SND_SCROLL);
607 void multi_common_move_to_bottom()
609 // if there's nowhere to scroll down, do nothing
610 if(Multi_common_num_text_lines <= Multi_common_text_max_display[gr_screen.res]){
614 Multi_common_top_text_line = Multi_common_num_text_lines - Multi_common_text_max_display[gr_screen.res];
617 void multi_common_set_text(const char *str, int auto_scroll)
620 // store the entire string as well
621 if(strlen(str) > MULTI_COMMON_MAX_TEXT){
624 SDL_strlcpy(Multi_common_all_text, str, SDL_arraysize(Multi_common_all_text));
627 // split the whole thing up
628 multi_common_split_text();
630 // scroll to the bottom if we're supposed to
632 multi_common_move_to_bottom();
636 void multi_common_add_text(const char *str, int auto_scroll)
639 // store the entire string as well
640 if((strlen(str) + strlen(Multi_common_all_text)) > MULTI_COMMON_MAX_TEXT){
643 SDL_strlcat(Multi_common_all_text, str, SDL_arraysize(Multi_common_all_text));
646 // split the whole thing up
647 multi_common_split_text();
649 // scroll to the bottom if we're supposed to
651 multi_common_move_to_bottom();
655 void multi_common_split_text()
658 int n_chars[MAX_BRIEF_LINES];
659 char *p_str[MAX_BRIEF_LINES];
661 n_lines = split_str(Multi_common_all_text, Multi_common_text_coords[gr_screen.res][2], n_chars, p_str, MULTI_COMMON_TEXT_MAX_LINES, MULTI_COMMON_TEXT_META_CHAR);
662 SDL_assert(n_lines != -1);
664 for ( i = 0; i < n_lines; i++ ) {
665 SDL_assert(n_chars[i] < MULTI_COMMON_TEXT_MAX_LINE_LENGTH);
666 int len = min(n_chars[i] + 1, MULTI_COMMON_TEXT_MAX_LINE_LENGTH);
667 SDL_strlcpy(Multi_common_text[i], p_str[i], len);
668 Multi_common_text[i][n_chars[i]] = 0;
669 drop_leading_white_space(Multi_common_text[i]);
672 Multi_common_top_text_line = 0;
673 Multi_common_num_text_lines = n_lines;
676 void multi_common_render_text()
678 int i, fh, line_count;
680 fh = gr_get_font_height();
683 gr_set_color_fast(&Color_text_normal);
684 for ( i = Multi_common_top_text_line; i < Multi_common_num_text_lines; i++ ) {
685 if ( line_count >= Multi_common_text_max_display[gr_screen.res] ){
688 gr_string(Multi_common_text_coords[gr_screen.res][0], Multi_common_text_coords[gr_screen.res][1] + (line_count*fh), Multi_common_text[i]);
692 if ( (Multi_common_num_text_lines - Multi_common_top_text_line) > Multi_common_text_max_display[gr_screen.res] ) {
693 gr_set_color_fast(&Color_bright_red);
694 gr_string(Multi_common_text_coords[gr_screen.res][0], (Multi_common_text_coords[gr_screen.res][1] + Multi_common_text_coords[gr_screen.res][3])-5, XSTR("more",755));
698 // common notification messaging stuff
699 #define MULTI_COMMON_NOTIFY_TIME 3500
700 int Multi_common_join_y[GR_NUM_RESOLUTIONS] = {
704 int Multi_common_create_y[GR_NUM_RESOLUTIONS] = {
709 int Multi_common_jw_y[GR_NUM_RESOLUTIONS] = {
714 int Multi_common_msg_y[GR_NUM_RESOLUTIONS] = {
719 char Multi_common_notify_text[200];
720 int Multi_common_notify_stamp;
722 void multi_common_notify_init()
724 SDL_strlcpy(Multi_common_notify_text, "", SDL_arraysize(Multi_common_notify_text));
725 Multi_common_notify_stamp = -1;
728 // add a notification string, drawing appropriately depending on the state/screen we're in
729 void multi_common_add_notify(const char *str)
732 SDL_strlcpy(Multi_common_notify_text, str, SDL_arraysize(Multi_common_notify_text));
733 Multi_common_notify_stamp = timestamp(MULTI_COMMON_NOTIFY_TIME);
737 // process/display notification messages
738 void multi_common_notify_do()
740 if(Multi_common_notify_stamp != -1){
741 if(timestamp_elapsed(Multi_common_notify_stamp)){
742 Multi_common_notify_stamp = -1;
745 gr_get_string_size(&w,&h,Multi_common_notify_text);
746 gr_set_color_fast(&Color_white);
748 // determine where it should be placed based upon which screen we're on
750 switch(gameseq_get_state()){
751 case GS_STATE_MULTI_JOIN_GAME :
752 y = Multi_common_join_y[gr_screen.res];
754 case GS_STATE_MULTI_HOST_SETUP :
755 y = Multi_common_create_y[gr_screen.res];
757 case GS_STATE_MULTI_CLIENT_SETUP :
758 y = Multi_common_jw_y[gr_screen.res];
760 case GS_STATE_MULTI_START_GAME :
761 y = Multi_common_msg_y[gr_screen.res];
765 gr_string((gr_screen.max_w - w)/2, y, Multi_common_notify_text);
772 int Multi_common_icons[MULTI_NUM_COMMON_ICONS];
774 const char *Multi_common_icon_names[MULTI_NUM_COMMON_ICONS] = {
775 "DotRed", // voice denied
776 "DotGreen", // voice recording
777 "OvalGreen", // team 0
778 "OvalGreen01", // team 0 select
780 "OvalRed01", // team 1 select
781 "mp_coop", // coop mission
782 "mp_teams", // TvT mission
783 "mp_furball", // furball mission
784 "icon-volition", // volition mission
785 "icon-valid", // mission is valid
790 "icon-silent" // SilentThreat
794 // width and height of the icons
795 int Multi_common_icon_dims[MULTI_NUM_COMMON_ICONS][2] = {
796 {11, 11}, // voice denied
797 {11, 11}, // voice recording
799 {11, 11}, // team 0 select
801 {11, 11}, // team 1 select
804 {18, 11}, // mp furball
805 {9, 9}, // volition mission
806 {8, 8}, // mission is valid
811 {16, 7} // silent threat
815 void multi_load_common_icons()
820 for(idx=0; idx<MULTI_NUM_COMMON_ICONS; idx++){
821 Multi_common_icons[idx] = -1;
822 Multi_common_icons[idx] = bm_load(Multi_common_icon_names[idx]);
826 void multi_unload_common_icons()
831 for(idx=0; idx<MULTI_NUM_COMMON_ICONS; idx++){
832 if(Multi_common_icons[idx] != -1){
833 bm_unload(Multi_common_icons[idx]);
834 Multi_common_icons[idx] = -1;
839 // display any relevant voice status icons
840 void multi_common_voice_display_status()
842 switch(multi_voice_status()){
843 // i have been denied the voice token
844 case MULTI_VOICE_STATUS_DENIED:
845 if(Multi_common_icons[MICON_VOICE_DENIED] != -1){
846 gr_set_bitmap(Multi_common_icons[MICON_VOICE_DENIED], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
851 // i am currently recording
852 case MULTI_VOICE_STATUS_RECORDING:
853 if(Multi_common_icons[MICON_VOICE_RECORDING] != -1){
854 gr_set_bitmap(Multi_common_icons[MICON_VOICE_RECORDING], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
859 // i am currently playing back sound
860 case MULTI_VOICE_STATUS_PLAYING:
863 // the system is currently idle
864 case MULTI_VOICE_STATUS_IDLE:
870 // palette initialization stuff
871 #define MULTI_COMMON_PALETTE_FNAME "InterfacePalette"
874 int Multi_common_interface_palette = -1;
876 void multi_common_load_palette();
877 void multi_common_set_palette();
878 void multi_common_unload_palette();
880 // load in the palette if it doesn't already exist
881 void multi_common_load_palette()
883 if(Multi_common_interface_palette != -1){
887 Multi_common_interface_palette = bm_load(MULTI_COMMON_PALETTE_FNAME);
888 if(Multi_common_interface_palette == -1){
889 nprintf(("Network","Error loading multiplayer common palette!\n"));
893 // set the common palette to be the active one
894 void multi_common_set_palette()
896 // if the palette is not loaded yet, do so now
897 if(Multi_common_interface_palette == -1){
898 multi_common_load_palette();
902 // unload the bitmap palette
903 void multi_common_unload_palette()
905 if(Multi_common_interface_palette != -1){
906 bm_unload(Multi_common_interface_palette);
907 Multi_common_interface_palette = -1;
911 void multi_common_verify_cd()
917 // -------------------------------------------------------------------------------------------------------------
919 // MULTIPLAYER JOIN SCREEN
922 #define MULTI_JOIN_NUM_BUTTONS 11
926 #define MULTI_JOIN_PALETTE "InterfacePalette"
928 static const char *Multi_join_bitmap_fname[GR_NUM_RESOLUTIONS] = {
929 "MultiJoin", // GR_640
930 "2_MultiJoin" // GR_1024
933 static const char *Multi_join_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
934 "MultiJoin-M", // GR_640
935 "2_MultiJoin-M" // GR_1024
941 const char *Mj_slider_name[GR_NUM_RESOLUTIONS] = {
945 int Mj_slider_coords[GR_NUM_RESOLUTIONS][4] = {
956 #define MJ_SCROLL_UP 0
957 #define MJ_SCROLL_DOWN 1
959 #define MJ_SCROLL_INFO_UP 3
960 #define MJ_SCROLL_INFO_DOWN 4
961 #define MJ_JOIN_OBSERVER 5
962 #define MJ_START_GAME 6
968 // uses MULTI_JOIN_REFRESH_TIME as its timestamp
969 int Multi_join_glr_stamp;
971 #define MULTI_JOIN_PING_TIME 15000 // how often we ping all the known servers
972 int Multi_join_ping_stamp;
973 UI_WINDOW Multi_join_window; // the window object for the join screen
974 UI_BUTTON Multi_join_select_button; // for selecting list items
976 UI_SLIDER2 Multi_join_slider; // handy dandy slider
978 int Multi_join_bitmap; // the background bitmap
980 ui_button_info Multi_join_buttons[GR_NUM_RESOLUTIONS][MULTI_JOIN_NUM_BUTTONS] = {
983 ui_button_info("MJ_00", 0, 85, -1, -1, 0),
984 ui_button_info("MJ_01", 0, 125, -1, -1, 1),
985 ui_button_info("MJ_03", 20, 324, -1, -1, 3),
986 ui_button_info("MJ_04", 0, 399, -1, -1, 4),
987 ui_button_info("MJ_05", 0, 436, -1, -1, 5),
988 ui_button_info("MJ_15", 450, 323, -1, -1, 15),
989 ui_button_info("MJ_10", 519, 323, -1, -1, 10),
990 ui_button_info("MJ_06", 574, 323, -1, -1, 6),
991 ui_button_info("MJ_08", 470, 427, -1, -1, 8),
992 ui_button_info("MJ_09", 448, 454, -1, -1, 9),
993 ui_button_info("MJ_07", 563, 411, -1, -1, 7),
995 ui_button_info( "MJ_00", 1, 57, -1, -1, 0 ), // scroll up
996 ui_button_info( "MJ_02", 1, 297, -1, -1, 2 ), // scroll down
997 ui_button_info( "MJ_03", 10, 338, 65, 364, 3 ), // refresh
998 ui_button_info( "MJ_04", 1, 405, -1, -1, 4 ), // scroll info up
999 ui_button_info( "MJ_05", 1, 446, -1, -1, 5 ), // scroll info down
1000 ui_button_info( "MJ_06", 489, 339, -1, -1, 6 ), // join as observer
1001 ui_button_info( "MJ_07", 538, 339, -1, -1, 7 ), // create game
1002 ui_button_info( "MJ_08", 583, 339, 588, 376, 8 ), // cancel
1003 ui_button_info( "MJ_09", 534, 426, -1, -1, 9 ), // help
1004 ui_button_info( "MJ_10", 534, 454, -1, -1, 10 ), // options
1005 ui_button_info( "MJ_11", 571, 426, 589, 416, 11 ), // join
1009 ui_button_info( "2_MJ_00", 2, 92, -1, -1, 0 ), // scroll up
1010 ui_button_info( "2_MJ_02", 2, 475, -1, -1, 2 ), // scroll down
1011 ui_button_info( "2_MJ_03", 16, 541, 104, 582, 3 ), // refresh
1012 ui_button_info( "2_MJ_04", 2, 648, -1, -1, 4 ), // scroll info up
1013 ui_button_info( "2_MJ_05", 2, 713, -1, -1, 5 ), // scroll info down
1014 ui_button_info( "2_MJ_06", 783, 542, -1, -1, 6 ), // join as observer
1015 ui_button_info( "2_MJ_07", 861, 542, -1, -1, 7 ), // create game
1016 ui_button_info( "2_MJ_08", 933, 542, 588, 376, 8 ), // cancel
1017 ui_button_info( "2_MJ_09", 854, 681, -1, -1, 9 ), // help
1018 ui_button_info( "2_MJ_10", 854, 727, -1, -1, 10 ), // options
1019 ui_button_info( "2_MJ_11", 914, 681, 937, 668, 11 ), // join
1024 #define MULTI_JOIN_NUM_TEXT 13
1026 UI_XSTR Multi_join_text[GR_NUM_RESOLUTIONS][MULTI_JOIN_NUM_TEXT] = {
1028 {"Refresh", 1299, 65, 364, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_REFRESH].button},
1029 {"Join as", 1300, 476, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_JOIN_OBSERVER].button},
1030 {"Observer", 1301, 467, 385, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_JOIN_OBSERVER].button},
1031 {"Create", 1408, 535, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_START_GAME].button},
1032 {"Game", 1302, 541, 385, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_START_GAME].button},
1033 {"Cancel", 387, 588, 376, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[0][MJ_CANCEL].button},
1034 {"Help", 928, 479, 436, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_HELP].button},
1035 {"Options", 1036, 479, 460, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_OPTIONS].button},
1036 {"Join", 1303, 589, 416, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[0][MJ_ACCEPT].button},
1037 {"Status", 1304, 37, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
1038 {"Server", 1305, 116, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
1039 {"Players", 1306, 471, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
1040 {"Ping", 1307, 555, 37, UI_XSTR_COLOR_GREEN, -1, NULL}
1043 {"Refresh", 1299, 104, 582, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_REFRESH].button},
1044 {"Join as", 1300, 783, 602, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_JOIN_OBSERVER].button},
1045 {"Observer", 1301, 774, 611, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_JOIN_OBSERVER].button},
1046 {"Create", 1408, 868, 602, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_START_GAME].button},
1047 {"Game", 1302, 872, 611, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_START_GAME].button},
1048 {"Cancel", 387, 941, 602, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[1][MJ_CANCEL].button},
1049 {"Help", 928, 782, 699, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_HELP].button},
1050 {"Options", 1036, 782, 736, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_OPTIONS].button},
1051 {"Join", 1303, 937, 668, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[1][MJ_ACCEPT].button},
1052 {"Status", 1304, 60, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
1053 {"Server", 1305, 186, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
1054 {"Players", 1306, 753, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
1055 {"Ping", 1307, 888, 60, UI_XSTR_COLOR_GREEN, -1, NULL}
1060 // constants for coordinate look ups
1061 #define MJ_X_COORD 0
1062 #define MJ_Y_COORD 1
1063 #define MJ_W_COORD 2
1064 #define MJ_H_COORD 3
1066 #define MULTI_JOIN_SENT_WAIT 10000 // wait this long since a join was sent to allow another
1067 int Multi_join_sent_stamp;
1069 // game information text areas
1070 int Mj_max_game_items[GR_NUM_RESOLUTIONS] = {
1075 int Mj_list_y[GR_NUM_RESOLUTIONS] = {
1080 int Mj_status_coords[GR_NUM_RESOLUTIONS][4] = {
1089 int Mj_game_icon_coords[GR_NUM_RESOLUTIONS][3] = {
1098 int Mj_speed_coords[GR_NUM_RESOLUTIONS][4] = {
1107 int Mj_game_name_coords[GR_NUM_RESOLUTIONS][4] = {
1116 int Mj_players_coords[GR_NUM_RESOLUTIONS][4] = {
1125 int Mj_ping_coords[GR_NUM_RESOLUTIONS][4] = {
1134 // game speed labels
1135 #define MJ_NUM_SPEED_LABELS 5
1136 const char *Multi_join_speed_labels[MJ_NUM_SPEED_LABELS] = {
1143 color *Multi_join_speed_colors[MJ_NUM_SPEED_LABELS] = {
1146 &Color_bright_green,
1147 &Color_bright_green,
1151 int Mj_cd_coords[GR_NUM_RESOLUTIONS] = {
1156 // extents of the entire boundable game info region
1157 // NOTE : these numbers are completely empirical
1158 #define MJ_PING_GREEN 160
1159 #define MJ_PING_YELLOW 300
1161 int Mj_list_area_coords[GR_NUM_RESOLUTIONS][4] = {
1170 // PXO channel filter
1171 #define MJ_PXO_FILTER_Y 0
1173 // special chars to indicate various status modes for servers
1174 #define MJ_CHAR_STANDALONE "*"
1175 #define MJ_CHAR_CAMPAIGN "c"
1178 // various interface indices
1179 int Multi_join_list_start; // where to start displaying from
1180 active_game *Multi_join_list_start_item; // a pointer to the corresponding active_game
1181 int Multi_join_list_selected; // which item we have selected
1182 active_game *Multi_join_selected_item; // a pointer to the corresponding active_game
1184 // use this macro to modify the list start
1185 #define MJ_LIST_START_INC() do { Multi_join_list_start++; } while(0);
1186 #define MJ_LIST_START_DEC() do { Multi_join_list_start--; } while(0);
1187 #define MJ_LIST_START_SET(vl) do { Multi_join_list_start = vl; } while(0);
1189 // if we should be sending a join request at the end of the frame
1190 int Multi_join_should_send = -1;
1192 // master tracker details
1193 int Multi_join_frame_count; // keep a count of frames displayed
1194 int Multi_join_mt_tried_verify; // already tried verifying the pilot with the tracker
1196 // data stuff for auto joining a game
1197 #define MULTI_AUTOJOIN_JOIN_STAMP 2000
1198 #define MULTI_AUTOJOIN_QUERY_STAMP 2000
1200 int Multi_did_autojoin;
1201 net_addr Multi_autojoin_addr;
1202 int Multi_autojoin_join_stamp;
1203 int Multi_autojoin_query_stamp;
1206 join_request Multi_join_request;
1208 // LOCAL function definitions
1209 void multi_join_check_buttons();
1210 void multi_join_button_pressed(int n);
1211 void multi_join_display_games();
1212 void multi_join_blit_game_status(active_game *game, int y);
1213 void multi_join_load_tcp_addrs();
1214 void multi_join_do_netstuff();
1215 void multi_join_ping_all();
1216 void multi_join_process_select();
1217 void multi_join_list_scroll_up();
1218 void multi_join_list_scroll_down();
1219 void multi_join_list_page_up();
1220 void multi_join_list_page_down();
1221 active_game *multi_join_get_game(int n);
1222 void multi_join_cull_timeouts();
1223 void multi_join_handle_item_cull(active_game *item, int item_index);
1224 void multi_join_send_join_request(int as_observer);
1225 void multi_join_create_game();
1226 void multi_join_blit_top_stuff();
1227 int multi_join_maybe_warn();
1228 int multi_join_warn_pxo();
1229 void multi_join_blit_protocol();
1233 active_game ag, *newitem;;
1236 dc_get_arg(ARG_INT);
1237 for(idx=0; idx<Dc_arg_int; idx++){
1238 // stuff some fake info
1239 memset(&ag, 0, sizeof(active_game));
1240 SDL_snprintf(ag.name, SDL_arraysize(ag.name), "Game %d", idx);
1241 ag.version = MULTI_FS_SERVER_VERSION;
1242 ag.comp_version = MULTI_FS_SERVER_VERSION;
1243 ag.server_addr.addr[0] = (char)idx;
1244 ag.flags = (AG_FLAG_COOP | AG_FLAG_FORMING | AG_FLAG_STANDALONE);
1247 newitem = multi_update_active_games(&ag);
1249 // timestamp it so we get random timeouts
1250 if(newitem != NULL){
1251 // newitem->heard_from_timer = timestamp((int)frand_range(500.0f, 10000.0f));
1256 void multi_join_notify_new_game()
1259 // reset the # of items
1260 Multi_join_slider.set_numberItems(Active_game_count > Mj_max_game_items[gr_screen.res] ? Active_game_count - Mj_max_game_items[gr_screen.res] : 0);
1261 Multi_join_slider.force_currentItem(Multi_join_list_start);
1265 int multi_join_autojoin_do()
1267 // if we have an active game on the list, then return a positive value so that we
1268 // can join the game
1269 if ( Active_game_head && (Active_game_count > 0) ) {
1270 Multi_join_selected_item = Active_game_head;
1274 // send out a server_query again
1275 if ( timestamp_elapsed(Multi_autojoin_query_stamp) ) {
1276 send_server_query(&Multi_autojoin_addr);
1277 Multi_autojoin_query_stamp = timestamp(MULTI_AUTOJOIN_QUERY_STAMP);
1283 void multi_join_game_init()
1287 // do the multiplayer init stuff - multi_level_init() now does all net_player zeroing.
1288 // setup various multiplayer things
1289 SDL_assert( Game_mode & GM_MULTIPLAYER );
1290 SDL_assert( Net_player != NULL );
1292 memset( &Netgame, 0, sizeof(Netgame) );
1295 Net_player->flags |= NETINFO_FLAG_DO_NETWORKING;
1296 Net_player->player = Player;
1297 memcpy(&Net_player->p_info.addr,&Psnet_my_addr,sizeof(net_addr));
1299 // check for the existence of a CD
1300 multi_common_verify_cd();
1302 // load my local netplayer options
1303 multi_options_local_load(&Net_player->p_info.options, Net_player);
1309 common_set_interface_palette(MULTI_JOIN_PALETTE);
1312 // destroy any chatbox contents which previously existed (from another game)
1315 // create the interface window
1316 Multi_join_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
1317 Multi_join_window.set_mask_bmap(Multi_join_bitmap_mask_fname[gr_screen.res]);
1319 // load the background bitmap
1320 Multi_join_bitmap = bm_load(Multi_join_bitmap_fname[gr_screen.res]);
1321 if(Multi_join_bitmap < 0){
1322 // we failed to load the bitmap - this is very bad
1326 // intialize the endgame system
1327 multi_endgame_init();
1329 // initialize the common notification messaging
1330 multi_common_notify_init();
1332 // initialize the common text area
1333 multi_common_set_text("");
1335 // load and use the common interface palette
1336 multi_common_load_palette();
1337 multi_common_set_palette();
1339 // load the help overlay
1340 help_overlay_load(MULTI_JOIN_OVERLAY);
1341 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1343 // do TCP and VMT specific initialization
1344 if ( !Multi_options_g.pxo ) {
1345 // if this is a TCP (non tracker) game, we'll load up our default address list right now
1346 multi_join_load_tcp_addrs();
1348 multi_fs_tracker_send_game_request();
1351 // initialize any and all timestamps
1352 Multi_join_glr_stamp = -1;
1353 Multi_join_ping_stamp = -1;
1354 Multi_join_sent_stamp = -1;
1356 // reset frame count
1357 Multi_join_frame_count = 0;
1359 // haven't tried to verify on the tracker yet.
1360 Multi_join_mt_tried_verify = 0;
1362 // clear our all game lists to save hassles
1363 multi_join_clear_game_list();
1365 // create the interface buttons
1366 for(idx=0; idx<MULTI_JOIN_NUM_BUTTONS; idx++){
1367 // create the object
1368 Multi_join_buttons[gr_screen.res][idx].button.create(&Multi_join_window,"", Multi_join_buttons[gr_screen.res][idx].x, Multi_join_buttons[gr_screen.res][idx].y, 1, 1, 0, 1);
1370 // set the sound to play when highlighted
1371 Multi_join_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
1373 // set the ani for the button
1374 Multi_join_buttons[gr_screen.res][idx].button.set_bmaps(Multi_join_buttons[gr_screen.res][idx].filename);
1377 Multi_join_buttons[gr_screen.res][idx].button.link_hotspot(Multi_join_buttons[gr_screen.res][idx].hotspot);
1382 for(idx=0; idx<MULTI_JOIN_NUM_TEXT; idx++){
1383 Multi_join_window.add_XSTR(&Multi_join_text[gr_screen.res][idx]);
1387 Multi_join_should_send = -1;
1389 // close any previously open chatbox
1392 // create the list item select button
1393 Multi_join_select_button.create(&Multi_join_window, "", Mj_list_area_coords[gr_screen.res][MJ_X_COORD], Mj_list_area_coords[gr_screen.res][MJ_Y_COORD], Mj_list_area_coords[gr_screen.res][MJ_W_COORD], Mj_list_area_coords[gr_screen.res][MJ_H_COORD], 0, 1);
1394 Multi_join_select_button.hide();
1398 Multi_join_slider.create(&Multi_join_window, Mj_slider_coords[gr_screen.res][MJ_X_COORD], Mj_slider_coords[gr_screen.res][MJ_Y_COORD], Mj_slider_coords[gr_screen.res][MJ_W_COORD], Mj_slider_coords[gr_screen.res][MJ_H_COORD], 0, Mj_slider_name[gr_screen.res], &multi_join_list_scroll_up, &multi_join_list_scroll_down, NULL);
1401 // if starting a network game, then go to the create game screen
1402 if ( Cmdline_start_netgame ) {
1403 multi_join_create_game();
1404 } else if ( Cmdline_connect_addr != NULL ) {
1409 // joining a game. Send a join request to the given IP address, and wait for the return.
1410 memset( &Multi_autojoin_addr, 0, sizeof(net_addr) );
1411 Multi_autojoin_addr.type = NET_TCP;
1413 // create the address, looking out for port number at the end
1414 port_num = DEFAULT_GAME_PORT;
1415 p = strrchr(Cmdline_connect_addr, ':');
1419 port_num = (short)atoi(p);
1421 ip_addr = inet_addr(Cmdline_connect_addr);
1422 memcpy(Multi_autojoin_addr.addr, &ip_addr, IP_ADDRESS_LENGTH);
1423 Multi_autojoin_addr.port = port_num;
1425 send_server_query(&Multi_autojoin_addr);
1426 Multi_autojoin_query_stamp = timestamp(MULTI_AUTOJOIN_QUERY_STAMP);
1427 Multi_did_autojoin = 0;
1431 void multi_join_clear_game_list()
1434 Multi_join_list_selected = -1;
1435 Multi_join_selected_item = NULL;
1436 MJ_LIST_START_SET(-1);
1437 Multi_join_list_start_item = NULL;
1439 // free up the active game list
1440 multi_free_active_games();
1442 // initialize the active game list
1443 Active_game_head = NULL;
1444 Active_game_count = 0;
1447 void multi_join_game_do_frame()
1449 // check the status of our reliable socket. If not valid, popup error and return to main menu
1450 // I put this code here to avoid nasty gameseq issues with states. Also, we will have nice
1451 // background for the popup
1452 if ( !psnet_rel_check() ) {
1453 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("Network Error. Try exiting and restarting FreeSpace to clear the error. Otherwise, please reboot your machine.",756));
1454 gameseq_post_event(GS_EVENT_MAIN_MENU);
1458 // return here since we will be moving to the next stage anyway -- I don't want to see the backgrounds of
1459 // all the screens for < 1 second for every screen we automatically move to.
1460 if ( Cmdline_start_netgame ) {
1464 // when joining a network game, wait for the server query to come back, and then join the game
1465 if ( Cmdline_connect_addr != NULL ) {
1468 if ( !Multi_did_autojoin ) {
1469 rval = popup_till_condition(multi_join_autojoin_do, XSTR("&Cancel", 779), XSTR("Joining netgame", 1500) );
1471 // cancel was hit. Send the user back to the main hall
1472 gameseq_post_event(GS_EVENT_MAIN_MENU);
1473 Cmdline_connect_addr = NULL; // reset this value.
1476 // when we get here, we have the data -- join the game.
1477 multi_join_send_join_request(0);
1478 Multi_autojoin_join_stamp = timestamp(MULTI_AUTOJOIN_JOIN_STAMP);
1479 Multi_did_autojoin = 1;
1482 if ( timestamp_elapsed(Multi_autojoin_join_stamp) ) {
1483 multi_join_send_join_request(0);
1484 Multi_autojoin_join_stamp = timestamp(MULTI_AUTOJOIN_JOIN_STAMP);
1490 // reset the should send var
1491 Multi_join_should_send = -1;
1493 int k = Multi_join_window.process();
1495 // process any keypresses
1498 if(help_overlay_active(MULTI_JOIN_OVERLAY)){
1499 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1501 if (Multi_options_g.pxo == 1) {
1502 gameseq_post_event(GS_EVENT_PXO);
1504 gameseq_post_event(GS_EVENT_MAIN_MENU);
1507 gamesnd_play_iface(SND_USER_SELECT);
1511 // page up the game list
1513 multi_join_list_page_up();
1515 Multi_join_slider.force_currentItem(Multi_join_list_start);
1520 multi_pinfo_popup(Net_player);
1523 // page down the game list
1525 multi_join_list_page_down();
1527 Multi_join_slider.force_currentItem(Multi_join_list_start);
1531 // send out a ping-all
1533 multi_join_ping_all();
1534 Multi_join_ping_stamp = timestamp(MULTI_JOIN_PING_TIME);
1537 // shortcut to start a game
1539 multi_join_create_game();
1542 // scroll the game list up
1544 multi_join_list_scroll_up();
1546 Multi_join_slider.force_currentItem(Multi_join_list_start);
1550 // scroll the game list down
1552 multi_join_list_scroll_down();
1554 Multi_join_slider.force_currentItem(Multi_join_list_start);
1559 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
1560 help_overlay_set_state(MULTI_JOIN_OVERLAY, 0);
1563 // do any network related stuff
1564 multi_join_do_netstuff();
1566 // process any button clicks
1567 multi_join_check_buttons();
1569 // process any list selection stuff
1570 multi_join_process_select();
1572 // draw the background, etc
1574 GR_MAYBE_CLEAR_RES(Multi_join_bitmap);
1575 if(Multi_join_bitmap != -1){
1576 gr_set_bitmap(Multi_join_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1579 Multi_join_window.draw();
1581 // display the active games
1582 multi_join_display_games();
1584 // display any text in the info area
1585 multi_common_render_text();
1587 // display any pending notification messages
1588 multi_common_notify_do();
1590 // blit the CD icon and any PXO filter stuff
1591 multi_join_blit_top_stuff();
1593 // draw the help overlay
1594 help_overlay_maybe_blit(MULTI_JOIN_OVERLAY);
1599 // if we are supposed to be sending a join request
1600 if(Multi_join_should_send != -1){
1601 multi_join_send_join_request(Multi_join_should_send);
1603 Multi_join_should_send = -1;
1605 // increment the frame count
1606 Multi_join_frame_count++;
1609 void multi_join_game_close()
1611 // unload any bitmaps
1612 if(!bm_unload(Multi_join_bitmap)){
1613 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_join_bitmap_fname[gr_screen.res]));
1616 // unload the help overlay
1617 help_overlay_unload(MULTI_JOIN_OVERLAY);
1619 // free up the active game list
1620 multi_free_active_games();
1622 // destroy the UI_WINDOW
1623 Multi_join_window.destroy();
1626 common_free_interface_palette();
1630 void multi_join_check_buttons()
1633 for(idx=0;idx<MULTI_JOIN_NUM_BUTTONS;idx++){
1634 // we only really need to check for one button pressed at a time, so we can break after
1636 if(Multi_join_buttons[gr_screen.res][idx].button.pressed()){
1637 multi_join_button_pressed(idx);
1643 void multi_join_button_pressed(int n)
1647 // if we're player PXO, go back there
1648 if (Multi_options_g.pxo == 1) {
1649 gameseq_post_event(GS_EVENT_PXO);
1651 gameseq_post_event(GS_EVENT_MAIN_MENU);
1653 gamesnd_play_iface(SND_USER_SELECT);
1656 if(Active_game_count <= 0){
1657 multi_common_add_notify(XSTR("No games found!",757));
1658 gamesnd_play_iface(SND_GENERAL_FAIL);
1659 } else if(Multi_join_list_selected == -1){
1660 multi_common_add_notify(XSTR("No game selected!",758));
1661 gamesnd_play_iface(SND_GENERAL_FAIL);
1662 } else if((Multi_join_sent_stamp != -1) && !timestamp_elapsed(Multi_join_sent_stamp)){
1663 multi_common_add_notify(XSTR("Still waiting on previous join request!",759));
1664 gamesnd_play_iface(SND_GENERAL_FAIL);
1666 // otherwise, if he's already played PXO games, warn him
1668 if(Player->flags & PLAYER_FLAGS_HAS_PLAYED_PXO){
1669 if(!multi_join_warn_pxo()){
1675 // send the join request here
1676 SDL_assert(Multi_join_selected_item != NULL);
1678 // send a join request packet
1679 Multi_join_should_send = 0;
1681 gamesnd_play_iface(SND_COMMIT_PRESSED);
1687 if(!help_overlay_active(MULTI_JOIN_OVERLAY)){
1688 help_overlay_set_state(MULTI_JOIN_OVERLAY,1);
1690 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1694 // scroll the game list up
1696 multi_join_list_scroll_up();
1698 Multi_join_slider.force_currentItem(Multi_join_list_start);
1702 // scroll the game list down
1703 case MJ_SCROLL_DOWN:
1704 multi_join_list_scroll_down();
1706 Multi_join_slider.force_currentItem(Multi_join_list_start);
1710 // scroll the info text box up
1711 case MJ_SCROLL_INFO_UP:
1712 multi_common_scroll_text_up();
1715 // scroll the info text box down
1716 case MJ_SCROLL_INFO_DOWN:
1717 multi_common_scroll_text_down();
1720 // go to the options screen
1722 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
1725 // go to the start game screen
1727 multi_join_create_game();
1730 // refresh the game/server list
1732 gamesnd_play_iface(SND_USER_SELECT);
1733 broadcast_game_query();
1736 // join a game as an observer
1737 case MJ_JOIN_OBSERVER:
1738 if(Active_game_count <= 0){
1739 multi_common_add_notify(XSTR("No games found!",757));
1740 gamesnd_play_iface(SND_GENERAL_FAIL);
1741 } else if(Multi_join_list_selected == -1){
1742 multi_common_add_notify(XSTR("No game selected!",758));
1743 gamesnd_play_iface(SND_GENERAL_FAIL);
1744 } else if((Multi_join_sent_stamp != -1) && !timestamp_elapsed(Multi_join_sent_stamp)){
1745 multi_common_add_notify(XSTR("Still waiting on previous join request!",759));
1746 gamesnd_play_iface(SND_GENERAL_FAIL);
1748 // send the join request here
1749 SDL_assert(Multi_join_selected_item != NULL);
1751 Multi_join_should_send = 1;
1753 gamesnd_play_iface(SND_COMMIT_PRESSED);
1758 multi_common_add_notify(XSTR("Not implemented yet!",760));
1759 gamesnd_play_iface(SND_GENERAL_FAIL);
1764 // display all relevant info for active games
1765 void multi_join_display_games()
1767 active_game *moveup = Multi_join_list_start_item;
1771 int y_start = Mj_list_y[gr_screen.res];
1776 // blit the game status (including text and type icon)
1777 multi_join_blit_game_status(moveup,y_start);
1779 // get the connection type
1780 con_type = (moveup->flags & AG_FLAG_CONNECTION_SPEED_MASK) >> AG_FLAG_CONNECTION_BIT;
1781 if((con_type > 4) || (con_type < 0)){
1785 // display the connection speed
1787 SDL_strlcpy(str, Multi_join_speed_labels[con_type], SDL_arraysize(str));
1788 gr_set_color_fast(Multi_join_speed_colors[con_type]);
1789 gr_string(Mj_speed_coords[gr_screen.res][MJ_X_COORD], y_start, str);
1791 // we'll want to have different colors for highlighted items, etc.
1792 if(moveup == Multi_join_selected_item){
1793 gr_set_color_fast(&Color_text_selected);
1795 gr_set_color_fast(&Color_text_normal);
1798 // display the game name, adding appropriate status chars
1800 if(moveup->flags & AG_FLAG_STANDALONE){
1801 SDL_strlcat(str, MJ_CHAR_STANDALONE, SDL_arraysize(str));
1803 if(moveup->flags & AG_FLAG_CAMPAIGN){
1804 SDL_strlcat(str, MJ_CHAR_CAMPAIGN, SDL_arraysize(str));
1807 // tack on the actual server name
1808 SDL_strlcat(str, " ", SDL_arraysize(str));
1809 SDL_strlcat(str, moveup->name, SDL_arraysize(str));
1810 if(strlen(moveup->mission_name) > 0){
1811 SDL_strlcat(str, " / ", SDL_arraysize(str));
1812 SDL_strlcat(str, moveup->mission_name, SDL_arraysize(str));
1815 // make sure the string fits in the display area and draw it
1816 gr_force_fit_string(str,200,Mj_game_name_coords[gr_screen.res][MJ_W_COORD]);
1817 gr_string(Mj_game_name_coords[gr_screen.res][MJ_X_COORD],y_start,str);
1819 // display the ping time
1820 if(moveup->ping.ping_avg > 0){
1821 if(moveup->ping.ping_avg > 1000){
1822 gr_set_color_fast(&Color_bright_red);
1823 SDL_strlcpy(str, XSTR("> 1 sec",761), SDL_arraysize(str));
1825 // set the appropriate ping time color indicator
1826 if(moveup->ping.ping_avg > MJ_PING_YELLOW){
1827 gr_set_color_fast(&Color_bright_red);
1828 } else if(moveup->ping.ping_avg > MJ_PING_YELLOW){
1829 gr_set_color_fast(&Color_bright_yellow);
1831 gr_set_color_fast(&Color_bright_green);
1834 SDL_snprintf(str, SDL_arraysize(str), "%d%s", moveup->ping.ping_avg, XSTR(" ms",762));
1837 gr_string(Mj_ping_coords[gr_screen.res][MJ_X_COORD],y_start,str);
1840 // display the number of players (be sure to center it)
1841 if(moveup == Multi_join_selected_item){
1842 gr_set_color_fast(&Color_text_selected);
1844 gr_set_color_fast(&Color_text_normal);
1846 SDL_snprintf(str, SDL_arraysize(str), "%d", moveup->num_players);
1847 gr_get_string_size(&w,&h,str);
1848 gr_string(Mj_players_coords[gr_screen.res][MJ_X_COORD] + (Mj_players_coords[gr_screen.res][MJ_W_COORD] - w)/2,y_start,str);
1852 moveup = moveup->next;
1853 } while((moveup != Active_game_head) && (count < Mj_max_game_items[gr_screen.res]));
1855 // if there are no items on the list, display this info
1857 gr_set_color_fast(&Color_bright);
1858 gr_string(Mj_game_name_coords[gr_screen.res][MJ_X_COORD] - 30,y_start,XSTR("<No game servers found>",763));
1862 void multi_join_blit_game_status(active_game *game, int y)
1865 char status_text[25];
1867 // blit the proper icon
1869 switch( game->flags & AG_FLAG_TYPE_MASK ){
1872 if(Multi_common_icons[MICON_COOP] != -1){
1873 gr_set_bitmap(Multi_common_icons[MICON_COOP], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1878 // team vs. team game
1880 if(Multi_common_icons[MICON_TVT] != -1){
1881 gr_set_bitmap(Multi_common_icons[MICON_TVT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1888 case AG_FLAG_DOGFIGHT:
1889 if(Multi_common_icons[MICON_DOGFIGHT] != -1){
1890 gr_set_bitmap(Multi_common_icons[MICON_DOGFIGHT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1896 // if we're supposed to draw a bitmap
1898 gr_bitmap(Mj_game_icon_coords[gr_screen.res][MJ_X_COORD],y-1);
1901 // blit the proper status text
1902 memset(status_text,0,25);
1904 switch( game->flags & AG_FLAG_STATE_MASK ){
1905 case AG_FLAG_FORMING:
1906 gr_set_color_fast(&Color_bright_green);
1907 SDL_strlcpy(status_text, XSTR("Forming", 764), SDL_arraysize(status_text));
1909 case AG_FLAG_BRIEFING:
1910 gr_set_color_fast(&Color_bright_red);
1911 SDL_strlcpy(status_text, XSTR("Briefing", 765), SDL_arraysize(status_text));
1913 case AG_FLAG_DEBRIEF:
1914 gr_set_color_fast(&Color_bright_red);
1915 SDL_strlcpy(status_text, XSTR("Debrief", 766), SDL_arraysize(status_text));
1918 gr_set_color_fast(&Color_bright_red);
1919 SDL_strlcpy(status_text, XSTR("Paused", 767), SDL_arraysize(status_text));
1921 case AG_FLAG_IN_MISSION:
1922 gr_set_color_fast(&Color_bright_red);
1923 SDL_strlcpy(status_text, XSTR("Playing", 768), SDL_arraysize(status_text));
1926 gr_set_color_fast(&Color_bright);
1927 SDL_strlcpy(status_text, XSTR("Unknown", 769), SDL_arraysize(status_text));
1930 gr_get_string_size(&str_w,NULL,status_text);
1931 gr_string(Mj_status_coords[gr_screen.res][MJ_X_COORD] + ((Mj_status_coords[gr_screen.res][MJ_W_COORD] - str_w)/2),y,status_text);
1934 // load in a list of active games from our tcp.cfg file
1935 void multi_join_load_tcp_addrs()
1937 char line[MAX_IP_STRING];
1942 // attempt to open the ip list file
1943 file = cfopen(IP_CONFIG_FNAME,"rt",CFILE_NORMAL,CF_TYPE_DATA);
1945 nprintf(("Network","Error loading tcp.cfg file!\n"));
1949 // free up any existing server list
1950 multi_free_server_list();
1952 // read in all the strings in the file
1953 while(!cfeof(file)){
1955 cfgets(line,MAX_IP_STRING,file);
1957 // strip off any newline character
1958 if(line[strlen(line) - 1] == '\n'){
1959 line[strlen(line) - 1] = '\0';
1962 // empty lines don't get processed
1963 if( (line[0] == '\0') || (line[0] == '\n') ){
1967 if ( !psnet_is_valid_ip_string(line) ) {
1968 nprintf(("Network","Invalid ip string (%s)\n",line));
1970 // copy the server ip address
1971 memset(&addr,0,sizeof(net_addr));
1972 addr.type = NET_TCP;
1973 psnet_string_to_addr(&addr, line, SDL_arraysize(line));
1974 if ( addr.port == 0 ){
1975 addr.port = DEFAULT_GAME_PORT;
1978 // create a new server item on the list
1979 item = multi_new_server_item();
1981 memcpy(&item->server_addr,&addr,sizeof(net_addr));
1989 // do stuff like pinging servers, sending out requests, etc
1990 void multi_join_do_netstuff()
1992 // handle game query stuff
1993 if(Multi_join_glr_stamp == -1){
1994 broadcast_game_query();
1996 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
1997 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME_LOCAL);
1999 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME);
2002 // otherwise send out game query and restamp
2003 else if(timestamp_elapsed(Multi_join_glr_stamp)){
2004 broadcast_game_query();
2006 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
2007 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME_LOCAL);
2009 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME);
2013 // check to see if we've been accepted. If so, put up message saying so
2014 if ( Net_player->flags & (NETINFO_FLAG_ACCEPT_INGAME|NETINFO_FLAG_ACCEPT_CLIENT|NETINFO_FLAG_ACCEPT_HOST|NETINFO_FLAG_ACCEPT_OBSERVER) ) {
2015 multi_common_add_notify(XSTR("Accepted. Waiting for player data.",770));
2018 // check to see if any join packets we have sent have timed out
2019 if((Multi_join_sent_stamp != -1) && (timestamp_elapsed(Multi_join_sent_stamp))){
2020 Multi_join_sent_stamp = -1;
2021 multi_common_add_notify(XSTR("Join request timed out!",771));
2024 // check to see if we should be pinging everyone
2025 if((Multi_join_ping_stamp == -1) || (timestamp_elapsed(Multi_join_ping_stamp))){
2026 multi_join_ping_all();
2027 Multi_join_ping_stamp = timestamp(MULTI_JOIN_PING_TIME);
2031 multi_join_cull_timeouts();
2034 // evaluate a returned pong.
2035 void multi_join_eval_pong(net_addr *addr, fix pong_time)
2038 active_game *moveup = Active_game_head;
2043 if(psnet_same(&moveup->server_addr,addr)){
2045 multi_ping_eval_pong(&moveup->ping);
2049 moveup = moveup->next;
2051 } while(moveup != Active_game_head);
2054 // update the game's ping
2056 if(found && (moveup->ping_end > moveup->ping_start)){
2057 moveup->ping_time = f2fl(moveup->ping_end - moveup->ping_start);
2058 moveup->ping_start = -1;
2059 moveup->ping_end = -1;
2064 // ping all the server on the list
2065 void multi_join_ping_all()
2067 active_game *moveup = Active_game_head;
2072 moveup->ping_start = timer_get_fixed_seconds();
2073 moveup->ping_end = -1;
2074 send_ping(&moveup->server_addr);
2076 multi_ping_send(&moveup->server_addr,&moveup->ping);
2078 moveup = moveup->next;
2079 } while(moveup != Active_game_head);
2083 void multi_join_process_select()
2085 // if we don't have anything selected and there are items on the list - select the first one
2086 if((Multi_join_list_selected == -1) && (Active_game_count > 0)){
2087 Multi_join_list_selected = 0;
2088 Multi_join_selected_item = multi_join_get_game(0);
2089 MJ_LIST_START_SET(0);
2090 Multi_join_list_start_item = Multi_join_selected_item;
2092 // send a mission description request to this guy
2093 send_netgame_descript_packet(&Multi_join_selected_item->server_addr,0);
2094 multi_common_set_text("");
2096 // I sure hope this doesn't happen
2097 SDL_assert(Multi_join_selected_item != NULL);
2100 // otherwise see if he's clicked on an item
2101 else if(Multi_join_select_button.pressed() && (Active_game_count > 0)){
2103 Multi_join_select_button.get_mouse_pos(NULL,&y);
2105 if(item + Multi_join_list_start < Active_game_count){
2106 gamesnd_play_iface(SND_IFACE_MOUSE_CLICK);
2108 Multi_join_list_selected = item + Multi_join_list_start;
2109 Multi_join_selected_item = multi_join_get_game(Multi_join_list_selected);
2111 // I sure hope this doesn't happen
2112 SDL_assert(Multi_join_selected_item != NULL);
2114 // send a mission description request to this guy
2115 send_netgame_descript_packet(&Multi_join_selected_item->server_addr,0);
2116 multi_common_set_text("");
2120 // if he's double clicked, then select it and accept
2121 if(Multi_join_select_button.double_clicked()){
2123 Multi_join_select_button.get_mouse_pos(NULL,&y);
2125 if(item == Multi_join_list_selected){
2126 multi_join_button_pressed(MJ_ACCEPT);
2131 // return game n (0 based index)
2132 active_game *multi_join_get_game(int n)
2134 active_game *moveup = Active_game_head;
2141 moveup = moveup->next;
2142 while((moveup != Active_game_head) && (count != n)){
2143 moveup = moveup->next;
2146 if(moveup == Active_game_head){
2147 nprintf(("Network","Warning, couldn't find game item %d!\n",n));
2157 // scroll through the game list
2158 void multi_join_list_scroll_up()
2160 // if we're not at the beginning of the list, scroll up
2161 if(Multi_join_list_start_item != Active_game_head){
2162 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2164 MJ_LIST_START_DEC();
2166 gamesnd_play_iface(SND_SCROLL);
2168 gamesnd_play_iface(SND_GENERAL_FAIL);
2172 // scroll through the game list
2173 void multi_join_list_scroll_down()
2175 if((Active_game_count - Multi_join_list_start) > Mj_max_game_items[gr_screen.res]){
2176 Multi_join_list_start_item = Multi_join_list_start_item->next;
2178 MJ_LIST_START_INC();
2180 gamesnd_play_iface(SND_SCROLL);
2182 gamesnd_play_iface(SND_GENERAL_FAIL);
2186 void multi_join_list_page_up()
2188 // in this case, just set us to the beginning of the list
2189 if((Multi_join_list_start - Mj_max_game_items[gr_screen.res]) < 0){
2190 Multi_join_list_start_item = Active_game_head;
2192 MJ_LIST_START_SET(0);
2194 gamesnd_play_iface(SND_SCROLL);
2196 // otherwise page the whole thing up
2198 for(idx=0; idx<Mj_max_game_items[gr_screen.res]; idx++){
2199 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2201 MJ_LIST_START_DEC();
2203 gamesnd_play_iface(SND_SCROLL);
2207 void multi_join_list_page_down()
2211 // page the whole thing down
2212 while((count < Mj_max_game_items[gr_screen.res]) && ((Active_game_count - Multi_join_list_start) > Mj_max_game_items[gr_screen.res])){
2213 Multi_join_list_start_item = Multi_join_list_start_item->next;
2214 MJ_LIST_START_INC();
2219 gamesnd_play_iface(SND_SCROLL);
2222 void multi_join_cull_timeouts()
2224 active_game *backup;
2226 active_game *moveup = Active_game_head;
2228 // traverse through the entire list if any items exist
2232 if((moveup->heard_from_timer != -1) && (timestamp_elapsed(moveup->heard_from_timer))){
2233 Active_game_count--;
2235 // if this is the head of the list
2236 if(moveup == Active_game_head){
2237 // if this is the _only_ item on the list
2238 if(moveup->next == Active_game_head){
2239 // handle any gui details related to deleting this item
2240 multi_join_handle_item_cull(Active_game_head, count);
2242 free(Active_game_head);
2243 Active_game_head = NULL;
2246 // if there are other items on the list
2248 // handle any gui details related to deleting this item
2249 multi_join_handle_item_cull(moveup, count);
2251 Active_game_head = moveup->next;
2252 Active_game_head->prev = moveup->prev;
2253 Active_game_head->prev->next = Active_game_head;
2255 moveup = Active_game_head;
2258 // if its somewhere else on the list
2260 // handle any gui details related to deleting this item
2261 multi_join_handle_item_cull(moveup, count);
2263 // if its the last item on the list
2264 moveup->next->prev = moveup->prev;
2265 moveup->prev->next = moveup->next;
2267 // if it was the last element on the list, return
2268 if(moveup->next == Active_game_head){
2272 backup = moveup->next;
2278 moveup = moveup->next;
2281 } while(moveup != Active_game_head);
2285 // deep magic begins here.
2286 void multi_join_handle_item_cull(active_game *item, int item_index)
2288 // if this is the only item on the list, unset everything
2289 if(item->next == item){
2290 Multi_join_list_selected = -1;
2291 Multi_join_selected_item = NULL;
2294 Multi_join_slider.set_numberItems(0);
2296 MJ_LIST_START_SET(-1);
2297 Multi_join_list_start_item = NULL;
2303 // see if we should be adjusting our currently selected item
2304 if(item_index <= Multi_join_list_selected){
2305 // the selected item is the head of the list
2306 if(Multi_join_selected_item == Active_game_head){
2307 // move the pointer up since this item is about to be destroyed
2308 Multi_join_selected_item = Multi_join_selected_item->next;
2310 // if this is the item being deleted, select the previous one
2311 if(item == Multi_join_selected_item){
2313 Multi_join_selected_item = Multi_join_selected_item->prev;
2315 // decrement the selected index by 1
2316 Multi_join_list_selected--;
2318 // now we know its a previous item, so our pointer stays the same but our index goes down by one, since there will be
2319 // 1 less item on the list
2321 // decrement the selected index by 1
2322 Multi_join_list_selected--;
2327 // see if we should be adjusting out current start position
2328 if(item_index <= Multi_join_list_start){
2329 // the start position is the head of the list
2330 if(Multi_join_list_start_item == Active_game_head){
2331 // move the pointer up since this item is about to be destroyed
2332 Multi_join_list_start_item = Multi_join_list_start_item->next;
2334 // if this is the item being deleted, select the previous one
2335 if(item == Multi_join_list_start_item){
2336 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2338 // decrement the starting index by 1
2339 MJ_LIST_START_DEC();
2341 // but decrement the starting index by 1
2342 MJ_LIST_START_DEC();
2347 // maybe go back up a bit so that we always have a full page of items
2348 if(Active_game_count > Mj_max_game_items[gr_screen.res]){
2349 while((Active_game_count - Multi_join_list_start) < Mj_max_game_items[gr_screen.res]){
2350 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2351 MJ_LIST_START_DEC();
2355 // set slider location
2357 Multi_join_slider.set_numberItems(Active_game_count > Mj_max_game_items[gr_screen.res] ? Active_game_count - Mj_max_game_items[gr_screen.res] : 0);
2358 Multi_join_slider.force_currentItem(Multi_join_list_start);
2362 void multi_join_send_join_request(int as_observer)
2364 // don't do anything if we have no items selected
2365 if(Multi_join_selected_item == NULL){
2369 // 5/26/98 -- for team v team games, don't allow ingame joining :-(
2370 if ( (Multi_join_selected_item->flags & AG_FLAG_TEAMS) && (Multi_join_selected_item->flags & (AG_FLAG_PAUSE|AG_FLAG_IN_MISSION)) ) {
2371 popup(0, 1, POPUP_OK, XSTR("Joining ingame is currently not allowed for team vs. team games",772));
2375 memset(&Multi_join_request,0,sizeof(join_request));
2377 // if the netgame is in password mode, put up a request for the password
2378 if(Multi_join_selected_item->flags & AG_FLAG_PASSWD){
2379 if(!multi_passwd_popup(Multi_join_request.passwd, SDL_arraysize(Multi_join_request.passwd))){
2383 nprintf(("Password : %s\n",Multi_join_request.passwd));
2386 // fill out the join request struct
2387 SDL_strlcpy(Multi_join_request.callsign, Player->callsign, SDL_arraysize(Multi_join_request.callsign));
2388 if(strlen(Player->image_filename) > 0){
2389 SDL_strlcpy(Multi_join_request.image_filename, Player->image_filename, SDL_arraysize(Multi_join_request.image_filename));
2392 if(strlen(Player->squad_filename) > 0){
2393 SDL_strlcpy(Multi_join_request.squad_filename, Player->squad_filename, SDL_arraysize(Multi_join_request.squad_filename));
2397 // tracker id (if any)
2398 Multi_join_request.tracker_id = Multi_tracker_id;
2400 // player's rank (at least, what he wants you to _believe_)
2401 Multi_join_request.player_rank = (ubyte)Player->stats.rank;
2404 Multi_join_request.flags = 0;
2406 Multi_join_request.flags |= JOIN_FLAG_AS_OBSERVER;
2409 // if the player has hacked data
2410 if(game_hacked_data()){
2411 Multi_join_request.flags |= JOIN_FLAG_HAXOR;
2416 SDL_strlcpy(Multi_join_request.pxo_squad_name, Multi_tracker_squad_name, LOGIN_LEN);
2419 // version of this server
2420 Multi_join_request.version = MULTI_FS_SERVER_VERSION;
2422 // server compatible version
2423 Multi_join_request.comp_version = MULTI_FS_SERVER_COMPATIBLE_VERSION;
2425 // his local player options
2426 memcpy(&Multi_join_request.player_options,&Player->m_local_options,sizeof(multi_local_options));
2428 // set the server address for the netgame
2429 memcpy(&Netgame.server_addr,&Multi_join_selected_item->server_addr,sizeof(net_addr));
2431 // send a join request to the guy
2432 send_join_packet(&Multi_join_selected_item->server_addr,&Multi_join_request);
2435 Multi_join_sent_stamp = timestamp(MULTI_JOIN_SENT_WAIT);
2438 multi_common_add_notify(XSTR("Sending join request...",773));
2441 void multi_join_create_game()
2443 // maybe warn the player about possible crappy server conditions
2444 if(!multi_join_maybe_warn()){
2448 // make sure to flag ourself as being the master
2449 Net_player->flags |= (NETINFO_FLAG_AM_MASTER | NETINFO_FLAG_GAME_HOST);
2450 Net_player->state = NETPLAYER_STATE_HOST_SETUP;
2452 // if we're in PXO mode, mark it down in our player struct
2453 if(MULTI_IS_TRACKER_GAME){
2454 Player->flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
2455 Player->save_flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
2457 // otherwise, if he's already played PXO games, warn him
2460 if(Player->flags & PLAYER_FLAGS_HAS_PLAYED_PXO){
2461 if(!multi_join_warn_pxo()){
2468 gameseq_post_event(GS_EVENT_MULTI_START_GAME);
2469 gamesnd_play_iface(SND_USER_SELECT);
2472 void multi_join_reset_join_stamp()
2474 // unset the timestamp here so the user can immediately send another join request
2475 Multi_join_sent_stamp = -1;
2476 multi_common_add_notify("");
2479 void multi_join_blit_top_stuff()
2481 // blit the cd icon if he has one
2482 if(Multi_has_cd && (Multi_common_icons[MICON_CD] != -1)){
2485 bm_get_info(Multi_common_icons[MICON_CD], &cd_w, NULL, NULL, NULL, NULL);
2487 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
2488 gr_bitmap((gr_screen.max_w / 2) - (cd_w / 2), Mj_cd_coords[gr_screen.res]);
2492 #define CW_CODE_CANCEL 0 // cancel the action
2493 #define CW_CODE_OK 1 // continue anyway
2494 #define CW_CODE_INFO 2 // gimme some more information
2496 #define LOW_WARN_TEXT XSTR("Warning - You have low object updates selected. A server with low object updates will not be able to handle more than 1 client without performance problems",775)
2497 #define LOW_INFO_TEXT XSTR("Low update level caps all bandwidth at ~2000 bytes/second. It is appropriate for clients with 28.8 modems, but is not reccomended for servers. In addition, any clients connecting to this server should use low object updates as well. To change your settings go to the options menu (f2), and select the Multi button",776)
2499 #define MED_WARN_TEXT XSTR("Warning - You have medium object updates selected. A server with medium object updates will not be able to handle more than 1 or 2 clients without performance problems",777)
2500 #define MED_INFO_TEXT XSTR("Medium update level caps all bandwidth at ~4000 bytes/second. It is appropriate for clients with 56.6 modems, but is not reccomended for servers. In addition, any clients connecting to this server should use low object updates as well. To change your settings go to the options menu (f2), and select the Multi button",778)
2502 int multi_join_warn_update_low(int code)
2506 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),LOW_WARN_TEXT);
2509 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),LOW_INFO_TEXT);
2512 return CW_CODE_CANCEL;
2515 int multi_join_warn_update_medium(int code)
2519 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),MED_WARN_TEXT);
2522 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),MED_INFO_TEXT);
2525 return CW_CODE_CANCEL;
2528 int multi_join_maybe_warn()
2532 // if the player is set for low updates
2533 if(Player->m_local_options.obj_update_level == OBJ_UPDATE_LOW){
2536 code = multi_join_warn_update_low(code);
2537 } while((code != CW_CODE_CANCEL) && (code != CW_CODE_OK));
2542 // if the player is set for medium updates
2543 else if(Player->m_local_options.obj_update_level == OBJ_UPDATE_MEDIUM){
2546 code = multi_join_warn_update_medium(code);
2547 } while((code != CW_CODE_CANCEL) && (code != CW_CODE_OK));
2555 int multi_join_warn_pxo()
2557 // return popup(PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON | PF_TITLE_BIG | PF_TITLE_RED, 2, XSTR("&Back", 995), XSTR("&Continue",780), XSTR("Warning\n\nThis pilot has played PXO games. If you continue and play a non-PXO game, your stats will not be updated", 1006)) <= 0 ? 0 : 1;
2561 void multi_join_blit_protocol()
2563 gr_set_color_fast(&Color_bright);
2565 switch(Socket_type){
2568 gr_string(5, 2, "TCP");
2577 // -------------------------------------------------------------------------------------------------
2579 // MULTIPLAYER START GAME screen
2584 #define MULTI_SG_PALETTE "InterfacePalette"
2586 static const char *Multi_sg_bitmap_fname[GR_NUM_RESOLUTIONS] = {
2587 "MultiStartGame", // GR_640
2588 "2_MultiStartGame" // GR_1024
2591 static const char *Multi_sg_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
2592 "MultiStartGame-M", // GR_640
2593 "2_MultiStartGame-M" // GR_1024
2598 int Multi_sg_rank_max_display[GR_NUM_RESOLUTIONS] = {
2603 // constants for coordinate look ups
2604 #define MSG_X_COORD 0
2605 #define MSG_Y_COORD 1
2606 #define MSG_W_COORD 2
2607 #define MSG_H_COORD 3
2611 // input password field
2612 int Msg_passwd_coords[GR_NUM_RESOLUTIONS][4] = {
2625 // input game title field
2626 int Msg_title_coords[GR_NUM_RESOLUTIONS][4] = {
2639 // rank selected field
2640 int Msg_rank_sel_coords[GR_NUM_RESOLUTIONS][4] = {
2654 int Msg_rank_list_coords[GR_NUM_RESOLUTIONS][4] = {
2670 #define MULTI_SG_NUM_BUTTONS 12
2671 #define MSG_OPEN_GAME 0
2672 #define MSG_CLOSED_GAME 1
2673 #define MSG_PASSWD_GAME 2
2674 #define MSG_RESTRICTED_GAME 3
2675 #define MSG_RANK_SET_GAME 4
2676 #define MSG_RANK_SCROLL_UP 5
2677 #define MSG_RANK_SCROLL_DOWN 6
2678 #define MSG_RANK_ABOVE 7
2679 #define MSG_RANK_BELOW 8
2681 #define MSG_OPTIONS 10
2682 #define MSG_ACCEPT 11
2684 #define MULTI_SG_NUM_BUTTONS 10
2685 #define MSG_OPEN_GAME 0
2686 //#define MSG_CLOSED_GAME 1
2687 //#define MSG_RESTRICTED_GAME 2
2688 #define MSG_PASSWD_GAME 1
2689 #define MSG_RANK_SET_GAME 2
2690 #define MSG_RANK_SCROLL_UP 3
2691 #define MSG_RANK_SCROLL_DOWN 4
2692 #define MSG_RANK_ABOVE 5
2693 #define MSG_RANK_BELOW 6
2695 #define MSG_OPTIONS 8
2696 #define MSG_ACCEPT 9
2699 UI_WINDOW Multi_sg_window; // the window object for the join screen
2700 UI_BUTTON Multi_sg_rank_button; // for selecting the rank marker
2701 UI_INPUTBOX Multi_sg_game_name; // for Netgame.name
2702 UI_INPUTBOX Multi_sg_game_passwd; // for Netgame.passwd
2703 int Multi_sg_bitmap; // the background bitmap
2705 ui_button_info Multi_sg_buttons[GR_NUM_RESOLUTIONS][MULTI_SG_NUM_BUTTONS] = {
2708 ui_button_info("MSG_00", 75, 111, -1, -1, 0),
2709 ui_button_info("MSG_01", 75, 139, -1, -1, 1),
2710 ui_button_info("MSG_02", 75, 164, -1, -1, 2),
2711 ui_button_info("MSG_03", 75, 199, -1, -1, 3),
2712 ui_button_info("MSG_04", 75, 243, -1, -1, 4),
2713 ui_button_info("MSG_05", 376, 231, -1, -1, 5),
2714 ui_button_info("MSG_06", 376, 258, -1, -1, 6),
2715 ui_button_info("MSG_07", 376, 291, -1, -1, 7),
2716 ui_button_info("MSG_08", 376, 320, -1, -1, 8),
2717 ui_button_info("MSG_09", 469, 427, -1, -1, 9),
2718 ui_button_info("MSG_10", 447, 452, -1, -1, 10),
2719 ui_button_info("MSG_11", 561, 411, -1, -1, 11),
2721 ui_button_info("MSG_00", 1, 184, 34, 191, 2), // open
2722 // ui_button_info("MSG_01", 1, 159, 34, 166, 1), // closed
2723 // ui_button_info("MSG_02", 1, 184, 34, 191, 2), // restricted
2724 ui_button_info("MSG_03", 1, 209, 34, 218, 3), // password
2725 ui_button_info("MSG_04", 1, 257, 34, 266, 4), // rank set
2726 ui_button_info("MSG_05", 1, 282, -1, -1, 5), // rank scroll up
2727 ui_button_info("MSG_06", 1, 307, -1, -1, 6), // rank scroll down
2728 ui_button_info("MSG_07", 177, 282, 210, 290, 7), // rank above
2729 ui_button_info("MSG_08", 177, 307, 210, 315, 8), // rank below
2730 ui_button_info("MSG_09", 536, 429, 500, 440, 9), // help
2731 ui_button_info("MSG_10", 536, 454, 479, 464, 10), // options
2732 ui_button_info("MSG_11", 576, 432, 571, 415, 11), // accept
2736 ui_button_info("2_MSG_00", 2, 295, 51, 307, 2), // open
2737 // ui_button_info("2_MSG_01", 2, 254, 51, 267, 1), // closed
2738 // ui_button_info("2_MSG_02", 2, 295, 51, 307, 2), // restricted
2739 ui_button_info("2_MSG_03", 2, 335, 51, 350, 3), // password
2740 ui_button_info("2_MSG_04", 2, 412, 51, 426, 4), // rank set
2741 ui_button_info("2_MSG_05", 2, 452, -1, -1, 5), // rank scroll up
2742 ui_button_info("2_MSG_06", 2, 492, -1, -1, 6), // rank scroll down
2743 ui_button_info("2_MSG_07", 284, 452, 335, 465, 7), // rank above
2744 ui_button_info("2_MSG_08", 284, 492, 335, 505, 8), // rank below
2745 ui_button_info("2_MSG_09", 858, 687, 817, 706, 9), // help
2746 ui_button_info("2_MSG_10", 858, 728, 797, 743, 10), // options
2747 ui_button_info("2_MSG_11", 921, 692, 921, 664, 11), // accept
2748 #ifdef MAKE_FS1 // filler for extra FS1 buttons
2749 ui_button_info("none", -1, -1, -1, -1, -1),
2750 ui_button_info("none", -1, -1, -1, -1, -1),
2756 #define MULTI_SG_NUM_TEXT 11
2758 UI_XSTR Multi_sg_text[GR_NUM_RESOLUTIONS][MULTI_SG_NUM_TEXT] = {
2760 {"Open", 1322, 34, 191, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_OPEN_GAME].button},
2761 // {"Closed", 1323, 34, 166, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_CLOSED_GAME].button},
2762 // {"Restricted", 1324, 34, 191, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RESTRICTED_GAME].button},
2763 {"Password Protected", 1325, 34, 218, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_PASSWD_GAME].button},
2764 {"Allow Rank", 1326, 34, 266, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_SET_GAME].button},
2765 {"Above", 1327, 210, 290, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_ABOVE].button},
2766 {"Below", 1328, 210, 315, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_BELOW].button},
2767 {"Help", 928, 500, 440, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_HELP].button},
2768 {"Options", 1036, 479, 464, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_OPTIONS].button},
2769 {"Accept", 1035, 571, 415, UI_XSTR_COLOR_PINK, -1, &Multi_sg_buttons[0][MSG_ACCEPT].button},
2770 {"Start Game", 1329, 26, 10, UI_XSTR_COLOR_GREEN, -1, NULL},
2771 {"Title", 1330, 26, 31, UI_XSTR_COLOR_GREEN, -1, NULL},
2772 {"Game Type", 1331, 12, 165, UI_XSTR_COLOR_GREEN, -1, NULL},
2775 {"Open", 1322, 51, 307, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_OPEN_GAME].button},
2776 // {"Closed", 1323, 51, 267, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_CLOSED_GAME].button},
2777 // {"Restricted", 1324, 51, 307, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RESTRICTED_GAME].button},
2778 {"Password Protected", 1325, 51, 350, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_PASSWD_GAME].button},
2779 {"Allow Rank", 1326, 51, 426, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_SET_GAME].button},
2780 {"Above", 1327, 335, 465, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_ABOVE].button},
2781 {"Below", 1328, 335, 505, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_BELOW].button},
2782 {"Help", 928, 817, 706, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_HELP].button},
2783 {"Options", 1036, 797, 743, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_OPTIONS].button},
2784 {"Accept", 1035, 921, 664, UI_XSTR_COLOR_PINK, -1, &Multi_sg_buttons[1][MSG_ACCEPT].button},
2785 {"Start Game", 1329, 42, 22, UI_XSTR_COLOR_GREEN, -1, NULL},
2786 {"Title", 1330, 42, 50, UI_XSTR_COLOR_GREEN, -1, NULL},
2787 {"Game Type", 1331, 20, 264, UI_XSTR_COLOR_GREEN, -1, NULL},
2792 // starting index for displaying ranks
2793 int Multi_sg_rank_start;
2794 int Multi_sg_rank_select;
2796 // netgame pointer to indirect through
2797 netgame_info *Multi_sg_netgame;
2799 // hold temporary values in this structure when on a standalone server
2800 netgame_info Multi_sg_netgame_temp;
2802 // forward declarations
2803 void multi_sg_check_buttons();
2804 void multi_sg_button_pressed(int n);
2805 void multi_sg_init_gamenet();
2806 void multi_sg_draw_radio_buttons();
2807 void multi_sg_rank_scroll_up();
2808 void multi_sg_rank_scroll_down();
2809 void multi_sg_rank_display_stuff();
2810 void multi_sg_rank_process_select();
2811 void multi_sg_rank_build_name(char *in, char *out, const int max_outlen);
2812 void multi_sg_check_passwd();
2813 void multi_sg_check_name();
2814 void multi_sg_release_passwd();
2815 int multi_sg_rank_select_valid(int rank);
2816 void multi_sg_select_rank_default();
2818 // function which takes a rank name and returns the index. Useful for commandline options
2819 // for above and below rank. We return the index of the rank in the Ranks[] array. If
2820 // the rank isn't found, we return -1
2821 int multi_start_game_rank_from_name( char *rank ) {
2825 for ( i = 0; i <= MAX_FREESPACE1_RANK; i++ ) {
2827 for ( i = 0; i <= MAX_FREESPACE2_RANK; i++ ) {
2829 if ( !SDL_strcasecmp(Ranks[i].name, rank) ) {
2837 void multi_start_game_init()
2841 // initialize the gamenet
2842 multi_sg_init_gamenet();
2844 // create the interface window
2845 Multi_sg_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
2846 Multi_sg_window.set_mask_bmap(Multi_sg_bitmap_mask_fname[gr_screen.res]);
2848 // load the background bitmap
2849 Multi_sg_bitmap = bm_load(Multi_sg_bitmap_fname[gr_screen.res]);
2850 if(Multi_sg_bitmap < 0){
2851 // we failed to load the bitmap - this is very bad
2855 // initialize the common notification messaging
2856 multi_common_notify_init();
2858 // initialize the common text area
2859 multi_common_set_text("");
2861 // use the common interface palette
2862 multi_common_set_palette();
2864 // create the interface buttons
2865 for(idx=0; idx<MULTI_SG_NUM_BUTTONS; idx++){
2866 // create the object
2867 Multi_sg_buttons[gr_screen.res][idx].button.create(&Multi_sg_window, "", Multi_sg_buttons[gr_screen.res][idx].x, Multi_sg_buttons[gr_screen.res][idx].y, 1, 1, 0, 1);
2869 // set the sound to play when highlighted
2870 Multi_sg_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
2872 // set the ani for the button
2873 Multi_sg_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sg_buttons[gr_screen.res][idx].filename);
2876 Multi_sg_buttons[gr_screen.res][idx].button.link_hotspot(Multi_sg_buttons[gr_screen.res][idx].hotspot);
2881 for(idx=0; idx<MULTI_SG_NUM_TEXT; idx++){
2882 Multi_sg_window.add_XSTR(&Multi_sg_text[gr_screen.res][idx]);
2886 // load the help overlay
2887 help_overlay_load(MULTI_START_OVERLAY);
2888 help_overlay_set_state(MULTI_START_OVERLAY,0);
2890 // intiialize the rank selection items
2891 multi_sg_select_rank_default();
2892 Multi_sg_rank_start = Multi_sg_rank_select;
2894 // create the rank select button
2895 Multi_sg_rank_button.create(&Multi_sg_window,"",Msg_rank_list_coords[gr_screen.res][MSG_X_COORD],Msg_rank_list_coords[gr_screen.res][MSG_Y_COORD],Msg_rank_list_coords[gr_screen.res][MSG_W_COORD],Msg_rank_list_coords[gr_screen.res][MSG_H_COORD],0,1);
2896 Multi_sg_rank_button.hide();
2898 // create the netgame name input box
2899 Multi_sg_game_name.create(&Multi_sg_window,Msg_title_coords[gr_screen.res][MSG_X_COORD],Msg_title_coords[gr_screen.res][MSG_Y_COORD],Msg_title_coords[gr_screen.res][MSG_W_COORD],MAX_GAMENAME_LEN,"",UI_INPUTBOX_FLAG_ESC_CLR | UI_INPUTBOX_FLAG_INVIS,-1,&Color_normal);
2901 // create the netgame password input box, and disable it by default
2902 Multi_sg_game_passwd.create(&Multi_sg_window,Msg_passwd_coords[gr_screen.res][MSG_X_COORD],Msg_passwd_coords[gr_screen.res][MSG_Y_COORD],Msg_passwd_coords[gr_screen.res][MSG_W_COORD],16,"",UI_INPUTBOX_FLAG_ESC_CLR | UI_INPUTBOX_FLAG_PASSWD | UI_INPUTBOX_FLAG_INVIS,-1,&Color_normal);
2903 Multi_sg_game_passwd.hide();
2904 Multi_sg_game_passwd.disable();
2906 // set the netgame text to this gadget and make it have focus
2907 Multi_sg_game_name.set_text(Multi_sg_netgame->name);
2908 Multi_sg_game_name.set_focus();
2910 // if starting a netgame, set the name of the game and any other options that are appropriate
2911 if ( Cmdline_start_netgame ) {
2912 if ( Cmdline_game_name != NULL ) {
2913 SDL_strlcpy( Multi_sg_netgame->name, Cmdline_game_name, SDL_arraysize(Multi_sg_netgame->name) );
2914 Multi_sg_game_name.set_text(Multi_sg_netgame->name);
2917 // deal with the different game types -- only one should even be active, so we will just go down
2918 // the line. Last one wins.
2919 if ( Cmdline_closed_game ) {
2920 Multi_sg_netgame->mode = NG_MODE_CLOSED;
2921 } else if ( Cmdline_restricted_game ) {
2922 Multi_sg_netgame->mode = NG_MODE_RESTRICTED;
2923 } else if ( Cmdline_game_password != NULL ) {
2924 Multi_sg_netgame->mode = NG_MODE_PASSWORD;
2925 SDL_strlcpy(Multi_sg_netgame->passwd, Cmdline_game_password, SDL_arraysize(Multi_sg_netgame->passwd));
2926 Multi_sg_game_passwd.set_text(Multi_sg_netgame->passwd);
2929 // deal with rank above and rank below
2930 if ( (Cmdline_rank_above != NULL) || (Cmdline_rank_below != NULL) ) {
2934 if ( Cmdline_rank_above != NULL ) {
2935 rank_str = Cmdline_rank_above;
2937 rank_str = Cmdline_rank_below;
2940 // try and get the rank index from the name -- if found, then set the rank base
2941 // and the game type. apparently we only support either above or below, not both
2942 // together, so I make a random choice
2943 rank = multi_start_game_rank_from_name( rank_str );
2945 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
2947 // now an arbitrary decision
2948 if ( Cmdline_rank_above != NULL ) {
2949 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
2951 Multi_sg_netgame->mode = NG_MODE_RANK_BELOW;
2956 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
2959 if ( multi_fs_tracker_inited() ) {
2960 multi_fs_tracker_login_freespace();
2964 void multi_start_game_do()
2966 // return here since we will be moving to the next stage anyway -- I don't want to see the backgrounds of
2967 // all the screens for < 1 second for every screen we automatically move to.
2968 if ( Cmdline_start_netgame ) {
2972 int k = Multi_sg_window.process();
2974 // process any keypresses
2977 if(help_overlay_active(MULTI_START_OVERLAY)){
2978 help_overlay_set_state(MULTI_START_OVERLAY,0);
2980 gamesnd_play_iface(SND_USER_SELECT);
2981 multi_quit_game(PROMPT_NONE);
2986 case SDLK_LCTRL + SDLK_RETURN :
2987 case SDLK_RCTRL + SDLK_RETURN :
2988 gamesnd_play_iface(SND_COMMIT_PRESSED);
2989 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
2993 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
2994 help_overlay_set_state(MULTI_START_OVERLAY, 0);
2997 // check to see if the user has selected a different rank
2998 multi_sg_rank_process_select();
3000 // check any button presses
3001 multi_sg_check_buttons();
3003 // check to see if any of the input boxes have changed, and update the appropriate Netgame fields if necessary
3004 multi_sg_check_passwd();
3005 multi_sg_check_name();
3007 // draw the background, etc
3009 GR_MAYBE_CLEAR_RES(Multi_sg_bitmap);
3010 if(Multi_sg_bitmap != -1){
3011 gr_set_bitmap(Multi_sg_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
3014 Multi_sg_window.draw();
3016 // display rank stuff
3017 multi_sg_rank_display_stuff();
3019 // display any pending notification messages
3020 multi_common_notify_do();
3022 // draw all radio button
3023 multi_sg_draw_radio_buttons();
3025 // draw the help overlay
3026 help_overlay_maybe_blit(MULTI_START_OVERLAY);
3032 void multi_start_game_close()
3034 // if i'm the host on a standalone server, send him my start game options (passwd, mode, etc)
3035 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
3036 multi_options_update_start_game(Multi_sg_netgame);
3039 // unload any bitmaps
3040 if(!bm_unload(Multi_sg_bitmap)){
3041 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_sg_bitmap_fname[gr_screen.res]));
3044 // unload the help overlay
3045 help_overlay_unload(MULTI_START_OVERLAY);
3047 // destroy the UI_WINDOW
3048 Multi_sg_window.destroy();
3051 void multi_sg_check_buttons()
3054 for(idx=0;idx<MULTI_SG_NUM_BUTTONS;idx++){
3055 // we only really need to check for one button pressed at a time, so we can break after
3057 if(Multi_sg_buttons[gr_screen.res][idx].button.pressed()){
3058 multi_sg_button_pressed(idx);
3064 void multi_sg_button_pressed(int n)
3067 // go to the options screen
3069 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
3074 if(!help_overlay_active(MULTI_START_OVERLAY)){
3075 help_overlay_set_state(MULTI_START_OVERLAY,1);
3077 help_overlay_set_state(MULTI_START_OVERLAY,0);
3081 // the open button was pressed
3083 // if the closed option is selected
3084 if(Multi_sg_netgame->mode != NG_MODE_OPEN){
3085 Multi_sg_netgame->mode = NG_MODE_OPEN;
3087 gamesnd_play_iface(SND_USER_SELECT);
3089 // release the password control if necessary
3090 multi_sg_release_passwd();
3092 // if its already selected
3094 gamesnd_play_iface(SND_GENERAL_FAIL);
3099 // the open button was pressed
3100 case MSG_CLOSED_GAME:
3101 // if the closed option is selected
3102 if(Multi_sg_netgame->mode != NG_MODE_CLOSED){
3103 Multi_sg_netgame->mode = NG_MODE_CLOSED;
3105 gamesnd_play_iface(SND_USER_SELECT);
3107 // release the password control if necessary
3108 multi_sg_release_passwd();
3110 // if its already selected
3112 gamesnd_play_iface(SND_GENERAL_FAIL);
3117 // toggle password protection
3118 case MSG_PASSWD_GAME:
3119 // if we selected it
3120 if(Multi_sg_netgame->mode != NG_MODE_PASSWORD){
3121 gamesnd_play_iface(SND_USER_SELECT);
3123 Multi_sg_game_passwd.enable();
3124 Multi_sg_game_passwd.unhide();
3125 Multi_sg_game_passwd.set_focus();
3127 Multi_sg_netgame->mode = NG_MODE_PASSWORD;
3129 // copy in the current network password
3130 Multi_sg_game_passwd.set_text(Multi_sg_netgame->passwd);
3132 gamesnd_play_iface(SND_GENERAL_FAIL);
3137 // toggle "restricted" on or off
3138 case MSG_RESTRICTED_GAME:
3139 if(Multi_sg_netgame->mode != NG_MODE_RESTRICTED){
3140 gamesnd_play_iface(SND_USER_SELECT);
3141 Multi_sg_netgame->mode = NG_MODE_RESTRICTED;
3143 // release the password control if necessary
3144 multi_sg_release_passwd();
3146 gamesnd_play_iface(SND_GENERAL_FAIL);
3151 // turn off all rank requirements
3152 case MSG_RANK_SET_GAME:
3153 // if either is set, then turn then both off
3154 if((Multi_sg_netgame->mode != NG_MODE_RANK_BELOW) && (Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE)){
3155 gamesnd_play_iface(SND_USER_SELECT);
3157 // set it to the default case if we're turning it off
3158 multi_sg_select_rank_default();
3159 Multi_sg_rank_start = Multi_sg_rank_select;
3161 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
3163 // release the password control if necessary
3164 multi_sg_release_passwd();
3166 gamesnd_play_iface(SND_GENERAL_FAIL);
3170 // rank above was pressed
3171 case MSG_RANK_ABOVE :
3172 if((Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE) || (Multi_sg_netgame->mode == NG_MODE_RANK_BELOW)){
3173 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
3175 // select the first item
3176 multi_sg_select_rank_default();
3177 Multi_sg_rank_start = Multi_sg_rank_select;
3180 gamesnd_play_iface(SND_USER_SELECT);
3182 gamesnd_play_iface(SND_GENERAL_FAIL);
3186 // rank below was pressed
3187 case MSG_RANK_BELOW :
3188 if((Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE) || (Multi_sg_netgame->mode == NG_MODE_RANK_BELOW)){
3189 Multi_sg_netgame->mode = NG_MODE_RANK_BELOW;
3191 // select the first item
3192 multi_sg_select_rank_default();
3193 Multi_sg_rank_start = Multi_sg_rank_select;
3196 gamesnd_play_iface(SND_USER_SELECT);
3198 gamesnd_play_iface(SND_GENERAL_FAIL);
3202 // scroll the rank list up
3203 case MSG_RANK_SCROLL_UP:
3204 multi_sg_rank_scroll_up();
3207 // scroll the rank list down
3208 case MSG_RANK_SCROLL_DOWN:
3209 multi_sg_rank_scroll_down();
3212 // move to the create game screen
3214 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
3215 gamesnd_play_iface(SND_COMMIT_PRESSED);
3219 gamesnd_play_iface(SND_GENERAL_FAIL);
3220 multi_common_add_notify(XSTR("Not implemented yet!",760));
3225 // NOTE : this is where all Netgame initialization should take place on the host
3226 void multi_sg_init_gamenet()
3228 char buf[128],out_name[128];
3230 net_player *server_save;
3232 // back this data up in case we are already connected to a standalone
3233 memcpy(&save,&Netgame.server_addr,sizeof(net_addr));
3234 server_save = Netgame.server;
3236 // remove campaign flags
3237 Game_mode &= ~(GM_CAMPAIGN_MODE);
3239 // clear out the Netgame structure and start filling in the values
3240 memset( &Netgame, 0, sizeof(Netgame) );
3241 memset( &Multi_sg_netgame_temp, 0, sizeof(netgame_info) );
3243 // if we're on the standalone, we're not the server, so we don't care about setting the netgame state
3244 if(Net_player->state != NETPLAYER_STATE_STD_HOST_SETUP){
3245 Netgame.game_state = NETGAME_STATE_HOST_SETUP;
3246 Multi_sg_netgame = &Netgame;
3249 ml_string(NOX("Starting netgame as Host/Server"));
3251 Multi_sg_netgame = &Multi_sg_netgame_temp;
3255 memcpy(&temp_addr.s_addr, &Netgame.server_addr, 4);
3256 char *server_addr = inet_ntoa(temp_addr);
3257 ml_printf(NOX("Starting netgame as Host on Standalone server : %s"), (server_addr == NULL) ? NOX("Unknown") : server_addr);
3260 Net_player->tracker_player_id = Multi_tracker_id;
3262 Multi_sg_netgame->security = (rand() % 32766) + 1; // get some random security number
3263 Multi_sg_netgame->mode = NG_MODE_OPEN;
3264 Multi_sg_netgame->rank_base = RANK_ENSIGN;
3265 if(Multi_sg_netgame->security < 16){
3266 Multi_sg_netgame->security += 16;
3269 // set the version_info field
3270 Multi_sg_netgame->version_info = NG_VERSION_ID;
3272 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
3273 Netgame.host = Net_player;
3276 // set the default netgame flags
3277 Multi_sg_netgame->flags = 0;
3279 // intialize endgame stuff
3280 multi_endgame_init();
3282 // load in my netgame options
3283 multi_options_netgame_load(&Netgame.options);
3285 // load my local netplayer options
3286 multi_options_local_load(&Net_player->p_info.options, Net_player);
3288 // setup the default game name, taking care of string length and player callsigns
3289 memset(out_name,0,128);
3291 pilot_format_callsign_personal(Player->callsign, out_name, SDL_arraysize(out_name));
3292 SDL_snprintf(buf, SDL_arraysize(buf), XSTR("%s game",782), out_name); // [[ %s will be a pilot's name ]]
3293 if ( strlen(buf) > MAX_GAMENAME_LEN ){
3294 SDL_strlcpy(buf, XSTR("Temporary name",783), SDL_arraysize(buf));
3296 SDL_strlcpy(Multi_sg_netgame->name, buf, SDL_arraysize(Multi_sg_netgame->name));
3298 // set the default qos and duration
3299 multi_voice_maybe_update_vars(Netgame.options.voice_qos,Netgame.options.voice_record_time);
3301 // make sure to set the server correctly (me or the standalone)
3302 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3303 memcpy(&Netgame.server_addr, &Psnet_my_addr,sizeof(net_addr));
3304 Netgame.server = Net_player;
3305 Net_player->player_id = multi_get_new_id();
3307 // setup debug flags
3308 Netgame.debug_flags = 0;
3310 if(!Cmdline_server_firing){
3311 Netgame.debug_flags |= NETD_FLAG_CLIENT_FIRING;
3313 if(!Cmdline_client_dodamage){
3314 Netgame.debug_flags |= NETD_FLAG_CLIENT_NODAMAGE;
3318 memcpy(&Netgame.server_addr,&save,sizeof(net_addr));
3319 Netgame.server = server_save;
3322 // if I have a cd or not
3324 Net_player->flags |= NETINFO_FLAG_HAS_CD;
3327 // if I have hacked data
3328 if(game_hacked_data()){
3329 Net_player->flags |= NETINFO_FLAG_HAXOR;
3332 // assign my player struct and other data
3333 Net_player->flags |= (NETINFO_FLAG_CONNECTED | NETINFO_FLAG_DO_NETWORKING);
3334 Net_player->s_info.voice_token_timestamp = -1;
3336 // if we're supposed to flush our cache directory, do so now
3337 if(Net_player->p_info.options.flags & MLO_FLAG_FLUSH_CACHE){
3338 multi_flush_multidata_cache();
3341 ml_string(NOX("Flushing multi-data cache"));
3347 void multi_sg_draw_radio_buttons()
3349 // draw the appropriate radio button
3350 switch(Multi_sg_netgame->mode){
3352 Multi_sg_buttons[gr_screen.res][MSG_OPEN_GAME].button.draw_forced(2);
3356 case NG_MODE_CLOSED:
3357 Multi_sg_buttons[gr_screen.res][MSG_CLOSED_GAME].button.draw_forced(2);
3361 case NG_MODE_PASSWORD:
3362 Multi_sg_buttons[gr_screen.res][MSG_PASSWD_GAME].button.draw_forced(2);
3366 case NG_MODE_RESTRICTED:
3367 Multi_sg_buttons[gr_screen.res][MSG_RESTRICTED_GAME].button.draw_forced(2);
3371 case NG_MODE_RANK_ABOVE:
3372 Multi_sg_buttons[gr_screen.res][MSG_RANK_SET_GAME].button.draw_forced(2);
3373 Multi_sg_buttons[gr_screen.res][MSG_RANK_ABOVE].button.draw_forced(2);
3375 case NG_MODE_RANK_BELOW:
3376 Multi_sg_buttons[gr_screen.res][MSG_RANK_SET_GAME].button.draw_forced(2);
3377 Multi_sg_buttons[gr_screen.res][MSG_RANK_BELOW].button.draw_forced(2);
3382 void multi_sg_rank_scroll_up()
3384 // if he doesn't have either of the rank flags set, then ignore this
3385 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3389 if(Multi_sg_rank_start > 0){
3390 Multi_sg_rank_start--;
3391 gamesnd_play_iface(SND_SCROLL);
3393 gamesnd_play_iface(SND_GENERAL_FAIL);
3397 void multi_sg_rank_scroll_down()
3399 // if he doesn't have either of the rank flags set, then ignore this
3400 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3404 if((NUM_RANKS - Multi_sg_rank_start) > Multi_sg_rank_max_display[gr_screen.res]){
3405 Multi_sg_rank_start++;
3406 gamesnd_play_iface(SND_SCROLL);
3408 gamesnd_play_iface(SND_GENERAL_FAIL);
3412 void multi_sg_rank_display_stuff()
3417 // if he doesn't have either of the rank flags set, then ignore this
3418 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3422 // display the list of ranks
3423 y = Msg_rank_list_coords[gr_screen.res][MSG_Y_COORD];
3424 idx = Multi_sg_rank_start;
3426 while((count < NUM_RANKS) && (count < Multi_sg_rank_max_display[gr_screen.res]) && (idx < NUM_RANKS)){
3427 // if its the selected item, then color it differently
3428 if(idx == Multi_sg_rank_select){
3429 gr_set_color_fast(&Color_text_selected);
3431 gr_set_color_fast(&Color_text_normal);
3435 multi_sg_rank_build_name(Ranks[idx].name, rank_name, sizeof(rank_name));
3436 gr_string(Msg_rank_list_coords[gr_screen.res][MSG_X_COORD],y,rank_name);
3444 // display the selected rank
3446 gr_set_color_fast(&Color_bright);
3447 multi_sg_rank_build_name(Ranks[Multi_sg_netgame->rank_base].name, rank_name, SDL_arraysize(rank_name));
3448 gr_string(Msg_rank_sel_coords[gr_screen.res][MSG_X_COORD],Msg_rank_sel_coords[gr_screen.res][MSG_Y_COORD],rank_name);
3452 void multi_sg_rank_process_select()
3456 // if he doesn't have either of the rank flags set, then ignore this
3457 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3461 // see if he's clicked on an item on the rank list
3462 if(Multi_sg_rank_button.pressed()){
3464 Multi_sg_rank_button.get_mouse_pos(NULL,&y);
3467 if(item + Multi_sg_rank_start < NUM_RANKS){
3468 // evaluate whether this rank is valid for the guy to pick
3469 if(multi_sg_rank_select_valid(item + Multi_sg_rank_start)){
3470 gamesnd_play_iface(SND_USER_SELECT);
3472 Multi_sg_rank_select = item + Multi_sg_rank_start;
3474 // set the Netgame rank
3475 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
3477 gamesnd_play_iface(SND_GENERAL_FAIL);
3479 memset(string,0,255);
3480 SDL_snprintf(string,SDL_arraysize(string),XSTR("Illegal value for a host of your rank (%s)\n",784),Ranks[Net_player->player->stats.rank].name);
3481 multi_common_add_notify(string);
3487 void multi_sg_rank_build_name(char *in, char *out, const int max_outlen)
3492 SDL_strlcpy(use, in, SDL_arraysize(use));
3493 first = strtok(use," ");
3495 // just copy the string
3497 SDL_strlcpy(out, in, max_outlen);
3500 // if the first part of the string is lieutenant, then abbreivate it and tack on the rest of the string
3501 if (SDL_strcasecmp(first,XSTR("lieutenant",785)) == 0) {
3502 first = strtok(NULL, NOX("\n"));
3504 // if he's not just a plain lieutenant
3506 SDL_snprintf(out, max_outlen, "%s%s", XSTR("Lt. ",786), first); // [[ lieutenant ]]
3508 // if he _is_ just a plain lieutenant
3510 SDL_strlcpy(out, in, max_outlen);
3513 SDL_strlcpy(out, in, max_outlen);
3517 void multi_sg_check_passwd()
3519 // check to see if the password input box has been pressed
3520 if(Multi_sg_game_passwd.changed()){
3521 Multi_sg_game_passwd.get_text(Multi_sg_netgame->passwd);
3525 void multi_sg_check_name()
3527 // check to see if the game name input box has been pressed
3528 if(Multi_sg_game_name.changed()){
3529 Multi_sg_game_name.get_text(Multi_sg_netgame->name);
3533 void multi_sg_release_passwd()
3535 // hide and disable the password input box
3536 Multi_sg_game_passwd.hide();
3537 Multi_sg_game_passwd.disable();
3539 // set the focus back to the name input box
3540 Multi_sg_game_name.set_focus();
3543 int multi_sg_rank_select_valid(int rank)
3546 if(Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE){
3547 if(Net_player->player->stats.rank >= rank){
3553 if(Net_player->player->stats.rank <= rank){
3561 void multi_sg_select_rank_default()
3563 // pick our rank for now
3564 Multi_sg_rank_select = Net_player->player->stats.rank;
3566 // set the Netgame rank
3567 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
3570 // -------------------------------------------------------------------------------------------------
3572 // MULTIPLAYER CREATE GAME screen
3577 const char *Multi_create_bitmap_fname[GR_NUM_RESOLUTIONS] = {
3578 "MultiCreate", // GR_640
3579 "2_MultiCreate" // GR_1024
3582 const char *Multi_create_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
3583 "MultiCreate-M", // GR_640
3584 "2_MultiCreate-M" // GR_1024
3587 const char *Multi_create_loading_fname[GR_NUM_RESOLUTIONS] = {
3588 "PleaseWait", // GR_640
3589 "2_PleaseWait" // GR_1024
3593 #define MULTI_CREATE_NUM_BUTTONS 23
3596 #define MC_SHOW_ALL 0
3597 #define MC_SHOW_COOP 1
3598 #define MC_SHOW_TEAM 2
3599 #define MC_SHOW_DOGFIGHT 3
3600 #define MC_PXO_REFRESH 4
3601 #define MC_PILOT_INFO 5
3602 #define MC_SCROLL_LIST_UP 6
3603 #define MC_SCROLL_LIST_DOWN 7
3604 #define MC_SCROLL_PLAYERS_UP 8
3605 #define MC_SCROLL_PLAYERS_DOWN 9
3606 #define MC_MISSION_FILTER 10
3607 #define MC_CAMPAIGN_FILTER 11
3608 #define MC_CANCEL 12
3613 #define MC_SCROLL_INFO_UP 17
3614 #define MC_SCROLL_INFO_DOWN 18
3615 #define MC_HOST_OPTIONS 19
3617 #define MC_OPTIONS 21
3618 #define MC_ACCEPT 22
3621 UI_WINDOW Multi_create_window; // the window object for the create screen
3622 UI_BUTTON Multi_create_player_select_button; // for selecting players
3623 UI_BUTTON Multi_create_list_select_button; // for selecting missions/campaigns
3624 int Multi_create_bitmap; // the background bitmap
3625 UI_SLIDER2 Multi_create_slider; // for create list
3627 // constants for coordinate look ups
3628 #define MC_X_COORD 0
3629 #define MC_Y_COORD 1
3630 #define MC_W_COORD 2
3631 #define MC_H_COORD 3
3633 ui_button_info Multi_create_buttons[GR_NUM_RESOLUTIONS][MULTI_CREATE_NUM_BUTTONS] = {
3636 ui_button_info("MC_18", 34, 131, -1, -1, 18), // all
3637 ui_button_info("MC_19", 72, 131, -1, -1, 19), // coop
3638 ui_button_info("MC_20", 120, 131, -1, -1, 20), // team
3639 // ui_button_info("MC_21", 166, 131, -1, -1, 21), // dogfight
3640 ui_button_info("none", -1, -1, -1, -1, -1), // dogfight (not used)
3641 ui_button_info("none", -1, -1, -1, -1, -1), // pxo?
3642 ui_button_info("MC_26", 540, 114, -1, -1, 26), // pilot info
3643 ui_button_info("MC_03", 0, 187, -1, -1, 2), // scroll list up
3644 ui_button_info("MC_02", 0, 227, -1, -1, 3), // scroll list down
3645 ui_button_info("MC_04", 611, 182, -1, -1, 4), // scroll players up
3646 ui_button_info("MC_05", 611, 221, -1, -1, 5), // scroll players down
3647 ui_button_info("MC_06", 18, 322, -1, -1, 6), // mission filter
3648 ui_button_info("MC_07", 18, 344, -1, -1, 7), // campaign filter
3649 ui_button_info("MC_10", 317, 339, -1, -1, 10), // cancel
3650 ui_button_info("MC_14", 464, 350, -1, -1, 14), // team 1
3651 ui_button_info("MC_15", 498, 350, -1, -1, 15), // team 2
3652 ui_button_info("MC_16", 527, 346, -1, -1, 16), // kick
3653 ui_button_info("MC_17", 572, 346, -1, -1, 17), // close
3654 ui_button_info("MC_08", 0, 398, -1, -1, 8), // scroll mission info up
3655 ui_button_info("MC_09", 0, 435, -1, -1, 9), // scroll mission info down
3656 ui_button_info("MC_27", 447, 402, -1, -1, 27), // host options
3657 ui_button_info("MC_11", 510, 428, -1, -1, 11), // help
3658 ui_button_info("MC_12", 510, 453, -1, -1, 12), // options
3659 ui_button_info("Mc_13", 562, 412, -1, -1, 13), // commit
3661 ui_button_info("MC_00", 32, 129, 36, 158, 0), // show all missions
3662 ui_button_info("MC_01", 76, 129, 71, 158, 1), // show coop missions
3663 ui_button_info("MC_02", 121, 129, 119, 158, 2), // show team missions
3664 ui_button_info("MC_03", 164, 129, 166, 158, 3), // show dogfight missions
3665 ui_button_info("MC_04", 399, 129, 229, 130, 4), // pxo mission refresh
3666 ui_button_info("MC_05", 567, 123, 467, 132, 5), // pilot info
3667 ui_button_info("MC_06", 1, 161, -1, -1, 6), // scroll mission info up
3668 ui_button_info("MC_08", 1, 304, -1, -1, 8), // scroll mission info down
3669 ui_button_info("MC_09", 613, 160, -1, -1, 9), // scroll players up
3670 ui_button_info("MC_10", 613, 202, -1, -1, 10), // scroll players down
3671 ui_button_info("MC_11", 22, 346, 27, 376, 11), // mission filter
3672 ui_button_info("MC_12", 104, 346, 110, 376, 12), // campaign filter
3673 ui_button_info("MC_13", 392, 341, 328, 364, 13), // cancel
3674 ui_button_info("MC_14", 472, 352, 482, 381, 14), // team 0
3675 ui_button_info("MC_15", 506, 352, 514, 381, 15), // team 1
3676 ui_button_info("MC_16", 539, 346, 539, 381, 16), // kick
3677 ui_button_info("MC_17", 589, 346, 582, 381, 17), // close
3678 ui_button_info("MC_18", 1, 406, -1, -1, 18), // scroll list up
3679 ui_button_info("MC_19", 1, 447, -1, -1, 19), // scroll list down
3680 ui_button_info("MC_20", 499, 434, 436, 423, 20), // host options
3681 ui_button_info("MC_21", 534, 426, -1, -1, 21), // help
3682 ui_button_info("MC_22", 534, 452, -1, -1, 22), // options
3683 ui_button_info("MC_23", 571, 426, 572, 413, 23), // commit
3687 ui_button_info("2_MC_00", 51, 207, 61, 253, 0), // show all missions
3688 ui_button_info("2_MC_01", 122, 207, 124, 253, 1), // show coop missions
3689 ui_button_info("2_MC_02", 193, 207, 194, 253, 2), // show team missions
3690 ui_button_info("2_MC_03", 263, 207, 261, 253, 3), // show dogfight missions
3691 ui_button_info("2_MC_04", 639, 207, 479, 218, 4), // pxo mission refresh
3692 ui_button_info("2_MC_05", 907, 197, 748, 216, 5), // pilot info
3693 ui_button_info("2_MC_06", 1, 258, -1, -1, 6), // scroll mission info up
3694 ui_button_info("2_MC_08", 1, 487, -1, -1, 8), // scroll mission info down
3695 ui_button_info("2_MC_09", 981, 256, -1, -1, 9), // scroll players up
3696 ui_button_info("2_MC_10", 981, 323, -1, -1, 10), // scroll players down
3697 ui_button_info("2_MC_11", 35, 554, 46, 601, 11), // mission filter
3698 ui_button_info("2_MC_12", 166, 554, 174, 601, 12), // campaign filter
3699 ui_button_info("2_MC_13", 628, 545, 559, 582, 13), // cancel
3700 ui_button_info("2_MC_14", 756, 564, 772, 610, 14), // team 0
3701 ui_button_info("2_MC_15", 810, 564, 826, 610, 15), // team 1
3702 ui_button_info("2_MC_16", 862, 554, 872, 610, 16), // kick
3703 ui_button_info("2_MC_17", 943, 554, 949, 610, 17), // close
3704 ui_button_info("2_MC_18", 1, 649, -1, -1, 18), // scroll list up
3705 ui_button_info("2_MC_19", 1, 716, -1, -1, 19), // scroll list down
3706 ui_button_info("2_MC_20", 798, 695, 726, 667, 20), // host options
3707 ui_button_info("2_MC_21", 854, 681, -1, -1, 21), // help
3708 ui_button_info("2_MC_22", 854, 724, -1, -1, 22), // options
3709 ui_button_info("2_MC_23", 914, 681, 932, 667, 23), // commit
3714 #define MULTI_CREATE_NUM_TEXT 0
3716 #define MULTI_CREATE_NUM_TEXT 15
3718 UI_XSTR Multi_create_text[GR_NUM_RESOLUTIONS][MULTI_CREATE_NUM_BUTTONS] = {
3720 // not needed for FS1
3722 {"All", 1256, 36, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_ALL].button},
3723 {"Coop", 1257, 71, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_COOP].button},
3724 {"Team", 1258, 119, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_TEAM].button},
3725 {"Dogfight", 1259, 166, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_DOGFIGHT].button},
3726 {"Refresh Missions", 1260, 229, 130, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_PXO_REFRESH].button},
3727 {"Pilot Info", 1261, 467, 132, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_PILOT_INFO].button},
3728 {"Missions", 1262, 27, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_MISSION_FILTER].button},
3729 {"Campaigns", 1263, 110, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_CAMPAIGN_FILTER].button},
3730 {"Cancel", 387, 328, 364, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_CANCEL].button},
3731 {"1", 1264, 482, 381, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_TEAM0].button},
3732 {"2", 1265, 514, 381, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_TEAM1].button},
3733 {"Kick", 1266, 539, 381, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_KICK].button},
3734 {"Close", 1508, 582, 381, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_CLOSE].button},
3735 {"Host Options", 1267, 436, 423, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_HOST_OPTIONS].button},
3736 {"Commit", 1062, 572, 413, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_ACCEPT].button}
3740 // not needed for FS1
3742 {"All", 1256, 61, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_ALL].button},
3743 {"Coop", 1257, 124, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_COOP].button},
3744 {"Team", 1258, 194, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_TEAM].button},
3745 {"Dogfight", 1259, 261, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_DOGFIGHT].button},
3746 {"Refresh Missions", 1260, 501, 218, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_PXO_REFRESH].button},
3747 {"Pilot Info", 1261, 814, 216, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_PILOT_INFO].button},
3748 {"Missions", 1262, 46, 601, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_MISSION_FILTER].button},
3749 {"Campaigns", 1263, 174, 601, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_CAMPAIGN_FILTER].button},
3750 {"Cancel", 387, 559, 582, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_CANCEL].button},
3751 {"1", 1264, 772, 610, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_TEAM0].button},
3752 {"2", 1265, 826, 610, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_TEAM1].button},
3753 {"Kick", 1266, 872, 610, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_KICK].button},
3754 {"Close", 1508, 949, 610, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_CLOSE].button},
3755 {"Host Options", 1267, 755, 683, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_HOST_OPTIONS].button},
3756 {"Commit", 1062, 932, 667, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_ACCEPT].button}
3762 // squad war checkbox
3763 UI_CHECKBOX Multi_create_sw_checkbox;
3764 const char *Multi_create_sw_checkbox_fname[GR_NUM_RESOLUTIONS] = {
3768 int Multi_create_sw_checkbox_coords[GR_NUM_RESOLUTIONS][2] = {
3776 int Multi_create_sw_checkbox_text[GR_NUM_RESOLUTIONS][2] = {
3786 // game information text areas
3787 int Mc_list_coords[GR_NUM_RESOLUTIONS][4] = {
3800 int Mc_players_coords[GR_NUM_RESOLUTIONS][4] = {
3813 int Mc_info_coords[GR_NUM_RESOLUTIONS][4] = {
3826 // mission icon stuff
3827 int Mc_icon_type_coords[GR_NUM_RESOLUTIONS][2] = {
3829 38, -2 // y is an offset
3832 61, -2 // y is an offset
3836 int Mc_icon_volition_coords[GR_NUM_RESOLUTIONS][2] = {
3838 61, -1 // y is an offset
3841 98, 1 // y is an offset
3845 int Mc_icon_silent_coords[GR_NUM_RESOLUTIONS][2] = {
3847 72, 0 // y is an offset
3850 115, 0 // y is an offset
3854 int Mc_icon_valid_coords[GR_NUM_RESOLUTIONS][2] = {
3856 91, 0 // y is an offset
3859 146, 0 // y is an offset
3863 // mission/campaign list column areas
3864 int Mc_column1_w[GR_NUM_RESOLUTIONS] = {
3869 int Mc_column2_w[GR_NUM_RESOLUTIONS] = {
3874 int Mc_column3_w[GR_NUM_RESOLUTIONS] = {
3879 int Mc_mission_name_x[GR_NUM_RESOLUTIONS] = {
3884 int Mc_mission_count_x[GR_NUM_RESOLUTIONS] = {
3889 int Mc_mission_fname_x[GR_NUM_RESOLUTIONS] = {
3894 int Mc_create_game_text[GR_NUM_RESOLUTIONS][2] = {
3895 {13, 116}, // GR_640
3896 {21, 186} // GR_1024
3899 int Mc_players_text[GR_NUM_RESOLUTIONS][2] = {
3900 {467, 150}, // GR_640
3901 {747, 240} // GR_1024
3904 int Mc_team_text[GR_NUM_RESOLUTIONS][2] = {
3905 {484, 342}, // GR_640
3906 {774, 547} // GR_1024
3909 int Mc_slider_coords[GR_NUM_RESOLUTIONS][4] = {
3911 3, 197, 13, 105 // GR_640
3914 5, 316, 20, 168 // GR_1024
3918 const char *Mc_slider_bitmap[GR_NUM_RESOLUTIONS] = {
3923 // player list control thingie defs
3924 #define MULTI_CREATE_PLIST_MAX_DISPLAY 20
3925 int Multi_create_plist_select_flag; // flag indicating if we have a play selected
3926 short Multi_create_plist_select_id; // the net address of the currently selected player (for lookup)
3928 // master tracker details
3929 int Multi_create_frame_count; // framecount
3930 int Multi_create_mt_tried_login; // attempted to login this server on the MT
3932 // mission filter settings
3933 int Multi_create_filter; // what mode we're in
3935 // game/campaign list control defs
3936 int Multi_create_list_max_display[GR_NUM_RESOLUTIONS] = {
3942 int Multi_create_list_count; // number of items in listbox
3943 int Multi_create_list_mode; // 0 == mission mode, 1 == campaign mode
3944 int Multi_create_list_start; // where to start displaying from
3945 int Multi_create_list_select; // which item is currently highlighted
3946 int Multi_create_files_loaded;
3948 char Multi_create_files_array[MULTI_CREATE_MAX_LIST_ITEMS][MAX_FILENAME_LEN];
3950 int Multi_create_mission_count; // how many we have
3951 int Multi_create_campaign_count;
3952 multi_create_info Multi_create_mission_list[MULTI_CREATE_MAX_LIST_ITEMS];
3953 multi_create_info Multi_create_campaign_list[MULTI_CREATE_MAX_LIST_ITEMS];
3955 // use a pointer for the file list. Will point to either the missions or the campaigns
3956 multi_create_info *Multi_create_file_list;
3958 // LOCAL function definitions
3959 void multi_create_check_buttons();
3960 void multi_create_button_pressed(int n);
3961 void multi_create_init_as_server();
3962 void multi_create_init_as_client();
3963 void multi_create_do_netstuff();
3964 void multi_create_plist_scroll_up();
3965 void multi_create_plist_scroll_down();
3966 void multi_create_plist_process();
3967 void multi_create_plist_blit_normal();
3968 void multi_create_plist_blit_team();
3969 void multi_create_list_scroll_up();
3970 void multi_create_list_scroll_down();
3971 void multi_create_list_do();
3972 void multi_create_list_select_item(int n);
3973 void multi_create_list_blit_icons(int list_index, int y_start);
3974 void multi_create_accept_hit();
3975 void multi_create_draw_filter_buttons();
3976 void multi_create_set_selected_team(int team);
3977 short multi_create_get_mouse_id();
3978 int multi_create_ok_to_commit();
3979 int multi_create_verify_cds();
3980 void multi_create_refresh_pxo();
3981 void multi_create_sw_clicked();
3983 // since we can selectively filter out mission/campaign types we always need to map a selected index (which is relative
3984 // to the displayed list), to an absolute index (which is relative to the total file list - some of which may filtered out)
3985 void multi_create_select_to_filename(int select_index, char *filename, const int max_filelen);
3986 int multi_create_select_to_index(int select_index);
3988 int Multi_create_should_show_popup = 0;
3991 // sorting function to sort mission lists.. Basic sorting on mission name
3992 int multi_create_sort_func(const void *a, const void *b)
3994 multi_create_info *m1, *m2;
3996 m1 = (multi_create_info *)a;
3997 m2 = (multi_create_info *)b;
3999 return ( strcmp(m1->name, m2->name) );
4002 void multi_create_setup_list_data(int mode)
4004 int idx,should_sort,switched_modes;
4006 // set the current mode
4009 if((Multi_create_list_mode != mode) && (mode != -1)){
4010 Multi_create_list_mode = mode;
4013 // set up the list pointers
4014 if ( Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ) {
4015 Multi_create_file_list = Multi_create_mission_list;
4016 } else if ( Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ) {
4017 Multi_create_file_list = Multi_create_campaign_list;
4023 // get the mission count based upon the filter selected
4024 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
4025 switch(Multi_create_filter){
4026 case MISSION_TYPE_MULTI:
4027 Multi_create_list_count = Multi_create_mission_count;
4030 Multi_create_list_count = 0;
4031 // find all missions which match
4032 for(idx=0;idx<Multi_create_mission_count;idx++){
4033 if(Multi_create_mission_list[idx].flags & Multi_create_filter){
4034 Multi_create_list_count++;
4038 // if we switched modes and we have more than 0 items, sort them
4039 if(switched_modes && (Multi_create_list_count > 0)){
4044 } else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
4045 switch(Multi_create_filter){
4046 case MISSION_TYPE_MULTI:
4047 Multi_create_list_count = Multi_create_campaign_count;
4050 Multi_create_list_count = 0;
4051 // find all missions which match
4052 for(idx=0;idx<Multi_create_campaign_count;idx++){
4053 if(Multi_create_campaign_list[idx].flags & Multi_create_filter){
4054 Multi_create_list_count++;
4058 // if we switched modes and we have more than 0 items, sort them
4059 if(switched_modes && (Multi_create_list_count > 0)){
4066 // reset the list start and selected indices
4067 Multi_create_list_start = 0;
4068 Multi_create_list_select = -1;
4069 multi_create_list_select_item(Multi_create_list_start);
4071 // sort the list of missions if necessary
4073 qsort(Multi_create_file_list, Multi_create_list_count, sizeof(multi_create_info), multi_create_sort_func);
4078 Multi_create_slider.set_numberItems(Multi_create_list_count > Multi_create_list_max_display[gr_screen.res] ? Multi_create_list_count-Multi_create_list_max_display[gr_screen.res] : 0);
4082 void multi_create_game_init()
4087 // now make sure to initialze various netgame stuff based upon whether we're on a standalone or not
4088 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4089 multi_create_init_as_server();
4091 multi_create_init_as_client();
4094 // initialize the player list data
4095 Multi_create_plist_select_flag = 0;
4096 Multi_create_plist_select_id = -1;
4098 // create the interface window
4099 Multi_create_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
4100 Multi_create_window.set_mask_bmap(Multi_create_bitmap_mask_fname[gr_screen.res]);
4102 // load the background bitmap
4103 Multi_create_bitmap = bm_load(Multi_create_bitmap_fname[gr_screen.res]);
4104 if(Multi_create_bitmap < 0){
4105 // we failed to load the bitmap - this is very bad
4109 // close any previous existing instances of the chatbox and create a new one
4113 // load the help overlay
4114 help_overlay_load(MULTI_CREATE_OVERLAY);
4115 help_overlay_set_state(MULTI_CREATE_OVERLAY, 0);
4117 // initialize the common notification messaging
4118 multi_common_notify_init();
4120 // use the common interface palette
4121 multi_common_set_palette();
4123 // create the interface buttons
4124 for(idx=0; idx<MULTI_CREATE_NUM_BUTTONS; idx++){
4125 b = &Multi_create_buttons[gr_screen.res][idx];
4127 // create the object
4128 b->button.create(&Multi_create_window, "", b->x, b->y, 1, 1, ((idx == MC_SCROLL_LIST_UP) || (idx == MC_SCROLL_LIST_DOWN)), 1);
4130 // set the sound to play when highlighted
4131 b->button.set_highlight_action(common_play_highlight_sound);
4133 // set the ani for the button
4134 b->button.set_bmaps(b->filename);
4137 b->button.link_hotspot(b->hotspot);
4139 // some special case stuff for the pxo refresh button
4140 if(idx == MC_PXO_REFRESH){
4141 // if not a PXO game, or if I'm not a server disable and hide the button
4142 if(!MULTI_IS_TRACKER_GAME || !MULTIPLAYER_MASTER){
4144 b->button.disable();
4150 for(idx=0; idx<MULTI_CREATE_NUM_TEXT; idx++){
4151 Multi_create_window.add_XSTR(&Multi_create_text[gr_screen.res][idx]);
4155 // if this is a PXO game, enable the squadwar checkbox
4156 Multi_create_sw_checkbox.create(&Multi_create_window, "", Multi_create_sw_checkbox_coords[gr_screen.res][0], Multi_create_sw_checkbox_coords[gr_screen.res][1], 0);
4157 Multi_create_sw_checkbox.set_bmaps(Multi_create_sw_checkbox_fname[gr_screen.res], 6, 0);
4158 if(!MULTI_IS_TRACKER_GAME){
4159 Multi_create_sw_checkbox.hide();
4160 Multi_create_sw_checkbox.disable();
4165 // disable squad war button in demo
4166 Multi_create_sw_checkbox.hide();
4167 Multi_create_sw_checkbox.disable();
4170 // initialize the mission type filtering mode
4171 Multi_create_filter = MISSION_TYPE_MULTI;
4173 // initialize the list mode, and load in a list
4174 memset(Multi_create_mission_list, 0, sizeof(multi_create_info) * MULTI_CREATE_MAX_LIST_ITEMS);
4175 memset(Multi_create_campaign_list, 0, sizeof(multi_create_info) * MULTI_CREATE_MAX_LIST_ITEMS);
4176 for(idx=0; idx<MULTI_CREATE_MAX_LIST_ITEMS; idx++){
4177 Multi_create_mission_list[idx].valid_status = MVALID_STATUS_UNKNOWN;
4178 Multi_create_campaign_list[idx].valid_status = MVALID_STATUS_UNKNOWN;
4180 Multi_create_list_mode = MULTI_CREATE_SHOW_MISSIONS;
4181 Multi_create_list_start = -1;
4182 Multi_create_list_select = -1;
4183 Multi_create_list_count = 0;
4186 Multi_create_slider.create(&Multi_create_window, Mc_slider_coords[gr_screen.res][MC_X_COORD], Mc_slider_coords[gr_screen.res][MC_Y_COORD], Mc_slider_coords[gr_screen.res][MC_W_COORD],Mc_slider_coords[gr_screen.res][MC_H_COORD], MULTI_CREATE_MAX_LIST_ITEMS, Mc_slider_bitmap[gr_screen.res], &multi_create_list_scroll_up, &multi_create_list_scroll_down, NULL);
4189 // create the player list select button
4190 Multi_create_player_select_button.create(&Multi_create_window, "", Mc_players_coords[gr_screen.res][MC_X_COORD], Mc_players_coords[gr_screen.res][MC_Y_COORD], Mc_players_coords[gr_screen.res][MC_W_COORD], Mc_players_coords[gr_screen.res][MC_H_COORD], 0, 1);
4191 Multi_create_player_select_button.hide();
4193 // create the mission/campaign list select button
4194 Multi_create_list_select_button.create(&Multi_create_window, "", Mc_list_coords[gr_screen.res][MC_X_COORD], Mc_list_coords[gr_screen.res][MC_Y_COORD], Mc_list_coords[gr_screen.res][MC_W_COORD], Mc_list_coords[gr_screen.res][MC_H_COORD], 0, 1);
4195 Multi_create_list_select_button.hide();
4197 // set hotkeys for a couple of things.
4198 Multi_create_buttons[gr_screen.res][MC_ACCEPT].button.set_hotkey(KEY_CTRLED+SDLK_RETURN);
4200 // init some master tracker stuff
4201 Multi_create_frame_count = 0;
4202 Multi_create_mt_tried_login = 0;
4204 // remove campaign flags
4205 Game_mode &= ~(GM_CAMPAIGN_MODE);
4207 // send any pilots as appropriate
4208 multi_data_send_my_junk();
4209 Multi_create_file_list = Multi_create_mission_list;
4211 Multi_create_campaign_count = 0;
4212 Multi_create_mission_count = 0;
4213 Multi_create_files_loaded = 0;
4216 void multi_create_game_do()
4220 const char *loading_str = XSTR("Loading", 1336);
4224 // set this if we want to show the pilot info popup
4225 Multi_create_should_show_popup = 0;
4227 // first thing is to load the files
4228 if ( !Multi_create_files_loaded ) {
4229 // if I am a client, send a list request to the server for the missions
4230 if ( MULTIPLAYER_CLIENT ) {
4231 send_mission_list_request( MISSION_LIST_REQUEST );
4235 loading_bitmap = bm_load(Multi_create_loading_fname[gr_screen.res]);
4237 // draw the background, etc
4239 GR_MAYBE_CLEAR_RES(Multi_create_bitmap);
4240 if(Multi_create_bitmap != -1){
4241 gr_set_bitmap(Multi_create_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4245 if ( loading_bitmap > -1 ){
4246 gr_set_bitmap(loading_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4248 gr_bitmap( Please_wait_coords[gr_screen.res][MC_X_COORD], Please_wait_coords[gr_screen.res][MC_Y_COORD] );
4250 // draw "Loading" on it
4252 gr_set_color_fast(&Color_normal);
4254 gr_get_string_size(&str_w, &str_h, loading_str);
4255 gr_string((gr_screen.max_w - str_w) / 2, (gr_screen.max_h - str_h) / 2, loading_str);
4261 multi_create_list_load_missions();
4262 multi_create_list_load_campaigns();
4264 // if this is a tracker game, validate missions
4265 if(MULTI_IS_TRACKER_GAME){
4266 multi_update_valid_missions();
4269 // update the file list
4270 multi_create_setup_list_data(MULTI_CREATE_SHOW_MISSIONS);
4273 // don't bother setting netgame state if ont the server
4274 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4275 Netgame.game_state = NETGAME_STATE_FORMING;
4276 send_netgame_update_packet();
4279 // if we're on the standalone we have to tell him that we're now in the host setup screen
4280 Net_player->state = NETPLAYER_STATE_HOST_SETUP;
4281 send_netplayer_update_packet();
4283 Multi_create_files_loaded = 1;
4286 int k = chatbox_process();
4287 k = Multi_create_window.process(k,0);
4290 // same as the cancel button
4292 if(help_overlay_active(MULTI_CREATE_OVERLAY)){
4293 help_overlay_set_state(MULTI_CREATE_OVERLAY,0);
4295 gamesnd_play_iface(SND_USER_SELECT);
4296 multi_quit_game(PROMPT_HOST);
4301 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
4302 help_overlay_set_state(MULTI_CREATE_OVERLAY, 0);
4305 // process any button clicks
4306 multi_create_check_buttons();
4308 // do any network related stuff
4309 multi_create_do_netstuff();
4311 // draw the background, etc
4313 GR_MAYBE_CLEAR_RES(Multi_create_bitmap);
4314 if(Multi_create_bitmap != -1){
4315 gr_set_bitmap(Multi_create_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4319 // if we're not in team vs. team mode, don't draw the team buttons
4320 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
4321 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.hide();
4322 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.hide();
4323 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.disable();
4324 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.disable();
4326 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.enable();
4327 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.enable();
4328 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.unhide();
4329 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.unhide();
4332 // draw the window itself
4333 Multi_create_window.draw();
4335 gr_set_color_fast(&Color_normal);
4338 // draw Create Game text
4339 gr_string(Mc_create_game_text[gr_screen.res][MC_X_COORD], Mc_create_game_text[gr_screen.res][MC_Y_COORD], XSTR("Create Game", 1268));
4341 // draw players text
4342 gr_string(Mc_players_text[gr_screen.res][MC_X_COORD], Mc_players_text[gr_screen.res][MC_Y_COORD], XSTR("Players", 1269));
4344 // draw players text
4345 gr_string(Mc_team_text[gr_screen.res][MC_X_COORD], Mc_team_text[gr_screen.res][MC_Y_COORD], XSTR("Team", 1258));
4348 // process and display the player list
4349 // NOTE : this must be done before the buttons are checked to insure that a player hasn't dropped
4350 multi_create_plist_process();
4351 if(Netgame.type_flags & NG_TYPE_TEAM){
4352 multi_create_plist_blit_team();
4354 multi_create_plist_blit_normal();
4357 // process and display the game/campaign list
4358 multi_create_list_do();
4360 // draw the correct mission filter button
4361 multi_create_draw_filter_buttons();
4363 // display any text in the info area
4364 multi_common_render_text();
4366 // display any pending notification messages
4367 multi_common_notify_do();
4369 // force the correct mission/campaign button to light up
4370 if( Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ){
4371 Multi_create_buttons[gr_screen.res][MC_MISSION_FILTER].button.draw_forced(2);
4373 Multi_create_buttons[gr_screen.res][MC_CAMPAIGN_FILTER].button.draw_forced(2);
4376 // force draw the closed button if it is toggled on
4377 if(Netgame.flags & NG_FLAG_TEMP_CLOSED){
4378 Multi_create_buttons[gr_screen.res][MC_CLOSE].button.draw_forced(2);
4381 // process and show the chatbox thingie
4385 Multi_create_window.draw_tooltip();
4387 // display the voice status indicator
4388 multi_common_voice_display_status();
4390 // blit the help overlay if necessary
4391 help_overlay_maybe_blit(MULTI_CREATE_OVERLAY);
4394 if(MULTI_IS_TRACKER_GAME){
4395 if(Netgame.type_flags & NG_TYPE_SW){
4396 gr_set_color_fast(&Color_bright);
4398 gr_set_color_fast(&Color_normal);
4401 gr_string(Multi_create_sw_checkbox_text[gr_screen.res][0], Multi_create_sw_checkbox_text[gr_screen.res][1], "SquadWar");
4408 // if we're supposed to show the pilot info popup, do it now
4409 if(Multi_create_should_show_popup){
4410 // get the player index and address of the player item the mouse is currently over
4411 if(Multi_create_plist_select_flag){
4412 player_index = find_player_id(Multi_create_plist_select_id);
4413 if(player_index != -1){
4414 multi_pinfo_popup(&Net_players[player_index]);
4419 // increment the frame count
4420 Multi_create_frame_count++;
4423 void multi_create_game_close()
4425 // unload any bitmaps
4426 if(!bm_unload(Multi_create_bitmap)){
4427 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_create_bitmap_fname[gr_screen.res]));
4430 // unload the help overlay
4431 help_overlay_unload(MULTI_CREATE_OVERLAY);
4433 // destroy the chatbox
4436 // destroy the UI_WINDOW
4437 Multi_create_window.destroy();
4440 void multi_create_check_buttons()
4443 for(idx=0;idx<MULTI_CREATE_NUM_BUTTONS;idx++){
4444 // we only really need to check for one button pressed at a time, so we can break after
4446 if(Multi_create_buttons[gr_screen.res][idx].button.pressed()){
4447 multi_create_button_pressed(idx);
4453 // if the squad war checkbox was clicked
4454 if(Multi_create_sw_checkbox.changed()){
4455 multi_create_sw_clicked();
4460 void multi_create_button_pressed(int n)
4466 gamesnd_play_iface(SND_USER_SELECT);
4467 multi_quit_game(PROMPT_HOST);
4470 // if valid commit conditions have not been met
4471 if(!multi_create_ok_to_commit()){
4476 multi_create_accept_hit();
4481 if(!help_overlay_active(MULTI_CREATE_OVERLAY)){
4482 help_overlay_set_state(MULTI_CREATE_OVERLAY,1);
4484 help_overlay_set_state(MULTI_CREATE_OVERLAY,0);
4488 // scroll the info text box up
4489 case MC_SCROLL_INFO_UP:
4490 multi_common_scroll_text_up();
4493 // scroll the info text box down
4494 case MC_SCROLL_INFO_DOWN:
4495 multi_common_scroll_text_down();
4498 // scroll the player list up
4499 case MC_SCROLL_PLAYERS_UP:
4500 multi_create_plist_scroll_up();
4503 // scroll the player list down
4504 case MC_SCROLL_PLAYERS_DOWN:
4505 multi_create_plist_scroll_down();
4508 // scroll the game/campaign list up
4509 case MC_SCROLL_LIST_UP:
4510 multi_create_list_scroll_up();
4512 Multi_create_slider.forceUp(); // move slider up
4516 // scroll the game/campaign list down
4517 case MC_SCROLL_LIST_DOWN:
4518 multi_create_list_scroll_down();
4520 Multi_create_slider.forceDown(); // move slider down
4524 // go to the options screen
4526 gamesnd_play_iface(SND_USER_SELECT);
4527 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
4530 // show all missions
4532 if(Multi_create_filter != MISSION_TYPE_MULTI){
4533 gamesnd_play_iface(SND_USER_SELECT);
4534 Multi_create_filter = MISSION_TYPE_MULTI;
4535 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4537 gamesnd_play_iface(SND_GENERAL_FAIL);
4541 // show cooperative missions
4543 if(Multi_create_filter != MISSION_TYPE_MULTI_COOP){
4544 gamesnd_play_iface(SND_USER_SELECT);
4545 Multi_create_filter = MISSION_TYPE_MULTI_COOP;
4546 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4548 gamesnd_play_iface(SND_GENERAL_FAIL);
4552 // show team vs. team missions
4554 if(Multi_create_filter != MISSION_TYPE_MULTI_TEAMS){
4555 gamesnd_play_iface(SND_USER_SELECT);
4556 Multi_create_filter = MISSION_TYPE_MULTI_TEAMS;
4557 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4559 gamesnd_play_iface(SND_GENERAL_FAIL);
4563 // show dogfight missions
4565 case MC_SHOW_DOGFIGHT:
4566 if (Multi_create_filter != MISSION_TYPE_MULTI_DOGFIGHT){
4567 gamesnd_play_iface(SND_USER_SELECT);
4568 Multi_create_filter = MISSION_TYPE_MULTI_DOGFIGHT;
4569 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4571 gamesnd_play_iface(SND_GENERAL_FAIL);
4576 // toggle temporary netgame closed on/off
4578 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4579 Netgame.flags ^= NG_FLAG_TEMP_CLOSED;
4581 Netgame.options.flags |= MLO_FLAG_TEMP_CLOSED;
4582 multi_options_update_netgame();
4584 gamesnd_play_iface(SND_USER_SELECT);
4587 // kick the currently selected player (if possible)
4589 // lookup the player at the specified index
4590 if(Multi_create_plist_select_flag){
4591 idx = find_player_id(Multi_create_plist_select_id);
4592 // kick him - but don't ban him
4594 multi_kick_player(idx,0);
4599 // switch to individual mission mode and load in a list
4600 case MC_MISSION_FILTER:
4601 if(Multi_create_list_mode != MULTI_CREATE_SHOW_MISSIONS){
4602 Netgame.campaign_mode = MP_SINGLE;
4604 gamesnd_play_iface(SND_USER_SELECT);
4606 // update the file list
4607 multi_create_setup_list_data(MULTI_CREATE_SHOW_MISSIONS);
4609 gamesnd_play_iface(SND_GENERAL_FAIL);
4613 // switch to campaign mode and load in a list
4614 case MC_CAMPAIGN_FILTER:
4615 // switch off squad war
4617 Multi_create_sw_checkbox.set_state(0);
4619 Netgame.type_flags = NG_TYPE_COOP;
4621 if(Multi_create_list_mode != MULTI_CREATE_SHOW_CAMPAIGNS){
4622 Netgame.campaign_mode = MP_CAMPAIGN;
4624 gamesnd_play_iface(SND_USER_SELECT);
4626 // update the file list
4627 multi_create_setup_list_data(MULTI_CREATE_SHOW_CAMPAIGNS);
4629 gamesnd_play_iface(SND_GENERAL_FAIL);
4633 // attempt to set the selected player's team
4635 multi_create_set_selected_team(0);
4636 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4637 multi_team_send_update();
4641 // attempt to set the selected player's team
4643 multi_create_set_selected_team(1);
4644 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4645 multi_team_send_update();
4649 // popup the pilot info dialog for the currently selected pilot (will occur at the end of the frame)
4651 Multi_create_should_show_popup = 1;
4654 // go to the host options screen
4655 case MC_HOST_OPTIONS:
4656 gamesnd_play_iface(SND_USER_SELECT);
4657 gameseq_post_event(GS_EVENT_MULTI_HOST_OPTIONS);
4660 // refresh PXO file list
4661 case MC_PXO_REFRESH:
4662 if(!MULTI_IS_TRACKER_GAME){
4665 multi_create_refresh_pxo();
4669 gamesnd_play_iface(SND_GENERAL_FAIL);
4670 multi_common_add_notify(XSTR("Not implemented yet!",760));
4675 // do stuff like pinging servers, sending out requests, etc
4676 void multi_create_do_netstuff()
4680 // if not on a standalone
4681 void multi_create_init_as_server()
4683 // set me up as the host and master
4684 Net_player->flags |= (NETINFO_FLAG_AM_MASTER | NETINFO_FLAG_GAME_HOST);
4687 // if on a standalone
4688 void multi_create_init_as_client()
4690 Net_player->flags |= NETINFO_FLAG_GAME_HOST;
4693 // scroll up through the player list
4694 void multi_create_plist_scroll_up()
4696 gamesnd_play_iface(SND_GENERAL_FAIL);
4699 // scroll down through the player list
4700 void multi_create_plist_scroll_down()
4702 gamesnd_play_iface(SND_GENERAL_FAIL);
4705 void multi_create_plist_process()
4707 int test_count,idx,player_index;
4709 // first determine if there are 0 players in the game. This should never happen since the host is _always_ in the game
4711 for(idx=0;idx<MAX_PLAYERS;idx++){
4712 // count anyone except the standalone server (if applicable)
4713 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
4717 if(test_count <= 0){
4721 // if we had a selected item but that player has left, select myself instead
4722 if(Multi_create_plist_select_flag){
4723 player_index = find_player_id(Multi_create_plist_select_id);
4724 if(player_index == -1){
4725 Multi_create_plist_select_id = Net_player->player_id;
4728 Multi_create_plist_select_flag = 1;
4729 Multi_create_plist_select_id = Net_player->player_id;
4732 // if the player has clicked somewhere in the player list area
4733 if(Multi_create_player_select_button.pressed()){
4736 // get the player index and address of the player item the mouse is currently over
4737 player_id = multi_create_get_mouse_id();
4738 player_index = find_player_id(player_id);
4739 if(player_index != -1){
4740 Multi_create_plist_select_flag = 1;
4741 Multi_create_plist_select_id = player_id;
4746 void multi_create_plist_blit_normal()
4749 char str[CALLSIGN_LEN+5];
4750 int y_start = Mc_players_coords[gr_screen.res][MC_Y_COORD];
4753 // display all the players
4754 for(idx=0;idx<MAX_PLAYERS;idx++){
4755 // count anyone except the standalone server (if applicable)
4756 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
4760 // highlight him if he's the host
4761 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4762 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4763 gr_set_color_fast(&Color_text_active_hi);
4765 gr_set_color_fast(&Color_bright);
4768 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4769 gr_set_color_fast(&Color_text_active);
4771 gr_set_color_fast(&Color_text_normal);
4775 // optionally draw his CD status
4776 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4777 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4778 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4780 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4783 // make sure the string will fit, then display it
4784 SDL_strlcpy(str, Net_players[idx].player->callsign, SDL_arraysize(str));
4785 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4786 SDL_strlcat(str, XSTR("(O)",787), SDL_arraysize(str)); // [[ Observer ]]
4788 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4789 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4796 void multi_create_plist_blit_team()
4799 char str[CALLSIGN_LEN+1];
4800 int y_start = Mc_players_coords[gr_screen.res][MC_Y_COORD];
4803 // display all the red players first
4804 for(idx=0;idx<MAX_PLAYERS;idx++){
4805 // count anyone except the standalone server (if applicable)
4806 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
4807 // reset total offset
4810 // highlight him if he's the host
4811 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4812 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4813 gr_set_color_fast(&Color_text_active_hi);
4815 // be sure to blit the correct team button
4816 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.draw_forced(2);
4818 gr_set_color_fast(&Color_bright);
4821 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4822 gr_set_color_fast(&Color_text_active);
4824 // be sure to blit the correct team button
4825 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.draw_forced(2);
4827 gr_set_color_fast(&Color_text_normal);
4831 // optionally draw his CD status
4832 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4833 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4834 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4836 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4839 // blit the red team indicator
4840 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
4841 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
4842 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4843 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4845 total_offset += Multi_common_icon_dims[MICON_TEAM0_SELECT][0] + 1;
4848 if(Multi_common_icons[MICON_TEAM0] != -1){
4849 gr_set_bitmap(Multi_common_icons[MICON_TEAM0], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4850 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4852 total_offset += Multi_common_icon_dims[MICON_TEAM0][0] + 1;
4856 // make sure the string will fit
4857 SDL_strlcpy(str, Net_players[idx].player->callsign, SDL_arraysize(str));
4858 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4859 SDL_strlcat(str, XSTR("(O)",787), SDL_arraysize(str));
4861 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4863 // display him in the correct half of the list depending on his team
4864 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4869 // display all the green players next
4870 for(idx=0;idx<MAX_PLAYERS;idx++){
4871 // count anyone except the standalone server (if applicable)
4872 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
4873 // reset total offset
4876 // highlight him if he's the host
4877 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4878 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4879 gr_set_color_fast(&Color_text_active_hi);
4881 // be sure to blit the correct team button
4882 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.draw_forced(2);
4884 gr_set_color_fast(&Color_bright);
4887 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4888 gr_set_color_fast(&Color_text_active);
4890 // be sure to blit the correct team button
4891 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.draw_forced(2);
4893 gr_set_color_fast(&Color_text_normal);
4897 // optionally draw his CD status
4898 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4899 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4900 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4902 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4905 // blit the red team indicator
4906 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
4907 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
4908 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4909 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4911 total_offset += Multi_common_icon_dims[MICON_TEAM1_SELECT][0] + 1;
4914 if(Multi_common_icons[MICON_TEAM1] != -1){
4915 gr_set_bitmap(Multi_common_icons[MICON_TEAM1], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4916 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4918 total_offset += Multi_common_icon_dims[MICON_TEAM1][0] + 1;
4922 // make sure the string will fit
4923 SDL_strlcpy(str, Net_players[idx].player->callsign, SDL_arraysize(str));
4924 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4925 SDL_strlcat(str, XSTR("(O)",787), SDL_arraysize(str));
4927 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4929 // display him in the correct half of the list depending on his team
4930 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4936 void multi_create_list_scroll_up()
4938 if(Multi_create_list_start > 0){
4939 Multi_create_list_start--;
4941 gamesnd_play_iface(SND_SCROLL);
4943 gamesnd_play_iface(SND_GENERAL_FAIL);
4947 void multi_create_list_scroll_down()
4949 if((Multi_create_list_count - Multi_create_list_start) > Multi_create_list_max_display[gr_screen.res]){
4950 Multi_create_list_start++;
4952 gamesnd_play_iface(SND_SCROLL);
4954 gamesnd_play_iface(SND_GENERAL_FAIL);
4958 // gets a list of multiplayer misisons
4959 void multi_create_list_load_missions()
4961 char *fname, mission_name[NAME_LENGTH+1];
4965 SDL_snprintf(wild_card, SDL_arraysize(wild_card), "*%s", FS_MISSION_FILE_EXT);
4966 file_count = cf_get_file_list_preallocated(MULTI_CREATE_MAX_LIST_ITEMS, Multi_create_files_array, NULL, CF_TYPE_MISSIONS, wild_card);
4967 Multi_create_mission_count = 0;
4969 // maybe create a standalone dialog
4970 if(Game_mode & GM_STANDALONE_SERVER){
4971 std_create_gen_dialog("Loading missions");
4972 std_gen_set_text("Mission:", 1);
4975 for(idx = 0; idx < file_count; idx++){
4976 int flags,max_players;
4980 fname = Multi_create_files_array[idx];
4982 // tack on any necessary file extension
4983 filename = cf_add_ext( fname, FS_MISSION_FILE_EXT );
4985 // for multiplayer beta builds, only accept builtin missions
4986 #if defined(MULTIPLAYER_BETA_BUILD) || defined(FS2_DEMO)
4987 if(game_find_builtin_mission(filename) == NULL){
4990 #elif defined(PD_BUILD)
4991 if((game_find_builtin_mission(filename) == NULL) && !strstr(filename, "peterdrake")){
4996 if(Game_mode & GM_STANDALONE_SERVER){
4997 std_gen_set_text(filename, 2);
5000 flags = mission_parse_is_multi(filename, mission_name);
5002 // if the mission is a multiplayer mission, then add it to the mission list
5004 max_players = mission_parse_get_multi_mission_info( filename );
5005 m_respawn = The_mission.num_respawns;
5007 if ( Multi_create_mission_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
5008 multi_create_info *mcip;
5010 mcip = &Multi_create_mission_list[Multi_create_mission_count];
5011 SDL_strlcpy(mcip->filename, filename, SDL_arraysize(mcip->filename));
5012 SDL_strlcpy(mcip->name, mission_name, SDL_arraysize(mcip->name));
5013 mcip->flags = flags;
5014 mcip->respawn = m_respawn;
5015 mcip->max_players = (ubyte)max_players;
5017 // get any additional information for possibly builtin missions
5018 fs_builtin_mission *fb = game_find_builtin_mission(filename);
5022 Multi_create_mission_count++;
5028 Multi_create_slider.set_numberItems(Multi_create_mission_count > Multi_create_list_max_display[gr_screen.res] ? Multi_create_mission_count-Multi_create_list_max_display[gr_screen.res] : 0);
5031 // maybe create a standalone dialog
5032 if(Game_mode & GM_STANDALONE_SERVER){
5033 std_destroy_gen_dialog();
5037 void multi_create_list_load_campaigns()
5040 int idx, file_count;
5041 int campaign_type,max_players;
5045 // maybe create a standalone dialog
5046 if(Game_mode & GM_STANDALONE_SERVER){
5047 std_create_gen_dialog("Loading campaigns");
5048 std_gen_set_text("Campaign:", 1);
5051 Multi_create_campaign_count = 0;
5052 SDL_snprintf(wild_card, SDL_arraysize(wild_card), "*%s", FS_CAMPAIGN_FILE_EXT);
5053 file_count = cf_get_file_list_preallocated(MULTI_CREATE_MAX_LIST_ITEMS, Multi_create_files_array, NULL, CF_TYPE_MISSIONS, wild_card);
5054 for(idx = 0; idx < file_count; idx++){
5056 char *filename, name[NAME_LENGTH];
5058 fname = Multi_create_files_array[idx];
5060 // tack on any necessary file extension
5061 filename = cf_add_ext( fname, FS_CAMPAIGN_FILE_EXT );
5063 // for multiplayer beta builds, only accept builtin missions
5064 #if defined(MULTIPLAYER_BETA_BUILD) || defined(FS2_DEMO)
5065 if(game_find_builtin_mission(filename) == NULL){
5068 #elif defined(PD_BUILD)
5069 if((game_find_builtin_mission(filename) == NULL) && !strstr(filename, "peterdrake")){
5074 if(Game_mode & GM_STANDALONE_SERVER){
5075 std_gen_set_text(filename, 2);
5078 // if the campaign is a multiplayer campaign, then add the data to the campaign list items
5079 flags = mission_campaign_parse_is_multi( filename, name, SDL_arraysize(name) );
5080 if( flags != CAMPAIGN_TYPE_SINGLE && mission_campaign_get_info(filename,title,&campaign_type,&max_players)) {
5081 if ( Multi_create_campaign_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
5082 multi_create_info *mcip;
5084 mcip = &Multi_create_campaign_list[Multi_create_campaign_count];
5085 SDL_strlcpy(mcip->filename, filename, SDL_arraysize(mcip->filename));
5086 SDL_strlcpy(mcip->name, name, SDL_arraysize(mcip->name));
5088 // setup various flags
5089 if ( flags == CAMPAIGN_TYPE_MULTI_COOP ){
5090 mcip->flags = MISSION_TYPE_MULTI_COOP | MISSION_TYPE_MULTI;
5091 } else if ( flags == CAMPAIGN_TYPE_MULTI_TEAMS ) {
5092 mcip->flags = MISSION_TYPE_MULTI_TEAMS | MISSION_TYPE_MULTI;
5094 Int3(); // bogus campaign multi type -- find allender
5097 // 0 respawns for campaign files (should be contained within the mission files themselves)
5100 // 0 max players for campaign files
5101 mcip->max_players = (unsigned char)max_players;
5103 // get any additional information for possibly builtin missions
5104 fs_builtin_mission *fb = game_find_builtin_mission(filename);
5108 Multi_create_campaign_count++;
5113 // maybe create a standalone dialog
5114 if(Game_mode & GM_STANDALONE_SERVER){
5115 std_destroy_gen_dialog();
5119 void multi_create_list_do()
5122 int start_index,stop_index;
5123 char selected_name[255];
5125 // bail early if there aren't any selectable items
5126 if(Multi_create_list_count == 0){
5130 // first check to see if the user has clicked on an item
5131 if(Multi_create_list_select_button.pressed()){
5133 Multi_create_list_select_button.get_mouse_pos(NULL,&y);
5136 // make sure we are selectedin valid indices
5137 if((item < Multi_create_list_max_display[gr_screen.res]) && (item >= 0)){
5138 item += Multi_create_list_start;
5140 if(item < Multi_create_list_count){
5141 multi_create_list_select_item(item);
5142 gamesnd_play_iface(SND_IFACE_MOUSE_CLICK);
5147 // bail early if we don't have a start position
5148 if(Multi_create_list_start == -1){
5152 // display the list of individual campaigns/missions
5154 int y_start = Mc_list_coords[gr_screen.res][MC_Y_COORD];
5156 start_index = multi_create_select_to_index(Multi_create_list_start);
5157 stop_index = Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ? Multi_create_mission_count : Multi_create_campaign_count;
5158 for(idx=start_index; idx<stop_index; idx++){
5159 // see if we should drop out
5160 if(count == Multi_create_list_max_display[gr_screen.res]){
5164 // see if we should filter out this mission
5165 if ( !(Multi_create_file_list[idx].flags & Multi_create_filter) ){
5169 // highlight the selected item
5170 multi_create_select_to_filename(Multi_create_list_select, selected_name, SDL_arraysize(selected_name));
5171 if(!strcmp(selected_name,Multi_create_file_list[idx].filename)){
5172 gr_set_color_fast(&Color_text_selected);
5174 gr_set_color_fast(&Color_text_normal);
5177 // draw the type icon
5178 multi_create_list_blit_icons(idx, y_start);
5180 // force fit the mission name string
5181 SDL_strlcpy(selected_name, Multi_create_file_list[idx].name, SDL_arraysize(selected_name));
5182 gr_force_fit_string(selected_name,255,Mc_column1_w[gr_screen.res]);
5183 gr_string(Mc_mission_name_x[gr_screen.res],y_start,selected_name);
5185 // draw the max players if in mission mode
5186 SDL_snprintf(selected_name,SDL_arraysize(selected_name),"%d",(int)Multi_create_file_list[idx].max_players);
5187 gr_string(Mc_mission_count_x[gr_screen.res],y_start,selected_name);
5189 // force fit the mission filename string
5190 SDL_strlcpy(selected_name, Multi_create_file_list[idx].filename, SDL_arraysize(selected_name));
5191 gr_force_fit_string(selected_name,255,Mc_column3_w[gr_screen.res]);
5192 gr_string(Mc_mission_fname_x[gr_screen.res],y_start,selected_name);
5199 // takes care of stuff like changing indices around and setting up the netgame structure
5200 void multi_create_list_select_item(int n)
5202 int abs_index,campaign_type,max_players;
5203 char title[NAME_LENGTH+1];
5204 netgame_info ng_temp;
5207 char *campaign_desc;
5209 // if not on the standalone server
5210 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5213 // on the standalone
5215 memset(&ng_temp,0,sizeof(netgame_info));
5219 if ( n != Multi_create_list_select ) {
5220 // check to see if this is a valid index, and bail if it is not
5221 abs_index = multi_create_select_to_index(n);
5222 if(abs_index == -1){
5226 Multi_create_list_select = n;
5228 // set the mission name
5229 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5230 multi_create_select_to_filename(n, ng->mission_name, SDL_arraysize(ng->mission_name));
5232 multi_create_select_to_filename(n, ng->campaign_name, SDL_arraysize(ng->campaign_name));
5235 // make sure the netgame type is properly set
5236 int old_type = Netgame.type_flags;
5237 abs_index = multi_create_select_to_index(n);
5238 if(abs_index != -1){
5239 if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_TEAMS){
5241 // if we're in squad war mode, leave it as squad war
5242 if(old_type & NG_TYPE_SW){
5243 ng->type_flags = NG_TYPE_SW;
5245 ng->type_flags = NG_TYPE_TVT;
5248 ng->type_flags = NG_TYPE_TVT;
5250 } else if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_COOP){
5251 ng->type_flags = NG_TYPE_COOP;
5252 } else if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_DOGFIGHT){
5253 ng->type_flags = NG_TYPE_DOGFIGHT;
5258 // if we're no longer in a TvT game, just uncheck the squadwar checkbox
5259 if(!(ng->type_flags & NG_TYPE_TEAM)){
5260 Multi_create_sw_checkbox.set_state(0);
5264 // if we switched from something else to team vs. team mode, do some special processing
5265 if((ng->type_flags & NG_TYPE_TEAM) && (ng->type_flags != old_type) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
5269 switch(Multi_create_list_mode){
5270 case MULTI_CREATE_SHOW_MISSIONS:
5271 // don't forget to update the info box window thingie
5272 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5273 ship_init(); // mwa -- 10/15/97. Call this function to reset number of ships in mission
5274 ng->max_players = mission_parse_get_multi_mission_info( ng->mission_name );
5276 SDL_assert(ng->max_players > 0);
5277 SDL_strlcpy(ng->title, The_mission.name, SDL_arraysize(ng->title));
5279 // set the information area text
5280 multi_common_set_text(The_mission.mission_desc);
5282 // if we're on the standalone, send a request for the description
5284 send_netgame_descript_packet(&Netgame.server_addr,0);
5285 multi_common_set_text("");
5288 // set the respawns as appropriate
5289 if(Netgame.options.respawn <= Multi_create_file_list[abs_index].respawn){
5290 ng->respawn = Netgame.options.respawn;
5291 nprintf(("Network","Using netgame options for respawn count (%d %d)\n",Netgame.options.respawn,Multi_create_file_list[abs_index].respawn));
5293 ng->respawn = Multi_create_file_list[abs_index].respawn;
5294 nprintf(("Network","Using mission settings for respawn count (%d %d)\n",Netgame.options.respawn,Multi_create_file_list[abs_index].respawn));
5297 case MULTI_CREATE_SHOW_CAMPAIGNS:
5298 // if not on the standalone server
5299 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5300 // get the campaign info
5301 memset(title,0,NAME_LENGTH+1);
5302 if(!mission_campaign_get_info(ng->campaign_name,title,&campaign_type,&max_players, &campaign_desc)) {
5303 memset(ng->campaign_name,0,NAME_LENGTH+1);
5304 ng->max_players = 0;
5306 // if we successfully got the # of players
5308 memset(ng->title,0,NAME_LENGTH+1);
5309 SDL_strlcpy(ng->title, title, SDL_arraysize(ng->title));
5310 ng->max_players = max_players;
5313 nprintf(("Network","MC MAX PLAYERS : %d\n",ng->max_players));
5315 // set the information area text
5316 // multi_common_set_text(ng->title);
5317 multi_common_set_text(campaign_desc);
5319 // if on the standalone server, send a request for the description
5321 // no descriptions currently kept for campaigns
5324 // netgame respawns are always 0 for campaigns (until the first mission is loaded)
5329 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5331 send_netgame_update_packet();
5333 // update all machines about stuff like respawns, etc.
5334 multi_options_update_netgame();
5336 multi_options_update_mission(ng, Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ? 1 : 0);
5341 void multi_create_list_blit_icons(int list_index, int y_start)
5343 multi_create_info *mcip;
5344 fs_builtin_mission *fb;
5347 // get a pointer to the list item
5348 max_index = Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ? Multi_create_mission_count - 1 : Multi_create_campaign_count - 1;
5349 if((list_index < 0) || (list_index > max_index)){
5352 mcip = &Multi_create_file_list[list_index];
5354 // blit the multiplayer type icons
5355 if(mcip->flags & MISSION_TYPE_MULTI_COOP){
5356 if(Multi_common_icons[MICON_COOP] >= 0){
5357 gr_set_bitmap(Multi_common_icons[MICON_COOP], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5358 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5360 } else if(mcip->flags & MISSION_TYPE_MULTI_TEAMS){
5361 if(Multi_common_icons[MICON_TVT] >= 0){
5362 gr_set_bitmap(Multi_common_icons[MICON_TVT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5363 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5365 } else if(mcip->flags & MISSION_TYPE_MULTI_DOGFIGHT){
5366 if(Multi_common_icons[MICON_DOGFIGHT] >= 0){
5367 gr_set_bitmap(Multi_common_icons[MICON_DOGFIGHT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5368 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5372 // if its a valid mission, blit the valid mission icon
5373 if(MULTI_IS_TRACKER_GAME && (mcip->valid_status == MVALID_STATUS_VALID)){
5374 if(Multi_common_icons[MICON_VALID] >= 0){
5375 gr_set_bitmap(Multi_common_icons[MICON_VALID], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5376 gr_bitmap(Mc_icon_valid_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_valid_coords[gr_screen.res][MC_Y_COORD]);
5380 // now see if its a builtin mission
5381 fb = game_find_builtin_mission(mcip->filename);
5382 // if the mission is from volition, blit the volition icon
5383 if((fb != NULL) && (fb->flags & FSB_FROM_VOLITION)){
5384 if(Multi_common_icons[MICON_VOLITION] >= 0){
5385 gr_set_bitmap(Multi_common_icons[MICON_VOLITION], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5386 gr_bitmap(Mc_icon_volition_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_volition_coords[gr_screen.res][MC_Y_COORD]);
5391 // check for mdisk mission
5392 fb = game_find_builtin_mission(mcip->filename);
5393 if((fb != NULL) && (fb->flags & FSB_FROM_MDISK)){
5394 if(Multi_common_icons[MICON_MDISK] >= 0){
5395 gr_set_bitmap(Multi_common_icons[MICON_MDISK], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5396 gr_bitmap(Mc_icon_silent_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_silent_coords[gr_screen.res][MC_Y_COORD]);
5402 void multi_create_accept_hit()
5404 char selected_name[255];
5405 int start_campaign = 0;
5407 // make sure all players have finished joining
5408 if(!multi_netplayer_state_check(NETPLAYER_STATE_JOINED,1)){
5409 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("Please wait until all clients have finished joining",788));
5410 gamesnd_play_iface(SND_GENERAL_FAIL);
5413 gamesnd_play_iface(SND_COMMIT_PRESSED);
5416 // do single mission stuff
5417 switch(Multi_create_list_mode){
5418 case MULTI_CREATE_SHOW_MISSIONS:
5419 if(Multi_create_list_select != -1){
5420 // set the netgame mode
5421 Netgame.campaign_mode = MP_SINGLE;
5423 // setup various filenames and mission names
5424 multi_create_select_to_filename(Multi_create_list_select, selected_name, SDL_arraysize(selected_name));
5425 SDL_strlcpy( Game_current_mission_filename, selected_name, MAX_FILENAME_LEN );
5426 SDL_strlcpy( Netgame.mission_name, selected_name, MAX_FILENAME_LEN );
5429 ml_printf(NOX("Starting single mission %s, with %d players"), Game_current_mission_filename, multi_num_players());
5431 multi_common_add_notify(XSTR("No mission selected!",789));
5436 case MULTI_CREATE_SHOW_CAMPAIGNS:
5437 // do campaign related stuff
5438 if(Multi_create_list_select != -1){
5439 // set the netgame mode
5440 Netgame.campaign_mode = MP_CAMPAIGN;
5442 // start a campaign instead of a single mission
5443 multi_create_select_to_filename(Multi_create_list_select, selected_name, SDL_arraysize(selected_name));
5444 multi_campaign_start(selected_name);
5448 ml_printf(NOX("Starting campaign %s, with %d players"), selected_name, multi_num_players());
5450 multi_common_add_notify(XSTR("No campaign selected!",790));
5456 // if this is a team vs team situation, lock the players send a final team update
5457 if((Netgame.type_flags & NG_TYPE_TEAM) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
5458 multi_team_host_lock_all();
5459 multi_team_send_update();
5462 // if not on the standalone, move to the mission sync state which will take care of everything
5463 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5464 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
5465 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
5467 // otherwise tell the standalone to do so
5469 // when the standalone receives this, he'll do the mission syncing himself
5470 send_mission_sync_packet(MULTI_SYNC_PRE_BRIEFING,start_campaign);
5474 void multi_create_draw_filter_buttons()
5476 // highlight the correct filter button
5477 if ( Multi_create_filter == MISSION_TYPE_MULTI ){
5478 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL].button.draw_forced(2);
5479 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_COOP ) {
5480 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 1].button.draw_forced(2);
5481 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_TEAMS ) {
5482 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 2].button.draw_forced(2);
5483 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_DOGFIGHT ){
5484 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 3].button.draw_forced(2);
5490 void multi_create_set_selected_team(int team)
5494 // if we don't currently have a player selected, don't do anything
5495 if(!Multi_create_plist_select_flag){
5496 gamesnd_play_iface(SND_GENERAL_FAIL);
5499 gamesnd_play_iface(SND_USER_SELECT);
5501 // otherwise attempt to set the team for this guy
5502 player_index = find_player_id(Multi_create_plist_select_id);
5503 if(player_index != -1){
5504 multi_team_set_team(&Net_players[player_index],team);
5508 void multi_create_handle_join(net_player *pl)
5510 // for now just play a bloop sound
5511 gamesnd_play_iface(SND_ICON_DROP_ON_WING);
5514 // fill in net address of player the mouse is over, return player index (or -1 if none)
5515 short multi_create_get_mouse_id()
5517 // determine where he clicked (y pixel value)
5519 Multi_create_player_select_button.get_mouse_pos(NULL,&y);
5521 // select things a little differently if we're in team vs. team or non-team vs. team mode
5523 if(Netgame.type_flags & NG_TYPE_TEAM){
5524 int player_index = -1;
5526 // look through all of team red first
5527 for(idx=0;idx<MAX_PLAYERS;idx++){
5528 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
5531 // if this is the _nth_ guy
5539 // if we still haven't found him yet, look through the green team
5540 if(player_index == -1){
5541 for(idx=0;idx<MAX_PLAYERS;idx++){
5542 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
5544 // if this is the _nth_ guy
5553 if(player_index != -1){
5554 return Net_players[player_index].player_id;
5557 // select the nth active player if possible, disregarding the standalone server
5558 for(idx=0;idx<MAX_PLAYERS;idx++){
5559 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
5562 // if this is the _nth_ guy
5564 return Net_players[idx].player_id;
5573 void multi_create_select_to_filename(int select_index, char *filename, const int max_filelen)
5577 // look through the mission list
5578 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5579 for(idx=0;idx<Multi_create_mission_count;idx++){
5580 if(Multi_create_file_list[idx].flags & Multi_create_filter){
5584 // if we found the item
5585 if(select_index < 0){
5586 SDL_strlcpy(filename, Multi_create_file_list[idx].filename, max_filelen);
5591 // look through the campaign list
5592 else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
5593 for(idx=0;idx<Multi_create_campaign_count;idx++){
5596 // if we found the item
5597 if(select_index < 0){
5598 SDL_strlcpy(filename, Multi_create_file_list[idx].filename, max_filelen);
5604 SDL_strlcpy(filename, "", max_filelen);
5607 int multi_create_select_to_index(int select_index)
5610 int lookup_index = 0;
5612 // look through the mission list
5613 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5614 for(idx=0;idx<Multi_create_mission_count;idx++){
5615 if(Multi_create_file_list[idx].flags & Multi_create_filter){
5619 // if we found the item
5620 if(select_index < lookup_index){
5625 // look through the campaign list
5626 else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
5627 for(idx=0;idx<Multi_create_campaign_count;idx++){
5630 // if we found the item
5631 if(select_index < 0){
5640 int multi_create_ok_to_commit()
5642 int player_count, observer_count, idx;
5643 int notify_of_hacked_ships_tbl = 0;
5644 int notify_of_hacked_weapons_tbl = 0;
5645 char err_string[255];
5649 // make sure we have a valid mission selected
5650 if(Multi_create_list_select < 0){
5654 // if this is not a valid mission, let the player know
5655 abs_index = multi_create_select_to_index(Multi_create_list_select);
5660 // if we're playing with a hacked ships.tbl (on PXO)
5661 notify_of_hacked_ships_tbl = 0;
5662 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5663 if(!Game_ships_tbl_valid){
5664 notify_of_hacked_ships_tbl = 1;
5667 if(Netgame.flags & NG_FLAG_HACKED_SHIPS_TBL){
5668 notify_of_hacked_ships_tbl = 1;
5671 if(!MULTI_IS_TRACKER_GAME){
5672 notify_of_hacked_ships_tbl = 0;
5674 if(notify_of_hacked_ships_tbl){
5675 if(popup(PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON, 2, XSTR("&Back", 995), XSTR("&Continue", 780), XSTR("You or the server you are playing on has a hacked ships.tbl. Your stats will not be updated on PXO", 1051)) <= 0){
5680 // if we're playing with a hacked weapons.tbl (on PXO)
5681 notify_of_hacked_weapons_tbl = 0;
5682 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5683 if(!Game_weapons_tbl_valid){
5684 notify_of_hacked_weapons_tbl = 1;
5687 if(Netgame.flags & NG_FLAG_HACKED_WEAPONS_TBL){
5688 notify_of_hacked_weapons_tbl = 1;
5691 if(!MULTI_IS_TRACKER_GAME){
5692 notify_of_hacked_weapons_tbl = 0;
5694 if(notify_of_hacked_weapons_tbl){
5695 if(popup(PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON, 2, XSTR("&Back", 995), XSTR("&Continue", 780), XSTR("You or the server you are playing on has a hacked weapons.tbl. Your stats will not be updated on PXO", 1052)) <= 0){
5700 // if any of the players have hacked data
5702 for(idx=0; idx<MAX_PLAYERS; idx++){
5703 // look for hacked players
5704 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAXOR)){
5708 // message everyone - haha
5709 if(Net_players[idx].player != NULL){
5710 SDL_snprintf(err_string, SDL_arraysize(err_string), "%s %s", Net_players[idx].player->callsign, XSTR("has hacked tables/data", 1271));
5712 SDL_snprintf(err_string, SDL_arraysize(err_string), "somebody %s", XSTR("has hacked tables/data", 1271));
5714 send_game_chat_packet(Net_player, err_string, MULTI_MSG_ALL, NULL, NULL, 1);
5717 // if we found a hacked set of data
5720 if(MULTI_IS_TRACKER_GAME){
5722 // don't allow squad war matches to continue
5723 if(Netgame.type_flags & NG_TYPE_SW){
5725 // if this is squad war, don't allow it to continue
5726 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("One or more players has hacked data files. You cannot play a SquadWar match unless all clients have legal data", 1272));
5731 // otherwise, warn the players that stats will not saved
5733 // if this is squad war, don't allow it to continue
5734 if(popup(PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON, 2, XSTR("&Back", 995), XSTR("&Continue", 780), XSTR("One or more players has hacked data files. If you continue, stats will not be stored at the end of the mission", 1273)) <= 0){
5739 // warn the players that stats will not saved
5740 if(popup(PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON, 2, XSTR("&Back", 995), XSTR("&Continue", 780), XSTR("One or more players has hacked data files. If you continue, stats will not be stored at the end of the mission", 1273)) <= 0){
5745 // non-pxo, just give a notice
5747 if(popup(PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON, 2, XSTR("&Back", 995), XSTR("&Continue", 780), XSTR("One or more players has hacked data files", 1274)) <= 0){
5753 // check to see that we don't have too many observers
5754 observer_count = multi_num_observers();
5755 if(observer_count > Netgame.options.max_observers){
5756 // print up the error string
5757 SDL_snprintf(err_string,SDL_arraysize(err_string),XSTR("There are too many observers in the game\n\nMax : %d\nCurrently %d\n\nPlease dump a few",791),Netgame.options.max_observers,observer_count);
5759 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, err_string);
5763 // check to see that we have a valid # of players for the the # of ships in the game
5764 player_count = multi_num_players();
5765 if(player_count > Netgame.max_players){
5766 // print up the error string
5767 SDL_snprintf(err_string,SDL_arraysize(err_string),XSTR("There are too many players in the game\n\nMax : %d\nCurrently %d\n\nPlease dump a few", 792), Netgame.max_players,player_count);
5769 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, err_string);
5773 // check to see if teams are assigned properly in a team vs. team situation
5774 if(Netgame.type_flags & NG_TYPE_TEAM){
5775 if(!multi_team_ok_to_commit()){
5776 gamesnd_play_iface(SND_GENERAL_FAIL);
5777 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("Teams and/or team captains are not assigned properly", 793));
5783 if(!multi_create_verify_cds()){
5784 gamesnd_play_iface(SND_GENERAL_FAIL);
5786 #ifdef MULTIPLAYER_BETA_BUILD
5787 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, "You need 1 CD for every player!");
5789 #ifdef DVD_MESSAGE_HACK
5790 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You need 1 DVD for every 4 players!", 794));
5792 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You need 1 CD for every 4 players!", 794));
5798 // if we're playing on the tracker
5799 if(MULTI_IS_TRACKER_GAME){
5800 #ifdef PXO_CHECK_VALID_MISSIONS
5801 if((Multi_create_file_list == Multi_create_mission_list) && (Multi_create_file_list[abs_index].valid_status != MVALID_STATUS_VALID)){
5802 if(popup(PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON, 2, XSTR("&Back", 995), XSTR("&Continue", 780), XSTR("You have selected a mission which is either invalid or unknown to PXO. Your stats will not be saved if you continue",996)) <= 0){
5809 if(!(Netgame.type_flags & NG_TYPE_SW)){
5810 // if he is playing by himself, tell him stats will not be accepted
5811 if(multi_num_players() == 1){
5812 if(popup(PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG, 2, XSTR("&Back", 995), XSTR("&Continue", 780), XSTR("Warning\n\nIf you start a PXO mission by yourself, your stats will not be updated", 997)) <= 0){
5820 return multi_sw_ok_to_commit();
5828 int multi_create_verify_cds()
5830 int player_count = multi_num_players();
5834 // count how many cds we have
5836 for(idx=0;idx<MAX_PLAYERS;idx++){
5837 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAS_CD)){
5842 // for the beta, everyone must have a CD
5843 #ifdef MULTIPLAYER_BETA_BUILD
5844 if(multi_cd_count < player_count){
5848 // determine if we have enough
5849 float ratio = (float)player_count / (float)multi_cd_count;
5850 // greater than a 4 to 1 ratio
5856 // we meet the conditions
5860 // returns an index into Multi_create_mission_list
5861 int multi_create_lookup_mission(char *fname)
5865 for(idx=0; idx<Multi_create_mission_count; idx++){
5866 if(!SDL_strcasecmp(fname, Multi_create_mission_list[idx].filename)){
5871 // couldn't find the mission
5875 // returns an index into Multi_create_campaign_list
5876 int multi_create_lookup_campaign(char *fname)
5880 for(idx=0; idx<Multi_create_campaign_count; idx++){
5881 if(!SDL_strcasecmp(fname, Multi_create_campaign_list[idx].filename)){
5886 // couldn't find the campaign
5890 void multi_create_refresh_pxo()
5892 // delete mvalid.cfg if it exists
5893 cf_delete(MULTI_VALID_MISSION_FILE, CF_TYPE_DATA);
5895 // refresh missions from the tracker
5896 multi_update_valid_missions();
5899 void multi_create_sw_clicked()
5902 netgame_info ng_temp;
5905 int file_index = multi_create_select_to_index(Multi_create_list_select);
5907 // either a temporary netgame or the real one
5908 if(MULTIPLAYER_MASTER){
5915 // maybe switch squad war off
5916 if(!Multi_create_sw_checkbox.checked()){
5917 // if the mission selected is a coop mission, go back to coop mode
5918 SDL_assert(file_index != -1);
5919 if(file_index == -1){
5920 ng->type_flags = NG_TYPE_COOP;
5922 if(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_TEAMS){
5923 ng->type_flags = NG_TYPE_TVT;
5924 } else if(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_DOGFIGHT){
5925 ng->type_flags = NG_TYPE_DOGFIGHT;
5927 ng->type_flags = NG_TYPE_COOP;
5930 // switch squad war on
5932 SDL_assert(file_index != -1);
5933 if((file_index == -1) || !(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_TEAMS)){
5934 Multi_create_sw_checkbox.set_state(0);
5936 // at this point we know its safe to switch squad war on
5937 ng->type_flags = NG_TYPE_SW;
5942 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5944 send_netgame_update_packet();
5946 // update all machines about stuff like respawns, etc.
5947 multi_options_update_netgame();
5949 // on the standalone
5951 // standalone will take care of polling the usertracker
5952 multi_options_update_mission(ng, Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ? 1 : 0);
5958 // -------------------------------------------------------------------------------------------------------------
5960 // MULTIPLAYER HOST OPTIONS SCREEN
5963 #define MULTI_HO_NUM_BUTTONS 12
5964 #define MULTI_HO_NUM_RADIO_BUTTONS 10
5968 #define MULTI_HO_PALETTE "InterfacePalette"
5970 static const char *Multi_ho_bitmap_fname[GR_NUM_RESOLUTIONS] = {
5971 "MultiHost", // GR_640
5972 "2_MultiHost" // GR_1024
5975 static const char *Multi_ho_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
5976 "MultiHost-M", // GR_640
5977 "2_MultiHost-M" // GR_1024
5981 UI_WINDOW Multi_ho_window; // the window object for the join screen
5982 UI_INPUTBOX Multi_ho_respawns; // the # of respawns allowed in the game
5983 UI_INPUTBOX Multi_ho_time_limit; // mission time limit
5984 UI_INPUTBOX Multi_ho_voice_wait; // wait time between tokens
5985 UI_INPUTBOX Multi_ho_kill_limit; // kill limit in a furball mission
5986 UI_INPUTBOX Multi_ho_obs; // # of observers we'll allow
5987 int Multi_ho_bitmap; // the background bitmap
5989 // constants for coordinate lookup
5990 #define MULTI_HO_X_COORD 0
5991 #define MULTI_HO_Y_COORD 1
5992 #define MULTI_HO_W_COORD 2
5993 #define MULTI_HO_H_COORD 3
5994 #define MULTI_HO_TEXT_X_COORD 4
5995 #define MULTI_HO_TEXT_Y_COORD 5
5998 #define MULTI_HO_MSG_RANK 0 // highest ranking players can do messaging
5999 #define MULTI_HO_MSG_LEADER 1 // wing/team leaders can do messaging
6000 #define MULTI_HO_MSG_ANY 2 // any player can do messaging
6001 #define MULTI_HO_MSG_HOST 3 // only the host can do messaging
6002 #define MULTI_HO_END_RANK 4 // highest rank can and host can end mission
6003 #define MULTI_HO_END_LEADER 5 // wing/team leaders and host can end the mission
6004 #define MULTI_HO_END_ANY 6 // any player can end the mission
6005 #define MULTI_HO_END_HOST 7 // only host can end the mission
6006 #define MULTI_HO_VOICE_ON 8 // voice toggled on
6007 #define MULTI_HO_VOICE_OFF 9 // voice toggled off
6008 #define MULTI_HO_HOST_MODIFIES 10 // only the host or team captains can modify ships/weapons in briefing
6009 #define MULTI_HO_ACCEPT 11 // accept button
6011 ui_button_info Multi_ho_buttons[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_BUTTONS] = {
6014 // who is allowed to message
6015 ui_button_info("MH_00", 13, 157, -1, -1, 0), // highest rank
6016 ui_button_info("MH_01", 13, 179, -1, -1, 1), // team/wing leader
6017 ui_button_info("MH_02", 13, 200, -1, -1, 2), // any
6018 ui_button_info("MH_03", 13, 223, -1, -1, 3), // host
6020 // who is allowed to end the mission
6021 ui_button_info("MH_09", 13, 273, -1, -1, 9), // highest rank
6022 ui_button_info("MH_10", 13, 295, -1, -1, 10), // team/wing leader
6023 ui_button_info("MH_11", 13, 317, -1, -1, 11), // any
6024 ui_button_info("MH_12", 13, 339, -1, -1, 12), // host
6026 // voice on/off button
6027 ui_button_info("MH_14", 396, 156, -1, -1, 14),
6028 ui_button_info("MH_15", 453, 156, -1, -1, 15),
6030 // host modifies ships
6031 ui_button_info("MH_19", 215, 410, -1, -1, 19),
6034 ui_button_info("MH_18", 560, 411, -1, -1, 18),
6036 // who is allowed to message
6037 ui_button_info("MH_00", 3, 160, 46, 166, 0), // highest rank
6038 ui_button_info("MH_01", 3, 179, 46, 185, 1), // team/wing leader
6039 ui_button_info("MH_02", 3, 196, 46, 203, 2), // any
6040 ui_button_info("MH_03", 3, 214, 46, 220, 3), // host
6042 // who is allowed to end the mission
6043 ui_button_info("MH_04", 3, 257, 46, 265, 4), // highest rank
6044 ui_button_info("MH_05", 3, 276, 46, 283, 5), // team/wing leader
6045 ui_button_info("MH_06", 3, 294, 46, 300, 6), // any
6046 ui_button_info("MH_07", 3, 311, 46, 317, 7), // host
6048 // voice on/off button
6049 ui_button_info("MH_09", 542, 158, 545, 185, 9),
6050 ui_button_info("MH_10", 598, 158, 604, 185, 10),
6052 // host modifies ships
6053 ui_button_info("MH_13", 542, 377, 437, 363, 13),
6056 ui_button_info("MH_14", 572, 428, 580, 414, 14),
6060 // who is allowed to message
6061 ui_button_info("2_MH_00", 5, 256, 73, 269, 0), // highest rank
6062 ui_button_info("2_MH_01", 5, 286, 73, 297, 1), // team/wing leader
6063 ui_button_info("2_MH_02", 5, 314, 73, 325, 2), // any
6064 ui_button_info("2_MH_03", 5, 341, 73, 352, 3), // host
6066 // who is allowed to end the mission
6067 ui_button_info("2_MH_04", 5, 412, 73, 425, 4), // highest rank
6068 ui_button_info("2_MH_05", 5, 442, 73, 452, 5), // team/wing leader
6069 ui_button_info("2_MH_06", 5, 470, 73, 480, 6), // any
6070 ui_button_info("2_MH_07", 5, 497, 73, 508, 7), // host
6072 // voice on/off button
6073 ui_button_info("2_MH_09", 867, 253, 872, 296, 9),
6074 ui_button_info("2_MH_10", 957, 253, 966, 296, 10),
6076 // host modifies ships
6077 ui_button_info("2_MH_13", 867, 603, 784, 581, 13),
6080 ui_button_info("2_MH_14", 916, 685, 925, 665, 14),
6083 UI_XSTR Multi_ho_text[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_BUTTONS] = {
6085 // not needed for FS1
6087 {"Highest rank", 1280, 46, 166, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_RANK].button},
6088 {"Team / wing-leader", 1281, 46, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_LEADER].button},
6089 {"Any", 1282, 46, 203, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_ANY].button},
6090 {"Host", 1283, 46, 220, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_HOST].button},
6091 {"Highest rank", 1280, 46, 265, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_RANK].button},
6092 {"Team / wing-leader", 1281, 46, 283, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_LEADER].button},
6093 {"Any", 1282, 46, 300, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_ANY].button},
6094 {"Host", 1283, 46, 317, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_HOST].button},
6095 {"On", 1285, 545, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_VOICE_ON].button},
6096 {"Off", 1286, 604, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_VOICE_OFF].button},
6097 {"Host modifies ships", 1287, 437, 363, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_HOST_MODIFIES].button},
6098 {"Exit", 1417, 572, 418, UI_XSTR_COLOR_PINK, -1, &Multi_ho_buttons[0][MULTI_HO_ACCEPT].button},
6102 // not needed for FS1
6104 {"Highest rank", 1280, 62, 269, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_RANK].button},
6105 {"Team / wing-leader", 1281, 62, 297, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_LEADER].button},
6106 {"Any", 1282, 62, 325, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_ANY].button},
6107 {"Host", 1283, 62, 352, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_HOST].button},
6108 {"Highest rank", 1280, 62, 425, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_RANK].button},
6109 {"Team / wing-leader", 1281, 62, 452, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_LEADER].button},
6110 {"Any", 1282, 62, 480, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_ANY].button},
6111 {"Host", 1283, 62, 508, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_HOST].button},
6112 {"On", 1285, 877, 294, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_VOICE_ON].button},
6113 {"Off", 1286, 967, 293, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_VOICE_OFF].button},
6114 {"Host modifies ships", 1287, 869, 589, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_HOST_MODIFIES].button},
6115 {"Exit", 1417, 953, 672, UI_XSTR_COLOR_PINK, -1, &Multi_ho_buttons[1][MULTI_HO_ACCEPT].button},
6120 // radio button controls
6121 #define MULTI_HO_NUM_RADIO_GROUPS 3
6122 #define MULTI_HO_MSG_GROUP 0 // group dealing with squadmate messaging
6123 #define MULTI_HO_END_GROUP 1 // group dealing with ending the mission
6124 #define MULTI_HO_VOICE_GROUP 2 // group dealing with voice stuff
6125 int Multi_ho_radio_groups[MULTI_HO_NUM_RADIO_GROUPS] = { // currently selected button in the radio button group
6128 int Multi_ho_radio_info[MULTI_HO_NUM_RADIO_BUTTONS][3] = { // info related to each of the radio buttons themselves
6129 // { group #, value, button id# }
6130 {0, 0, 0}, // highest ranking players can do messaging
6131 {0, 1, 1}, // wing/team leaders can do messaging
6132 {0, 2, 2}, // any player can do messaging
6133 {0, 3, 3}, // only host can do messaging
6134 {1, 0, 4}, // highest rank and host can end the mission
6135 {1, 1, 5}, // team/wing leader can end the mission
6136 {1, 2, 6}, // any player can end the mission
6137 {1, 3, 7}, // only the host can end the mission
6138 {2, 0, 8}, // voice toggled on
6139 {2, 1, 9} // voice toggled off
6143 #define MULTI_HO_NUM_SLIDERS 3
6144 #define MULTI_HO_SLIDER_VOICE_QOS 0 // voice quality of sound
6145 #define MULTI_HO_SLIDER_VOICE_DUR 1 // max duration of voice recording
6146 #define MULTI_HO_SLIDER_SKILL 2 // skill level
6148 const char *filename;
6153 UI_DOT_SLIDER_NEW slider; // because we have a class inside this struct, we need the constructor below..
6155 ho_sliders(const char *name, int x1, int y1, int xt1, int yt1, int h, int _dot_w, int _dots) : filename(name), x(x1), y(y1), xt(xt1), yt(yt1), hotspot(h), dot_w(_dot_w), dots(_dots){}
6157 ho_sliders Multi_ho_sliders[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_SLIDERS] = {
6160 ho_sliders("MH_16", 396, 210, -1, -1, 16, 19, 10), // voice qos
6161 ho_sliders("MH_17", 396, 263, -1, -1, 17, 19, 10), // voice duration
6162 ho_sliders("MH_13", 10, 387, -1, -1, 13, 36, 5), // skill level
6164 ho_sliders("MH_11", 428, 214, 437, 199, 11, 19, 10), // voice qos
6165 ho_sliders("MH_12", 428, 261, 437, 246, 12, 19, 10), // voice duration
6166 ho_sliders("MH_08", 237, 454, 230, 411, 8, 36, 5), // skill level
6170 ho_sliders("2_MH_11", 684, 343, 690, 323, 11, 32, 10), // voice qos
6171 ho_sliders("2_MH_12", 685, 418, 837, 468, 12, 32, 10), // voice duration
6172 ho_sliders("2_MH_08", 379, 727, 369, 663, 8, 60, 5), // skill level
6176 int Multi_ho_mission_respawn;
6178 int Multi_ho_host_modifies;
6180 // whether or not any of the inputboxes on this screen had focus last frame
6181 int Multi_ho_lastframe_input = 0;
6183 // game information text areas
6187 #define MULTI_HO_NUM_TITLES 14
6189 UI_XSTR Multi_ho_titles[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_TITLES] = {
6191 { "AI Orders", 1289, 32, 144, UI_XSTR_COLOR_GREEN, -1, NULL },
6192 { "End Mission", 1290, 32, 242, UI_XSTR_COLOR_GREEN, -1, NULL },
6193 { "Time Limit", 1291, 32, 347, UI_XSTR_COLOR_GREEN, -1, NULL },
6194 { "Min", 1292, 74, 362, UI_XSTR_COLOR_GREEN, -1, NULL },
6195 { "Respawn Limit", 1288, 32, 378, UI_XSTR_COLOR_GREEN, -1, NULL },
6196 { "Kill Limit", 1293, 32, 409, UI_XSTR_COLOR_GREEN, -1, NULL },
6197 { "Observers", 1294, 32, 441, UI_XSTR_COLOR_GREEN, -1, NULL },
6198 { "Skill Level", 1284, 230, 411, UI_XSTR_COLOR_GREEN, -1, NULL },
6199 { "Voice Transmission", 1295, 437, 144, UI_XSTR_COLOR_GREEN, -1, NULL },
6200 { "Voice Quality", 1296, 437, 199, UI_XSTR_COLOR_GREEN, -1, NULL },
6201 { "Message Duration", 1297, 437, 246, UI_XSTR_COLOR_GREEN, -1, NULL },
6202 { "sec", 1522, 523, 292, UI_XSTR_COLOR_GREEN, -1, NULL },
6203 { "sec", 1523, 523, 332, UI_XSTR_COLOR_GREEN, -1, NULL },
6204 { "Voice Wait", 1298, 437, 313, UI_XSTR_COLOR_GREEN, -1, NULL },
6207 { "AI Orders", 1289, 48, 238, UI_XSTR_COLOR_GREEN, -1, NULL },
6208 { "End Mission", 1290, 48, 394, UI_XSTR_COLOR_GREEN, -1, NULL },
6209 { "Time Limit", 1291, 50, 568, UI_XSTR_COLOR_GREEN, -1, NULL },
6210 { "Min", 1292, 119, 581, UI_XSTR_COLOR_GREEN, -1, NULL },
6211 { "Respawn Limit", 1288, 50, 618, UI_XSTR_COLOR_GREEN, -1, NULL },
6212 { "Kill Limit", 1293, 50, 668, UI_XSTR_COLOR_GREEN, -1, NULL },
6213 { "Observers", 1294, 50, 718, UI_XSTR_COLOR_GREEN, -1, NULL },
6214 { "Skill Level", 1284, 398, 670, UI_XSTR_COLOR_GREEN, -1, NULL },
6215 { "Voice Transmission", 1295, 869, 239, UI_XSTR_COLOR_GREEN, -1, NULL },
6216 { "Voice Quality", 1296, 690, 331, UI_XSTR_COLOR_GREEN, -1, NULL },
6217 { "Message Duration", 1297, 690, 405, UI_XSTR_COLOR_GREEN, -1, NULL },
6218 { "sec", 1522, 837, 467, UI_XSTR_COLOR_GREEN, -1, NULL },
6219 { "sec", 1523, 837, 534, UI_XSTR_COLOR_GREEN, -1, NULL },
6220 { "Voice Wait", 1298, 742, 510, UI_XSTR_COLOR_GREEN, -1, NULL },
6225 // mission time limit input box
6226 int Ho_time_coords[GR_NUM_RESOLUTIONS][4] = {
6239 // furball kill limit input box
6240 int Ho_kill_coords[GR_NUM_RESOLUTIONS][4] = {
6253 // voice recording duration text display area
6254 int Ho_vd_coords[GR_NUM_RESOLUTIONS][4] = {
6267 // voice token wait input box
6268 int Ho_vw_coords[GR_NUM_RESOLUTIONS][6] = {
6281 // observer count input box
6282 int Ho_obs_coords[GR_NUM_RESOLUTIONS][4] = {
6295 // skill text description area
6296 int Ho_st_coords[GR_NUM_RESOLUTIONS][4] = {
6309 // respawn input box
6310 int Ho_rsp_coords[GR_NUM_RESOLUTIONS][6] = {
6323 // respawn max text area
6324 int Ho_max_rsp_coords[GR_NUM_RESOLUTIONS][2] = {
6337 // maximum values for various input boxes (to notify user of overruns)
6338 #define MULTI_HO_MAX_TIME_LIMIT 500
6339 #define MULTI_HO_MAX_TOKEN_WAIT 5
6340 #define MULTI_HO_MAX_KILL_LIMIT 9999
6341 #define MULTI_HO_MAX_OBS 4
6343 // LOCAL function definitions
6344 void multi_ho_check_buttons();
6345 void multi_ho_button_pressed(int n);
6346 void multi_ho_draw_radio_groups();
6347 void multi_ho_accept_hit();
6348 void multi_ho_get_options();
6349 void multi_ho_apply_options();
6350 void multi_ho_display_record_time();
6351 int multi_ho_check_values();
6352 void multi_ho_check_focus();
6353 void multi_ho_blit_max_respawns();
6354 void multi_ho_display_skill_level();
6356 void multi_host_options_init()
6360 // create the interface window
6361 Multi_ho_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
6362 Multi_ho_window.set_mask_bmap(Multi_ho_bitmap_mask_fname[gr_screen.res]);
6364 // load the background bitmap
6365 Multi_ho_bitmap = bm_load(Multi_ho_bitmap_fname[gr_screen.res]);
6366 if(Multi_ho_bitmap < 0){
6367 // we failed to load the bitmap - this is very bad
6371 // initialize the common notification messaging
6372 multi_common_notify_init();
6374 // use the common interface palette
6375 multi_common_set_palette();
6377 // create the interface buttons
6378 for(idx=0;idx<MULTI_HO_NUM_BUTTONS;idx++){
6379 // create the object
6380 Multi_ho_buttons[gr_screen.res][idx].button.create(&Multi_ho_window, "", Multi_ho_buttons[gr_screen.res][idx].x, Multi_ho_buttons[gr_screen.res][idx].y, 1, 1, 0, 1);
6382 // set the sound to play when highlighted
6383 Multi_ho_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
6385 // set the ani for the button
6386 Multi_ho_buttons[gr_screen.res][idx].button.set_bmaps(Multi_ho_buttons[gr_screen.res][idx].filename);
6388 // set the hotspot, ignoring the skill level button
6389 Multi_ho_buttons[gr_screen.res][idx].button.link_hotspot(Multi_ho_buttons[gr_screen.res][idx].hotspot);
6393 Multi_ho_window.add_XSTR(&Multi_ho_text[gr_screen.res][idx]);
6399 for(idx=0; idx<MULTI_HO_NUM_TITLES; idx++){
6400 Multi_ho_window.add_XSTR(&Multi_ho_titles[gr_screen.res][idx]);
6404 // create the interface sliders
6405 for(idx=0; idx<MULTI_HO_NUM_SLIDERS; idx++){
6406 // create the object
6407 Multi_ho_sliders[gr_screen.res][idx].slider.create(&Multi_ho_window, Multi_ho_sliders[gr_screen.res][idx].x, Multi_ho_sliders[gr_screen.res][idx].y, Multi_ho_sliders[gr_screen.res][idx].dots, Multi_ho_sliders[gr_screen.res][idx].filename, Multi_ho_sliders[gr_screen.res][idx].hotspot, NULL, -1, -1, -1, NULL, -1, -1, -1, Multi_ho_sliders[gr_screen.res][idx].dot_w);
6410 // create the respawn count input box
6411 Multi_ho_respawns.create(&Multi_ho_window,Ho_rsp_coords[gr_screen.res][MULTI_HO_X_COORD],Ho_rsp_coords[gr_screen.res][MULTI_HO_Y_COORD],Ho_rsp_coords[gr_screen.res][MULTI_HO_W_COORD],6,"",UI_INPUTBOX_FLAG_ESC_FOC | UI_INPUTBOX_FLAG_INVIS | UI_INPUTBOX_FLAG_NO_LETTERS,-1,&Color_bright);
6412 // if we're in campaign mode, disable it
6413 if(Netgame.campaign_mode == MP_CAMPAIGN){
6414 Multi_ho_respawns.set_text(XSTR("NA",795)); // [[ Not applicable ]]
6415 Multi_ho_respawns.disable();
6417 Multi_ho_respawns.set_valid_chars("0123456789");
6420 // create the time limit input box
6421 Multi_ho_time_limit.create(&Multi_ho_window, Ho_time_coords[gr_screen.res][MULTI_HO_X_COORD], Ho_time_coords[gr_screen.res][MULTI_HO_Y_COORD], Ho_time_coords[gr_screen.res][MULTI_HO_W_COORD], 3, "", UI_INPUTBOX_FLAG_ESC_FOC | UI_INPUTBOX_FLAG_INVIS | UI_INPUTBOX_FLAG_NO_LETTERS, -1, &Color_bright);
6422 Multi_ho_time_limit.set_valid_chars("-0123456789");
6424 // create the voice token wait input box
6425 Multi_ho_voice_wait.create(&Multi_ho_window, Ho_vw_coords[gr_screen.res][MULTI_HO_X_COORD], Ho_vw_coords[gr_screen.res][MULTI_HO_Y_COORD], Ho_vw_coords[gr_screen.res][MULTI_HO_W_COORD], 1, "", UI_INPUTBOX_FLAG_ESC_FOC | UI_INPUTBOX_FLAG_INVIS | UI_INPUTBOX_FLAG_NO_LETTERS, -1, &Color_bright);
6426 Multi_ho_voice_wait.set_valid_chars("01243456789");
6428 // create the furball kill limit input box
6429 Multi_ho_kill_limit.create(&Multi_ho_window, Ho_kill_coords[gr_screen.res][MULTI_HO_X_COORD], Ho_kill_coords[gr_screen.res][MULTI_HO_Y_COORD], Ho_kill_coords[gr_screen.res][MULTI_HO_W_COORD], 4, "", UI_INPUTBOX_FLAG_ESC_FOC | UI_INPUTBOX_FLAG_INVIS | UI_INPUTBOX_FLAG_NO_LETTERS, -1, &Color_bright);
6430 Multi_ho_kill_limit.set_valid_chars("0123456789");
6432 // create the observer limit input box
6433 Multi_ho_obs.create(&Multi_ho_window, Ho_obs_coords[gr_screen.res][MULTI_HO_X_COORD], Ho_obs_coords[gr_screen.res][MULTI_HO_Y_COORD], Ho_obs_coords[gr_screen.res][MULTI_HO_W_COORD], 1, "", UI_INPUTBOX_FLAG_ESC_FOC | UI_INPUTBOX_FLAG_INVIS | UI_INPUTBOX_FLAG_NO_LETTERS, -1, &Color_bright);
6434 Multi_ho_obs.set_valid_chars("01234");
6436 // load in the current netgame defaults
6437 multi_ho_get_options();
6439 // whether or not any of the inputboxes on this screen had focus last frame
6440 Multi_ho_lastframe_input = 0;
6442 // get the # of respawns for the currently selected mission (if any)
6443 if(Multi_create_list_select != -1){
6444 int abs_index = multi_create_select_to_index(Multi_create_list_select);
6446 // if he has a valid mission selected
6448 Multi_ho_mission_respawn = (int)Multi_create_file_list[abs_index].respawn;
6450 Multi_ho_mission_respawn = -1;
6453 Multi_ho_mission_respawn = -1;
6457 void multi_ho_update_sliders()
6459 // game skill slider
6460 if (Game_skill_level != Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos) {
6461 if ( !(Netgame.type_flags & NG_TYPE_TEAM) ){
6462 Game_skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6463 gamesnd_play_iface(SND_USER_SELECT);
6465 Game_skill_level = NUM_SKILL_LEVELS / 2;
6469 // get the voice qos options
6470 if (Netgame.options.voice_qos != (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1)) {
6471 Netgame.options.voice_qos = (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1);
6472 gamesnd_play_iface(SND_USER_SELECT);
6475 // get the voice duration options
6476 if (Netgame.options.voice_record_time != (int)(0.5f * (float)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 1000.0f)) {
6477 Netgame.options.voice_record_time = (int)(0.5f * (float)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 1000.0f);
6478 gamesnd_play_iface(SND_USER_SELECT);
6483 void multi_host_options_do()
6488 k = Multi_ho_window.process();
6491 // process any keypresses
6494 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
6497 case KEY_CTRLED + SDLK_RETURN :
6498 gamesnd_play_iface(SND_COMMIT_PRESSED);
6499 multi_ho_accept_hit();
6503 // process any button clicks
6504 multi_ho_check_buttons();
6506 // update the sliders
6507 multi_ho_update_sliders();
6509 // make sure that the chatbox inputbox and any inputbox on this screen are mutually exclusive in terms of focus
6510 multi_ho_check_focus();
6512 // draw the background, etc
6514 GR_MAYBE_CLEAR_RES(Multi_ho_bitmap);
6515 if(Multi_ho_bitmap != -1){
6516 gr_set_bitmap(Multi_ho_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
6519 Multi_ho_window.draw();
6521 // draw all the radio buttons properly
6522 multi_ho_draw_radio_groups();
6524 // display any pending notification messages
6525 multi_common_notify_do();
6527 // display the voice record time settings
6528 multi_ho_display_record_time();
6530 // maybe display the max # of respawns next to the respawn input box
6531 multi_ho_blit_max_respawns();
6533 // blit the proper skill level
6534 multi_ho_display_skill_level();
6536 // blit the "host modifies button"
6537 if(Multi_ho_host_modifies){
6538 Multi_ho_buttons[gr_screen.res][MULTI_HO_HOST_MODIFIES].button.draw_forced(2);
6541 // process and show the chatbox thingie
6545 Multi_ho_window.draw_tooltip();
6547 // display the voice status indicator
6548 multi_common_voice_display_status();
6554 void multi_host_options_close()
6556 // unload any bitmaps
6557 if(!bm_unload(Multi_ho_bitmap)){
6558 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_ho_bitmap_fname[gr_screen.res]));
6561 // destroy the UI_WINDOW
6562 Multi_ho_window.destroy();
6565 void multi_ho_check_buttons()
6568 for(idx=0;idx<MULTI_HO_NUM_BUTTONS;idx++){
6569 // we only really need to check for one button pressed at a time, so we can break after
6571 if(Multi_ho_buttons[gr_screen.res][idx].button.pressed()){
6572 multi_ho_button_pressed(idx);
6578 void multi_ho_button_pressed(int n)
6580 int radio_index,idx;
6581 int x_pixel,y_pixel;
6583 // get the pixel position of the click
6584 Multi_ho_buttons[gr_screen.res][n].button.get_mouse_pos(&x_pixel,&y_pixel);
6587 // clicked on the accept button
6588 case MULTI_HO_ACCEPT:
6589 gamesnd_play_iface(SND_COMMIT_PRESSED);
6590 multi_ho_accept_hit();
6593 // clicked on the host/captains only modify button
6594 case MULTI_HO_HOST_MODIFIES:
6595 // toggle it on or off
6596 Multi_ho_host_modifies = !Multi_ho_host_modifies;
6597 gamesnd_play_iface(SND_USER_SELECT);
6601 // look through the radio buttons and see which one this corresponds to
6603 for(idx=0;idx<MULTI_HO_NUM_RADIO_BUTTONS;idx++){
6604 if(Multi_ho_radio_info[idx][2] == n){
6609 SDL_assert(radio_index != -1);
6611 // check to see if a radio button was pressed
6612 if(radio_index < MULTI_HO_NUM_RADIO_BUTTONS){
6613 // see if this value is already picked for this radio group
6614 if(Multi_ho_radio_groups[Multi_ho_radio_info[radio_index][0]] != Multi_ho_radio_info[radio_index][1]){
6615 gamesnd_play_iface(SND_USER_SELECT);
6616 Multi_ho_radio_groups[Multi_ho_radio_info[radio_index][0]] = Multi_ho_radio_info[radio_index][1];
6618 gamesnd_play_iface(SND_GENERAL_FAIL);
6623 void multi_ho_draw_radio_groups()
6627 // go through each item and draw it if it is the selected button in its respective group
6628 for(idx=0;idx<MULTI_HO_NUM_RADIO_BUTTONS;idx++){
6629 /// if this button is the currently selected one in its group
6630 if(Multi_ho_radio_info[idx][1] == Multi_ho_radio_groups[Multi_ho_radio_info[idx][0]]){
6631 Multi_ho_buttons[gr_screen.res][Multi_ho_radio_info[idx][2]].button.draw_forced(2);
6636 void multi_ho_accept_hit()
6640 // check the values in the input boxes
6641 if(!multi_ho_check_values()){
6645 // zero out the netgame flags
6648 // set default options
6649 Netgame.options.flags = (MSO_FLAG_INGAME_XFER | MSO_FLAG_ACCEPT_PIX);
6651 // set the squadmate messaging flags
6652 switch(Multi_ho_radio_groups[MULTI_HO_MSG_GROUP]){
6654 Netgame.options.squad_set = MSO_SQUAD_RANK;
6657 Netgame.options.squad_set = MSO_SQUAD_LEADER;
6660 Netgame.options.squad_set = MSO_SQUAD_ANY;
6663 Netgame.options.squad_set = MSO_SQUAD_HOST;
6669 // set the end mission flags
6670 switch(Multi_ho_radio_groups[MULTI_HO_END_GROUP]){
6672 Netgame.options.endgame_set = MSO_END_RANK;
6675 Netgame.options.endgame_set = MSO_END_LEADER;
6678 Netgame.options.endgame_set = MSO_END_ANY;
6681 Netgame.options.endgame_set = MSO_END_HOST;
6687 // set the voice toggle
6688 switch(Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP]){
6690 Netgame.options.flags &= ~(MSO_FLAG_NO_VOICE);
6693 Netgame.options.flags |= MSO_FLAG_NO_VOICE;
6699 // get the voice qos options
6700 Netgame.options.voice_qos = (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1);
6702 // get the voice duration options
6703 Netgame.options.voice_record_time = (int)(0.5f * (float)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 1000.0f);
6705 // set the skill level. If in team vs. team mode, preserve the old setting before saving
6706 // the pilot file. I'll bet that this doesn't work though because the pilot file gets
6707 // written in a bunch of locations....sigh.
6708 if ( !(Netgame.type_flags & NG_TYPE_TEAM) ){
6709 Game_skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6711 Game_skill_level = NUM_SKILL_LEVELS / 2;
6714 // set the netgame respawn count
6715 // maybe warn the user that respawns will not be used for a campaign mission
6716 if(Netgame.campaign_mode == MP_SINGLE){
6717 Multi_ho_respawns.get_text(resp_str);
6718 uint temp_respawn = (uint)atoi(resp_str);
6719 // if he currently has no mission selected, let the user set any # of respawns
6720 if((int)temp_respawn > Multi_ho_mission_respawn){
6721 if(Multi_ho_mission_respawn == -1){
6722 Netgame.respawn = temp_respawn;
6723 Netgame.options.respawn = temp_respawn;
6725 // this should have been taken care of by the interface code
6730 Netgame.options.respawn = temp_respawn;
6731 Netgame.respawn = temp_respawn;
6735 // get the mission time limit
6736 Multi_ho_time_limit.get_text(resp_str);
6737 int temp_time = atoi(resp_str);
6739 Netgame.options.mission_time_limit = fl2f(-1.0f);
6740 } else if(temp_time > MULTI_HO_MAX_TIME_LIMIT){
6743 Netgame.options.mission_time_limit = fl2f(60.0f * (float)temp_time);
6746 // get observer count options
6747 Multi_ho_obs.get_text(resp_str);
6748 int temp_obs = atoi(resp_str);
6749 if(temp_obs > MULTI_HO_MAX_OBS){
6752 Netgame.options.max_observers = (ubyte)temp_obs;
6754 // get the furball kill limit
6755 Multi_ho_kill_limit.get_text(resp_str);
6756 int temp_kills = atoi(resp_str);
6757 if(temp_kills > MULTI_HO_MAX_KILL_LIMIT){
6760 Netgame.options.kill_limit = temp_kills;
6762 // get the token wait limit
6763 Multi_ho_voice_wait.get_text(resp_str);
6764 int temp_wait = atoi(resp_str);
6765 if(temp_wait > MULTI_HO_MAX_TOKEN_WAIT){
6768 Netgame.options.voice_token_wait = (temp_wait * 1000);
6770 // set the netgame option
6771 Netgame.options.skill_level = (ubyte)Game_skill_level;
6773 // get whether we're in host/captains only modify mode
6774 Netgame.options.flags &= ~(MSO_FLAG_SS_LEADERS);
6775 if(Multi_ho_host_modifies){
6776 Netgame.options.flags |= MSO_FLAG_SS_LEADERS;
6779 // store these values locally
6780 memcpy(&Player->m_local_options,&Net_player->p_info.options,sizeof(multi_local_options));
6781 memcpy(&Player->m_server_options,&Netgame.options,sizeof(multi_server_options));
6782 write_pilot_file(Player);
6784 // apply any changes in settings (notify everyone of voice qos changes, etc)
6785 multi_ho_apply_options();
6787 // move back to the create game screen
6788 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
6791 void multi_ho_get_options()
6795 // set the squadmate messaging buttons
6796 switch(Netgame.options.squad_set){
6797 case MSO_SQUAD_RANK :
6798 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 0;
6800 case MSO_SQUAD_LEADER:
6801 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 1;
6804 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 2;
6806 case MSO_SQUAD_HOST:
6807 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 3;
6813 // set the mission end buttons
6814 switch(Netgame.options.endgame_set){
6816 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 0;
6818 case MSO_END_LEADER:
6819 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 1;
6822 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 2;
6825 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 3;
6831 // set the voice toggle buttons
6832 if(Netgame.options.flags & MSO_FLAG_NO_VOICE){
6833 Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP] = 1;
6835 Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP] = 0;
6838 // get the voice qos options
6839 SDL_assert((Netgame.options.voice_qos >= 1) && (Netgame.options.voice_qos <= 10));
6840 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos = (Netgame.options.voice_qos - 1);
6842 // get the voice duration options
6843 SDL_assert((Netgame.options.voice_record_time > 0) && (Netgame.options.voice_record_time <= MULTI_VOICE_MAX_TIME));
6844 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos = ((int)((float)Netgame.options.voice_record_time / 500.0f)) - 1;
6846 // get the current skill level
6847 SDL_assert((Game_skill_level >= 0) && (Game_skill_level < NUM_SKILL_LEVELS));
6848 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos = Game_skill_level;
6850 // get the # of observers
6851 memset(resp_str,0,10);
6852 SDL_snprintf(resp_str,SDL_arraysize(resp_str),"%d",Netgame.options.max_observers);
6853 Multi_ho_obs.set_text(resp_str);
6855 // set the respawn count
6856 if(Netgame.campaign_mode == MP_SINGLE){
6857 memset(resp_str,0,10);
6858 SDL_snprintf(resp_str,SDL_arraysize(resp_str),"%d",Netgame.respawn);
6859 Multi_ho_respawns.set_text(resp_str);
6862 // set the mission time limit
6863 memset(resp_str,0,10);
6864 float tl = f2fl(Netgame.options.mission_time_limit);
6865 SDL_snprintf(resp_str,SDL_arraysize(resp_str),"%d",(int)(tl / 60.0f));
6866 Multi_ho_time_limit.set_text(resp_str);
6868 // set the furball kill limit
6869 memset(resp_str,0,10);
6870 SDL_snprintf(resp_str,SDL_arraysize(resp_str),"%d",Netgame.options.kill_limit);
6871 Multi_ho_kill_limit.set_text(resp_str);
6873 // set the token wait time
6874 memset(resp_str,0,10);
6875 SDL_snprintf(resp_str,SDL_arraysize(resp_str),"%d",Netgame.options.voice_token_wait / 1000);
6876 Multi_ho_voice_wait.set_text(resp_str);
6878 // get whether we're in host/captains only modify mode
6879 if(Netgame.options.flags & MSO_FLAG_SS_LEADERS){
6880 Multi_ho_host_modifies = 1;
6882 Multi_ho_host_modifies = 0;
6886 void multi_ho_apply_options()
6888 // if the voice qos or duration has changed, apply the change
6889 multi_voice_maybe_update_vars(Netgame.options.voice_qos,Netgame.options.voice_record_time);
6891 // send an options update
6892 multi_options_update_netgame();
6895 // display the voice record time settings
6896 void multi_ho_display_record_time()
6899 int full_seconds, half_seconds;
6902 memset(time_str,0,30);
6905 full_seconds = (((Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 500) / 1000);
6907 // get the half-seconds
6908 half_seconds = ((((Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 500) % 1000) / 500) * 5;
6910 // format the string
6911 SDL_snprintf(time_str,SDL_arraysize(time_str),"%d.%d",full_seconds,half_seconds);
6912 gr_set_color_fast(&Color_bright);
6913 gr_string(Ho_vd_coords[gr_screen.res][MULTI_HO_X_COORD],Ho_vd_coords[gr_screen.res][MULTI_HO_Y_COORD],time_str);
6916 int multi_ho_check_values()
6920 memset(val_txt,0,255);
6922 // check against respawn settings
6923 if(Multi_ho_mission_respawn != -1){
6924 Multi_ho_respawns.get_text(val_txt);
6925 // if the value is invalid, let the user know
6926 if(atoi(val_txt) > Multi_ho_mission_respawn){
6927 memset(val_txt,0,255);
6928 SDL_snprintf(val_txt,SDL_arraysize(val_txt),XSTR("Warning\nRespawn count in greater than mission specified max (%d)",796),Multi_ho_mission_respawn);
6929 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6934 // check against mission time limit max
6935 Multi_ho_time_limit.get_text(val_txt);
6936 // if the value is invalid, force it to be valid
6937 if(atoi(val_txt) > MULTI_HO_MAX_TIME_LIMIT){
6938 memset(val_txt,0,255);
6939 SDL_snprintf(val_txt,SDL_arraysize(val_txt),XSTR("Warning\nMission time limit is greater than max allowed (%d)",797),MULTI_HO_MAX_TIME_LIMIT);
6940 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6944 // check against max observer limit
6945 Multi_ho_obs.get_text(val_txt);
6946 // if the value is invalid, force it to be valid
6947 if(atoi(val_txt) > MULTI_HO_MAX_OBS){
6948 memset(val_txt,0,255);
6949 SDL_snprintf(val_txt,SDL_arraysize(val_txt),XSTR("Warning\nObserver count is greater than max allowed (%d)",798),MULTI_HO_MAX_OBS);
6950 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6954 // check against furball kill limit
6955 Multi_ho_kill_limit.get_text(val_txt);
6956 // if the value is invalid, force it to be valid
6957 if(atoi(val_txt) > MULTI_HO_MAX_KILL_LIMIT){
6958 memset(val_txt,0,255);
6959 SDL_snprintf(val_txt,SDL_arraysize(val_txt),XSTR("Warning\nMission kill limit is greater than max allowed (%d)",799),MULTI_HO_MAX_KILL_LIMIT);
6960 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6964 // check against the token wait limit
6965 Multi_ho_voice_wait.get_text(val_txt);
6966 if(atoi(val_txt) > MULTI_HO_MAX_TOKEN_WAIT){
6967 memset(val_txt,0,255);
6968 SDL_snprintf(val_txt,SDL_arraysize(val_txt),XSTR("Warning\nvoice wait time is greater than max allowed (%d)",800),MULTI_HO_MAX_TOKEN_WAIT);
6969 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6973 // all values are valid
6977 void multi_ho_check_focus()
6979 // if an inputbox has been pressed (hit enter), lose its focus
6980 if (Multi_ho_respawns.pressed() || Multi_ho_time_limit.pressed() || Multi_ho_voice_wait.pressed() || Multi_ho_kill_limit.pressed() || Multi_ho_obs.pressed()) {
6981 Multi_ho_respawns.clear_focus();
6982 Multi_ho_time_limit.clear_focus();
6983 Multi_ho_voice_wait.clear_focus();
6984 Multi_ho_kill_limit.clear_focus();
6985 Multi_ho_obs.clear_focus();
6986 gamesnd_play_iface(SND_COMMIT_PRESSED);
6987 chatbox_set_focus();
6988 Multi_ho_lastframe_input = 0;
6990 } else if(!Multi_ho_lastframe_input) {
6991 // if we didn't have focus last frame
6992 if(Multi_ho_respawns.has_focus() || Multi_ho_time_limit.has_focus() || Multi_ho_kill_limit.has_focus() || Multi_ho_voice_wait.has_focus() ){
6993 chatbox_lose_focus();
6995 Multi_ho_lastframe_input = 1;
6998 // if we _did_ have focus last frame
7000 // if we no longer have focus on any of the input boxes, set the focus on the chatbox
7001 if(!Multi_ho_respawns.has_focus() && !Multi_ho_time_limit.has_focus() && !Multi_ho_kill_limit.has_focus() && !Multi_ho_voice_wait.has_focus() && !chatbox_has_focus()){
7002 chatbox_set_focus();
7004 // if the chatbox now has focus, clear all focus from our inputboxes
7005 else if (chatbox_has_focus()) {
7006 Multi_ho_respawns.clear_focus();
7007 Multi_ho_time_limit.clear_focus();
7008 Multi_ho_kill_limit.clear_focus();
7009 Multi_ho_voice_wait.clear_focus();
7011 Multi_ho_lastframe_input = 0;
7016 void multi_ho_blit_max_respawns()
7020 // if we're in campaign mode, do nothing
7021 if(Netgame.campaign_mode == MP_CAMPAIGN){
7025 // otherwise blit the max as specified by the current mission file
7026 SDL_snprintf(string,SDL_arraysize(string),"(%d)",Multi_ho_mission_respawn);
7027 gr_set_color_fast(&Color_normal);
7028 gr_string(Ho_max_rsp_coords[gr_screen.res][MULTI_HO_X_COORD], Ho_max_rsp_coords[gr_screen.res][MULTI_HO_Y_COORD], string);
7031 void multi_ho_display_skill_level()
7033 int skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
7036 SDL_assert((skill_level >= 0) && (skill_level < NUM_SKILL_LEVELS));
7037 if((skill_level < 0) || (skill_level >= NUM_SKILL_LEVELS)){
7041 gr_set_color_fast(&Color_bright);
7042 gr_string(Ho_st_coords[gr_screen.res][0], Ho_st_coords[gr_screen.res][1], Skill_level_names(skill_level, 1));
7045 // -------------------------------------------------------------------------------------------------------------
7047 // MULTIPLAYER JOIN SCREEN
7050 #define MULTI_JW_NUM_BUTTONS 8
7054 #define MULTI_JW_PALETTE "InterfacePalette"
7056 static const char *Multi_jw_bitmap_fname[GR_NUM_RESOLUTIONS] = {
7057 "MultiJoinWait", // GR_640
7058 "2_MultiJoinWait" // GR_1024
7061 static const char *Multi_jw_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
7062 "MultiJoinWait-M", // GR_640
7063 "2_MultiJoinWait-M" // GR_1024
7069 #define MJW_SCROLL_PLAYERS_UP 0
7070 #define MJW_SCROLL_PLAYERS_DOWN 1
7073 #define MJW_PILOT_INFO 4
7074 #define MJW_SCROLL_INFO_UP 5
7075 #define MJW_SCROLL_INFO_DOWN 6
7076 #define MJW_CANCEL 7
7078 UI_WINDOW Multi_jw_window; // the window object for the join screen
7079 int Multi_jw_bitmap; // the background bitmap
7081 // constants for coordinate lookup
7082 #define MJW_X_COORD 0
7083 #define MJW_Y_COORD 1
7084 #define MJW_W_COORD 2
7085 #define MJW_H_COORD 3
7087 ui_button_info Multi_jw_buttons[GR_NUM_RESOLUTIONS][MULTI_JW_NUM_BUTTONS] = {
7090 ui_button_info("MJW_00", 0, 50, -1, -1, 0),
7091 ui_button_info("MJW_01", 0, 87, -1, -1, 1),
7092 ui_button_info("MJW_02", 20, 219, -1, -1, 2),
7093 ui_button_info("MJW_03", 73, 219, -1, -1, 3),
7094 ui_button_info("MJW_09", 131, 213, -1, -1, 9),
7095 ui_button_info("MJW_05", 0, 398, -1, -1, 5),
7096 ui_button_info("MJW_06", 0, 435, -1, -1, 6),
7097 ui_button_info("MJW_04", 559, 411, -1, -1, 4),
7099 ui_button_info("MJW_00", 1, 24, -1, -1, 0),
7100 ui_button_info("MJW_01", 1, 66, -1, -1, 1),
7101 ui_button_info("MJW_02", 30, 244, 20, 272, 2),
7102 ui_button_info("MJW_03", 84, 244, 73, 272, 3),
7103 ui_button_info("MJW_04", 139, 242, 134, 272, 4),
7104 ui_button_info("MJW_05", 1, 406, -1, -1, 5),
7105 ui_button_info("MJW_06", 1, 447, -1, -1, 6),
7106 ui_button_info("MJW_07", 577, 428, 570, 414, 7),
7110 ui_button_info("2_MJW_00", 2, 38, -1, -1, 0),
7111 ui_button_info("2_MJW_01", 2, 106, -1, -1, 1),
7112 ui_button_info("2_MJW_02", 48, 390, 47, 435, 2),
7113 ui_button_info("2_MJW_03", 134, 390, 133, 435, 3),
7114 ui_button_info("2_MJW_04", 223, 388, 225, 435, 4),
7115 ui_button_info("2_MJW_05", 2, 649, -1, -1, 5),
7116 ui_button_info("2_MJW_06", 2, 715, -1, -1, 6),
7117 ui_button_info("2_MJW_07", 923, 685, 931, 667, 7),
7122 #define MULTI_JW_NUM_TEXT 7
7124 UI_XSTR Multi_jw_text[GR_NUM_RESOLUTIONS][MULTI_JW_NUM_TEXT] = {
7126 { "Team 1", 1308, 20, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_TEAM0].button },
7127 { "Team 2", 1309, 73, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_TEAM1].button },
7128 { "Pilot", 1310, 134, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_PILOT_INFO].button },
7129 { "Info", 1311, 134, 283, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_PILOT_INFO].button },
7130 { "Cancel", 387, 570, 414, UI_XSTR_COLOR_PINK, -1, &Multi_jw_buttons[0][MJW_CANCEL].button },
7131 { "Players", 1269, 38, 8, UI_XSTR_COLOR_GREEN, -1, NULL },
7132 { "Choose Team", 1312, 27, 231, UI_XSTR_COLOR_GREEN, -1, NULL },
7135 { "Team 1", 1308, 47, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_TEAM0].button },
7136 { "Team 2", 1309, 133, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_TEAM1].button },
7137 { "Pilot", 1310, 225, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_PILOT_INFO].button },
7138 { "Info", 1311, 225, 446, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_PILOT_INFO].button },
7139 { "Cancel", 387, 931, 667, UI_XSTR_COLOR_PINK, -1, &Multi_jw_buttons[1][MJW_CANCEL].button },
7140 { "Players", 1269, 165, 12, UI_XSTR_COLOR_GREEN, -1, NULL },
7141 { "Choose Team", 1312, 45, 373, UI_XSTR_COLOR_GREEN, -1, NULL },
7146 int Mjw_players_coords[GR_NUM_RESOLUTIONS][4] = {
7159 int Mjw_mission_name_coords[GR_NUM_RESOLUTIONS][2] = {
7169 // squad war checkbox
7170 UI_CHECKBOX Multi_jw_sw_checkbox;
7171 const char *Multi_jw_sw_checkbox_fname[GR_NUM_RESOLUTIONS] = {
7175 int Multi_jw_sw_checkbox_coords[GR_NUM_RESOLUTIONS][2] = {
7183 int Multi_jw_sw_checkbox_text[GR_NUM_RESOLUTIONS][2] = {
7194 // player list control thingie defs
7195 #define MULTI_JW_PLIST_MAX_DISPLAY 19
7196 int Multi_jw_plist_select_flag; // indicates whether we currently have a selected player
7197 short Multi_jw_plist_select_id; // id of the current selected player
7198 UI_BUTTON Multi_jw_plist_select_button; // for selecting a player
7200 int Multi_jw_should_show_popup = 0;
7202 // LOCAL function definitions
7203 void multi_jw_check_buttons();
7204 void multi_jw_button_pressed(int n);
7205 void multi_jw_do_netstuff();
7206 void multi_jw_scroll_players_up();
7207 void multi_jw_scroll_players_down();
7208 void multi_jw_plist_process();
7209 void multi_jw_plist_blit_normal();
7210 void multi_jw_plist_blit_team();
7211 short multi_jw_get_mouse_id();
7213 void multi_game_client_setup_init()
7217 // create the interface window
7218 Multi_jw_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
7219 Multi_jw_window.set_mask_bmap(Multi_jw_bitmap_mask_fname[gr_screen.res]);
7221 // load the background bitmap
7222 Multi_jw_bitmap = bm_load(Multi_jw_bitmap_fname[gr_screen.res]);
7223 if(Multi_jw_bitmap < 0){
7224 // we failed to load the bitmap - this is very bad
7228 // initialize the player list data
7229 Multi_jw_plist_select_flag = 0;
7230 Multi_jw_plist_select_id = -1;
7232 // kill any old instances of the chatbox and create a new one
7234 chatbox_create(CHATBOX_FLAG_BIG | CHATBOX_FLAG_DRAW_BOX | CHATBOX_FLAG_BUTTONS);
7236 // initialize the common notification messaging
7237 multi_common_notify_init();
7239 // initialize the common mission info display area.
7240 multi_common_set_text("");
7242 // use the common interface palette
7243 multi_common_set_palette();
7245 // create the interface buttons
7246 for(idx=0; idx<MULTI_JW_NUM_BUTTONS; idx++){
7247 // create the object
7248 Multi_jw_buttons[gr_screen.res][idx].button.create(&Multi_jw_window, "", Multi_jw_buttons[gr_screen.res][idx].x, Multi_jw_buttons[gr_screen.res][idx].y, 1, 1, 0, 1);
7250 // set the sound to play when highlighted
7251 Multi_jw_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
7253 // set the ani for the button
7254 Multi_jw_buttons[gr_screen.res][idx].button.set_bmaps(Multi_jw_buttons[gr_screen.res][idx].filename);
7257 Multi_jw_buttons[gr_screen.res][idx].button.link_hotspot(Multi_jw_buttons[gr_screen.res][idx].hotspot);
7261 // if this is a PXO game, enable the squadwar checkbox
7262 Multi_jw_sw_checkbox.create(&Multi_jw_window, "", Multi_jw_sw_checkbox_coords[gr_screen.res][0], Multi_jw_sw_checkbox_coords[gr_screen.res][1], 0);
7263 Multi_jw_sw_checkbox.set_bmaps(Multi_jw_sw_checkbox_fname[gr_screen.res], 6, 0);
7264 Multi_jw_sw_checkbox.disable();
7265 if(!MULTI_IS_TRACKER_GAME){
7266 Multi_jw_sw_checkbox.hide();
7272 for(idx=0; idx<MULTI_JW_NUM_TEXT; idx++){
7273 Multi_jw_window.add_XSTR(&Multi_jw_text[gr_screen.res][idx]);
7277 // create the player select list button and hide it
7278 Multi_jw_plist_select_button.create(&Multi_jw_window, "", Mjw_players_coords[gr_screen.res][MJW_X_COORD], Mjw_players_coords[gr_screen.res][MJW_Y_COORD], Mjw_players_coords[gr_screen.res][MJW_W_COORD], Mjw_players_coords[gr_screen.res][MJW_H_COORD], 0, 1);
7279 Multi_jw_plist_select_button.hide();
7282 Multi_jw_buttons[gr_screen.res][MJW_CANCEL].button.set_hotkey(SDLK_ESCAPE);
7284 // remove campaign flags
7285 Game_mode &= ~(GM_CAMPAIGN_MODE);
7287 // tell the server we have finished joining
7288 Net_player->state = NETPLAYER_STATE_JOINED;
7289 send_netplayer_update_packet();
7292 ml_printf(NOX("Joined netgame %s"), Netgame.name);
7294 // send any appropriate files
7295 multi_data_send_my_junk();
7298 void multi_game_client_setup_do_frame()
7301 int k = chatbox_process();
7302 char mission_text[255];
7303 /*k =*/ Multi_jw_window.process(k,0);
7305 Multi_jw_should_show_popup = 0;
7307 // process any button clicks
7308 multi_jw_check_buttons();
7310 // do any network related stuff
7311 multi_jw_do_netstuff();
7313 // draw the background, etc
7315 GR_MAYBE_CLEAR_RES(Multi_jw_bitmap);
7316 if(Multi_jw_bitmap != -1){
7317 gr_set_bitmap(Multi_jw_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7321 // if we're not in team vs. team mode, don't draw the team buttons
7322 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
7323 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.hide();
7324 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.hide();
7325 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.disable();
7326 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.disable();
7328 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.enable();
7329 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.enable();
7330 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.unhide();
7331 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.unhide();
7335 if(MULTI_IS_TRACKER_GAME){
7336 // maybe check the squadwar button
7337 if(Netgame.type_flags & NG_TYPE_SW){
7338 Multi_jw_sw_checkbox.set_state(1);
7339 gr_set_color_fast(&Color_bright);
7341 Multi_jw_sw_checkbox.set_state(0);
7342 gr_set_color_fast(&Color_normal);
7345 gr_string(Multi_jw_sw_checkbox_text[gr_screen.res][0], Multi_jw_sw_checkbox_text[gr_screen.res][1], "SquadWar");
7349 // draw the UI window
7350 Multi_jw_window.draw();
7352 // process and display the player list
7353 // NOTE : this must be done before the buttons are checked to insure that a player hasn't dropped
7354 multi_jw_plist_process();
7355 if(Netgame.type_flags & NG_TYPE_TEAM){
7356 multi_jw_plist_blit_team();
7358 multi_jw_plist_blit_normal();
7361 // display any text in the info area
7362 multi_common_render_text();
7364 // display any pending notification messages
7365 multi_common_notify_do();
7367 // blit the mission filename if possible
7368 if(Netgame.campaign_mode){
7369 if(strlen(Netgame.campaign_name) > 0){
7370 SDL_strlcpy(mission_text, Netgame.campaign_name, SDL_arraysize(mission_text));
7372 if(strlen(Netgame.title) > 0){
7373 SDL_strlcat(mission_text, ", ", SDL_arraysize(mission_text));
7374 SDL_strlcat(mission_text, Netgame.title, SDL_arraysize(mission_text));
7377 gr_set_color_fast(&Color_bright_white);
7378 gr_string(Mjw_mission_name_coords[gr_screen.res][MJW_X_COORD],Mjw_mission_name_coords[gr_screen.res][MJW_Y_COORD],mission_text);
7381 if(strlen(Netgame.mission_name) > 0){
7382 SDL_strlcpy(mission_text, Netgame.mission_name, SDL_arraysize(mission_text));
7384 if(strlen(Netgame.title) > 0){
7385 SDL_strlcat(mission_text, ", ", SDL_arraysize(mission_text));
7386 SDL_strlcat(mission_text, Netgame.title, SDL_arraysize(mission_text));
7389 gr_set_color_fast(&Color_bright_white);
7390 gr_string(Mjw_mission_name_coords[gr_screen.res][MJW_X_COORD],Mjw_mission_name_coords[gr_screen.res][MJW_Y_COORD],mission_text);
7394 // process and show the chatbox thingie
7398 Multi_jw_window.draw_tooltip();
7400 // display the voice status indicator
7401 multi_common_voice_display_status();
7406 // if we're supposed to be displaying a pilot info popup
7407 if(Multi_jw_should_show_popup){
7408 player_index = find_player_id(Multi_jw_plist_select_id);
7409 if(player_index != -1){
7410 multi_pinfo_popup(&Net_players[player_index]);
7415 void multi_game_client_setup_close()
7417 // unload any bitmaps
7418 if(!bm_unload(Multi_jw_bitmap)){
7419 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_jw_bitmap_fname[gr_screen.res]));
7422 // destroy the chatbox
7425 // destroy the UI_WINDOW
7426 Multi_jw_window.destroy();
7429 if(Netgame.game_state == NETGAME_STATE_MISSION_SYNC){
7430 gamesnd_play_iface(SND_COMMIT_PRESSED);
7435 void multi_jw_check_buttons()
7438 for(idx=0;idx<MULTI_JW_NUM_BUTTONS;idx++){
7439 // we only really need to check for one button pressed at a time, so we can break after
7441 if(Multi_jw_buttons[gr_screen.res][idx].button.pressed()){
7442 multi_jw_button_pressed(idx);
7448 void multi_jw_button_pressed(int n)
7452 gamesnd_play_iface(SND_USER_SELECT);
7453 multi_quit_game(PROMPT_CLIENT);
7455 case MJW_SCROLL_PLAYERS_UP:
7456 multi_jw_scroll_players_up();
7458 case MJW_SCROLL_PLAYERS_DOWN:
7459 multi_jw_scroll_players_down();
7461 case MJW_SCROLL_INFO_UP:
7462 multi_common_scroll_text_up();
7464 case MJW_SCROLL_INFO_DOWN:
7465 multi_common_scroll_text_down();
7468 // request to set myself to team 0
7470 gamesnd_play_iface(SND_USER_SELECT);
7471 multi_team_set_team(Net_player,0);
7474 // request to set myself to team 1
7476 gamesnd_play_iface(SND_USER_SELECT);
7477 multi_team_set_team(Net_player,1);
7481 case MJW_PILOT_INFO:
7482 Multi_jw_should_show_popup = 1;
7486 multi_common_add_notify(XSTR("Not implemented yet!",760));
7491 // do stuff like pinging servers, sending out requests, etc
7492 void multi_jw_do_netstuff()
7496 void multi_jw_scroll_players_up()
7498 gamesnd_play_iface(SND_GENERAL_FAIL);
7501 // scroll down through the player list
7502 void multi_jw_scroll_players_down()
7504 gamesnd_play_iface(SND_GENERAL_FAIL);
7507 void multi_jw_plist_process()
7509 int test_count,player_index,idx;
7511 // first determine if there are 0 players in the game. This should never happen since the host is _always_ in the game
7513 for(idx=0;idx<MAX_PLAYERS;idx++){
7514 // count anyone except the standalone server (if applicable)
7515 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7519 if(test_count <= 0){
7523 // if we had a selected item but that player has left, select myself instead
7524 if(Multi_jw_plist_select_flag){
7525 player_index = find_player_id(Multi_jw_plist_select_id);
7526 if(player_index == -1){
7527 Multi_jw_plist_select_id = Net_player->player_id;
7530 Multi_jw_plist_select_flag = 1;
7531 Multi_jw_plist_select_id = Net_player->player_id;
7534 // if the player has clicked somewhere in the player list area
7535 if(Multi_jw_plist_select_button.pressed()){
7539 player_id = multi_jw_get_mouse_id();
7540 player_index = find_player_id(player_id);
7541 if(player_index != -1){
7542 Multi_jw_plist_select_id = player_id;
7543 Multi_jw_plist_select_flag = 1;
7548 void multi_jw_plist_blit_normal()
7551 char str[CALLSIGN_LEN+1];
7552 int y_start = Mjw_players_coords[gr_screen.res][MJW_Y_COORD];
7555 // display all the players
7556 for(idx=0;idx<MAX_PLAYERS;idx++){
7557 // reset total offset
7560 // count anyone except the standalone server (if applicable)
7561 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7562 // highlight him if he's the host
7563 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7564 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7565 gr_set_color_fast(&Color_text_active_hi);
7567 gr_set_color_fast(&Color_bright);
7570 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7571 gr_set_color_fast(&Color_text_active);
7573 gr_set_color_fast(&Color_text_normal);
7577 // optionally draw his CD status
7578 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7579 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7580 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7582 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7585 // make sure the string will fit, then display it
7586 SDL_strlcpy(str, Net_players[idx].player->callsign, SDL_arraysize(str));
7587 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
7588 SDL_strlcat(str, "(0)", SDL_arraysize(str));
7590 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7591 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7598 void multi_jw_plist_blit_team()
7601 char str[CALLSIGN_LEN+1];
7602 int y_start = Mjw_players_coords[gr_screen.res][MJW_Y_COORD];
7605 // always blit the proper team button based on _my_ team status
7606 if(Net_player->p_info.team == 0){
7607 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.draw_forced(2);
7609 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.draw_forced(2);
7612 // display all the red players first
7613 for(idx=0;idx<MAX_PLAYERS;idx++){
7614 // reset total offset
7617 // count anyone except the standalone server (if applicable)
7618 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
7619 // highlight him if he's the host
7620 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7621 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7622 gr_set_color_fast(&Color_text_active_hi);
7624 gr_set_color_fast(&Color_bright);
7627 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7628 gr_set_color_fast(&Color_text_active);
7630 gr_set_color_fast(&Color_text_normal);
7634 // optionally draw his CD status
7635 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7636 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7637 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7639 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7642 // blit the red team indicator
7643 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
7644 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
7645 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7646 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7648 total_offset += Multi_common_icon_dims[MICON_TEAM0_SELECT][0] + 1;
7651 if(Multi_common_icons[MICON_TEAM0] != -1){
7652 gr_set_bitmap(Multi_common_icons[MICON_TEAM0], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7653 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7655 total_offset += Multi_common_icon_dims[MICON_TEAM0][0] + 1;
7659 // make sure the string will fit
7660 SDL_strlcpy(str, Net_players[idx].player->callsign, SDL_arraysize(str));
7661 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7663 // display him in the correct half of the list depending on his team
7664 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7669 // display all the green players next
7670 for(idx=0;idx<MAX_PLAYERS;idx++){
7671 // reset total offset
7674 // count anyone except the standalone server (if applicable)
7675 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
7676 // highlight him if he's the host
7677 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7678 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7679 gr_set_color_fast(&Color_text_active_hi);
7681 gr_set_color_fast(&Color_bright);
7684 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7685 gr_set_color_fast(&Color_text_active);
7687 gr_set_color_fast(&Color_text_normal);
7691 // optionally draw his CD status
7692 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7693 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7694 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7696 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7699 // blit the red team indicator
7700 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
7701 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
7702 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7703 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7705 total_offset += Multi_common_icon_dims[MICON_TEAM1_SELECT][0] + 1;
7708 if(Multi_common_icons[MICON_TEAM1] != -1){
7709 gr_set_bitmap(Multi_common_icons[MICON_TEAM1], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7710 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7712 total_offset += Multi_common_icon_dims[MICON_TEAM1][0] + 1;
7716 // make sure the string will fit
7717 SDL_strlcpy(str, Net_players[idx].player->callsign, SDL_arraysize(str));
7718 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
7719 SDL_strlcat(str, "(0)", SDL_arraysize(str));
7721 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7723 // display him in the correct half of the list depending on his team
7724 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7730 void multi_jw_handle_join(net_player *pl)
7732 // for now just play a bloop sound
7733 gamesnd_play_iface(SND_ICON_DROP_ON_WING);
7736 short multi_jw_get_mouse_id()
7738 // determine where he clicked (y pixel value)
7740 Multi_jw_plist_select_button.get_mouse_pos(NULL,&y);
7742 // select things a little differently if we're in team vs. team or non-team vs. team mode
7744 if(Netgame.type_flags & NG_TYPE_TEAM){
7745 int player_index = -1;
7747 // look through all of team red first
7748 for(idx=0;idx<MAX_PLAYERS;idx++){
7749 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
7752 // if this is the _nth_ guy
7760 // if we still haven't found him yet, look through the green team
7761 if(player_index == -1){
7762 for(idx=0;idx<MAX_PLAYERS;idx++){
7763 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
7765 // if this is the _nth_ guy
7773 if(player_index != -1){
7774 return Net_players[idx].player_id;
7777 // select the nth active player if possible, disregarding the standalone server
7778 for(idx=0;idx<MAX_PLAYERS;idx++){
7779 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7782 // if this is the _nth_ guy
7784 return Net_players[idx].player_id;
7795 // -------------------------------------------------------------------------------------------------------------
7797 // MULTIPLAYER WAIT/SYNCH SCREEN
7800 #define MULTI_SYNC_HOST_COUNT 4 // host uses 4 buttons (and sometimes 5)
7801 #define MULTI_SYNC_CLIENT_COUNT 3 // client only uses 3 buttons
7803 const char *Multi_sync_bitmap_fname[GR_NUM_RESOLUTIONS] = {
7804 "MultiSynch", // GR_640
7805 "2_MultiSynch" // GR_1024
7808 const char *Multi_sync_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
7809 "MultiSynch-M", // GR_640
7810 "2_MultiSynch-M" // GR_1024
7815 // constants for coordinate lookup
7816 #define MS_X_COORD 0
7817 #define MS_Y_COORD 1
7818 #define MS_W_COORD 2
7819 #define MS_H_COORD 3
7821 UI_WINDOW Multi_sync_window; // the window object for the join screen
7822 int Multi_sync_button_count; // the # of buttons to use for this instance of mission sync
7823 int Multi_sync_bitmap; // the background bitmap
7826 #define MULTI_SYNC_NUM_BUTTONS 5
7827 #define MS_SCROLL_INFO_UP 0
7828 #define MS_SCROLL_INFO_DOWN 1
7832 ui_button_info Multi_sync_buttons[GR_NUM_RESOLUTIONS][MULTI_SYNC_NUM_BUTTONS] = {
7835 ui_button_info("MS_00", 0, 396, -1, -1, 0),
7836 ui_button_info("MS_01", 0, 435, -1, -1, 1),
7837 ui_button_info("MS_02", 496, 411, -1, -1, 2),
7838 ui_button_info("MS_04", 436, 403, -1, -1, 4),
7839 ui_button_info("MS_03", 556, 398, -1, -1, 3),
7841 ui_button_info("MS_00", 1, 404, -1, -1, 0),
7842 ui_button_info("MS_01", 1, 446, -1, -1, 1),
7843 ui_button_info("MS_03", 518, 426, 519, 416, 3),
7844 ui_button_info("MS_02", 469, 426, 479, 416, 2),
7845 ui_button_info("MS_04", 571, 420, 577, 416, 4),
7849 ui_button_info("2_MS_00", 2, 647, -1, -1, 0),
7850 ui_button_info("2_MS_01", 2, 713, -1, -1, 1),
7851 ui_button_info("2_MS_03", 829, 682, 831, 667, 3),
7852 ui_button_info("2_MS_02", 751, 682, 766, 667, 2),
7853 ui_button_info("2_MS_04", 914, 672, 924, 667, 4),
7859 #define MULTI_SYNC_NUM_TEXT 5
7861 #define MST_LAUNCH 2
7862 UI_XSTR Multi_sync_text[GR_NUM_RESOLUTIONS][MULTI_SYNC_NUM_TEXT] = {
7864 { "Kick", 1266, 479, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_KICK].button },
7865 { "Cancel", 387, 519, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_CANCEL].button },
7866 { "Launch", 801, 577, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_LAUNCH].button },
7867 { "Players", 1269, 23, 133, UI_XSTR_COLOR_GREEN, -1, NULL },
7868 { "Status", 1304, 228, 133, UI_XSTR_COLOR_GREEN, -1, NULL }
7871 { "Kick", 1266, 766, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_KICK].button },
7872 { "Cancel", 387, 831, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_CANCEL].button },
7873 { "Launch", 801, 924, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_LAUNCH].button },
7874 { "Players", 1269, 38, 214, UI_XSTR_COLOR_GREEN, -1, NULL },
7875 { "Status", 1304, 366, 214, UI_XSTR_COLOR_GREEN, -1, NULL }
7881 int Ms_status_coords[GR_NUM_RESOLUTIONS][4] = {
7894 // player status coords
7895 int Ms_status2_coords[GR_NUM_RESOLUTIONS][4] = {
7908 int Ms_cd_icon_offset[GR_NUM_RESOLUTIONS] = {
7913 int Ms_team_icon_offset[GR_NUM_RESOLUTIONS] = {
7918 // player currently selected, index into Net_players[]
7919 int Multi_sync_player_select = -1;
7921 // player list control thingie defs
7922 #define MULTI_SYNC_PLIST_MAX_DISPLAY 15
7923 int Multi_sync_plist_start; // where to start displaying from
7924 int Multi_sync_plist_count; // how many we have
7926 // list select button
7927 UI_BUTTON Multi_sync_plist_button;
7929 int Multi_sync_mode = -1;
7931 #define MULTI_SYNC_COUNTDOWN_TIME 5 // in seconds
7932 float Multi_sync_countdown_timer;
7933 int Multi_sync_countdown = -1;
7935 int Multi_launch_button_created;
7938 // countdown animation timer
7939 const char* Multi_sync_countdown_fname[GR_NUM_RESOLUTIONS] = {
7941 "2_Count" // GR_1024
7944 int Multi_sync_countdown_coords[GR_NUM_RESOLUTIONS][2] = {
7955 anim *Multi_sync_countdown_anim = NULL;
7956 anim_instance *Multi_sync_countdown_instance = NULL;
7959 // PREBRIEFING STUFF
7960 // syncing flags used by the server
7961 int Mission_sync_flags = 0;
7962 #define MS_FLAG_SENT_FILESIG (1<<0) // sent filesig requests
7963 #define MS_FLAG_SENT_LOAD (1<<1) // sent load packets
7964 #define MS_FLAG_PUSHED_BRIEFING (1<<2) // pushed everyone else into the briefing
7965 #define MS_FLAG_POST_DATA (1<<3) // sent the post data block
7966 #define MS_FLAG_WSS_SLOTS (1<<4) // all players have received wss slot data
7967 #define MS_FLAG_PSETTINGS (1<<5) // send the player settings packet
7968 #define MS_FLAG_MT_STATS_START (1<<6) // server has started getting player stats from the tracker
7969 #define MS_FLAG_MT_STATS_DONE (1<<7) // server has finished getting player stats from the tracker (success or fail)
7970 #define MS_FLAG_TS_SLOTS (1<<8) // team/ship slots have been sent
7971 #define MS_FLAG_DATA_DONE (1<<9) // done transferring all necessary data
7972 #define MS_FLAG_CAMP_DONE (1<<10) // send campaign pool/goal/event stuff
7974 // POSTBRIEFING STUFF
7975 int Multi_state_timestamp;
7976 int Multi_sync_launch_pressed;
7978 // LOCAL function definitions
7979 void multi_sync_check_buttons();
7980 void multi_sync_button_pressed(int n);
7981 void multi_sync_scroll_info_up();
7982 void multi_sync_scroll_info_down();
7983 void multi_sync_display_name(const char *name, int index, int np_index); // display info on the left hand portion of the status window thingie
7984 void multi_sync_display_status(const char *status, int index); // display info on the right hand portion of the status window thingie
7985 void multi_sync_force_start_pre();
7986 void multi_sync_force_start_post();
7987 void multi_sync_launch();
7988 void multi_sync_create_launch_button();
7989 void multi_sync_blit_screen_all();
7990 void multi_sync_handle_plist();
7992 void multi_sync_common_init();
7993 void multi_sync_common_do();
7994 void multi_sync_common_close();
7996 void multi_sync_pre_init();
7997 void multi_sync_pre_do();
7998 void multi_sync_pre_close();
8000 void multi_sync_post_init();
8001 void multi_sync_post_do();
8002 void multi_sync_post_close();
8007 // perform the correct init functions
8008 void multi_sync_init()
8010 Multi_sync_countdown = -1;
8014 // reset all timestamp
8015 multi_reset_timestamps();
8017 extern time_t Player_multi_died_check;
8018 Player_multi_died_check = -1;
8020 if(!(Game_mode & GM_STANDALONE_SERVER)){
8021 multi_sync_common_init();
8024 switch(Multi_sync_mode){
8025 case MULTI_SYNC_PRE_BRIEFING:
8026 multi_sync_pre_init();
8028 case MULTI_SYNC_POST_BRIEFING:
8029 multi_sync_post_init();
8031 case MULTI_SYNC_INGAME:
8032 multi_ingame_sync_init();
8037 // perform the correct do frame functions
8038 void multi_sync_do()
8040 if(!(Game_mode & GM_STANDALONE_SERVER)){
8041 multi_sync_common_do();
8044 // if the netgame is ending, don't do any sync processing
8045 if(multi_endgame_ending()){
8049 // process appropriateliy
8050 switch(Multi_sync_mode){
8051 case MULTI_SYNC_PRE_BRIEFING:
8052 multi_sync_pre_do();
8054 case MULTI_SYNC_POST_BRIEFING:
8055 multi_sync_post_do();
8057 case MULTI_SYNC_INGAME:
8058 multi_ingame_sync_do();
8061 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8062 if(Multi_sync_bitmap != -1){
8063 gr_set_bitmap(Multi_sync_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8066 Multi_sync_window.draw();
8068 multi_sync_blit_screen_all();
8075 // perform the correct close functions
8076 void multi_sync_close()
8078 switch(Multi_sync_mode){
8079 case MULTI_SYNC_PRE_BRIEFING:
8080 multi_sync_pre_close();
8082 case MULTI_SYNC_POST_BRIEFING:
8083 multi_sync_post_close();
8085 case MULTI_SYNC_INGAME:
8086 multi_ingame_sync_close();
8090 if(!(Game_mode & GM_STANDALONE_SERVER)){
8091 multi_sync_common_close();
8095 const char *multi_sync_tooltip_handler(const char *str)
8097 if (!SDL_strcasecmp(str, NOX("@launch"))) {
8098 if (Multi_launch_button_created){
8099 return XSTR("Launch",801);
8106 void multi_sync_common_init()
8110 // create the interface window
8111 Multi_sync_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
8112 Multi_sync_window.set_mask_bmap(Multi_sync_bitmap_mask_fname[gr_screen.res]);
8113 Multi_sync_window.tooltip_handler = multi_sync_tooltip_handler;
8115 // load the background bitmap
8116 Multi_sync_bitmap = bm_load(Multi_sync_bitmap_fname[gr_screen.res]);
8117 if (Multi_sync_bitmap < 0) {
8118 // we failed to load the bitmap - this is very bad
8122 // initialize the player list data
8123 Multi_sync_plist_start = 0;
8124 Multi_sync_plist_count = 1; // we can pretty safely assume that there's one player in the game - me.
8126 Multi_launch_button_created = 0;
8128 // create the chatbox thingie (shouldn't be necesary to do this, but we'll put it in for good measure)
8131 // force the chatbox to be small
8132 chatbox_force_small();
8134 // initialize the common notification messaging
8135 multi_common_notify_init();
8137 // initialize the common mission info display area.
8138 multi_common_set_text("");
8140 // use the common interface palette
8141 multi_common_set_palette();
8143 // don't select any player yet.
8144 Multi_sync_player_select = -1;
8146 // determine how many of the 5 buttons to create
8147 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8148 Multi_sync_button_count = MULTI_SYNC_HOST_COUNT;
8150 Multi_sync_button_count = MULTI_SYNC_CLIENT_COUNT;
8152 // create the interface buttons
8153 for(idx=0; idx<Multi_sync_button_count; idx++){
8154 // create the object
8155 Multi_sync_buttons[gr_screen.res][idx].button.create(&Multi_sync_window, "", Multi_sync_buttons[gr_screen.res][idx].x, Multi_sync_buttons[gr_screen.res][idx].y, 1, 1, 0, 1);
8157 // set the sound to play when highlighted
8158 Multi_sync_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
8160 // set the ani for the button
8161 // this wierdness is necessary because cancel and kick buttons aren't drawn on the background bitmap,
8162 // so we have to load in frame 0, too (the file should exist)
8163 if ((idx == MS_CANCEL) || (idx == MS_KICK) || (idx == MS_LAUNCH)) {
8164 Multi_sync_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sync_buttons[gr_screen.res][idx].filename, 3, 0);
8166 Multi_sync_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sync_buttons[gr_screen.res][idx].filename);
8170 Multi_sync_buttons[gr_screen.res][idx].button.link_hotspot(Multi_sync_buttons[gr_screen.res][idx].hotspot);
8175 for(idx=0; idx<MULTI_SYNC_NUM_TEXT; idx++) {
8176 // don't create the "launch" button text just yet
8177 if(idx == MST_LAUNCH) {
8180 // multiplayer clients should ignore the kick button
8181 if(!MULTIPLAYER_MASTER && !MULTIPLAYER_HOST && (idx == MST_KICK)) {
8185 Multi_sync_window.add_XSTR(&Multi_sync_text[gr_screen.res][idx]);
8189 // create the player list select button and hide it
8190 Multi_sync_plist_button.create(&Multi_sync_window, "", Ms_status_coords[gr_screen.res][MS_X_COORD], Ms_status_coords[gr_screen.res][MS_Y_COORD], Ms_status_coords[gr_screen.res][MS_W_COORD], Ms_status_coords[gr_screen.res][MS_H_COORD], 0, 1);
8191 Multi_sync_plist_button.hide();
8193 // set up hotkeys for certain common functions
8194 Multi_sync_buttons[gr_screen.res][MS_CANCEL].button.set_hotkey(SDLK_ESCAPE);
8197 void multi_sync_common_do()
8199 int k = chatbox_process();
8200 k = Multi_sync_window.process(k);
8202 // process the player list
8203 multi_sync_handle_plist();
8205 // process any button clicks
8206 multi_sync_check_buttons();
8208 // process any keypresses
8212 gamesnd_play_iface(SND_USER_SELECT);
8213 multi_quit_game(PROMPT_ALL);
8218 void multi_sync_common_close()
8220 // unload any bitmaps
8221 if(!bm_unload(Multi_sync_bitmap)){
8222 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_sync_bitmap_fname[gr_screen.res]));
8225 extern time_t Player_multi_died_check;
8226 Player_multi_died_check = -1;
8228 // destroy the UI_WINDOW
8229 Multi_sync_window.destroy();
8232 void multi_sync_blit_screen_all()
8239 // display any text in the info area
8240 multi_common_render_text();
8242 // display any pending notification messages
8243 multi_common_notify_do();
8245 // display any info about visible players
8247 for(idx=0;idx<MAX_PLAYERS;idx++){
8248 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
8249 // display his name and status
8250 multi_sync_display_name(Net_players[idx].player->callsign,count,idx);
8252 // get the player state
8253 state = Net_players[idx].state;
8255 // if we're ingame joining, show all other players except myself as "playing"
8256 if((Net_player != NULL) && (&Net_players[idx] != Net_player) && ((Multi_sync_mode == MULTI_SYNC_INGAME) || (Net_player->flags & NETINFO_FLAG_INGAME_JOIN)) ){
8257 state = NETPLAYER_STATE_IN_MISSION;
8261 case NETPLAYER_STATE_MISSION_LOADING:
8262 multi_sync_display_status(XSTR("Mission Loading",802),count);
8264 case NETPLAYER_STATE_INGAME_SHIP_SELECT: // I don't think its possible to see this state, but...
8265 multi_sync_display_status(XSTR("Ingame Ship Select",803),count);
8267 case NETPLAYER_STATE_DEBRIEF:
8268 multi_sync_display_status(XSTR("Debriefing",804),count);
8270 case NETPLAYER_STATE_MISSION_SYNC:
8271 multi_sync_display_status(XSTR("Mission Sync",805),count);
8273 case NETPLAYER_STATE_JOINING:
8274 multi_sync_display_status(XSTR("Joining",806),count);
8276 case NETPLAYER_STATE_JOINED:
8277 multi_sync_display_status(XSTR("Joined",807),count);
8279 case NETPLAYER_STATE_SLOT_ACK :
8280 multi_sync_display_status(XSTR("Slot Ack",808),count);
8282 case NETPLAYER_STATE_BRIEFING:
8283 multi_sync_display_status(XSTR("Briefing",765),count);
8285 case NETPLAYER_STATE_SHIP_SELECT:
8286 multi_sync_display_status(XSTR("Ship Select",809),count);
8288 case NETPLAYER_STATE_WEAPON_SELECT:
8289 multi_sync_display_status(XSTR("Weapon Select",810),count);
8291 case NETPLAYER_STATE_WAITING:
8292 multi_sync_display_status(XSTR("Waiting",811),count);
8294 case NETPLAYER_STATE_IN_MISSION:
8295 multi_sync_display_status(XSTR("In Mission",812),count);
8297 case NETPLAYER_STATE_MISSION_LOADED:
8298 multi_sync_display_status(XSTR("Mission Loaded",813),count);
8300 case NETPLAYER_STATE_DATA_LOAD:
8301 multi_sync_display_status(XSTR("Data loading",814),count);
8303 case NETPLAYER_STATE_SETTINGS_ACK:
8304 multi_sync_display_status(XSTR("Ready To Enter Mission",815),count);
8306 case NETPLAYER_STATE_INGAME_SHIPS:
8307 multi_sync_display_status(XSTR("Ingame Ships Packet Ack",816),count);
8309 case NETPLAYER_STATE_INGAME_WINGS:
8310 multi_sync_display_status(XSTR("Ingame Wings Packet Ack",817),count);
8312 case NETPLAYER_STATE_INGAME_RPTS:
8313 multi_sync_display_status(XSTR("Ingame Respawn Points Ack",818),count);
8315 case NETPLAYER_STATE_SLOTS_ACK:
8316 multi_sync_display_status(XSTR("Ingame Weapon Slots Ack",819),count);
8318 case NETPLAYER_STATE_POST_DATA_ACK:
8319 multi_sync_display_status(XSTR("Post Briefing Data Block Ack",820),count);
8321 case NETPLAYER_STATE_FLAG_ACK :
8322 multi_sync_display_status(XSTR("Flags Ack",821),count);
8324 case NETPLAYER_STATE_MT_STATS :
8325 multi_sync_display_status(XSTR("Parallax Online Stats Updating",822),count);
8327 case NETPLAYER_STATE_WSS_ACK :
8328 multi_sync_display_status(XSTR("Weapon Slots Ack",823),count);
8330 case NETPLAYER_STATE_HOST_SETUP :
8331 multi_sync_display_status(XSTR("Host setup",824),count);
8333 case NETPLAYER_STATE_DEBRIEF_ACCEPT:
8334 multi_sync_display_status(XSTR("Debrief accept",825),count);
8336 case NETPLAYER_STATE_DEBRIEF_REPLAY:
8337 multi_sync_display_status(XSTR("Debrief replay",826),count);
8339 case NETPLAYER_STATE_CPOOL_ACK:
8340 multi_sync_display_status(XSTR("Campaign ship/weapon ack",827),count);
8342 case NETPLAYER_STATE_MISSION_XFER :
8344 // server should display the pct completion of all clients
8345 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8346 if(Net_players[idx].s_info.xfer_handle != -1){
8347 pct_complete = multi_xfer_pct_complete(Net_players[idx].s_info.xfer_handle);
8349 // if we've got a valid xfer handle
8350 if((pct_complete >= 0.0) && (pct_complete <= 1.0)){
8351 SDL_snprintf(txt,SDL_arraysize(txt),XSTR("Mission file xfer %d%%",828),(int)(pct_complete * 100.0f));
8355 SDL_strlcpy(txt, XSTR("Mission file xfer", 829), SDL_arraysize(txt));
8358 SDL_strlcpy(txt, XSTR("Mission file xfer", 829), SDL_arraysize(txt));
8361 // clients should display only for themselves (which is the only thing they know)
8363 // if we've got a valid file xfer handle
8364 if((&Net_players[idx] == Net_player) && (Net_player->s_info.xfer_handle != -1)){
8365 pct_complete = multi_xfer_pct_complete(Net_player->s_info.xfer_handle);
8367 // if we've got a valid xfer handle
8368 if((pct_complete >= 0.0) && (pct_complete <= 1.0)){
8369 SDL_snprintf(txt,SDL_arraysize(txt),XSTR("Mission file xfer %d%%",828),(int)(pct_complete * 100.0f));
8373 SDL_strlcpy(txt, XSTR("Mission file xfer", 829), SDL_arraysize(txt));
8378 SDL_strlcpy(txt, XSTR("Mission file xfer", 829), SDL_arraysize(txt));
8383 multi_sync_display_status(txt,count);
8386 nprintf(("Network","Unhandled player state : %d !\n",Net_players[idx].state));
8393 // display the mission start countdown timer (if any)
8394 anim_render_all(GS_STATE_MULTI_MISSION_SYNC,flFrametime);
8396 // process and show the chatbox thingie
8400 Multi_sync_window.draw_tooltip();
8402 // display the voice status indicator
8403 multi_common_voice_display_status();
8406 void multi_sync_check_buttons()
8409 for(idx=0;idx<Multi_sync_button_count;idx++){
8410 // we only really need to check for one button pressed at a time, so we can break after
8412 if(Multi_sync_buttons[gr_screen.res][idx].button.pressed()){
8413 multi_sync_button_pressed(idx);
8419 void multi_sync_button_pressed(int n)
8424 gamesnd_play_iface(SND_USER_SELECT);
8425 multi_quit_game(PROMPT_ALL);
8428 // scroll the info box up
8429 case MS_SCROLL_INFO_UP:
8430 multi_common_scroll_text_up();
8433 // scroll the info box down
8434 case MS_SCROLL_INFO_DOWN:
8435 multi_common_scroll_text_down();
8440 // if we have a currently selected player, kick him
8441 if(Multi_sync_player_select >= 0){
8442 multi_kick_player(Multi_sync_player_select);
8446 // start the final launch countdown (post-sync only)
8448 multi_sync_start_countdown();
8451 // doesn't do anything
8457 void multi_sync_pre_init()
8461 Netgame.game_state = NETGAME_STATE_MISSION_SYNC;
8463 // if we're in teamplay mode, always force skill level to be medium
8464 if((Netgame.type_flags & NG_TYPE_TEAM) && (Net_player->flags & NETINFO_FLAG_GAME_HOST)){
8465 Netgame.options.skill_level = NUM_SKILL_LEVELS / 2;
8466 Game_skill_level = NUM_SKILL_LEVELS / 2;
8467 multi_options_update_netgame();
8470 // notify everyone of when we get here
8471 if(!(Game_mode & GM_STANDALONE_SERVER)){
8472 Net_player->state = NETPLAYER_STATE_MISSION_SYNC;
8473 send_netplayer_update_packet();
8476 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8478 ml_string(NOX("Server performing pre-briefing data sync"));
8480 if(!(Game_mode & GM_STANDALONE_SERVER)){
8481 multi_common_set_text(XSTR("Server performing sync\n",830),1);
8484 // maybe initialize tvt and squad war stuff
8485 if(Netgame.type_flags & NG_TYPE_TEAM){
8486 multi_team_level_init();
8489 // force everyone into this state
8490 send_netgame_update_packet();
8492 if(!(Game_mode & GM_STANDALONE_SERVER)){
8493 multi_common_add_text(XSTR("Send update packet\n",831),1);
8496 // setup some of my own data
8497 Net_player->flags |= NETINFO_FLAG_MISSION_OK;
8499 // do any output stuff
8500 if(Game_mode & GM_STANDALONE_SERVER){
8501 std_debug_set_standalone_state_string("Mission Sync");
8504 // do this here to insure we have the most up to date file checksum info
8505 multi_get_mission_checksum(Game_current_mission_filename);
8506 // parse_get_file_signature(Game_current_mission_filename);
8508 if(!(Game_mode & GM_STANDALONE_SERVER)){
8509 multi_common_add_text(XSTR("Got file signatures\n",832),1);
8512 if(!(Game_mode & GM_STANDALONE_SERVER)){
8513 multi_common_add_text(XSTR("Sending update state packet\n",833),1);
8517 // if we're not in team vs. team mode - set all player teams to be 0, and unset all captaincy bits
8518 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
8519 for(idx=0;idx<MAX_PLAYERS;idx++){
8520 Net_players[idx].p_info.team = 0;
8521 Net_players[idx].flags &= ~(NETINFO_FLAG_TEAM_CAPTAIN);
8525 // we aren't necessarily xferring the mission file yet
8526 SDL_assert(Net_player->s_info.xfer_handle == -1);
8528 // always call this for good measure
8529 multi_campaign_flush_data();
8531 Mission_sync_flags = 0;
8532 Multi_mission_loaded = 0;
8535 void multi_sync_pre_do()
8539 // If I'm the server, wait for everyone to arrive in this state, then begin transferring data, etc.
8540 // all servers (standalone or no, go through this)
8541 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8542 // wait for everyone to arrive, then request filesig from all of them
8543 if(multi_netplayer_state_check(NETPLAYER_STATE_MISSION_SYNC) && !(Mission_sync_flags & MS_FLAG_SENT_FILESIG) && !multi_endgame_ending()){
8544 send_file_sig_request(Netgame.mission_name);
8545 Mission_sync_flags |= MS_FLAG_SENT_FILESIG;
8547 if(!(Game_mode & GM_STANDALONE_SERVER)){
8548 multi_common_add_text(XSTR("Sent filesig request\n",834),1);
8552 // if we're waiting for players to receive files, then check on their status
8553 if((Mission_sync_flags & MS_FLAG_SENT_FILESIG) && !multi_netplayer_flag_check(NETINFO_FLAG_MISSION_OK) && !multi_endgame_ending()){
8554 for(idx=0;idx<MAX_PLAYERS;idx++){
8555 // if this player is in the process of xferring a file
8556 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].s_info.xfer_handle != -1)){
8557 switch(multi_xfer_get_status(Net_players[idx].s_info.xfer_handle)){
8558 // if it has successfully completed, set his ok flag
8559 case MULTI_XFER_SUCCESS :
8561 Net_players[idx].flags |= NETINFO_FLAG_MISSION_OK;
8563 // release the xfer instance handle
8564 multi_xfer_release_handle(Net_players[idx].s_info.xfer_handle);
8565 Net_players[idx].s_info.xfer_handle = -1;
8567 // if it has failed or timed-out, kick the player
8568 case MULTI_XFER_TIMEDOUT:
8569 case MULTI_XFER_FAIL:
8570 // release the xfer handle
8571 multi_xfer_release_handle(Net_players[idx].s_info.xfer_handle);
8572 Net_players[idx].s_info.xfer_handle = -1;
8575 multi_kick_player(idx, 0, KICK_REASON_BAD_XFER);
8582 // NOTE : this is now obsolete
8583 // once everyone is verified, do any data transfer necessary
8584 if(multi_netplayer_flag_check(NETINFO_FLAG_MISSION_OK) && !(Mission_sync_flags & MS_FLAG_DATA_DONE) && !multi_endgame_ending()){
8585 // do nothing for now
8586 Mission_sync_flags |= MS_FLAG_DATA_DONE;
8588 // send campaign pool data
8589 multi_campaign_send_pool_status();
8592 // wait for everyone to ack on campaign pool data (even in non-campaign situations)
8593 if((Mission_sync_flags & MS_FLAG_DATA_DONE) && !(Mission_sync_flags & MS_FLAG_CAMP_DONE) && !multi_endgame_ending()){
8594 // check to see if everyone has acked the campaign pool data
8595 if(multi_netplayer_state_check(NETPLAYER_STATE_CPOOL_ACK)){
8596 Mission_sync_flags |= MS_FLAG_CAMP_DONE;
8600 // once everyone is verified, tell them to load the mission
8601 // also make sure to load the mission myself _AFTER_ telling everyone to do so. This makes the whole process
8602 // move along faster
8603 if((Mission_sync_flags & MS_FLAG_CAMP_DONE) && !(Mission_sync_flags & MS_FLAG_SENT_LOAD) && !multi_endgame_ending()){
8604 send_netplayer_load_packet(NULL);
8605 Mission_sync_flags |= MS_FLAG_SENT_LOAD;
8607 if(!(Game_mode & GM_STANDALONE_SERVER)){
8608 multi_common_add_text(XSTR("Sent load packet\n",835),1);
8611 // load the mission myself, as soon as possible
8612 if(!Multi_mission_loaded){
8613 nprintf(("Network","Server loading mission..."));
8615 // update everyone about my status
8616 Net_player->state = NETPLAYER_STATE_MISSION_LOADING;
8617 send_netplayer_update_packet();
8619 game_start_mission();
8621 nprintf(("Network","Done\n"));
8622 Multi_mission_loaded = 1;
8624 // update everyone about my status
8625 Net_player->state = NETPLAYER_STATE_MISSION_LOADED;
8626 send_netplayer_update_packet();
8628 if(!(Game_mode & GM_STANDALONE_SERVER)){
8629 multi_common_add_text(XSTR("Loaded mission locally\n",836),1);
8634 // if everyone has loaded the mission, randomly assign players to ships
8635 if(multi_netplayer_state_check(NETPLAYER_STATE_MISSION_LOADED) && !(Mission_sync_flags & MS_FLAG_TS_SLOTS) && !multi_endgame_ending()){
8636 // call the team select function to assign players to their ships, wings, etc
8637 multi_ts_assign_players_all();
8638 send_netplayer_slot_packet();
8641 Mission_sync_flags |= MS_FLAG_TS_SLOTS;
8644 // if everyone has loaded the mission, move to the team select stage
8645 if(Sync_test && multi_netplayer_state_check(NETPLAYER_STATE_SLOT_ACK) && !(Mission_sync_flags & MS_FLAG_PUSHED_BRIEFING) && !multi_endgame_ending()){
8646 Netgame.game_state = NETGAME_STATE_BRIEFING;
8647 send_netgame_update_packet(); // this will push everyone into the next state
8649 // the standalone moves to his own wait state, whereas in the normal game mode, the server/host moves in to the
8650 // team select state
8651 if(Game_mode & GM_STANDALONE_SERVER){
8652 gameseq_post_event(GS_EVENT_MULTI_STD_WAIT);
8654 gameseq_post_event(GS_EVENT_START_GAME);
8657 Mission_sync_flags |= MS_FLAG_PUSHED_BRIEFING;
8659 if(!(Game_mode & GM_STANDALONE_SERVER)){
8660 multi_common_add_text(XSTR("Moving to team select\n",837),1);
8664 // clients should detect here if they are doing a file xfer and do error processing
8665 if((Net_player->state == NETPLAYER_STATE_MISSION_XFER) && (Net_player->s_info.xfer_handle != -1) && !multi_endgame_ending()){
8666 switch(multi_xfer_get_status(Net_player->s_info.xfer_handle)){
8667 // if it has successfully completed, set his ok flag
8668 case MULTI_XFER_SUCCESS :
8669 // release my xfer handle
8670 multi_xfer_release_handle(Net_player->s_info.xfer_handle);
8671 Net_player->s_info.xfer_handle = -1;
8674 // if it has failed or timed-out, kick the player
8675 case MULTI_XFER_TIMEDOUT:
8676 case MULTI_XFER_FAIL:
8677 // release my xfer handle
8678 multi_xfer_release_handle(Net_player->s_info.xfer_handle);
8679 Net_player->s_info.xfer_handle = -1;
8681 // leave the game qith an error code
8682 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_XFER_FAIL);
8689 if(!(Game_mode & GM_STANDALONE_SERVER)){
8691 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8692 if(Multi_sync_bitmap != -1){
8693 gr_set_bitmap(Multi_sync_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8696 Multi_sync_window.draw();
8698 multi_sync_blit_screen_all();
8704 void multi_sync_pre_close()
8706 // at this point, we should shut down any file xfers...
8707 if(Net_player->s_info.xfer_handle != -1){
8708 nprintf(("Network","WARNING - killing file xfer while leaving mission sync state!!!\n"));
8710 multi_xfer_abort(Net_player->s_info.xfer_handle);
8711 Net_player->s_info.xfer_handle = -1;
8715 void multi_sync_post_init()
8717 multi_reset_timestamps();
8719 Multi_state_timestamp = timestamp(0);
8722 ml_string(NOX("Performing post-briefing data sync"));
8724 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8725 multi_common_add_text(XSTR("Server performing sync\n",830),1);
8727 multi_common_add_text(XSTR("Client performing sync\n",838),1);
8730 // everyone should re-initialize these
8731 init_multiplayer_stats();
8733 // reset all sequencing info
8734 multi_oo_reset_sequencing();
8736 // if I am not the master of the game, then send the firing information for my ship
8738 if ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) ){
8739 send_firing_info_packet();
8742 // if I'm not a standalone server, load up the countdown stuff
8743 if(!(Game_mode & GM_STANDALONE_SERVER)){
8744 Multi_sync_countdown_anim = NULL;
8745 Multi_sync_countdown_instance = NULL;
8746 Multi_sync_countdown_anim = anim_load(Multi_sync_countdown_fname[gr_screen.res]);
8747 if(Multi_sync_countdown_anim == NULL){
8748 nprintf(("General","WARNING!, Could not load countdown animation %s!\n",Multi_sync_countdown_fname[gr_screen.res]));
8752 // create objects for all permanent observers
8753 multi_obs_level_init();
8755 // clear the game start countdown timer
8756 Multi_sync_countdown_timer = -1.0f;
8757 Multi_sync_countdown = -1;
8759 // if this is a team vs. team mission, mark all ship teams appropriately
8760 if(Netgame.type_flags & NG_TYPE_TEAM){
8761 multi_team_mark_all_ships();
8764 Mission_sync_flags = 0;
8765 Multi_sync_launch_pressed = 0;
8768 #define MULTI_POST_TIMESTAMP 7000
8770 extern int create_wings();
8772 void multi_sync_post_do()
8776 // only if the host is also the master should he be doing this (non-standalone situation)
8777 if ( Net_player->flags & NETINFO_FLAG_AM_MASTER ) {
8779 // once everyone gets to this screen, send them the ship classes of all ships.
8780 if(multi_netplayer_state_check(NETPLAYER_STATE_WAITING) && !(Mission_sync_flags & MS_FLAG_POST_DATA)) {
8781 // only the host should ever do this
8782 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8783 // at this point we want to delete all necessary ships, change all necessary ship classes, and set all weapons up
8784 multi_ts_create_wings();
8786 // update player ets settings
8787 for(idx=0;idx<MAX_PLAYERS;idx++){
8788 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].player->objnum != -1)){
8789 multi_server_update_player_weapons(&Net_players[idx],&Ships[Objects[Net_players[idx].player->objnum].instance]);
8794 // note that this is done a little differently for standalones and nonstandalones
8795 send_post_sync_data_packet();
8797 multi_common_add_text(XSTR("Sending post briefing block information\n",839),1);
8799 Mission_sync_flags |= MS_FLAG_POST_DATA;
8802 // send weapon slots data
8803 if(multi_netplayer_state_check(NETPLAYER_STATE_POST_DATA_ACK) && !(Mission_sync_flags & MS_FLAG_WSS_SLOTS)) {
8804 // note that this is done a little differently for standalones and nonstandalones
8805 if(Netgame.type_flags & NG_TYPE_TEAM){
8806 send_wss_slots_data_packet(0,0);
8807 send_wss_slots_data_packet(1,1);
8809 send_wss_slots_data_packet(0,1);
8812 multi_common_add_text(XSTR("Sending weapon slots information\n",840),1);
8814 Mission_sync_flags |= MS_FLAG_WSS_SLOTS;
8817 // once weapon information is received, send player settings info
8818 if ( multi_netplayer_state_check(NETPLAYER_STATE_WSS_ACK) && !(Mission_sync_flags & MS_FLAG_PSETTINGS)) {
8819 send_player_settings_packet();
8821 // server (specifically, the standalone), should set this here
8822 Net_player->state = NETPLAYER_STATE_SETTINGS_ACK;
8823 send_netplayer_update_packet();
8825 multi_common_add_text(XSTR("Sending player settings packets\n",841),1);
8827 Mission_sync_flags |= MS_FLAG_PSETTINGS;
8830 // check to see if the countdown timer has started and act appropriately
8831 if( Multi_sync_countdown_timer > -1.0f ) {
8833 // increment by frametime.
8834 Multi_sync_countdown_timer += flFrametime;
8836 // if the animation is not playing, start it
8837 if(!(Game_mode & GM_STANDALONE_SERVER) && (Multi_sync_countdown_instance == NULL) && (Multi_sync_countdown_anim != NULL)){
8838 anim_play_struct aps;
8840 anim_play_init(&aps, Multi_sync_countdown_anim, Multi_sync_countdown_coords[gr_screen.res][MS_X_COORD], Multi_sync_countdown_coords[gr_screen.res][MS_Y_COORD]);
8841 aps.screen_id = GS_STATE_MULTI_MISSION_SYNC;
8842 aps.framerate_independent = 1;
8844 Multi_sync_countdown_instance = anim_play(&aps);
8847 // if the next second has expired
8848 if( Multi_sync_countdown_timer >= 1.0f ) {
8850 Multi_sync_countdown--;
8851 Multi_sync_countdown_timer = 0.0f;
8853 // if the countdown has reached 0, launch the mission
8854 if(Multi_sync_countdown == 0){
8855 Multi_sync_countdown_timer = -1.0f;
8857 Multi_sync_launch_pressed = 0;
8858 multi_sync_launch();
8860 // otherwise send a countdown packet
8862 send_countdown_packet(Multi_sync_countdown);
8867 // jump into the mission myself
8868 if((Multi_sync_countdown == 0) && multi_netplayer_state_check(NETPLAYER_STATE_IN_MISSION)){
8869 if(!((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !Multi_sync_launch_pressed)){
8870 Net_player->state = NETPLAYER_STATE_IN_MISSION;
8871 Netgame.game_state = NETGAME_STATE_IN_MISSION;
8872 gameseq_post_event(GS_EVENT_ENTER_GAME);
8874 multi_common_add_text(XSTR("Moving into game\n",842),1);
8878 // maybe start the animation countdown
8879 if((Multi_sync_countdown >= 0) && (Multi_sync_countdown_instance == NULL) && (Multi_sync_countdown_anim != NULL)){
8880 anim_play_struct aps;
8882 anim_play_init(&aps, Multi_sync_countdown_anim, Multi_sync_countdown_coords[gr_screen.res][MS_X_COORD], Multi_sync_countdown_coords[gr_screen.res][MS_Y_COORD]);
8883 aps.screen_id = GS_STATE_MULTI_MISSION_SYNC;
8884 aps.framerate_independent = 1;
8886 Multi_sync_countdown_instance = anim_play(&aps);
8890 // host - specific stuff
8891 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8892 // create the launch button so the host can click
8893 if( Sync_test && multi_netplayer_state_check(NETPLAYER_STATE_SETTINGS_ACK) ){
8894 multi_sync_create_launch_button();
8899 if(!(Game_mode & GM_STANDALONE_SERVER)){
8901 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8902 if(Multi_sync_bitmap != -1){
8903 gr_set_bitmap(Multi_sync_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8906 Multi_sync_window.draw();
8908 multi_sync_blit_screen_all();
8914 void multi_sync_post_close()
8918 // if I'm not a standalone server, unload up the countdown stuff
8919 if(!(Game_mode & GM_STANDALONE_SERVER)){
8920 // release all rendering animation instances (should only be 1)
8921 anim_release_all_instances(GS_STATE_MULTI_MISSION_SYNC);
8922 Multi_sync_countdown_instance = NULL;
8924 // free up the countdown animation
8925 if(Multi_sync_countdown_anim != NULL){
8926 anim_free(Multi_sync_countdown_anim);
8927 Multi_sync_countdown_anim = NULL;
8931 // all players should reset sequencing
8932 for(idx=0;idx<MAX_PLAYERS;idx++){
8933 if(Net_player->flags & NETINFO_FLAG_CONNECTED){
8934 Net_players[idx].client_cinfo_seq = 0;
8935 Net_players[idx].client_server_seq = 0;
8939 // multiplayer dogfight
8940 multi_df_level_pre_enter();
8942 // clients should clear obj_pair array and add pair for themselves
8944 if ( MULTIPLAYER_CLIENT ) {
8946 obj_add_pairs( OBJ_INDEX(Player_obj) );
8951 void multi_sync_display_name(const char *name, int index, int np_index)
8953 char fit[CALLSIGN_LEN];
8955 // make sure the string actually fits
8956 SDL_strlcpy(fit, name, SDL_arraysize(fit));
8958 // if we're in team vs. team mode
8959 if(Netgame.type_flags & NG_TYPE_TEAM){
8960 gr_force_fit_string(fit,CALLSIGN_LEN, Ms_status2_coords[gr_screen.res][MS_X_COORD] - Ms_status_coords[gr_screen.res][MS_X_COORD] - 20 - Ms_cd_icon_offset[gr_screen.res] - Ms_team_icon_offset[gr_screen.res]);
8962 // if this is the currently selected player, draw him highlighted
8963 if(np_index == Multi_sync_player_select){
8964 gr_set_color_fast(&Color_text_selected);
8966 gr_set_color_fast(&Color_text_normal);
8970 gr_string(Ms_status_coords[gr_screen.res][0] + Ms_cd_icon_offset[gr_screen.res] + Ms_team_icon_offset[gr_screen.res], Ms_status_coords[gr_screen.res][MS_Y_COORD] + (index * 10),fit);
8972 // blit his team icon
8974 if(Net_players[np_index].p_info.team == 0){
8975 // blit the team captain icon
8976 if(Net_players[np_index].flags & NETINFO_FLAG_TEAM_CAPTAIN){
8977 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
8978 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8979 gr_bitmap(Ms_status_coords[gr_screen.res][MS_X_COORD] + Ms_cd_icon_offset[gr_screen.res], Ms_status_coords[gr_screen.res][MS_Y_COORD] + (index * 10) - 2);
8982 // normal team member icon
8984 if(Multi_common_icons[MICON_TEAM0] != -1){
8985 gr_set_bitmap(Multi_common_icons[MICON_TEAM0], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8986 gr_bitmap(Ms_status_coords[gr_screen.res][MS_X_COORD] + Ms_cd_icon_offset[gr_screen.res], Ms_status_coords[gr_screen.res][MS_Y_COORD] + (index * 10) - 2);
8991 else if(Net_players[np_index].p_info.team == 1){
8992 // blit the team captain icon
8993 if(Net_players[np_index].flags & NETINFO_FLAG_TEAM_CAPTAIN){
8994 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
8995 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8996 gr_bitmap(Ms_status_coords[gr_screen.res][MS_X_COORD] + Ms_cd_icon_offset[gr_screen.res], Ms_status_coords[gr_screen.res][MS_Y_COORD] + (index * 10) - 2);
8999 // normal team member icon
9001 if(Multi_common_icons[MICON_TEAM1] != -1){
9002 gr_set_bitmap(Multi_common_icons[MICON_TEAM1], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
9003 gr_bitmap(Ms_status_coords[gr_screen.res][MS_X_COORD] + Ms_cd_icon_offset[gr_screen.res], Ms_status_coords[gr_screen.res][MS_Y_COORD] + (index * 10) - 2);
9008 gr_force_fit_string(fit, CALLSIGN_LEN, Ms_status2_coords[gr_screen.res][MS_X_COORD] - Ms_status_coords[gr_screen.res][MS_X_COORD] - 20 - Ms_cd_icon_offset[gr_screen.res]);
9010 // if this is the currently selected player, draw him highlighted
9011 if(np_index == Multi_sync_player_select){
9012 gr_set_color_fast(&Color_text_selected);
9014 gr_set_color_fast(&Color_text_normal);
9018 gr_string(Ms_status_coords[gr_screen.res][MS_X_COORD] + Ms_cd_icon_offset[gr_screen.res], Ms_status_coords[gr_screen.res][MS_Y_COORD] + (index * 10),fit);
9021 // maybe blit his CD status icon
9022 if((Net_players[np_index].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
9023 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
9024 gr_bitmap(Ms_status_coords[gr_screen.res][MS_X_COORD], Ms_status_coords[gr_screen.res][MS_Y_COORD] + (index * 10));
9028 void multi_sync_display_status(const char *status, int index)
9032 // make sure the string actually fits
9033 SDL_strlcpy(fit, status, SDL_arraysize(fit));
9034 gr_force_fit_string(fit, 250, Ms_status2_coords[gr_screen.res][MS_W_COORD] - 20);
9035 gr_set_color_fast(&Color_bright);
9036 gr_string(Ms_status2_coords[gr_screen.res][MS_X_COORD], Ms_status2_coords[gr_screen.res][MS_Y_COORD] + (index * 10), fit);
9039 void multi_sync_force_start_pre()
9042 int want_state = NETPLAYER_STATE_SLOT_ACK; // kick any players who are still in this state
9044 // go through the player list and boot anyone who isn't in the right state
9045 for(idx=0;idx<MAX_PLAYERS;idx++){
9046 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx]) && (Net_players[idx].state == want_state)){
9047 multi_kick_player(idx,0);
9052 void multi_sync_force_start_post()
9056 int num_kill_states;
9058 // determine the state we want all players in so that we can find those who are not in the state
9059 kill_state[0] = NETPLAYER_STATE_BRIEFING;
9060 kill_state[1] = NETPLAYER_STATE_SHIP_SELECT;
9061 kill_state[2] = NETPLAYER_STATE_WEAPON_SELECT;
9062 num_kill_states = 3;
9064 // go through the player list and boot anyone who isn't in the right state
9065 for(idx=0;idx<MAX_PLAYERS;idx++){
9066 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx])){
9067 // check against all kill state
9068 for(idx2 = 0;idx2<num_kill_states;idx2++){
9069 if(Net_players[idx].state == kill_state[idx2]){
9070 multi_kick_player(idx,0);
9078 void multi_sync_start_countdown()
9080 // don't allow repeat button presses
9081 if(Multi_sync_launch_pressed){
9085 Multi_sync_launch_pressed = 1;
9087 // if I'm the server, begin the countdown
9088 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
9089 gamesnd_play_iface(SND_COMMIT_PRESSED);
9090 Multi_sync_countdown_timer = 0.0f;
9091 Multi_sync_countdown = MULTI_SYNC_COUNTDOWN_TIME;
9093 // send an initial countdown value
9094 send_countdown_packet(Multi_sync_countdown);
9096 // otherwise send the "start countdown" packet to the standalone
9098 SDL_assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
9099 send_countdown_packet(-1);
9103 void multi_sync_launch()
9105 // don't allow repeat button presses
9106 if(Multi_sync_launch_pressed){
9110 Multi_sync_launch_pressed = 1;
9113 ml_printf(NOX("Entering mission %s"), Game_current_mission_filename);
9115 // tell everyone to jump into the mission
9116 send_jump_into_mission_packet();
9117 Multi_state_timestamp = timestamp(MULTI_POST_TIMESTAMP);
9119 // set the # of players at the start of the mission
9120 Multi_num_players_at_start = multi_num_players();
9121 nprintf(("Network","# of players at start of mission : %d\n", Multi_num_players_at_start));
9123 // initialize datarate limiting for all clients
9124 multi_oo_rate_init_all();
9126 multi_common_add_text(XSTR("Sending mission start packet\n",843),1);
9129 void multi_sync_create_launch_button()
9131 if (!Multi_launch_button_created) {
9132 // create the object
9133 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.create(&Multi_sync_window, "", Multi_sync_buttons[gr_screen.res][MS_LAUNCH].x, Multi_sync_buttons[gr_screen.res][MS_LAUNCH].y, 1, 1, 0, 1);
9135 // set the sound to play when highlighted
9136 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_highlight_action(common_play_highlight_sound);
9138 // set the ani for the button
9139 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_bmaps(Multi_sync_buttons[gr_screen.res][MS_LAUNCH].filename, 3, 0);
9142 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.link_hotspot(Multi_sync_buttons[gr_screen.res][MS_LAUNCH].hotspot);
9145 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_hotkey(KEY_CTRLED+SDLK_RETURN);
9148 // create the text for the button
9149 Multi_sync_window.add_XSTR(&Multi_sync_text[gr_screen.res][MST_LAUNCH]);
9152 // increment the button count so we start checking this one
9153 Multi_sync_button_count++;
9155 Multi_launch_button_created = 1;
9159 void multi_sync_handle_plist()
9165 // if we don't have a currently selected player, select one
9166 if((Multi_sync_player_select < 0) || !MULTI_CONNECTED(Net_players[Multi_sync_player_select])){
9167 for(idx=0;idx<MAX_PLAYERS;idx++){
9168 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
9169 Multi_sync_player_select = idx;
9175 // check for button list presses
9176 if(Multi_sync_plist_button.pressed()){
9177 // get the y mouse coords
9178 Multi_sync_plist_button.get_mouse_pos(NULL,&my);
9180 // get the index of the item selected
9181 select_index = my / 10;
9183 // if the index is greater than the current # connections, do nothing
9184 if(select_index > (multi_num_connections() - 1)){
9188 // translate into an absolute Net_players[] index (get the Nth net player)
9189 Multi_sync_player_select = -1;
9190 for(idx=0;idx<MAX_PLAYERS;idx++){
9191 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
9195 // if we've found the item we're looking for
9196 if(select_index < 0){
9197 Multi_sync_player_select = idx;
9202 // if for some bizarre reason, this is an invalid player, unselect him and wait for the next interation
9204 if((Multi_sync_player_select >= 0) && (!MULTI_CONNECTED(Net_players[Multi_sync_player_select]) || MULTI_STANDALONE(Net_players[Multi_sync_player_select])) ){
9205 Multi_sync_player_select = -1;
9211 // -------------------------------------------------------------------------------------------------------------
9213 // MULTIPLAYER DEBRIEF SCREEN
9216 // other relevant data
9217 int Multi_debrief_accept_hit;
9218 int Multi_debrief_replay_hit;
9220 // set if the server has left the game
9221 int Multi_debrief_server_left = 0;
9223 // if we've reported on TvT status all players are in the debrief
9224 int Multi_debrief_reported_tvt = 0;
9226 // whether stats are being accepted
9227 // -1 == no decision yet
9230 int Multi_debrief_stats_accept_code = -1;
9232 int Multi_debrief_server_framecount = 0;
9234 float Multi_debrief_time = 0.0f;
9235 float Multi_debrief_resend_time = 10.0f;
9237 void multi_debrief_init()
9241 Multi_debrief_time = 0.0f;
9242 Multi_debrief_resend_time = 10.0f;
9244 // do this to notify the standalone or the normal server that we're in the debrief state and ready to receive packets
9245 if (!(Net_player->flags & NETINFO_FLAG_AM_MASTER)) {
9246 Net_player->state = NETPLAYER_STATE_DEBRIEF;
9247 send_netplayer_update_packet();
9250 // unflag some stuff
9251 for(idx=0;idx<MAX_PLAYERS;idx++){
9252 if(MULTI_CONNECTED(Net_players[idx])){
9253 Net_players[idx].flags &= ~(NETINFO_FLAG_RESPAWNING | NETINFO_FLAG_LIMBO | NETINFO_FLAG_WARPING_OUT);
9257 // if text input mode is active, clear it
9258 multi_msg_text_flush();
9260 // the server has not left yet
9261 Multi_debrief_server_left = 0;
9263 // have not hit accept or replay yet
9264 Multi_debrief_accept_hit = 0;
9265 Multi_debrief_replay_hit = 0;
9267 // stats have not been accepted yet
9268 Multi_debrief_stats_accept_code = -1;
9270 // mark stats as not being store yet
9271 Netgame.flags &= ~(NG_FLAG_STORED_MT_STATS);
9273 // no report on TvT yet
9274 Multi_debrief_reported_tvt = 0;
9276 Multi_debrief_server_framecount = 0;
9279 void multi_debrief_do_frame()
9281 Multi_debrief_time += flFrametime;
9283 // set the netgame state to be debriefing when appropriate
9284 if((Net_player->flags & NETINFO_FLAG_AM_MASTER) && (Netgame.game_state != NETGAME_STATE_DEBRIEF) && multi_netplayer_state_check3(NETPLAYER_STATE_DEBRIEF, NETPLAYER_STATE_DEBRIEF_ACCEPT, NETPLAYER_STATE_DEBRIEF_REPLAY)){
9285 Netgame.game_state = NETGAME_STATE_DEBRIEF;
9286 send_netgame_update_packet();
9289 // evaluate all server stuff
9290 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
9291 multi_debrief_server_process();
9295 void multi_debrief_close()
9297 if ( MULTIPLAYER_CLIENT && (Netgame.game_state == NETGAME_STATE_MISSION_SYNC) ){
9298 gamesnd_play_iface( SND_COMMIT_PRESSED );
9302 // handle optional mission loop
9303 void multi_maybe_set_mission_loop()
9305 int cur = Campaign.current_mission;
9306 if (Campaign.missions[cur].has_mission_loop) {
9307 SDL_assert(Campaign.loop_mission != CAMPAIGN_LOOP_MISSION_UNINITIALIZED);
9309 bool require_repeat_mission = (Campaign.current_mission == Campaign.next_mission);
9311 // check for (1) mission loop available, (2) dont have to repeat last mission
9312 if ( (Campaign.missions[cur].has_mission_loop && (Campaign.loop_mission != -1)) && !require_repeat_mission ) {
9315 debrief_assemble_optional_mission_popup_text(buffer, SDL_arraysize(buffer), Campaign.missions[cur].mission_loop_desc);
9317 int choice = popup(0 , 2, POPUP_NO, POPUP_YES, buffer);
9319 Campaign.loop_enabled = 1;
9320 Campaign.next_mission = Campaign.loop_mission;
9325 // handle all cases for when the accept key is hit in a multiplayer debriefing
9326 void multi_debrief_accept_hit()
9328 // if we already accepted, do nothing
9329 // but he may need to hit accept again after the server has left the game, so allow this
9330 if(Multi_debrief_accept_hit){
9334 // mark this so that we don't hit it again
9335 Multi_debrief_accept_hit = 1;
9337 gamesnd_play_iface(SND_COMMIT_PRESSED);
9339 // if the server has left the game, always just end the game.
9340 if(Multi_debrief_server_left){
9341 if(!multi_quit_game(PROMPT_ALL)){
9342 Multi_debrief_server_left = 1;
9343 Multi_debrief_accept_hit = 0;
9345 Multi_debrief_server_left = 0;
9348 // query the host and see if he wants to accept stats
9349 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
9350 // if we're on a tracker game, he gets no choice for storing stats
9351 if(MULTI_IS_TRACKER_GAME){
9353 int stats_saved = multi_fs_tracker_store_stats();
9355 if (Netgame.type_flags & NG_TYPE_SW) {
9356 multi_sw_report(stats_saved);
9359 multi_fs_tracker_store_stats();
9362 multi_maybe_set_mission_loop();
9364 int res = popup(PF_TITLE | PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON | PF_IGNORE_ESC,3,XSTR("&Cancel",779),XSTR("&Accept",844),XSTR("&Toss",845),XSTR("(Continue Netgame)\nDo you wish to accept these stats?",846));
9366 // evaluate the result
9371 Multi_debrief_accept_hit = 0;
9374 // set the accept code to be "not accepting"
9376 multi_debrief_stats_toss();
9377 multi_maybe_set_mission_loop();
9380 // accept the stats and continue
9382 multi_debrief_stats_accept();
9383 multi_maybe_set_mission_loop();
9389 // set my netplayer state to be "debrief_accept", and be done with it
9390 Net_player->state = NETPLAYER_STATE_DEBRIEF_ACCEPT;
9391 send_netplayer_update_packet();
9395 // handle all cases for when the escape key is hit in a multiplayer debriefing
9396 void multi_debrief_esc_hit()
9400 // if the server has left
9401 if(Multi_debrief_server_left){
9402 multi_quit_game(PROMPT_ALL);
9407 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
9408 // if the stats have already been accepted
9409 if((Multi_debrief_stats_accept_code != -1) || (MULTI_IS_TRACKER_GAME)){
9410 if (Multi_debrief_stats_accept_code == 1) {
9412 int stats_saved = multi_fs_tracker_store_stats();
9414 if (Netgame.type_flags & NG_TYPE_SW) {
9415 multi_sw_report(stats_saved);
9418 multi_fs_tracker_store_stats();
9422 multi_quit_game(PROMPT_HOST);
9424 res = popup(PF_TITLE | PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON | PF_IGNORE_ESC,3,XSTR("&Cancel",779),XSTR("&Accept",844),XSTR("&Toss",845),XSTR("(Exit Netgame)\nDo you wish to accept these stats?",847));
9426 // evaluate the result
9433 // set the accept code to be "not accepting"
9435 multi_debrief_stats_toss();
9436 multi_quit_game(PROMPT_NONE);
9439 // accept the stats and continue
9441 multi_debrief_stats_accept();
9442 multi_quit_game(PROMPT_NONE);
9447 // if the stats haven't been accepted yet, or this is a tracker game
9448 if((Multi_debrief_stats_accept_code == -1) && !(MULTI_IS_TRACKER_GAME)){
9449 res = popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON,2,XSTR("&Cancel",779),XSTR("&Leave",848),XSTR("Are you sure you want to leave the netgame before stats are stored?",849));
9451 // evaluate the result
9453 multi_quit_game(PROMPT_NONE);
9456 // otherwise go through the normal endgame channels
9458 multi_quit_game(PROMPT_ALL);
9463 void multi_debrief_replay_hit()
9465 // only the host should ever get here
9466 SDL_assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
9468 // if the button was already pressed, do nothing
9469 if(Multi_debrief_accept_hit){
9473 // same as hittin the except button except no stats are kept
9474 Multi_debrief_accept_hit = 1;
9476 // mark myself as being in the replay state so we know what to do next
9477 Net_player->state = NETPLAYER_STATE_DEBRIEF_REPLAY;
9478 send_netplayer_update_packet();
9481 // call this when the server has left and we would otherwise be saying "contact lost with server
9482 void multi_debrief_server_left()
9485 Multi_debrief_server_left = 1;
9487 // undo any "accept" hit so that clients can hit accept again to leave
9488 Multi_debrief_accept_hit = 0;
9491 void multi_debrief_stats_accept()
9493 // don't do anything if we've already accepted
9494 if(Multi_debrief_stats_accept_code != -1){
9498 Multi_debrief_stats_accept_code = 1;
9500 // if we're the host, and we're on a standalone, tell the standalone to begin the stats storing process
9501 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) || (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
9502 // send a packet to the players telling them to store their stats
9503 send_store_stats_packet(1);
9506 // add a chat line saying "stats have been accepted"
9507 multi_display_chat_msg(XSTR("<stats have been accepted>",850),0,0);
9510 ml_string(NOX("Stats stored"));
9513 void multi_debrief_stats_toss()
9515 // don't do anything if we've already accepted
9516 if(Multi_debrief_stats_accept_code != -1){
9520 Multi_debrief_stats_accept_code = 0;
9522 // if we're the host, and we're on a standalone, tell everyone to "toss" stats
9523 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) || (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
9524 // send a packet to the players telling them to store their stats
9525 send_store_stats_packet(0);
9528 // add a chat line saying "stats have been accepted"
9529 multi_display_chat_msg(XSTR("<stats have been tossed>",851),0,0);
9532 ml_string(NOX("Stats tossed"));
9535 int multi_debrief_stats_accept_code()
9537 return Multi_debrief_stats_accept_code;
9540 void multi_debrief_server_process()
9543 int player_status,other_status;
9545 Multi_debrief_server_framecount++;
9547 // if we're > 10 seconds into the debrief and not everyone is here, try warping everyone out again
9548 if((Multi_debrief_time >= Multi_debrief_resend_time) && !multi_netplayer_state_check3(NETPLAYER_STATE_DEBRIEF, NETPLAYER_STATE_DEBRIEF_ACCEPT, NETPLAYER_STATE_DEBRIEF_REPLAY, 1)){
9549 // find all players who are not in the debrief state and hit them with the endgame packet
9550 for(idx=0; idx<MAX_PLAYERS; idx++){
9551 if( MULTI_CONNECTED(Net_players[idx]) && (Net_player != &Net_players[idx]) && ((Net_players[idx].state != NETPLAYER_STATE_DEBRIEF) || (Net_players[idx].state != NETPLAYER_STATE_DEBRIEF_ACCEPT) || (Net_players[idx].state != NETPLAYER_STATE_DEBRIEF_REPLAY)) ){
9552 send_endgame_packet(&Net_players[idx]);
9557 Multi_debrief_resend_time += 7.0f;
9560 // evaluate the status of all players in the game (0 == not ready, 1 == ready to continue, 2 == ready to replay)
9563 // check all players
9564 for(idx=0;idx<MAX_PLAYERS;idx++){
9565 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_HOST(Net_players[idx])){
9566 if(Net_players[idx].state != NETPLAYER_STATE_DEBRIEF_ACCEPT){
9573 // if we haven't already reported TvT results
9574 if(multi_netplayer_state_check3(NETPLAYER_STATE_DEBRIEF, NETPLAYER_STATE_DEBRIEF_ACCEPT, NETPLAYER_STATE_DEBRIEF_REPLAY) && (Netgame.type_flags & NG_TYPE_TEAM) && !Multi_debrief_reported_tvt && (Multi_debrief_server_framecount > 4)){
9575 multi_team_report();
9576 Multi_debrief_reported_tvt = 1;
9579 // if all other players are good to go, check the host
9581 // if he is ready to continue
9582 if(Netgame.host->state == NETPLAYER_STATE_DEBRIEF_ACCEPT){
9585 // if he wants to replay the mission
9586 else if(Netgame.host->state == NETPLAYER_STATE_DEBRIEF_REPLAY){
9589 // if he is not ready
9594 // if all players are _not_ good to go
9599 // if we're in the debriefing state in a campaign mode, process accordingly
9600 if(Netgame.campaign_mode == MP_CAMPAIGN){
9601 multi_campaign_do_debrief(player_status);
9603 // otherwise process as normal (looking for all players to be ready to go to the next mission
9605 if(player_status == 1){
9606 multi_flush_mission_stuff();
9608 // set the netgame state to be forming and continue
9609 Netgame.game_state = NETGAME_STATE_FORMING;
9610 send_netgame_update_packet();
9612 // move to the proper state
9613 if(Game_mode & GM_STANDALONE_SERVER){
9614 gameseq_post_event(GS_EVENT_STANDALONE_MAIN);
9616 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
9619 multi_reset_timestamps();
9620 } else if(player_status == 2){
9621 multi_flush_mission_stuff();
9623 // tell everyone to move into the pre-briefing sync state
9624 Netgame.game_state = NETGAME_STATE_MISSION_SYNC;
9625 send_netgame_update_packet();
9627 // move back to the mission sync screen for the same mission again
9628 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
9629 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
9631 multi_reset_timestamps();
9637 // -------------------------------------------------------------------------------------------------------------
9639 // MULTIPLAYER PASSWORD POPUP
9644 static const char *Multi_pwd_bitmap_fname[GR_NUM_RESOLUTIONS] = {
9645 "Password", // GR_640
9646 "2_Password" // GR_1024
9649 static const char *Multi_pwd_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
9650 "Password-M", // GR_640
9651 "2_Password-M" // GR_1024
9656 // constants for coordinate lookup
9657 #define MPWD_X_COORD 0
9658 #define MPWD_Y_COORD 1
9659 #define MPWD_W_COORD 2
9660 #define MPWD_H_COORD 3
9663 #define MULTI_PWD_NUM_BUTTONS 2
9664 #define MPWD_CANCEL 0
9665 #define MPWD_COMMIT 1
9667 // password area defs
9668 int Mpwd_coords[GR_NUM_RESOLUTIONS][4] = {
9681 UI_WINDOW Multi_pwd_window; // the window object for the join screen
9682 UI_INPUTBOX Multi_pwd_passwd; // for Netgame.passwd
9683 int Multi_pwd_bitmap; // the background bitmap
9684 int Multi_passwd_background = -1;
9685 int Multi_passwd_done = -1;
9686 int Multi_passwd_running = 0;
9689 ui_button_info Multi_pwd_buttons[GR_NUM_RESOLUTIONS][MULTI_PWD_NUM_BUTTONS] = {
9692 ui_button_info("PWB_00", 402, 134, -1, -1, 0),
9693 ui_button_info("PWB_01", 450, 134, -1, -1, 1),
9695 ui_button_info("PWB_00", 411, 151, 405, 141, 0),
9696 ui_button_info("PWB_01", 460, 151, 465, 141, 1),
9700 ui_button_info("2_PWB_00", 659, 242, 649, 225, 0),
9701 ui_button_info("2_PWB_01", 737, 242, 736, 225, 1),
9707 #define MULTI_PWD_NUM_TEXT 3
9709 UI_XSTR Multi_pwd_text[GR_NUM_RESOLUTIONS][MULTI_PWD_NUM_TEXT] = {
9711 { "Cancel", 387, 400, 141, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[0][MPWD_CANCEL].button},
9712 { "Commit", 1062, 455, 141, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[0][MPWD_COMMIT].button},
9713 { "Enter Password", 1332, 149, 92, UI_XSTR_COLOR_GREEN, -1, NULL},
9716 { "Cancel", 387, 649, 225, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[1][MPWD_CANCEL].button},
9717 { "Commit", 1062, 736, 225, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[1][MPWD_COMMIT].button},
9718 { "Enter Password", 1332, 239, 148, UI_XSTR_COLOR_GREEN, -1, NULL},
9723 // initialize all graphics, etc
9724 void multi_passwd_init()
9728 // store the background as it currently is
9729 Multi_passwd_background = gr_save_screen();
9731 // create the interface window
9732 Multi_pwd_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
9733 Multi_pwd_window.set_mask_bmap(Multi_pwd_bitmap_mask_fname[gr_screen.res]);
9735 // load the background bitmap
9736 Multi_pwd_bitmap = bm_load(Multi_pwd_bitmap_fname[gr_screen.res]);
9737 if(Multi_pwd_bitmap < 0){
9738 // we failed to load the bitmap - this is very bad
9742 // create the interface buttons
9743 for(idx=0; idx<MULTI_PWD_NUM_BUTTONS; idx++){
9744 // create the object
9745 Multi_pwd_buttons[gr_screen.res][idx].button.create(&Multi_pwd_window, "", Multi_pwd_buttons[gr_screen.res][idx].x, Multi_pwd_buttons[gr_screen.res][idx].y, 1, 1, 0, 1);
9747 // set the sound to play when highlighted
9748 Multi_pwd_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
9750 // set the ani for the button
9751 Multi_pwd_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pwd_buttons[gr_screen.res][idx].filename);
9754 Multi_pwd_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pwd_buttons[gr_screen.res][idx].hotspot);
9759 for(idx=0; idx<MULTI_PWD_NUM_TEXT; idx++){
9760 Multi_pwd_window.add_XSTR(&Multi_pwd_text[gr_screen.res][idx]);
9764 // create the password input box
9765 Multi_pwd_passwd.create(&Multi_pwd_window, Mpwd_coords[gr_screen.res][MPWD_X_COORD], Mpwd_coords[gr_screen.res][MPWD_Y_COORD],Mpwd_coords[gr_screen.res][MPWD_W_COORD], MAX_PASSWD_LEN, "", UI_INPUTBOX_FLAG_ESC_CLR | UI_INPUTBOX_FLAG_INVIS, -1, &Color_normal);
9766 Multi_pwd_passwd.set_focus();
9768 // link the enter key to ACCEPT
9769 Multi_pwd_buttons[gr_screen.res][MPWD_COMMIT].button.set_hotkey(SDLK_RETURN);
9771 Multi_passwd_done = -1;
9772 Multi_passwd_running = 1;
9775 // close down all graphics, etc
9776 void multi_passwd_close()
9778 // unload any bitmaps
9779 bm_release(Multi_pwd_bitmap);
9781 // destroy the UI_WINDOW
9782 Multi_pwd_window.destroy();
9784 // free up the saved background screen
9785 if(Multi_passwd_background >= 0){
9786 gr_free_screen(Multi_passwd_background);
9787 Multi_passwd_background = -1;
9790 Multi_passwd_running = 0;
9793 // process any button pressed
9794 void multi_passwd_process_buttons()
9796 // if the accept button was pressed
9797 if(Multi_pwd_buttons[gr_screen.res][MPWD_COMMIT].button.pressed()){
9798 gamesnd_play_iface(SND_USER_SELECT);
9799 Multi_passwd_done = 1;
9802 // if the cancel button was pressed
9803 if(Multi_pwd_buttons[gr_screen.res][MPWD_CANCEL].button.pressed()){
9804 gamesnd_play_iface(SND_USER_SELECT);
9805 Multi_passwd_done = 0;
9809 // run the passwd popup
9810 void multi_passwd_do(char *passwd, const int max_passlen)
9814 while(Multi_passwd_done == -1){
9815 // set frametime and run background stuff
9816 game_set_frametime(-1);
9817 game_do_state_common(gameseq_get_state());
9819 k = Multi_pwd_window.process();
9821 // process any keypresses
9824 // set this to indicate the user has cancelled for one reason or another
9825 Multi_passwd_done = 0;
9829 // if the input box text has changed
9830 if(Multi_pwd_passwd.changed()){
9831 SDL_strlcpy(passwd, "", max_passlen);
9832 Multi_pwd_passwd.get_text(passwd);
9835 // process any button pressed
9836 multi_passwd_process_buttons();
9838 // draw the background, etc
9841 if(Multi_passwd_background >= 0){
9842 gr_restore_screen(Multi_passwd_background);
9844 gr_set_bitmap(Multi_pwd_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
9846 Multi_pwd_window.draw();
9853 // bring up the password string popup, fill in passwd (return 1 if accept was pressed, 0 if cancel was pressed)
9854 int multi_passwd_popup(char *passwd, const int max_plen)
9856 // if the popup is already running for some reason, don't do anything
9857 if(Multi_passwd_running){
9861 // initialize all graphics
9862 multi_passwd_init();
9865 multi_passwd_do(passwd, max_plen);
9867 // shut everything down
9868 multi_passwd_close();
9870 return Multi_passwd_done;