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_gui.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"
531 // -------------------------------------------------------------------------------------------------------------
533 // MULTIPLAYER COMMON interface controls
536 // the common text info box stuff. This is lifted almost directly from Alans briefing code (minus the spiffy colored, scrolling
538 int Multi_common_text_coords[GR_NUM_RESOLUTIONS][4] = {
551 int Multi_common_text_max_display[GR_NUM_RESOLUTIONS] = {
560 #define MULTI_COMMON_TEXT_META_CHAR '$'
561 #define MULTI_COMMON_TEXT_MAX_LINE_LENGTH 100
562 #define MULTI_COMMON_TEXT_MAX_LINES 20
563 #define MULTI_COMMON_MAX_TEXT (MULTI_COMMON_TEXT_MAX_LINES * MULTI_COMMON_TEXT_MAX_LINE_LENGTH)
565 char Multi_common_all_text[MULTI_COMMON_MAX_TEXT];
566 char Multi_common_text[MULTI_COMMON_TEXT_MAX_LINES][MULTI_COMMON_TEXT_MAX_LINE_LENGTH];
568 int Multi_common_top_text_line = -1; // where to start displaying from
569 int Multi_common_num_text_lines = 0; // how many lines we have
571 void multi_common_scroll_text_up();
572 void multi_common_scroll_text_down();
573 void multi_common_move_to_bottom();
574 void multi_common_render_text();
575 void multi_common_split_text();
577 #define MAX_IP_STRING 255 // maximum length for ip string
579 void multi_common_scroll_text_up()
581 Multi_common_top_text_line--;
582 if ( Multi_common_top_text_line < 0 ) {
583 Multi_common_top_text_line = 0;
584 if ( !mouse_down(MOUSE_LEFT_BUTTON) )
585 gamesnd_play_iface(SND_GENERAL_FAIL);
588 gamesnd_play_iface(SND_SCROLL);
592 void multi_common_scroll_text_down()
594 Multi_common_top_text_line++;
595 if ( (Multi_common_num_text_lines - Multi_common_top_text_line) < Multi_common_text_max_display[gr_screen.res] ) {
596 Multi_common_top_text_line--;
597 if ( !mouse_down(MOUSE_LEFT_BUTTON) ){
598 gamesnd_play_iface(SND_GENERAL_FAIL);
601 gamesnd_play_iface(SND_SCROLL);
605 void multi_common_move_to_bottom()
607 // if there's nowhere to scroll down, do nothing
608 if(Multi_common_num_text_lines <= Multi_common_text_max_display[gr_screen.res]){
612 Multi_common_top_text_line = Multi_common_num_text_lines - Multi_common_text_max_display[gr_screen.res];
615 void multi_common_set_text(const char *str, int auto_scroll)
618 // store the entire string as well
619 if(strlen(str) > MULTI_COMMON_MAX_TEXT){
622 strcpy(Multi_common_all_text,str);
625 // split the whole thing up
626 multi_common_split_text();
628 // scroll to the bottom if we're supposed to
630 multi_common_move_to_bottom();
634 void multi_common_add_text(const char *str, int auto_scroll)
637 // store the entire string as well
638 if((strlen(str) + strlen(Multi_common_all_text)) > MULTI_COMMON_MAX_TEXT){
641 strcat(Multi_common_all_text,str);
644 // split the whole thing up
645 multi_common_split_text();
647 // scroll to the bottom if we're supposed to
649 multi_common_move_to_bottom();
653 void multi_common_split_text()
656 int n_chars[MAX_BRIEF_LINES];
657 char *p_str[MAX_BRIEF_LINES];
659 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);
660 SDL_assert(n_lines != -1);
662 for ( i = 0; i < n_lines; i++ ) {
663 SDL_assert(n_chars[i] < MULTI_COMMON_TEXT_MAX_LINE_LENGTH);
664 strncpy(Multi_common_text[i], p_str[i], n_chars[i]);
665 Multi_common_text[i][n_chars[i]] = 0;
666 drop_leading_white_space(Multi_common_text[i]);
669 Multi_common_top_text_line = 0;
670 Multi_common_num_text_lines = n_lines;
673 void multi_common_render_text()
675 int i, fh, line_count;
677 fh = gr_get_font_height();
680 gr_set_color_fast(&Color_text_normal);
681 for ( i = Multi_common_top_text_line; i < Multi_common_num_text_lines; i++ ) {
682 if ( line_count >= Multi_common_text_max_display[gr_screen.res] ){
685 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]);
689 if ( (Multi_common_num_text_lines - Multi_common_top_text_line) > Multi_common_text_max_display[gr_screen.res] ) {
690 gr_set_color_fast(&Color_bright_red);
691 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));
695 // common notification messaging stuff
696 #define MULTI_COMMON_NOTIFY_TIME 3500
697 int Multi_common_join_y[GR_NUM_RESOLUTIONS] = {
701 int Multi_common_create_y[GR_NUM_RESOLUTIONS] = {
706 int Multi_common_jw_y[GR_NUM_RESOLUTIONS] = {
711 int Multi_common_msg_y[GR_NUM_RESOLUTIONS] = {
716 char Multi_common_notify_text[200];
717 int Multi_common_notify_stamp;
719 void multi_common_notify_init()
721 strcpy(Multi_common_notify_text,"");
722 Multi_common_notify_stamp = -1;
725 // add a notification string, drawing appropriately depending on the state/screen we're in
726 void multi_common_add_notify(const char *str)
729 strcpy(Multi_common_notify_text,str);
730 Multi_common_notify_stamp = timestamp(MULTI_COMMON_NOTIFY_TIME);
734 // process/display notification messages
735 void multi_common_notify_do()
737 if(Multi_common_notify_stamp != -1){
738 if(timestamp_elapsed(Multi_common_notify_stamp)){
739 Multi_common_notify_stamp = -1;
742 gr_get_string_size(&w,&h,Multi_common_notify_text);
743 gr_set_color_fast(&Color_white);
745 // determine where it should be placed based upon which screen we're on
747 switch(gameseq_get_state()){
748 case GS_STATE_MULTI_JOIN_GAME :
749 y = Multi_common_join_y[gr_screen.res];
751 case GS_STATE_MULTI_HOST_SETUP :
752 y = Multi_common_create_y[gr_screen.res];
754 case GS_STATE_MULTI_CLIENT_SETUP :
755 y = Multi_common_jw_y[gr_screen.res];
757 case GS_STATE_MULTI_START_GAME :
758 y = Multi_common_msg_y[gr_screen.res];
762 gr_string((gr_screen.max_w - w)/2, y, Multi_common_notify_text);
769 int Multi_common_icons[MULTI_NUM_COMMON_ICONS];
771 const char *Multi_common_icon_names[MULTI_NUM_COMMON_ICONS] = {
772 "DotRed", // voice denied
773 "DotGreen", // voice recording
774 "OvalGreen", // team 0
775 "OvalGreen01", // team 0 select
777 "OvalRed01", // team 1 select
778 "mp_coop", // coop mission
779 "mp_teams", // TvT mission
780 "mp_furball", // furball mission
781 "icon-volition", // volition mission
782 "icon-valid", // mission is valid
787 "icon-silent" // SilentThreat
791 // width and height of the icons
792 int Multi_common_icon_dims[MULTI_NUM_COMMON_ICONS][2] = {
793 {11, 11}, // voice denied
794 {11, 11}, // voice recording
796 {11, 11}, // team 0 select
798 {11, 11}, // team 1 select
801 {18, 11}, // mp furball
802 {9, 9}, // volition mission
803 {8, 8}, // mission is valid
808 {16, 7} // silent threat
812 void multi_load_common_icons()
817 for(idx=0; idx<MULTI_NUM_COMMON_ICONS; idx++){
818 Multi_common_icons[idx] = -1;
819 Multi_common_icons[idx] = bm_load(Multi_common_icon_names[idx]);
823 void multi_unload_common_icons()
828 for(idx=0; idx<MULTI_NUM_COMMON_ICONS; idx++){
829 if(Multi_common_icons[idx] != -1){
830 bm_unload(Multi_common_icons[idx]);
831 Multi_common_icons[idx] = -1;
836 // display any relevant voice status icons
837 void multi_common_voice_display_status()
839 switch(multi_voice_status()){
840 // i have been denied the voice token
841 case MULTI_VOICE_STATUS_DENIED:
842 if(Multi_common_icons[MICON_VOICE_DENIED] != -1){
843 gr_set_bitmap(Multi_common_icons[MICON_VOICE_DENIED], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
848 // i am currently recording
849 case MULTI_VOICE_STATUS_RECORDING:
850 if(Multi_common_icons[MICON_VOICE_RECORDING] != -1){
851 gr_set_bitmap(Multi_common_icons[MICON_VOICE_RECORDING], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
856 // i am currently playing back sound
857 case MULTI_VOICE_STATUS_PLAYING:
860 // the system is currently idle
861 case MULTI_VOICE_STATUS_IDLE:
867 // palette initialization stuff
868 #define MULTI_COMMON_PALETTE_FNAME "InterfacePalette"
871 int Multi_common_interface_palette = -1;
873 void multi_common_load_palette();
874 void multi_common_set_palette();
875 void multi_common_unload_palette();
877 // load in the palette if it doesn't already exist
878 void multi_common_load_palette()
880 if(Multi_common_interface_palette != -1){
884 Multi_common_interface_palette = bm_load(MULTI_COMMON_PALETTE_FNAME);
885 if(Multi_common_interface_palette == -1){
886 nprintf(("Network","Error loading multiplayer common palette!\n"));
890 // set the common palette to be the active one
891 void multi_common_set_palette()
893 // if the palette is not loaded yet, do so now
894 if(Multi_common_interface_palette == -1){
895 multi_common_load_palette();
898 if(Multi_common_interface_palette != -1){
899 #ifndef HARDWARE_ONLY
900 palette_use_bm_palette(Multi_common_interface_palette);
905 // unload the bitmap palette
906 void multi_common_unload_palette()
908 if(Multi_common_interface_palette != -1){
909 bm_unload(Multi_common_interface_palette);
910 Multi_common_interface_palette = -1;
914 void multi_common_verify_cd()
920 // -------------------------------------------------------------------------------------------------------------
922 // MULTIPLAYER JOIN SCREEN
925 #define MULTI_JOIN_NUM_BUTTONS 11
929 #define MULTI_JOIN_PALETTE "InterfacePalette"
931 static const char *Multi_join_bitmap_fname[GR_NUM_RESOLUTIONS] = {
932 "MultiJoin", // GR_640
933 "2_MultiJoin" // GR_1024
936 static const char *Multi_join_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
937 "MultiJoin-M", // GR_640
938 "2_MultiJoin-M" // GR_1024
944 const char *Mj_slider_name[GR_NUM_RESOLUTIONS] = {
948 int Mj_slider_coords[GR_NUM_RESOLUTIONS][4] = {
959 #define MJ_SCROLL_UP 0
960 #define MJ_SCROLL_DOWN 1
962 #define MJ_SCROLL_INFO_UP 3
963 #define MJ_SCROLL_INFO_DOWN 4
964 #define MJ_JOIN_OBSERVER 5
965 #define MJ_START_GAME 6
971 // uses MULTI_JOIN_REFRESH_TIME as its timestamp
972 int Multi_join_glr_stamp;
974 #define MULTI_JOIN_PING_TIME 15000 // how often we ping all the known servers
975 int Multi_join_ping_stamp;
976 UI_WINDOW Multi_join_window; // the window object for the join screen
977 UI_BUTTON Multi_join_select_button; // for selecting list items
979 UI_SLIDER2 Multi_join_slider; // handy dandy slider
981 int Multi_join_bitmap; // the background bitmap
983 ui_button_info Multi_join_buttons[GR_NUM_RESOLUTIONS][MULTI_JOIN_NUM_BUTTONS] = {
986 ui_button_info("MJ_00", 0, 85, -1, -1, 0),
987 ui_button_info("MJ_01", 0, 125, -1, -1, 1),
988 ui_button_info("MJ_03", 20, 324, -1, -1, 3),
989 ui_button_info("MJ_04", 0, 399, -1, -1, 4),
990 ui_button_info("MJ_05", 0, 436, -1, -1, 5),
991 ui_button_info("MJ_15", 450, 323, -1, -1, 15),
992 ui_button_info("MJ_10", 519, 323, -1, -1, 10),
993 ui_button_info("MJ_06", 574, 323, -1, -1, 6),
994 ui_button_info("MJ_08", 470, 427, -1, -1, 8),
995 ui_button_info("MJ_09", 448, 454, -1, -1, 9),
996 ui_button_info("MJ_07", 563, 411, -1, -1, 7),
998 ui_button_info( "MJ_00", 1, 57, -1, -1, 0 ), // scroll up
999 ui_button_info( "MJ_02", 1, 297, -1, -1, 2 ), // scroll down
1000 ui_button_info( "MJ_03", 10, 338, 65, 364, 3 ), // refresh
1001 ui_button_info( "MJ_04", 1, 405, -1, -1, 4 ), // scroll info up
1002 ui_button_info( "MJ_05", 1, 446, -1, -1, 5 ), // scroll info down
1003 ui_button_info( "MJ_06", 489, 339, -1, -1, 6 ), // join as observer
1004 ui_button_info( "MJ_07", 538, 339, -1, -1, 7 ), // create game
1005 ui_button_info( "MJ_08", 583, 339, 588, 376, 8 ), // cancel
1006 ui_button_info( "MJ_09", 534, 426, -1, -1, 9 ), // help
1007 ui_button_info( "MJ_10", 534, 454, -1, -1, 10 ), // options
1008 ui_button_info( "MJ_11", 571, 426, 589, 416, 11 ), // join
1012 ui_button_info( "2_MJ_00", 2, 92, -1, -1, 0 ), // scroll up
1013 ui_button_info( "2_MJ_02", 2, 475, -1, -1, 2 ), // scroll down
1014 ui_button_info( "2_MJ_03", 16, 541, 104, 582, 3 ), // refresh
1015 ui_button_info( "2_MJ_04", 2, 648, -1, -1, 4 ), // scroll info up
1016 ui_button_info( "2_MJ_05", 2, 713, -1, -1, 5 ), // scroll info down
1017 ui_button_info( "2_MJ_06", 783, 542, -1, -1, 6 ), // join as observer
1018 ui_button_info( "2_MJ_07", 861, 542, -1, -1, 7 ), // create game
1019 ui_button_info( "2_MJ_08", 933, 542, 588, 376, 8 ), // cancel
1020 ui_button_info( "2_MJ_09", 854, 681, -1, -1, 9 ), // help
1021 ui_button_info( "2_MJ_10", 854, 727, -1, -1, 10 ), // options
1022 ui_button_info( "2_MJ_11", 914, 681, 937, 668, 11 ), // join
1027 #define MULTI_JOIN_NUM_TEXT 13
1029 UI_XSTR Multi_join_text[GR_NUM_RESOLUTIONS][MULTI_JOIN_NUM_TEXT] = {
1031 {"Refresh", 1299, 65, 364, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_REFRESH].button},
1032 {"Join as", 1300, 476, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_JOIN_OBSERVER].button},
1033 {"Observer", 1301, 467, 385, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_JOIN_OBSERVER].button},
1034 {"Create", 1408, 535, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_START_GAME].button},
1035 {"Game", 1302, 541, 385, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_START_GAME].button},
1036 {"Cancel", 387, 588, 376, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[0][MJ_CANCEL].button},
1037 {"Help", 928, 479, 436, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_HELP].button},
1038 {"Options", 1036, 479, 460, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_OPTIONS].button},
1039 {"Join", 1303, 589, 416, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[0][MJ_ACCEPT].button},
1040 {"Status", 1304, 37, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
1041 {"Server", 1305, 116, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
1042 {"Players", 1306, 471, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
1043 {"Ping", 1307, 555, 37, UI_XSTR_COLOR_GREEN, -1, NULL}
1046 {"Refresh", 1299, 104, 582, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_REFRESH].button},
1047 {"Join as", 1300, 783, 602, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_JOIN_OBSERVER].button},
1048 {"Observer", 1301, 774, 611, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_JOIN_OBSERVER].button},
1049 {"Create", 1408, 868, 602, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_START_GAME].button},
1050 {"Game", 1302, 872, 611, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_START_GAME].button},
1051 {"Cancel", 387, 941, 602, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[1][MJ_CANCEL].button},
1052 {"Help", 928, 782, 699, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_HELP].button},
1053 {"Options", 1036, 782, 736, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_OPTIONS].button},
1054 {"Join", 1303, 937, 668, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[1][MJ_ACCEPT].button},
1055 {"Status", 1304, 60, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
1056 {"Server", 1305, 186, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
1057 {"Players", 1306, 753, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
1058 {"Ping", 1307, 888, 60, UI_XSTR_COLOR_GREEN, -1, NULL}
1063 // constants for coordinate look ups
1064 #define MJ_X_COORD 0
1065 #define MJ_Y_COORD 1
1066 #define MJ_W_COORD 2
1067 #define MJ_H_COORD 3
1069 #define MULTI_JOIN_SENT_WAIT 10000 // wait this long since a join was sent to allow another
1070 int Multi_join_sent_stamp;
1072 // game information text areas
1073 int Mj_max_game_items[GR_NUM_RESOLUTIONS] = {
1078 int Mj_list_y[GR_NUM_RESOLUTIONS] = {
1083 int Mj_status_coords[GR_NUM_RESOLUTIONS][4] = {
1092 int Mj_game_icon_coords[GR_NUM_RESOLUTIONS][3] = {
1101 int Mj_speed_coords[GR_NUM_RESOLUTIONS][4] = {
1110 int Mj_game_name_coords[GR_NUM_RESOLUTIONS][4] = {
1119 int Mj_players_coords[GR_NUM_RESOLUTIONS][4] = {
1128 int Mj_ping_coords[GR_NUM_RESOLUTIONS][4] = {
1137 // game speed labels
1138 #define MJ_NUM_SPEED_LABELS 5
1139 const char *Multi_join_speed_labels[MJ_NUM_SPEED_LABELS] = {
1146 color *Multi_join_speed_colors[MJ_NUM_SPEED_LABELS] = {
1149 &Color_bright_green,
1150 &Color_bright_green,
1154 int Mj_cd_coords[GR_NUM_RESOLUTIONS] = {
1159 // extents of the entire boundable game info region
1160 // NOTE : these numbers are completely empirical
1161 #define MJ_PING_GREEN 160
1162 #define MJ_PING_YELLOW 300
1164 int Mj_list_area_coords[GR_NUM_RESOLUTIONS][4] = {
1173 // PXO channel filter
1174 #define MJ_PXO_FILTER_Y 0
1176 // special chars to indicate various status modes for servers
1177 #define MJ_CHAR_STANDALONE "*"
1178 #define MJ_CHAR_CAMPAIGN "c"
1181 // various interface indices
1182 int Multi_join_list_start; // where to start displaying from
1183 active_game *Multi_join_list_start_item; // a pointer to the corresponding active_game
1184 int Multi_join_list_selected; // which item we have selected
1185 active_game *Multi_join_selected_item; // a pointer to the corresponding active_game
1187 // use this macro to modify the list start
1188 #define MJ_LIST_START_INC() do { Multi_join_list_start++; } while(0);
1189 #define MJ_LIST_START_DEC() do { Multi_join_list_start--; } while(0);
1190 #define MJ_LIST_START_SET(vl) do { Multi_join_list_start = vl; } while(0);
1192 // if we should be sending a join request at the end of the frame
1193 int Multi_join_should_send = -1;
1195 // master tracker details
1196 int Multi_join_frame_count; // keep a count of frames displayed
1197 int Multi_join_mt_tried_verify; // already tried verifying the pilot with the tracker
1199 // data stuff for auto joining a game
1200 #define MULTI_AUTOJOIN_JOIN_STAMP 2000
1201 #define MULTI_AUTOJOIN_QUERY_STAMP 2000
1203 int Multi_did_autojoin;
1204 net_addr Multi_autojoin_addr;
1205 int Multi_autojoin_join_stamp;
1206 int Multi_autojoin_query_stamp;
1209 join_request Multi_join_request;
1211 // LOCAL function definitions
1212 void multi_join_check_buttons();
1213 void multi_join_button_pressed(int n);
1214 void multi_join_display_games();
1215 void multi_join_blit_game_status(active_game *game, int y);
1216 void multi_join_load_tcp_addrs();
1217 void multi_join_do_netstuff();
1218 void multi_join_ping_all();
1219 void multi_join_process_select();
1220 void multi_join_list_scroll_up();
1221 void multi_join_list_scroll_down();
1222 void multi_join_list_page_up();
1223 void multi_join_list_page_down();
1224 active_game *multi_join_get_game(int n);
1225 void multi_join_cull_timeouts();
1226 void multi_join_handle_item_cull(active_game *item, int item_index);
1227 void multi_join_send_join_request(int as_observer);
1228 void multi_join_create_game();
1229 void multi_join_blit_top_stuff();
1230 int multi_join_maybe_warn();
1231 int multi_join_warn_pxo();
1232 void multi_join_blit_protocol();
1236 active_game ag, *newitem;;
1239 dc_get_arg(ARG_INT);
1240 for(idx=0; idx<Dc_arg_int; idx++){
1241 // stuff some fake info
1242 memset(&ag, 0, sizeof(active_game));
1243 sprintf(ag.name, "Game %d", idx);
1244 ag.version = MULTI_FS_SERVER_VERSION;
1245 ag.comp_version = MULTI_FS_SERVER_VERSION;
1246 ag.server_addr.addr[0] = (char)idx;
1247 ag.flags = (AG_FLAG_COOP | AG_FLAG_FORMING | AG_FLAG_STANDALONE);
1250 newitem = multi_update_active_games(&ag);
1252 // timestamp it so we get random timeouts
1253 if(newitem != NULL){
1254 // newitem->heard_from_timer = timestamp((int)frand_range(500.0f, 10000.0f));
1259 void multi_join_notify_new_game()
1262 // reset the # of items
1263 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);
1264 Multi_join_slider.force_currentItem(Multi_join_list_start);
1268 int multi_join_autojoin_do()
1270 // if we have an active game on the list, then return a positive value so that we
1271 // can join the game
1272 if ( Active_game_head && (Active_game_count > 0) ) {
1273 Multi_join_selected_item = Active_game_head;
1277 // send out a server_query again
1278 if ( timestamp_elapsed(Multi_autojoin_query_stamp) ) {
1279 send_server_query(&Multi_autojoin_addr);
1280 Multi_autojoin_query_stamp = timestamp(MULTI_AUTOJOIN_QUERY_STAMP);
1286 void multi_join_game_init()
1290 // do the multiplayer init stuff - multi_level_init() now does all net_player zeroing.
1291 // setup various multiplayer things
1292 SDL_assert( Game_mode & GM_MULTIPLAYER );
1293 SDL_assert( Net_player != NULL );
1295 switch (Multi_options_g.protocol) {
1297 ADDRESS_LENGTH = IPX_ADDRESS_LENGTH;
1298 PORT_LENGTH = IPX_PORT_LENGTH;
1302 ADDRESS_LENGTH = IP_ADDRESS_LENGTH;
1303 PORT_LENGTH = IP_PORT_LENGTH;
1312 memset( &Netgame, 0, sizeof(Netgame) );
1315 Net_player->flags |= NETINFO_FLAG_DO_NETWORKING;
1316 Net_player->player = Player;
1317 memcpy(&Net_player->p_info.addr,&Psnet_my_addr,sizeof(net_addr));
1319 // check for the existence of a CD
1320 multi_common_verify_cd();
1322 // load my local netplayer options
1323 multi_options_local_load(&Net_player->p_info.options, Net_player);
1329 common_set_interface_palette(MULTI_JOIN_PALETTE);
1332 // destroy any chatbox contents which previously existed (from another game)
1335 // create the interface window
1336 Multi_join_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
1337 Multi_join_window.set_mask_bmap(Multi_join_bitmap_mask_fname[gr_screen.res]);
1339 // load the background bitmap
1340 Multi_join_bitmap = bm_load(Multi_join_bitmap_fname[gr_screen.res]);
1341 if(Multi_join_bitmap < 0){
1342 // we failed to load the bitmap - this is very bad
1346 // intialize the endgame system
1347 multi_endgame_init();
1349 // initialize the common notification messaging
1350 multi_common_notify_init();
1352 // initialize the common text area
1353 multi_common_set_text("");
1355 // load and use the common interface palette
1356 multi_common_load_palette();
1357 multi_common_set_palette();
1359 // load the help overlay
1360 help_overlay_load(MULTI_JOIN_OVERLAY);
1361 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1363 // do TCP and VMT specific initialization
1364 if(Multi_options_g.protocol == NET_TCP){
1365 // if this is a TCP (non tracker) game, we'll load up our default address list right now
1366 multi_join_load_tcp_addrs();
1369 // initialize any and all timestamps
1370 Multi_join_glr_stamp = -1;
1371 Multi_join_ping_stamp = -1;
1372 Multi_join_sent_stamp = -1;
1374 // reset frame count
1375 Multi_join_frame_count = 0;
1377 // haven't tried to verify on the tracker yet.
1378 Multi_join_mt_tried_verify = 0;
1380 // clear our all game lists to save hassles
1381 multi_join_clear_game_list();
1383 // create the interface buttons
1384 for(idx=0; idx<MULTI_JOIN_NUM_BUTTONS; idx++){
1385 // create the object
1386 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);
1388 // set the sound to play when highlighted
1389 Multi_join_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
1391 // set the ani for the button
1392 Multi_join_buttons[gr_screen.res][idx].button.set_bmaps(Multi_join_buttons[gr_screen.res][idx].filename);
1395 Multi_join_buttons[gr_screen.res][idx].button.link_hotspot(Multi_join_buttons[gr_screen.res][idx].hotspot);
1400 for(idx=0; idx<MULTI_JOIN_NUM_TEXT; idx++){
1401 Multi_join_window.add_XSTR(&Multi_join_text[gr_screen.res][idx]);
1405 Multi_join_should_send = -1;
1407 // close any previously open chatbox
1410 // create the list item select button
1411 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);
1412 Multi_join_select_button.hide();
1416 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);
1419 // if starting a network game, then go to the create game screen
1420 if ( Cmdline_start_netgame ) {
1421 multi_join_create_game();
1422 } else if ( Cmdline_connect_addr != NULL ) {
1427 // joining a game. Send a join request to the given IP address, and wait for the return.
1428 memset( &Multi_autojoin_addr, 0, sizeof(net_addr) );
1429 Multi_autojoin_addr.type = NET_TCP;
1431 // create the address, looking out for port number at the end
1432 port_num = DEFAULT_GAME_PORT;
1433 p = strrchr(Cmdline_connect_addr, ':');
1437 port_num = (short)atoi(p);
1439 ip_addr = inet_addr(Cmdline_connect_addr);
1440 memcpy(Multi_autojoin_addr.addr, &ip_addr, 4);
1441 Multi_autojoin_addr.port = port_num;
1443 send_server_query(&Multi_autojoin_addr);
1444 Multi_autojoin_query_stamp = timestamp(MULTI_AUTOJOIN_QUERY_STAMP);
1445 Multi_did_autojoin = 0;
1449 void multi_join_clear_game_list()
1452 Multi_join_list_selected = -1;
1453 Multi_join_selected_item = NULL;
1454 MJ_LIST_START_SET(-1);
1455 Multi_join_list_start_item = NULL;
1457 // free up the active game list
1458 multi_free_active_games();
1460 // initialize the active game list
1461 Active_game_head = NULL;
1462 Active_game_count = 0;
1465 void multi_join_game_do_frame()
1467 // check the status of our reliable socket. If not valid, popup error and return to main menu
1468 // I put this code here to avoid nasty gameseq issues with states. Also, we will have nice
1469 // background for the popup
1470 if ( !psnet_rel_check() ) {
1471 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));
1472 gameseq_post_event(GS_EVENT_MAIN_MENU);
1476 // return here since we will be moving to the next stage anyway -- I don't want to see the backgrounds of
1477 // all the screens for < 1 second for every screen we automatically move to.
1478 if ( Cmdline_start_netgame ) {
1482 // when joining a network game, wait for the server query to come back, and then join the game
1483 if ( Cmdline_connect_addr != NULL ) {
1486 if ( !Multi_did_autojoin ) {
1487 rval = popup_till_condition(multi_join_autojoin_do, XSTR("&Cancel", 779), XSTR("Joining netgame", 1500) );
1489 // cancel was hit. Send the user back to the main hall
1490 gameseq_post_event(GS_EVENT_MAIN_MENU);
1491 Cmdline_connect_addr = NULL; // reset this value.
1494 // when we get here, we have the data -- join the game.
1495 multi_join_send_join_request(0);
1496 Multi_autojoin_join_stamp = timestamp(MULTI_AUTOJOIN_JOIN_STAMP);
1497 Multi_did_autojoin = 1;
1500 if ( timestamp_elapsed(Multi_autojoin_join_stamp) ) {
1501 multi_join_send_join_request(0);
1502 Multi_autojoin_join_stamp = timestamp(MULTI_AUTOJOIN_JOIN_STAMP);
1508 // reset the should send var
1509 Multi_join_should_send = -1;
1511 int k = Multi_join_window.process();
1513 // process any keypresses
1516 if(help_overlay_active(MULTI_JOIN_OVERLAY)){
1517 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1519 gameseq_post_event(GS_EVENT_MAIN_MENU);
1520 gamesnd_play_iface(SND_USER_SELECT);
1524 // page up the game list
1526 multi_join_list_page_up();
1528 Multi_join_slider.force_currentItem(Multi_join_list_start);
1533 multi_pinfo_popup(Net_player);
1536 // page down the game list
1538 multi_join_list_page_down();
1540 Multi_join_slider.force_currentItem(Multi_join_list_start);
1544 // send out a ping-all
1546 multi_join_ping_all();
1547 Multi_join_ping_stamp = timestamp(MULTI_JOIN_PING_TIME);
1550 // shortcut to start a game
1552 multi_join_create_game();
1555 // scroll the game list up
1557 multi_join_list_scroll_up();
1559 Multi_join_slider.force_currentItem(Multi_join_list_start);
1563 // scroll the game list down
1565 multi_join_list_scroll_down();
1567 Multi_join_slider.force_currentItem(Multi_join_list_start);
1572 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
1573 help_overlay_set_state(MULTI_JOIN_OVERLAY, 0);
1576 // do any network related stuff
1577 multi_join_do_netstuff();
1579 // process any button clicks
1580 multi_join_check_buttons();
1582 // process any list selection stuff
1583 multi_join_process_select();
1585 // draw the background, etc
1587 GR_MAYBE_CLEAR_RES(Multi_join_bitmap);
1588 if(Multi_join_bitmap != -1){
1589 gr_set_bitmap(Multi_join_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1592 Multi_join_window.draw();
1594 // display the active games
1595 multi_join_display_games();
1597 // display any text in the info area
1598 multi_common_render_text();
1600 // display any pending notification messages
1601 multi_common_notify_do();
1603 // blit the CD icon and any PXO filter stuff
1604 multi_join_blit_top_stuff();
1606 // draw the help overlay
1607 help_overlay_maybe_blit(MULTI_JOIN_OVERLAY);
1612 // if we are supposed to be sending a join request
1613 if(Multi_join_should_send != -1){
1614 multi_join_send_join_request(Multi_join_should_send);
1616 Multi_join_should_send = -1;
1618 // increment the frame count
1619 Multi_join_frame_count++;
1622 void multi_join_game_close()
1624 // unload any bitmaps
1625 if(!bm_unload(Multi_join_bitmap)){
1626 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_join_bitmap_fname[gr_screen.res]));
1629 // unload the help overlay
1630 help_overlay_unload(MULTI_JOIN_OVERLAY);
1632 // free up the active game list
1633 multi_free_active_games();
1635 // destroy the UI_WINDOW
1636 Multi_join_window.destroy();
1639 common_free_interface_palette();
1643 void multi_join_check_buttons()
1646 for(idx=0;idx<MULTI_JOIN_NUM_BUTTONS;idx++){
1647 // we only really need to check for one button pressed at a time, so we can break after
1649 if(Multi_join_buttons[gr_screen.res][idx].button.pressed()){
1650 multi_join_button_pressed(idx);
1656 void multi_join_button_pressed(int n)
1660 // if we're player PXO, go back there
1661 gameseq_post_event(GS_EVENT_MAIN_MENU);
1662 gamesnd_play_iface(SND_USER_SELECT);
1665 if(Active_game_count <= 0){
1666 multi_common_add_notify(XSTR("No games found!",757));
1667 gamesnd_play_iface(SND_GENERAL_FAIL);
1668 } else if(Multi_join_list_selected == -1){
1669 multi_common_add_notify(XSTR("No game selected!",758));
1670 gamesnd_play_iface(SND_GENERAL_FAIL);
1671 } else if((Multi_join_sent_stamp != -1) && !timestamp_elapsed(Multi_join_sent_stamp)){
1672 multi_common_add_notify(XSTR("Still waiting on previous join request!",759));
1673 gamesnd_play_iface(SND_GENERAL_FAIL);
1675 // otherwise, if he's already played PXO games, warn him
1677 if(Player->flags & PLAYER_FLAGS_HAS_PLAYED_PXO){
1678 if(!multi_join_warn_pxo()){
1684 // send the join request here
1685 SDL_assert(Multi_join_selected_item != NULL);
1687 // send a join request packet
1688 Multi_join_should_send = 0;
1690 gamesnd_play_iface(SND_COMMIT_PRESSED);
1696 if(!help_overlay_active(MULTI_JOIN_OVERLAY)){
1697 help_overlay_set_state(MULTI_JOIN_OVERLAY,1);
1699 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1703 // scroll the game list up
1705 multi_join_list_scroll_up();
1707 Multi_join_slider.force_currentItem(Multi_join_list_start);
1711 // scroll the game list down
1712 case MJ_SCROLL_DOWN:
1713 multi_join_list_scroll_down();
1715 Multi_join_slider.force_currentItem(Multi_join_list_start);
1719 // scroll the info text box up
1720 case MJ_SCROLL_INFO_UP:
1721 multi_common_scroll_text_up();
1724 // scroll the info text box down
1725 case MJ_SCROLL_INFO_DOWN:
1726 multi_common_scroll_text_down();
1729 // go to the options screen
1731 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
1734 // go to the start game screen
1736 multi_join_create_game();
1739 // refresh the game/server list
1741 gamesnd_play_iface(SND_USER_SELECT);
1742 broadcast_game_query();
1745 // join a game as an observer
1746 case MJ_JOIN_OBSERVER:
1747 if(Active_game_count <= 0){
1748 multi_common_add_notify(XSTR("No games found!",757));
1749 gamesnd_play_iface(SND_GENERAL_FAIL);
1750 } else if(Multi_join_list_selected == -1){
1751 multi_common_add_notify(XSTR("No game selected!",758));
1752 gamesnd_play_iface(SND_GENERAL_FAIL);
1753 } else if((Multi_join_sent_stamp != -1) && !timestamp_elapsed(Multi_join_sent_stamp)){
1754 multi_common_add_notify(XSTR("Still waiting on previous join request!",759));
1755 gamesnd_play_iface(SND_GENERAL_FAIL);
1757 // send the join request here
1758 SDL_assert(Multi_join_selected_item != NULL);
1760 Multi_join_should_send = 1;
1762 gamesnd_play_iface(SND_COMMIT_PRESSED);
1767 multi_common_add_notify(XSTR("Not implemented yet!",760));
1768 gamesnd_play_iface(SND_GENERAL_FAIL);
1773 // display all relevant info for active games
1774 void multi_join_display_games()
1776 active_game *moveup = Multi_join_list_start_item;
1780 int y_start = Mj_list_y[gr_screen.res];
1785 // blit the game status (including text and type icon)
1786 multi_join_blit_game_status(moveup,y_start);
1788 // get the connection type
1789 con_type = (moveup->flags & AG_FLAG_CONNECTION_SPEED_MASK) >> AG_FLAG_CONNECTION_BIT;
1790 if((con_type > 4) || (con_type < 0)){
1794 // display the connection speed
1796 strcpy(str, Multi_join_speed_labels[con_type]);
1797 gr_set_color_fast(Multi_join_speed_colors[con_type]);
1798 gr_string(Mj_speed_coords[gr_screen.res][MJ_X_COORD], y_start, str);
1800 // we'll want to have different colors for highlighted items, etc.
1801 if(moveup == Multi_join_selected_item){
1802 gr_set_color_fast(&Color_text_selected);
1804 gr_set_color_fast(&Color_text_normal);
1807 // display the game name, adding appropriate status chars
1809 if(moveup->flags & AG_FLAG_STANDALONE){
1810 strcat(str,MJ_CHAR_STANDALONE);
1812 if(moveup->flags & AG_FLAG_CAMPAIGN){
1813 strcat(str,MJ_CHAR_CAMPAIGN);
1816 // tack on the actual server name
1818 strcat(str,moveup->name);
1819 if(strlen(moveup->mission_name) > 0){
1821 strcat(str,moveup->mission_name);
1824 // make sure the string fits in the display area and draw it
1825 gr_force_fit_string(str,200,Mj_game_name_coords[gr_screen.res][MJ_W_COORD]);
1826 gr_string(Mj_game_name_coords[gr_screen.res][MJ_X_COORD],y_start,str);
1828 // display the ping time
1829 if(moveup->ping.ping_avg > 0){
1830 if(moveup->ping.ping_avg > 1000){
1831 gr_set_color_fast(&Color_bright_red);
1832 strcpy(str,XSTR("> 1 sec",761));
1834 // set the appropriate ping time color indicator
1835 if(moveup->ping.ping_avg > MJ_PING_YELLOW){
1836 gr_set_color_fast(&Color_bright_red);
1837 } else if(moveup->ping.ping_avg > MJ_PING_YELLOW){
1838 gr_set_color_fast(&Color_bright_yellow);
1840 gr_set_color_fast(&Color_bright_green);
1843 sprintf(str,"%d",moveup->ping.ping_avg);
1844 strcat(str,XSTR(" ms",762)); // [[ Milliseconds ]]
1847 gr_string(Mj_ping_coords[gr_screen.res][MJ_X_COORD],y_start,str);
1850 // display the number of players (be sure to center it)
1851 if(moveup == Multi_join_selected_item){
1852 gr_set_color_fast(&Color_text_selected);
1854 gr_set_color_fast(&Color_text_normal);
1856 sprintf(str,"%d",moveup->num_players);
1857 gr_get_string_size(&w,&h,str);
1858 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);
1862 moveup = moveup->next;
1863 } while((moveup != Active_game_head) && (count < Mj_max_game_items[gr_screen.res]));
1865 // if there are no items on the list, display this info
1867 gr_set_color_fast(&Color_bright);
1868 gr_string(Mj_game_name_coords[gr_screen.res][MJ_X_COORD] - 30,y_start,XSTR("<No game servers found>",763));
1872 void multi_join_blit_game_status(active_game *game, int y)
1875 char status_text[25];
1877 // blit the proper icon
1879 switch( game->flags & AG_FLAG_TYPE_MASK ){
1882 if(Multi_common_icons[MICON_COOP] != -1){
1883 gr_set_bitmap(Multi_common_icons[MICON_COOP], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1888 // team vs. team game
1890 if(Multi_common_icons[MICON_TVT] != -1){
1891 gr_set_bitmap(Multi_common_icons[MICON_TVT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1898 case AG_FLAG_DOGFIGHT:
1899 if(Multi_common_icons[MICON_DOGFIGHT] != -1){
1900 gr_set_bitmap(Multi_common_icons[MICON_DOGFIGHT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1906 // if we're supposed to draw a bitmap
1908 gr_bitmap(Mj_game_icon_coords[gr_screen.res][MJ_X_COORD],y-1);
1911 // blit the proper status text
1912 memset(status_text,0,25);
1914 switch( game->flags & AG_FLAG_STATE_MASK ){
1915 case AG_FLAG_FORMING:
1916 gr_set_color_fast(&Color_bright_green);
1917 strcpy(status_text,XSTR("Forming",764));
1919 case AG_FLAG_BRIEFING:
1920 gr_set_color_fast(&Color_bright_red);
1921 strcpy(status_text,XSTR("Briefing",765));
1923 case AG_FLAG_DEBRIEF:
1924 gr_set_color_fast(&Color_bright_red);
1925 strcpy(status_text,XSTR("Debrief",766));
1928 gr_set_color_fast(&Color_bright_red);
1929 strcpy(status_text,XSTR("Paused",767));
1931 case AG_FLAG_IN_MISSION:
1932 gr_set_color_fast(&Color_bright_red);
1933 strcpy(status_text,XSTR("Playing",768));
1936 gr_set_color_fast(&Color_bright);
1937 strcpy(status_text,XSTR("Unknown",769));
1940 gr_get_string_size(&str_w,NULL,status_text);
1941 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);
1944 // load in a list of active games from our tcp.cfg file
1945 void multi_join_load_tcp_addrs()
1947 char line[MAX_IP_STRING];
1952 // attempt to open the ip list file
1953 file = cfopen(IP_CONFIG_FNAME,"rt",CFILE_NORMAL,CF_TYPE_DATA);
1955 nprintf(("Network","Error loading tcp.cfg file!\n"));
1959 // free up any existing server list
1960 multi_free_server_list();
1962 // read in all the strings in the file
1963 while(!cfeof(file)){
1965 cfgets(line,MAX_IP_STRING,file);
1967 // strip off any newline character
1968 if(line[strlen(line) - 1] == '\n'){
1969 line[strlen(line) - 1] = '\0';
1972 // empty lines don't get processed
1973 if( (line[0] == '\0') || (line[0] == '\n') ){
1977 if ( !psnet_is_valid_ip_string(line) ) {
1978 nprintf(("Network","Invalid ip string (%s)\n",line));
1980 // copy the server ip address
1981 memset(&addr,0,sizeof(net_addr));
1982 addr.type = NET_TCP;
1983 psnet_string_to_addr(&addr,line);
1984 if ( addr.port == 0 ){
1985 addr.port = DEFAULT_GAME_PORT;
1988 // create a new server item on the list
1989 item = multi_new_server_item();
1991 memcpy(&item->server_addr,&addr,sizeof(net_addr));
1999 // do stuff like pinging servers, sending out requests, etc
2000 void multi_join_do_netstuff()
2002 // handle game query stuff
2003 if(Multi_join_glr_stamp == -1){
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);
2012 // otherwise send out game query and restamp
2013 else if(timestamp_elapsed(Multi_join_glr_stamp)){
2014 broadcast_game_query();
2016 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
2017 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME_LOCAL);
2019 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME);
2023 // check to see if we've been accepted. If so, put up message saying so
2024 if ( Net_player->flags & (NETINFO_FLAG_ACCEPT_INGAME|NETINFO_FLAG_ACCEPT_CLIENT|NETINFO_FLAG_ACCEPT_HOST|NETINFO_FLAG_ACCEPT_OBSERVER) ) {
2025 multi_common_add_notify(XSTR("Accepted. Waiting for player data.",770));
2028 // check to see if any join packets we have sent have timed out
2029 if((Multi_join_sent_stamp != -1) && (timestamp_elapsed(Multi_join_sent_stamp))){
2030 Multi_join_sent_stamp = -1;
2031 multi_common_add_notify(XSTR("Join request timed out!",771));
2034 // check to see if we should be pinging everyone
2035 if((Multi_join_ping_stamp == -1) || (timestamp_elapsed(Multi_join_ping_stamp))){
2036 multi_join_ping_all();
2037 Multi_join_ping_stamp = timestamp(MULTI_JOIN_PING_TIME);
2041 multi_join_cull_timeouts();
2044 // evaluate a returned pong.
2045 void multi_join_eval_pong(net_addr *addr, fix pong_time)
2048 active_game *moveup = Active_game_head;
2053 if(psnet_same(&moveup->server_addr,addr)){
2055 multi_ping_eval_pong(&moveup->ping);
2059 moveup = moveup->next;
2061 } while(moveup != Active_game_head);
2064 // update the game's ping
2066 if(found && (moveup->ping_end > moveup->ping_start)){
2067 moveup->ping_time = f2fl(moveup->ping_end - moveup->ping_start);
2068 moveup->ping_start = -1;
2069 moveup->ping_end = -1;
2074 // ping all the server on the list
2075 void multi_join_ping_all()
2077 active_game *moveup = Active_game_head;
2082 moveup->ping_start = timer_get_fixed_seconds();
2083 moveup->ping_end = -1;
2084 send_ping(&moveup->server_addr);
2086 multi_ping_send(&moveup->server_addr,&moveup->ping);
2088 moveup = moveup->next;
2089 } while(moveup != Active_game_head);
2093 void multi_join_process_select()
2095 // if we don't have anything selected and there are items on the list - select the first one
2096 if((Multi_join_list_selected == -1) && (Active_game_count > 0)){
2097 Multi_join_list_selected = 0;
2098 Multi_join_selected_item = multi_join_get_game(0);
2099 MJ_LIST_START_SET(0);
2100 Multi_join_list_start_item = Multi_join_selected_item;
2102 // send a mission description request to this guy
2103 send_netgame_descript_packet(&Multi_join_selected_item->server_addr,0);
2104 multi_common_set_text("");
2106 // I sure hope this doesn't happen
2107 SDL_assert(Multi_join_selected_item != NULL);
2110 // otherwise see if he's clicked on an item
2111 else if(Multi_join_select_button.pressed() && (Active_game_count > 0)){
2113 Multi_join_select_button.get_mouse_pos(NULL,&y);
2115 if(item + Multi_join_list_start < Active_game_count){
2116 gamesnd_play_iface(SND_IFACE_MOUSE_CLICK);
2118 Multi_join_list_selected = item + Multi_join_list_start;
2119 Multi_join_selected_item = multi_join_get_game(Multi_join_list_selected);
2121 // I sure hope this doesn't happen
2122 SDL_assert(Multi_join_selected_item != NULL);
2124 // send a mission description request to this guy
2125 send_netgame_descript_packet(&Multi_join_selected_item->server_addr,0);
2126 multi_common_set_text("");
2130 // if he's double clicked, then select it and accept
2131 if(Multi_join_select_button.double_clicked()){
2133 Multi_join_select_button.get_mouse_pos(NULL,&y);
2135 if(item == Multi_join_list_selected){
2136 multi_join_button_pressed(MJ_ACCEPT);
2141 // return game n (0 based index)
2142 active_game *multi_join_get_game(int n)
2144 active_game *moveup = Active_game_head;
2151 moveup = moveup->next;
2152 while((moveup != Active_game_head) && (count != n)){
2153 moveup = moveup->next;
2156 if(moveup == Active_game_head){
2157 nprintf(("Network","Warning, couldn't find game item %d!\n",n));
2167 // scroll through the game list
2168 void multi_join_list_scroll_up()
2170 // if we're not at the beginning of the list, scroll up
2171 if(Multi_join_list_start_item != Active_game_head){
2172 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2174 MJ_LIST_START_DEC();
2176 gamesnd_play_iface(SND_SCROLL);
2178 gamesnd_play_iface(SND_GENERAL_FAIL);
2182 // scroll through the game list
2183 void multi_join_list_scroll_down()
2185 if((Active_game_count - Multi_join_list_start) > Mj_max_game_items[gr_screen.res]){
2186 Multi_join_list_start_item = Multi_join_list_start_item->next;
2188 MJ_LIST_START_INC();
2190 gamesnd_play_iface(SND_SCROLL);
2192 gamesnd_play_iface(SND_GENERAL_FAIL);
2196 void multi_join_list_page_up()
2198 // in this case, just set us to the beginning of the list
2199 if((Multi_join_list_start - Mj_max_game_items[gr_screen.res]) < 0){
2200 Multi_join_list_start_item = Active_game_head;
2202 MJ_LIST_START_SET(0);
2204 gamesnd_play_iface(SND_SCROLL);
2206 // otherwise page the whole thing up
2208 for(idx=0; idx<Mj_max_game_items[gr_screen.res]; idx++){
2209 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2211 MJ_LIST_START_DEC();
2213 gamesnd_play_iface(SND_SCROLL);
2217 void multi_join_list_page_down()
2221 // page the whole thing down
2222 while((count < Mj_max_game_items[gr_screen.res]) && ((Active_game_count - Multi_join_list_start) > Mj_max_game_items[gr_screen.res])){
2223 Multi_join_list_start_item = Multi_join_list_start_item->next;
2224 MJ_LIST_START_INC();
2229 gamesnd_play_iface(SND_SCROLL);
2232 void multi_join_cull_timeouts()
2234 active_game *backup;
2236 active_game *moveup = Active_game_head;
2238 // traverse through the entire list if any items exist
2242 if((moveup->heard_from_timer != -1) && (timestamp_elapsed(moveup->heard_from_timer))){
2243 Active_game_count--;
2245 // if this is the head of the list
2246 if(moveup == Active_game_head){
2247 // if this is the _only_ item on the list
2248 if(moveup->next == Active_game_head){
2249 // handle any gui details related to deleting this item
2250 multi_join_handle_item_cull(Active_game_head, count);
2252 free(Active_game_head);
2253 Active_game_head = NULL;
2256 // if there are other items on the list
2258 // handle any gui details related to deleting this item
2259 multi_join_handle_item_cull(moveup, count);
2261 Active_game_head = moveup->next;
2262 Active_game_head->prev = moveup->prev;
2263 Active_game_head->prev->next = Active_game_head;
2265 moveup = Active_game_head;
2268 // if its somewhere else on the list
2270 // handle any gui details related to deleting this item
2271 multi_join_handle_item_cull(moveup, count);
2273 // if its the last item on the list
2274 moveup->next->prev = moveup->prev;
2275 moveup->prev->next = moveup->next;
2277 // if it was the last element on the list, return
2278 if(moveup->next == Active_game_head){
2282 backup = moveup->next;
2288 moveup = moveup->next;
2291 } while(moveup != Active_game_head);
2295 // deep magic begins here.
2296 void multi_join_handle_item_cull(active_game *item, int item_index)
2298 // if this is the only item on the list, unset everything
2299 if(item->next == item){
2300 Multi_join_list_selected = -1;
2301 Multi_join_selected_item = NULL;
2304 Multi_join_slider.set_numberItems(0);
2306 MJ_LIST_START_SET(-1);
2307 Multi_join_list_start_item = NULL;
2313 // see if we should be adjusting our currently selected item
2314 if(item_index <= Multi_join_list_selected){
2315 // the selected item is the head of the list
2316 if(Multi_join_selected_item == Active_game_head){
2317 // move the pointer up since this item is about to be destroyed
2318 Multi_join_selected_item = Multi_join_selected_item->next;
2320 // if this is the item being deleted, select the previous one
2321 if(item == Multi_join_selected_item){
2323 Multi_join_selected_item = Multi_join_selected_item->prev;
2325 // decrement the selected index by 1
2326 Multi_join_list_selected--;
2328 // now we know its a previous item, so our pointer stays the same but our index goes down by one, since there will be
2329 // 1 less item on the list
2331 // decrement the selected index by 1
2332 Multi_join_list_selected--;
2337 // see if we should be adjusting out current start position
2338 if(item_index <= Multi_join_list_start){
2339 // the start position is the head of the list
2340 if(Multi_join_list_start_item == Active_game_head){
2341 // move the pointer up since this item is about to be destroyed
2342 Multi_join_list_start_item = Multi_join_list_start_item->next;
2344 // if this is the item being deleted, select the previous one
2345 if(item == Multi_join_list_start_item){
2346 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2348 // decrement the starting index by 1
2349 MJ_LIST_START_DEC();
2351 // but decrement the starting index by 1
2352 MJ_LIST_START_DEC();
2357 // maybe go back up a bit so that we always have a full page of items
2358 if(Active_game_count > Mj_max_game_items[gr_screen.res]){
2359 while((Active_game_count - Multi_join_list_start) < Mj_max_game_items[gr_screen.res]){
2360 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2361 MJ_LIST_START_DEC();
2365 // set slider location
2367 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);
2368 Multi_join_slider.force_currentItem(Multi_join_list_start);
2372 void multi_join_send_join_request(int as_observer)
2374 // don't do anything if we have no items selected
2375 if(Multi_join_selected_item == NULL){
2379 // 5/26/98 -- for team v team games, don't allow ingame joining :-(
2380 if ( (Multi_join_selected_item->flags & AG_FLAG_TEAMS) && (Multi_join_selected_item->flags & (AG_FLAG_PAUSE|AG_FLAG_IN_MISSION)) ) {
2381 popup(0, 1, POPUP_OK, XSTR("Joining ingame is currently not allowed for team vs. team games",772));
2385 memset(&Multi_join_request,0,sizeof(join_request));
2387 // if the netgame is in password mode, put up a request for the password
2388 if(Multi_join_selected_item->flags & AG_FLAG_PASSWD){
2389 if(!multi_passwd_popup(Multi_join_request.passwd)){
2393 nprintf(("Password : %s\n",Multi_join_request.passwd));
2396 // fill out the join request struct
2397 strcpy(Multi_join_request.callsign,Player->callsign);
2398 if(strlen(Player->image_filename) > 0){
2399 strcpy(Multi_join_request.image_filename, Player->image_filename);
2402 if(strlen(Player->squad_filename) > 0){
2403 strcpy(Multi_join_request.squad_filename, Player->squad_filename);
2407 // tracker id (if any)
2408 Multi_join_request.tracker_id = Multi_tracker_id;
2410 // player's rank (at least, what he wants you to _believe_)
2411 Multi_join_request.player_rank = (ubyte)Player->stats.rank;
2414 Multi_join_request.flags = 0;
2416 Multi_join_request.flags |= JOIN_FLAG_AS_OBSERVER;
2419 // if the player has hacked data
2420 if(game_hacked_data()){
2421 Multi_join_request.flags |= JOIN_FLAG_HAXOR;
2426 strncpy(Multi_join_request.pxo_squad_name, Multi_tracker_squad_name, LOGIN_LEN);
2429 // version of this server
2430 Multi_join_request.version = MULTI_FS_SERVER_VERSION;
2432 // server compatible version
2433 Multi_join_request.comp_version = MULTI_FS_SERVER_COMPATIBLE_VERSION;
2435 // his local player options
2436 memcpy(&Multi_join_request.player_options,&Player->m_local_options,sizeof(multi_local_options));
2438 // set the server address for the netgame
2439 memcpy(&Netgame.server_addr,&Multi_join_selected_item->server_addr,sizeof(net_addr));
2441 // send a join request to the guy
2442 send_join_packet(&Multi_join_selected_item->server_addr,&Multi_join_request);
2445 Multi_join_sent_stamp = timestamp(MULTI_JOIN_SENT_WAIT);
2448 multi_common_add_notify(XSTR("Sending join request...",773));
2451 void multi_join_create_game()
2453 // maybe warn the player about possible crappy server conditions
2454 if(!multi_join_maybe_warn()){
2458 // make sure to flag ourself as being the master
2459 Net_player->flags |= (NETINFO_FLAG_AM_MASTER | NETINFO_FLAG_GAME_HOST);
2460 Net_player->state = NETPLAYER_STATE_HOST_SETUP;
2462 // if we're in PXO mode, mark it down in our player struct
2463 if(MULTI_IS_TRACKER_GAME){
2464 Player->flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
2465 Player->save_flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
2467 // otherwise, if he's already played PXO games, warn him
2470 if(Player->flags & PLAYER_FLAGS_HAS_PLAYED_PXO){
2471 if(!multi_join_warn_pxo()){
2478 gameseq_post_event(GS_EVENT_MULTI_START_GAME);
2479 gamesnd_play_iface(SND_USER_SELECT);
2482 void multi_join_reset_join_stamp()
2484 // unset the timestamp here so the user can immediately send another join request
2485 Multi_join_sent_stamp = -1;
2486 multi_common_add_notify("");
2489 void multi_join_blit_top_stuff()
2491 // blit the cd icon if he has one
2492 if(Multi_has_cd && (Multi_common_icons[MICON_CD] != -1)){
2495 bm_get_info(Multi_common_icons[MICON_CD], &cd_w, NULL, NULL, NULL, NULL);
2497 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
2498 gr_bitmap((gr_screen.max_w / 2) - (cd_w / 2), Mj_cd_coords[gr_screen.res]);
2502 #define CW_CODE_CANCEL 0 // cancel the action
2503 #define CW_CODE_OK 1 // continue anyway
2504 #define CW_CODE_INFO 2 // gimme some more information
2506 #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)
2507 #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)
2509 #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)
2510 #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)
2512 int multi_join_warn_update_low(int code)
2516 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),LOW_WARN_TEXT);
2519 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),LOW_INFO_TEXT);
2522 return CW_CODE_CANCEL;
2525 int multi_join_warn_update_medium(int code)
2529 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),MED_WARN_TEXT);
2532 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),MED_INFO_TEXT);
2535 return CW_CODE_CANCEL;
2538 int multi_join_maybe_warn()
2542 // if the player is set for low updates
2543 if(Player->m_local_options.obj_update_level == OBJ_UPDATE_LOW){
2546 code = multi_join_warn_update_low(code);
2547 } while((code != CW_CODE_CANCEL) && (code != CW_CODE_OK));
2552 // if the player is set for medium updates
2553 else if(Player->m_local_options.obj_update_level == OBJ_UPDATE_MEDIUM){
2556 code = multi_join_warn_update_medium(code);
2557 } while((code != CW_CODE_CANCEL) && (code != CW_CODE_OK));
2565 int multi_join_warn_pxo()
2567 // 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;
2571 void multi_join_blit_protocol()
2573 gr_set_color_fast(&Color_bright);
2575 switch(Socket_type){
2578 gr_string(5, 2, "TCP");
2582 gr_string(5, 2, "IPX");
2588 // -------------------------------------------------------------------------------------------------
2590 // MULTIPLAYER START GAME screen
2595 #define MULTI_SG_PALETTE "InterfacePalette"
2597 static const char *Multi_sg_bitmap_fname[GR_NUM_RESOLUTIONS] = {
2598 "MultiStartGame", // GR_640
2599 "2_MultiStartGame" // GR_1024
2602 static const char *Multi_sg_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
2603 "MultiStartGame-M", // GR_640
2604 "2_MultiStartGame-M" // GR_1024
2609 int Multi_sg_rank_max_display[GR_NUM_RESOLUTIONS] = {
2614 // constants for coordinate look ups
2615 #define MSG_X_COORD 0
2616 #define MSG_Y_COORD 1
2617 #define MSG_W_COORD 2
2618 #define MSG_H_COORD 3
2622 // input password field
2623 int Msg_passwd_coords[GR_NUM_RESOLUTIONS][4] = {
2636 // input game title field
2637 int Msg_title_coords[GR_NUM_RESOLUTIONS][4] = {
2650 // rank selected field
2651 int Msg_rank_sel_coords[GR_NUM_RESOLUTIONS][4] = {
2665 int Msg_rank_list_coords[GR_NUM_RESOLUTIONS][4] = {
2681 #define MULTI_SG_NUM_BUTTONS 12
2682 #define MSG_OPEN_GAME 0
2683 #define MSG_CLOSED_GAME 1
2684 #define MSG_PASSWD_GAME 2
2685 #define MSG_RESTRICTED_GAME 3
2686 #define MSG_RANK_SET_GAME 4
2687 #define MSG_RANK_SCROLL_UP 5
2688 #define MSG_RANK_SCROLL_DOWN 6
2689 #define MSG_RANK_ABOVE 7
2690 #define MSG_RANK_BELOW 8
2692 #define MSG_OPTIONS 10
2693 #define MSG_ACCEPT 11
2695 #define MULTI_SG_NUM_BUTTONS 10
2696 #define MSG_OPEN_GAME 0
2697 //#define MSG_CLOSED_GAME 1
2698 //#define MSG_RESTRICTED_GAME 2
2699 #define MSG_PASSWD_GAME 1
2700 #define MSG_RANK_SET_GAME 2
2701 #define MSG_RANK_SCROLL_UP 3
2702 #define MSG_RANK_SCROLL_DOWN 4
2703 #define MSG_RANK_ABOVE 5
2704 #define MSG_RANK_BELOW 6
2706 #define MSG_OPTIONS 8
2707 #define MSG_ACCEPT 9
2710 UI_WINDOW Multi_sg_window; // the window object for the join screen
2711 UI_BUTTON Multi_sg_rank_button; // for selecting the rank marker
2712 UI_INPUTBOX Multi_sg_game_name; // for Netgame.name
2713 UI_INPUTBOX Multi_sg_game_passwd; // for Netgame.passwd
2714 int Multi_sg_bitmap; // the background bitmap
2716 ui_button_info Multi_sg_buttons[GR_NUM_RESOLUTIONS][MULTI_SG_NUM_BUTTONS] = {
2719 ui_button_info("MSG_00", 75, 111, -1, -1, 0),
2720 ui_button_info("MSG_01", 75, 139, -1, -1, 1),
2721 ui_button_info("MSG_02", 75, 164, -1, -1, 2),
2722 ui_button_info("MSG_03", 75, 199, -1, -1, 3),
2723 ui_button_info("MSG_04", 75, 243, -1, -1, 4),
2724 ui_button_info("MSG_05", 376, 231, -1, -1, 5),
2725 ui_button_info("MSG_06", 376, 258, -1, -1, 6),
2726 ui_button_info("MSG_07", 376, 291, -1, -1, 7),
2727 ui_button_info("MSG_08", 376, 320, -1, -1, 8),
2728 ui_button_info("MSG_09", 469, 427, -1, -1, 9),
2729 ui_button_info("MSG_10", 447, 452, -1, -1, 10),
2730 ui_button_info("MSG_11", 561, 411, -1, -1, 11),
2732 ui_button_info("MSG_00", 1, 184, 34, 191, 2), // open
2733 // ui_button_info("MSG_01", 1, 159, 34, 166, 1), // closed
2734 // ui_button_info("MSG_02", 1, 184, 34, 191, 2), // restricted
2735 ui_button_info("MSG_03", 1, 209, 34, 218, 3), // password
2736 ui_button_info("MSG_04", 1, 257, 34, 266, 4), // rank set
2737 ui_button_info("MSG_05", 1, 282, -1, -1, 5), // rank scroll up
2738 ui_button_info("MSG_06", 1, 307, -1, -1, 6), // rank scroll down
2739 ui_button_info("MSG_07", 177, 282, 210, 290, 7), // rank above
2740 ui_button_info("MSG_08", 177, 307, 210, 315, 8), // rank below
2741 ui_button_info("MSG_09", 536, 429, 500, 440, 9), // help
2742 ui_button_info("MSG_10", 536, 454, 479, 464, 10), // options
2743 ui_button_info("MSG_11", 576, 432, 571, 415, 11), // accept
2747 ui_button_info("2_MSG_00", 2, 295, 51, 307, 2), // open
2748 // ui_button_info("2_MSG_01", 2, 254, 51, 267, 1), // closed
2749 // ui_button_info("2_MSG_02", 2, 295, 51, 307, 2), // restricted
2750 ui_button_info("2_MSG_03", 2, 335, 51, 350, 3), // password
2751 ui_button_info("2_MSG_04", 2, 412, 51, 426, 4), // rank set
2752 ui_button_info("2_MSG_05", 2, 452, -1, -1, 5), // rank scroll up
2753 ui_button_info("2_MSG_06", 2, 492, -1, -1, 6), // rank scroll down
2754 ui_button_info("2_MSG_07", 284, 452, 335, 465, 7), // rank above
2755 ui_button_info("2_MSG_08", 284, 492, 335, 505, 8), // rank below
2756 ui_button_info("2_MSG_09", 858, 687, 817, 706, 9), // help
2757 ui_button_info("2_MSG_10", 858, 728, 797, 743, 10), // options
2758 ui_button_info("2_MSG_11", 921, 692, 921, 664, 11), // accept
2759 #ifdef MAKE_FS1 // filler for extra FS1 buttons
2760 ui_button_info("none", -1, -1, -1, -1, -1),
2761 ui_button_info("none", -1, -1, -1, -1, -1),
2767 #define MULTI_SG_NUM_TEXT 11
2769 UI_XSTR Multi_sg_text[GR_NUM_RESOLUTIONS][MULTI_SG_NUM_TEXT] = {
2771 {"Open", 1322, 34, 191, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_OPEN_GAME].button},
2772 // {"Closed", 1323, 34, 166, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_CLOSED_GAME].button},
2773 // {"Restricted", 1324, 34, 191, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RESTRICTED_GAME].button},
2774 {"Password Protected", 1325, 34, 218, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_PASSWD_GAME].button},
2775 {"Allow Rank", 1326, 34, 266, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_SET_GAME].button},
2776 {"Above", 1327, 210, 290, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_ABOVE].button},
2777 {"Below", 1328, 210, 315, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_BELOW].button},
2778 {"Help", 928, 500, 440, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_HELP].button},
2779 {"Options", 1036, 479, 464, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_OPTIONS].button},
2780 {"Accept", 1035, 571, 415, UI_XSTR_COLOR_PINK, -1, &Multi_sg_buttons[0][MSG_ACCEPT].button},
2781 {"Start Game", 1329, 26, 10, UI_XSTR_COLOR_GREEN, -1, NULL},
2782 {"Title", 1330, 26, 31, UI_XSTR_COLOR_GREEN, -1, NULL},
2783 {"Game Type", 1331, 12, 165, UI_XSTR_COLOR_GREEN, -1, NULL},
2786 {"Open", 1322, 51, 307, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_OPEN_GAME].button},
2787 // {"Closed", 1323, 51, 267, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_CLOSED_GAME].button},
2788 // {"Restricted", 1324, 51, 307, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RESTRICTED_GAME].button},
2789 {"Password Protected", 1325, 51, 350, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_PASSWD_GAME].button},
2790 {"Allow Rank", 1326, 51, 426, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_SET_GAME].button},
2791 {"Above", 1327, 335, 465, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_ABOVE].button},
2792 {"Below", 1328, 335, 505, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_BELOW].button},
2793 {"Help", 928, 817, 706, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_HELP].button},
2794 {"Options", 1036, 797, 743, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_OPTIONS].button},
2795 {"Accept", 1035, 921, 664, UI_XSTR_COLOR_PINK, -1, &Multi_sg_buttons[1][MSG_ACCEPT].button},
2796 {"Start Game", 1329, 42, 22, UI_XSTR_COLOR_GREEN, -1, NULL},
2797 {"Title", 1330, 42, 50, UI_XSTR_COLOR_GREEN, -1, NULL},
2798 {"Game Type", 1331, 20, 264, UI_XSTR_COLOR_GREEN, -1, NULL},
2803 // starting index for displaying ranks
2804 int Multi_sg_rank_start;
2805 int Multi_sg_rank_select;
2807 // netgame pointer to indirect through
2808 netgame_info *Multi_sg_netgame;
2810 // hold temporary values in this structure when on a standalone server
2811 netgame_info Multi_sg_netgame_temp;
2813 // forward declarations
2814 void multi_sg_check_buttons();
2815 void multi_sg_button_pressed(int n);
2816 void multi_sg_init_gamenet();
2817 void multi_sg_draw_radio_buttons();
2818 void multi_sg_rank_scroll_up();
2819 void multi_sg_rank_scroll_down();
2820 void multi_sg_rank_display_stuff();
2821 void multi_sg_rank_process_select();
2822 void multi_sg_rank_build_name(char *in,char *out);
2823 void multi_sg_check_passwd();
2824 void multi_sg_check_name();
2825 void multi_sg_release_passwd();
2826 int multi_sg_rank_select_valid(int rank);
2827 void multi_sg_select_rank_default();
2829 // function which takes a rank name and returns the index. Useful for commandline options
2830 // for above and below rank. We return the index of the rank in the Ranks[] array. If
2831 // the rank isn't found, we return -1
2832 int multi_start_game_rank_from_name( char *rank ) {
2836 for ( i = 0; i <= MAX_FREESPACE1_RANK; i++ ) {
2838 for ( i = 0; i <= MAX_FREESPACE2_RANK; i++ ) {
2840 if ( !SDL_strcasecmp(Ranks[i].name, rank) ) {
2848 void multi_start_game_init()
2852 // initialize the gamenet
2853 multi_sg_init_gamenet();
2855 // create the interface window
2856 Multi_sg_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
2857 Multi_sg_window.set_mask_bmap(Multi_sg_bitmap_mask_fname[gr_screen.res]);
2859 // load the background bitmap
2860 Multi_sg_bitmap = bm_load(Multi_sg_bitmap_fname[gr_screen.res]);
2861 if(Multi_sg_bitmap < 0){
2862 // we failed to load the bitmap - this is very bad
2866 // initialize the common notification messaging
2867 multi_common_notify_init();
2869 // initialize the common text area
2870 multi_common_set_text("");
2872 // use the common interface palette
2873 multi_common_set_palette();
2875 // create the interface buttons
2876 for(idx=0; idx<MULTI_SG_NUM_BUTTONS; idx++){
2877 // create the object
2878 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);
2880 // set the sound to play when highlighted
2881 Multi_sg_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
2883 // set the ani for the button
2884 Multi_sg_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sg_buttons[gr_screen.res][idx].filename);
2887 Multi_sg_buttons[gr_screen.res][idx].button.link_hotspot(Multi_sg_buttons[gr_screen.res][idx].hotspot);
2892 for(idx=0; idx<MULTI_SG_NUM_TEXT; idx++){
2893 Multi_sg_window.add_XSTR(&Multi_sg_text[gr_screen.res][idx]);
2897 // load the help overlay
2898 help_overlay_load(MULTI_START_OVERLAY);
2899 help_overlay_set_state(MULTI_START_OVERLAY,0);
2901 // intiialize the rank selection items
2902 multi_sg_select_rank_default();
2903 Multi_sg_rank_start = Multi_sg_rank_select;
2905 // create the rank select button
2906 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);
2907 Multi_sg_rank_button.hide();
2909 // create the netgame name input box
2910 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);
2912 // create the netgame password input box, and disable it by default
2913 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);
2914 Multi_sg_game_passwd.hide();
2915 Multi_sg_game_passwd.disable();
2917 // set the netgame text to this gadget and make it have focus
2918 Multi_sg_game_name.set_text(Multi_sg_netgame->name);
2919 Multi_sg_game_name.set_focus();
2921 // if starting a netgame, set the name of the game and any other options that are appropriate
2922 if ( Cmdline_start_netgame ) {
2923 if ( Cmdline_game_name != NULL ) {
2924 strcpy( Multi_sg_netgame->name, Cmdline_game_name );
2925 Multi_sg_game_name.set_text(Multi_sg_netgame->name);
2928 // deal with the different game types -- only one should even be active, so we will just go down
2929 // the line. Last one wins.
2930 if ( Cmdline_closed_game ) {
2931 Multi_sg_netgame->mode = NG_MODE_CLOSED;
2932 } else if ( Cmdline_restricted_game ) {
2933 Multi_sg_netgame->mode = NG_MODE_RESTRICTED;
2934 } else if ( Cmdline_game_password != NULL ) {
2935 Multi_sg_netgame->mode = NG_MODE_PASSWORD;
2936 strcpy(Multi_sg_netgame->passwd, Cmdline_game_password);
2937 Multi_sg_game_passwd.set_text(Multi_sg_netgame->passwd);
2940 // deal with rank above and rank below
2941 if ( (Cmdline_rank_above != NULL) || (Cmdline_rank_below != NULL) ) {
2945 if ( Cmdline_rank_above != NULL ) {
2946 rank_str = Cmdline_rank_above;
2948 rank_str = Cmdline_rank_below;
2951 // try and get the rank index from the name -- if found, then set the rank base
2952 // and the game type. apparently we only support either above or below, not both
2953 // together, so I make a random choice
2954 rank = multi_start_game_rank_from_name( rank_str );
2956 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
2958 // now an arbitrary decision
2959 if ( Cmdline_rank_above != NULL ) {
2960 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
2962 Multi_sg_netgame->mode = NG_MODE_RANK_BELOW;
2967 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
2971 void multi_start_game_do()
2973 // return here since we will be moving to the next stage anyway -- I don't want to see the backgrounds of
2974 // all the screens for < 1 second for every screen we automatically move to.
2975 if ( Cmdline_start_netgame ) {
2979 int k = Multi_sg_window.process();
2981 // process any keypresses
2984 if(help_overlay_active(MULTI_START_OVERLAY)){
2985 help_overlay_set_state(MULTI_START_OVERLAY,0);
2987 gamesnd_play_iface(SND_USER_SELECT);
2988 multi_quit_game(PROMPT_NONE);
2993 case SDLK_LCTRL + SDLK_RETURN :
2994 case SDLK_RCTRL + SDLK_RETURN :
2995 gamesnd_play_iface(SND_COMMIT_PRESSED);
2996 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
3000 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
3001 help_overlay_set_state(MULTI_START_OVERLAY, 0);
3004 // check to see if the user has selected a different rank
3005 multi_sg_rank_process_select();
3007 // check any button presses
3008 multi_sg_check_buttons();
3010 // check to see if any of the input boxes have changed, and update the appropriate Netgame fields if necessary
3011 multi_sg_check_passwd();
3012 multi_sg_check_name();
3014 // draw the background, etc
3016 GR_MAYBE_CLEAR_RES(Multi_sg_bitmap);
3017 if(Multi_sg_bitmap != -1){
3018 gr_set_bitmap(Multi_sg_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
3021 Multi_sg_window.draw();
3023 // display rank stuff
3024 multi_sg_rank_display_stuff();
3026 // display any pending notification messages
3027 multi_common_notify_do();
3029 // draw all radio button
3030 multi_sg_draw_radio_buttons();
3032 // draw the help overlay
3033 help_overlay_maybe_blit(MULTI_START_OVERLAY);
3039 void multi_start_game_close()
3041 // if i'm the host on a standalone server, send him my start game options (passwd, mode, etc)
3042 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
3043 multi_options_update_start_game(Multi_sg_netgame);
3046 // unload any bitmaps
3047 if(!bm_unload(Multi_sg_bitmap)){
3048 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_sg_bitmap_fname[gr_screen.res]));
3051 // unload the help overlay
3052 help_overlay_unload(MULTI_START_OVERLAY);
3054 // destroy the UI_WINDOW
3055 Multi_sg_window.destroy();
3058 void multi_sg_check_buttons()
3061 for(idx=0;idx<MULTI_SG_NUM_BUTTONS;idx++){
3062 // we only really need to check for one button pressed at a time, so we can break after
3064 if(Multi_sg_buttons[gr_screen.res][idx].button.pressed()){
3065 multi_sg_button_pressed(idx);
3071 void multi_sg_button_pressed(int n)
3074 // go to the options screen
3076 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
3081 if(!help_overlay_active(MULTI_START_OVERLAY)){
3082 help_overlay_set_state(MULTI_START_OVERLAY,1);
3084 help_overlay_set_state(MULTI_START_OVERLAY,0);
3088 // the open button was pressed
3090 // if the closed option is selected
3091 if(Multi_sg_netgame->mode != NG_MODE_OPEN){
3092 Multi_sg_netgame->mode = NG_MODE_OPEN;
3094 gamesnd_play_iface(SND_USER_SELECT);
3096 // release the password control if necessary
3097 multi_sg_release_passwd();
3099 // if its already selected
3101 gamesnd_play_iface(SND_GENERAL_FAIL);
3106 // the open button was pressed
3107 case MSG_CLOSED_GAME:
3108 // if the closed option is selected
3109 if(Multi_sg_netgame->mode != NG_MODE_CLOSED){
3110 Multi_sg_netgame->mode = NG_MODE_CLOSED;
3112 gamesnd_play_iface(SND_USER_SELECT);
3114 // release the password control if necessary
3115 multi_sg_release_passwd();
3117 // if its already selected
3119 gamesnd_play_iface(SND_GENERAL_FAIL);
3124 // toggle password protection
3125 case MSG_PASSWD_GAME:
3126 // if we selected it
3127 if(Multi_sg_netgame->mode != NG_MODE_PASSWORD){
3128 gamesnd_play_iface(SND_USER_SELECT);
3130 Multi_sg_game_passwd.enable();
3131 Multi_sg_game_passwd.unhide();
3132 Multi_sg_game_passwd.set_focus();
3134 Multi_sg_netgame->mode = NG_MODE_PASSWORD;
3136 // copy in the current network password
3137 Multi_sg_game_passwd.set_text(Multi_sg_netgame->passwd);
3139 gamesnd_play_iface(SND_GENERAL_FAIL);
3144 // toggle "restricted" on or off
3145 case MSG_RESTRICTED_GAME:
3146 if(Multi_sg_netgame->mode != NG_MODE_RESTRICTED){
3147 gamesnd_play_iface(SND_USER_SELECT);
3148 Multi_sg_netgame->mode = NG_MODE_RESTRICTED;
3150 // release the password control if necessary
3151 multi_sg_release_passwd();
3153 gamesnd_play_iface(SND_GENERAL_FAIL);
3158 // turn off all rank requirements
3159 case MSG_RANK_SET_GAME:
3160 // if either is set, then turn then both off
3161 if((Multi_sg_netgame->mode != NG_MODE_RANK_BELOW) && (Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE)){
3162 gamesnd_play_iface(SND_USER_SELECT);
3164 // set it to the default case if we're turning it off
3165 multi_sg_select_rank_default();
3166 Multi_sg_rank_start = Multi_sg_rank_select;
3168 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
3170 // release the password control if necessary
3171 multi_sg_release_passwd();
3173 gamesnd_play_iface(SND_GENERAL_FAIL);
3177 // rank above was pressed
3178 case MSG_RANK_ABOVE :
3179 if((Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE) || (Multi_sg_netgame->mode == NG_MODE_RANK_BELOW)){
3180 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
3182 // select the first item
3183 multi_sg_select_rank_default();
3184 Multi_sg_rank_start = Multi_sg_rank_select;
3187 gamesnd_play_iface(SND_USER_SELECT);
3189 gamesnd_play_iface(SND_GENERAL_FAIL);
3193 // rank below was pressed
3194 case MSG_RANK_BELOW :
3195 if((Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE) || (Multi_sg_netgame->mode == NG_MODE_RANK_BELOW)){
3196 Multi_sg_netgame->mode = NG_MODE_RANK_BELOW;
3198 // select the first item
3199 multi_sg_select_rank_default();
3200 Multi_sg_rank_start = Multi_sg_rank_select;
3203 gamesnd_play_iface(SND_USER_SELECT);
3205 gamesnd_play_iface(SND_GENERAL_FAIL);
3209 // scroll the rank list up
3210 case MSG_RANK_SCROLL_UP:
3211 multi_sg_rank_scroll_up();
3214 // scroll the rank list down
3215 case MSG_RANK_SCROLL_DOWN:
3216 multi_sg_rank_scroll_down();
3219 // move to the create game screen
3221 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
3222 gamesnd_play_iface(SND_COMMIT_PRESSED);
3226 gamesnd_play_iface(SND_GENERAL_FAIL);
3227 multi_common_add_notify(XSTR("Not implemented yet!",760));
3232 // NOTE : this is where all Netgame initialization should take place on the host
3233 void multi_sg_init_gamenet()
3235 char buf[128],out_name[128];
3237 net_player *server_save;
3239 // back this data up in case we are already connected to a standalone
3240 memcpy(&save,&Netgame.server_addr,sizeof(net_addr));
3241 server_save = Netgame.server;
3243 // remove campaign flags
3244 Game_mode &= ~(GM_CAMPAIGN_MODE);
3246 // clear out the Netgame structure and start filling in the values
3247 memset( &Netgame, 0, sizeof(Netgame) );
3248 memset( &Multi_sg_netgame_temp, 0, sizeof(netgame_info) );
3250 // if we're on the standalone, we're not the server, so we don't care about setting the netgame state
3251 if(Net_player->state != NETPLAYER_STATE_STD_HOST_SETUP){
3252 Netgame.game_state = NETGAME_STATE_HOST_SETUP;
3253 Multi_sg_netgame = &Netgame;
3256 ml_string(NOX("Starting netgame as Host/Server"));
3258 Multi_sg_netgame = &Multi_sg_netgame_temp;
3262 memcpy(&temp_addr.s_addr, &Netgame.server_addr, 4);
3263 char *server_addr = inet_ntoa(temp_addr);
3264 ml_printf(NOX("Starting netgame as Host on Standalone server : %s"), (server_addr == NULL) ? NOX("Unknown") : server_addr);
3267 Net_player->tracker_player_id = Multi_tracker_id;
3269 Multi_sg_netgame->security = (rand() % 32766) + 1; // get some random security number
3270 Multi_sg_netgame->mode = NG_MODE_OPEN;
3271 Multi_sg_netgame->rank_base = RANK_ENSIGN;
3272 if(Multi_sg_netgame->security < 16){
3273 Multi_sg_netgame->security += 16;
3276 // set the version_info field
3277 Multi_sg_netgame->version_info = NG_VERSION_ID;
3279 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
3280 Netgame.host = Net_player;
3283 // set the default netgame flags
3284 Multi_sg_netgame->flags = 0;
3286 // intialize endgame stuff
3287 multi_endgame_init();
3289 // load in my netgame options
3290 multi_options_netgame_load(&Netgame.options);
3292 // load my local netplayer options
3293 multi_options_local_load(&Net_player->p_info.options, Net_player);
3295 // setup the default game name, taking care of string length and player callsigns
3296 memset(out_name,0,128);
3298 pilot_format_callsign_personal(Player->callsign,out_name);
3299 sprintf(buf, XSTR("%s game",782), out_name); // [[ %s will be a pilot's name ]]
3300 if ( strlen(buf) > MAX_GAMENAME_LEN ){
3301 strcpy(buf, XSTR("Temporary name",783));
3303 strcpy(Multi_sg_netgame->name, buf);
3305 // set the default qos and duration
3306 multi_voice_maybe_update_vars(Netgame.options.voice_qos,Netgame.options.voice_record_time);
3308 // make sure to set the server correctly (me or the standalone)
3309 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3310 memcpy(&Netgame.server_addr, &Psnet_my_addr,sizeof(net_addr));
3311 Netgame.server = Net_player;
3312 Net_player->player_id = multi_get_new_id();
3314 // setup debug flags
3315 Netgame.debug_flags = 0;
3317 if(!Cmdline_server_firing){
3318 Netgame.debug_flags |= NETD_FLAG_CLIENT_FIRING;
3320 if(!Cmdline_client_dodamage){
3321 Netgame.debug_flags |= NETD_FLAG_CLIENT_NODAMAGE;
3325 memcpy(&Netgame.server_addr,&save,sizeof(net_addr));
3326 Netgame.server = server_save;
3329 // if I have a cd or not
3331 Net_player->flags |= NETINFO_FLAG_HAS_CD;
3334 // if I have hacked data
3335 if(game_hacked_data()){
3336 Net_player->flags |= NETINFO_FLAG_HAXOR;
3339 // assign my player struct and other data
3340 Net_player->flags |= (NETINFO_FLAG_CONNECTED | NETINFO_FLAG_DO_NETWORKING);
3341 Net_player->s_info.voice_token_timestamp = -1;
3343 // if we're supposed to flush our cache directory, do so now
3344 if(Net_player->p_info.options.flags & MLO_FLAG_FLUSH_CACHE){
3345 multi_flush_multidata_cache();
3348 ml_string(NOX("Flushing multi-data cache"));
3354 void multi_sg_draw_radio_buttons()
3356 // draw the appropriate radio button
3357 switch(Multi_sg_netgame->mode){
3359 Multi_sg_buttons[gr_screen.res][MSG_OPEN_GAME].button.draw_forced(2);
3363 case NG_MODE_CLOSED:
3364 Multi_sg_buttons[gr_screen.res][MSG_CLOSED_GAME].button.draw_forced(2);
3368 case NG_MODE_PASSWORD:
3369 Multi_sg_buttons[gr_screen.res][MSG_PASSWD_GAME].button.draw_forced(2);
3373 case NG_MODE_RESTRICTED:
3374 Multi_sg_buttons[gr_screen.res][MSG_RESTRICTED_GAME].button.draw_forced(2);
3378 case NG_MODE_RANK_ABOVE:
3379 Multi_sg_buttons[gr_screen.res][MSG_RANK_SET_GAME].button.draw_forced(2);
3380 Multi_sg_buttons[gr_screen.res][MSG_RANK_ABOVE].button.draw_forced(2);
3382 case NG_MODE_RANK_BELOW:
3383 Multi_sg_buttons[gr_screen.res][MSG_RANK_SET_GAME].button.draw_forced(2);
3384 Multi_sg_buttons[gr_screen.res][MSG_RANK_BELOW].button.draw_forced(2);
3389 void multi_sg_rank_scroll_up()
3391 // if he doesn't have either of the rank flags set, then ignore this
3392 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3396 if(Multi_sg_rank_start > 0){
3397 Multi_sg_rank_start--;
3398 gamesnd_play_iface(SND_SCROLL);
3400 gamesnd_play_iface(SND_GENERAL_FAIL);
3404 void multi_sg_rank_scroll_down()
3406 // if he doesn't have either of the rank flags set, then ignore this
3407 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3411 if((NUM_RANKS - Multi_sg_rank_start) > Multi_sg_rank_max_display[gr_screen.res]){
3412 Multi_sg_rank_start++;
3413 gamesnd_play_iface(SND_SCROLL);
3415 gamesnd_play_iface(SND_GENERAL_FAIL);
3419 void multi_sg_rank_display_stuff()
3424 // if he doesn't have either of the rank flags set, then ignore this
3425 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3429 // display the list of ranks
3430 y = Msg_rank_list_coords[gr_screen.res][MSG_Y_COORD];
3431 idx = Multi_sg_rank_start;
3433 while((count < NUM_RANKS) && (count < Multi_sg_rank_max_display[gr_screen.res]) && (idx < NUM_RANKS)){
3434 // if its the selected item, then color it differently
3435 if(idx == Multi_sg_rank_select){
3436 gr_set_color_fast(&Color_text_selected);
3438 gr_set_color_fast(&Color_text_normal);
3442 multi_sg_rank_build_name(Ranks[idx].name,rank_name);
3443 gr_string(Msg_rank_list_coords[gr_screen.res][MSG_X_COORD],y,rank_name);
3451 // display the selected rank
3453 gr_set_color_fast(&Color_bright);
3454 multi_sg_rank_build_name(Ranks[Multi_sg_netgame->rank_base].name,rank_name);
3455 gr_string(Msg_rank_sel_coords[gr_screen.res][MSG_X_COORD],Msg_rank_sel_coords[gr_screen.res][MSG_Y_COORD],rank_name);
3459 void multi_sg_rank_process_select()
3463 // if he doesn't have either of the rank flags set, then ignore this
3464 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3468 // see if he's clicked on an item on the rank list
3469 if(Multi_sg_rank_button.pressed()){
3471 Multi_sg_rank_button.get_mouse_pos(NULL,&y);
3474 if(item + Multi_sg_rank_start < NUM_RANKS){
3475 // evaluate whether this rank is valid for the guy to pick
3476 if(multi_sg_rank_select_valid(item + Multi_sg_rank_start)){
3477 gamesnd_play_iface(SND_USER_SELECT);
3479 Multi_sg_rank_select = item + Multi_sg_rank_start;
3481 // set the Netgame rank
3482 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
3484 gamesnd_play_iface(SND_GENERAL_FAIL);
3486 memset(string,0,255);
3487 sprintf(string,XSTR("Illegal value for a host of your rank (%s)\n",784),Ranks[Net_player->player->stats.rank].name);
3488 multi_common_add_notify(string);
3494 void multi_sg_rank_build_name(char *in,char *out)
3500 first = strtok(use," ");
3502 // just copy the string
3507 // if the first part of the string is lieutenant, then abbreivate it and tack on the rest of the string
3508 if (SDL_strcasecmp(first,XSTR("lieutenant",785)) == 0) {
3509 first = strtok(NULL, NOX("\n"));
3511 // if he's not just a plain lieutenant
3513 strcpy(out,XSTR("Lt. ",786)); // [[ lieutenant ]]
3516 // if he _is_ just a plain lieutenant
3525 void multi_sg_check_passwd()
3527 // check to see if the password input box has been pressed
3528 if(Multi_sg_game_passwd.changed()){
3529 Multi_sg_game_passwd.get_text(Multi_sg_netgame->passwd);
3533 void multi_sg_check_name()
3535 // check to see if the game name input box has been pressed
3536 if(Multi_sg_game_name.changed()){
3537 Multi_sg_game_name.get_text(Multi_sg_netgame->name);
3541 void multi_sg_release_passwd()
3543 // hide and disable the password input box
3544 Multi_sg_game_passwd.hide();
3545 Multi_sg_game_passwd.disable();
3547 // set the focus back to the name input box
3548 Multi_sg_game_name.set_focus();
3551 int multi_sg_rank_select_valid(int rank)
3554 if(Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE){
3555 if(Net_player->player->stats.rank >= rank){
3561 if(Net_player->player->stats.rank <= rank){
3569 void multi_sg_select_rank_default()
3571 // pick our rank for now
3572 Multi_sg_rank_select = Net_player->player->stats.rank;
3574 // set the Netgame rank
3575 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
3578 // -------------------------------------------------------------------------------------------------
3580 // MULTIPLAYER CREATE GAME screen
3585 const char *Multi_create_bitmap_fname[GR_NUM_RESOLUTIONS] = {
3586 "MultiCreate", // GR_640
3587 "2_MultiCreate" // GR_1024
3590 const char *Multi_create_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
3591 "MultiCreate-M", // GR_640
3592 "2_MultiCreate-M" // GR_1024
3595 const char *Multi_create_loading_fname[GR_NUM_RESOLUTIONS] = {
3596 "PleaseWait", // GR_640
3597 "2_PleaseWait" // GR_1024
3601 #define MULTI_CREATE_NUM_BUTTONS 23
3604 #define MC_SHOW_ALL 0
3605 #define MC_SHOW_COOP 1
3606 #define MC_SHOW_TEAM 2
3607 #define MC_SHOW_DOGFIGHT 3
3608 #define MC_PXO_REFRESH 4
3609 #define MC_PILOT_INFO 5
3610 #define MC_SCROLL_LIST_UP 6
3611 #define MC_SCROLL_LIST_DOWN 7
3612 #define MC_SCROLL_PLAYERS_UP 8
3613 #define MC_SCROLL_PLAYERS_DOWN 9
3614 #define MC_MISSION_FILTER 10
3615 #define MC_CAMPAIGN_FILTER 11
3616 #define MC_CANCEL 12
3621 #define MC_SCROLL_INFO_UP 17
3622 #define MC_SCROLL_INFO_DOWN 18
3623 #define MC_HOST_OPTIONS 19
3625 #define MC_OPTIONS 21
3626 #define MC_ACCEPT 22
3629 UI_WINDOW Multi_create_window; // the window object for the create screen
3630 UI_BUTTON Multi_create_player_select_button; // for selecting players
3631 UI_BUTTON Multi_create_list_select_button; // for selecting missions/campaigns
3632 int Multi_create_bitmap; // the background bitmap
3633 UI_SLIDER2 Multi_create_slider; // for create list
3635 // constants for coordinate look ups
3636 #define MC_X_COORD 0
3637 #define MC_Y_COORD 1
3638 #define MC_W_COORD 2
3639 #define MC_H_COORD 3
3641 ui_button_info Multi_create_buttons[GR_NUM_RESOLUTIONS][MULTI_CREATE_NUM_BUTTONS] = {
3644 ui_button_info("MC_18", 34, 131, -1, -1, 18), // all
3645 ui_button_info("MC_19", 72, 131, -1, -1, 19), // coop
3646 ui_button_info("MC_20", 120, 131, -1, -1, 20), // team
3647 // ui_button_info("MC_21", 166, 131, -1, -1, 21), // dogfight
3648 ui_button_info("none", -1, -1, -1, -1, -1), // dogfight (not used)
3649 ui_button_info("none", -1, -1, -1, -1, -1), // pxo?
3650 ui_button_info("MC_26", 540, 114, -1, -1, 26), // pilot info
3651 ui_button_info("MC_03", 0, 187, -1, -1, 2), // scroll list up
3652 ui_button_info("MC_02", 0, 227, -1, -1, 3), // scroll list down
3653 ui_button_info("MC_04", 611, 182, -1, -1, 4), // scroll players up
3654 ui_button_info("MC_05", 611, 221, -1, -1, 5), // scroll players down
3655 ui_button_info("MC_06", 18, 322, -1, -1, 6), // mission filter
3656 ui_button_info("MC_07", 18, 344, -1, -1, 7), // campaign filter
3657 ui_button_info("MC_10", 317, 339, -1, -1, 10), // cancel
3658 ui_button_info("MC_14", 464, 350, -1, -1, 14), // team 1
3659 ui_button_info("MC_15", 498, 350, -1, -1, 15), // team 2
3660 ui_button_info("MC_16", 527, 346, -1, -1, 16), // kick
3661 ui_button_info("MC_17", 572, 346, -1, -1, 17), // close
3662 ui_button_info("MC_08", 0, 398, -1, -1, 8), // scroll mission info up
3663 ui_button_info("MC_09", 0, 435, -1, -1, 9), // scroll mission info down
3664 ui_button_info("MC_27", 447, 402, -1, -1, 27), // host options
3665 ui_button_info("MC_11", 510, 428, -1, -1, 11), // help
3666 ui_button_info("MC_12", 510, 453, -1, -1, 12), // options
3667 ui_button_info("Mc_13", 562, 412, -1, -1, 13), // commit
3669 ui_button_info("MC_00", 32, 129, 36, 158, 0), // show all missions
3670 ui_button_info("MC_01", 76, 129, 71, 158, 1), // show coop missions
3671 ui_button_info("MC_02", 121, 129, 119, 158, 2), // show team missions
3672 ui_button_info("MC_03", 164, 129, 166, 158, 3), // show dogfight missions
3673 ui_button_info("MC_04", 399, 129, 229, 130, 4), // pxo mission refresh
3674 ui_button_info("MC_05", 567, 123, 467, 132, 5), // pilot info
3675 ui_button_info("MC_06", 1, 161, -1, -1, 6), // scroll mission info up
3676 ui_button_info("MC_08", 1, 304, -1, -1, 8), // scroll mission info down
3677 ui_button_info("MC_09", 613, 160, -1, -1, 9), // scroll players up
3678 ui_button_info("MC_10", 613, 202, -1, -1, 10), // scroll players down
3679 ui_button_info("MC_11", 22, 346, 27, 376, 11), // mission filter
3680 ui_button_info("MC_12", 104, 346, 110, 376, 12), // campaign filter
3681 ui_button_info("MC_13", 392, 341, 328, 364, 13), // cancel
3682 ui_button_info("MC_14", 472, 352, 482, 381, 14), // team 0
3683 ui_button_info("MC_15", 506, 352, 514, 381, 15), // team 1
3684 ui_button_info("MC_16", 539, 346, 539, 381, 16), // kick
3685 ui_button_info("MC_17", 589, 346, 582, 381, 17), // close
3686 ui_button_info("MC_18", 1, 406, -1, -1, 18), // scroll list up
3687 ui_button_info("MC_19", 1, 447, -1, -1, 19), // scroll list down
3688 ui_button_info("MC_20", 499, 434, 436, 423, 20), // host options
3689 ui_button_info("MC_21", 534, 426, -1, -1, 21), // help
3690 ui_button_info("MC_22", 534, 452, -1, -1, 22), // options
3691 ui_button_info("MC_23", 571, 426, 572, 413, 23), // commit
3695 ui_button_info("2_MC_00", 51, 207, 61, 253, 0), // show all missions
3696 ui_button_info("2_MC_01", 122, 207, 124, 253, 1), // show coop missions
3697 ui_button_info("2_MC_02", 193, 207, 194, 253, 2), // show team missions
3698 ui_button_info("2_MC_03", 263, 207, 261, 253, 3), // show dogfight missions
3699 ui_button_info("2_MC_04", 639, 207, 479, 218, 4), // pxo mission refresh
3700 ui_button_info("2_MC_05", 907, 197, 748, 216, 5), // pilot info
3701 ui_button_info("2_MC_06", 1, 258, -1, -1, 6), // scroll mission info up
3702 ui_button_info("2_MC_08", 1, 487, -1, -1, 8), // scroll mission info down
3703 ui_button_info("2_MC_09", 981, 256, -1, -1, 9), // scroll players up
3704 ui_button_info("2_MC_10", 981, 323, -1, -1, 10), // scroll players down
3705 ui_button_info("2_MC_11", 35, 554, 46, 601, 11), // mission filter
3706 ui_button_info("2_MC_12", 166, 554, 174, 601, 12), // campaign filter
3707 ui_button_info("2_MC_13", 628, 545, 559, 582, 13), // cancel
3708 ui_button_info("2_MC_14", 756, 564, 772, 610, 14), // team 0
3709 ui_button_info("2_MC_15", 810, 564, 826, 610, 15), // team 1
3710 ui_button_info("2_MC_16", 862, 554, 872, 610, 16), // kick
3711 ui_button_info("2_MC_17", 943, 554, 949, 610, 17), // close
3712 ui_button_info("2_MC_18", 1, 649, -1, -1, 18), // scroll list up
3713 ui_button_info("2_MC_19", 1, 716, -1, -1, 19), // scroll list down
3714 ui_button_info("2_MC_20", 798, 695, 726, 667, 20), // host options
3715 ui_button_info("2_MC_21", 854, 681, -1, -1, 21), // help
3716 ui_button_info("2_MC_22", 854, 724, -1, -1, 22), // options
3717 ui_button_info("2_MC_23", 914, 681, 932, 667, 23), // commit
3722 #define MULTI_CREATE_NUM_TEXT 0
3724 #define MULTI_CREATE_NUM_TEXT 15
3726 UI_XSTR Multi_create_text[GR_NUM_RESOLUTIONS][MULTI_CREATE_NUM_BUTTONS] = {
3728 // not needed for FS1
3730 {"All", 1256, 36, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_ALL].button},
3731 {"Coop", 1257, 71, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_COOP].button},
3732 {"Team", 1258, 119, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_TEAM].button},
3733 {"Dogfight", 1259, 166, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_DOGFIGHT].button},
3734 {"Refresh Missions", 1260, 229, 130, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_PXO_REFRESH].button},
3735 {"Pilot Info", 1261, 467, 132, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_PILOT_INFO].button},
3736 {"Missions", 1262, 27, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_MISSION_FILTER].button},
3737 {"Campaigns", 1263, 110, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_CAMPAIGN_FILTER].button},
3738 {"Cancel", 387, 328, 364, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_CANCEL].button},
3739 {"1", 1264, 482, 381, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_TEAM0].button},
3740 {"2", 1265, 514, 381, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_TEAM1].button},
3741 {"Kick", 1266, 539, 381, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_KICK].button},
3742 {"Close", 1508, 582, 381, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_CLOSE].button},
3743 {"Host Options", 1267, 436, 423, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_HOST_OPTIONS].button},
3744 {"Commit", 1062, 572, 413, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_ACCEPT].button}
3748 // not needed for FS1
3750 {"All", 1256, 61, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_ALL].button},
3751 {"Coop", 1257, 124, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_COOP].button},
3752 {"Team", 1258, 194, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_TEAM].button},
3753 {"Dogfight", 1259, 261, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_DOGFIGHT].button},
3754 {"Refresh Missions", 1260, 501, 218, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_PXO_REFRESH].button},
3755 {"Pilot Info", 1261, 814, 216, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_PILOT_INFO].button},
3756 {"Missions", 1262, 46, 601, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_MISSION_FILTER].button},
3757 {"Campaigns", 1263, 174, 601, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_CAMPAIGN_FILTER].button},
3758 {"Cancel", 387, 559, 582, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_CANCEL].button},
3759 {"1", 1264, 772, 610, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_TEAM0].button},
3760 {"2", 1265, 826, 610, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_TEAM1].button},
3761 {"Kick", 1266, 872, 610, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_KICK].button},
3762 {"Close", 1508, 949, 610, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_CLOSE].button},
3763 {"Host Options", 1267, 755, 683, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_HOST_OPTIONS].button},
3764 {"Commit", 1062, 932, 667, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_ACCEPT].button}
3769 // squad war checkbox
3770 UI_CHECKBOX Multi_create_sw_checkbox;
3771 const char *Multi_create_sw_checkbox_fname[GR_NUM_RESOLUTIONS] = {
3775 int Multi_create_sw_checkbox_coords[GR_NUM_RESOLUTIONS][2] = {
3783 int Multi_create_sw_checkbox_text[GR_NUM_RESOLUTIONS][2] = {
3792 // game information text areas
3793 int Mc_list_coords[GR_NUM_RESOLUTIONS][4] = {
3806 int Mc_players_coords[GR_NUM_RESOLUTIONS][4] = {
3819 int Mc_info_coords[GR_NUM_RESOLUTIONS][4] = {
3832 // mission icon stuff
3833 int Mc_icon_type_coords[GR_NUM_RESOLUTIONS][2] = {
3835 38, -2 // y is an offset
3838 61, -2 // y is an offset
3842 int Mc_icon_volition_coords[GR_NUM_RESOLUTIONS][2] = {
3844 61, -1 // y is an offset
3847 98, 1 // y is an offset
3851 int Mc_icon_silent_coords[GR_NUM_RESOLUTIONS][2] = {
3853 72, 0 // y is an offset
3856 115, 0 // y is an offset
3860 int Mc_icon_valid_coords[GR_NUM_RESOLUTIONS][2] = {
3862 91, 0 // y is an offset
3865 146, 0 // y is an offset
3869 // mission/campaign list column areas
3870 int Mc_column1_w[GR_NUM_RESOLUTIONS] = {
3875 int Mc_column2_w[GR_NUM_RESOLUTIONS] = {
3880 int Mc_column3_w[GR_NUM_RESOLUTIONS] = {
3885 int Mc_mission_name_x[GR_NUM_RESOLUTIONS] = {
3890 int Mc_mission_count_x[GR_NUM_RESOLUTIONS] = {
3895 int Mc_mission_fname_x[GR_NUM_RESOLUTIONS] = {
3900 int Mc_create_game_text[GR_NUM_RESOLUTIONS][2] = {
3901 {13, 116}, // GR_640
3902 {21, 186} // GR_1024
3905 int Mc_players_text[GR_NUM_RESOLUTIONS][2] = {
3906 {467, 150}, // GR_640
3907 {747, 240} // GR_1024
3910 int Mc_team_text[GR_NUM_RESOLUTIONS][2] = {
3911 {484, 342}, // GR_640
3912 {774, 547} // GR_1024
3915 int Mc_slider_coords[GR_NUM_RESOLUTIONS][4] = {
3917 3, 197, 13, 105 // GR_640
3920 5, 316, 20, 168 // GR_1024
3924 const char *Mc_slider_bitmap[GR_NUM_RESOLUTIONS] = {
3929 // player list control thingie defs
3930 #define MULTI_CREATE_PLIST_MAX_DISPLAY 20
3931 int Multi_create_plist_select_flag; // flag indicating if we have a play selected
3932 short Multi_create_plist_select_id; // the net address of the currently selected player (for lookup)
3934 // master tracker details
3935 int Multi_create_frame_count; // framecount
3936 int Multi_create_mt_tried_login; // attempted to login this server on the MT
3938 // mission filter settings
3939 int Multi_create_filter; // what mode we're in
3941 // game/campaign list control defs
3942 int Multi_create_list_max_display[GR_NUM_RESOLUTIONS] = {
3948 int Multi_create_list_count; // number of items in listbox
3949 int Multi_create_list_mode; // 0 == mission mode, 1 == campaign mode
3950 int Multi_create_list_start; // where to start displaying from
3951 int Multi_create_list_select; // which item is currently highlighted
3952 int Multi_create_files_loaded;
3954 char Multi_create_files_array[MULTI_CREATE_MAX_LIST_ITEMS][MAX_FILENAME_LEN];
3956 int Multi_create_mission_count; // how many we have
3957 int Multi_create_campaign_count;
3958 multi_create_info Multi_create_mission_list[MULTI_CREATE_MAX_LIST_ITEMS];
3959 multi_create_info Multi_create_campaign_list[MULTI_CREATE_MAX_LIST_ITEMS];
3961 // use a pointer for the file list. Will point to either the missions or the campaigns
3962 multi_create_info *Multi_create_file_list;
3964 // LOCAL function definitions
3965 void multi_create_check_buttons();
3966 void multi_create_button_pressed(int n);
3967 void multi_create_init_as_server();
3968 void multi_create_init_as_client();
3969 void multi_create_do_netstuff();
3970 void multi_create_plist_scroll_up();
3971 void multi_create_plist_scroll_down();
3972 void multi_create_plist_process();
3973 void multi_create_plist_blit_normal();
3974 void multi_create_plist_blit_team();
3975 void multi_create_list_scroll_up();
3976 void multi_create_list_scroll_down();
3977 void multi_create_list_do();
3978 void multi_create_list_select_item(int n);
3979 void multi_create_list_blit_icons(int list_index, int y_start);
3980 void multi_create_accept_hit();
3981 void multi_create_draw_filter_buttons();
3982 void multi_create_set_selected_team(int team);
3983 short multi_create_get_mouse_id();
3984 int multi_create_ok_to_commit();
3985 int multi_create_verify_cds();
3986 void multi_create_refresh_pxo();
3987 void multi_create_sw_clicked();
3989 // since we can selectively filter out mission/campaign types we always need to map a selected index (which is relative
3990 // to the displayed list), to an absolute index (which is relative to the total file list - some of which may filtered out)
3991 void multi_create_select_to_filename(int select_index,char *filename);
3992 int multi_create_select_to_index(int select_index);
3994 int Multi_create_should_show_popup = 0;
3997 // sorting function to sort mission lists.. Basic sorting on mission name
3998 int multi_create_sort_func(const void *a, const void *b)
4000 multi_create_info *m1, *m2;
4002 m1 = (multi_create_info *)a;
4003 m2 = (multi_create_info *)b;
4005 return ( strcmp(m1->name, m2->name) );
4008 void multi_create_setup_list_data(int mode)
4010 int idx,should_sort,switched_modes;
4012 // set the current mode
4015 if((Multi_create_list_mode != mode) && (mode != -1)){
4016 Multi_create_list_mode = mode;
4019 // set up the list pointers
4020 if ( Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ) {
4021 Multi_create_file_list = Multi_create_mission_list;
4022 } else if ( Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ) {
4023 Multi_create_file_list = Multi_create_campaign_list;
4029 // get the mission count based upon the filter selected
4030 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
4031 switch(Multi_create_filter){
4032 case MISSION_TYPE_MULTI:
4033 Multi_create_list_count = Multi_create_mission_count;
4036 Multi_create_list_count = 0;
4037 // find all missions which match
4038 for(idx=0;idx<Multi_create_mission_count;idx++){
4039 if(Multi_create_mission_list[idx].flags & Multi_create_filter){
4040 Multi_create_list_count++;
4044 // if we switched modes and we have more than 0 items, sort them
4045 if(switched_modes && (Multi_create_list_count > 0)){
4050 } else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
4051 switch(Multi_create_filter){
4052 case MISSION_TYPE_MULTI:
4053 Multi_create_list_count = Multi_create_campaign_count;
4056 Multi_create_list_count = 0;
4057 // find all missions which match
4058 for(idx=0;idx<Multi_create_campaign_count;idx++){
4059 if(Multi_create_campaign_list[idx].flags & Multi_create_filter){
4060 Multi_create_list_count++;
4064 // if we switched modes and we have more than 0 items, sort them
4065 if(switched_modes && (Multi_create_list_count > 0)){
4072 // reset the list start and selected indices
4073 Multi_create_list_start = 0;
4074 Multi_create_list_select = -1;
4075 multi_create_list_select_item(Multi_create_list_start);
4077 // sort the list of missions if necessary
4079 qsort(Multi_create_file_list, Multi_create_list_count, sizeof(multi_create_info), multi_create_sort_func);
4084 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);
4088 void multi_create_game_init()
4093 // now make sure to initialze various netgame stuff based upon whether we're on a standalone or not
4094 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4095 multi_create_init_as_server();
4097 multi_create_init_as_client();
4100 // initialize the player list data
4101 Multi_create_plist_select_flag = 0;
4102 Multi_create_plist_select_id = -1;
4104 // create the interface window
4105 Multi_create_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
4106 Multi_create_window.set_mask_bmap(Multi_create_bitmap_mask_fname[gr_screen.res]);
4108 // load the background bitmap
4109 Multi_create_bitmap = bm_load(Multi_create_bitmap_fname[gr_screen.res]);
4110 if(Multi_create_bitmap < 0){
4111 // we failed to load the bitmap - this is very bad
4115 // close any previous existing instances of the chatbox and create a new one
4119 // load the help overlay
4120 help_overlay_load(MULTI_CREATE_OVERLAY);
4121 help_overlay_set_state(MULTI_CREATE_OVERLAY, 0);
4123 // initialize the common notification messaging
4124 multi_common_notify_init();
4126 // use the common interface palette
4127 multi_common_set_palette();
4129 // create the interface buttons
4130 for(idx=0; idx<MULTI_CREATE_NUM_BUTTONS; idx++){
4131 b = &Multi_create_buttons[gr_screen.res][idx];
4133 // create the object
4134 b->button.create(&Multi_create_window, "", b->x, b->y, 1, 1, ((idx == MC_SCROLL_LIST_UP) || (idx == MC_SCROLL_LIST_DOWN)), 1);
4136 // set the sound to play when highlighted
4137 b->button.set_highlight_action(common_play_highlight_sound);
4139 // set the ani for the button
4140 b->button.set_bmaps(b->filename);
4143 b->button.link_hotspot(b->hotspot);
4145 // some special case stuff for the pxo refresh button
4146 if(idx == MC_PXO_REFRESH){
4147 // if not a PXO game, or if I'm not a server disable and hide the button
4148 if(!MULTI_IS_TRACKER_GAME || !MULTIPLAYER_MASTER){
4150 b->button.disable();
4156 for(idx=0; idx<MULTI_CREATE_NUM_TEXT; idx++){
4157 Multi_create_window.add_XSTR(&Multi_create_text[gr_screen.res][idx]);
4160 // if this is a PXO game, enable the squadwar checkbox
4161 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);
4162 Multi_create_sw_checkbox.set_bmaps(Multi_create_sw_checkbox_fname[gr_screen.res], 6, 0);
4163 if(!MULTI_IS_TRACKER_GAME){
4164 Multi_create_sw_checkbox.hide();
4165 Multi_create_sw_checkbox.disable();
4169 // disable squad war button in demo
4170 Multi_create_sw_checkbox.hide();
4171 Multi_create_sw_checkbox.disable();
4174 // initialize the mission type filtering mode
4175 Multi_create_filter = MISSION_TYPE_MULTI;
4177 // initialize the list mode, and load in a list
4178 memset(Multi_create_mission_list, 0, sizeof(multi_create_info) * MULTI_CREATE_MAX_LIST_ITEMS);
4179 memset(Multi_create_campaign_list, 0, sizeof(multi_create_info) * MULTI_CREATE_MAX_LIST_ITEMS);
4180 for(idx=0; idx<MULTI_CREATE_MAX_LIST_ITEMS; idx++){
4181 Multi_create_mission_list[idx].valid_status = MVALID_STATUS_UNKNOWN;
4182 Multi_create_campaign_list[idx].valid_status = MVALID_STATUS_UNKNOWN;
4184 Multi_create_list_mode = MULTI_CREATE_SHOW_MISSIONS;
4185 Multi_create_list_start = -1;
4186 Multi_create_list_select = -1;
4187 Multi_create_list_count = 0;
4190 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);
4193 // create the player list select button
4194 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);
4195 Multi_create_player_select_button.hide();
4197 // create the mission/campaign list select button
4198 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);
4199 Multi_create_list_select_button.hide();
4201 // set hotkeys for a couple of things.
4202 Multi_create_buttons[gr_screen.res][MC_ACCEPT].button.set_hotkey(KEY_CTRLED+SDLK_RETURN);
4204 // init some master tracker stuff
4205 Multi_create_frame_count = 0;
4206 Multi_create_mt_tried_login = 0;
4208 // remove campaign flags
4209 Game_mode &= ~(GM_CAMPAIGN_MODE);
4211 // send any pilots as appropriate
4212 multi_data_send_my_junk();
4213 Multi_create_file_list = Multi_create_mission_list;
4215 Multi_create_campaign_count = 0;
4216 Multi_create_mission_count = 0;
4217 Multi_create_files_loaded = 0;
4220 void multi_create_game_do()
4224 const char *loading_str = XSTR("Loading", 1336);
4228 // set this if we want to show the pilot info popup
4229 Multi_create_should_show_popup = 0;
4231 // first thing is to load the files
4232 if ( !Multi_create_files_loaded ) {
4233 // if I am a client, send a list request to the server for the missions
4234 if ( MULTIPLAYER_CLIENT ) {
4235 send_mission_list_request( MISSION_LIST_REQUEST );
4239 loading_bitmap = bm_load(Multi_create_loading_fname[gr_screen.res]);
4241 // draw the background, etc
4243 GR_MAYBE_CLEAR_RES(Multi_create_bitmap);
4244 if(Multi_create_bitmap != -1){
4245 gr_set_bitmap(Multi_create_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4249 if ( loading_bitmap > -1 ){
4250 gr_set_bitmap(loading_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4252 gr_bitmap( Please_wait_coords[gr_screen.res][MC_X_COORD], Please_wait_coords[gr_screen.res][MC_Y_COORD] );
4254 // draw "Loading" on it
4256 gr_set_color_fast(&Color_normal);
4258 gr_get_string_size(&str_w, &str_h, loading_str);
4259 gr_string((gr_screen.max_w - str_w) / 2, (gr_screen.max_h - str_h) / 2, loading_str);
4265 multi_create_list_load_missions();
4266 multi_create_list_load_campaigns();
4268 // if this is a tracker game, validate missions
4269 if(MULTI_IS_TRACKER_GAME){
4270 multi_update_valid_missions();
4273 // update the file list
4274 multi_create_setup_list_data(MULTI_CREATE_SHOW_MISSIONS);
4277 // don't bother setting netgame state if ont the server
4278 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4279 Netgame.game_state = NETGAME_STATE_FORMING;
4280 send_netgame_update_packet();
4283 // if we're on the standalone we have to tell him that we're now in the host setup screen
4284 Net_player->state = NETPLAYER_STATE_HOST_SETUP;
4285 send_netplayer_update_packet();
4287 Multi_create_files_loaded = 1;
4290 int k = chatbox_process();
4291 k = Multi_create_window.process(k,0);
4294 // same as the cancel button
4296 if(help_overlay_active(MULTI_CREATE_OVERLAY)){
4297 help_overlay_set_state(MULTI_CREATE_OVERLAY,0);
4299 gamesnd_play_iface(SND_USER_SELECT);
4300 multi_quit_game(PROMPT_HOST);
4305 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
4306 help_overlay_set_state(MULTI_CREATE_OVERLAY, 0);
4309 // process any button clicks
4310 multi_create_check_buttons();
4312 // do any network related stuff
4313 multi_create_do_netstuff();
4315 // draw the background, etc
4317 GR_MAYBE_CLEAR_RES(Multi_create_bitmap);
4318 if(Multi_create_bitmap != -1){
4319 gr_set_bitmap(Multi_create_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4323 // if we're not in team vs. team mode, don't draw the team buttons
4324 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
4325 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.hide();
4326 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.hide();
4327 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.disable();
4328 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.disable();
4330 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.enable();
4331 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.enable();
4332 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.unhide();
4333 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.unhide();
4336 // draw the window itself
4337 Multi_create_window.draw();
4339 gr_set_color_fast(&Color_normal);
4342 // draw Create Game text
4343 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));
4345 // draw players text
4346 gr_string(Mc_players_text[gr_screen.res][MC_X_COORD], Mc_players_text[gr_screen.res][MC_Y_COORD], XSTR("Players", 1269));
4348 // draw players text
4349 gr_string(Mc_team_text[gr_screen.res][MC_X_COORD], Mc_team_text[gr_screen.res][MC_Y_COORD], XSTR("Team", 1258));
4352 // process and display the player list
4353 // NOTE : this must be done before the buttons are checked to insure that a player hasn't dropped
4354 multi_create_plist_process();
4355 if(Netgame.type_flags & NG_TYPE_TEAM){
4356 multi_create_plist_blit_team();
4358 multi_create_plist_blit_normal();
4361 // process and display the game/campaign list
4362 multi_create_list_do();
4364 // draw the correct mission filter button
4365 multi_create_draw_filter_buttons();
4367 // display any text in the info area
4368 multi_common_render_text();
4370 // display any pending notification messages
4371 multi_common_notify_do();
4373 // force the correct mission/campaign button to light up
4374 if( Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ){
4375 Multi_create_buttons[gr_screen.res][MC_MISSION_FILTER].button.draw_forced(2);
4377 Multi_create_buttons[gr_screen.res][MC_CAMPAIGN_FILTER].button.draw_forced(2);
4380 // force draw the closed button if it is toggled on
4381 if(Netgame.flags & NG_FLAG_TEMP_CLOSED){
4382 Multi_create_buttons[gr_screen.res][MC_CLOSE].button.draw_forced(2);
4385 // process and show the chatbox thingie
4389 Multi_create_window.draw_tooltip();
4391 // display the voice status indicator
4392 multi_common_voice_display_status();
4394 // blit the help overlay if necessary
4395 help_overlay_maybe_blit(MULTI_CREATE_OVERLAY);
4398 if(MULTI_IS_TRACKER_GAME){
4399 if(Netgame.type_flags & NG_TYPE_SW){
4400 gr_set_color_fast(&Color_bright);
4402 gr_set_color_fast(&Color_normal);
4404 gr_string(Multi_create_sw_checkbox_text[gr_screen.res][0], Multi_create_sw_checkbox_text[gr_screen.res][1], "SquadWar");
4410 // if we're supposed to show the pilot info popup, do it now
4411 if(Multi_create_should_show_popup){
4412 // get the player index and address of the player item the mouse is currently over
4413 if(Multi_create_plist_select_flag){
4414 player_index = find_player_id(Multi_create_plist_select_id);
4415 if(player_index != -1){
4416 multi_pinfo_popup(&Net_players[player_index]);
4421 // increment the frame count
4422 Multi_create_frame_count++;
4425 void multi_create_game_close()
4427 // unload any bitmaps
4428 if(!bm_unload(Multi_create_bitmap)){
4429 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_create_bitmap_fname[gr_screen.res]));
4432 // unload the help overlay
4433 help_overlay_unload(MULTI_CREATE_OVERLAY);
4435 // destroy the chatbox
4438 // destroy the UI_WINDOW
4439 Multi_create_window.destroy();
4442 void multi_create_check_buttons()
4445 for(idx=0;idx<MULTI_CREATE_NUM_BUTTONS;idx++){
4446 // we only really need to check for one button pressed at a time, so we can break after
4448 if(Multi_create_buttons[gr_screen.res][idx].button.pressed()){
4449 multi_create_button_pressed(idx);
4454 // if the squad war checkbox was clicked
4455 if(Multi_create_sw_checkbox.changed()){
4456 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
4616 Multi_create_sw_checkbox.set_state(0);
4617 Netgame.type_flags = NG_TYPE_COOP;
4619 if(Multi_create_list_mode != MULTI_CREATE_SHOW_CAMPAIGNS){
4620 Netgame.campaign_mode = MP_CAMPAIGN;
4622 gamesnd_play_iface(SND_USER_SELECT);
4624 // update the file list
4625 multi_create_setup_list_data(MULTI_CREATE_SHOW_CAMPAIGNS);
4627 gamesnd_play_iface(SND_GENERAL_FAIL);
4631 // attempt to set the selected player's team
4633 multi_create_set_selected_team(0);
4634 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4635 multi_team_send_update();
4639 // attempt to set the selected player's team
4641 multi_create_set_selected_team(1);
4642 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4643 multi_team_send_update();
4647 // popup the pilot info dialog for the currently selected pilot (will occur at the end of the frame)
4649 Multi_create_should_show_popup = 1;
4652 // go to the host options screen
4653 case MC_HOST_OPTIONS:
4654 gamesnd_play_iface(SND_USER_SELECT);
4655 gameseq_post_event(GS_EVENT_MULTI_HOST_OPTIONS);
4658 // refresh PXO file list
4659 case MC_PXO_REFRESH:
4660 if(!MULTI_IS_TRACKER_GAME){
4663 multi_create_refresh_pxo();
4667 gamesnd_play_iface(SND_GENERAL_FAIL);
4668 multi_common_add_notify(XSTR("Not implemented yet!",760));
4673 // do stuff like pinging servers, sending out requests, etc
4674 void multi_create_do_netstuff()
4678 // if not on a standalone
4679 void multi_create_init_as_server()
4681 // set me up as the host and master
4682 Net_player->flags |= (NETINFO_FLAG_AM_MASTER | NETINFO_FLAG_GAME_HOST);
4685 // if on a standalone
4686 void multi_create_init_as_client()
4688 Net_player->flags |= NETINFO_FLAG_GAME_HOST;
4691 // scroll up through the player list
4692 void multi_create_plist_scroll_up()
4694 gamesnd_play_iface(SND_GENERAL_FAIL);
4697 // scroll down through the player list
4698 void multi_create_plist_scroll_down()
4700 gamesnd_play_iface(SND_GENERAL_FAIL);
4703 void multi_create_plist_process()
4705 int test_count,idx,player_index;
4707 // first determine if there are 0 players in the game. This should never happen since the host is _always_ in the game
4709 for(idx=0;idx<MAX_PLAYERS;idx++){
4710 // count anyone except the standalone server (if applicable)
4711 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
4715 if(test_count <= 0){
4719 // if we had a selected item but that player has left, select myself instead
4720 if(Multi_create_plist_select_flag){
4721 player_index = find_player_id(Multi_create_plist_select_id);
4722 if(player_index == -1){
4723 Multi_create_plist_select_id = Net_player->player_id;
4726 Multi_create_plist_select_flag = 1;
4727 Multi_create_plist_select_id = Net_player->player_id;
4730 // if the player has clicked somewhere in the player list area
4731 if(Multi_create_player_select_button.pressed()){
4734 // get the player index and address of the player item the mouse is currently over
4735 player_id = multi_create_get_mouse_id();
4736 player_index = find_player_id(player_id);
4737 if(player_index != -1){
4738 Multi_create_plist_select_flag = 1;
4739 Multi_create_plist_select_id = player_id;
4744 void multi_create_plist_blit_normal()
4747 char str[CALLSIGN_LEN+5];
4748 int y_start = Mc_players_coords[gr_screen.res][MC_Y_COORD];
4751 // display all the players
4752 for(idx=0;idx<MAX_PLAYERS;idx++){
4753 // count anyone except the standalone server (if applicable)
4754 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
4758 // highlight him if he's the host
4759 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4760 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4761 gr_set_color_fast(&Color_text_active_hi);
4763 gr_set_color_fast(&Color_bright);
4766 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4767 gr_set_color_fast(&Color_text_active);
4769 gr_set_color_fast(&Color_text_normal);
4773 // optionally draw his CD status
4774 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4775 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4776 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4778 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4781 // make sure the string will fit, then display it
4782 strcpy(str,Net_players[idx].player->callsign);
4783 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4784 strcat(str,XSTR("(O)",787)); // [[ Observer ]]
4786 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4787 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4794 void multi_create_plist_blit_team()
4797 char str[CALLSIGN_LEN+1];
4798 int y_start = Mc_players_coords[gr_screen.res][MC_Y_COORD];
4801 // display all the red players first
4802 for(idx=0;idx<MAX_PLAYERS;idx++){
4803 // count anyone except the standalone server (if applicable)
4804 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
4805 // reset total offset
4808 // highlight him if he's the host
4809 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4810 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4811 gr_set_color_fast(&Color_text_active_hi);
4813 // be sure to blit the correct team button
4814 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.draw_forced(2);
4816 gr_set_color_fast(&Color_bright);
4819 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4820 gr_set_color_fast(&Color_text_active);
4822 // be sure to blit the correct team button
4823 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.draw_forced(2);
4825 gr_set_color_fast(&Color_text_normal);
4829 // optionally draw his CD status
4830 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4831 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4832 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4834 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4837 // blit the red team indicator
4838 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
4839 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
4840 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4841 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4843 total_offset += Multi_common_icon_dims[MICON_TEAM0_SELECT][0] + 1;
4846 if(Multi_common_icons[MICON_TEAM0] != -1){
4847 gr_set_bitmap(Multi_common_icons[MICON_TEAM0], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4848 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4850 total_offset += Multi_common_icon_dims[MICON_TEAM0][0] + 1;
4854 // make sure the string will fit
4855 strcpy(str,Net_players[idx].player->callsign);
4856 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4857 strcat(str,XSTR("(O)",787));
4859 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4861 // display him in the correct half of the list depending on his team
4862 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4867 // display all the green players next
4868 for(idx=0;idx<MAX_PLAYERS;idx++){
4869 // count anyone except the standalone server (if applicable)
4870 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
4871 // reset total offset
4874 // highlight him if he's the host
4875 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4876 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4877 gr_set_color_fast(&Color_text_active_hi);
4879 // be sure to blit the correct team button
4880 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.draw_forced(2);
4882 gr_set_color_fast(&Color_bright);
4885 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4886 gr_set_color_fast(&Color_text_active);
4888 // be sure to blit the correct team button
4889 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.draw_forced(2);
4891 gr_set_color_fast(&Color_text_normal);
4895 // optionally draw his CD status
4896 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4897 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4898 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4900 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4903 // blit the red team indicator
4904 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
4905 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
4906 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4907 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4909 total_offset += Multi_common_icon_dims[MICON_TEAM1_SELECT][0] + 1;
4912 if(Multi_common_icons[MICON_TEAM1] != -1){
4913 gr_set_bitmap(Multi_common_icons[MICON_TEAM1], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4914 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4916 total_offset += Multi_common_icon_dims[MICON_TEAM1][0] + 1;
4920 // make sure the string will fit
4921 strcpy(str,Net_players[idx].player->callsign);
4922 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4923 strcat(str,XSTR("(O)",787));
4925 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4927 // display him in the correct half of the list depending on his team
4928 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4934 void multi_create_list_scroll_up()
4936 if(Multi_create_list_start > 0){
4937 Multi_create_list_start--;
4939 gamesnd_play_iface(SND_SCROLL);
4941 gamesnd_play_iface(SND_GENERAL_FAIL);
4945 void multi_create_list_scroll_down()
4947 if((Multi_create_list_count - Multi_create_list_start) > Multi_create_list_max_display[gr_screen.res]){
4948 Multi_create_list_start++;
4950 gamesnd_play_iface(SND_SCROLL);
4952 gamesnd_play_iface(SND_GENERAL_FAIL);
4956 // gets a list of multiplayer misisons
4957 void multi_create_list_load_missions()
4959 char *fname, mission_name[NAME_LENGTH+1];
4960 char wild_card[256];
4963 memset(wild_card, 0, 256);
4964 strcpy(wild_card, NOX("*"));
4965 strcat(wild_card, 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 strcpy(mcip->filename, filename );
5012 strcpy(mcip->name, mission_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;
5043 char wild_card[256];
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 memset(wild_card, 0, 256);
5053 strcpy(wild_card, NOX("*"));
5054 strcat(wild_card, FS_CAMPAIGN_FILE_EXT);
5055 file_count = cf_get_file_list_preallocated(MULTI_CREATE_MAX_LIST_ITEMS, Multi_create_files_array, NULL, CF_TYPE_MISSIONS, wild_card);
5056 for(idx = 0; idx < file_count; idx++){
5058 char *filename, name[NAME_LENGTH];
5060 fname = Multi_create_files_array[idx];
5062 // tack on any necessary file extension
5063 filename = cf_add_ext( fname, FS_CAMPAIGN_FILE_EXT );
5065 // for multiplayer beta builds, only accept builtin missions
5066 #if defined(MULTIPLAYER_BETA_BUILD) || defined(FS2_DEMO)
5067 if(game_find_builtin_mission(filename) == NULL){
5070 #elif defined(PD_BUILD)
5071 if((game_find_builtin_mission(filename) == NULL) && !strstr(filename, "peterdrake")){
5076 if(Game_mode & GM_STANDALONE_SERVER){
5077 std_gen_set_text(filename, 2);
5080 // if the campaign is a multiplayer campaign, then add the data to the campaign list items
5081 flags = mission_campaign_parse_is_multi( filename, name );
5082 if( flags != CAMPAIGN_TYPE_SINGLE && mission_campaign_get_info(filename,title,&campaign_type,&max_players)) {
5083 if ( Multi_create_campaign_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
5084 multi_create_info *mcip;
5086 mcip = &Multi_create_campaign_list[Multi_create_campaign_count];
5087 strcpy(mcip->filename, filename );
5088 strcpy(mcip->name, name );
5090 // setup various flags
5091 if ( flags == CAMPAIGN_TYPE_MULTI_COOP ){
5092 mcip->flags = MISSION_TYPE_MULTI_COOP | MISSION_TYPE_MULTI;
5093 } else if ( flags == CAMPAIGN_TYPE_MULTI_TEAMS ) {
5094 mcip->flags = MISSION_TYPE_MULTI_TEAMS | MISSION_TYPE_MULTI;
5096 Int3(); // bogus campaign multi type -- find allender
5099 // 0 respawns for campaign files (should be contained within the mission files themselves)
5102 // 0 max players for campaign files
5103 mcip->max_players = (unsigned char)max_players;
5105 // get any additional information for possibly builtin missions
5106 fs_builtin_mission *fb = game_find_builtin_mission(filename);
5110 Multi_create_campaign_count++;
5115 // maybe create a standalone dialog
5116 if(Game_mode & GM_STANDALONE_SERVER){
5117 std_destroy_gen_dialog();
5121 void multi_create_list_do()
5124 int start_index,stop_index;
5125 char selected_name[255];
5127 // bail early if there aren't any selectable items
5128 if(Multi_create_list_count == 0){
5132 // first check to see if the user has clicked on an item
5133 if(Multi_create_list_select_button.pressed()){
5135 Multi_create_list_select_button.get_mouse_pos(NULL,&y);
5138 // make sure we are selectedin valid indices
5139 if((item < Multi_create_list_max_display[gr_screen.res]) && (item >= 0)){
5140 item += Multi_create_list_start;
5142 if(item < Multi_create_list_count){
5143 multi_create_list_select_item(item);
5144 gamesnd_play_iface(SND_IFACE_MOUSE_CLICK);
5149 // bail early if we don't have a start position
5150 if(Multi_create_list_start == -1){
5154 // display the list of individual campaigns/missions
5156 int y_start = Mc_list_coords[gr_screen.res][MC_Y_COORD];
5158 start_index = multi_create_select_to_index(Multi_create_list_start);
5159 stop_index = Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ? Multi_create_mission_count : Multi_create_campaign_count;
5160 for(idx=start_index; idx<stop_index; idx++){
5161 // see if we should drop out
5162 if(count == Multi_create_list_max_display[gr_screen.res]){
5166 // see if we should filter out this mission
5167 if ( !(Multi_create_file_list[idx].flags & Multi_create_filter) ){
5171 // highlight the selected item
5172 multi_create_select_to_filename(Multi_create_list_select,selected_name);
5173 if(!strcmp(selected_name,Multi_create_file_list[idx].filename)){
5174 gr_set_color_fast(&Color_text_selected);
5176 gr_set_color_fast(&Color_text_normal);
5179 // draw the type icon
5180 multi_create_list_blit_icons(idx, y_start);
5182 // force fit the mission name string
5183 strcpy(selected_name,Multi_create_file_list[idx].name);
5184 gr_force_fit_string(selected_name,255,Mc_column1_w[gr_screen.res]);
5185 gr_string(Mc_mission_name_x[gr_screen.res],y_start,selected_name);
5187 // draw the max players if in mission mode
5188 sprintf(selected_name,"%d",(int)Multi_create_file_list[idx].max_players);
5189 gr_string(Mc_mission_count_x[gr_screen.res],y_start,selected_name);
5191 // force fit the mission filename string
5192 strcpy(selected_name,Multi_create_file_list[idx].filename);
5193 gr_force_fit_string(selected_name,255,Mc_column3_w[gr_screen.res]);
5194 gr_string(Mc_mission_fname_x[gr_screen.res],y_start,selected_name);
5201 // takes care of stuff like changing indices around and setting up the netgame structure
5202 void multi_create_list_select_item(int n)
5204 int abs_index,campaign_type,max_players;
5205 char title[NAME_LENGTH+1];
5206 netgame_info ng_temp;
5209 char *campaign_desc;
5211 // if not on the standalone server
5212 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5215 // on the standalone
5217 memset(&ng_temp,0,sizeof(netgame_info));
5221 if ( n != Multi_create_list_select ) {
5222 // check to see if this is a valid index, and bail if it is not
5223 abs_index = multi_create_select_to_index(n);
5224 if(abs_index == -1){
5228 Multi_create_list_select = n;
5230 // set the mission name
5231 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5232 multi_create_select_to_filename(n,ng->mission_name);
5234 multi_create_select_to_filename(n,ng->campaign_name);
5237 // make sure the netgame type is properly set
5238 int old_type = Netgame.type_flags;
5239 abs_index = multi_create_select_to_index(n);
5240 if(abs_index != -1){
5241 if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_TEAMS){
5242 // if we're in squad war mode, leave it as squad war
5243 if(old_type & NG_TYPE_SW){
5244 ng->type_flags = NG_TYPE_SW;
5246 ng->type_flags = NG_TYPE_TVT;
5248 } else if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_COOP){
5249 ng->type_flags = NG_TYPE_COOP;
5250 } else if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_DOGFIGHT){
5251 ng->type_flags = NG_TYPE_DOGFIGHT;
5255 // if we're no longer in a TvT game, just uncheck the squadwar checkbox
5256 if(!(ng->type_flags & NG_TYPE_TEAM)){
5257 Multi_create_sw_checkbox.set_state(0);
5260 // if we switched from something else to team vs. team mode, do some special processing
5261 if((ng->type_flags & NG_TYPE_TEAM) && (ng->type_flags != old_type) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
5265 switch(Multi_create_list_mode){
5266 case MULTI_CREATE_SHOW_MISSIONS:
5267 // don't forget to update the info box window thingie
5268 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5269 ship_init(); // mwa -- 10/15/97. Call this function to reset number of ships in mission
5270 ng->max_players = mission_parse_get_multi_mission_info( ng->mission_name );
5272 SDL_assert(ng->max_players > 0);
5273 strcpy(ng->title,The_mission.name);
5275 // set the information area text
5276 multi_common_set_text(The_mission.mission_desc);
5278 // if we're on the standalone, send a request for the description
5280 send_netgame_descript_packet(&Netgame.server_addr,0);
5281 multi_common_set_text("");
5284 // set the respawns as appropriate
5285 if(Netgame.options.respawn <= Multi_create_file_list[abs_index].respawn){
5286 ng->respawn = Netgame.options.respawn;
5287 nprintf(("Network","Using netgame options for respawn count (%d %d)\n",Netgame.options.respawn,Multi_create_file_list[abs_index].respawn));
5289 ng->respawn = Multi_create_file_list[abs_index].respawn;
5290 nprintf(("Network","Using mission settings for respawn count (%d %d)\n",Netgame.options.respawn,Multi_create_file_list[abs_index].respawn));
5293 case MULTI_CREATE_SHOW_CAMPAIGNS:
5294 // if not on the standalone server
5295 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5296 // get the campaign info
5297 memset(title,0,NAME_LENGTH+1);
5298 if(!mission_campaign_get_info(ng->campaign_name,title,&campaign_type,&max_players, &campaign_desc)) {
5299 memset(ng->campaign_name,0,NAME_LENGTH+1);
5300 ng->max_players = 0;
5302 // if we successfully got the # of players
5304 memset(ng->title,0,NAME_LENGTH+1);
5305 strcpy(ng->title,title);
5306 ng->max_players = max_players;
5309 nprintf(("Network","MC MAX PLAYERS : %d\n",ng->max_players));
5311 // set the information area text
5312 // multi_common_set_text(ng->title);
5313 multi_common_set_text(campaign_desc);
5315 // if on the standalone server, send a request for the description
5317 // no descriptions currently kept for campaigns
5320 // netgame respawns are always 0 for campaigns (until the first mission is loaded)
5325 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5327 send_netgame_update_packet();
5329 // update all machines about stuff like respawns, etc.
5330 multi_options_update_netgame();
5332 multi_options_update_mission(ng, Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ? 1 : 0);
5337 void multi_create_list_blit_icons(int list_index, int y_start)
5339 multi_create_info *mcip;
5340 fs_builtin_mission *fb;
5343 // get a pointer to the list item
5344 max_index = Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ? Multi_create_mission_count - 1 : Multi_create_campaign_count - 1;
5345 if((list_index < 0) || (list_index > max_index)){
5348 mcip = &Multi_create_file_list[list_index];
5350 // blit the multiplayer type icons
5351 if(mcip->flags & MISSION_TYPE_MULTI_COOP){
5352 if(Multi_common_icons[MICON_COOP] >= 0){
5353 gr_set_bitmap(Multi_common_icons[MICON_COOP], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5354 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5356 } else if(mcip->flags & MISSION_TYPE_MULTI_TEAMS){
5357 if(Multi_common_icons[MICON_TVT] >= 0){
5358 gr_set_bitmap(Multi_common_icons[MICON_TVT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5359 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5361 } else if(mcip->flags & MISSION_TYPE_MULTI_DOGFIGHT){
5362 if(Multi_common_icons[MICON_DOGFIGHT] >= 0){
5363 gr_set_bitmap(Multi_common_icons[MICON_DOGFIGHT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5364 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5368 // if its a valid mission, blit the valid mission icon
5369 if(MULTI_IS_TRACKER_GAME && (mcip->valid_status == MVALID_STATUS_VALID)){
5370 if(Multi_common_icons[MICON_VALID] >= 0){
5371 gr_set_bitmap(Multi_common_icons[MICON_VALID], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5372 gr_bitmap(Mc_icon_valid_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_valid_coords[gr_screen.res][MC_Y_COORD]);
5376 // now see if its a builtin mission
5377 fb = game_find_builtin_mission(mcip->filename);
5378 // if the mission is from volition, blit the volition icon
5379 if((fb != NULL) && (fb->flags & FSB_FROM_VOLITION)){
5380 if(Multi_common_icons[MICON_VOLITION] >= 0){
5381 gr_set_bitmap(Multi_common_icons[MICON_VOLITION], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5382 gr_bitmap(Mc_icon_volition_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_volition_coords[gr_screen.res][MC_Y_COORD]);
5387 // check for mdisk mission
5388 fb = game_find_builtin_mission(mcip->filename);
5389 if((fb != NULL) && (fb->flags & FSB_FROM_MDISK)){
5390 if(Multi_common_icons[MICON_MDISK] >= 0){
5391 gr_set_bitmap(Multi_common_icons[MICON_MDISK], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5392 gr_bitmap(Mc_icon_silent_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_silent_coords[gr_screen.res][MC_Y_COORD]);
5398 void multi_create_accept_hit()
5400 char selected_name[255];
5401 int start_campaign = 0;
5403 // make sure all players have finished joining
5404 if(!multi_netplayer_state_check(NETPLAYER_STATE_JOINED,1)){
5405 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("Please wait until all clients have finished joining",788));
5406 gamesnd_play_iface(SND_GENERAL_FAIL);
5409 gamesnd_play_iface(SND_COMMIT_PRESSED);
5412 // do single mission stuff
5413 switch(Multi_create_list_mode){
5414 case MULTI_CREATE_SHOW_MISSIONS:
5415 if(Multi_create_list_select != -1){
5416 // set the netgame mode
5417 Netgame.campaign_mode = MP_SINGLE;
5419 // setup various filenames and mission names
5420 multi_create_select_to_filename(Multi_create_list_select,selected_name);
5421 strncpy( Game_current_mission_filename, selected_name, MAX_FILENAME_LEN );
5422 strncpy(Netgame.mission_name,selected_name,MAX_FILENAME_LEN);
5425 ml_printf(NOX("Starting single mission %s, with %d players"), Game_current_mission_filename, multi_num_players());
5427 multi_common_add_notify(XSTR("No mission selected!",789));
5432 case MULTI_CREATE_SHOW_CAMPAIGNS:
5433 // do campaign related stuff
5434 if(Multi_create_list_select != -1){
5435 // set the netgame mode
5436 Netgame.campaign_mode = MP_CAMPAIGN;
5438 // start a campaign instead of a single mission
5439 multi_create_select_to_filename(Multi_create_list_select,selected_name);
5440 multi_campaign_start(selected_name);
5444 ml_printf(NOX("Starting campaign %s, with %d players"), selected_name, multi_num_players());
5446 multi_common_add_notify(XSTR("No campaign selected!",790));
5452 // if this is a team vs team situation, lock the players send a final team update
5453 if((Netgame.type_flags & NG_TYPE_TEAM) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
5454 multi_team_host_lock_all();
5455 multi_team_send_update();
5458 // if not on the standalone, move to the mission sync state which will take care of everything
5459 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5460 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
5461 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
5463 // otherwise tell the standalone to do so
5465 // when the standalone receives this, he'll do the mission syncing himself
5466 send_mission_sync_packet(MULTI_SYNC_PRE_BRIEFING,start_campaign);
5470 void multi_create_draw_filter_buttons()
5472 // highlight the correct filter button
5473 if ( Multi_create_filter == MISSION_TYPE_MULTI ){
5474 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL].button.draw_forced(2);
5475 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_COOP ) {
5476 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 1].button.draw_forced(2);
5477 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_TEAMS ) {
5478 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 2].button.draw_forced(2);
5479 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_DOGFIGHT ){
5480 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 3].button.draw_forced(2);
5486 void multi_create_set_selected_team(int team)
5490 // if we don't currently have a player selected, don't do anything
5491 if(!Multi_create_plist_select_flag){
5492 gamesnd_play_iface(SND_GENERAL_FAIL);
5495 gamesnd_play_iface(SND_USER_SELECT);
5497 // otherwise attempt to set the team for this guy
5498 player_index = find_player_id(Multi_create_plist_select_id);
5499 if(player_index != -1){
5500 multi_team_set_team(&Net_players[player_index],team);
5504 void multi_create_handle_join(net_player *pl)
5506 // for now just play a bloop sound
5507 gamesnd_play_iface(SND_ICON_DROP_ON_WING);
5510 // fill in net address of player the mouse is over, return player index (or -1 if none)
5511 short multi_create_get_mouse_id()
5513 // determine where he clicked (y pixel value)
5515 Multi_create_player_select_button.get_mouse_pos(NULL,&y);
5517 // select things a little differently if we're in team vs. team or non-team vs. team mode
5519 if(Netgame.type_flags & NG_TYPE_TEAM){
5520 int player_index = -1;
5522 // look through all of team red first
5523 for(idx=0;idx<MAX_PLAYERS;idx++){
5524 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
5527 // if this is the _nth_ guy
5535 // if we still haven't found him yet, look through the green team
5536 if(player_index == -1){
5537 for(idx=0;idx<MAX_PLAYERS;idx++){
5538 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
5540 // if this is the _nth_ guy
5549 if(player_index != -1){
5550 return Net_players[player_index].player_id;
5553 // select the nth active player if possible, disregarding the standalone server
5554 for(idx=0;idx<MAX_PLAYERS;idx++){
5555 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
5558 // if this is the _nth_ guy
5560 return Net_players[idx].player_id;
5569 void multi_create_select_to_filename(int select_index,char *filename)
5573 // look through the mission list
5574 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5575 for(idx=0;idx<Multi_create_mission_count;idx++){
5576 if(Multi_create_file_list[idx].flags & Multi_create_filter){
5580 // if we found the item
5581 if(select_index < 0){
5582 strcpy(filename,Multi_create_file_list[idx].filename);
5587 // look through the campaign list
5588 else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
5589 for(idx=0;idx<Multi_create_campaign_count;idx++){
5592 // if we found the item
5593 if(select_index < 0){
5594 strcpy(filename,Multi_create_file_list[idx].filename);
5600 strcpy(filename,"");
5603 int multi_create_select_to_index(int select_index)
5606 int lookup_index = 0;
5608 // look through the mission list
5609 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5610 for(idx=0;idx<Multi_create_mission_count;idx++){
5611 if(Multi_create_file_list[idx].flags & Multi_create_filter){
5615 // if we found the item
5616 if(select_index < lookup_index){
5621 // look through the campaign list
5622 else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
5623 for(idx=0;idx<Multi_create_campaign_count;idx++){
5626 // if we found the item
5627 if(select_index < 0){
5636 int multi_create_ok_to_commit()
5638 int player_count, observer_count, idx;
5639 int notify_of_hacked_ships_tbl = 0;
5640 int notify_of_hacked_weapons_tbl = 0;
5641 char err_string[255];
5645 // make sure we have a valid mission selected
5646 if(Multi_create_list_select < 0){
5650 // if this is not a valid mission, let the player know
5651 abs_index = multi_create_select_to_index(Multi_create_list_select);
5656 // if we're playing with a hacked ships.tbl (on PXO)
5657 notify_of_hacked_ships_tbl = 0;
5658 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5659 if(!Game_ships_tbl_valid){
5660 notify_of_hacked_ships_tbl = 1;
5663 if(Netgame.flags & NG_FLAG_HACKED_SHIPS_TBL){
5664 notify_of_hacked_ships_tbl = 1;
5667 if(!MULTI_IS_TRACKER_GAME){
5668 notify_of_hacked_ships_tbl = 0;
5670 if(notify_of_hacked_ships_tbl){
5671 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){
5676 // if we're playing with a hacked weapons.tbl (on PXO)
5677 notify_of_hacked_weapons_tbl = 0;
5678 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5679 if(!Game_weapons_tbl_valid){
5680 notify_of_hacked_weapons_tbl = 1;
5683 if(Netgame.flags & NG_FLAG_HACKED_WEAPONS_TBL){
5684 notify_of_hacked_weapons_tbl = 1;
5687 if(!MULTI_IS_TRACKER_GAME){
5688 notify_of_hacked_weapons_tbl = 0;
5690 if(notify_of_hacked_weapons_tbl){
5691 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){
5696 // if any of the players have hacked data
5698 for(idx=0; idx<MAX_PLAYERS; idx++){
5699 // look for hacked players
5700 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAXOR)){
5704 // message everyone - haha
5705 if(Net_players[idx].player != NULL){
5706 sprintf(err_string, "%s %s", Net_players[idx].player->callsign, XSTR("has hacked tables/data", 1271));
5708 sprintf(err_string, "somebody %s", XSTR("has hacked tables/data", 1271));
5710 send_game_chat_packet(Net_player, err_string, MULTI_MSG_ALL, NULL, NULL, 1);
5713 // if we found a hacked set of data
5716 if(MULTI_IS_TRACKER_GAME){
5717 // don't allow squad war matches to continue
5718 if(Netgame.type_flags & NG_TYPE_SW){
5720 // if this is squad war, don't allow it to continue
5721 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));
5726 // otherwise, warn the players that stats will not saved
5728 // if this is squad war, don't allow it to continue
5729 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){
5734 // non-pxo, just give a notice
5736 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){
5742 // check to see that we don't have too many observers
5743 observer_count = multi_num_observers();
5744 if(observer_count > Netgame.options.max_observers){
5745 // print up the error string
5746 sprintf(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);
5748 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, err_string);
5752 // check to see that we have a valid # of players for the the # of ships in the game
5753 player_count = multi_num_players();
5754 if(player_count > Netgame.max_players){
5755 // print up the error string
5756 sprintf(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);
5758 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, err_string);
5762 // check to see if teams are assigned properly in a team vs. team situation
5763 if(Netgame.type_flags & NG_TYPE_TEAM){
5764 if(!multi_team_ok_to_commit()){
5765 gamesnd_play_iface(SND_GENERAL_FAIL);
5766 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("Teams and/or team captains are not assigned properly", 793));
5772 if(!multi_create_verify_cds()){
5773 gamesnd_play_iface(SND_GENERAL_FAIL);
5775 #ifdef MULTIPLAYER_BETA_BUILD
5776 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, "You need 1 CD for every player!");
5778 #ifdef DVD_MESSAGE_HACK
5779 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You need 1 DVD for every 4 players!", 794));
5781 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You need 1 CD for every 4 players!", 794));
5787 // if we're playing on the tracker
5788 if(MULTI_IS_TRACKER_GAME){
5789 #ifdef PXO_CHECK_VALID_MISSIONS
5790 if((Multi_create_file_list == Multi_create_mission_list) && (Multi_create_file_list[abs_index].valid_status != MVALID_STATUS_VALID)){
5791 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){
5798 if(!(Netgame.type_flags & NG_TYPE_SW)){
5799 // if he is playing by himself, tell him stats will not be accepted
5800 if(multi_num_players() == 1){
5801 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){
5814 int multi_create_verify_cds()
5816 int player_count = multi_num_players();
5820 // count how many cds we have
5822 for(idx=0;idx<MAX_PLAYERS;idx++){
5823 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAS_CD)){
5828 // for the beta, everyone must have a CD
5829 #ifdef MULTIPLAYER_BETA_BUILD
5830 if(multi_cd_count < player_count){
5834 // determine if we have enough
5835 float ratio = (float)player_count / (float)multi_cd_count;
5836 // greater than a 4 to 1 ratio
5842 // we meet the conditions
5846 // returns an index into Multi_create_mission_list
5847 int multi_create_lookup_mission(char *fname)
5851 for(idx=0; idx<Multi_create_mission_count; idx++){
5852 if(!SDL_strcasecmp(fname, Multi_create_mission_list[idx].filename)){
5857 // couldn't find the mission
5861 // returns an index into Multi_create_campaign_list
5862 int multi_create_lookup_campaign(char *fname)
5866 for(idx=0; idx<Multi_create_campaign_count; idx++){
5867 if(!SDL_strcasecmp(fname, Multi_create_campaign_list[idx].filename)){
5872 // couldn't find the campaign
5876 void multi_create_refresh_pxo()
5878 // delete mvalid.cfg if it exists
5879 cf_delete(MULTI_VALID_MISSION_FILE, CF_TYPE_DATA);
5881 // refresh missions from the tracker
5882 multi_update_valid_missions();
5885 void multi_create_sw_clicked()
5887 netgame_info ng_temp;
5890 int file_index = multi_create_select_to_index(Multi_create_list_select);
5892 // either a temporary netgame or the real one
5893 if(MULTIPLAYER_MASTER){
5900 // maybe switch squad war off
5901 if(!Multi_create_sw_checkbox.checked()){
5902 // if the mission selected is a coop mission, go back to coop mode
5903 SDL_assert(file_index != -1);
5904 if(file_index == -1){
5905 ng->type_flags = NG_TYPE_COOP;
5907 if(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_TEAMS){
5908 ng->type_flags = NG_TYPE_TVT;
5909 } else if(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_DOGFIGHT){
5910 ng->type_flags = NG_TYPE_DOGFIGHT;
5912 ng->type_flags = NG_TYPE_COOP;
5915 // switch squad war on
5917 SDL_assert(file_index != -1);
5918 if((file_index == -1) || !(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_TEAMS)){
5919 Multi_create_sw_checkbox.set_state(0);
5921 // at this point we know its safe to switch squad war on
5922 ng->type_flags = NG_TYPE_SW;
5927 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5929 send_netgame_update_packet();
5931 // update all machines about stuff like respawns, etc.
5932 multi_options_update_netgame();
5934 // on the standalone
5936 // standalone will take care of polling the usertracker
5937 multi_options_update_mission(ng, Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ? 1 : 0);
5942 // -------------------------------------------------------------------------------------------------------------
5944 // MULTIPLAYER HOST OPTIONS SCREEN
5947 #define MULTI_HO_NUM_BUTTONS 12
5948 #define MULTI_HO_NUM_RADIO_BUTTONS 10
5952 #define MULTI_HO_PALETTE "InterfacePalette"
5954 static const char *Multi_ho_bitmap_fname[GR_NUM_RESOLUTIONS] = {
5955 "MultiHost", // GR_640
5956 "2_MultiHost" // GR_1024
5959 static const char *Multi_ho_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
5960 "MultiHost-M", // GR_640
5961 "2_MultiHost-M" // GR_1024
5965 UI_WINDOW Multi_ho_window; // the window object for the join screen
5966 UI_INPUTBOX Multi_ho_respawns; // the # of respawns allowed in the game
5967 UI_INPUTBOX Multi_ho_time_limit; // mission time limit
5968 UI_INPUTBOX Multi_ho_voice_wait; // wait time between tokens
5969 UI_INPUTBOX Multi_ho_kill_limit; // kill limit in a furball mission
5970 UI_INPUTBOX Multi_ho_obs; // # of observers we'll allow
5971 int Multi_ho_bitmap; // the background bitmap
5973 // constants for coordinate lookup
5974 #define MULTI_HO_X_COORD 0
5975 #define MULTI_HO_Y_COORD 1
5976 #define MULTI_HO_W_COORD 2
5977 #define MULTI_HO_H_COORD 3
5978 #define MULTI_HO_TEXT_X_COORD 4
5979 #define MULTI_HO_TEXT_Y_COORD 5
5982 #define MULTI_HO_MSG_RANK 0 // highest ranking players can do messaging
5983 #define MULTI_HO_MSG_LEADER 1 // wing/team leaders can do messaging
5984 #define MULTI_HO_MSG_ANY 2 // any player can do messaging
5985 #define MULTI_HO_MSG_HOST 3 // only the host can do messaging
5986 #define MULTI_HO_END_RANK 4 // highest rank can and host can end mission
5987 #define MULTI_HO_END_LEADER 5 // wing/team leaders and host can end the mission
5988 #define MULTI_HO_END_ANY 6 // any player can end the mission
5989 #define MULTI_HO_END_HOST 7 // only host can end the mission
5990 #define MULTI_HO_VOICE_ON 8 // voice toggled on
5991 #define MULTI_HO_VOICE_OFF 9 // voice toggled off
5992 #define MULTI_HO_HOST_MODIFIES 10 // only the host or team captains can modify ships/weapons in briefing
5993 #define MULTI_HO_ACCEPT 11 // accept button
5995 ui_button_info Multi_ho_buttons[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_BUTTONS] = {
5998 // who is allowed to message
5999 ui_button_info("MH_00", 13, 157, -1, -1, 0), // highest rank
6000 ui_button_info("MH_01", 13, 179, -1, -1, 1), // team/wing leader
6001 ui_button_info("MH_02", 13, 200, -1, -1, 2), // any
6002 ui_button_info("MH_03", 13, 223, -1, -1, 3), // host
6004 // who is allowed to end the mission
6005 ui_button_info("MH_09", 13, 273, -1, -1, 9), // highest rank
6006 ui_button_info("MH_10", 13, 295, -1, -1, 10), // team/wing leader
6007 ui_button_info("MH_11", 13, 317, -1, -1, 11), // any
6008 ui_button_info("MH_12", 13, 339, -1, -1, 12), // host
6010 // voice on/off button
6011 ui_button_info("MH_14", 396, 156, -1, -1, 14),
6012 ui_button_info("MH_15", 453, 156, -1, -1, 15),
6014 // host modifies ships
6015 ui_button_info("MH_19", 215, 410, -1, -1, 19),
6018 ui_button_info("MH_18", 560, 411, -1, -1, 18),
6020 // who is allowed to message
6021 ui_button_info("MH_00", 3, 160, 46, 166, 0), // highest rank
6022 ui_button_info("MH_01", 3, 179, 46, 185, 1), // team/wing leader
6023 ui_button_info("MH_02", 3, 196, 46, 203, 2), // any
6024 ui_button_info("MH_03", 3, 214, 46, 220, 3), // host
6026 // who is allowed to end the mission
6027 ui_button_info("MH_04", 3, 257, 46, 265, 4), // highest rank
6028 ui_button_info("MH_05", 3, 276, 46, 283, 5), // team/wing leader
6029 ui_button_info("MH_06", 3, 294, 46, 300, 6), // any
6030 ui_button_info("MH_07", 3, 311, 46, 317, 7), // host
6032 // voice on/off button
6033 ui_button_info("MH_09", 542, 158, 545, 185, 9),
6034 ui_button_info("MH_10", 598, 158, 604, 185, 10),
6036 // host modifies ships
6037 ui_button_info("MH_13", 542, 377, 437, 363, 13),
6040 ui_button_info("MH_14", 572, 428, 580, 414, 14),
6044 // who is allowed to message
6045 ui_button_info("2_MH_00", 5, 256, 73, 269, 0), // highest rank
6046 ui_button_info("2_MH_01", 5, 286, 73, 297, 1), // team/wing leader
6047 ui_button_info("2_MH_02", 5, 314, 73, 325, 2), // any
6048 ui_button_info("2_MH_03", 5, 341, 73, 352, 3), // host
6050 // who is allowed to end the mission
6051 ui_button_info("2_MH_04", 5, 412, 73, 425, 4), // highest rank
6052 ui_button_info("2_MH_05", 5, 442, 73, 452, 5), // team/wing leader
6053 ui_button_info("2_MH_06", 5, 470, 73, 480, 6), // any
6054 ui_button_info("2_MH_07", 5, 497, 73, 508, 7), // host
6056 // voice on/off button
6057 ui_button_info("2_MH_09", 867, 253, 872, 296, 9),
6058 ui_button_info("2_MH_10", 957, 253, 966, 296, 10),
6060 // host modifies ships
6061 ui_button_info("2_MH_13", 867, 603, 784, 581, 13),
6064 ui_button_info("2_MH_14", 916, 685, 925, 665, 14),
6067 UI_XSTR Multi_ho_text[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_BUTTONS] = {
6069 // not needed for FS1
6071 {"Highest rank", 1280, 46, 166, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_RANK].button},
6072 {"Team / wing-leader", 1281, 46, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_LEADER].button},
6073 {"Any", 1282, 46, 203, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_ANY].button},
6074 {"Host", 1283, 46, 220, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_HOST].button},
6075 {"Highest rank", 1280, 46, 265, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_RANK].button},
6076 {"Team / wing-leader", 1281, 46, 283, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_LEADER].button},
6077 {"Any", 1282, 46, 300, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_ANY].button},
6078 {"Host", 1283, 46, 317, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_HOST].button},
6079 {"On", 1285, 545, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_VOICE_ON].button},
6080 {"Off", 1286, 604, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_VOICE_OFF].button},
6081 {"Host modifies ships", 1287, 437, 363, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_HOST_MODIFIES].button},
6082 {"Exit", 1417, 572, 418, UI_XSTR_COLOR_PINK, -1, &Multi_ho_buttons[0][MULTI_HO_ACCEPT].button},
6086 // not needed for FS1
6088 {"Highest rank", 1280, 62, 269, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_RANK].button},
6089 {"Team / wing-leader", 1281, 62, 297, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_LEADER].button},
6090 {"Any", 1282, 62, 325, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_ANY].button},
6091 {"Host", 1283, 62, 352, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_HOST].button},
6092 {"Highest rank", 1280, 62, 425, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_RANK].button},
6093 {"Team / wing-leader", 1281, 62, 452, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_LEADER].button},
6094 {"Any", 1282, 62, 480, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_ANY].button},
6095 {"Host", 1283, 62, 508, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_HOST].button},
6096 {"On", 1285, 877, 294, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_VOICE_ON].button},
6097 {"Off", 1286, 967, 293, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_VOICE_OFF].button},
6098 {"Host modifies ships", 1287, 869, 589, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_HOST_MODIFIES].button},
6099 {"Exit", 1417, 953, 672, UI_XSTR_COLOR_PINK, -1, &Multi_ho_buttons[1][MULTI_HO_ACCEPT].button},
6104 // radio button controls
6105 #define MULTI_HO_NUM_RADIO_GROUPS 3
6106 #define MULTI_HO_MSG_GROUP 0 // group dealing with squadmate messaging
6107 #define MULTI_HO_END_GROUP 1 // group dealing with ending the mission
6108 #define MULTI_HO_VOICE_GROUP 2 // group dealing with voice stuff
6109 int Multi_ho_radio_groups[MULTI_HO_NUM_RADIO_GROUPS] = { // currently selected button in the radio button group
6112 int Multi_ho_radio_info[MULTI_HO_NUM_RADIO_BUTTONS][3] = { // info related to each of the radio buttons themselves
6113 // { group #, value, button id# }
6114 {0, 0, 0}, // highest ranking players can do messaging
6115 {0, 1, 1}, // wing/team leaders can do messaging
6116 {0, 2, 2}, // any player can do messaging
6117 {0, 3, 3}, // only host can do messaging
6118 {1, 0, 4}, // highest rank and host can end the mission
6119 {1, 1, 5}, // team/wing leader can end the mission
6120 {1, 2, 6}, // any player can end the mission
6121 {1, 3, 7}, // only the host can end the mission
6122 {2, 0, 8}, // voice toggled on
6123 {2, 1, 9} // voice toggled off
6127 #define MULTI_HO_NUM_SLIDERS 3
6128 #define MULTI_HO_SLIDER_VOICE_QOS 0 // voice quality of sound
6129 #define MULTI_HO_SLIDER_VOICE_DUR 1 // max duration of voice recording
6130 #define MULTI_HO_SLIDER_SKILL 2 // skill level
6132 const char *filename;
6137 UI_DOT_SLIDER_NEW slider; // because we have a class inside this struct, we need the constructor below..
6139 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){}
6141 ho_sliders Multi_ho_sliders[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_SLIDERS] = {
6144 ho_sliders("MH_16", 396, 210, -1, -1, 16, 19, 10), // voice qos
6145 ho_sliders("MH_17", 396, 263, -1, -1, 17, 19, 10), // voice duration
6146 ho_sliders("MH_13", 10, 387, -1, -1, 13, 36, 5), // skill level
6148 ho_sliders("MH_11", 428, 214, 437, 199, 11, 19, 10), // voice qos
6149 ho_sliders("MH_12", 428, 261, 437, 246, 12, 19, 10), // voice duration
6150 ho_sliders("MH_08", 237, 454, 230, 411, 8, 36, 5), // skill level
6154 ho_sliders("2_MH_11", 684, 343, 690, 323, 11, 32, 10), // voice qos
6155 ho_sliders("2_MH_12", 685, 418, 837, 468, 12, 32, 10), // voice duration
6156 ho_sliders("2_MH_08", 379, 727, 369, 663, 8, 60, 5), // skill level
6160 int Multi_ho_mission_respawn;
6162 int Multi_ho_host_modifies;
6164 // whether or not any of the inputboxes on this screen had focus last frame
6165 int Multi_ho_lastframe_input = 0;
6167 // game information text areas
6171 #define MULTI_HO_NUM_TITLES 14
6173 UI_XSTR Multi_ho_titles[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_TITLES] = {
6175 { "AI Orders", 1289, 32, 144, UI_XSTR_COLOR_GREEN, -1, NULL },
6176 { "End Mission", 1290, 32, 242, UI_XSTR_COLOR_GREEN, -1, NULL },
6177 { "Time Limit", 1291, 32, 347, UI_XSTR_COLOR_GREEN, -1, NULL },
6178 { "Min", 1292, 74, 362, UI_XSTR_COLOR_GREEN, -1, NULL },
6179 { "Respawn Limit", 1288, 32, 378, UI_XSTR_COLOR_GREEN, -1, NULL },
6180 { "Kill Limit", 1293, 32, 409, UI_XSTR_COLOR_GREEN, -1, NULL },
6181 { "Observers", 1294, 32, 441, UI_XSTR_COLOR_GREEN, -1, NULL },
6182 { "Skill Level", 1284, 230, 411, UI_XSTR_COLOR_GREEN, -1, NULL },
6183 { "Voice Transmission", 1295, 437, 144, UI_XSTR_COLOR_GREEN, -1, NULL },
6184 { "Voice Quality", 1296, 437, 199, UI_XSTR_COLOR_GREEN, -1, NULL },
6185 { "Message Duration", 1297, 437, 246, UI_XSTR_COLOR_GREEN, -1, NULL },
6186 { "sec", 1522, 523, 292, UI_XSTR_COLOR_GREEN, -1, NULL },
6187 { "sec", 1523, 523, 332, UI_XSTR_COLOR_GREEN, -1, NULL },
6188 { "Voice Wait", 1298, 437, 313, UI_XSTR_COLOR_GREEN, -1, NULL },
6191 { "AI Orders", 1289, 48, 238, UI_XSTR_COLOR_GREEN, -1, NULL },
6192 { "End Mission", 1290, 48, 394, UI_XSTR_COLOR_GREEN, -1, NULL },
6193 { "Time Limit", 1291, 50, 568, UI_XSTR_COLOR_GREEN, -1, NULL },
6194 { "Min", 1292, 119, 581, UI_XSTR_COLOR_GREEN, -1, NULL },
6195 { "Respawn Limit", 1288, 50, 618, UI_XSTR_COLOR_GREEN, -1, NULL },
6196 { "Kill Limit", 1293, 50, 668, UI_XSTR_COLOR_GREEN, -1, NULL },
6197 { "Observers", 1294, 50, 718, UI_XSTR_COLOR_GREEN, -1, NULL },
6198 { "Skill Level", 1284, 398, 670, UI_XSTR_COLOR_GREEN, -1, NULL },
6199 { "Voice Transmission", 1295, 869, 239, UI_XSTR_COLOR_GREEN, -1, NULL },
6200 { "Voice Quality", 1296, 690, 331, UI_XSTR_COLOR_GREEN, -1, NULL },
6201 { "Message Duration", 1297, 690, 405, UI_XSTR_COLOR_GREEN, -1, NULL },
6202 { "sec", 1522, 837, 467, UI_XSTR_COLOR_GREEN, -1, NULL },
6203 { "sec", 1523, 837, 534, UI_XSTR_COLOR_GREEN, -1, NULL },
6204 { "Voice Wait", 1298, 742, 510, UI_XSTR_COLOR_GREEN, -1, NULL },
6209 // mission time limit input box
6210 int Ho_time_coords[GR_NUM_RESOLUTIONS][4] = {
6223 // furball kill limit input box
6224 int Ho_kill_coords[GR_NUM_RESOLUTIONS][4] = {
6237 // voice recording duration text display area
6238 int Ho_vd_coords[GR_NUM_RESOLUTIONS][4] = {
6251 // voice token wait input box
6252 int Ho_vw_coords[GR_NUM_RESOLUTIONS][6] = {
6265 // observer count input box
6266 int Ho_obs_coords[GR_NUM_RESOLUTIONS][4] = {
6279 // skill text description area
6280 int Ho_st_coords[GR_NUM_RESOLUTIONS][4] = {
6293 // respawn input box
6294 int Ho_rsp_coords[GR_NUM_RESOLUTIONS][6] = {
6307 // respawn max text area
6308 int Ho_max_rsp_coords[GR_NUM_RESOLUTIONS][2] = {
6321 // maximum values for various input boxes (to notify user of overruns)
6322 #define MULTI_HO_MAX_TIME_LIMIT 500
6323 #define MULTI_HO_MAX_TOKEN_WAIT 5
6324 #define MULTI_HO_MAX_KILL_LIMIT 9999
6325 #define MULTI_HO_MAX_OBS 4
6327 // LOCAL function definitions
6328 void multi_ho_check_buttons();
6329 void multi_ho_button_pressed(int n);
6330 void multi_ho_draw_radio_groups();
6331 void multi_ho_accept_hit();
6332 void multi_ho_get_options();
6333 void multi_ho_apply_options();
6334 void multi_ho_display_record_time();
6335 int multi_ho_check_values();
6336 void multi_ho_check_focus();
6337 void multi_ho_blit_max_respawns();
6338 void multi_ho_display_skill_level();
6340 void multi_host_options_init()
6344 // create the interface window
6345 Multi_ho_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
6346 Multi_ho_window.set_mask_bmap(Multi_ho_bitmap_mask_fname[gr_screen.res]);
6348 // load the background bitmap
6349 Multi_ho_bitmap = bm_load(Multi_ho_bitmap_fname[gr_screen.res]);
6350 if(Multi_ho_bitmap < 0){
6351 // we failed to load the bitmap - this is very bad
6355 // initialize the common notification messaging
6356 multi_common_notify_init();
6358 // use the common interface palette
6359 multi_common_set_palette();
6361 // create the interface buttons
6362 for(idx=0;idx<MULTI_HO_NUM_BUTTONS;idx++){
6363 // create the object
6364 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);
6366 // set the sound to play when highlighted
6367 Multi_ho_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
6369 // set the ani for the button
6370 Multi_ho_buttons[gr_screen.res][idx].button.set_bmaps(Multi_ho_buttons[gr_screen.res][idx].filename);
6372 // set the hotspot, ignoring the skill level button
6373 Multi_ho_buttons[gr_screen.res][idx].button.link_hotspot(Multi_ho_buttons[gr_screen.res][idx].hotspot);
6377 Multi_ho_window.add_XSTR(&Multi_ho_text[gr_screen.res][idx]);
6383 for(idx=0; idx<MULTI_HO_NUM_TITLES; idx++){
6384 Multi_ho_window.add_XSTR(&Multi_ho_titles[gr_screen.res][idx]);
6388 // create the interface sliders
6389 for(idx=0; idx<MULTI_HO_NUM_SLIDERS; idx++){
6390 // create the object
6391 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);
6394 // create the respawn count input box
6395 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);
6396 // if we're in campaign mode, disable it
6397 if(Netgame.campaign_mode == MP_CAMPAIGN){
6398 Multi_ho_respawns.set_text(XSTR("NA",795)); // [[ Not applicable ]]
6399 Multi_ho_respawns.disable();
6401 Multi_ho_respawns.set_valid_chars("0123456789");
6404 // create the time limit input box
6405 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);
6406 Multi_ho_time_limit.set_valid_chars("-0123456789");
6408 // create the voice token wait input box
6409 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);
6410 Multi_ho_voice_wait.set_valid_chars("01243456789");
6412 // create the furball kill limit input box
6413 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);
6414 Multi_ho_kill_limit.set_valid_chars("0123456789");
6416 // create the observer limit input box
6417 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);
6418 Multi_ho_obs.set_valid_chars("01234");
6420 // load in the current netgame defaults
6421 multi_ho_get_options();
6423 // whether or not any of the inputboxes on this screen had focus last frame
6424 Multi_ho_lastframe_input = 0;
6426 // get the # of respawns for the currently selected mission (if any)
6427 if(Multi_create_list_select != -1){
6428 int abs_index = multi_create_select_to_index(Multi_create_list_select);
6430 // if he has a valid mission selected
6432 Multi_ho_mission_respawn = (int)Multi_create_file_list[abs_index].respawn;
6434 Multi_ho_mission_respawn = -1;
6437 Multi_ho_mission_respawn = -1;
6441 void multi_ho_update_sliders()
6443 // game skill slider
6444 if (Game_skill_level != Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos) {
6445 if ( !(Netgame.type_flags & NG_TYPE_TEAM) ){
6446 Game_skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6447 gamesnd_play_iface(SND_USER_SELECT);
6449 Game_skill_level = NUM_SKILL_LEVELS / 2;
6453 // get the voice qos options
6454 if (Netgame.options.voice_qos != (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1)) {
6455 Netgame.options.voice_qos = (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1);
6456 gamesnd_play_iface(SND_USER_SELECT);
6459 // get the voice duration options
6460 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)) {
6461 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);
6462 gamesnd_play_iface(SND_USER_SELECT);
6467 void multi_host_options_do()
6472 k = Multi_ho_window.process();
6475 // process any keypresses
6478 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
6481 case KEY_CTRLED + SDLK_RETURN :
6482 gamesnd_play_iface(SND_COMMIT_PRESSED);
6483 multi_ho_accept_hit();
6487 // process any button clicks
6488 multi_ho_check_buttons();
6490 // update the sliders
6491 multi_ho_update_sliders();
6493 // make sure that the chatbox inputbox and any inputbox on this screen are mutually exclusive in terms of focus
6494 multi_ho_check_focus();
6496 // draw the background, etc
6498 GR_MAYBE_CLEAR_RES(Multi_ho_bitmap);
6499 if(Multi_ho_bitmap != -1){
6500 gr_set_bitmap(Multi_ho_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
6503 Multi_ho_window.draw();
6505 // draw all the radio buttons properly
6506 multi_ho_draw_radio_groups();
6508 // display any pending notification messages
6509 multi_common_notify_do();
6511 // display the voice record time settings
6512 multi_ho_display_record_time();
6514 // maybe display the max # of respawns next to the respawn input box
6515 multi_ho_blit_max_respawns();
6517 // blit the proper skill level
6518 multi_ho_display_skill_level();
6520 // blit the "host modifies button"
6521 if(Multi_ho_host_modifies){
6522 Multi_ho_buttons[gr_screen.res][MULTI_HO_HOST_MODIFIES].button.draw_forced(2);
6525 // process and show the chatbox thingie
6529 Multi_ho_window.draw_tooltip();
6531 // display the voice status indicator
6532 multi_common_voice_display_status();
6538 void multi_host_options_close()
6540 // unload any bitmaps
6541 if(!bm_unload(Multi_ho_bitmap)){
6542 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_ho_bitmap_fname[gr_screen.res]));
6545 // destroy the UI_WINDOW
6546 Multi_ho_window.destroy();
6549 void multi_ho_check_buttons()
6552 for(idx=0;idx<MULTI_HO_NUM_BUTTONS;idx++){
6553 // we only really need to check for one button pressed at a time, so we can break after
6555 if(Multi_ho_buttons[gr_screen.res][idx].button.pressed()){
6556 multi_ho_button_pressed(idx);
6562 void multi_ho_button_pressed(int n)
6564 int radio_index,idx;
6565 int x_pixel,y_pixel;
6567 // get the pixel position of the click
6568 Multi_ho_buttons[gr_screen.res][n].button.get_mouse_pos(&x_pixel,&y_pixel);
6571 // clicked on the accept button
6572 case MULTI_HO_ACCEPT:
6573 gamesnd_play_iface(SND_COMMIT_PRESSED);
6574 multi_ho_accept_hit();
6577 // clicked on the host/captains only modify button
6578 case MULTI_HO_HOST_MODIFIES:
6579 // toggle it on or off
6580 Multi_ho_host_modifies = !Multi_ho_host_modifies;
6581 gamesnd_play_iface(SND_USER_SELECT);
6585 // look through the radio buttons and see which one this corresponds to
6587 for(idx=0;idx<MULTI_HO_NUM_RADIO_BUTTONS;idx++){
6588 if(Multi_ho_radio_info[idx][2] == n){
6593 SDL_assert(radio_index != -1);
6595 // check to see if a radio button was pressed
6596 if(radio_index < MULTI_HO_NUM_RADIO_BUTTONS){
6597 // see if this value is already picked for this radio group
6598 if(Multi_ho_radio_groups[Multi_ho_radio_info[radio_index][0]] != Multi_ho_radio_info[radio_index][1]){
6599 gamesnd_play_iface(SND_USER_SELECT);
6600 Multi_ho_radio_groups[Multi_ho_radio_info[radio_index][0]] = Multi_ho_radio_info[radio_index][1];
6602 gamesnd_play_iface(SND_GENERAL_FAIL);
6607 void multi_ho_draw_radio_groups()
6611 // go through each item and draw it if it is the selected button in its respective group
6612 for(idx=0;idx<MULTI_HO_NUM_RADIO_BUTTONS;idx++){
6613 /// if this button is the currently selected one in its group
6614 if(Multi_ho_radio_info[idx][1] == Multi_ho_radio_groups[Multi_ho_radio_info[idx][0]]){
6615 Multi_ho_buttons[gr_screen.res][Multi_ho_radio_info[idx][2]].button.draw_forced(2);
6620 void multi_ho_accept_hit()
6624 // check the values in the input boxes
6625 if(!multi_ho_check_values()){
6629 // zero out the netgame flags
6632 // set default options
6633 Netgame.options.flags = (MSO_FLAG_INGAME_XFER | MSO_FLAG_ACCEPT_PIX);
6635 // set the squadmate messaging flags
6636 switch(Multi_ho_radio_groups[MULTI_HO_MSG_GROUP]){
6638 Netgame.options.squad_set = MSO_SQUAD_RANK;
6641 Netgame.options.squad_set = MSO_SQUAD_LEADER;
6644 Netgame.options.squad_set = MSO_SQUAD_ANY;
6647 Netgame.options.squad_set = MSO_SQUAD_HOST;
6653 // set the end mission flags
6654 switch(Multi_ho_radio_groups[MULTI_HO_END_GROUP]){
6656 Netgame.options.endgame_set = MSO_END_RANK;
6659 Netgame.options.endgame_set = MSO_END_LEADER;
6662 Netgame.options.endgame_set = MSO_END_ANY;
6665 Netgame.options.endgame_set = MSO_END_HOST;
6671 // set the voice toggle
6672 switch(Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP]){
6674 Netgame.options.flags &= ~(MSO_FLAG_NO_VOICE);
6677 Netgame.options.flags |= MSO_FLAG_NO_VOICE;
6683 // get the voice qos options
6684 Netgame.options.voice_qos = (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1);
6686 // get the voice duration options
6687 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);
6689 // set the skill level. If in team vs. team mode, preserve the old setting before saving
6690 // the pilot file. I'll bet that this doesn't work though because the pilot file gets
6691 // written in a bunch of locations....sigh.
6692 if ( !(Netgame.type_flags & NG_TYPE_TEAM) ){
6693 Game_skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6695 Game_skill_level = NUM_SKILL_LEVELS / 2;
6698 // set the netgame respawn count
6699 // maybe warn the user that respawns will not be used for a campaign mission
6700 if(Netgame.campaign_mode == MP_SINGLE){
6701 Multi_ho_respawns.get_text(resp_str);
6702 uint temp_respawn = (uint)atoi(resp_str);
6703 // if he currently has no mission selected, let the user set any # of respawns
6704 if((int)temp_respawn > Multi_ho_mission_respawn){
6705 if(Multi_ho_mission_respawn == -1){
6706 Netgame.respawn = temp_respawn;
6707 Netgame.options.respawn = temp_respawn;
6709 // this should have been taken care of by the interface code
6714 Netgame.options.respawn = temp_respawn;
6715 Netgame.respawn = temp_respawn;
6719 // get the mission time limit
6720 Multi_ho_time_limit.get_text(resp_str);
6721 int temp_time = atoi(resp_str);
6723 Netgame.options.mission_time_limit = fl2f(-1.0f);
6724 } else if(temp_time > MULTI_HO_MAX_TIME_LIMIT){
6727 Netgame.options.mission_time_limit = fl2f(60.0f * (float)temp_time);
6730 // get observer count options
6731 Multi_ho_obs.get_text(resp_str);
6732 int temp_obs = atoi(resp_str);
6733 if(temp_obs > MULTI_HO_MAX_OBS){
6736 Netgame.options.max_observers = (ubyte)temp_obs;
6738 // get the furball kill limit
6739 Multi_ho_kill_limit.get_text(resp_str);
6740 int temp_kills = atoi(resp_str);
6741 if(temp_kills > MULTI_HO_MAX_KILL_LIMIT){
6744 Netgame.options.kill_limit = temp_kills;
6746 // get the token wait limit
6747 Multi_ho_voice_wait.get_text(resp_str);
6748 int temp_wait = atoi(resp_str);
6749 if(temp_wait > MULTI_HO_MAX_TOKEN_WAIT){
6752 Netgame.options.voice_token_wait = (temp_wait * 1000);
6754 // set the netgame option
6755 Netgame.options.skill_level = (ubyte)Game_skill_level;
6757 // get whether we're in host/captains only modify mode
6758 Netgame.options.flags &= ~(MSO_FLAG_SS_LEADERS);
6759 if(Multi_ho_host_modifies){
6760 Netgame.options.flags |= MSO_FLAG_SS_LEADERS;
6763 // store these values locally
6764 memcpy(&Player->m_local_options,&Net_player->p_info.options,sizeof(multi_local_options));
6765 memcpy(&Player->m_server_options,&Netgame.options,sizeof(multi_server_options));
6766 write_pilot_file(Player);
6768 // apply any changes in settings (notify everyone of voice qos changes, etc)
6769 multi_ho_apply_options();
6771 // move back to the create game screen
6772 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
6775 void multi_ho_get_options()
6779 // set the squadmate messaging buttons
6780 switch(Netgame.options.squad_set){
6781 case MSO_SQUAD_RANK :
6782 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 0;
6784 case MSO_SQUAD_LEADER:
6785 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 1;
6788 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 2;
6790 case MSO_SQUAD_HOST:
6791 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 3;
6797 // set the mission end buttons
6798 switch(Netgame.options.endgame_set){
6800 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 0;
6802 case MSO_END_LEADER:
6803 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 1;
6806 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 2;
6809 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 3;
6815 // set the voice toggle buttons
6816 if(Netgame.options.flags & MSO_FLAG_NO_VOICE){
6817 Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP] = 1;
6819 Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP] = 0;
6822 // get the voice qos options
6823 SDL_assert((Netgame.options.voice_qos >= 1) && (Netgame.options.voice_qos <= 10));
6824 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos = (Netgame.options.voice_qos - 1);
6826 // get the voice duration options
6827 SDL_assert((Netgame.options.voice_record_time > 0) && (Netgame.options.voice_record_time <= MULTI_VOICE_MAX_TIME));
6828 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos = ((int)((float)Netgame.options.voice_record_time / 500.0f)) - 1;
6830 // get the current skill level
6831 SDL_assert((Game_skill_level >= 0) && (Game_skill_level < NUM_SKILL_LEVELS));
6832 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos = Game_skill_level;
6834 // get the # of observers
6835 memset(resp_str,0,10);
6836 sprintf(resp_str,"%d",Netgame.options.max_observers);
6837 Multi_ho_obs.set_text(resp_str);
6839 // set the respawn count
6840 if(Netgame.campaign_mode == MP_SINGLE){
6841 memset(resp_str,0,10);
6842 sprintf(resp_str,"%d",Netgame.respawn);
6843 Multi_ho_respawns.set_text(resp_str);
6846 // set the mission time limit
6847 memset(resp_str,0,10);
6848 float tl = f2fl(Netgame.options.mission_time_limit);
6849 sprintf(resp_str,"%d",(int)(tl / 60.0f));
6850 Multi_ho_time_limit.set_text(resp_str);
6852 // set the furball kill limit
6853 memset(resp_str,0,10);
6854 sprintf(resp_str,"%d",Netgame.options.kill_limit);
6855 Multi_ho_kill_limit.set_text(resp_str);
6857 // set the token wait time
6858 memset(resp_str,0,10);
6859 sprintf(resp_str,"%d",Netgame.options.voice_token_wait / 1000);
6860 Multi_ho_voice_wait.set_text(resp_str);
6862 // get whether we're in host/captains only modify mode
6863 if(Netgame.options.flags & MSO_FLAG_SS_LEADERS){
6864 Multi_ho_host_modifies = 1;
6866 Multi_ho_host_modifies = 0;
6870 void multi_ho_apply_options()
6872 // if the voice qos or duration has changed, apply the change
6873 multi_voice_maybe_update_vars(Netgame.options.voice_qos,Netgame.options.voice_record_time);
6875 // send an options update
6876 multi_options_update_netgame();
6879 // display the voice record time settings
6880 void multi_ho_display_record_time()
6883 int full_seconds, half_seconds;
6886 memset(time_str,0,30);
6889 full_seconds = (((Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 500) / 1000);
6891 // get the half-seconds
6892 half_seconds = ((((Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 500) % 1000) / 500) * 5;
6894 // format the string
6895 sprintf(time_str,"%d.%d",full_seconds,half_seconds);
6896 gr_set_color_fast(&Color_bright);
6897 gr_string(Ho_vd_coords[gr_screen.res][MULTI_HO_X_COORD],Ho_vd_coords[gr_screen.res][MULTI_HO_Y_COORD],time_str);
6900 int multi_ho_check_values()
6904 memset(val_txt,0,255);
6906 // check against respawn settings
6907 if(Multi_ho_mission_respawn != -1){
6908 Multi_ho_respawns.get_text(val_txt);
6909 // if the value is invalid, let the user know
6910 if(atoi(val_txt) > Multi_ho_mission_respawn){
6911 memset(val_txt,0,255);
6912 sprintf(val_txt,XSTR("Warning\nRespawn count in greater than mission specified max (%d)",796),Multi_ho_mission_respawn);
6913 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6918 // check against mission time limit max
6919 Multi_ho_time_limit.get_text(val_txt);
6920 // if the value is invalid, force it to be valid
6921 if(atoi(val_txt) > MULTI_HO_MAX_TIME_LIMIT){
6922 memset(val_txt,0,255);
6923 sprintf(val_txt,XSTR("Warning\nMission time limit is greater than max allowed (%d)",797),MULTI_HO_MAX_TIME_LIMIT);
6924 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6928 // check against max observer limit
6929 Multi_ho_obs.get_text(val_txt);
6930 // if the value is invalid, force it to be valid
6931 if(atoi(val_txt) > MULTI_HO_MAX_OBS){
6932 memset(val_txt,0,255);
6933 sprintf(val_txt,XSTR("Warning\nObserver count is greater than max allowed (%d)",798),MULTI_HO_MAX_OBS);
6934 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6938 // check against furball kill limit
6939 Multi_ho_kill_limit.get_text(val_txt);
6940 // if the value is invalid, force it to be valid
6941 if(atoi(val_txt) > MULTI_HO_MAX_KILL_LIMIT){
6942 memset(val_txt,0,255);
6943 sprintf(val_txt,XSTR("Warning\nMission kill limit is greater than max allowed (%d)",799),MULTI_HO_MAX_KILL_LIMIT);
6944 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6948 // check against the token wait limit
6949 Multi_ho_voice_wait.get_text(val_txt);
6950 if(atoi(val_txt) > MULTI_HO_MAX_TOKEN_WAIT){
6951 memset(val_txt,0,255);
6952 sprintf(val_txt,XSTR("Warning\nvoice wait time is greater than max allowed (%d)",800),MULTI_HO_MAX_TOKEN_WAIT);
6953 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6957 // all values are valid
6961 void multi_ho_check_focus()
6963 // if an inputbox has been pressed (hit enter), lose its focus
6964 if (Multi_ho_respawns.pressed() || Multi_ho_time_limit.pressed() || Multi_ho_voice_wait.pressed() || Multi_ho_kill_limit.pressed() || Multi_ho_obs.pressed()) {
6965 Multi_ho_respawns.clear_focus();
6966 Multi_ho_time_limit.clear_focus();
6967 Multi_ho_voice_wait.clear_focus();
6968 Multi_ho_kill_limit.clear_focus();
6969 Multi_ho_obs.clear_focus();
6970 gamesnd_play_iface(SND_COMMIT_PRESSED);
6971 chatbox_set_focus();
6972 Multi_ho_lastframe_input = 0;
6974 } else if(!Multi_ho_lastframe_input) {
6975 // if we didn't have focus last frame
6976 if(Multi_ho_respawns.has_focus() || Multi_ho_time_limit.has_focus() || Multi_ho_kill_limit.has_focus() || Multi_ho_voice_wait.has_focus() ){
6977 chatbox_lose_focus();
6979 Multi_ho_lastframe_input = 1;
6982 // if we _did_ have focus last frame
6984 // if we no longer have focus on any of the input boxes, set the focus on the chatbox
6985 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()){
6986 chatbox_set_focus();
6988 // if the chatbox now has focus, clear all focus from our inputboxes
6989 else if (chatbox_has_focus()) {
6990 Multi_ho_respawns.clear_focus();
6991 Multi_ho_time_limit.clear_focus();
6992 Multi_ho_kill_limit.clear_focus();
6993 Multi_ho_voice_wait.clear_focus();
6995 Multi_ho_lastframe_input = 0;
7000 void multi_ho_blit_max_respawns()
7004 // if we're in campaign mode, do nothing
7005 if(Netgame.campaign_mode == MP_CAMPAIGN){
7009 // otherwise blit the max as specified by the current mission file
7010 sprintf(string,"(%d)",Multi_ho_mission_respawn);
7011 gr_set_color_fast(&Color_normal);
7012 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);
7015 void multi_ho_display_skill_level()
7017 int skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
7020 SDL_assert((skill_level >= 0) && (skill_level < NUM_SKILL_LEVELS));
7021 if((skill_level < 0) || (skill_level >= NUM_SKILL_LEVELS)){
7025 gr_set_color_fast(&Color_bright);
7026 gr_string(Ho_st_coords[gr_screen.res][0], Ho_st_coords[gr_screen.res][1], Skill_level_names(skill_level, 1));
7029 // -------------------------------------------------------------------------------------------------------------
7031 // MULTIPLAYER JOIN SCREEN
7034 #define MULTI_JW_NUM_BUTTONS 8
7038 #define MULTI_JW_PALETTE "InterfacePalette"
7040 static const char *Multi_jw_bitmap_fname[GR_NUM_RESOLUTIONS] = {
7041 "MultiJoinWait", // GR_640
7042 "2_MultiJoinWait" // GR_1024
7045 static const char *Multi_jw_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
7046 "MultiJoinWait-M", // GR_640
7047 "2_MultiJoinWait-M" // GR_1024
7053 #define MJW_SCROLL_PLAYERS_UP 0
7054 #define MJW_SCROLL_PLAYERS_DOWN 1
7057 #define MJW_PILOT_INFO 4
7058 #define MJW_SCROLL_INFO_UP 5
7059 #define MJW_SCROLL_INFO_DOWN 6
7060 #define MJW_CANCEL 7
7062 UI_WINDOW Multi_jw_window; // the window object for the join screen
7063 int Multi_jw_bitmap; // the background bitmap
7065 // constants for coordinate lookup
7066 #define MJW_X_COORD 0
7067 #define MJW_Y_COORD 1
7068 #define MJW_W_COORD 2
7069 #define MJW_H_COORD 3
7071 ui_button_info Multi_jw_buttons[GR_NUM_RESOLUTIONS][MULTI_JW_NUM_BUTTONS] = {
7074 ui_button_info("MJW_00", 0, 50, -1, -1, 0),
7075 ui_button_info("MJW_01", 0, 87, -1, -1, 1),
7076 ui_button_info("MJW_02", 20, 219, -1, -1, 2),
7077 ui_button_info("MJW_03", 73, 219, -1, -1, 3),
7078 ui_button_info("MJW_09", 131, 213, -1, -1, 9),
7079 ui_button_info("MJW_05", 0, 398, -1, -1, 5),
7080 ui_button_info("MJW_06", 0, 435, -1, -1, 6),
7081 ui_button_info("MJW_04", 559, 411, -1, -1, 4),
7083 ui_button_info("MJW_00", 1, 24, -1, -1, 0),
7084 ui_button_info("MJW_01", 1, 66, -1, -1, 1),
7085 ui_button_info("MJW_02", 30, 244, 20, 272, 2),
7086 ui_button_info("MJW_03", 84, 244, 73, 272, 3),
7087 ui_button_info("MJW_04", 139, 242, 134, 272, 4),
7088 ui_button_info("MJW_05", 1, 406, -1, -1, 5),
7089 ui_button_info("MJW_06", 1, 447, -1, -1, 6),
7090 ui_button_info("MJW_07", 577, 428, 570, 414, 7),
7094 ui_button_info("2_MJW_00", 2, 38, -1, -1, 0),
7095 ui_button_info("2_MJW_01", 2, 106, -1, -1, 1),
7096 ui_button_info("2_MJW_02", 48, 390, 47, 435, 2),
7097 ui_button_info("2_MJW_03", 134, 390, 133, 435, 3),
7098 ui_button_info("2_MJW_04", 223, 388, 225, 435, 4),
7099 ui_button_info("2_MJW_05", 2, 649, -1, -1, 5),
7100 ui_button_info("2_MJW_06", 2, 715, -1, -1, 6),
7101 ui_button_info("2_MJW_07", 923, 685, 931, 667, 7),
7106 #define MULTI_JW_NUM_TEXT 7
7108 UI_XSTR Multi_jw_text[GR_NUM_RESOLUTIONS][MULTI_JW_NUM_TEXT] = {
7110 { "Team 1", 1308, 20, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_TEAM0].button },
7111 { "Team 2", 1309, 73, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_TEAM1].button },
7112 { "Pilot", 1310, 134, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_PILOT_INFO].button },
7113 { "Info", 1311, 134, 283, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_PILOT_INFO].button },
7114 { "Cancel", 387, 570, 414, UI_XSTR_COLOR_PINK, -1, &Multi_jw_buttons[0][MJW_CANCEL].button },
7115 { "Players", 1269, 38, 8, UI_XSTR_COLOR_GREEN, -1, NULL },
7116 { "Choose Team", 1312, 27, 231, UI_XSTR_COLOR_GREEN, -1, NULL },
7119 { "Team 1", 1308, 47, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_TEAM0].button },
7120 { "Team 2", 1309, 133, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_TEAM1].button },
7121 { "Pilot", 1310, 225, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_PILOT_INFO].button },
7122 { "Info", 1311, 225, 446, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_PILOT_INFO].button },
7123 { "Cancel", 387, 931, 667, UI_XSTR_COLOR_PINK, -1, &Multi_jw_buttons[1][MJW_CANCEL].button },
7124 { "Players", 1269, 165, 12, UI_XSTR_COLOR_GREEN, -1, NULL },
7125 { "Choose Team", 1312, 45, 373, UI_XSTR_COLOR_GREEN, -1, NULL },
7130 int Mjw_players_coords[GR_NUM_RESOLUTIONS][4] = {
7143 int Mjw_mission_name_coords[GR_NUM_RESOLUTIONS][2] = {
7152 // squad war checkbox
7153 UI_CHECKBOX Multi_jw_sw_checkbox;
7154 const char *Multi_jw_sw_checkbox_fname[GR_NUM_RESOLUTIONS] = {
7158 int Multi_jw_sw_checkbox_coords[GR_NUM_RESOLUTIONS][2] = {
7166 int Multi_jw_sw_checkbox_text[GR_NUM_RESOLUTIONS][2] = {
7176 // player list control thingie defs
7177 #define MULTI_JW_PLIST_MAX_DISPLAY 19
7178 int Multi_jw_plist_select_flag; // indicates whether we currently have a selected player
7179 short Multi_jw_plist_select_id; // id of the current selected player
7180 UI_BUTTON Multi_jw_plist_select_button; // for selecting a player
7182 int Multi_jw_should_show_popup = 0;
7184 // LOCAL function definitions
7185 void multi_jw_check_buttons();
7186 void multi_jw_button_pressed(int n);
7187 void multi_jw_do_netstuff();
7188 void multi_jw_scroll_players_up();
7189 void multi_jw_scroll_players_down();
7190 void multi_jw_plist_process();
7191 void multi_jw_plist_blit_normal();
7192 void multi_jw_plist_blit_team();
7193 short multi_jw_get_mouse_id();
7195 void multi_game_client_setup_init()
7199 // create the interface window
7200 Multi_jw_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
7201 Multi_jw_window.set_mask_bmap(Multi_jw_bitmap_mask_fname[gr_screen.res]);
7203 // load the background bitmap
7204 Multi_jw_bitmap = bm_load(Multi_jw_bitmap_fname[gr_screen.res]);
7205 if(Multi_jw_bitmap < 0){
7206 // we failed to load the bitmap - this is very bad
7210 // initialize the player list data
7211 Multi_jw_plist_select_flag = 0;
7212 Multi_jw_plist_select_id = -1;
7214 // kill any old instances of the chatbox and create a new one
7216 chatbox_create(CHATBOX_FLAG_BIG | CHATBOX_FLAG_DRAW_BOX | CHATBOX_FLAG_BUTTONS);
7218 // initialize the common notification messaging
7219 multi_common_notify_init();
7221 // initialize the common mission info display area.
7222 multi_common_set_text("");
7224 // use the common interface palette
7225 multi_common_set_palette();
7227 // create the interface buttons
7228 for(idx=0; idx<MULTI_JW_NUM_BUTTONS; idx++){
7229 // create the object
7230 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);
7232 // set the sound to play when highlighted
7233 Multi_jw_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
7235 // set the ani for the button
7236 Multi_jw_buttons[gr_screen.res][idx].button.set_bmaps(Multi_jw_buttons[gr_screen.res][idx].filename);
7239 Multi_jw_buttons[gr_screen.res][idx].button.link_hotspot(Multi_jw_buttons[gr_screen.res][idx].hotspot);
7242 // if this is a PXO game, enable the squadwar checkbox
7243 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);
7244 Multi_jw_sw_checkbox.set_bmaps(Multi_jw_sw_checkbox_fname[gr_screen.res], 6, 0);
7245 Multi_jw_sw_checkbox.disable();
7246 if(!MULTI_IS_TRACKER_GAME){
7247 Multi_jw_sw_checkbox.hide();
7252 for(idx=0; idx<MULTI_JW_NUM_TEXT; idx++){
7253 Multi_jw_window.add_XSTR(&Multi_jw_text[gr_screen.res][idx]);
7257 // create the player select list button and hide it
7258 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);
7259 Multi_jw_plist_select_button.hide();
7262 Multi_jw_buttons[gr_screen.res][MJW_CANCEL].button.set_hotkey(SDLK_ESCAPE);
7264 // remove campaign flags
7265 Game_mode &= ~(GM_CAMPAIGN_MODE);
7267 // tell the server we have finished joining
7268 Net_player->state = NETPLAYER_STATE_JOINED;
7269 send_netplayer_update_packet();
7272 ml_printf(NOX("Joined netgame %s"), Netgame.name);
7274 // send any appropriate files
7275 multi_data_send_my_junk();
7278 void multi_game_client_setup_do_frame()
7281 int k = chatbox_process();
7282 char mission_text[255];
7283 k = Multi_jw_window.process(k,0);
7285 Multi_jw_should_show_popup = 0;
7287 // process any button clicks
7288 multi_jw_check_buttons();
7290 // do any network related stuff
7291 multi_jw_do_netstuff();
7293 // draw the background, etc
7295 GR_MAYBE_CLEAR_RES(Multi_jw_bitmap);
7296 if(Multi_jw_bitmap != -1){
7297 gr_set_bitmap(Multi_jw_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7301 // if we're not in team vs. team mode, don't draw the team buttons
7302 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
7303 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.hide();
7304 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.hide();
7305 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.disable();
7306 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.disable();
7308 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.enable();
7309 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.enable();
7310 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.unhide();
7311 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.unhide();
7314 if(MULTI_IS_TRACKER_GAME){
7315 // maybe check the squadwar button
7316 if(Netgame.type_flags & NG_TYPE_SW){
7317 Multi_jw_sw_checkbox.set_state(1);
7318 gr_set_color_fast(&Color_bright);
7320 Multi_jw_sw_checkbox.set_state(0);
7321 gr_set_color_fast(&Color_normal);
7324 gr_string(Multi_jw_sw_checkbox_text[gr_screen.res][0], Multi_jw_sw_checkbox_text[gr_screen.res][1], "SquadWar");
7327 // draw the UI window
7328 Multi_jw_window.draw();
7330 // process and display the player list
7331 // NOTE : this must be done before the buttons are checked to insure that a player hasn't dropped
7332 multi_jw_plist_process();
7333 if(Netgame.type_flags & NG_TYPE_TEAM){
7334 multi_jw_plist_blit_team();
7336 multi_jw_plist_blit_normal();
7339 // display any text in the info area
7340 multi_common_render_text();
7342 // display any pending notification messages
7343 multi_common_notify_do();
7345 // blit the mission filename if possible
7346 if(Netgame.campaign_mode){
7347 if(strlen(Netgame.campaign_name) > 0){
7348 strcpy(mission_text,Netgame.campaign_name);
7350 if(strlen(Netgame.title) > 0){
7351 strcat(mission_text,", ");
7352 strcat(mission_text,Netgame.title);
7355 gr_set_color_fast(&Color_bright_white);
7356 gr_string(Mjw_mission_name_coords[gr_screen.res][MJW_X_COORD],Mjw_mission_name_coords[gr_screen.res][MJW_Y_COORD],mission_text);
7359 if(strlen(Netgame.mission_name) > 0){
7360 strcpy(mission_text,Netgame.mission_name);
7362 if(strlen(Netgame.title) > 0){
7363 strcat(mission_text,", ");
7364 strcat(mission_text,Netgame.title);
7367 gr_set_color_fast(&Color_bright_white);
7368 gr_string(Mjw_mission_name_coords[gr_screen.res][MJW_X_COORD],Mjw_mission_name_coords[gr_screen.res][MJW_Y_COORD],mission_text);
7372 // process and show the chatbox thingie
7376 Multi_jw_window.draw_tooltip();
7378 // display the voice status indicator
7379 multi_common_voice_display_status();
7384 // if we're supposed to be displaying a pilot info popup
7385 if(Multi_jw_should_show_popup){
7386 player_index = find_player_id(Multi_jw_plist_select_id);
7387 if(player_index != -1){
7388 multi_pinfo_popup(&Net_players[player_index]);
7393 void multi_game_client_setup_close()
7395 // unload any bitmaps
7396 if(!bm_unload(Multi_jw_bitmap)){
7397 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_jw_bitmap_fname[gr_screen.res]));
7400 // destroy the chatbox
7403 // destroy the UI_WINDOW
7404 Multi_jw_window.destroy();
7407 if(Netgame.game_state == NETGAME_STATE_MISSION_SYNC){
7408 gamesnd_play_iface(SND_COMMIT_PRESSED);
7413 void multi_jw_check_buttons()
7416 for(idx=0;idx<MULTI_JW_NUM_BUTTONS;idx++){
7417 // we only really need to check for one button pressed at a time, so we can break after
7419 if(Multi_jw_buttons[gr_screen.res][idx].button.pressed()){
7420 multi_jw_button_pressed(idx);
7426 void multi_jw_button_pressed(int n)
7430 gamesnd_play_iface(SND_USER_SELECT);
7431 multi_quit_game(PROMPT_CLIENT);
7433 case MJW_SCROLL_PLAYERS_UP:
7434 multi_jw_scroll_players_up();
7436 case MJW_SCROLL_PLAYERS_DOWN:
7437 multi_jw_scroll_players_down();
7439 case MJW_SCROLL_INFO_UP:
7440 multi_common_scroll_text_up();
7442 case MJW_SCROLL_INFO_DOWN:
7443 multi_common_scroll_text_down();
7446 // request to set myself to team 0
7448 gamesnd_play_iface(SND_USER_SELECT);
7449 multi_team_set_team(Net_player,0);
7452 // request to set myself to team 1
7454 gamesnd_play_iface(SND_USER_SELECT);
7455 multi_team_set_team(Net_player,1);
7459 case MJW_PILOT_INFO:
7460 Multi_jw_should_show_popup = 1;
7464 multi_common_add_notify(XSTR("Not implemented yet!",760));
7469 // do stuff like pinging servers, sending out requests, etc
7470 void multi_jw_do_netstuff()
7474 void multi_jw_scroll_players_up()
7476 gamesnd_play_iface(SND_GENERAL_FAIL);
7479 // scroll down through the player list
7480 void multi_jw_scroll_players_down()
7482 gamesnd_play_iface(SND_GENERAL_FAIL);
7485 void multi_jw_plist_process()
7487 int test_count,player_index,idx;
7489 // first determine if there are 0 players in the game. This should never happen since the host is _always_ in the game
7491 for(idx=0;idx<MAX_PLAYERS;idx++){
7492 // count anyone except the standalone server (if applicable)
7493 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7497 if(test_count <= 0){
7501 // if we had a selected item but that player has left, select myself instead
7502 if(Multi_jw_plist_select_flag){
7503 player_index = find_player_id(Multi_jw_plist_select_id);
7504 if(player_index == -1){
7505 Multi_jw_plist_select_id = Net_player->player_id;
7508 Multi_jw_plist_select_flag = 1;
7509 Multi_jw_plist_select_id = Net_player->player_id;
7512 // if the player has clicked somewhere in the player list area
7513 if(Multi_jw_plist_select_button.pressed()){
7517 player_id = multi_jw_get_mouse_id();
7518 player_index = find_player_id(player_id);
7519 if(player_index != -1){
7520 Multi_jw_plist_select_id = player_id;
7521 Multi_jw_plist_select_flag = 1;
7526 void multi_jw_plist_blit_normal()
7529 char str[CALLSIGN_LEN+1];
7530 int y_start = Mjw_players_coords[gr_screen.res][MJW_Y_COORD];
7533 // display all the players
7534 for(idx=0;idx<MAX_PLAYERS;idx++){
7535 // reset total offset
7538 // count anyone except the standalone server (if applicable)
7539 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7540 // highlight him if he's the host
7541 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7542 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7543 gr_set_color_fast(&Color_text_active_hi);
7545 gr_set_color_fast(&Color_bright);
7548 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7549 gr_set_color_fast(&Color_text_active);
7551 gr_set_color_fast(&Color_text_normal);
7555 // optionally draw his CD status
7556 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7557 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7558 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7560 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7563 // make sure the string will fit, then display it
7564 strcpy(str,Net_players[idx].player->callsign);
7565 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
7568 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7569 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7576 void multi_jw_plist_blit_team()
7579 char str[CALLSIGN_LEN+1];
7580 int y_start = Mjw_players_coords[gr_screen.res][MJW_Y_COORD];
7583 // always blit the proper team button based on _my_ team status
7584 if(Net_player->p_info.team == 0){
7585 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.draw_forced(2);
7587 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.draw_forced(2);
7590 // display all the red players first
7591 for(idx=0;idx<MAX_PLAYERS;idx++){
7592 // reset total offset
7595 // count anyone except the standalone server (if applicable)
7596 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
7597 // highlight him if he's the host
7598 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7599 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7600 gr_set_color_fast(&Color_text_active_hi);
7602 gr_set_color_fast(&Color_bright);
7605 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7606 gr_set_color_fast(&Color_text_active);
7608 gr_set_color_fast(&Color_text_normal);
7612 // optionally draw his CD status
7613 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7614 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7615 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7617 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7620 // blit the red team indicator
7621 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
7622 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
7623 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7624 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7626 total_offset += Multi_common_icon_dims[MICON_TEAM0_SELECT][0] + 1;
7629 if(Multi_common_icons[MICON_TEAM0] != -1){
7630 gr_set_bitmap(Multi_common_icons[MICON_TEAM0], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7631 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7633 total_offset += Multi_common_icon_dims[MICON_TEAM0][0] + 1;
7637 // make sure the string will fit
7638 strcpy(str,Net_players[idx].player->callsign);
7639 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7641 // display him in the correct half of the list depending on his team
7642 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7647 // display all the green players next
7648 for(idx=0;idx<MAX_PLAYERS;idx++){
7649 // reset total offset
7652 // count anyone except the standalone server (if applicable)
7653 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
7654 // highlight him if he's the host
7655 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7656 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7657 gr_set_color_fast(&Color_text_active_hi);
7659 gr_set_color_fast(&Color_bright);
7662 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7663 gr_set_color_fast(&Color_text_active);
7665 gr_set_color_fast(&Color_text_normal);
7669 // optionally draw his CD status
7670 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7671 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7672 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7674 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7677 // blit the red team indicator
7678 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
7679 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
7680 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7681 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7683 total_offset += Multi_common_icon_dims[MICON_TEAM1_SELECT][0] + 1;
7686 if(Multi_common_icons[MICON_TEAM1] != -1){
7687 gr_set_bitmap(Multi_common_icons[MICON_TEAM1], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7688 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7690 total_offset += Multi_common_icon_dims[MICON_TEAM1][0] + 1;
7694 // make sure the string will fit
7695 strcpy(str,Net_players[idx].player->callsign);
7696 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
7699 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7701 // display him in the correct half of the list depending on his team
7702 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7708 void multi_jw_handle_join(net_player *pl)
7710 // for now just play a bloop sound
7711 gamesnd_play_iface(SND_ICON_DROP_ON_WING);
7714 short multi_jw_get_mouse_id()
7716 // determine where he clicked (y pixel value)
7718 Multi_jw_plist_select_button.get_mouse_pos(NULL,&y);
7720 // select things a little differently if we're in team vs. team or non-team vs. team mode
7722 if(Netgame.type_flags & NG_TYPE_TEAM){
7723 int player_index = -1;
7725 // look through all of team red first
7726 for(idx=0;idx<MAX_PLAYERS;idx++){
7727 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
7730 // if this is the _nth_ guy
7738 // if we still haven't found him yet, look through the green team
7739 if(player_index == -1){
7740 for(idx=0;idx<MAX_PLAYERS;idx++){
7741 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
7743 // if this is the _nth_ guy
7751 if(player_index != -1){
7752 return Net_players[idx].player_id;
7755 // select the nth active player if possible, disregarding the standalone server
7756 for(idx=0;idx<MAX_PLAYERS;idx++){
7757 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7760 // if this is the _nth_ guy
7762 return Net_players[idx].player_id;
7773 // -------------------------------------------------------------------------------------------------------------
7775 // MULTIPLAYER WAIT/SYNCH SCREEN
7778 #define MULTI_SYNC_HOST_COUNT 4 // host uses 4 buttons (and sometimes 5)
7779 #define MULTI_SYNC_CLIENT_COUNT 3 // client only uses 3 buttons
7781 const char *Multi_sync_bitmap_fname[GR_NUM_RESOLUTIONS] = {
7782 "MultiSynch", // GR_640
7783 "2_MultiSynch" // GR_1024
7786 const char *Multi_sync_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
7787 "MultiSynch-M", // GR_640
7788 "2_MultiSynch-M" // GR_1024
7793 // constants for coordinate lookup
7794 #define MS_X_COORD 0
7795 #define MS_Y_COORD 1
7796 #define MS_W_COORD 2
7797 #define MS_H_COORD 3
7799 UI_WINDOW Multi_sync_window; // the window object for the join screen
7800 int Multi_sync_button_count; // the # of buttons to use for this instance of mission sync
7801 int Multi_sync_bitmap; // the background bitmap
7804 #define MULTI_SYNC_NUM_BUTTONS 5
7805 #define MS_SCROLL_INFO_UP 0
7806 #define MS_SCROLL_INFO_DOWN 1
7810 ui_button_info Multi_sync_buttons[GR_NUM_RESOLUTIONS][MULTI_SYNC_NUM_BUTTONS] = {
7813 ui_button_info("MS_00", 0, 396, -1, -1, 0),
7814 ui_button_info("MS_01", 0, 435, -1, -1, 1),
7815 ui_button_info("MS_02", 496, 411, -1, -1, 2),
7816 ui_button_info("MS_04", 436, 403, -1, -1, 4),
7817 ui_button_info("MS_03", 556, 398, -1, -1, 3),
7819 ui_button_info("MS_00", 1, 404, -1, -1, 0),
7820 ui_button_info("MS_01", 1, 446, -1, -1, 1),
7821 ui_button_info("MS_03", 518, 426, 519, 416, 3),
7822 ui_button_info("MS_02", 469, 426, 479, 416, 2),
7823 ui_button_info("MS_04", 571, 420, 577, 416, 4),
7827 ui_button_info("2_MS_00", 2, 647, -1, -1, 0),
7828 ui_button_info("2_MS_01", 2, 713, -1, -1, 1),
7829 ui_button_info("2_MS_03", 829, 682, 831, 667, 3),
7830 ui_button_info("2_MS_02", 751, 682, 766, 667, 2),
7831 ui_button_info("2_MS_04", 914, 672, 924, 667, 4),
7837 #define MULTI_SYNC_NUM_TEXT 5
7839 #define MST_LAUNCH 2
7840 UI_XSTR Multi_sync_text[GR_NUM_RESOLUTIONS][MULTI_SYNC_NUM_TEXT] = {
7842 { "Kick", 1266, 479, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_KICK].button },
7843 { "Cancel", 387, 519, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_CANCEL].button },
7844 { "Launch", 801, 577, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_LAUNCH].button },
7845 { "Players", 1269, 23, 133, UI_XSTR_COLOR_GREEN, -1, NULL },
7846 { "Status", 1304, 228, 133, UI_XSTR_COLOR_GREEN, -1, NULL }
7849 { "Kick", 1266, 766, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_KICK].button },
7850 { "Cancel", 387, 831, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_CANCEL].button },
7851 { "Launch", 801, 924, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_LAUNCH].button },
7852 { "Players", 1269, 38, 214, UI_XSTR_COLOR_GREEN, -1, NULL },
7853 { "Status", 1304, 366, 214, UI_XSTR_COLOR_GREEN, -1, NULL }
7859 int Ms_status_coords[GR_NUM_RESOLUTIONS][4] = {
7868 // player status coords
7869 int Ms_status2_coords[GR_NUM_RESOLUTIONS][4] = {
7878 int Ms_cd_icon_offset[GR_NUM_RESOLUTIONS] = {
7883 int Ms_team_icon_offset[GR_NUM_RESOLUTIONS] = {
7888 // player currently selected, index into Net_players[]
7889 int Multi_sync_player_select = -1;
7891 // player list control thingie defs
7892 #define MULTI_SYNC_PLIST_MAX_DISPLAY 15
7893 int Multi_sync_plist_start; // where to start displaying from
7894 int Multi_sync_plist_count; // how many we have
7896 // list select button
7897 UI_BUTTON Multi_sync_plist_button;
7899 int Multi_sync_mode = -1;
7901 #define MULTI_SYNC_COUNTDOWN_TIME 5 // in seconds
7902 float Multi_sync_countdown_timer;
7903 int Multi_sync_countdown = -1;
7905 int Multi_launch_button_created;
7908 // countdown animation timer
7909 const char* Multi_sync_countdown_fname[GR_NUM_RESOLUTIONS] = {
7911 "2_Count" // GR_1024
7914 int Multi_sync_countdown_coords[GR_NUM_RESOLUTIONS][2] = {
7925 anim *Multi_sync_countdown_anim = NULL;
7926 anim_instance *Multi_sync_countdown_instance = NULL;
7929 // PREBRIEFING STUFF
7930 // syncing flags used by the server
7931 int Mission_sync_flags = 0;
7932 #define MS_FLAG_SENT_FILESIG (1<<0) // sent filesig requests
7933 #define MS_FLAG_SENT_LOAD (1<<1) // sent load packets
7934 #define MS_FLAG_PUSHED_BRIEFING (1<<2) // pushed everyone else into the briefing
7935 #define MS_FLAG_POST_DATA (1<<3) // sent the post data block
7936 #define MS_FLAG_WSS_SLOTS (1<<4) // all players have received wss slot data
7937 #define MS_FLAG_PSETTINGS (1<<5) // send the player settings packet
7938 #define MS_FLAG_MT_STATS_START (1<<6) // server has started getting player stats from the tracker
7939 #define MS_FLAG_MT_STATS_DONE (1<<7) // server has finished getting player stats from the tracker (success or fail)
7940 #define MS_FLAG_TS_SLOTS (1<<8) // team/ship slots have been sent
7941 #define MS_FLAG_DATA_DONE (1<<9) // done transferring all necessary data
7942 #define MS_FLAG_CAMP_DONE (1<<10) // send campaign pool/goal/event stuff
7944 // POSTBRIEFING STUFF
7945 int Multi_state_timestamp;
7946 int Multi_sync_launch_pressed;
7948 // LOCAL function definitions
7949 void multi_sync_check_buttons();
7950 void multi_sync_button_pressed(int n);
7951 void multi_sync_scroll_info_up();
7952 void multi_sync_scroll_info_down();
7953 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
7954 void multi_sync_display_status(const char *status, int index); // display info on the right hand portion of the status window thingie
7955 void multi_sync_force_start_pre();
7956 void multi_sync_force_start_post();
7957 void multi_sync_launch();
7958 void multi_sync_create_launch_button();
7959 void multi_sync_blit_screen_all();
7960 void multi_sync_handle_plist();
7962 void multi_sync_common_init();
7963 void multi_sync_common_do();
7964 void multi_sync_common_close();
7966 void multi_sync_pre_init();
7967 void multi_sync_pre_do();
7968 void multi_sync_pre_close();
7970 void multi_sync_post_init();
7971 void multi_sync_post_do();
7972 void multi_sync_post_close();
7977 // perform the correct init functions
7978 void multi_sync_init()
7980 Multi_sync_countdown = -1;
7984 // reset all timestamp
7985 multi_reset_timestamps();
7987 extern int Player_multi_died_check;
7988 Player_multi_died_check = -1;
7990 if(!(Game_mode & GM_STANDALONE_SERVER)){
7991 multi_sync_common_init();
7994 switch(Multi_sync_mode){
7995 case MULTI_SYNC_PRE_BRIEFING:
7996 multi_sync_pre_init();
7998 case MULTI_SYNC_POST_BRIEFING:
7999 multi_sync_post_init();
8001 case MULTI_SYNC_INGAME:
8002 multi_ingame_sync_init();
8007 // perform the correct do frame functions
8008 void multi_sync_do()
8010 if(!(Game_mode & GM_STANDALONE_SERVER)){
8011 multi_sync_common_do();
8014 // if the netgame is ending, don't do any sync processing
8015 if(multi_endgame_ending()){
8019 // process appropriateliy
8020 switch(Multi_sync_mode){
8021 case MULTI_SYNC_PRE_BRIEFING:
8022 multi_sync_pre_do();
8024 case MULTI_SYNC_POST_BRIEFING:
8025 multi_sync_post_do();
8027 case MULTI_SYNC_INGAME:
8028 multi_ingame_sync_do();
8031 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8032 if(Multi_sync_bitmap != -1){
8033 gr_set_bitmap(Multi_sync_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8036 Multi_sync_window.draw();
8038 multi_sync_blit_screen_all();
8045 // perform the correct close functions
8046 void multi_sync_close()
8048 switch(Multi_sync_mode){
8049 case MULTI_SYNC_PRE_BRIEFING:
8050 multi_sync_pre_close();
8052 case MULTI_SYNC_POST_BRIEFING:
8053 multi_sync_post_close();
8055 case MULTI_SYNC_INGAME:
8056 multi_ingame_sync_close();
8060 if(!(Game_mode & GM_STANDALONE_SERVER)){
8061 multi_sync_common_close();
8065 const char *multi_sync_tooltip_handler(const char *str)
8067 if (!SDL_strcasecmp(str, NOX("@launch"))) {
8068 if (Multi_launch_button_created){
8069 return XSTR("Launch",801);
8076 void multi_sync_common_init()
8080 // create the interface window
8081 Multi_sync_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
8082 Multi_sync_window.set_mask_bmap(Multi_sync_bitmap_mask_fname[gr_screen.res]);
8083 Multi_sync_window.tooltip_handler = multi_sync_tooltip_handler;
8085 // load the background bitmap
8086 Multi_sync_bitmap = bm_load(Multi_sync_bitmap_fname[gr_screen.res]);
8087 if (Multi_sync_bitmap < 0) {
8088 // we failed to load the bitmap - this is very bad
8092 // initialize the player list data
8093 Multi_sync_plist_start = 0;
8094 Multi_sync_plist_count = 1; // we can pretty safely assume that there's one player in the game - me.
8096 Multi_launch_button_created = 0;
8098 // create the chatbox thingie (shouldn't be necesary to do this, but we'll put it in for good measure)
8101 // force the chatbox to be small
8102 chatbox_force_small();
8104 // initialize the common notification messaging
8105 multi_common_notify_init();
8107 // initialize the common mission info display area.
8108 multi_common_set_text("");
8110 // use the common interface palette
8111 multi_common_set_palette();
8113 // don't select any player yet.
8114 Multi_sync_player_select = -1;
8116 // determine how many of the 5 buttons to create
8117 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8118 Multi_sync_button_count = MULTI_SYNC_HOST_COUNT;
8120 Multi_sync_button_count = MULTI_SYNC_CLIENT_COUNT;
8122 // create the interface buttons
8123 for(idx=0; idx<Multi_sync_button_count; idx++){
8124 // create the object
8125 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);
8127 // set the sound to play when highlighted
8128 Multi_sync_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
8130 // set the ani for the button
8131 // this wierdness is necessary because cancel and kick buttons aren't drawn on the background bitmap,
8132 // so we have to load in frame 0, too (the file should exist)
8133 if ((idx == MS_CANCEL) || (idx == MS_KICK) || (idx == MS_LAUNCH)) {
8134 Multi_sync_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sync_buttons[gr_screen.res][idx].filename, 3, 0);
8136 Multi_sync_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sync_buttons[gr_screen.res][idx].filename);
8140 Multi_sync_buttons[gr_screen.res][idx].button.link_hotspot(Multi_sync_buttons[gr_screen.res][idx].hotspot);
8145 for(idx=0; idx<MULTI_SYNC_NUM_TEXT; idx++) {
8146 // don't create the "launch" button text just yet
8147 if(idx == MST_LAUNCH) {
8150 // multiplayer clients should ignore the kick button
8151 if(!MULTIPLAYER_MASTER && !MULTIPLAYER_HOST && (idx == MST_KICK)) {
8155 Multi_sync_window.add_XSTR(&Multi_sync_text[gr_screen.res][idx]);
8159 // create the player list select button and hide it
8160 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);
8161 Multi_sync_plist_button.hide();
8163 // set up hotkeys for certain common functions
8164 Multi_sync_buttons[gr_screen.res][MS_CANCEL].button.set_hotkey(SDLK_ESCAPE);
8167 void multi_sync_common_do()
8169 int k = chatbox_process();
8170 k = Multi_sync_window.process(k);
8172 // process the player list
8173 multi_sync_handle_plist();
8175 // process any button clicks
8176 multi_sync_check_buttons();
8178 // process any keypresses
8182 gamesnd_play_iface(SND_USER_SELECT);
8183 multi_quit_game(PROMPT_ALL);
8188 void multi_sync_common_close()
8190 // unload any bitmaps
8191 if(!bm_unload(Multi_sync_bitmap)){
8192 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_sync_bitmap_fname[gr_screen.res]));
8195 extern int Player_multi_died_check;
8196 Player_multi_died_check = -1;
8198 // destroy the UI_WINDOW
8199 Multi_sync_window.destroy();
8202 void multi_sync_blit_screen_all()
8209 // display any text in the info area
8210 multi_common_render_text();
8212 // display any pending notification messages
8213 multi_common_notify_do();
8215 // display any info about visible players
8217 for(idx=0;idx<MAX_PLAYERS;idx++){
8218 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
8219 // display his name and status
8220 multi_sync_display_name(Net_players[idx].player->callsign,count,idx);
8222 // get the player state
8223 state = Net_players[idx].state;
8225 // if we're ingame joining, show all other players except myself as "playing"
8226 if((Net_player != NULL) && (&Net_players[idx] != Net_player) && ((Multi_sync_mode == MULTI_SYNC_INGAME) || (Net_player->flags & NETINFO_FLAG_INGAME_JOIN)) ){
8227 state = NETPLAYER_STATE_IN_MISSION;
8231 case NETPLAYER_STATE_MISSION_LOADING:
8232 multi_sync_display_status(XSTR("Mission Loading",802),count);
8234 case NETPLAYER_STATE_INGAME_SHIP_SELECT: // I don't think its possible to see this state, but...
8235 multi_sync_display_status(XSTR("Ingame Ship Select",803),count);
8237 case NETPLAYER_STATE_DEBRIEF:
8238 multi_sync_display_status(XSTR("Debriefing",804),count);
8240 case NETPLAYER_STATE_MISSION_SYNC:
8241 multi_sync_display_status(XSTR("Mission Sync",805),count);
8243 case NETPLAYER_STATE_JOINING:
8244 multi_sync_display_status(XSTR("Joining",806),count);
8246 case NETPLAYER_STATE_JOINED:
8247 multi_sync_display_status(XSTR("Joined",807),count);
8249 case NETPLAYER_STATE_SLOT_ACK :
8250 multi_sync_display_status(XSTR("Slot Ack",808),count);
8252 case NETPLAYER_STATE_BRIEFING:
8253 multi_sync_display_status(XSTR("Briefing",765),count);
8255 case NETPLAYER_STATE_SHIP_SELECT:
8256 multi_sync_display_status(XSTR("Ship Select",809),count);
8258 case NETPLAYER_STATE_WEAPON_SELECT:
8259 multi_sync_display_status(XSTR("Weapon Select",810),count);
8261 case NETPLAYER_STATE_WAITING:
8262 multi_sync_display_status(XSTR("Waiting",811),count);
8264 case NETPLAYER_STATE_IN_MISSION:
8265 multi_sync_display_status(XSTR("In Mission",812),count);
8267 case NETPLAYER_STATE_MISSION_LOADED:
8268 multi_sync_display_status(XSTR("Mission Loaded",813),count);
8270 case NETPLAYER_STATE_DATA_LOAD:
8271 multi_sync_display_status(XSTR("Data loading",814),count);
8273 case NETPLAYER_STATE_SETTINGS_ACK:
8274 multi_sync_display_status(XSTR("Ready To Enter Mission",815),count);
8276 case NETPLAYER_STATE_INGAME_SHIPS:
8277 multi_sync_display_status(XSTR("Ingame Ships Packet Ack",816),count);
8279 case NETPLAYER_STATE_INGAME_WINGS:
8280 multi_sync_display_status(XSTR("Ingame Wings Packet Ack",817),count);
8282 case NETPLAYER_STATE_INGAME_RPTS:
8283 multi_sync_display_status(XSTR("Ingame Respawn Points Ack",818),count);
8285 case NETPLAYER_STATE_SLOTS_ACK:
8286 multi_sync_display_status(XSTR("Ingame Weapon Slots Ack",819),count);
8288 case NETPLAYER_STATE_POST_DATA_ACK:
8289 multi_sync_display_status(XSTR("Post Briefing Data Block Ack",820),count);
8291 case NETPLAYER_STATE_FLAG_ACK :
8292 multi_sync_display_status(XSTR("Flags Ack",821),count);
8294 case NETPLAYER_STATE_MT_STATS :
8295 multi_sync_display_status(XSTR("Parallax Online Stats Updating",822),count);
8297 case NETPLAYER_STATE_WSS_ACK :
8298 multi_sync_display_status(XSTR("Weapon Slots Ack",823),count);
8300 case NETPLAYER_STATE_HOST_SETUP :
8301 multi_sync_display_status(XSTR("Host setup",824),count);
8303 case NETPLAYER_STATE_DEBRIEF_ACCEPT:
8304 multi_sync_display_status(XSTR("Debrief accept",825),count);
8306 case NETPLAYER_STATE_DEBRIEF_REPLAY:
8307 multi_sync_display_status(XSTR("Debrief replay",826),count);
8309 case NETPLAYER_STATE_CPOOL_ACK:
8310 multi_sync_display_status(XSTR("Campaign ship/weapon ack",827),count);
8312 case NETPLAYER_STATE_MISSION_XFER :
8314 // server should display the pct completion of all clients
8315 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8316 if(Net_players[idx].s_info.xfer_handle != -1){
8317 pct_complete = multi_xfer_pct_complete(Net_players[idx].s_info.xfer_handle);
8319 // if we've got a valid xfer handle
8320 if((pct_complete >= 0.0) && (pct_complete <= 1.0)){
8321 sprintf(txt,XSTR("Mission file xfer %d%%",828),(int)(pct_complete * 100.0f));
8325 strcpy(txt,XSTR("Mission file xfer",829));
8328 strcpy(txt,XSTR("Mission file xfer",829));
8331 // clients should display only for themselves (which is the only thing they know)
8333 // if we've got a valid file xfer handle
8334 if((&Net_players[idx] == Net_player) && (Net_player->s_info.xfer_handle != -1)){
8335 pct_complete = multi_xfer_pct_complete(Net_player->s_info.xfer_handle);
8337 // if we've got a valid xfer handle
8338 if((pct_complete >= 0.0) && (pct_complete <= 1.0)){
8339 sprintf(txt,XSTR("Mission file xfer %d%%",828),(int)(pct_complete * 100.0f));
8343 strcpy(txt,XSTR("Mission file xfer",829));
8348 strcpy(txt,XSTR("Mission file xfer",829));
8353 multi_sync_display_status(txt,count);
8356 nprintf(("Network","Unhandled player state : %d !\n",Net_players[idx].state));
8363 // display the mission start countdown timer (if any)
8364 anim_render_all(GS_STATE_MULTI_MISSION_SYNC,flFrametime);
8366 // process and show the chatbox thingie
8370 Multi_sync_window.draw_tooltip();
8372 // display the voice status indicator
8373 multi_common_voice_display_status();
8376 void multi_sync_check_buttons()
8379 for(idx=0;idx<Multi_sync_button_count;idx++){
8380 // we only really need to check for one button pressed at a time, so we can break after
8382 if(Multi_sync_buttons[gr_screen.res][idx].button.pressed()){
8383 multi_sync_button_pressed(idx);
8389 void multi_sync_button_pressed(int n)
8394 gamesnd_play_iface(SND_USER_SELECT);
8395 multi_quit_game(PROMPT_ALL);
8398 // scroll the info box up
8399 case MS_SCROLL_INFO_UP:
8400 multi_common_scroll_text_up();
8403 // scroll the info box down
8404 case MS_SCROLL_INFO_DOWN:
8405 multi_common_scroll_text_down();
8410 // if we have a currently selected player, kick him
8411 if(Multi_sync_player_select >= 0){
8412 multi_kick_player(Multi_sync_player_select);
8416 // start the final launch countdown (post-sync only)
8418 multi_sync_start_countdown();
8421 // doesn't do anything
8427 void multi_sync_pre_init()
8431 Netgame.game_state = NETGAME_STATE_MISSION_SYNC;
8433 // if we're in teamplay mode, always force skill level to be medium
8434 if((Netgame.type_flags & NG_TYPE_TEAM) && (Net_player->flags & NETINFO_FLAG_GAME_HOST)){
8435 Netgame.options.skill_level = NUM_SKILL_LEVELS / 2;
8436 Game_skill_level = NUM_SKILL_LEVELS / 2;
8437 multi_options_update_netgame();
8440 // notify everyone of when we get here
8441 if(!(Game_mode & GM_STANDALONE_SERVER)){
8442 Net_player->state = NETPLAYER_STATE_MISSION_SYNC;
8443 send_netplayer_update_packet();
8446 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8448 ml_string(NOX("Server performing pre-briefing data sync"));
8450 if(!(Game_mode & GM_STANDALONE_SERVER)){
8451 multi_common_set_text(XSTR("Server performing sync\n",830),1);
8454 // maybe initialize tvt and squad war stuff
8455 if(Netgame.type_flags & NG_TYPE_TEAM){
8456 multi_team_level_init();
8459 // force everyone into this state
8460 send_netgame_update_packet();
8462 if(!(Game_mode & GM_STANDALONE_SERVER)){
8463 multi_common_add_text(XSTR("Send update packet\n",831),1);
8466 // setup some of my own data
8467 Net_player->flags |= NETINFO_FLAG_MISSION_OK;
8469 // do any output stuff
8470 if(Game_mode & GM_STANDALONE_SERVER){
8471 std_debug_set_standalone_state_string("Mission Sync");
8474 // do this here to insure we have the most up to date file checksum info
8475 multi_get_mission_checksum(Game_current_mission_filename);
8476 // parse_get_file_signature(Game_current_mission_filename);
8478 if(!(Game_mode & GM_STANDALONE_SERVER)){
8479 multi_common_add_text(XSTR("Got file signatures\n",832),1);
8482 if(!(Game_mode & GM_STANDALONE_SERVER)){
8483 multi_common_add_text(XSTR("Sending update state packet\n",833),1);
8487 // if we're not in team vs. team mode - set all player teams to be 0, and unset all captaincy bits
8488 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
8489 for(idx=0;idx<MAX_PLAYERS;idx++){
8490 Net_players[idx].p_info.team = 0;
8491 Net_players[idx].flags &= ~(NETINFO_FLAG_TEAM_CAPTAIN);
8495 // we aren't necessarily xferring the mission file yet
8496 SDL_assert(Net_player->s_info.xfer_handle == -1);
8498 // always call this for good measure
8499 multi_campaign_flush_data();
8501 Mission_sync_flags = 0;
8502 Multi_mission_loaded = 0;
8505 void multi_sync_pre_do()
8509 // If I'm the server, wait for everyone to arrive in this state, then begin transferring data, etc.
8510 // all servers (standalone or no, go through this)
8511 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8512 // wait for everyone to arrive, then request filesig from all of them
8513 if(multi_netplayer_state_check(NETPLAYER_STATE_MISSION_SYNC) && !(Mission_sync_flags & MS_FLAG_SENT_FILESIG) && !multi_endgame_ending()){
8514 send_file_sig_request(Netgame.mission_name);
8515 Mission_sync_flags |= MS_FLAG_SENT_FILESIG;
8517 if(!(Game_mode & GM_STANDALONE_SERVER)){
8518 multi_common_add_text(XSTR("Sent filesig request\n",834),1);
8522 // if we're waiting for players to receive files, then check on their status
8523 if((Mission_sync_flags & MS_FLAG_SENT_FILESIG) && !multi_netplayer_flag_check(NETINFO_FLAG_MISSION_OK) && !multi_endgame_ending()){
8524 for(idx=0;idx<MAX_PLAYERS;idx++){
8525 // if this player is in the process of xferring a file
8526 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].s_info.xfer_handle != -1)){
8527 switch(multi_xfer_get_status(Net_players[idx].s_info.xfer_handle)){
8528 // if it has successfully completed, set his ok flag
8529 case MULTI_XFER_SUCCESS :
8531 Net_players[idx].flags |= NETINFO_FLAG_MISSION_OK;
8533 // release the xfer instance handle
8534 multi_xfer_release_handle(Net_players[idx].s_info.xfer_handle);
8535 Net_players[idx].s_info.xfer_handle = -1;
8537 // if it has failed or timed-out, kick the player
8538 case MULTI_XFER_TIMEDOUT:
8539 case MULTI_XFER_FAIL:
8540 // release the xfer handle
8541 multi_xfer_release_handle(Net_players[idx].s_info.xfer_handle);
8542 Net_players[idx].s_info.xfer_handle = -1;
8545 multi_kick_player(idx, 0, KICK_REASON_BAD_XFER);
8552 // NOTE : this is now obsolete
8553 // once everyone is verified, do any data transfer necessary
8554 if(multi_netplayer_flag_check(NETINFO_FLAG_MISSION_OK) && !(Mission_sync_flags & MS_FLAG_DATA_DONE) && !multi_endgame_ending()){
8555 // do nothing for now
8556 Mission_sync_flags |= MS_FLAG_DATA_DONE;
8558 // send campaign pool data
8559 multi_campaign_send_pool_status();
8562 // wait for everyone to ack on campaign pool data (even in non-campaign situations)
8563 if((Mission_sync_flags & MS_FLAG_DATA_DONE) && !(Mission_sync_flags & MS_FLAG_CAMP_DONE) && !multi_endgame_ending()){
8564 // check to see if everyone has acked the campaign pool data
8565 if(multi_netplayer_state_check(NETPLAYER_STATE_CPOOL_ACK)){
8566 Mission_sync_flags |= MS_FLAG_CAMP_DONE;
8570 // once everyone is verified, tell them to load the mission
8571 // also make sure to load the mission myself _AFTER_ telling everyone to do so. This makes the whole process
8572 // move along faster
8573 if((Mission_sync_flags & MS_FLAG_CAMP_DONE) && !(Mission_sync_flags & MS_FLAG_SENT_LOAD) && !multi_endgame_ending()){
8574 send_netplayer_load_packet(NULL);
8575 Mission_sync_flags |= MS_FLAG_SENT_LOAD;
8577 if(!(Game_mode & GM_STANDALONE_SERVER)){
8578 multi_common_add_text(XSTR("Sent load packet\n",835),1);
8581 // load the mission myself, as soon as possible
8582 if(!Multi_mission_loaded){
8583 nprintf(("Network","Server loading mission..."));
8585 // update everyone about my status
8586 Net_player->state = NETPLAYER_STATE_MISSION_LOADING;
8587 send_netplayer_update_packet();
8589 game_start_mission();
8591 nprintf(("Network","Done\n"));
8592 Multi_mission_loaded = 1;
8594 // update everyone about my status
8595 Net_player->state = NETPLAYER_STATE_MISSION_LOADED;
8596 send_netplayer_update_packet();
8598 if(!(Game_mode & GM_STANDALONE_SERVER)){
8599 multi_common_add_text(XSTR("Loaded mission locally\n",836),1);
8604 // if everyone has loaded the mission, randomly assign players to ships
8605 if(multi_netplayer_state_check(NETPLAYER_STATE_MISSION_LOADED) && !(Mission_sync_flags & MS_FLAG_TS_SLOTS) && !multi_endgame_ending()){
8606 // call the team select function to assign players to their ships, wings, etc
8607 multi_ts_assign_players_all();
8608 send_netplayer_slot_packet();
8611 Mission_sync_flags |= MS_FLAG_TS_SLOTS;
8614 // if everyone has loaded the mission, move to the team select stage
8615 if(Sync_test && multi_netplayer_state_check(NETPLAYER_STATE_SLOT_ACK) && !(Mission_sync_flags & MS_FLAG_PUSHED_BRIEFING) && !multi_endgame_ending()){
8616 Netgame.game_state = NETGAME_STATE_BRIEFING;
8617 send_netgame_update_packet(); // this will push everyone into the next state
8619 // the standalone moves to his own wait state, whereas in the normal game mode, the server/host moves in to the
8620 // team select state
8621 if(Game_mode & GM_STANDALONE_SERVER){
8622 gameseq_post_event(GS_EVENT_MULTI_STD_WAIT);
8624 gameseq_post_event(GS_EVENT_START_GAME);
8627 Mission_sync_flags |= MS_FLAG_PUSHED_BRIEFING;
8629 if(!(Game_mode & GM_STANDALONE_SERVER)){
8630 multi_common_add_text(XSTR("Moving to team select\n",837),1);
8634 // clients should detect here if they are doing a file xfer and do error processing
8635 if((Net_player->state == NETPLAYER_STATE_MISSION_XFER) && (Net_player->s_info.xfer_handle != -1) && !multi_endgame_ending()){
8636 switch(multi_xfer_get_status(Net_player->s_info.xfer_handle)){
8637 // if it has successfully completed, set his ok flag
8638 case MULTI_XFER_SUCCESS :
8639 // release my xfer handle
8640 multi_xfer_release_handle(Net_player->s_info.xfer_handle);
8641 Net_player->s_info.xfer_handle = -1;
8644 // if it has failed or timed-out, kick the player
8645 case MULTI_XFER_TIMEDOUT:
8646 case MULTI_XFER_FAIL:
8647 // release my xfer handle
8648 multi_xfer_release_handle(Net_player->s_info.xfer_handle);
8649 Net_player->s_info.xfer_handle = -1;
8651 // leave the game qith an error code
8652 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_XFER_FAIL);
8659 if(!(Game_mode & GM_STANDALONE_SERVER)){
8661 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8662 if(Multi_sync_bitmap != -1){
8663 gr_set_bitmap(Multi_sync_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8666 Multi_sync_window.draw();
8668 multi_sync_blit_screen_all();
8674 void multi_sync_pre_close()
8676 // at this point, we should shut down any file xfers...
8677 if(Net_player->s_info.xfer_handle != -1){
8678 nprintf(("Network","WARNING - killing file xfer while leaving mission sync state!!!\n"));
8680 multi_xfer_abort(Net_player->s_info.xfer_handle);
8681 Net_player->s_info.xfer_handle = -1;
8685 void multi_sync_post_init()
8687 multi_reset_timestamps();
8689 Multi_state_timestamp = timestamp(0);
8692 ml_string(NOX("Performing post-briefing data sync"));
8694 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8695 multi_common_add_text(XSTR("Server performing sync\n",830),1);
8697 multi_common_add_text(XSTR("Client performing sync\n",838),1);
8700 // everyone should re-initialize these
8701 init_multiplayer_stats();
8703 // reset all sequencing info
8704 multi_oo_reset_sequencing();
8706 // if I am not the master of the game, then send the firing information for my ship
8708 if ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) ){
8709 send_firing_info_packet();
8712 // if I'm not a standalone server, load up the countdown stuff
8713 if(!(Game_mode & GM_STANDALONE_SERVER)){
8714 Multi_sync_countdown_anim = NULL;
8715 Multi_sync_countdown_instance = NULL;
8716 Multi_sync_countdown_anim = anim_load(Multi_sync_countdown_fname[gr_screen.res]);
8717 if(Multi_sync_countdown_anim == NULL){
8718 nprintf(("General","WARNING!, Could not load countdown animation %s!\n",Multi_sync_countdown_fname[gr_screen.res]));
8722 // create objects for all permanent observers
8723 multi_obs_level_init();
8725 // clear the game start countdown timer
8726 Multi_sync_countdown_timer = -1.0f;
8727 Multi_sync_countdown = -1;
8729 // if this is a team vs. team mission, mark all ship teams appropriately
8730 if(Netgame.type_flags & NG_TYPE_TEAM){
8731 multi_team_mark_all_ships();
8734 Mission_sync_flags = 0;
8735 Multi_sync_launch_pressed = 0;
8738 #define MULTI_POST_TIMESTAMP 7000
8740 extern int create_wings();
8742 void multi_sync_post_do()
8746 // only if the host is also the master should he be doing this (non-standalone situation)
8747 if ( Net_player->flags & NETINFO_FLAG_AM_MASTER ) {
8749 // once everyone gets to this screen, send them the ship classes of all ships.
8750 if(multi_netplayer_state_check(NETPLAYER_STATE_WAITING) && !(Mission_sync_flags & MS_FLAG_POST_DATA)) {
8751 // only the host should ever do this
8752 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8753 // at this point we want to delete all necessary ships, change all necessary ship classes, and set all weapons up
8754 multi_ts_create_wings();
8756 // update player ets settings
8757 for(idx=0;idx<MAX_PLAYERS;idx++){
8758 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].player->objnum != -1)){
8759 multi_server_update_player_weapons(&Net_players[idx],&Ships[Objects[Net_players[idx].player->objnum].instance]);
8764 // note that this is done a little differently for standalones and nonstandalones
8765 send_post_sync_data_packet();
8767 multi_common_add_text(XSTR("Sending post briefing block information\n",839),1);
8769 Mission_sync_flags |= MS_FLAG_POST_DATA;
8772 // send weapon slots data
8773 if(multi_netplayer_state_check(NETPLAYER_STATE_POST_DATA_ACK) && !(Mission_sync_flags & MS_FLAG_WSS_SLOTS)) {
8774 // note that this is done a little differently for standalones and nonstandalones
8775 if(Netgame.type_flags & NG_TYPE_TEAM){
8776 send_wss_slots_data_packet(0,0);
8777 send_wss_slots_data_packet(1,1);
8779 send_wss_slots_data_packet(0,1);
8782 multi_common_add_text(XSTR("Sending weapon slots information\n",840),1);
8784 Mission_sync_flags |= MS_FLAG_WSS_SLOTS;
8787 // once weapon information is received, send player settings info
8788 if ( multi_netplayer_state_check(NETPLAYER_STATE_WSS_ACK) && !(Mission_sync_flags & MS_FLAG_PSETTINGS)) {
8789 send_player_settings_packet();
8791 // server (specifically, the standalone), should set this here
8792 Net_player->state = NETPLAYER_STATE_SETTINGS_ACK;
8793 send_netplayer_update_packet();
8795 multi_common_add_text(XSTR("Sending player settings packets\n",841),1);
8797 Mission_sync_flags |= MS_FLAG_PSETTINGS;
8800 // check to see if the countdown timer has started and act appropriately
8801 if( Multi_sync_countdown_timer > -1.0f ) {
8803 // increment by frametime.
8804 Multi_sync_countdown_timer += flFrametime;
8806 // if the animation is not playing, start it
8807 if(!(Game_mode & GM_STANDALONE_SERVER) && (Multi_sync_countdown_instance == NULL) && (Multi_sync_countdown_anim != NULL)){
8808 anim_play_struct aps;
8810 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]);
8811 aps.screen_id = GS_STATE_MULTI_MISSION_SYNC;
8812 aps.framerate_independent = 1;
8814 Multi_sync_countdown_instance = anim_play(&aps);
8817 // if the next second has expired
8818 if( Multi_sync_countdown_timer >= 1.0f ) {
8820 Multi_sync_countdown--;
8821 Multi_sync_countdown_timer = 0.0f;
8823 // if the countdown has reached 0, launch the mission
8824 if(Multi_sync_countdown == 0){
8825 Multi_sync_countdown_timer = -1.0f;
8827 Multi_sync_launch_pressed = 0;
8828 multi_sync_launch();
8830 // otherwise send a countdown packet
8832 send_countdown_packet(Multi_sync_countdown);
8837 // jump into the mission myself
8838 if((Multi_sync_countdown == 0) && multi_netplayer_state_check(NETPLAYER_STATE_IN_MISSION)){
8839 if(!((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !Multi_sync_launch_pressed)){
8840 Net_player->state = NETPLAYER_STATE_IN_MISSION;
8841 Netgame.game_state = NETGAME_STATE_IN_MISSION;
8842 gameseq_post_event(GS_EVENT_ENTER_GAME);
8844 multi_common_add_text(XSTR("Moving into game\n",842),1);
8848 // maybe start the animation countdown
8849 if((Multi_sync_countdown >= 0) && (Multi_sync_countdown_instance == NULL) && (Multi_sync_countdown_anim != NULL)){
8850 anim_play_struct aps;
8852 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]);
8853 aps.screen_id = GS_STATE_MULTI_MISSION_SYNC;
8854 aps.framerate_independent = 1;
8856 Multi_sync_countdown_instance = anim_play(&aps);
8860 // host - specific stuff
8861 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8862 // create the launch button so the host can click
8863 if( Sync_test && multi_netplayer_state_check(NETPLAYER_STATE_SETTINGS_ACK) ){
8864 multi_sync_create_launch_button();
8869 if(!(Game_mode & GM_STANDALONE_SERVER)){
8871 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8872 if(Multi_sync_bitmap != -1){
8873 gr_set_bitmap(Multi_sync_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8876 Multi_sync_window.draw();
8878 multi_sync_blit_screen_all();
8884 void multi_sync_post_close()
8888 // if I'm not a standalone server, unload up the countdown stuff
8889 if(!(Game_mode & GM_STANDALONE_SERVER)){
8890 // release all rendering animation instances (should only be 1)
8891 anim_release_all_instances(GS_STATE_MULTI_MISSION_SYNC);
8892 Multi_sync_countdown_instance = NULL;
8894 // free up the countdown animation
8895 if(Multi_sync_countdown_anim != NULL){
8896 anim_free(Multi_sync_countdown_anim);
8897 Multi_sync_countdown_anim = NULL;
8901 // all players should reset sequencing
8902 for(idx=0;idx<MAX_PLAYERS;idx++){
8903 if(Net_player->flags & NETINFO_FLAG_CONNECTED){
8904 Net_players[idx].client_cinfo_seq = 0;
8905 Net_players[idx].client_server_seq = 0;
8909 // multiplayer dogfight
8910 multi_df_level_pre_enter();
8912 // clients should clear obj_pair array and add pair for themselves
8914 if ( MULTIPLAYER_CLIENT ) {
8916 obj_add_pairs( OBJ_INDEX(Player_obj) );
8921 void multi_sync_display_name(const char *name, int index, int np_index)
8923 char fit[CALLSIGN_LEN];
8925 // make sure the string actually fits
8928 // if we're in team vs. team mode
8929 if(Netgame.type_flags & NG_TYPE_TEAM){
8930 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]);
8932 // if this is the currently selected player, draw him highlighted
8933 if(np_index == Multi_sync_player_select){
8934 gr_set_color_fast(&Color_text_selected);
8936 gr_set_color_fast(&Color_text_normal);
8940 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);
8942 // blit his team icon
8944 if(Net_players[np_index].p_info.team == 0){
8945 // blit the team captain icon
8946 if(Net_players[np_index].flags & NETINFO_FLAG_TEAM_CAPTAIN){
8947 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
8948 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8949 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);
8952 // normal team member icon
8954 if(Multi_common_icons[MICON_TEAM0] != -1){
8955 gr_set_bitmap(Multi_common_icons[MICON_TEAM0], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8956 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);
8961 else if(Net_players[np_index].p_info.team == 1){
8962 // blit the team captain icon
8963 if(Net_players[np_index].flags & NETINFO_FLAG_TEAM_CAPTAIN){
8964 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
8965 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8966 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);
8969 // normal team member icon
8971 if(Multi_common_icons[MICON_TEAM1] != -1){
8972 gr_set_bitmap(Multi_common_icons[MICON_TEAM1], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8973 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);
8978 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]);
8980 // if this is the currently selected player, draw him highlighted
8981 if(np_index == Multi_sync_player_select){
8982 gr_set_color_fast(&Color_text_selected);
8984 gr_set_color_fast(&Color_text_normal);
8988 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);
8991 // maybe blit his CD status icon
8992 if((Net_players[np_index].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
8993 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8994 gr_bitmap(Ms_status_coords[gr_screen.res][MS_X_COORD], Ms_status_coords[gr_screen.res][MS_Y_COORD] + (index * 10));
8998 void multi_sync_display_status(const char *status, int index)
9002 // make sure the string actually fits
9003 strcpy(fit, status);
9004 gr_force_fit_string(fit, 250, Ms_status2_coords[gr_screen.res][MS_W_COORD] - 20);
9005 gr_set_color_fast(&Color_bright);
9006 gr_string(Ms_status2_coords[gr_screen.res][MS_X_COORD], Ms_status2_coords[gr_screen.res][MS_Y_COORD] + (index * 10), fit);
9009 void multi_sync_force_start_pre()
9012 int want_state = NETPLAYER_STATE_SLOT_ACK; // kick any players who are still in this state
9014 // go through the player list and boot anyone who isn't in the right state
9015 for(idx=0;idx<MAX_PLAYERS;idx++){
9016 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx]) && (Net_players[idx].state == want_state)){
9017 multi_kick_player(idx,0);
9022 void multi_sync_force_start_post()
9026 int num_kill_states;
9028 // determine the state we want all players in so that we can find those who are not in the state
9029 kill_state[0] = NETPLAYER_STATE_BRIEFING;
9030 kill_state[1] = NETPLAYER_STATE_SHIP_SELECT;
9031 kill_state[2] = NETPLAYER_STATE_WEAPON_SELECT;
9032 num_kill_states = 3;
9034 // go through the player list and boot anyone who isn't in the right state
9035 for(idx=0;idx<MAX_PLAYERS;idx++){
9036 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx])){
9037 // check against all kill state
9038 for(idx2 = 0;idx2<num_kill_states;idx2++){
9039 if(Net_players[idx].state == kill_state[idx2]){
9040 multi_kick_player(idx,0);
9048 void multi_sync_start_countdown()
9050 // don't allow repeat button presses
9051 if(Multi_sync_launch_pressed){
9055 Multi_sync_launch_pressed = 1;
9057 // if I'm the server, begin the countdown
9058 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
9059 gamesnd_play_iface(SND_COMMIT_PRESSED);
9060 Multi_sync_countdown_timer = 0.0f;
9061 Multi_sync_countdown = MULTI_SYNC_COUNTDOWN_TIME;
9063 // send an initial countdown value
9064 send_countdown_packet(Multi_sync_countdown);
9066 // otherwise send the "start countdown" packet to the standalone
9068 SDL_assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
9069 send_countdown_packet(-1);
9073 void multi_sync_launch()
9075 // don't allow repeat button presses
9076 if(Multi_sync_launch_pressed){
9080 Multi_sync_launch_pressed = 1;
9083 ml_printf(NOX("Entering mission %s"), Game_current_mission_filename);
9085 // tell everyone to jump into the mission
9086 send_jump_into_mission_packet();
9087 Multi_state_timestamp = timestamp(MULTI_POST_TIMESTAMP);
9089 // set the # of players at the start of the mission
9090 Multi_num_players_at_start = multi_num_players();
9091 nprintf(("Network","# of players at start of mission : %d\n", Multi_num_players_at_start));
9093 // initialize datarate limiting for all clients
9094 multi_oo_rate_init_all();
9096 multi_common_add_text(XSTR("Sending mission start packet\n",843),1);
9099 void multi_sync_create_launch_button()
9101 if (!Multi_launch_button_created) {
9102 // create the object
9103 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);
9105 // set the sound to play when highlighted
9106 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_highlight_action(common_play_highlight_sound);
9108 // set the ani for the button
9109 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_bmaps(Multi_sync_buttons[gr_screen.res][MS_LAUNCH].filename, 3, 0);
9112 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.link_hotspot(Multi_sync_buttons[gr_screen.res][MS_LAUNCH].hotspot);
9115 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_hotkey(KEY_CTRLED+SDLK_RETURN);
9118 // create the text for the button
9119 Multi_sync_window.add_XSTR(&Multi_sync_text[gr_screen.res][MST_LAUNCH]);
9122 // increment the button count so we start checking this one
9123 Multi_sync_button_count++;
9125 Multi_launch_button_created = 1;
9129 void multi_sync_handle_plist()
9135 // if we don't have a currently selected player, select one
9136 if((Multi_sync_player_select < 0) || !MULTI_CONNECTED(Net_players[Multi_sync_player_select])){
9137 for(idx=0;idx<MAX_PLAYERS;idx++){
9138 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
9139 Multi_sync_player_select = idx;
9145 // check for button list presses
9146 if(Multi_sync_plist_button.pressed()){
9147 // get the y mouse coords
9148 Multi_sync_plist_button.get_mouse_pos(NULL,&my);
9150 // get the index of the item selected
9151 select_index = my / 10;
9153 // if the index is greater than the current # connections, do nothing
9154 if(select_index > (multi_num_connections() - 1)){
9158 // translate into an absolute Net_players[] index (get the Nth net player)
9159 Multi_sync_player_select = -1;
9160 for(idx=0;idx<MAX_PLAYERS;idx++){
9161 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
9165 // if we've found the item we're looking for
9166 if(select_index < 0){
9167 Multi_sync_player_select = idx;
9172 // if for some bizarre reason, this is an invalid player, unselect him and wait for the next interation
9174 if((Multi_sync_player_select >= 0) && (!MULTI_CONNECTED(Net_players[Multi_sync_player_select]) || MULTI_STANDALONE(Net_players[Multi_sync_player_select])) ){
9175 Multi_sync_player_select = -1;
9181 // -------------------------------------------------------------------------------------------------------------
9183 // MULTIPLAYER DEBRIEF SCREEN
9186 // other relevant data
9187 int Multi_debrief_accept_hit;
9188 int Multi_debrief_replay_hit;
9190 // set if the server has left the game
9191 int Multi_debrief_server_left = 0;
9193 // if we've reported on TvT status all players are in the debrief
9194 int Multi_debrief_reported_tvt = 0;
9196 // whether stats are being accepted
9197 // -1 == no decision yet
9200 int Multi_debrief_stats_accept_code = -1;
9202 int Multi_debrief_server_framecount = 0;
9204 float Multi_debrief_time = 0.0f;
9205 float Multi_debrief_resend_time = 10.0f;
9207 void multi_debrief_init()
9211 Multi_debrief_time = 0.0f;
9212 Multi_debrief_resend_time = 10.0f;
9214 // do this to notify the standalone or the normal server that we're in the debrief state and ready to receive packets
9215 if (!(Net_player->flags & NETINFO_FLAG_AM_MASTER)) {
9216 Net_player->state = NETPLAYER_STATE_DEBRIEF;
9217 send_netplayer_update_packet();
9220 // unflag some stuff
9221 for(idx=0;idx<MAX_PLAYERS;idx++){
9222 if(MULTI_CONNECTED(Net_players[idx])){
9223 Net_players[idx].flags &= ~(NETINFO_FLAG_RESPAWNING | NETINFO_FLAG_LIMBO | NETINFO_FLAG_WARPING_OUT);
9227 // if text input mode is active, clear it
9228 multi_msg_text_flush();
9230 // the server has not left yet
9231 Multi_debrief_server_left = 0;
9233 // have not hit accept or replay yet
9234 Multi_debrief_accept_hit = 0;
9235 Multi_debrief_replay_hit = 0;
9237 // stats have not been accepted yet
9238 Multi_debrief_stats_accept_code = -1;
9240 // mark stats as not being store yet
9241 Netgame.flags &= ~(NG_FLAG_STORED_MT_STATS);
9243 // no report on TvT yet
9244 Multi_debrief_reported_tvt = 0;
9246 Multi_debrief_server_framecount = 0;
9249 void multi_debrief_do_frame()
9251 Multi_debrief_time += flFrametime;
9253 // set the netgame state to be debriefing when appropriate
9254 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)){
9255 Netgame.game_state = NETGAME_STATE_DEBRIEF;
9256 send_netgame_update_packet();
9259 // evaluate all server stuff
9260 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
9261 multi_debrief_server_process();
9265 void multi_debrief_close()
9267 if ( MULTIPLAYER_CLIENT && (Netgame.game_state == NETGAME_STATE_MISSION_SYNC) ){
9268 gamesnd_play_iface( SND_COMMIT_PRESSED );
9272 // handle optional mission loop
9273 void multi_maybe_set_mission_loop()
9275 int cur = Campaign.current_mission;
9276 if (Campaign.missions[cur].has_mission_loop) {
9277 SDL_assert(Campaign.loop_mission != CAMPAIGN_LOOP_MISSION_UNINITIALIZED);
9279 bool require_repeat_mission = (Campaign.current_mission == Campaign.next_mission);
9281 // check for (1) mission loop available, (2) dont have to repeat last mission
9282 if ( (Campaign.missions[cur].has_mission_loop && (Campaign.loop_mission != -1)) && !require_repeat_mission ) {
9285 debrief_assemble_optional_mission_popup_text(buffer, Campaign.missions[cur].mission_loop_desc);
9287 int choice = popup(0 , 2, POPUP_NO, POPUP_YES, buffer);
9289 Campaign.loop_enabled = 1;
9290 Campaign.next_mission = Campaign.loop_mission;
9295 // handle all cases for when the accept key is hit in a multiplayer debriefing
9296 void multi_debrief_accept_hit()
9298 // if we already accepted, do nothing
9299 // but he may need to hit accept again after the server has left the game, so allow this
9300 if(Multi_debrief_accept_hit){
9304 // mark this so that we don't hit it again
9305 Multi_debrief_accept_hit = 1;
9307 gamesnd_play_iface(SND_COMMIT_PRESSED);
9309 // if the server has left the game, always just end the game.
9310 if(Multi_debrief_server_left){
9311 if(!multi_quit_game(PROMPT_ALL)){
9312 Multi_debrief_server_left = 1;
9313 Multi_debrief_accept_hit = 0;
9315 Multi_debrief_server_left = 0;
9318 // query the host and see if he wants to accept stats
9319 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
9320 // if we're on a tracker game, he gets no choice for storing stats
9321 if(MULTI_IS_TRACKER_GAME){
9322 multi_maybe_set_mission_loop();
9324 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));
9326 // evaluate the result
9331 Multi_debrief_accept_hit = 0;
9334 // set the accept code to be "not accepting"
9336 multi_debrief_stats_toss();
9337 multi_maybe_set_mission_loop();
9340 // accept the stats and continue
9342 multi_debrief_stats_accept();
9343 multi_maybe_set_mission_loop();
9349 // set my netplayer state to be "debrief_accept", and be done with it
9350 Net_player->state = NETPLAYER_STATE_DEBRIEF_ACCEPT;
9351 send_netplayer_update_packet();
9355 // handle all cases for when the escape key is hit in a multiplayer debriefing
9356 void multi_debrief_esc_hit()
9360 // if the server has left
9361 if(Multi_debrief_server_left){
9362 multi_quit_game(PROMPT_ALL);
9367 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
9368 // if the stats have already been accepted
9369 if((Multi_debrief_stats_accept_code != -1) || (MULTI_IS_TRACKER_GAME)){
9370 multi_quit_game(PROMPT_HOST);
9372 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));
9374 // evaluate the result
9381 // set the accept code to be "not accepting"
9383 multi_debrief_stats_toss();
9384 multi_quit_game(PROMPT_NONE);
9387 // accept the stats and continue
9389 multi_debrief_stats_accept();
9390 multi_quit_game(PROMPT_NONE);
9395 // if the stats haven't been accepted yet, or this is a tracker game
9396 if((Multi_debrief_stats_accept_code == -1) && !(MULTI_IS_TRACKER_GAME)){
9397 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));
9399 // evaluate the result
9401 multi_quit_game(PROMPT_NONE);
9404 // otherwise go through the normal endgame channels
9406 multi_quit_game(PROMPT_ALL);
9411 void multi_debrief_replay_hit()
9413 // only the host should ever get here
9414 SDL_assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
9416 // if the button was already pressed, do nothing
9417 if(Multi_debrief_accept_hit){
9421 // same as hittin the except button except no stats are kept
9422 Multi_debrief_accept_hit = 1;
9424 // mark myself as being in the replay state so we know what to do next
9425 Net_player->state = NETPLAYER_STATE_DEBRIEF_REPLAY;
9426 send_netplayer_update_packet();
9429 // call this when the server has left and we would otherwise be saying "contact lost with server
9430 void multi_debrief_server_left()
9433 Multi_debrief_server_left = 1;
9435 // undo any "accept" hit so that clients can hit accept again to leave
9436 Multi_debrief_accept_hit = 0;
9439 void multi_debrief_stats_accept()
9441 // don't do anything if we've already accepted
9442 if(Multi_debrief_stats_accept_code != -1){
9446 Multi_debrief_stats_accept_code = 1;
9448 // if we're the host, and we're on a standalone, tell the standalone to begin the stats storing process
9449 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) || (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
9450 // send a packet to the players telling them to store their stats
9451 send_store_stats_packet(1);
9454 // add a chat line saying "stats have been accepted"
9455 multi_display_chat_msg(XSTR("<stats have been accepted>",850),0,0);
9458 ml_string(NOX("Stats stored"));
9461 void multi_debrief_stats_toss()
9463 // don't do anything if we've already accepted
9464 if(Multi_debrief_stats_accept_code != -1){
9468 Multi_debrief_stats_accept_code = 0;
9470 // if we're the host, and we're on a standalone, tell everyone to "toss" stats
9471 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) || (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
9472 // send a packet to the players telling them to store their stats
9473 send_store_stats_packet(0);
9476 // add a chat line saying "stats have been accepted"
9477 multi_display_chat_msg(XSTR("<stats have been tossed>",851),0,0);
9480 ml_string(NOX("Stats tossed"));
9483 int multi_debrief_stats_accept_code()
9485 return Multi_debrief_stats_accept_code;
9488 void multi_debrief_server_process()
9491 int player_status,other_status;
9493 Multi_debrief_server_framecount++;
9495 // if we're > 10 seconds into the debrief and not everyone is here, try warping everyone out again
9496 if((Multi_debrief_time >= Multi_debrief_resend_time) && !multi_netplayer_state_check3(NETPLAYER_STATE_DEBRIEF, NETPLAYER_STATE_DEBRIEF_ACCEPT, NETPLAYER_STATE_DEBRIEF_REPLAY, 1)){
9497 // find all players who are not in the debrief state and hit them with the endgame packet
9498 for(idx=0; idx<MAX_PLAYERS; idx++){
9499 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)) ){
9500 send_endgame_packet(&Net_players[idx]);
9505 Multi_debrief_resend_time += 7.0f;
9508 // evaluate the status of all players in the game (0 == not ready, 1 == ready to continue, 2 == ready to replay)
9511 // check all players
9512 for(idx=0;idx<MAX_PLAYERS;idx++){
9513 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_HOST(Net_players[idx])){
9514 if(Net_players[idx].state != NETPLAYER_STATE_DEBRIEF_ACCEPT){
9521 // if we haven't already reported TvT results
9522 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)){
9523 multi_team_report();
9524 Multi_debrief_reported_tvt = 1;
9527 // if all other players are good to go, check the host
9529 // if he is ready to continue
9530 if(Netgame.host->state == NETPLAYER_STATE_DEBRIEF_ACCEPT){
9533 // if he wants to replay the mission
9534 else if(Netgame.host->state == NETPLAYER_STATE_DEBRIEF_REPLAY){
9537 // if he is not ready
9542 // if all players are _not_ good to go
9547 // if we're in the debriefing state in a campaign mode, process accordingly
9548 if(Netgame.campaign_mode == MP_CAMPAIGN){
9549 multi_campaign_do_debrief(player_status);
9551 // otherwise process as normal (looking for all players to be ready to go to the next mission
9553 if(player_status == 1){
9554 multi_flush_mission_stuff();
9556 // set the netgame state to be forming and continue
9557 Netgame.game_state = NETGAME_STATE_FORMING;
9558 send_netgame_update_packet();
9560 // move to the proper state
9561 if(Game_mode & GM_STANDALONE_SERVER){
9562 gameseq_post_event(GS_EVENT_STANDALONE_MAIN);
9564 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
9567 multi_reset_timestamps();
9568 } else if(player_status == 2){
9569 multi_flush_mission_stuff();
9571 // tell everyone to move into the pre-briefing sync state
9572 Netgame.game_state = NETGAME_STATE_MISSION_SYNC;
9573 send_netgame_update_packet();
9575 // move back to the mission sync screen for the same mission again
9576 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
9577 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
9579 multi_reset_timestamps();
9585 // -------------------------------------------------------------------------------------------------------------
9587 // MULTIPLAYER PASSWORD POPUP
9592 static const char *Multi_pwd_bitmap_fname[GR_NUM_RESOLUTIONS] = {
9593 "Password", // GR_640
9594 "2_Password" // GR_1024
9597 static const char *Multi_pwd_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
9598 "Password-M", // GR_640
9599 "2_Password-M" // GR_1024
9604 // constants for coordinate lookup
9605 #define MPWD_X_COORD 0
9606 #define MPWD_Y_COORD 1
9607 #define MPWD_W_COORD 2
9608 #define MPWD_H_COORD 3
9611 #define MULTI_PWD_NUM_BUTTONS 2
9612 #define MPWD_CANCEL 0
9613 #define MPWD_COMMIT 1
9615 // password area defs
9616 int Mpwd_coords[GR_NUM_RESOLUTIONS][4] = {
9629 UI_WINDOW Multi_pwd_window; // the window object for the join screen
9630 UI_INPUTBOX Multi_pwd_passwd; // for Netgame.passwd
9631 int Multi_pwd_bitmap; // the background bitmap
9632 int Multi_passwd_background = -1;
9633 int Multi_passwd_done = -1;
9634 int Multi_passwd_running = 0;
9637 ui_button_info Multi_pwd_buttons[GR_NUM_RESOLUTIONS][MULTI_PWD_NUM_BUTTONS] = {
9640 ui_button_info("PWB_00", 402, 134, -1, -1, 0),
9641 ui_button_info("PWB_01", 450, 134, -1, -1, 1),
9643 ui_button_info("PWB_00", 411, 151, 405, 141, 0),
9644 ui_button_info("PWB_01", 460, 151, 465, 141, 1),
9648 ui_button_info("2_PWB_00", 659, 242, 649, 225, 0),
9649 ui_button_info("2_PWB_01", 737, 242, 736, 225, 1),
9655 #define MULTI_PWD_NUM_TEXT 3
9657 UI_XSTR Multi_pwd_text[GR_NUM_RESOLUTIONS][MULTI_PWD_NUM_TEXT] = {
9659 { "Cancel", 387, 400, 141, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[0][MPWD_CANCEL].button},
9660 { "Commit", 1062, 455, 141, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[0][MPWD_COMMIT].button},
9661 { "Enter Password", 1332, 149, 92, UI_XSTR_COLOR_GREEN, -1, NULL},
9664 { "Cancel", 387, 649, 225, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[1][MPWD_CANCEL].button},
9665 { "Commit", 1062, 736, 225, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[1][MPWD_COMMIT].button},
9666 { "Enter Password", 1332, 239, 148, UI_XSTR_COLOR_GREEN, -1, NULL},
9671 // initialize all graphics, etc
9672 void multi_passwd_init()
9676 // store the background as it currently is
9677 Multi_passwd_background = gr_save_screen();
9679 // create the interface window
9680 Multi_pwd_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
9681 Multi_pwd_window.set_mask_bmap(Multi_pwd_bitmap_mask_fname[gr_screen.res]);
9683 // load the background bitmap
9684 Multi_pwd_bitmap = bm_load(Multi_pwd_bitmap_fname[gr_screen.res]);
9685 if(Multi_pwd_bitmap < 0){
9686 // we failed to load the bitmap - this is very bad
9690 // create the interface buttons
9691 for(idx=0; idx<MULTI_PWD_NUM_BUTTONS; idx++){
9692 // create the object
9693 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);
9695 // set the sound to play when highlighted
9696 Multi_pwd_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
9698 // set the ani for the button
9699 Multi_pwd_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pwd_buttons[gr_screen.res][idx].filename);
9702 Multi_pwd_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pwd_buttons[gr_screen.res][idx].hotspot);
9707 for(idx=0; idx<MULTI_PWD_NUM_TEXT; idx++){
9708 Multi_pwd_window.add_XSTR(&Multi_pwd_text[gr_screen.res][idx]);
9712 // create the password input box
9713 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);
9714 Multi_pwd_passwd.set_focus();
9716 // link the enter key to ACCEPT
9717 Multi_pwd_buttons[gr_screen.res][MPWD_COMMIT].button.set_hotkey(SDLK_RETURN);
9719 Multi_passwd_done = -1;
9720 Multi_passwd_running = 1;
9723 // close down all graphics, etc
9724 void multi_passwd_close()
9726 // unload any bitmaps
9727 bm_release(Multi_pwd_bitmap);
9729 // destroy the UI_WINDOW
9730 Multi_pwd_window.destroy();
9732 // free up the saved background screen
9733 if(Multi_passwd_background >= 0){
9734 gr_free_screen(Multi_passwd_background);
9735 Multi_passwd_background = -1;
9738 Multi_passwd_running = 0;
9741 // process any button pressed
9742 void multi_passwd_process_buttons()
9744 // if the accept button was pressed
9745 if(Multi_pwd_buttons[gr_screen.res][MPWD_COMMIT].button.pressed()){
9746 gamesnd_play_iface(SND_USER_SELECT);
9747 Multi_passwd_done = 1;
9750 // if the cancel button was pressed
9751 if(Multi_pwd_buttons[gr_screen.res][MPWD_CANCEL].button.pressed()){
9752 gamesnd_play_iface(SND_USER_SELECT);
9753 Multi_passwd_done = 0;
9757 // run the passwd popup
9758 void multi_passwd_do(char *passwd)
9762 while(Multi_passwd_done == -1){
9763 // set frametime and run background stuff
9764 game_set_frametime(-1);
9765 game_do_state_common(gameseq_get_state());
9767 k = Multi_pwd_window.process();
9769 // process any keypresses
9772 // set this to indicate the user has cancelled for one reason or another
9773 Multi_passwd_done = 0;
9777 // if the input box text has changed
9778 if(Multi_pwd_passwd.changed()){
9780 Multi_pwd_passwd.get_text(passwd);
9783 // process any button pressed
9784 multi_passwd_process_buttons();
9786 // draw the background, etc
9789 if(Multi_passwd_background >= 0){
9790 gr_restore_screen(Multi_passwd_background);
9792 gr_set_bitmap(Multi_pwd_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
9794 Multi_pwd_window.draw();
9801 // bring up the password string popup, fill in passwd (return 1 if accept was pressed, 0 if cancel was pressed)
9802 int multi_passwd_popup(char *passwd)
9804 // if the popup is already running for some reason, don't do anything
9805 if(Multi_passwd_running){
9809 // initialize all graphics
9810 multi_passwd_init();
9813 multi_passwd_do(passwd);
9815 // shut everything down
9816 multi_passwd_close();
9818 return Multi_passwd_done;