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();
899 // unload the bitmap palette
900 void multi_common_unload_palette()
902 if(Multi_common_interface_palette != -1){
903 bm_unload(Multi_common_interface_palette);
904 Multi_common_interface_palette = -1;
908 void multi_common_verify_cd()
914 // -------------------------------------------------------------------------------------------------------------
916 // MULTIPLAYER JOIN SCREEN
919 #define MULTI_JOIN_NUM_BUTTONS 11
923 #define MULTI_JOIN_PALETTE "InterfacePalette"
925 static const char *Multi_join_bitmap_fname[GR_NUM_RESOLUTIONS] = {
926 "MultiJoin", // GR_640
927 "2_MultiJoin" // GR_1024
930 static const char *Multi_join_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
931 "MultiJoin-M", // GR_640
932 "2_MultiJoin-M" // GR_1024
938 const char *Mj_slider_name[GR_NUM_RESOLUTIONS] = {
942 int Mj_slider_coords[GR_NUM_RESOLUTIONS][4] = {
953 #define MJ_SCROLL_UP 0
954 #define MJ_SCROLL_DOWN 1
956 #define MJ_SCROLL_INFO_UP 3
957 #define MJ_SCROLL_INFO_DOWN 4
958 #define MJ_JOIN_OBSERVER 5
959 #define MJ_START_GAME 6
965 // uses MULTI_JOIN_REFRESH_TIME as its timestamp
966 int Multi_join_glr_stamp;
968 #define MULTI_JOIN_PING_TIME 15000 // how often we ping all the known servers
969 int Multi_join_ping_stamp;
970 UI_WINDOW Multi_join_window; // the window object for the join screen
971 UI_BUTTON Multi_join_select_button; // for selecting list items
973 UI_SLIDER2 Multi_join_slider; // handy dandy slider
975 int Multi_join_bitmap; // the background bitmap
977 ui_button_info Multi_join_buttons[GR_NUM_RESOLUTIONS][MULTI_JOIN_NUM_BUTTONS] = {
980 ui_button_info("MJ_00", 0, 85, -1, -1, 0),
981 ui_button_info("MJ_01", 0, 125, -1, -1, 1),
982 ui_button_info("MJ_03", 20, 324, -1, -1, 3),
983 ui_button_info("MJ_04", 0, 399, -1, -1, 4),
984 ui_button_info("MJ_05", 0, 436, -1, -1, 5),
985 ui_button_info("MJ_15", 450, 323, -1, -1, 15),
986 ui_button_info("MJ_10", 519, 323, -1, -1, 10),
987 ui_button_info("MJ_06", 574, 323, -1, -1, 6),
988 ui_button_info("MJ_08", 470, 427, -1, -1, 8),
989 ui_button_info("MJ_09", 448, 454, -1, -1, 9),
990 ui_button_info("MJ_07", 563, 411, -1, -1, 7),
992 ui_button_info( "MJ_00", 1, 57, -1, -1, 0 ), // scroll up
993 ui_button_info( "MJ_02", 1, 297, -1, -1, 2 ), // scroll down
994 ui_button_info( "MJ_03", 10, 338, 65, 364, 3 ), // refresh
995 ui_button_info( "MJ_04", 1, 405, -1, -1, 4 ), // scroll info up
996 ui_button_info( "MJ_05", 1, 446, -1, -1, 5 ), // scroll info down
997 ui_button_info( "MJ_06", 489, 339, -1, -1, 6 ), // join as observer
998 ui_button_info( "MJ_07", 538, 339, -1, -1, 7 ), // create game
999 ui_button_info( "MJ_08", 583, 339, 588, 376, 8 ), // cancel
1000 ui_button_info( "MJ_09", 534, 426, -1, -1, 9 ), // help
1001 ui_button_info( "MJ_10", 534, 454, -1, -1, 10 ), // options
1002 ui_button_info( "MJ_11", 571, 426, 589, 416, 11 ), // join
1006 ui_button_info( "2_MJ_00", 2, 92, -1, -1, 0 ), // scroll up
1007 ui_button_info( "2_MJ_02", 2, 475, -1, -1, 2 ), // scroll down
1008 ui_button_info( "2_MJ_03", 16, 541, 104, 582, 3 ), // refresh
1009 ui_button_info( "2_MJ_04", 2, 648, -1, -1, 4 ), // scroll info up
1010 ui_button_info( "2_MJ_05", 2, 713, -1, -1, 5 ), // scroll info down
1011 ui_button_info( "2_MJ_06", 783, 542, -1, -1, 6 ), // join as observer
1012 ui_button_info( "2_MJ_07", 861, 542, -1, -1, 7 ), // create game
1013 ui_button_info( "2_MJ_08", 933, 542, 588, 376, 8 ), // cancel
1014 ui_button_info( "2_MJ_09", 854, 681, -1, -1, 9 ), // help
1015 ui_button_info( "2_MJ_10", 854, 727, -1, -1, 10 ), // options
1016 ui_button_info( "2_MJ_11", 914, 681, 937, 668, 11 ), // join
1021 #define MULTI_JOIN_NUM_TEXT 13
1023 UI_XSTR Multi_join_text[GR_NUM_RESOLUTIONS][MULTI_JOIN_NUM_TEXT] = {
1025 {"Refresh", 1299, 65, 364, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_REFRESH].button},
1026 {"Join as", 1300, 476, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_JOIN_OBSERVER].button},
1027 {"Observer", 1301, 467, 385, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_JOIN_OBSERVER].button},
1028 {"Create", 1408, 535, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_START_GAME].button},
1029 {"Game", 1302, 541, 385, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_START_GAME].button},
1030 {"Cancel", 387, 588, 376, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[0][MJ_CANCEL].button},
1031 {"Help", 928, 479, 436, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_HELP].button},
1032 {"Options", 1036, 479, 460, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_OPTIONS].button},
1033 {"Join", 1303, 589, 416, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[0][MJ_ACCEPT].button},
1034 {"Status", 1304, 37, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
1035 {"Server", 1305, 116, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
1036 {"Players", 1306, 471, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
1037 {"Ping", 1307, 555, 37, UI_XSTR_COLOR_GREEN, -1, NULL}
1040 {"Refresh", 1299, 104, 582, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_REFRESH].button},
1041 {"Join as", 1300, 783, 602, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_JOIN_OBSERVER].button},
1042 {"Observer", 1301, 774, 611, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_JOIN_OBSERVER].button},
1043 {"Create", 1408, 868, 602, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_START_GAME].button},
1044 {"Game", 1302, 872, 611, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_START_GAME].button},
1045 {"Cancel", 387, 941, 602, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[1][MJ_CANCEL].button},
1046 {"Help", 928, 782, 699, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_HELP].button},
1047 {"Options", 1036, 782, 736, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_OPTIONS].button},
1048 {"Join", 1303, 937, 668, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[1][MJ_ACCEPT].button},
1049 {"Status", 1304, 60, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
1050 {"Server", 1305, 186, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
1051 {"Players", 1306, 753, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
1052 {"Ping", 1307, 888, 60, UI_XSTR_COLOR_GREEN, -1, NULL}
1057 // constants for coordinate look ups
1058 #define MJ_X_COORD 0
1059 #define MJ_Y_COORD 1
1060 #define MJ_W_COORD 2
1061 #define MJ_H_COORD 3
1063 #define MULTI_JOIN_SENT_WAIT 10000 // wait this long since a join was sent to allow another
1064 int Multi_join_sent_stamp;
1066 // game information text areas
1067 int Mj_max_game_items[GR_NUM_RESOLUTIONS] = {
1072 int Mj_list_y[GR_NUM_RESOLUTIONS] = {
1077 int Mj_status_coords[GR_NUM_RESOLUTIONS][4] = {
1086 int Mj_game_icon_coords[GR_NUM_RESOLUTIONS][3] = {
1095 int Mj_speed_coords[GR_NUM_RESOLUTIONS][4] = {
1104 int Mj_game_name_coords[GR_NUM_RESOLUTIONS][4] = {
1113 int Mj_players_coords[GR_NUM_RESOLUTIONS][4] = {
1122 int Mj_ping_coords[GR_NUM_RESOLUTIONS][4] = {
1131 // game speed labels
1132 #define MJ_NUM_SPEED_LABELS 5
1133 const char *Multi_join_speed_labels[MJ_NUM_SPEED_LABELS] = {
1140 color *Multi_join_speed_colors[MJ_NUM_SPEED_LABELS] = {
1143 &Color_bright_green,
1144 &Color_bright_green,
1148 int Mj_cd_coords[GR_NUM_RESOLUTIONS] = {
1153 // extents of the entire boundable game info region
1154 // NOTE : these numbers are completely empirical
1155 #define MJ_PING_GREEN 160
1156 #define MJ_PING_YELLOW 300
1158 int Mj_list_area_coords[GR_NUM_RESOLUTIONS][4] = {
1167 // PXO channel filter
1168 #define MJ_PXO_FILTER_Y 0
1170 // special chars to indicate various status modes for servers
1171 #define MJ_CHAR_STANDALONE "*"
1172 #define MJ_CHAR_CAMPAIGN "c"
1175 // various interface indices
1176 int Multi_join_list_start; // where to start displaying from
1177 active_game *Multi_join_list_start_item; // a pointer to the corresponding active_game
1178 int Multi_join_list_selected; // which item we have selected
1179 active_game *Multi_join_selected_item; // a pointer to the corresponding active_game
1181 // use this macro to modify the list start
1182 #define MJ_LIST_START_INC() do { Multi_join_list_start++; } while(0);
1183 #define MJ_LIST_START_DEC() do { Multi_join_list_start--; } while(0);
1184 #define MJ_LIST_START_SET(vl) do { Multi_join_list_start = vl; } while(0);
1186 // if we should be sending a join request at the end of the frame
1187 int Multi_join_should_send = -1;
1189 // master tracker details
1190 int Multi_join_frame_count; // keep a count of frames displayed
1191 int Multi_join_mt_tried_verify; // already tried verifying the pilot with the tracker
1193 // data stuff for auto joining a game
1194 #define MULTI_AUTOJOIN_JOIN_STAMP 2000
1195 #define MULTI_AUTOJOIN_QUERY_STAMP 2000
1197 int Multi_did_autojoin;
1198 net_addr Multi_autojoin_addr;
1199 int Multi_autojoin_join_stamp;
1200 int Multi_autojoin_query_stamp;
1203 join_request Multi_join_request;
1205 // LOCAL function definitions
1206 void multi_join_check_buttons();
1207 void multi_join_button_pressed(int n);
1208 void multi_join_display_games();
1209 void multi_join_blit_game_status(active_game *game, int y);
1210 void multi_join_load_tcp_addrs();
1211 void multi_join_do_netstuff();
1212 void multi_join_ping_all();
1213 void multi_join_process_select();
1214 void multi_join_list_scroll_up();
1215 void multi_join_list_scroll_down();
1216 void multi_join_list_page_up();
1217 void multi_join_list_page_down();
1218 active_game *multi_join_get_game(int n);
1219 void multi_join_cull_timeouts();
1220 void multi_join_handle_item_cull(active_game *item, int item_index);
1221 void multi_join_send_join_request(int as_observer);
1222 void multi_join_create_game();
1223 void multi_join_blit_top_stuff();
1224 int multi_join_maybe_warn();
1225 int multi_join_warn_pxo();
1226 void multi_join_blit_protocol();
1230 active_game ag, *newitem;;
1233 dc_get_arg(ARG_INT);
1234 for(idx=0; idx<Dc_arg_int; idx++){
1235 // stuff some fake info
1236 memset(&ag, 0, sizeof(active_game));
1237 sprintf(ag.name, "Game %d", idx);
1238 ag.version = MULTI_FS_SERVER_VERSION;
1239 ag.comp_version = MULTI_FS_SERVER_VERSION;
1240 ag.server_addr.addr[0] = (char)idx;
1241 ag.flags = (AG_FLAG_COOP | AG_FLAG_FORMING | AG_FLAG_STANDALONE);
1244 newitem = multi_update_active_games(&ag);
1246 // timestamp it so we get random timeouts
1247 if(newitem != NULL){
1248 // newitem->heard_from_timer = timestamp((int)frand_range(500.0f, 10000.0f));
1253 void multi_join_notify_new_game()
1256 // reset the # of items
1257 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);
1258 Multi_join_slider.force_currentItem(Multi_join_list_start);
1262 int multi_join_autojoin_do()
1264 // if we have an active game on the list, then return a positive value so that we
1265 // can join the game
1266 if ( Active_game_head && (Active_game_count > 0) ) {
1267 Multi_join_selected_item = Active_game_head;
1271 // send out a server_query again
1272 if ( timestamp_elapsed(Multi_autojoin_query_stamp) ) {
1273 send_server_query(&Multi_autojoin_addr);
1274 Multi_autojoin_query_stamp = timestamp(MULTI_AUTOJOIN_QUERY_STAMP);
1280 void multi_join_game_init()
1284 // do the multiplayer init stuff - multi_level_init() now does all net_player zeroing.
1285 // setup various multiplayer things
1286 SDL_assert( Game_mode & GM_MULTIPLAYER );
1287 SDL_assert( Net_player != NULL );
1289 switch (Multi_options_g.protocol) {
1291 ADDRESS_LENGTH = IPX_ADDRESS_LENGTH;
1292 PORT_LENGTH = IPX_PORT_LENGTH;
1296 ADDRESS_LENGTH = IP_ADDRESS_LENGTH;
1297 PORT_LENGTH = IP_PORT_LENGTH;
1306 memset( &Netgame, 0, sizeof(Netgame) );
1309 Net_player->flags |= NETINFO_FLAG_DO_NETWORKING;
1310 Net_player->player = Player;
1311 memcpy(&Net_player->p_info.addr,&Psnet_my_addr,sizeof(net_addr));
1313 // check for the existence of a CD
1314 multi_common_verify_cd();
1316 // load my local netplayer options
1317 multi_options_local_load(&Net_player->p_info.options, Net_player);
1323 common_set_interface_palette(MULTI_JOIN_PALETTE);
1326 // destroy any chatbox contents which previously existed (from another game)
1329 // create the interface window
1330 Multi_join_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
1331 Multi_join_window.set_mask_bmap(Multi_join_bitmap_mask_fname[gr_screen.res]);
1333 // load the background bitmap
1334 Multi_join_bitmap = bm_load(Multi_join_bitmap_fname[gr_screen.res]);
1335 if(Multi_join_bitmap < 0){
1336 // we failed to load the bitmap - this is very bad
1340 // intialize the endgame system
1341 multi_endgame_init();
1343 // initialize the common notification messaging
1344 multi_common_notify_init();
1346 // initialize the common text area
1347 multi_common_set_text("");
1349 // load and use the common interface palette
1350 multi_common_load_palette();
1351 multi_common_set_palette();
1353 // load the help overlay
1354 help_overlay_load(MULTI_JOIN_OVERLAY);
1355 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1357 // do TCP and VMT specific initialization
1358 if(Multi_options_g.protocol == NET_TCP){
1359 // if this is a TCP (non tracker) game, we'll load up our default address list right now
1360 multi_join_load_tcp_addrs();
1363 // initialize any and all timestamps
1364 Multi_join_glr_stamp = -1;
1365 Multi_join_ping_stamp = -1;
1366 Multi_join_sent_stamp = -1;
1368 // reset frame count
1369 Multi_join_frame_count = 0;
1371 // haven't tried to verify on the tracker yet.
1372 Multi_join_mt_tried_verify = 0;
1374 // clear our all game lists to save hassles
1375 multi_join_clear_game_list();
1377 // create the interface buttons
1378 for(idx=0; idx<MULTI_JOIN_NUM_BUTTONS; idx++){
1379 // create the object
1380 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);
1382 // set the sound to play when highlighted
1383 Multi_join_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
1385 // set the ani for the button
1386 Multi_join_buttons[gr_screen.res][idx].button.set_bmaps(Multi_join_buttons[gr_screen.res][idx].filename);
1389 Multi_join_buttons[gr_screen.res][idx].button.link_hotspot(Multi_join_buttons[gr_screen.res][idx].hotspot);
1394 for(idx=0; idx<MULTI_JOIN_NUM_TEXT; idx++){
1395 Multi_join_window.add_XSTR(&Multi_join_text[gr_screen.res][idx]);
1399 Multi_join_should_send = -1;
1401 // close any previously open chatbox
1404 // create the list item select button
1405 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);
1406 Multi_join_select_button.hide();
1410 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);
1413 // if starting a network game, then go to the create game screen
1414 if ( Cmdline_start_netgame ) {
1415 multi_join_create_game();
1416 } else if ( Cmdline_connect_addr != NULL ) {
1421 // joining a game. Send a join request to the given IP address, and wait for the return.
1422 memset( &Multi_autojoin_addr, 0, sizeof(net_addr) );
1423 Multi_autojoin_addr.type = NET_TCP;
1425 // create the address, looking out for port number at the end
1426 port_num = DEFAULT_GAME_PORT;
1427 p = strrchr(Cmdline_connect_addr, ':');
1431 port_num = (short)atoi(p);
1433 ip_addr = inet_addr(Cmdline_connect_addr);
1434 memcpy(Multi_autojoin_addr.addr, &ip_addr, 4);
1435 Multi_autojoin_addr.port = port_num;
1437 send_server_query(&Multi_autojoin_addr);
1438 Multi_autojoin_query_stamp = timestamp(MULTI_AUTOJOIN_QUERY_STAMP);
1439 Multi_did_autojoin = 0;
1443 void multi_join_clear_game_list()
1446 Multi_join_list_selected = -1;
1447 Multi_join_selected_item = NULL;
1448 MJ_LIST_START_SET(-1);
1449 Multi_join_list_start_item = NULL;
1451 // free up the active game list
1452 multi_free_active_games();
1454 // initialize the active game list
1455 Active_game_head = NULL;
1456 Active_game_count = 0;
1459 void multi_join_game_do_frame()
1461 // check the status of our reliable socket. If not valid, popup error and return to main menu
1462 // I put this code here to avoid nasty gameseq issues with states. Also, we will have nice
1463 // background for the popup
1464 if ( !psnet_rel_check() ) {
1465 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));
1466 gameseq_post_event(GS_EVENT_MAIN_MENU);
1470 // return here since we will be moving to the next stage anyway -- I don't want to see the backgrounds of
1471 // all the screens for < 1 second for every screen we automatically move to.
1472 if ( Cmdline_start_netgame ) {
1476 // when joining a network game, wait for the server query to come back, and then join the game
1477 if ( Cmdline_connect_addr != NULL ) {
1480 if ( !Multi_did_autojoin ) {
1481 rval = popup_till_condition(multi_join_autojoin_do, XSTR("&Cancel", 779), XSTR("Joining netgame", 1500) );
1483 // cancel was hit. Send the user back to the main hall
1484 gameseq_post_event(GS_EVENT_MAIN_MENU);
1485 Cmdline_connect_addr = NULL; // reset this value.
1488 // when we get here, we have the data -- join the game.
1489 multi_join_send_join_request(0);
1490 Multi_autojoin_join_stamp = timestamp(MULTI_AUTOJOIN_JOIN_STAMP);
1491 Multi_did_autojoin = 1;
1494 if ( timestamp_elapsed(Multi_autojoin_join_stamp) ) {
1495 multi_join_send_join_request(0);
1496 Multi_autojoin_join_stamp = timestamp(MULTI_AUTOJOIN_JOIN_STAMP);
1502 // reset the should send var
1503 Multi_join_should_send = -1;
1505 int k = Multi_join_window.process();
1507 // process any keypresses
1510 if(help_overlay_active(MULTI_JOIN_OVERLAY)){
1511 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1513 gameseq_post_event(GS_EVENT_MAIN_MENU);
1514 gamesnd_play_iface(SND_USER_SELECT);
1518 // page up the game list
1520 multi_join_list_page_up();
1522 Multi_join_slider.force_currentItem(Multi_join_list_start);
1527 multi_pinfo_popup(Net_player);
1530 // page down the game list
1532 multi_join_list_page_down();
1534 Multi_join_slider.force_currentItem(Multi_join_list_start);
1538 // send out a ping-all
1540 multi_join_ping_all();
1541 Multi_join_ping_stamp = timestamp(MULTI_JOIN_PING_TIME);
1544 // shortcut to start a game
1546 multi_join_create_game();
1549 // scroll the game list up
1551 multi_join_list_scroll_up();
1553 Multi_join_slider.force_currentItem(Multi_join_list_start);
1557 // scroll the game list down
1559 multi_join_list_scroll_down();
1561 Multi_join_slider.force_currentItem(Multi_join_list_start);
1566 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
1567 help_overlay_set_state(MULTI_JOIN_OVERLAY, 0);
1570 // do any network related stuff
1571 multi_join_do_netstuff();
1573 // process any button clicks
1574 multi_join_check_buttons();
1576 // process any list selection stuff
1577 multi_join_process_select();
1579 // draw the background, etc
1581 GR_MAYBE_CLEAR_RES(Multi_join_bitmap);
1582 if(Multi_join_bitmap != -1){
1583 gr_set_bitmap(Multi_join_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1586 Multi_join_window.draw();
1588 // display the active games
1589 multi_join_display_games();
1591 // display any text in the info area
1592 multi_common_render_text();
1594 // display any pending notification messages
1595 multi_common_notify_do();
1597 // blit the CD icon and any PXO filter stuff
1598 multi_join_blit_top_stuff();
1600 // draw the help overlay
1601 help_overlay_maybe_blit(MULTI_JOIN_OVERLAY);
1606 // if we are supposed to be sending a join request
1607 if(Multi_join_should_send != -1){
1608 multi_join_send_join_request(Multi_join_should_send);
1610 Multi_join_should_send = -1;
1612 // increment the frame count
1613 Multi_join_frame_count++;
1616 void multi_join_game_close()
1618 // unload any bitmaps
1619 if(!bm_unload(Multi_join_bitmap)){
1620 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_join_bitmap_fname[gr_screen.res]));
1623 // unload the help overlay
1624 help_overlay_unload(MULTI_JOIN_OVERLAY);
1626 // free up the active game list
1627 multi_free_active_games();
1629 // destroy the UI_WINDOW
1630 Multi_join_window.destroy();
1633 common_free_interface_palette();
1637 void multi_join_check_buttons()
1640 for(idx=0;idx<MULTI_JOIN_NUM_BUTTONS;idx++){
1641 // we only really need to check for one button pressed at a time, so we can break after
1643 if(Multi_join_buttons[gr_screen.res][idx].button.pressed()){
1644 multi_join_button_pressed(idx);
1650 void multi_join_button_pressed(int n)
1654 // if we're player PXO, go back there
1655 gameseq_post_event(GS_EVENT_MAIN_MENU);
1656 gamesnd_play_iface(SND_USER_SELECT);
1659 if(Active_game_count <= 0){
1660 multi_common_add_notify(XSTR("No games found!",757));
1661 gamesnd_play_iface(SND_GENERAL_FAIL);
1662 } else if(Multi_join_list_selected == -1){
1663 multi_common_add_notify(XSTR("No game selected!",758));
1664 gamesnd_play_iface(SND_GENERAL_FAIL);
1665 } else if((Multi_join_sent_stamp != -1) && !timestamp_elapsed(Multi_join_sent_stamp)){
1666 multi_common_add_notify(XSTR("Still waiting on previous join request!",759));
1667 gamesnd_play_iface(SND_GENERAL_FAIL);
1669 // otherwise, if he's already played PXO games, warn him
1671 if(Player->flags & PLAYER_FLAGS_HAS_PLAYED_PXO){
1672 if(!multi_join_warn_pxo()){
1678 // send the join request here
1679 SDL_assert(Multi_join_selected_item != NULL);
1681 // send a join request packet
1682 Multi_join_should_send = 0;
1684 gamesnd_play_iface(SND_COMMIT_PRESSED);
1690 if(!help_overlay_active(MULTI_JOIN_OVERLAY)){
1691 help_overlay_set_state(MULTI_JOIN_OVERLAY,1);
1693 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1697 // scroll the game list up
1699 multi_join_list_scroll_up();
1701 Multi_join_slider.force_currentItem(Multi_join_list_start);
1705 // scroll the game list down
1706 case MJ_SCROLL_DOWN:
1707 multi_join_list_scroll_down();
1709 Multi_join_slider.force_currentItem(Multi_join_list_start);
1713 // scroll the info text box up
1714 case MJ_SCROLL_INFO_UP:
1715 multi_common_scroll_text_up();
1718 // scroll the info text box down
1719 case MJ_SCROLL_INFO_DOWN:
1720 multi_common_scroll_text_down();
1723 // go to the options screen
1725 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
1728 // go to the start game screen
1730 multi_join_create_game();
1733 // refresh the game/server list
1735 gamesnd_play_iface(SND_USER_SELECT);
1736 broadcast_game_query();
1739 // join a game as an observer
1740 case MJ_JOIN_OBSERVER:
1741 if(Active_game_count <= 0){
1742 multi_common_add_notify(XSTR("No games found!",757));
1743 gamesnd_play_iface(SND_GENERAL_FAIL);
1744 } else if(Multi_join_list_selected == -1){
1745 multi_common_add_notify(XSTR("No game selected!",758));
1746 gamesnd_play_iface(SND_GENERAL_FAIL);
1747 } else if((Multi_join_sent_stamp != -1) && !timestamp_elapsed(Multi_join_sent_stamp)){
1748 multi_common_add_notify(XSTR("Still waiting on previous join request!",759));
1749 gamesnd_play_iface(SND_GENERAL_FAIL);
1751 // send the join request here
1752 SDL_assert(Multi_join_selected_item != NULL);
1754 Multi_join_should_send = 1;
1756 gamesnd_play_iface(SND_COMMIT_PRESSED);
1761 multi_common_add_notify(XSTR("Not implemented yet!",760));
1762 gamesnd_play_iface(SND_GENERAL_FAIL);
1767 // display all relevant info for active games
1768 void multi_join_display_games()
1770 active_game *moveup = Multi_join_list_start_item;
1774 int y_start = Mj_list_y[gr_screen.res];
1779 // blit the game status (including text and type icon)
1780 multi_join_blit_game_status(moveup,y_start);
1782 // get the connection type
1783 con_type = (moveup->flags & AG_FLAG_CONNECTION_SPEED_MASK) >> AG_FLAG_CONNECTION_BIT;
1784 if((con_type > 4) || (con_type < 0)){
1788 // display the connection speed
1790 strcpy(str, Multi_join_speed_labels[con_type]);
1791 gr_set_color_fast(Multi_join_speed_colors[con_type]);
1792 gr_string(Mj_speed_coords[gr_screen.res][MJ_X_COORD], y_start, str);
1794 // we'll want to have different colors for highlighted items, etc.
1795 if(moveup == Multi_join_selected_item){
1796 gr_set_color_fast(&Color_text_selected);
1798 gr_set_color_fast(&Color_text_normal);
1801 // display the game name, adding appropriate status chars
1803 if(moveup->flags & AG_FLAG_STANDALONE){
1804 strcat(str,MJ_CHAR_STANDALONE);
1806 if(moveup->flags & AG_FLAG_CAMPAIGN){
1807 strcat(str,MJ_CHAR_CAMPAIGN);
1810 // tack on the actual server name
1812 strcat(str,moveup->name);
1813 if(strlen(moveup->mission_name) > 0){
1815 strcat(str,moveup->mission_name);
1818 // make sure the string fits in the display area and draw it
1819 gr_force_fit_string(str,200,Mj_game_name_coords[gr_screen.res][MJ_W_COORD]);
1820 gr_string(Mj_game_name_coords[gr_screen.res][MJ_X_COORD],y_start,str);
1822 // display the ping time
1823 if(moveup->ping.ping_avg > 0){
1824 if(moveup->ping.ping_avg > 1000){
1825 gr_set_color_fast(&Color_bright_red);
1826 strcpy(str,XSTR("> 1 sec",761));
1828 // set the appropriate ping time color indicator
1829 if(moveup->ping.ping_avg > MJ_PING_YELLOW){
1830 gr_set_color_fast(&Color_bright_red);
1831 } else if(moveup->ping.ping_avg > MJ_PING_YELLOW){
1832 gr_set_color_fast(&Color_bright_yellow);
1834 gr_set_color_fast(&Color_bright_green);
1837 sprintf(str,"%d",moveup->ping.ping_avg);
1838 strcat(str,XSTR(" ms",762)); // [[ Milliseconds ]]
1841 gr_string(Mj_ping_coords[gr_screen.res][MJ_X_COORD],y_start,str);
1844 // display the number of players (be sure to center it)
1845 if(moveup == Multi_join_selected_item){
1846 gr_set_color_fast(&Color_text_selected);
1848 gr_set_color_fast(&Color_text_normal);
1850 sprintf(str,"%d",moveup->num_players);
1851 gr_get_string_size(&w,&h,str);
1852 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);
1856 moveup = moveup->next;
1857 } while((moveup != Active_game_head) && (count < Mj_max_game_items[gr_screen.res]));
1859 // if there are no items on the list, display this info
1861 gr_set_color_fast(&Color_bright);
1862 gr_string(Mj_game_name_coords[gr_screen.res][MJ_X_COORD] - 30,y_start,XSTR("<No game servers found>",763));
1866 void multi_join_blit_game_status(active_game *game, int y)
1869 char status_text[25];
1871 // blit the proper icon
1873 switch( game->flags & AG_FLAG_TYPE_MASK ){
1876 if(Multi_common_icons[MICON_COOP] != -1){
1877 gr_set_bitmap(Multi_common_icons[MICON_COOP], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1882 // team vs. team game
1884 if(Multi_common_icons[MICON_TVT] != -1){
1885 gr_set_bitmap(Multi_common_icons[MICON_TVT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1892 case AG_FLAG_DOGFIGHT:
1893 if(Multi_common_icons[MICON_DOGFIGHT] != -1){
1894 gr_set_bitmap(Multi_common_icons[MICON_DOGFIGHT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1900 // if we're supposed to draw a bitmap
1902 gr_bitmap(Mj_game_icon_coords[gr_screen.res][MJ_X_COORD],y-1);
1905 // blit the proper status text
1906 memset(status_text,0,25);
1908 switch( game->flags & AG_FLAG_STATE_MASK ){
1909 case AG_FLAG_FORMING:
1910 gr_set_color_fast(&Color_bright_green);
1911 strcpy(status_text,XSTR("Forming",764));
1913 case AG_FLAG_BRIEFING:
1914 gr_set_color_fast(&Color_bright_red);
1915 strcpy(status_text,XSTR("Briefing",765));
1917 case AG_FLAG_DEBRIEF:
1918 gr_set_color_fast(&Color_bright_red);
1919 strcpy(status_text,XSTR("Debrief",766));
1922 gr_set_color_fast(&Color_bright_red);
1923 strcpy(status_text,XSTR("Paused",767));
1925 case AG_FLAG_IN_MISSION:
1926 gr_set_color_fast(&Color_bright_red);
1927 strcpy(status_text,XSTR("Playing",768));
1930 gr_set_color_fast(&Color_bright);
1931 strcpy(status_text,XSTR("Unknown",769));
1934 gr_get_string_size(&str_w,NULL,status_text);
1935 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);
1938 // load in a list of active games from our tcp.cfg file
1939 void multi_join_load_tcp_addrs()
1941 char line[MAX_IP_STRING];
1946 // attempt to open the ip list file
1947 file = cfopen(IP_CONFIG_FNAME,"rt",CFILE_NORMAL,CF_TYPE_DATA);
1949 nprintf(("Network","Error loading tcp.cfg file!\n"));
1953 // free up any existing server list
1954 multi_free_server_list();
1956 // read in all the strings in the file
1957 while(!cfeof(file)){
1959 cfgets(line,MAX_IP_STRING,file);
1961 // strip off any newline character
1962 if(line[strlen(line) - 1] == '\n'){
1963 line[strlen(line) - 1] = '\0';
1966 // empty lines don't get processed
1967 if( (line[0] == '\0') || (line[0] == '\n') ){
1971 if ( !psnet_is_valid_ip_string(line) ) {
1972 nprintf(("Network","Invalid ip string (%s)\n",line));
1974 // copy the server ip address
1975 memset(&addr,0,sizeof(net_addr));
1976 addr.type = NET_TCP;
1977 psnet_string_to_addr(&addr,line);
1978 if ( addr.port == 0 ){
1979 addr.port = DEFAULT_GAME_PORT;
1982 // create a new server item on the list
1983 item = multi_new_server_item();
1985 memcpy(&item->server_addr,&addr,sizeof(net_addr));
1993 // do stuff like pinging servers, sending out requests, etc
1994 void multi_join_do_netstuff()
1996 // handle game query stuff
1997 if(Multi_join_glr_stamp == -1){
1998 broadcast_game_query();
2000 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
2001 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME_LOCAL);
2003 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME);
2006 // otherwise send out game query and restamp
2007 else if(timestamp_elapsed(Multi_join_glr_stamp)){
2008 broadcast_game_query();
2010 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
2011 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME_LOCAL);
2013 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME);
2017 // check to see if we've been accepted. If so, put up message saying so
2018 if ( Net_player->flags & (NETINFO_FLAG_ACCEPT_INGAME|NETINFO_FLAG_ACCEPT_CLIENT|NETINFO_FLAG_ACCEPT_HOST|NETINFO_FLAG_ACCEPT_OBSERVER) ) {
2019 multi_common_add_notify(XSTR("Accepted. Waiting for player data.",770));
2022 // check to see if any join packets we have sent have timed out
2023 if((Multi_join_sent_stamp != -1) && (timestamp_elapsed(Multi_join_sent_stamp))){
2024 Multi_join_sent_stamp = -1;
2025 multi_common_add_notify(XSTR("Join request timed out!",771));
2028 // check to see if we should be pinging everyone
2029 if((Multi_join_ping_stamp == -1) || (timestamp_elapsed(Multi_join_ping_stamp))){
2030 multi_join_ping_all();
2031 Multi_join_ping_stamp = timestamp(MULTI_JOIN_PING_TIME);
2035 multi_join_cull_timeouts();
2038 // evaluate a returned pong.
2039 void multi_join_eval_pong(net_addr *addr, fix pong_time)
2042 active_game *moveup = Active_game_head;
2047 if(psnet_same(&moveup->server_addr,addr)){
2049 multi_ping_eval_pong(&moveup->ping);
2053 moveup = moveup->next;
2055 } while(moveup != Active_game_head);
2058 // update the game's ping
2060 if(found && (moveup->ping_end > moveup->ping_start)){
2061 moveup->ping_time = f2fl(moveup->ping_end - moveup->ping_start);
2062 moveup->ping_start = -1;
2063 moveup->ping_end = -1;
2068 // ping all the server on the list
2069 void multi_join_ping_all()
2071 active_game *moveup = Active_game_head;
2076 moveup->ping_start = timer_get_fixed_seconds();
2077 moveup->ping_end = -1;
2078 send_ping(&moveup->server_addr);
2080 multi_ping_send(&moveup->server_addr,&moveup->ping);
2082 moveup = moveup->next;
2083 } while(moveup != Active_game_head);
2087 void multi_join_process_select()
2089 // if we don't have anything selected and there are items on the list - select the first one
2090 if((Multi_join_list_selected == -1) && (Active_game_count > 0)){
2091 Multi_join_list_selected = 0;
2092 Multi_join_selected_item = multi_join_get_game(0);
2093 MJ_LIST_START_SET(0);
2094 Multi_join_list_start_item = Multi_join_selected_item;
2096 // send a mission description request to this guy
2097 send_netgame_descript_packet(&Multi_join_selected_item->server_addr,0);
2098 multi_common_set_text("");
2100 // I sure hope this doesn't happen
2101 SDL_assert(Multi_join_selected_item != NULL);
2104 // otherwise see if he's clicked on an item
2105 else if(Multi_join_select_button.pressed() && (Active_game_count > 0)){
2107 Multi_join_select_button.get_mouse_pos(NULL,&y);
2109 if(item + Multi_join_list_start < Active_game_count){
2110 gamesnd_play_iface(SND_IFACE_MOUSE_CLICK);
2112 Multi_join_list_selected = item + Multi_join_list_start;
2113 Multi_join_selected_item = multi_join_get_game(Multi_join_list_selected);
2115 // I sure hope this doesn't happen
2116 SDL_assert(Multi_join_selected_item != NULL);
2118 // send a mission description request to this guy
2119 send_netgame_descript_packet(&Multi_join_selected_item->server_addr,0);
2120 multi_common_set_text("");
2124 // if he's double clicked, then select it and accept
2125 if(Multi_join_select_button.double_clicked()){
2127 Multi_join_select_button.get_mouse_pos(NULL,&y);
2129 if(item == Multi_join_list_selected){
2130 multi_join_button_pressed(MJ_ACCEPT);
2135 // return game n (0 based index)
2136 active_game *multi_join_get_game(int n)
2138 active_game *moveup = Active_game_head;
2145 moveup = moveup->next;
2146 while((moveup != Active_game_head) && (count != n)){
2147 moveup = moveup->next;
2150 if(moveup == Active_game_head){
2151 nprintf(("Network","Warning, couldn't find game item %d!\n",n));
2161 // scroll through the game list
2162 void multi_join_list_scroll_up()
2164 // if we're not at the beginning of the list, scroll up
2165 if(Multi_join_list_start_item != Active_game_head){
2166 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2168 MJ_LIST_START_DEC();
2170 gamesnd_play_iface(SND_SCROLL);
2172 gamesnd_play_iface(SND_GENERAL_FAIL);
2176 // scroll through the game list
2177 void multi_join_list_scroll_down()
2179 if((Active_game_count - Multi_join_list_start) > Mj_max_game_items[gr_screen.res]){
2180 Multi_join_list_start_item = Multi_join_list_start_item->next;
2182 MJ_LIST_START_INC();
2184 gamesnd_play_iface(SND_SCROLL);
2186 gamesnd_play_iface(SND_GENERAL_FAIL);
2190 void multi_join_list_page_up()
2192 // in this case, just set us to the beginning of the list
2193 if((Multi_join_list_start - Mj_max_game_items[gr_screen.res]) < 0){
2194 Multi_join_list_start_item = Active_game_head;
2196 MJ_LIST_START_SET(0);
2198 gamesnd_play_iface(SND_SCROLL);
2200 // otherwise page the whole thing up
2202 for(idx=0; idx<Mj_max_game_items[gr_screen.res]; idx++){
2203 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2205 MJ_LIST_START_DEC();
2207 gamesnd_play_iface(SND_SCROLL);
2211 void multi_join_list_page_down()
2215 // page the whole thing down
2216 while((count < Mj_max_game_items[gr_screen.res]) && ((Active_game_count - Multi_join_list_start) > Mj_max_game_items[gr_screen.res])){
2217 Multi_join_list_start_item = Multi_join_list_start_item->next;
2218 MJ_LIST_START_INC();
2223 gamesnd_play_iface(SND_SCROLL);
2226 void multi_join_cull_timeouts()
2228 active_game *backup;
2230 active_game *moveup = Active_game_head;
2232 // traverse through the entire list if any items exist
2236 if((moveup->heard_from_timer != -1) && (timestamp_elapsed(moveup->heard_from_timer))){
2237 Active_game_count--;
2239 // if this is the head of the list
2240 if(moveup == Active_game_head){
2241 // if this is the _only_ item on the list
2242 if(moveup->next == Active_game_head){
2243 // handle any gui details related to deleting this item
2244 multi_join_handle_item_cull(Active_game_head, count);
2246 free(Active_game_head);
2247 Active_game_head = NULL;
2250 // if there are other items on the list
2252 // handle any gui details related to deleting this item
2253 multi_join_handle_item_cull(moveup, count);
2255 Active_game_head = moveup->next;
2256 Active_game_head->prev = moveup->prev;
2257 Active_game_head->prev->next = Active_game_head;
2259 moveup = Active_game_head;
2262 // if its somewhere else on the list
2264 // handle any gui details related to deleting this item
2265 multi_join_handle_item_cull(moveup, count);
2267 // if its the last item on the list
2268 moveup->next->prev = moveup->prev;
2269 moveup->prev->next = moveup->next;
2271 // if it was the last element on the list, return
2272 if(moveup->next == Active_game_head){
2276 backup = moveup->next;
2282 moveup = moveup->next;
2285 } while(moveup != Active_game_head);
2289 // deep magic begins here.
2290 void multi_join_handle_item_cull(active_game *item, int item_index)
2292 // if this is the only item on the list, unset everything
2293 if(item->next == item){
2294 Multi_join_list_selected = -1;
2295 Multi_join_selected_item = NULL;
2298 Multi_join_slider.set_numberItems(0);
2300 MJ_LIST_START_SET(-1);
2301 Multi_join_list_start_item = NULL;
2307 // see if we should be adjusting our currently selected item
2308 if(item_index <= Multi_join_list_selected){
2309 // the selected item is the head of the list
2310 if(Multi_join_selected_item == Active_game_head){
2311 // move the pointer up since this item is about to be destroyed
2312 Multi_join_selected_item = Multi_join_selected_item->next;
2314 // if this is the item being deleted, select the previous one
2315 if(item == Multi_join_selected_item){
2317 Multi_join_selected_item = Multi_join_selected_item->prev;
2319 // decrement the selected index by 1
2320 Multi_join_list_selected--;
2322 // now we know its a previous item, so our pointer stays the same but our index goes down by one, since there will be
2323 // 1 less item on the list
2325 // decrement the selected index by 1
2326 Multi_join_list_selected--;
2331 // see if we should be adjusting out current start position
2332 if(item_index <= Multi_join_list_start){
2333 // the start position is the head of the list
2334 if(Multi_join_list_start_item == Active_game_head){
2335 // move the pointer up since this item is about to be destroyed
2336 Multi_join_list_start_item = Multi_join_list_start_item->next;
2338 // if this is the item being deleted, select the previous one
2339 if(item == Multi_join_list_start_item){
2340 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2342 // decrement the starting index by 1
2343 MJ_LIST_START_DEC();
2345 // but decrement the starting index by 1
2346 MJ_LIST_START_DEC();
2351 // maybe go back up a bit so that we always have a full page of items
2352 if(Active_game_count > Mj_max_game_items[gr_screen.res]){
2353 while((Active_game_count - Multi_join_list_start) < Mj_max_game_items[gr_screen.res]){
2354 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2355 MJ_LIST_START_DEC();
2359 // set slider location
2361 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);
2362 Multi_join_slider.force_currentItem(Multi_join_list_start);
2366 void multi_join_send_join_request(int as_observer)
2368 // don't do anything if we have no items selected
2369 if(Multi_join_selected_item == NULL){
2373 // 5/26/98 -- for team v team games, don't allow ingame joining :-(
2374 if ( (Multi_join_selected_item->flags & AG_FLAG_TEAMS) && (Multi_join_selected_item->flags & (AG_FLAG_PAUSE|AG_FLAG_IN_MISSION)) ) {
2375 popup(0, 1, POPUP_OK, XSTR("Joining ingame is currently not allowed for team vs. team games",772));
2379 memset(&Multi_join_request,0,sizeof(join_request));
2381 // if the netgame is in password mode, put up a request for the password
2382 if(Multi_join_selected_item->flags & AG_FLAG_PASSWD){
2383 if(!multi_passwd_popup(Multi_join_request.passwd)){
2387 nprintf(("Password : %s\n",Multi_join_request.passwd));
2390 // fill out the join request struct
2391 strcpy(Multi_join_request.callsign,Player->callsign);
2392 if(strlen(Player->image_filename) > 0){
2393 strcpy(Multi_join_request.image_filename, Player->image_filename);
2396 if(strlen(Player->squad_filename) > 0){
2397 strcpy(Multi_join_request.squad_filename, Player->squad_filename);
2401 // tracker id (if any)
2402 Multi_join_request.tracker_id = Multi_tracker_id;
2404 // player's rank (at least, what he wants you to _believe_)
2405 Multi_join_request.player_rank = (ubyte)Player->stats.rank;
2408 Multi_join_request.flags = 0;
2410 Multi_join_request.flags |= JOIN_FLAG_AS_OBSERVER;
2413 // if the player has hacked data
2414 if(game_hacked_data()){
2415 Multi_join_request.flags |= JOIN_FLAG_HAXOR;
2420 strncpy(Multi_join_request.pxo_squad_name, Multi_tracker_squad_name, LOGIN_LEN);
2423 // version of this server
2424 Multi_join_request.version = MULTI_FS_SERVER_VERSION;
2426 // server compatible version
2427 Multi_join_request.comp_version = MULTI_FS_SERVER_COMPATIBLE_VERSION;
2429 // his local player options
2430 memcpy(&Multi_join_request.player_options,&Player->m_local_options,sizeof(multi_local_options));
2432 // set the server address for the netgame
2433 memcpy(&Netgame.server_addr,&Multi_join_selected_item->server_addr,sizeof(net_addr));
2435 // send a join request to the guy
2436 send_join_packet(&Multi_join_selected_item->server_addr,&Multi_join_request);
2439 Multi_join_sent_stamp = timestamp(MULTI_JOIN_SENT_WAIT);
2442 multi_common_add_notify(XSTR("Sending join request...",773));
2445 void multi_join_create_game()
2447 // maybe warn the player about possible crappy server conditions
2448 if(!multi_join_maybe_warn()){
2452 // make sure to flag ourself as being the master
2453 Net_player->flags |= (NETINFO_FLAG_AM_MASTER | NETINFO_FLAG_GAME_HOST);
2454 Net_player->state = NETPLAYER_STATE_HOST_SETUP;
2456 // if we're in PXO mode, mark it down in our player struct
2457 if(MULTI_IS_TRACKER_GAME){
2458 Player->flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
2459 Player->save_flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
2461 // otherwise, if he's already played PXO games, warn him
2464 if(Player->flags & PLAYER_FLAGS_HAS_PLAYED_PXO){
2465 if(!multi_join_warn_pxo()){
2472 gameseq_post_event(GS_EVENT_MULTI_START_GAME);
2473 gamesnd_play_iface(SND_USER_SELECT);
2476 void multi_join_reset_join_stamp()
2478 // unset the timestamp here so the user can immediately send another join request
2479 Multi_join_sent_stamp = -1;
2480 multi_common_add_notify("");
2483 void multi_join_blit_top_stuff()
2485 // blit the cd icon if he has one
2486 if(Multi_has_cd && (Multi_common_icons[MICON_CD] != -1)){
2489 bm_get_info(Multi_common_icons[MICON_CD], &cd_w, NULL, NULL, NULL, NULL);
2491 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
2492 gr_bitmap((gr_screen.max_w / 2) - (cd_w / 2), Mj_cd_coords[gr_screen.res]);
2496 #define CW_CODE_CANCEL 0 // cancel the action
2497 #define CW_CODE_OK 1 // continue anyway
2498 #define CW_CODE_INFO 2 // gimme some more information
2500 #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)
2501 #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)
2503 #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)
2504 #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)
2506 int multi_join_warn_update_low(int code)
2510 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),LOW_WARN_TEXT);
2513 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),LOW_INFO_TEXT);
2516 return CW_CODE_CANCEL;
2519 int multi_join_warn_update_medium(int code)
2523 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),MED_WARN_TEXT);
2526 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),MED_INFO_TEXT);
2529 return CW_CODE_CANCEL;
2532 int multi_join_maybe_warn()
2536 // if the player is set for low updates
2537 if(Player->m_local_options.obj_update_level == OBJ_UPDATE_LOW){
2540 code = multi_join_warn_update_low(code);
2541 } while((code != CW_CODE_CANCEL) && (code != CW_CODE_OK));
2546 // if the player is set for medium updates
2547 else if(Player->m_local_options.obj_update_level == OBJ_UPDATE_MEDIUM){
2550 code = multi_join_warn_update_medium(code);
2551 } while((code != CW_CODE_CANCEL) && (code != CW_CODE_OK));
2559 int multi_join_warn_pxo()
2561 // 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;
2565 void multi_join_blit_protocol()
2567 gr_set_color_fast(&Color_bright);
2569 switch(Socket_type){
2572 gr_string(5, 2, "TCP");
2576 gr_string(5, 2, "IPX");
2582 // -------------------------------------------------------------------------------------------------
2584 // MULTIPLAYER START GAME screen
2589 #define MULTI_SG_PALETTE "InterfacePalette"
2591 static const char *Multi_sg_bitmap_fname[GR_NUM_RESOLUTIONS] = {
2592 "MultiStartGame", // GR_640
2593 "2_MultiStartGame" // GR_1024
2596 static const char *Multi_sg_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
2597 "MultiStartGame-M", // GR_640
2598 "2_MultiStartGame-M" // GR_1024
2603 int Multi_sg_rank_max_display[GR_NUM_RESOLUTIONS] = {
2608 // constants for coordinate look ups
2609 #define MSG_X_COORD 0
2610 #define MSG_Y_COORD 1
2611 #define MSG_W_COORD 2
2612 #define MSG_H_COORD 3
2616 // input password field
2617 int Msg_passwd_coords[GR_NUM_RESOLUTIONS][4] = {
2630 // input game title field
2631 int Msg_title_coords[GR_NUM_RESOLUTIONS][4] = {
2644 // rank selected field
2645 int Msg_rank_sel_coords[GR_NUM_RESOLUTIONS][4] = {
2659 int Msg_rank_list_coords[GR_NUM_RESOLUTIONS][4] = {
2675 #define MULTI_SG_NUM_BUTTONS 12
2676 #define MSG_OPEN_GAME 0
2677 #define MSG_CLOSED_GAME 1
2678 #define MSG_PASSWD_GAME 2
2679 #define MSG_RESTRICTED_GAME 3
2680 #define MSG_RANK_SET_GAME 4
2681 #define MSG_RANK_SCROLL_UP 5
2682 #define MSG_RANK_SCROLL_DOWN 6
2683 #define MSG_RANK_ABOVE 7
2684 #define MSG_RANK_BELOW 8
2686 #define MSG_OPTIONS 10
2687 #define MSG_ACCEPT 11
2689 #define MULTI_SG_NUM_BUTTONS 10
2690 #define MSG_OPEN_GAME 0
2691 //#define MSG_CLOSED_GAME 1
2692 //#define MSG_RESTRICTED_GAME 2
2693 #define MSG_PASSWD_GAME 1
2694 #define MSG_RANK_SET_GAME 2
2695 #define MSG_RANK_SCROLL_UP 3
2696 #define MSG_RANK_SCROLL_DOWN 4
2697 #define MSG_RANK_ABOVE 5
2698 #define MSG_RANK_BELOW 6
2700 #define MSG_OPTIONS 8
2701 #define MSG_ACCEPT 9
2704 UI_WINDOW Multi_sg_window; // the window object for the join screen
2705 UI_BUTTON Multi_sg_rank_button; // for selecting the rank marker
2706 UI_INPUTBOX Multi_sg_game_name; // for Netgame.name
2707 UI_INPUTBOX Multi_sg_game_passwd; // for Netgame.passwd
2708 int Multi_sg_bitmap; // the background bitmap
2710 ui_button_info Multi_sg_buttons[GR_NUM_RESOLUTIONS][MULTI_SG_NUM_BUTTONS] = {
2713 ui_button_info("MSG_00", 75, 111, -1, -1, 0),
2714 ui_button_info("MSG_01", 75, 139, -1, -1, 1),
2715 ui_button_info("MSG_02", 75, 164, -1, -1, 2),
2716 ui_button_info("MSG_03", 75, 199, -1, -1, 3),
2717 ui_button_info("MSG_04", 75, 243, -1, -1, 4),
2718 ui_button_info("MSG_05", 376, 231, -1, -1, 5),
2719 ui_button_info("MSG_06", 376, 258, -1, -1, 6),
2720 ui_button_info("MSG_07", 376, 291, -1, -1, 7),
2721 ui_button_info("MSG_08", 376, 320, -1, -1, 8),
2722 ui_button_info("MSG_09", 469, 427, -1, -1, 9),
2723 ui_button_info("MSG_10", 447, 452, -1, -1, 10),
2724 ui_button_info("MSG_11", 561, 411, -1, -1, 11),
2726 ui_button_info("MSG_00", 1, 184, 34, 191, 2), // open
2727 // ui_button_info("MSG_01", 1, 159, 34, 166, 1), // closed
2728 // ui_button_info("MSG_02", 1, 184, 34, 191, 2), // restricted
2729 ui_button_info("MSG_03", 1, 209, 34, 218, 3), // password
2730 ui_button_info("MSG_04", 1, 257, 34, 266, 4), // rank set
2731 ui_button_info("MSG_05", 1, 282, -1, -1, 5), // rank scroll up
2732 ui_button_info("MSG_06", 1, 307, -1, -1, 6), // rank scroll down
2733 ui_button_info("MSG_07", 177, 282, 210, 290, 7), // rank above
2734 ui_button_info("MSG_08", 177, 307, 210, 315, 8), // rank below
2735 ui_button_info("MSG_09", 536, 429, 500, 440, 9), // help
2736 ui_button_info("MSG_10", 536, 454, 479, 464, 10), // options
2737 ui_button_info("MSG_11", 576, 432, 571, 415, 11), // accept
2741 ui_button_info("2_MSG_00", 2, 295, 51, 307, 2), // open
2742 // ui_button_info("2_MSG_01", 2, 254, 51, 267, 1), // closed
2743 // ui_button_info("2_MSG_02", 2, 295, 51, 307, 2), // restricted
2744 ui_button_info("2_MSG_03", 2, 335, 51, 350, 3), // password
2745 ui_button_info("2_MSG_04", 2, 412, 51, 426, 4), // rank set
2746 ui_button_info("2_MSG_05", 2, 452, -1, -1, 5), // rank scroll up
2747 ui_button_info("2_MSG_06", 2, 492, -1, -1, 6), // rank scroll down
2748 ui_button_info("2_MSG_07", 284, 452, 335, 465, 7), // rank above
2749 ui_button_info("2_MSG_08", 284, 492, 335, 505, 8), // rank below
2750 ui_button_info("2_MSG_09", 858, 687, 817, 706, 9), // help
2751 ui_button_info("2_MSG_10", 858, 728, 797, 743, 10), // options
2752 ui_button_info("2_MSG_11", 921, 692, 921, 664, 11), // accept
2753 #ifdef MAKE_FS1 // filler for extra FS1 buttons
2754 ui_button_info("none", -1, -1, -1, -1, -1),
2755 ui_button_info("none", -1, -1, -1, -1, -1),
2761 #define MULTI_SG_NUM_TEXT 11
2763 UI_XSTR Multi_sg_text[GR_NUM_RESOLUTIONS][MULTI_SG_NUM_TEXT] = {
2765 {"Open", 1322, 34, 191, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_OPEN_GAME].button},
2766 // {"Closed", 1323, 34, 166, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_CLOSED_GAME].button},
2767 // {"Restricted", 1324, 34, 191, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RESTRICTED_GAME].button},
2768 {"Password Protected", 1325, 34, 218, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_PASSWD_GAME].button},
2769 {"Allow Rank", 1326, 34, 266, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_SET_GAME].button},
2770 {"Above", 1327, 210, 290, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_ABOVE].button},
2771 {"Below", 1328, 210, 315, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_BELOW].button},
2772 {"Help", 928, 500, 440, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_HELP].button},
2773 {"Options", 1036, 479, 464, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_OPTIONS].button},
2774 {"Accept", 1035, 571, 415, UI_XSTR_COLOR_PINK, -1, &Multi_sg_buttons[0][MSG_ACCEPT].button},
2775 {"Start Game", 1329, 26, 10, UI_XSTR_COLOR_GREEN, -1, NULL},
2776 {"Title", 1330, 26, 31, UI_XSTR_COLOR_GREEN, -1, NULL},
2777 {"Game Type", 1331, 12, 165, UI_XSTR_COLOR_GREEN, -1, NULL},
2780 {"Open", 1322, 51, 307, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_OPEN_GAME].button},
2781 // {"Closed", 1323, 51, 267, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_CLOSED_GAME].button},
2782 // {"Restricted", 1324, 51, 307, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RESTRICTED_GAME].button},
2783 {"Password Protected", 1325, 51, 350, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_PASSWD_GAME].button},
2784 {"Allow Rank", 1326, 51, 426, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_SET_GAME].button},
2785 {"Above", 1327, 335, 465, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_ABOVE].button},
2786 {"Below", 1328, 335, 505, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_BELOW].button},
2787 {"Help", 928, 817, 706, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_HELP].button},
2788 {"Options", 1036, 797, 743, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_OPTIONS].button},
2789 {"Accept", 1035, 921, 664, UI_XSTR_COLOR_PINK, -1, &Multi_sg_buttons[1][MSG_ACCEPT].button},
2790 {"Start Game", 1329, 42, 22, UI_XSTR_COLOR_GREEN, -1, NULL},
2791 {"Title", 1330, 42, 50, UI_XSTR_COLOR_GREEN, -1, NULL},
2792 {"Game Type", 1331, 20, 264, UI_XSTR_COLOR_GREEN, -1, NULL},
2797 // starting index for displaying ranks
2798 int Multi_sg_rank_start;
2799 int Multi_sg_rank_select;
2801 // netgame pointer to indirect through
2802 netgame_info *Multi_sg_netgame;
2804 // hold temporary values in this structure when on a standalone server
2805 netgame_info Multi_sg_netgame_temp;
2807 // forward declarations
2808 void multi_sg_check_buttons();
2809 void multi_sg_button_pressed(int n);
2810 void multi_sg_init_gamenet();
2811 void multi_sg_draw_radio_buttons();
2812 void multi_sg_rank_scroll_up();
2813 void multi_sg_rank_scroll_down();
2814 void multi_sg_rank_display_stuff();
2815 void multi_sg_rank_process_select();
2816 void multi_sg_rank_build_name(char *in,char *out);
2817 void multi_sg_check_passwd();
2818 void multi_sg_check_name();
2819 void multi_sg_release_passwd();
2820 int multi_sg_rank_select_valid(int rank);
2821 void multi_sg_select_rank_default();
2823 // function which takes a rank name and returns the index. Useful for commandline options
2824 // for above and below rank. We return the index of the rank in the Ranks[] array. If
2825 // the rank isn't found, we return -1
2826 int multi_start_game_rank_from_name( char *rank ) {
2830 for ( i = 0; i <= MAX_FREESPACE1_RANK; i++ ) {
2832 for ( i = 0; i <= MAX_FREESPACE2_RANK; i++ ) {
2834 if ( !SDL_strcasecmp(Ranks[i].name, rank) ) {
2842 void multi_start_game_init()
2846 // initialize the gamenet
2847 multi_sg_init_gamenet();
2849 // create the interface window
2850 Multi_sg_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
2851 Multi_sg_window.set_mask_bmap(Multi_sg_bitmap_mask_fname[gr_screen.res]);
2853 // load the background bitmap
2854 Multi_sg_bitmap = bm_load(Multi_sg_bitmap_fname[gr_screen.res]);
2855 if(Multi_sg_bitmap < 0){
2856 // we failed to load the bitmap - this is very bad
2860 // initialize the common notification messaging
2861 multi_common_notify_init();
2863 // initialize the common text area
2864 multi_common_set_text("");
2866 // use the common interface palette
2867 multi_common_set_palette();
2869 // create the interface buttons
2870 for(idx=0; idx<MULTI_SG_NUM_BUTTONS; idx++){
2871 // create the object
2872 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);
2874 // set the sound to play when highlighted
2875 Multi_sg_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
2877 // set the ani for the button
2878 Multi_sg_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sg_buttons[gr_screen.res][idx].filename);
2881 Multi_sg_buttons[gr_screen.res][idx].button.link_hotspot(Multi_sg_buttons[gr_screen.res][idx].hotspot);
2886 for(idx=0; idx<MULTI_SG_NUM_TEXT; idx++){
2887 Multi_sg_window.add_XSTR(&Multi_sg_text[gr_screen.res][idx]);
2891 // load the help overlay
2892 help_overlay_load(MULTI_START_OVERLAY);
2893 help_overlay_set_state(MULTI_START_OVERLAY,0);
2895 // intiialize the rank selection items
2896 multi_sg_select_rank_default();
2897 Multi_sg_rank_start = Multi_sg_rank_select;
2899 // create the rank select button
2900 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);
2901 Multi_sg_rank_button.hide();
2903 // create the netgame name input box
2904 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);
2906 // create the netgame password input box, and disable it by default
2907 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);
2908 Multi_sg_game_passwd.hide();
2909 Multi_sg_game_passwd.disable();
2911 // set the netgame text to this gadget and make it have focus
2912 Multi_sg_game_name.set_text(Multi_sg_netgame->name);
2913 Multi_sg_game_name.set_focus();
2915 // if starting a netgame, set the name of the game and any other options that are appropriate
2916 if ( Cmdline_start_netgame ) {
2917 if ( Cmdline_game_name != NULL ) {
2918 strcpy( Multi_sg_netgame->name, Cmdline_game_name );
2919 Multi_sg_game_name.set_text(Multi_sg_netgame->name);
2922 // deal with the different game types -- only one should even be active, so we will just go down
2923 // the line. Last one wins.
2924 if ( Cmdline_closed_game ) {
2925 Multi_sg_netgame->mode = NG_MODE_CLOSED;
2926 } else if ( Cmdline_restricted_game ) {
2927 Multi_sg_netgame->mode = NG_MODE_RESTRICTED;
2928 } else if ( Cmdline_game_password != NULL ) {
2929 Multi_sg_netgame->mode = NG_MODE_PASSWORD;
2930 strcpy(Multi_sg_netgame->passwd, Cmdline_game_password);
2931 Multi_sg_game_passwd.set_text(Multi_sg_netgame->passwd);
2934 // deal with rank above and rank below
2935 if ( (Cmdline_rank_above != NULL) || (Cmdline_rank_below != NULL) ) {
2939 if ( Cmdline_rank_above != NULL ) {
2940 rank_str = Cmdline_rank_above;
2942 rank_str = Cmdline_rank_below;
2945 // try and get the rank index from the name -- if found, then set the rank base
2946 // and the game type. apparently we only support either above or below, not both
2947 // together, so I make a random choice
2948 rank = multi_start_game_rank_from_name( rank_str );
2950 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
2952 // now an arbitrary decision
2953 if ( Cmdline_rank_above != NULL ) {
2954 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
2956 Multi_sg_netgame->mode = NG_MODE_RANK_BELOW;
2961 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
2965 void multi_start_game_do()
2967 // return here since we will be moving to the next stage anyway -- I don't want to see the backgrounds of
2968 // all the screens for < 1 second for every screen we automatically move to.
2969 if ( Cmdline_start_netgame ) {
2973 int k = Multi_sg_window.process();
2975 // process any keypresses
2978 if(help_overlay_active(MULTI_START_OVERLAY)){
2979 help_overlay_set_state(MULTI_START_OVERLAY,0);
2981 gamesnd_play_iface(SND_USER_SELECT);
2982 multi_quit_game(PROMPT_NONE);
2987 case SDLK_LCTRL + SDLK_RETURN :
2988 case SDLK_RCTRL + SDLK_RETURN :
2989 gamesnd_play_iface(SND_COMMIT_PRESSED);
2990 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
2994 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
2995 help_overlay_set_state(MULTI_START_OVERLAY, 0);
2998 // check to see if the user has selected a different rank
2999 multi_sg_rank_process_select();
3001 // check any button presses
3002 multi_sg_check_buttons();
3004 // check to see if any of the input boxes have changed, and update the appropriate Netgame fields if necessary
3005 multi_sg_check_passwd();
3006 multi_sg_check_name();
3008 // draw the background, etc
3010 GR_MAYBE_CLEAR_RES(Multi_sg_bitmap);
3011 if(Multi_sg_bitmap != -1){
3012 gr_set_bitmap(Multi_sg_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
3015 Multi_sg_window.draw();
3017 // display rank stuff
3018 multi_sg_rank_display_stuff();
3020 // display any pending notification messages
3021 multi_common_notify_do();
3023 // draw all radio button
3024 multi_sg_draw_radio_buttons();
3026 // draw the help overlay
3027 help_overlay_maybe_blit(MULTI_START_OVERLAY);
3033 void multi_start_game_close()
3035 // if i'm the host on a standalone server, send him my start game options (passwd, mode, etc)
3036 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
3037 multi_options_update_start_game(Multi_sg_netgame);
3040 // unload any bitmaps
3041 if(!bm_unload(Multi_sg_bitmap)){
3042 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_sg_bitmap_fname[gr_screen.res]));
3045 // unload the help overlay
3046 help_overlay_unload(MULTI_START_OVERLAY);
3048 // destroy the UI_WINDOW
3049 Multi_sg_window.destroy();
3052 void multi_sg_check_buttons()
3055 for(idx=0;idx<MULTI_SG_NUM_BUTTONS;idx++){
3056 // we only really need to check for one button pressed at a time, so we can break after
3058 if(Multi_sg_buttons[gr_screen.res][idx].button.pressed()){
3059 multi_sg_button_pressed(idx);
3065 void multi_sg_button_pressed(int n)
3068 // go to the options screen
3070 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
3075 if(!help_overlay_active(MULTI_START_OVERLAY)){
3076 help_overlay_set_state(MULTI_START_OVERLAY,1);
3078 help_overlay_set_state(MULTI_START_OVERLAY,0);
3082 // the open button was pressed
3084 // if the closed option is selected
3085 if(Multi_sg_netgame->mode != NG_MODE_OPEN){
3086 Multi_sg_netgame->mode = NG_MODE_OPEN;
3088 gamesnd_play_iface(SND_USER_SELECT);
3090 // release the password control if necessary
3091 multi_sg_release_passwd();
3093 // if its already selected
3095 gamesnd_play_iface(SND_GENERAL_FAIL);
3100 // the open button was pressed
3101 case MSG_CLOSED_GAME:
3102 // if the closed option is selected
3103 if(Multi_sg_netgame->mode != NG_MODE_CLOSED){
3104 Multi_sg_netgame->mode = NG_MODE_CLOSED;
3106 gamesnd_play_iface(SND_USER_SELECT);
3108 // release the password control if necessary
3109 multi_sg_release_passwd();
3111 // if its already selected
3113 gamesnd_play_iface(SND_GENERAL_FAIL);
3118 // toggle password protection
3119 case MSG_PASSWD_GAME:
3120 // if we selected it
3121 if(Multi_sg_netgame->mode != NG_MODE_PASSWORD){
3122 gamesnd_play_iface(SND_USER_SELECT);
3124 Multi_sg_game_passwd.enable();
3125 Multi_sg_game_passwd.unhide();
3126 Multi_sg_game_passwd.set_focus();
3128 Multi_sg_netgame->mode = NG_MODE_PASSWORD;
3130 // copy in the current network password
3131 Multi_sg_game_passwd.set_text(Multi_sg_netgame->passwd);
3133 gamesnd_play_iface(SND_GENERAL_FAIL);
3138 // toggle "restricted" on or off
3139 case MSG_RESTRICTED_GAME:
3140 if(Multi_sg_netgame->mode != NG_MODE_RESTRICTED){
3141 gamesnd_play_iface(SND_USER_SELECT);
3142 Multi_sg_netgame->mode = NG_MODE_RESTRICTED;
3144 // release the password control if necessary
3145 multi_sg_release_passwd();
3147 gamesnd_play_iface(SND_GENERAL_FAIL);
3152 // turn off all rank requirements
3153 case MSG_RANK_SET_GAME:
3154 // if either is set, then turn then both off
3155 if((Multi_sg_netgame->mode != NG_MODE_RANK_BELOW) && (Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE)){
3156 gamesnd_play_iface(SND_USER_SELECT);
3158 // set it to the default case if we're turning it off
3159 multi_sg_select_rank_default();
3160 Multi_sg_rank_start = Multi_sg_rank_select;
3162 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
3164 // release the password control if necessary
3165 multi_sg_release_passwd();
3167 gamesnd_play_iface(SND_GENERAL_FAIL);
3171 // rank above was pressed
3172 case MSG_RANK_ABOVE :
3173 if((Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE) || (Multi_sg_netgame->mode == NG_MODE_RANK_BELOW)){
3174 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
3176 // select the first item
3177 multi_sg_select_rank_default();
3178 Multi_sg_rank_start = Multi_sg_rank_select;
3181 gamesnd_play_iface(SND_USER_SELECT);
3183 gamesnd_play_iface(SND_GENERAL_FAIL);
3187 // rank below was pressed
3188 case MSG_RANK_BELOW :
3189 if((Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE) || (Multi_sg_netgame->mode == NG_MODE_RANK_BELOW)){
3190 Multi_sg_netgame->mode = NG_MODE_RANK_BELOW;
3192 // select the first item
3193 multi_sg_select_rank_default();
3194 Multi_sg_rank_start = Multi_sg_rank_select;
3197 gamesnd_play_iface(SND_USER_SELECT);
3199 gamesnd_play_iface(SND_GENERAL_FAIL);
3203 // scroll the rank list up
3204 case MSG_RANK_SCROLL_UP:
3205 multi_sg_rank_scroll_up();
3208 // scroll the rank list down
3209 case MSG_RANK_SCROLL_DOWN:
3210 multi_sg_rank_scroll_down();
3213 // move to the create game screen
3215 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
3216 gamesnd_play_iface(SND_COMMIT_PRESSED);
3220 gamesnd_play_iface(SND_GENERAL_FAIL);
3221 multi_common_add_notify(XSTR("Not implemented yet!",760));
3226 // NOTE : this is where all Netgame initialization should take place on the host
3227 void multi_sg_init_gamenet()
3229 char buf[128],out_name[128];
3231 net_player *server_save;
3233 // back this data up in case we are already connected to a standalone
3234 memcpy(&save,&Netgame.server_addr,sizeof(net_addr));
3235 server_save = Netgame.server;
3237 // remove campaign flags
3238 Game_mode &= ~(GM_CAMPAIGN_MODE);
3240 // clear out the Netgame structure and start filling in the values
3241 memset( &Netgame, 0, sizeof(Netgame) );
3242 memset( &Multi_sg_netgame_temp, 0, sizeof(netgame_info) );
3244 // if we're on the standalone, we're not the server, so we don't care about setting the netgame state
3245 if(Net_player->state != NETPLAYER_STATE_STD_HOST_SETUP){
3246 Netgame.game_state = NETGAME_STATE_HOST_SETUP;
3247 Multi_sg_netgame = &Netgame;
3250 ml_string(NOX("Starting netgame as Host/Server"));
3252 Multi_sg_netgame = &Multi_sg_netgame_temp;
3256 memcpy(&temp_addr.s_addr, &Netgame.server_addr, 4);
3257 char *server_addr = inet_ntoa(temp_addr);
3258 ml_printf(NOX("Starting netgame as Host on Standalone server : %s"), (server_addr == NULL) ? NOX("Unknown") : server_addr);
3261 Net_player->tracker_player_id = Multi_tracker_id;
3263 Multi_sg_netgame->security = (rand() % 32766) + 1; // get some random security number
3264 Multi_sg_netgame->mode = NG_MODE_OPEN;
3265 Multi_sg_netgame->rank_base = RANK_ENSIGN;
3266 if(Multi_sg_netgame->security < 16){
3267 Multi_sg_netgame->security += 16;
3270 // set the version_info field
3271 Multi_sg_netgame->version_info = NG_VERSION_ID;
3273 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
3274 Netgame.host = Net_player;
3277 // set the default netgame flags
3278 Multi_sg_netgame->flags = 0;
3280 // intialize endgame stuff
3281 multi_endgame_init();
3283 // load in my netgame options
3284 multi_options_netgame_load(&Netgame.options);
3286 // load my local netplayer options
3287 multi_options_local_load(&Net_player->p_info.options, Net_player);
3289 // setup the default game name, taking care of string length and player callsigns
3290 memset(out_name,0,128);
3292 pilot_format_callsign_personal(Player->callsign,out_name);
3293 sprintf(buf, XSTR("%s game",782), out_name); // [[ %s will be a pilot's name ]]
3294 if ( strlen(buf) > MAX_GAMENAME_LEN ){
3295 strcpy(buf, XSTR("Temporary name",783));
3297 strcpy(Multi_sg_netgame->name, buf);
3299 // set the default qos and duration
3300 multi_voice_maybe_update_vars(Netgame.options.voice_qos,Netgame.options.voice_record_time);
3302 // make sure to set the server correctly (me or the standalone)
3303 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3304 memcpy(&Netgame.server_addr, &Psnet_my_addr,sizeof(net_addr));
3305 Netgame.server = Net_player;
3306 Net_player->player_id = multi_get_new_id();
3308 // setup debug flags
3309 Netgame.debug_flags = 0;
3311 if(!Cmdline_server_firing){
3312 Netgame.debug_flags |= NETD_FLAG_CLIENT_FIRING;
3314 if(!Cmdline_client_dodamage){
3315 Netgame.debug_flags |= NETD_FLAG_CLIENT_NODAMAGE;
3319 memcpy(&Netgame.server_addr,&save,sizeof(net_addr));
3320 Netgame.server = server_save;
3323 // if I have a cd or not
3325 Net_player->flags |= NETINFO_FLAG_HAS_CD;
3328 // if I have hacked data
3329 if(game_hacked_data()){
3330 Net_player->flags |= NETINFO_FLAG_HAXOR;
3333 // assign my player struct and other data
3334 Net_player->flags |= (NETINFO_FLAG_CONNECTED | NETINFO_FLAG_DO_NETWORKING);
3335 Net_player->s_info.voice_token_timestamp = -1;
3337 // if we're supposed to flush our cache directory, do so now
3338 if(Net_player->p_info.options.flags & MLO_FLAG_FLUSH_CACHE){
3339 multi_flush_multidata_cache();
3342 ml_string(NOX("Flushing multi-data cache"));
3348 void multi_sg_draw_radio_buttons()
3350 // draw the appropriate radio button
3351 switch(Multi_sg_netgame->mode){
3353 Multi_sg_buttons[gr_screen.res][MSG_OPEN_GAME].button.draw_forced(2);
3357 case NG_MODE_CLOSED:
3358 Multi_sg_buttons[gr_screen.res][MSG_CLOSED_GAME].button.draw_forced(2);
3362 case NG_MODE_PASSWORD:
3363 Multi_sg_buttons[gr_screen.res][MSG_PASSWD_GAME].button.draw_forced(2);
3367 case NG_MODE_RESTRICTED:
3368 Multi_sg_buttons[gr_screen.res][MSG_RESTRICTED_GAME].button.draw_forced(2);
3372 case NG_MODE_RANK_ABOVE:
3373 Multi_sg_buttons[gr_screen.res][MSG_RANK_SET_GAME].button.draw_forced(2);
3374 Multi_sg_buttons[gr_screen.res][MSG_RANK_ABOVE].button.draw_forced(2);
3376 case NG_MODE_RANK_BELOW:
3377 Multi_sg_buttons[gr_screen.res][MSG_RANK_SET_GAME].button.draw_forced(2);
3378 Multi_sg_buttons[gr_screen.res][MSG_RANK_BELOW].button.draw_forced(2);
3383 void multi_sg_rank_scroll_up()
3385 // if he doesn't have either of the rank flags set, then ignore this
3386 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3390 if(Multi_sg_rank_start > 0){
3391 Multi_sg_rank_start--;
3392 gamesnd_play_iface(SND_SCROLL);
3394 gamesnd_play_iface(SND_GENERAL_FAIL);
3398 void multi_sg_rank_scroll_down()
3400 // if he doesn't have either of the rank flags set, then ignore this
3401 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3405 if((NUM_RANKS - Multi_sg_rank_start) > Multi_sg_rank_max_display[gr_screen.res]){
3406 Multi_sg_rank_start++;
3407 gamesnd_play_iface(SND_SCROLL);
3409 gamesnd_play_iface(SND_GENERAL_FAIL);
3413 void multi_sg_rank_display_stuff()
3418 // if he doesn't have either of the rank flags set, then ignore this
3419 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3423 // display the list of ranks
3424 y = Msg_rank_list_coords[gr_screen.res][MSG_Y_COORD];
3425 idx = Multi_sg_rank_start;
3427 while((count < NUM_RANKS) && (count < Multi_sg_rank_max_display[gr_screen.res]) && (idx < NUM_RANKS)){
3428 // if its the selected item, then color it differently
3429 if(idx == Multi_sg_rank_select){
3430 gr_set_color_fast(&Color_text_selected);
3432 gr_set_color_fast(&Color_text_normal);
3436 multi_sg_rank_build_name(Ranks[idx].name,rank_name);
3437 gr_string(Msg_rank_list_coords[gr_screen.res][MSG_X_COORD],y,rank_name);
3445 // display the selected rank
3447 gr_set_color_fast(&Color_bright);
3448 multi_sg_rank_build_name(Ranks[Multi_sg_netgame->rank_base].name,rank_name);
3449 gr_string(Msg_rank_sel_coords[gr_screen.res][MSG_X_COORD],Msg_rank_sel_coords[gr_screen.res][MSG_Y_COORD],rank_name);
3453 void multi_sg_rank_process_select()
3457 // if he doesn't have either of the rank flags set, then ignore this
3458 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3462 // see if he's clicked on an item on the rank list
3463 if(Multi_sg_rank_button.pressed()){
3465 Multi_sg_rank_button.get_mouse_pos(NULL,&y);
3468 if(item + Multi_sg_rank_start < NUM_RANKS){
3469 // evaluate whether this rank is valid for the guy to pick
3470 if(multi_sg_rank_select_valid(item + Multi_sg_rank_start)){
3471 gamesnd_play_iface(SND_USER_SELECT);
3473 Multi_sg_rank_select = item + Multi_sg_rank_start;
3475 // set the Netgame rank
3476 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
3478 gamesnd_play_iface(SND_GENERAL_FAIL);
3480 memset(string,0,255);
3481 sprintf(string,XSTR("Illegal value for a host of your rank (%s)\n",784),Ranks[Net_player->player->stats.rank].name);
3482 multi_common_add_notify(string);
3488 void multi_sg_rank_build_name(char *in,char *out)
3494 first = strtok(use," ");
3496 // just copy the string
3501 // if the first part of the string is lieutenant, then abbreivate it and tack on the rest of the string
3502 if (SDL_strcasecmp(first,XSTR("lieutenant",785)) == 0) {
3503 first = strtok(NULL, NOX("\n"));
3505 // if he's not just a plain lieutenant
3507 strcpy(out,XSTR("Lt. ",786)); // [[ lieutenant ]]
3510 // if he _is_ just a plain lieutenant
3519 void multi_sg_check_passwd()
3521 // check to see if the password input box has been pressed
3522 if(Multi_sg_game_passwd.changed()){
3523 Multi_sg_game_passwd.get_text(Multi_sg_netgame->passwd);
3527 void multi_sg_check_name()
3529 // check to see if the game name input box has been pressed
3530 if(Multi_sg_game_name.changed()){
3531 Multi_sg_game_name.get_text(Multi_sg_netgame->name);
3535 void multi_sg_release_passwd()
3537 // hide and disable the password input box
3538 Multi_sg_game_passwd.hide();
3539 Multi_sg_game_passwd.disable();
3541 // set the focus back to the name input box
3542 Multi_sg_game_name.set_focus();
3545 int multi_sg_rank_select_valid(int rank)
3548 if(Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE){
3549 if(Net_player->player->stats.rank >= rank){
3555 if(Net_player->player->stats.rank <= rank){
3563 void multi_sg_select_rank_default()
3565 // pick our rank for now
3566 Multi_sg_rank_select = Net_player->player->stats.rank;
3568 // set the Netgame rank
3569 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
3572 // -------------------------------------------------------------------------------------------------
3574 // MULTIPLAYER CREATE GAME screen
3579 const char *Multi_create_bitmap_fname[GR_NUM_RESOLUTIONS] = {
3580 "MultiCreate", // GR_640
3581 "2_MultiCreate" // GR_1024
3584 const char *Multi_create_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
3585 "MultiCreate-M", // GR_640
3586 "2_MultiCreate-M" // GR_1024
3589 const char *Multi_create_loading_fname[GR_NUM_RESOLUTIONS] = {
3590 "PleaseWait", // GR_640
3591 "2_PleaseWait" // GR_1024
3595 #define MULTI_CREATE_NUM_BUTTONS 23
3598 #define MC_SHOW_ALL 0
3599 #define MC_SHOW_COOP 1
3600 #define MC_SHOW_TEAM 2
3601 #define MC_SHOW_DOGFIGHT 3
3602 #define MC_PXO_REFRESH 4
3603 #define MC_PILOT_INFO 5
3604 #define MC_SCROLL_LIST_UP 6
3605 #define MC_SCROLL_LIST_DOWN 7
3606 #define MC_SCROLL_PLAYERS_UP 8
3607 #define MC_SCROLL_PLAYERS_DOWN 9
3608 #define MC_MISSION_FILTER 10
3609 #define MC_CAMPAIGN_FILTER 11
3610 #define MC_CANCEL 12
3615 #define MC_SCROLL_INFO_UP 17
3616 #define MC_SCROLL_INFO_DOWN 18
3617 #define MC_HOST_OPTIONS 19
3619 #define MC_OPTIONS 21
3620 #define MC_ACCEPT 22
3623 UI_WINDOW Multi_create_window; // the window object for the create screen
3624 UI_BUTTON Multi_create_player_select_button; // for selecting players
3625 UI_BUTTON Multi_create_list_select_button; // for selecting missions/campaigns
3626 int Multi_create_bitmap; // the background bitmap
3627 UI_SLIDER2 Multi_create_slider; // for create list
3629 // constants for coordinate look ups
3630 #define MC_X_COORD 0
3631 #define MC_Y_COORD 1
3632 #define MC_W_COORD 2
3633 #define MC_H_COORD 3
3635 ui_button_info Multi_create_buttons[GR_NUM_RESOLUTIONS][MULTI_CREATE_NUM_BUTTONS] = {
3638 ui_button_info("MC_18", 34, 131, -1, -1, 18), // all
3639 ui_button_info("MC_19", 72, 131, -1, -1, 19), // coop
3640 ui_button_info("MC_20", 120, 131, -1, -1, 20), // team
3641 // ui_button_info("MC_21", 166, 131, -1, -1, 21), // dogfight
3642 ui_button_info("none", -1, -1, -1, -1, -1), // dogfight (not used)
3643 ui_button_info("none", -1, -1, -1, -1, -1), // pxo?
3644 ui_button_info("MC_26", 540, 114, -1, -1, 26), // pilot info
3645 ui_button_info("MC_03", 0, 187, -1, -1, 2), // scroll list up
3646 ui_button_info("MC_02", 0, 227, -1, -1, 3), // scroll list down
3647 ui_button_info("MC_04", 611, 182, -1, -1, 4), // scroll players up
3648 ui_button_info("MC_05", 611, 221, -1, -1, 5), // scroll players down
3649 ui_button_info("MC_06", 18, 322, -1, -1, 6), // mission filter
3650 ui_button_info("MC_07", 18, 344, -1, -1, 7), // campaign filter
3651 ui_button_info("MC_10", 317, 339, -1, -1, 10), // cancel
3652 ui_button_info("MC_14", 464, 350, -1, -1, 14), // team 1
3653 ui_button_info("MC_15", 498, 350, -1, -1, 15), // team 2
3654 ui_button_info("MC_16", 527, 346, -1, -1, 16), // kick
3655 ui_button_info("MC_17", 572, 346, -1, -1, 17), // close
3656 ui_button_info("MC_08", 0, 398, -1, -1, 8), // scroll mission info up
3657 ui_button_info("MC_09", 0, 435, -1, -1, 9), // scroll mission info down
3658 ui_button_info("MC_27", 447, 402, -1, -1, 27), // host options
3659 ui_button_info("MC_11", 510, 428, -1, -1, 11), // help
3660 ui_button_info("MC_12", 510, 453, -1, -1, 12), // options
3661 ui_button_info("Mc_13", 562, 412, -1, -1, 13), // commit
3663 ui_button_info("MC_00", 32, 129, 36, 158, 0), // show all missions
3664 ui_button_info("MC_01", 76, 129, 71, 158, 1), // show coop missions
3665 ui_button_info("MC_02", 121, 129, 119, 158, 2), // show team missions
3666 ui_button_info("MC_03", 164, 129, 166, 158, 3), // show dogfight missions
3667 ui_button_info("MC_04", 399, 129, 229, 130, 4), // pxo mission refresh
3668 ui_button_info("MC_05", 567, 123, 467, 132, 5), // pilot info
3669 ui_button_info("MC_06", 1, 161, -1, -1, 6), // scroll mission info up
3670 ui_button_info("MC_08", 1, 304, -1, -1, 8), // scroll mission info down
3671 ui_button_info("MC_09", 613, 160, -1, -1, 9), // scroll players up
3672 ui_button_info("MC_10", 613, 202, -1, -1, 10), // scroll players down
3673 ui_button_info("MC_11", 22, 346, 27, 376, 11), // mission filter
3674 ui_button_info("MC_12", 104, 346, 110, 376, 12), // campaign filter
3675 ui_button_info("MC_13", 392, 341, 328, 364, 13), // cancel
3676 ui_button_info("MC_14", 472, 352, 482, 381, 14), // team 0
3677 ui_button_info("MC_15", 506, 352, 514, 381, 15), // team 1
3678 ui_button_info("MC_16", 539, 346, 539, 381, 16), // kick
3679 ui_button_info("MC_17", 589, 346, 582, 381, 17), // close
3680 ui_button_info("MC_18", 1, 406, -1, -1, 18), // scroll list up
3681 ui_button_info("MC_19", 1, 447, -1, -1, 19), // scroll list down
3682 ui_button_info("MC_20", 499, 434, 436, 423, 20), // host options
3683 ui_button_info("MC_21", 534, 426, -1, -1, 21), // help
3684 ui_button_info("MC_22", 534, 452, -1, -1, 22), // options
3685 ui_button_info("MC_23", 571, 426, 572, 413, 23), // commit
3689 ui_button_info("2_MC_00", 51, 207, 61, 253, 0), // show all missions
3690 ui_button_info("2_MC_01", 122, 207, 124, 253, 1), // show coop missions
3691 ui_button_info("2_MC_02", 193, 207, 194, 253, 2), // show team missions
3692 ui_button_info("2_MC_03", 263, 207, 261, 253, 3), // show dogfight missions
3693 ui_button_info("2_MC_04", 639, 207, 479, 218, 4), // pxo mission refresh
3694 ui_button_info("2_MC_05", 907, 197, 748, 216, 5), // pilot info
3695 ui_button_info("2_MC_06", 1, 258, -1, -1, 6), // scroll mission info up
3696 ui_button_info("2_MC_08", 1, 487, -1, -1, 8), // scroll mission info down
3697 ui_button_info("2_MC_09", 981, 256, -1, -1, 9), // scroll players up
3698 ui_button_info("2_MC_10", 981, 323, -1, -1, 10), // scroll players down
3699 ui_button_info("2_MC_11", 35, 554, 46, 601, 11), // mission filter
3700 ui_button_info("2_MC_12", 166, 554, 174, 601, 12), // campaign filter
3701 ui_button_info("2_MC_13", 628, 545, 559, 582, 13), // cancel
3702 ui_button_info("2_MC_14", 756, 564, 772, 610, 14), // team 0
3703 ui_button_info("2_MC_15", 810, 564, 826, 610, 15), // team 1
3704 ui_button_info("2_MC_16", 862, 554, 872, 610, 16), // kick
3705 ui_button_info("2_MC_17", 943, 554, 949, 610, 17), // close
3706 ui_button_info("2_MC_18", 1, 649, -1, -1, 18), // scroll list up
3707 ui_button_info("2_MC_19", 1, 716, -1, -1, 19), // scroll list down
3708 ui_button_info("2_MC_20", 798, 695, 726, 667, 20), // host options
3709 ui_button_info("2_MC_21", 854, 681, -1, -1, 21), // help
3710 ui_button_info("2_MC_22", 854, 724, -1, -1, 22), // options
3711 ui_button_info("2_MC_23", 914, 681, 932, 667, 23), // commit
3716 #define MULTI_CREATE_NUM_TEXT 0
3718 #define MULTI_CREATE_NUM_TEXT 15
3720 UI_XSTR Multi_create_text[GR_NUM_RESOLUTIONS][MULTI_CREATE_NUM_BUTTONS] = {
3722 // not needed for FS1
3724 {"All", 1256, 36, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_ALL].button},
3725 {"Coop", 1257, 71, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_COOP].button},
3726 {"Team", 1258, 119, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_TEAM].button},
3727 {"Dogfight", 1259, 166, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_DOGFIGHT].button},
3728 {"Refresh Missions", 1260, 229, 130, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_PXO_REFRESH].button},
3729 {"Pilot Info", 1261, 467, 132, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_PILOT_INFO].button},
3730 {"Missions", 1262, 27, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_MISSION_FILTER].button},
3731 {"Campaigns", 1263, 110, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_CAMPAIGN_FILTER].button},
3732 {"Cancel", 387, 328, 364, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_CANCEL].button},
3733 {"1", 1264, 482, 381, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_TEAM0].button},
3734 {"2", 1265, 514, 381, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_TEAM1].button},
3735 {"Kick", 1266, 539, 381, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_KICK].button},
3736 {"Close", 1508, 582, 381, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_CLOSE].button},
3737 {"Host Options", 1267, 436, 423, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_HOST_OPTIONS].button},
3738 {"Commit", 1062, 572, 413, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_ACCEPT].button}
3742 // not needed for FS1
3744 {"All", 1256, 61, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_ALL].button},
3745 {"Coop", 1257, 124, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_COOP].button},
3746 {"Team", 1258, 194, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_TEAM].button},
3747 {"Dogfight", 1259, 261, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_DOGFIGHT].button},
3748 {"Refresh Missions", 1260, 501, 218, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_PXO_REFRESH].button},
3749 {"Pilot Info", 1261, 814, 216, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_PILOT_INFO].button},
3750 {"Missions", 1262, 46, 601, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_MISSION_FILTER].button},
3751 {"Campaigns", 1263, 174, 601, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_CAMPAIGN_FILTER].button},
3752 {"Cancel", 387, 559, 582, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_CANCEL].button},
3753 {"1", 1264, 772, 610, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_TEAM0].button},
3754 {"2", 1265, 826, 610, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_TEAM1].button},
3755 {"Kick", 1266, 872, 610, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_KICK].button},
3756 {"Close", 1508, 949, 610, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_CLOSE].button},
3757 {"Host Options", 1267, 755, 683, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_HOST_OPTIONS].button},
3758 {"Commit", 1062, 932, 667, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_ACCEPT].button}
3763 // squad war checkbox
3764 UI_CHECKBOX Multi_create_sw_checkbox;
3765 const char *Multi_create_sw_checkbox_fname[GR_NUM_RESOLUTIONS] = {
3769 int Multi_create_sw_checkbox_coords[GR_NUM_RESOLUTIONS][2] = {
3777 int Multi_create_sw_checkbox_text[GR_NUM_RESOLUTIONS][2] = {
3786 // game information text areas
3787 int Mc_list_coords[GR_NUM_RESOLUTIONS][4] = {
3800 int Mc_players_coords[GR_NUM_RESOLUTIONS][4] = {
3813 int Mc_info_coords[GR_NUM_RESOLUTIONS][4] = {
3826 // mission icon stuff
3827 int Mc_icon_type_coords[GR_NUM_RESOLUTIONS][2] = {
3829 38, -2 // y is an offset
3832 61, -2 // y is an offset
3836 int Mc_icon_volition_coords[GR_NUM_RESOLUTIONS][2] = {
3838 61, -1 // y is an offset
3841 98, 1 // y is an offset
3845 int Mc_icon_silent_coords[GR_NUM_RESOLUTIONS][2] = {
3847 72, 0 // y is an offset
3850 115, 0 // y is an offset
3854 int Mc_icon_valid_coords[GR_NUM_RESOLUTIONS][2] = {
3856 91, 0 // y is an offset
3859 146, 0 // y is an offset
3863 // mission/campaign list column areas
3864 int Mc_column1_w[GR_NUM_RESOLUTIONS] = {
3869 int Mc_column2_w[GR_NUM_RESOLUTIONS] = {
3874 int Mc_column3_w[GR_NUM_RESOLUTIONS] = {
3879 int Mc_mission_name_x[GR_NUM_RESOLUTIONS] = {
3884 int Mc_mission_count_x[GR_NUM_RESOLUTIONS] = {
3889 int Mc_mission_fname_x[GR_NUM_RESOLUTIONS] = {
3894 int Mc_create_game_text[GR_NUM_RESOLUTIONS][2] = {
3895 {13, 116}, // GR_640
3896 {21, 186} // GR_1024
3899 int Mc_players_text[GR_NUM_RESOLUTIONS][2] = {
3900 {467, 150}, // GR_640
3901 {747, 240} // GR_1024
3904 int Mc_team_text[GR_NUM_RESOLUTIONS][2] = {
3905 {484, 342}, // GR_640
3906 {774, 547} // GR_1024
3909 int Mc_slider_coords[GR_NUM_RESOLUTIONS][4] = {
3911 3, 197, 13, 105 // GR_640
3914 5, 316, 20, 168 // GR_1024
3918 const char *Mc_slider_bitmap[GR_NUM_RESOLUTIONS] = {
3923 // player list control thingie defs
3924 #define MULTI_CREATE_PLIST_MAX_DISPLAY 20
3925 int Multi_create_plist_select_flag; // flag indicating if we have a play selected
3926 short Multi_create_plist_select_id; // the net address of the currently selected player (for lookup)
3928 // master tracker details
3929 int Multi_create_frame_count; // framecount
3930 int Multi_create_mt_tried_login; // attempted to login this server on the MT
3932 // mission filter settings
3933 int Multi_create_filter; // what mode we're in
3935 // game/campaign list control defs
3936 int Multi_create_list_max_display[GR_NUM_RESOLUTIONS] = {
3942 int Multi_create_list_count; // number of items in listbox
3943 int Multi_create_list_mode; // 0 == mission mode, 1 == campaign mode
3944 int Multi_create_list_start; // where to start displaying from
3945 int Multi_create_list_select; // which item is currently highlighted
3946 int Multi_create_files_loaded;
3948 char Multi_create_files_array[MULTI_CREATE_MAX_LIST_ITEMS][MAX_FILENAME_LEN];
3950 int Multi_create_mission_count; // how many we have
3951 int Multi_create_campaign_count;
3952 multi_create_info Multi_create_mission_list[MULTI_CREATE_MAX_LIST_ITEMS];
3953 multi_create_info Multi_create_campaign_list[MULTI_CREATE_MAX_LIST_ITEMS];
3955 // use a pointer for the file list. Will point to either the missions or the campaigns
3956 multi_create_info *Multi_create_file_list;
3958 // LOCAL function definitions
3959 void multi_create_check_buttons();
3960 void multi_create_button_pressed(int n);
3961 void multi_create_init_as_server();
3962 void multi_create_init_as_client();
3963 void multi_create_do_netstuff();
3964 void multi_create_plist_scroll_up();
3965 void multi_create_plist_scroll_down();
3966 void multi_create_plist_process();
3967 void multi_create_plist_blit_normal();
3968 void multi_create_plist_blit_team();
3969 void multi_create_list_scroll_up();
3970 void multi_create_list_scroll_down();
3971 void multi_create_list_do();
3972 void multi_create_list_select_item(int n);
3973 void multi_create_list_blit_icons(int list_index, int y_start);
3974 void multi_create_accept_hit();
3975 void multi_create_draw_filter_buttons();
3976 void multi_create_set_selected_team(int team);
3977 short multi_create_get_mouse_id();
3978 int multi_create_ok_to_commit();
3979 int multi_create_verify_cds();
3980 void multi_create_refresh_pxo();
3981 void multi_create_sw_clicked();
3983 // since we can selectively filter out mission/campaign types we always need to map a selected index (which is relative
3984 // to the displayed list), to an absolute index (which is relative to the total file list - some of which may filtered out)
3985 void multi_create_select_to_filename(int select_index,char *filename);
3986 int multi_create_select_to_index(int select_index);
3988 int Multi_create_should_show_popup = 0;
3991 // sorting function to sort mission lists.. Basic sorting on mission name
3992 int multi_create_sort_func(const void *a, const void *b)
3994 multi_create_info *m1, *m2;
3996 m1 = (multi_create_info *)a;
3997 m2 = (multi_create_info *)b;
3999 return ( strcmp(m1->name, m2->name) );
4002 void multi_create_setup_list_data(int mode)
4004 int idx,should_sort,switched_modes;
4006 // set the current mode
4009 if((Multi_create_list_mode != mode) && (mode != -1)){
4010 Multi_create_list_mode = mode;
4013 // set up the list pointers
4014 if ( Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ) {
4015 Multi_create_file_list = Multi_create_mission_list;
4016 } else if ( Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ) {
4017 Multi_create_file_list = Multi_create_campaign_list;
4023 // get the mission count based upon the filter selected
4024 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
4025 switch(Multi_create_filter){
4026 case MISSION_TYPE_MULTI:
4027 Multi_create_list_count = Multi_create_mission_count;
4030 Multi_create_list_count = 0;
4031 // find all missions which match
4032 for(idx=0;idx<Multi_create_mission_count;idx++){
4033 if(Multi_create_mission_list[idx].flags & Multi_create_filter){
4034 Multi_create_list_count++;
4038 // if we switched modes and we have more than 0 items, sort them
4039 if(switched_modes && (Multi_create_list_count > 0)){
4044 } else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
4045 switch(Multi_create_filter){
4046 case MISSION_TYPE_MULTI:
4047 Multi_create_list_count = Multi_create_campaign_count;
4050 Multi_create_list_count = 0;
4051 // find all missions which match
4052 for(idx=0;idx<Multi_create_campaign_count;idx++){
4053 if(Multi_create_campaign_list[idx].flags & Multi_create_filter){
4054 Multi_create_list_count++;
4058 // if we switched modes and we have more than 0 items, sort them
4059 if(switched_modes && (Multi_create_list_count > 0)){
4066 // reset the list start and selected indices
4067 Multi_create_list_start = 0;
4068 Multi_create_list_select = -1;
4069 multi_create_list_select_item(Multi_create_list_start);
4071 // sort the list of missions if necessary
4073 qsort(Multi_create_file_list, Multi_create_list_count, sizeof(multi_create_info), multi_create_sort_func);
4078 Multi_create_slider.set_numberItems(Multi_create_list_count > Multi_create_list_max_display[gr_screen.res] ? Multi_create_list_count-Multi_create_list_max_display[gr_screen.res] : 0);
4082 void multi_create_game_init()
4087 // now make sure to initialze various netgame stuff based upon whether we're on a standalone or not
4088 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4089 multi_create_init_as_server();
4091 multi_create_init_as_client();
4094 // initialize the player list data
4095 Multi_create_plist_select_flag = 0;
4096 Multi_create_plist_select_id = -1;
4098 // create the interface window
4099 Multi_create_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
4100 Multi_create_window.set_mask_bmap(Multi_create_bitmap_mask_fname[gr_screen.res]);
4102 // load the background bitmap
4103 Multi_create_bitmap = bm_load(Multi_create_bitmap_fname[gr_screen.res]);
4104 if(Multi_create_bitmap < 0){
4105 // we failed to load the bitmap - this is very bad
4109 // close any previous existing instances of the chatbox and create a new one
4113 // load the help overlay
4114 help_overlay_load(MULTI_CREATE_OVERLAY);
4115 help_overlay_set_state(MULTI_CREATE_OVERLAY, 0);
4117 // initialize the common notification messaging
4118 multi_common_notify_init();
4120 // use the common interface palette
4121 multi_common_set_palette();
4123 // create the interface buttons
4124 for(idx=0; idx<MULTI_CREATE_NUM_BUTTONS; idx++){
4125 b = &Multi_create_buttons[gr_screen.res][idx];
4127 // create the object
4128 b->button.create(&Multi_create_window, "", b->x, b->y, 1, 1, ((idx == MC_SCROLL_LIST_UP) || (idx == MC_SCROLL_LIST_DOWN)), 1);
4130 // set the sound to play when highlighted
4131 b->button.set_highlight_action(common_play_highlight_sound);
4133 // set the ani for the button
4134 b->button.set_bmaps(b->filename);
4137 b->button.link_hotspot(b->hotspot);
4139 // some special case stuff for the pxo refresh button
4140 if(idx == MC_PXO_REFRESH){
4141 // if not a PXO game, or if I'm not a server disable and hide the button
4142 if(!MULTI_IS_TRACKER_GAME || !MULTIPLAYER_MASTER){
4144 b->button.disable();
4150 for(idx=0; idx<MULTI_CREATE_NUM_TEXT; idx++){
4151 Multi_create_window.add_XSTR(&Multi_create_text[gr_screen.res][idx]);
4154 // if this is a PXO game, enable the squadwar checkbox
4155 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);
4156 Multi_create_sw_checkbox.set_bmaps(Multi_create_sw_checkbox_fname[gr_screen.res], 6, 0);
4157 if(!MULTI_IS_TRACKER_GAME){
4158 Multi_create_sw_checkbox.hide();
4159 Multi_create_sw_checkbox.disable();
4163 // disable squad war button in demo
4164 Multi_create_sw_checkbox.hide();
4165 Multi_create_sw_checkbox.disable();
4168 // initialize the mission type filtering mode
4169 Multi_create_filter = MISSION_TYPE_MULTI;
4171 // initialize the list mode, and load in a list
4172 memset(Multi_create_mission_list, 0, sizeof(multi_create_info) * MULTI_CREATE_MAX_LIST_ITEMS);
4173 memset(Multi_create_campaign_list, 0, sizeof(multi_create_info) * MULTI_CREATE_MAX_LIST_ITEMS);
4174 for(idx=0; idx<MULTI_CREATE_MAX_LIST_ITEMS; idx++){
4175 Multi_create_mission_list[idx].valid_status = MVALID_STATUS_UNKNOWN;
4176 Multi_create_campaign_list[idx].valid_status = MVALID_STATUS_UNKNOWN;
4178 Multi_create_list_mode = MULTI_CREATE_SHOW_MISSIONS;
4179 Multi_create_list_start = -1;
4180 Multi_create_list_select = -1;
4181 Multi_create_list_count = 0;
4184 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);
4187 // create the player list select button
4188 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);
4189 Multi_create_player_select_button.hide();
4191 // create the mission/campaign list select button
4192 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);
4193 Multi_create_list_select_button.hide();
4195 // set hotkeys for a couple of things.
4196 Multi_create_buttons[gr_screen.res][MC_ACCEPT].button.set_hotkey(KEY_CTRLED+SDLK_RETURN);
4198 // init some master tracker stuff
4199 Multi_create_frame_count = 0;
4200 Multi_create_mt_tried_login = 0;
4202 // remove campaign flags
4203 Game_mode &= ~(GM_CAMPAIGN_MODE);
4205 // send any pilots as appropriate
4206 multi_data_send_my_junk();
4207 Multi_create_file_list = Multi_create_mission_list;
4209 Multi_create_campaign_count = 0;
4210 Multi_create_mission_count = 0;
4211 Multi_create_files_loaded = 0;
4214 void multi_create_game_do()
4218 const char *loading_str = XSTR("Loading", 1336);
4222 // set this if we want to show the pilot info popup
4223 Multi_create_should_show_popup = 0;
4225 // first thing is to load the files
4226 if ( !Multi_create_files_loaded ) {
4227 // if I am a client, send a list request to the server for the missions
4228 if ( MULTIPLAYER_CLIENT ) {
4229 send_mission_list_request( MISSION_LIST_REQUEST );
4233 loading_bitmap = bm_load(Multi_create_loading_fname[gr_screen.res]);
4235 // draw the background, etc
4237 GR_MAYBE_CLEAR_RES(Multi_create_bitmap);
4238 if(Multi_create_bitmap != -1){
4239 gr_set_bitmap(Multi_create_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4243 if ( loading_bitmap > -1 ){
4244 gr_set_bitmap(loading_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4246 gr_bitmap( Please_wait_coords[gr_screen.res][MC_X_COORD], Please_wait_coords[gr_screen.res][MC_Y_COORD] );
4248 // draw "Loading" on it
4250 gr_set_color_fast(&Color_normal);
4252 gr_get_string_size(&str_w, &str_h, loading_str);
4253 gr_string((gr_screen.max_w - str_w) / 2, (gr_screen.max_h - str_h) / 2, loading_str);
4259 multi_create_list_load_missions();
4260 multi_create_list_load_campaigns();
4262 // if this is a tracker game, validate missions
4263 if(MULTI_IS_TRACKER_GAME){
4264 multi_update_valid_missions();
4267 // update the file list
4268 multi_create_setup_list_data(MULTI_CREATE_SHOW_MISSIONS);
4271 // don't bother setting netgame state if ont the server
4272 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4273 Netgame.game_state = NETGAME_STATE_FORMING;
4274 send_netgame_update_packet();
4277 // if we're on the standalone we have to tell him that we're now in the host setup screen
4278 Net_player->state = NETPLAYER_STATE_HOST_SETUP;
4279 send_netplayer_update_packet();
4281 Multi_create_files_loaded = 1;
4284 int k = chatbox_process();
4285 k = Multi_create_window.process(k,0);
4288 // same as the cancel button
4290 if(help_overlay_active(MULTI_CREATE_OVERLAY)){
4291 help_overlay_set_state(MULTI_CREATE_OVERLAY,0);
4293 gamesnd_play_iface(SND_USER_SELECT);
4294 multi_quit_game(PROMPT_HOST);
4299 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
4300 help_overlay_set_state(MULTI_CREATE_OVERLAY, 0);
4303 // process any button clicks
4304 multi_create_check_buttons();
4306 // do any network related stuff
4307 multi_create_do_netstuff();
4309 // draw the background, etc
4311 GR_MAYBE_CLEAR_RES(Multi_create_bitmap);
4312 if(Multi_create_bitmap != -1){
4313 gr_set_bitmap(Multi_create_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4317 // if we're not in team vs. team mode, don't draw the team buttons
4318 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
4319 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.hide();
4320 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.hide();
4321 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.disable();
4322 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.disable();
4324 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.enable();
4325 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.enable();
4326 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.unhide();
4327 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.unhide();
4330 // draw the window itself
4331 Multi_create_window.draw();
4333 gr_set_color_fast(&Color_normal);
4336 // draw Create Game text
4337 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));
4339 // draw players text
4340 gr_string(Mc_players_text[gr_screen.res][MC_X_COORD], Mc_players_text[gr_screen.res][MC_Y_COORD], XSTR("Players", 1269));
4342 // draw players text
4343 gr_string(Mc_team_text[gr_screen.res][MC_X_COORD], Mc_team_text[gr_screen.res][MC_Y_COORD], XSTR("Team", 1258));
4346 // process and display the player list
4347 // NOTE : this must be done before the buttons are checked to insure that a player hasn't dropped
4348 multi_create_plist_process();
4349 if(Netgame.type_flags & NG_TYPE_TEAM){
4350 multi_create_plist_blit_team();
4352 multi_create_plist_blit_normal();
4355 // process and display the game/campaign list
4356 multi_create_list_do();
4358 // draw the correct mission filter button
4359 multi_create_draw_filter_buttons();
4361 // display any text in the info area
4362 multi_common_render_text();
4364 // display any pending notification messages
4365 multi_common_notify_do();
4367 // force the correct mission/campaign button to light up
4368 if( Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ){
4369 Multi_create_buttons[gr_screen.res][MC_MISSION_FILTER].button.draw_forced(2);
4371 Multi_create_buttons[gr_screen.res][MC_CAMPAIGN_FILTER].button.draw_forced(2);
4374 // force draw the closed button if it is toggled on
4375 if(Netgame.flags & NG_FLAG_TEMP_CLOSED){
4376 Multi_create_buttons[gr_screen.res][MC_CLOSE].button.draw_forced(2);
4379 // process and show the chatbox thingie
4383 Multi_create_window.draw_tooltip();
4385 // display the voice status indicator
4386 multi_common_voice_display_status();
4388 // blit the help overlay if necessary
4389 help_overlay_maybe_blit(MULTI_CREATE_OVERLAY);
4392 if(MULTI_IS_TRACKER_GAME){
4393 if(Netgame.type_flags & NG_TYPE_SW){
4394 gr_set_color_fast(&Color_bright);
4396 gr_set_color_fast(&Color_normal);
4398 gr_string(Multi_create_sw_checkbox_text[gr_screen.res][0], Multi_create_sw_checkbox_text[gr_screen.res][1], "SquadWar");
4404 // if we're supposed to show the pilot info popup, do it now
4405 if(Multi_create_should_show_popup){
4406 // get the player index and address of the player item the mouse is currently over
4407 if(Multi_create_plist_select_flag){
4408 player_index = find_player_id(Multi_create_plist_select_id);
4409 if(player_index != -1){
4410 multi_pinfo_popup(&Net_players[player_index]);
4415 // increment the frame count
4416 Multi_create_frame_count++;
4419 void multi_create_game_close()
4421 // unload any bitmaps
4422 if(!bm_unload(Multi_create_bitmap)){
4423 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_create_bitmap_fname[gr_screen.res]));
4426 // unload the help overlay
4427 help_overlay_unload(MULTI_CREATE_OVERLAY);
4429 // destroy the chatbox
4432 // destroy the UI_WINDOW
4433 Multi_create_window.destroy();
4436 void multi_create_check_buttons()
4439 for(idx=0;idx<MULTI_CREATE_NUM_BUTTONS;idx++){
4440 // we only really need to check for one button pressed at a time, so we can break after
4442 if(Multi_create_buttons[gr_screen.res][idx].button.pressed()){
4443 multi_create_button_pressed(idx);
4448 // if the squad war checkbox was clicked
4449 if(Multi_create_sw_checkbox.changed()){
4450 multi_create_sw_clicked();
4454 void multi_create_button_pressed(int n)
4460 gamesnd_play_iface(SND_USER_SELECT);
4461 multi_quit_game(PROMPT_HOST);
4464 // if valid commit conditions have not been met
4465 if(!multi_create_ok_to_commit()){
4470 multi_create_accept_hit();
4475 if(!help_overlay_active(MULTI_CREATE_OVERLAY)){
4476 help_overlay_set_state(MULTI_CREATE_OVERLAY,1);
4478 help_overlay_set_state(MULTI_CREATE_OVERLAY,0);
4482 // scroll the info text box up
4483 case MC_SCROLL_INFO_UP:
4484 multi_common_scroll_text_up();
4487 // scroll the info text box down
4488 case MC_SCROLL_INFO_DOWN:
4489 multi_common_scroll_text_down();
4492 // scroll the player list up
4493 case MC_SCROLL_PLAYERS_UP:
4494 multi_create_plist_scroll_up();
4497 // scroll the player list down
4498 case MC_SCROLL_PLAYERS_DOWN:
4499 multi_create_plist_scroll_down();
4502 // scroll the game/campaign list up
4503 case MC_SCROLL_LIST_UP:
4504 multi_create_list_scroll_up();
4506 Multi_create_slider.forceUp(); // move slider up
4510 // scroll the game/campaign list down
4511 case MC_SCROLL_LIST_DOWN:
4512 multi_create_list_scroll_down();
4514 Multi_create_slider.forceDown(); // move slider down
4518 // go to the options screen
4520 gamesnd_play_iface(SND_USER_SELECT);
4521 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
4524 // show all missions
4526 if(Multi_create_filter != MISSION_TYPE_MULTI){
4527 gamesnd_play_iface(SND_USER_SELECT);
4528 Multi_create_filter = MISSION_TYPE_MULTI;
4529 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4531 gamesnd_play_iface(SND_GENERAL_FAIL);
4535 // show cooperative missions
4537 if(Multi_create_filter != MISSION_TYPE_MULTI_COOP){
4538 gamesnd_play_iface(SND_USER_SELECT);
4539 Multi_create_filter = MISSION_TYPE_MULTI_COOP;
4540 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4542 gamesnd_play_iface(SND_GENERAL_FAIL);
4546 // show team vs. team missions
4548 if(Multi_create_filter != MISSION_TYPE_MULTI_TEAMS){
4549 gamesnd_play_iface(SND_USER_SELECT);
4550 Multi_create_filter = MISSION_TYPE_MULTI_TEAMS;
4551 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4553 gamesnd_play_iface(SND_GENERAL_FAIL);
4557 // show dogfight missions
4559 case MC_SHOW_DOGFIGHT:
4560 if (Multi_create_filter != MISSION_TYPE_MULTI_DOGFIGHT){
4561 gamesnd_play_iface(SND_USER_SELECT);
4562 Multi_create_filter = MISSION_TYPE_MULTI_DOGFIGHT;
4563 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4565 gamesnd_play_iface(SND_GENERAL_FAIL);
4570 // toggle temporary netgame closed on/off
4572 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4573 Netgame.flags ^= NG_FLAG_TEMP_CLOSED;
4575 Netgame.options.flags |= MLO_FLAG_TEMP_CLOSED;
4576 multi_options_update_netgame();
4578 gamesnd_play_iface(SND_USER_SELECT);
4581 // kick the currently selected player (if possible)
4583 // lookup the player at the specified index
4584 if(Multi_create_plist_select_flag){
4585 idx = find_player_id(Multi_create_plist_select_id);
4586 // kick him - but don't ban him
4588 multi_kick_player(idx,0);
4593 // switch to individual mission mode and load in a list
4594 case MC_MISSION_FILTER:
4595 if(Multi_create_list_mode != MULTI_CREATE_SHOW_MISSIONS){
4596 Netgame.campaign_mode = MP_SINGLE;
4598 gamesnd_play_iface(SND_USER_SELECT);
4600 // update the file list
4601 multi_create_setup_list_data(MULTI_CREATE_SHOW_MISSIONS);
4603 gamesnd_play_iface(SND_GENERAL_FAIL);
4607 // switch to campaign mode and load in a list
4608 case MC_CAMPAIGN_FILTER:
4609 // switch off squad war
4610 Multi_create_sw_checkbox.set_state(0);
4611 Netgame.type_flags = NG_TYPE_COOP;
4613 if(Multi_create_list_mode != MULTI_CREATE_SHOW_CAMPAIGNS){
4614 Netgame.campaign_mode = MP_CAMPAIGN;
4616 gamesnd_play_iface(SND_USER_SELECT);
4618 // update the file list
4619 multi_create_setup_list_data(MULTI_CREATE_SHOW_CAMPAIGNS);
4621 gamesnd_play_iface(SND_GENERAL_FAIL);
4625 // attempt to set the selected player's team
4627 multi_create_set_selected_team(0);
4628 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4629 multi_team_send_update();
4633 // attempt to set the selected player's team
4635 multi_create_set_selected_team(1);
4636 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4637 multi_team_send_update();
4641 // popup the pilot info dialog for the currently selected pilot (will occur at the end of the frame)
4643 Multi_create_should_show_popup = 1;
4646 // go to the host options screen
4647 case MC_HOST_OPTIONS:
4648 gamesnd_play_iface(SND_USER_SELECT);
4649 gameseq_post_event(GS_EVENT_MULTI_HOST_OPTIONS);
4652 // refresh PXO file list
4653 case MC_PXO_REFRESH:
4654 if(!MULTI_IS_TRACKER_GAME){
4657 multi_create_refresh_pxo();
4661 gamesnd_play_iface(SND_GENERAL_FAIL);
4662 multi_common_add_notify(XSTR("Not implemented yet!",760));
4667 // do stuff like pinging servers, sending out requests, etc
4668 void multi_create_do_netstuff()
4672 // if not on a standalone
4673 void multi_create_init_as_server()
4675 // set me up as the host and master
4676 Net_player->flags |= (NETINFO_FLAG_AM_MASTER | NETINFO_FLAG_GAME_HOST);
4679 // if on a standalone
4680 void multi_create_init_as_client()
4682 Net_player->flags |= NETINFO_FLAG_GAME_HOST;
4685 // scroll up through the player list
4686 void multi_create_plist_scroll_up()
4688 gamesnd_play_iface(SND_GENERAL_FAIL);
4691 // scroll down through the player list
4692 void multi_create_plist_scroll_down()
4694 gamesnd_play_iface(SND_GENERAL_FAIL);
4697 void multi_create_plist_process()
4699 int test_count,idx,player_index;
4701 // first determine if there are 0 players in the game. This should never happen since the host is _always_ in the game
4703 for(idx=0;idx<MAX_PLAYERS;idx++){
4704 // count anyone except the standalone server (if applicable)
4705 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
4709 if(test_count <= 0){
4713 // if we had a selected item but that player has left, select myself instead
4714 if(Multi_create_plist_select_flag){
4715 player_index = find_player_id(Multi_create_plist_select_id);
4716 if(player_index == -1){
4717 Multi_create_plist_select_id = Net_player->player_id;
4720 Multi_create_plist_select_flag = 1;
4721 Multi_create_plist_select_id = Net_player->player_id;
4724 // if the player has clicked somewhere in the player list area
4725 if(Multi_create_player_select_button.pressed()){
4728 // get the player index and address of the player item the mouse is currently over
4729 player_id = multi_create_get_mouse_id();
4730 player_index = find_player_id(player_id);
4731 if(player_index != -1){
4732 Multi_create_plist_select_flag = 1;
4733 Multi_create_plist_select_id = player_id;
4738 void multi_create_plist_blit_normal()
4741 char str[CALLSIGN_LEN+5];
4742 int y_start = Mc_players_coords[gr_screen.res][MC_Y_COORD];
4745 // display all the players
4746 for(idx=0;idx<MAX_PLAYERS;idx++){
4747 // count anyone except the standalone server (if applicable)
4748 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
4752 // highlight him if he's the host
4753 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4754 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4755 gr_set_color_fast(&Color_text_active_hi);
4757 gr_set_color_fast(&Color_bright);
4760 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4761 gr_set_color_fast(&Color_text_active);
4763 gr_set_color_fast(&Color_text_normal);
4767 // optionally draw his CD status
4768 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4769 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4770 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4772 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4775 // make sure the string will fit, then display it
4776 strcpy(str,Net_players[idx].player->callsign);
4777 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4778 strcat(str,XSTR("(O)",787)); // [[ Observer ]]
4780 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4781 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4788 void multi_create_plist_blit_team()
4791 char str[CALLSIGN_LEN+1];
4792 int y_start = Mc_players_coords[gr_screen.res][MC_Y_COORD];
4795 // display all the red players first
4796 for(idx=0;idx<MAX_PLAYERS;idx++){
4797 // count anyone except the standalone server (if applicable)
4798 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
4799 // reset total offset
4802 // highlight him if he's the host
4803 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4804 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4805 gr_set_color_fast(&Color_text_active_hi);
4807 // be sure to blit the correct team button
4808 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.draw_forced(2);
4810 gr_set_color_fast(&Color_bright);
4813 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4814 gr_set_color_fast(&Color_text_active);
4816 // be sure to blit the correct team button
4817 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.draw_forced(2);
4819 gr_set_color_fast(&Color_text_normal);
4823 // optionally draw his CD status
4824 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4825 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4826 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4828 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4831 // blit the red team indicator
4832 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
4833 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
4834 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4835 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4837 total_offset += Multi_common_icon_dims[MICON_TEAM0_SELECT][0] + 1;
4840 if(Multi_common_icons[MICON_TEAM0] != -1){
4841 gr_set_bitmap(Multi_common_icons[MICON_TEAM0], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4842 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4844 total_offset += Multi_common_icon_dims[MICON_TEAM0][0] + 1;
4848 // make sure the string will fit
4849 strcpy(str,Net_players[idx].player->callsign);
4850 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4851 strcat(str,XSTR("(O)",787));
4853 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4855 // display him in the correct half of the list depending on his team
4856 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4861 // display all the green players next
4862 for(idx=0;idx<MAX_PLAYERS;idx++){
4863 // count anyone except the standalone server (if applicable)
4864 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
4865 // reset total offset
4868 // highlight him if he's the host
4869 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4870 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4871 gr_set_color_fast(&Color_text_active_hi);
4873 // be sure to blit the correct team button
4874 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.draw_forced(2);
4876 gr_set_color_fast(&Color_bright);
4879 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4880 gr_set_color_fast(&Color_text_active);
4882 // be sure to blit the correct team button
4883 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.draw_forced(2);
4885 gr_set_color_fast(&Color_text_normal);
4889 // optionally draw his CD status
4890 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4891 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4892 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4894 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4897 // blit the red team indicator
4898 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
4899 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
4900 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4901 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4903 total_offset += Multi_common_icon_dims[MICON_TEAM1_SELECT][0] + 1;
4906 if(Multi_common_icons[MICON_TEAM1] != -1){
4907 gr_set_bitmap(Multi_common_icons[MICON_TEAM1], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4908 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4910 total_offset += Multi_common_icon_dims[MICON_TEAM1][0] + 1;
4914 // make sure the string will fit
4915 strcpy(str,Net_players[idx].player->callsign);
4916 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4917 strcat(str,XSTR("(O)",787));
4919 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4921 // display him in the correct half of the list depending on his team
4922 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4928 void multi_create_list_scroll_up()
4930 if(Multi_create_list_start > 0){
4931 Multi_create_list_start--;
4933 gamesnd_play_iface(SND_SCROLL);
4935 gamesnd_play_iface(SND_GENERAL_FAIL);
4939 void multi_create_list_scroll_down()
4941 if((Multi_create_list_count - Multi_create_list_start) > Multi_create_list_max_display[gr_screen.res]){
4942 Multi_create_list_start++;
4944 gamesnd_play_iface(SND_SCROLL);
4946 gamesnd_play_iface(SND_GENERAL_FAIL);
4950 // gets a list of multiplayer misisons
4951 void multi_create_list_load_missions()
4953 char *fname, mission_name[NAME_LENGTH+1];
4954 char wild_card[256];
4957 memset(wild_card, 0, 256);
4958 strcpy(wild_card, NOX("*"));
4959 strcat(wild_card, FS_MISSION_FILE_EXT);
4960 file_count = cf_get_file_list_preallocated(MULTI_CREATE_MAX_LIST_ITEMS, Multi_create_files_array, NULL, CF_TYPE_MISSIONS, wild_card);
4961 Multi_create_mission_count = 0;
4963 // maybe create a standalone dialog
4964 if(Game_mode & GM_STANDALONE_SERVER){
4965 std_create_gen_dialog("Loading missions");
4966 std_gen_set_text("Mission:", 1);
4969 for(idx = 0; idx < file_count; idx++){
4970 int flags,max_players;
4974 fname = Multi_create_files_array[idx];
4976 // tack on any necessary file extension
4977 filename = cf_add_ext( fname, FS_MISSION_FILE_EXT );
4979 // for multiplayer beta builds, only accept builtin missions
4980 #if defined(MULTIPLAYER_BETA_BUILD) || defined(FS2_DEMO)
4981 if(game_find_builtin_mission(filename) == NULL){
4984 #elif defined(PD_BUILD)
4985 if((game_find_builtin_mission(filename) == NULL) && !strstr(filename, "peterdrake")){
4990 if(Game_mode & GM_STANDALONE_SERVER){
4991 std_gen_set_text(filename, 2);
4994 flags = mission_parse_is_multi(filename, mission_name);
4996 // if the mission is a multiplayer mission, then add it to the mission list
4998 max_players = mission_parse_get_multi_mission_info( filename );
4999 m_respawn = The_mission.num_respawns;
5001 if ( Multi_create_mission_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
5002 multi_create_info *mcip;
5004 mcip = &Multi_create_mission_list[Multi_create_mission_count];
5005 strcpy(mcip->filename, filename );
5006 strcpy(mcip->name, mission_name );
5007 mcip->flags = flags;
5008 mcip->respawn = m_respawn;
5009 mcip->max_players = (ubyte)max_players;
5011 // get any additional information for possibly builtin missions
5012 fs_builtin_mission *fb = game_find_builtin_mission(filename);
5016 Multi_create_mission_count++;
5022 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);
5025 // maybe create a standalone dialog
5026 if(Game_mode & GM_STANDALONE_SERVER){
5027 std_destroy_gen_dialog();
5031 void multi_create_list_load_campaigns()
5034 int idx, file_count;
5035 int campaign_type,max_players;
5037 char wild_card[256];
5039 // maybe create a standalone dialog
5040 if(Game_mode & GM_STANDALONE_SERVER){
5041 std_create_gen_dialog("Loading campaigns");
5042 std_gen_set_text("Campaign:", 1);
5045 Multi_create_campaign_count = 0;
5046 memset(wild_card, 0, 256);
5047 strcpy(wild_card, NOX("*"));
5048 strcat(wild_card, FS_CAMPAIGN_FILE_EXT);
5049 file_count = cf_get_file_list_preallocated(MULTI_CREATE_MAX_LIST_ITEMS, Multi_create_files_array, NULL, CF_TYPE_MISSIONS, wild_card);
5050 for(idx = 0; idx < file_count; idx++){
5052 char *filename, name[NAME_LENGTH];
5054 fname = Multi_create_files_array[idx];
5056 // tack on any necessary file extension
5057 filename = cf_add_ext( fname, FS_CAMPAIGN_FILE_EXT );
5059 // for multiplayer beta builds, only accept builtin missions
5060 #if defined(MULTIPLAYER_BETA_BUILD) || defined(FS2_DEMO)
5061 if(game_find_builtin_mission(filename) == NULL){
5064 #elif defined(PD_BUILD)
5065 if((game_find_builtin_mission(filename) == NULL) && !strstr(filename, "peterdrake")){
5070 if(Game_mode & GM_STANDALONE_SERVER){
5071 std_gen_set_text(filename, 2);
5074 // if the campaign is a multiplayer campaign, then add the data to the campaign list items
5075 flags = mission_campaign_parse_is_multi( filename, name );
5076 if( flags != CAMPAIGN_TYPE_SINGLE && mission_campaign_get_info(filename,title,&campaign_type,&max_players)) {
5077 if ( Multi_create_campaign_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
5078 multi_create_info *mcip;
5080 mcip = &Multi_create_campaign_list[Multi_create_campaign_count];
5081 strcpy(mcip->filename, filename );
5082 strcpy(mcip->name, name );
5084 // setup various flags
5085 if ( flags == CAMPAIGN_TYPE_MULTI_COOP ){
5086 mcip->flags = MISSION_TYPE_MULTI_COOP | MISSION_TYPE_MULTI;
5087 } else if ( flags == CAMPAIGN_TYPE_MULTI_TEAMS ) {
5088 mcip->flags = MISSION_TYPE_MULTI_TEAMS | MISSION_TYPE_MULTI;
5090 Int3(); // bogus campaign multi type -- find allender
5093 // 0 respawns for campaign files (should be contained within the mission files themselves)
5096 // 0 max players for campaign files
5097 mcip->max_players = (unsigned char)max_players;
5099 // get any additional information for possibly builtin missions
5100 fs_builtin_mission *fb = game_find_builtin_mission(filename);
5104 Multi_create_campaign_count++;
5109 // maybe create a standalone dialog
5110 if(Game_mode & GM_STANDALONE_SERVER){
5111 std_destroy_gen_dialog();
5115 void multi_create_list_do()
5118 int start_index,stop_index;
5119 char selected_name[255];
5121 // bail early if there aren't any selectable items
5122 if(Multi_create_list_count == 0){
5126 // first check to see if the user has clicked on an item
5127 if(Multi_create_list_select_button.pressed()){
5129 Multi_create_list_select_button.get_mouse_pos(NULL,&y);
5132 // make sure we are selectedin valid indices
5133 if((item < Multi_create_list_max_display[gr_screen.res]) && (item >= 0)){
5134 item += Multi_create_list_start;
5136 if(item < Multi_create_list_count){
5137 multi_create_list_select_item(item);
5138 gamesnd_play_iface(SND_IFACE_MOUSE_CLICK);
5143 // bail early if we don't have a start position
5144 if(Multi_create_list_start == -1){
5148 // display the list of individual campaigns/missions
5150 int y_start = Mc_list_coords[gr_screen.res][MC_Y_COORD];
5152 start_index = multi_create_select_to_index(Multi_create_list_start);
5153 stop_index = Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ? Multi_create_mission_count : Multi_create_campaign_count;
5154 for(idx=start_index; idx<stop_index; idx++){
5155 // see if we should drop out
5156 if(count == Multi_create_list_max_display[gr_screen.res]){
5160 // see if we should filter out this mission
5161 if ( !(Multi_create_file_list[idx].flags & Multi_create_filter) ){
5165 // highlight the selected item
5166 multi_create_select_to_filename(Multi_create_list_select,selected_name);
5167 if(!strcmp(selected_name,Multi_create_file_list[idx].filename)){
5168 gr_set_color_fast(&Color_text_selected);
5170 gr_set_color_fast(&Color_text_normal);
5173 // draw the type icon
5174 multi_create_list_blit_icons(idx, y_start);
5176 // force fit the mission name string
5177 strcpy(selected_name,Multi_create_file_list[idx].name);
5178 gr_force_fit_string(selected_name,255,Mc_column1_w[gr_screen.res]);
5179 gr_string(Mc_mission_name_x[gr_screen.res],y_start,selected_name);
5181 // draw the max players if in mission mode
5182 sprintf(selected_name,"%d",(int)Multi_create_file_list[idx].max_players);
5183 gr_string(Mc_mission_count_x[gr_screen.res],y_start,selected_name);
5185 // force fit the mission filename string
5186 strcpy(selected_name,Multi_create_file_list[idx].filename);
5187 gr_force_fit_string(selected_name,255,Mc_column3_w[gr_screen.res]);
5188 gr_string(Mc_mission_fname_x[gr_screen.res],y_start,selected_name);
5195 // takes care of stuff like changing indices around and setting up the netgame structure
5196 void multi_create_list_select_item(int n)
5198 int abs_index,campaign_type,max_players;
5199 char title[NAME_LENGTH+1];
5200 netgame_info ng_temp;
5203 char *campaign_desc;
5205 // if not on the standalone server
5206 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5209 // on the standalone
5211 memset(&ng_temp,0,sizeof(netgame_info));
5215 if ( n != Multi_create_list_select ) {
5216 // check to see if this is a valid index, and bail if it is not
5217 abs_index = multi_create_select_to_index(n);
5218 if(abs_index == -1){
5222 Multi_create_list_select = n;
5224 // set the mission name
5225 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5226 multi_create_select_to_filename(n,ng->mission_name);
5228 multi_create_select_to_filename(n,ng->campaign_name);
5231 // make sure the netgame type is properly set
5232 int old_type = Netgame.type_flags;
5233 abs_index = multi_create_select_to_index(n);
5234 if(abs_index != -1){
5235 if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_TEAMS){
5236 // if we're in squad war mode, leave it as squad war
5237 if(old_type & NG_TYPE_SW){
5238 ng->type_flags = NG_TYPE_SW;
5240 ng->type_flags = NG_TYPE_TVT;
5242 } else if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_COOP){
5243 ng->type_flags = NG_TYPE_COOP;
5244 } else if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_DOGFIGHT){
5245 ng->type_flags = NG_TYPE_DOGFIGHT;
5249 // if we're no longer in a TvT game, just uncheck the squadwar checkbox
5250 if(!(ng->type_flags & NG_TYPE_TEAM)){
5251 Multi_create_sw_checkbox.set_state(0);
5254 // if we switched from something else to team vs. team mode, do some special processing
5255 if((ng->type_flags & NG_TYPE_TEAM) && (ng->type_flags != old_type) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
5259 switch(Multi_create_list_mode){
5260 case MULTI_CREATE_SHOW_MISSIONS:
5261 // don't forget to update the info box window thingie
5262 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5263 ship_init(); // mwa -- 10/15/97. Call this function to reset number of ships in mission
5264 ng->max_players = mission_parse_get_multi_mission_info( ng->mission_name );
5266 SDL_assert(ng->max_players > 0);
5267 strcpy(ng->title,The_mission.name);
5269 // set the information area text
5270 multi_common_set_text(The_mission.mission_desc);
5272 // if we're on the standalone, send a request for the description
5274 send_netgame_descript_packet(&Netgame.server_addr,0);
5275 multi_common_set_text("");
5278 // set the respawns as appropriate
5279 if(Netgame.options.respawn <= Multi_create_file_list[abs_index].respawn){
5280 ng->respawn = Netgame.options.respawn;
5281 nprintf(("Network","Using netgame options for respawn count (%d %d)\n",Netgame.options.respawn,Multi_create_file_list[abs_index].respawn));
5283 ng->respawn = Multi_create_file_list[abs_index].respawn;
5284 nprintf(("Network","Using mission settings for respawn count (%d %d)\n",Netgame.options.respawn,Multi_create_file_list[abs_index].respawn));
5287 case MULTI_CREATE_SHOW_CAMPAIGNS:
5288 // if not on the standalone server
5289 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5290 // get the campaign info
5291 memset(title,0,NAME_LENGTH+1);
5292 if(!mission_campaign_get_info(ng->campaign_name,title,&campaign_type,&max_players, &campaign_desc)) {
5293 memset(ng->campaign_name,0,NAME_LENGTH+1);
5294 ng->max_players = 0;
5296 // if we successfully got the # of players
5298 memset(ng->title,0,NAME_LENGTH+1);
5299 strcpy(ng->title,title);
5300 ng->max_players = max_players;
5303 nprintf(("Network","MC MAX PLAYERS : %d\n",ng->max_players));
5305 // set the information area text
5306 // multi_common_set_text(ng->title);
5307 multi_common_set_text(campaign_desc);
5309 // if on the standalone server, send a request for the description
5311 // no descriptions currently kept for campaigns
5314 // netgame respawns are always 0 for campaigns (until the first mission is loaded)
5319 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5321 send_netgame_update_packet();
5323 // update all machines about stuff like respawns, etc.
5324 multi_options_update_netgame();
5326 multi_options_update_mission(ng, Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ? 1 : 0);
5331 void multi_create_list_blit_icons(int list_index, int y_start)
5333 multi_create_info *mcip;
5334 fs_builtin_mission *fb;
5337 // get a pointer to the list item
5338 max_index = Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ? Multi_create_mission_count - 1 : Multi_create_campaign_count - 1;
5339 if((list_index < 0) || (list_index > max_index)){
5342 mcip = &Multi_create_file_list[list_index];
5344 // blit the multiplayer type icons
5345 if(mcip->flags & MISSION_TYPE_MULTI_COOP){
5346 if(Multi_common_icons[MICON_COOP] >= 0){
5347 gr_set_bitmap(Multi_common_icons[MICON_COOP], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5348 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5350 } else if(mcip->flags & MISSION_TYPE_MULTI_TEAMS){
5351 if(Multi_common_icons[MICON_TVT] >= 0){
5352 gr_set_bitmap(Multi_common_icons[MICON_TVT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5353 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5355 } else if(mcip->flags & MISSION_TYPE_MULTI_DOGFIGHT){
5356 if(Multi_common_icons[MICON_DOGFIGHT] >= 0){
5357 gr_set_bitmap(Multi_common_icons[MICON_DOGFIGHT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5358 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5362 // if its a valid mission, blit the valid mission icon
5363 if(MULTI_IS_TRACKER_GAME && (mcip->valid_status == MVALID_STATUS_VALID)){
5364 if(Multi_common_icons[MICON_VALID] >= 0){
5365 gr_set_bitmap(Multi_common_icons[MICON_VALID], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5366 gr_bitmap(Mc_icon_valid_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_valid_coords[gr_screen.res][MC_Y_COORD]);
5370 // now see if its a builtin mission
5371 fb = game_find_builtin_mission(mcip->filename);
5372 // if the mission is from volition, blit the volition icon
5373 if((fb != NULL) && (fb->flags & FSB_FROM_VOLITION)){
5374 if(Multi_common_icons[MICON_VOLITION] >= 0){
5375 gr_set_bitmap(Multi_common_icons[MICON_VOLITION], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5376 gr_bitmap(Mc_icon_volition_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_volition_coords[gr_screen.res][MC_Y_COORD]);
5381 // check for mdisk mission
5382 fb = game_find_builtin_mission(mcip->filename);
5383 if((fb != NULL) && (fb->flags & FSB_FROM_MDISK)){
5384 if(Multi_common_icons[MICON_MDISK] >= 0){
5385 gr_set_bitmap(Multi_common_icons[MICON_MDISK], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5386 gr_bitmap(Mc_icon_silent_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_silent_coords[gr_screen.res][MC_Y_COORD]);
5392 void multi_create_accept_hit()
5394 char selected_name[255];
5395 int start_campaign = 0;
5397 // make sure all players have finished joining
5398 if(!multi_netplayer_state_check(NETPLAYER_STATE_JOINED,1)){
5399 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("Please wait until all clients have finished joining",788));
5400 gamesnd_play_iface(SND_GENERAL_FAIL);
5403 gamesnd_play_iface(SND_COMMIT_PRESSED);
5406 // do single mission stuff
5407 switch(Multi_create_list_mode){
5408 case MULTI_CREATE_SHOW_MISSIONS:
5409 if(Multi_create_list_select != -1){
5410 // set the netgame mode
5411 Netgame.campaign_mode = MP_SINGLE;
5413 // setup various filenames and mission names
5414 multi_create_select_to_filename(Multi_create_list_select,selected_name);
5415 strncpy( Game_current_mission_filename, selected_name, MAX_FILENAME_LEN );
5416 strncpy(Netgame.mission_name,selected_name,MAX_FILENAME_LEN);
5419 ml_printf(NOX("Starting single mission %s, with %d players"), Game_current_mission_filename, multi_num_players());
5421 multi_common_add_notify(XSTR("No mission selected!",789));
5426 case MULTI_CREATE_SHOW_CAMPAIGNS:
5427 // do campaign related stuff
5428 if(Multi_create_list_select != -1){
5429 // set the netgame mode
5430 Netgame.campaign_mode = MP_CAMPAIGN;
5432 // start a campaign instead of a single mission
5433 multi_create_select_to_filename(Multi_create_list_select,selected_name);
5434 multi_campaign_start(selected_name);
5438 ml_printf(NOX("Starting campaign %s, with %d players"), selected_name, multi_num_players());
5440 multi_common_add_notify(XSTR("No campaign selected!",790));
5446 // if this is a team vs team situation, lock the players send a final team update
5447 if((Netgame.type_flags & NG_TYPE_TEAM) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
5448 multi_team_host_lock_all();
5449 multi_team_send_update();
5452 // if not on the standalone, move to the mission sync state which will take care of everything
5453 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5454 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
5455 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
5457 // otherwise tell the standalone to do so
5459 // when the standalone receives this, he'll do the mission syncing himself
5460 send_mission_sync_packet(MULTI_SYNC_PRE_BRIEFING,start_campaign);
5464 void multi_create_draw_filter_buttons()
5466 // highlight the correct filter button
5467 if ( Multi_create_filter == MISSION_TYPE_MULTI ){
5468 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL].button.draw_forced(2);
5469 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_COOP ) {
5470 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 1].button.draw_forced(2);
5471 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_TEAMS ) {
5472 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 2].button.draw_forced(2);
5473 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_DOGFIGHT ){
5474 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 3].button.draw_forced(2);
5480 void multi_create_set_selected_team(int team)
5484 // if we don't currently have a player selected, don't do anything
5485 if(!Multi_create_plist_select_flag){
5486 gamesnd_play_iface(SND_GENERAL_FAIL);
5489 gamesnd_play_iface(SND_USER_SELECT);
5491 // otherwise attempt to set the team for this guy
5492 player_index = find_player_id(Multi_create_plist_select_id);
5493 if(player_index != -1){
5494 multi_team_set_team(&Net_players[player_index],team);
5498 void multi_create_handle_join(net_player *pl)
5500 // for now just play a bloop sound
5501 gamesnd_play_iface(SND_ICON_DROP_ON_WING);
5504 // fill in net address of player the mouse is over, return player index (or -1 if none)
5505 short multi_create_get_mouse_id()
5507 // determine where he clicked (y pixel value)
5509 Multi_create_player_select_button.get_mouse_pos(NULL,&y);
5511 // select things a little differently if we're in team vs. team or non-team vs. team mode
5513 if(Netgame.type_flags & NG_TYPE_TEAM){
5514 int player_index = -1;
5516 // look through all of team red first
5517 for(idx=0;idx<MAX_PLAYERS;idx++){
5518 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
5521 // if this is the _nth_ guy
5529 // if we still haven't found him yet, look through the green team
5530 if(player_index == -1){
5531 for(idx=0;idx<MAX_PLAYERS;idx++){
5532 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
5534 // if this is the _nth_ guy
5543 if(player_index != -1){
5544 return Net_players[player_index].player_id;
5547 // select the nth active player if possible, disregarding the standalone server
5548 for(idx=0;idx<MAX_PLAYERS;idx++){
5549 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
5552 // if this is the _nth_ guy
5554 return Net_players[idx].player_id;
5563 void multi_create_select_to_filename(int select_index,char *filename)
5567 // look through the mission list
5568 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5569 for(idx=0;idx<Multi_create_mission_count;idx++){
5570 if(Multi_create_file_list[idx].flags & Multi_create_filter){
5574 // if we found the item
5575 if(select_index < 0){
5576 strcpy(filename,Multi_create_file_list[idx].filename);
5581 // look through the campaign list
5582 else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
5583 for(idx=0;idx<Multi_create_campaign_count;idx++){
5586 // if we found the item
5587 if(select_index < 0){
5588 strcpy(filename,Multi_create_file_list[idx].filename);
5594 strcpy(filename,"");
5597 int multi_create_select_to_index(int select_index)
5600 int lookup_index = 0;
5602 // look through the mission list
5603 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5604 for(idx=0;idx<Multi_create_mission_count;idx++){
5605 if(Multi_create_file_list[idx].flags & Multi_create_filter){
5609 // if we found the item
5610 if(select_index < lookup_index){
5615 // look through the campaign list
5616 else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
5617 for(idx=0;idx<Multi_create_campaign_count;idx++){
5620 // if we found the item
5621 if(select_index < 0){
5630 int multi_create_ok_to_commit()
5632 int player_count, observer_count, idx;
5633 int notify_of_hacked_ships_tbl = 0;
5634 int notify_of_hacked_weapons_tbl = 0;
5635 char err_string[255];
5639 // make sure we have a valid mission selected
5640 if(Multi_create_list_select < 0){
5644 // if this is not a valid mission, let the player know
5645 abs_index = multi_create_select_to_index(Multi_create_list_select);
5650 // if we're playing with a hacked ships.tbl (on PXO)
5651 notify_of_hacked_ships_tbl = 0;
5652 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5653 if(!Game_ships_tbl_valid){
5654 notify_of_hacked_ships_tbl = 1;
5657 if(Netgame.flags & NG_FLAG_HACKED_SHIPS_TBL){
5658 notify_of_hacked_ships_tbl = 1;
5661 if(!MULTI_IS_TRACKER_GAME){
5662 notify_of_hacked_ships_tbl = 0;
5664 if(notify_of_hacked_ships_tbl){
5665 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){
5670 // if we're playing with a hacked weapons.tbl (on PXO)
5671 notify_of_hacked_weapons_tbl = 0;
5672 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5673 if(!Game_weapons_tbl_valid){
5674 notify_of_hacked_weapons_tbl = 1;
5677 if(Netgame.flags & NG_FLAG_HACKED_WEAPONS_TBL){
5678 notify_of_hacked_weapons_tbl = 1;
5681 if(!MULTI_IS_TRACKER_GAME){
5682 notify_of_hacked_weapons_tbl = 0;
5684 if(notify_of_hacked_weapons_tbl){
5685 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){
5690 // if any of the players have hacked data
5692 for(idx=0; idx<MAX_PLAYERS; idx++){
5693 // look for hacked players
5694 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAXOR)){
5698 // message everyone - haha
5699 if(Net_players[idx].player != NULL){
5700 sprintf(err_string, "%s %s", Net_players[idx].player->callsign, XSTR("has hacked tables/data", 1271));
5702 sprintf(err_string, "somebody %s", XSTR("has hacked tables/data", 1271));
5704 send_game_chat_packet(Net_player, err_string, MULTI_MSG_ALL, NULL, NULL, 1);
5707 // if we found a hacked set of data
5710 if(MULTI_IS_TRACKER_GAME){
5711 // don't allow squad war matches to continue
5712 if(Netgame.type_flags & NG_TYPE_SW){
5714 // if this is squad war, don't allow it to continue
5715 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));
5720 // otherwise, warn the players that stats will not saved
5722 // if this is squad war, don't allow it to continue
5723 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){
5728 // non-pxo, just give a notice
5730 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){
5736 // check to see that we don't have too many observers
5737 observer_count = multi_num_observers();
5738 if(observer_count > Netgame.options.max_observers){
5739 // print up the error string
5740 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);
5742 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, err_string);
5746 // check to see that we have a valid # of players for the the # of ships in the game
5747 player_count = multi_num_players();
5748 if(player_count > Netgame.max_players){
5749 // print up the error string
5750 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);
5752 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, err_string);
5756 // check to see if teams are assigned properly in a team vs. team situation
5757 if(Netgame.type_flags & NG_TYPE_TEAM){
5758 if(!multi_team_ok_to_commit()){
5759 gamesnd_play_iface(SND_GENERAL_FAIL);
5760 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("Teams and/or team captains are not assigned properly", 793));
5766 if(!multi_create_verify_cds()){
5767 gamesnd_play_iface(SND_GENERAL_FAIL);
5769 #ifdef MULTIPLAYER_BETA_BUILD
5770 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, "You need 1 CD for every player!");
5772 #ifdef DVD_MESSAGE_HACK
5773 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You need 1 DVD for every 4 players!", 794));
5775 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You need 1 CD for every 4 players!", 794));
5781 // if we're playing on the tracker
5782 if(MULTI_IS_TRACKER_GAME){
5783 #ifdef PXO_CHECK_VALID_MISSIONS
5784 if((Multi_create_file_list == Multi_create_mission_list) && (Multi_create_file_list[abs_index].valid_status != MVALID_STATUS_VALID)){
5785 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){
5792 if(!(Netgame.type_flags & NG_TYPE_SW)){
5793 // if he is playing by himself, tell him stats will not be accepted
5794 if(multi_num_players() == 1){
5795 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){
5808 int multi_create_verify_cds()
5810 int player_count = multi_num_players();
5814 // count how many cds we have
5816 for(idx=0;idx<MAX_PLAYERS;idx++){
5817 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAS_CD)){
5822 // for the beta, everyone must have a CD
5823 #ifdef MULTIPLAYER_BETA_BUILD
5824 if(multi_cd_count < player_count){
5828 // determine if we have enough
5829 float ratio = (float)player_count / (float)multi_cd_count;
5830 // greater than a 4 to 1 ratio
5836 // we meet the conditions
5840 // returns an index into Multi_create_mission_list
5841 int multi_create_lookup_mission(char *fname)
5845 for(idx=0; idx<Multi_create_mission_count; idx++){
5846 if(!SDL_strcasecmp(fname, Multi_create_mission_list[idx].filename)){
5851 // couldn't find the mission
5855 // returns an index into Multi_create_campaign_list
5856 int multi_create_lookup_campaign(char *fname)
5860 for(idx=0; idx<Multi_create_campaign_count; idx++){
5861 if(!SDL_strcasecmp(fname, Multi_create_campaign_list[idx].filename)){
5866 // couldn't find the campaign
5870 void multi_create_refresh_pxo()
5872 // delete mvalid.cfg if it exists
5873 cf_delete(MULTI_VALID_MISSION_FILE, CF_TYPE_DATA);
5875 // refresh missions from the tracker
5876 multi_update_valid_missions();
5879 void multi_create_sw_clicked()
5881 netgame_info ng_temp;
5884 int file_index = multi_create_select_to_index(Multi_create_list_select);
5886 // either a temporary netgame or the real one
5887 if(MULTIPLAYER_MASTER){
5894 // maybe switch squad war off
5895 if(!Multi_create_sw_checkbox.checked()){
5896 // if the mission selected is a coop mission, go back to coop mode
5897 SDL_assert(file_index != -1);
5898 if(file_index == -1){
5899 ng->type_flags = NG_TYPE_COOP;
5901 if(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_TEAMS){
5902 ng->type_flags = NG_TYPE_TVT;
5903 } else if(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_DOGFIGHT){
5904 ng->type_flags = NG_TYPE_DOGFIGHT;
5906 ng->type_flags = NG_TYPE_COOP;
5909 // switch squad war on
5911 SDL_assert(file_index != -1);
5912 if((file_index == -1) || !(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_TEAMS)){
5913 Multi_create_sw_checkbox.set_state(0);
5915 // at this point we know its safe to switch squad war on
5916 ng->type_flags = NG_TYPE_SW;
5921 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5923 send_netgame_update_packet();
5925 // update all machines about stuff like respawns, etc.
5926 multi_options_update_netgame();
5928 // on the standalone
5930 // standalone will take care of polling the usertracker
5931 multi_options_update_mission(ng, Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ? 1 : 0);
5936 // -------------------------------------------------------------------------------------------------------------
5938 // MULTIPLAYER HOST OPTIONS SCREEN
5941 #define MULTI_HO_NUM_BUTTONS 12
5942 #define MULTI_HO_NUM_RADIO_BUTTONS 10
5946 #define MULTI_HO_PALETTE "InterfacePalette"
5948 static const char *Multi_ho_bitmap_fname[GR_NUM_RESOLUTIONS] = {
5949 "MultiHost", // GR_640
5950 "2_MultiHost" // GR_1024
5953 static const char *Multi_ho_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
5954 "MultiHost-M", // GR_640
5955 "2_MultiHost-M" // GR_1024
5959 UI_WINDOW Multi_ho_window; // the window object for the join screen
5960 UI_INPUTBOX Multi_ho_respawns; // the # of respawns allowed in the game
5961 UI_INPUTBOX Multi_ho_time_limit; // mission time limit
5962 UI_INPUTBOX Multi_ho_voice_wait; // wait time between tokens
5963 UI_INPUTBOX Multi_ho_kill_limit; // kill limit in a furball mission
5964 UI_INPUTBOX Multi_ho_obs; // # of observers we'll allow
5965 int Multi_ho_bitmap; // the background bitmap
5967 // constants for coordinate lookup
5968 #define MULTI_HO_X_COORD 0
5969 #define MULTI_HO_Y_COORD 1
5970 #define MULTI_HO_W_COORD 2
5971 #define MULTI_HO_H_COORD 3
5972 #define MULTI_HO_TEXT_X_COORD 4
5973 #define MULTI_HO_TEXT_Y_COORD 5
5976 #define MULTI_HO_MSG_RANK 0 // highest ranking players can do messaging
5977 #define MULTI_HO_MSG_LEADER 1 // wing/team leaders can do messaging
5978 #define MULTI_HO_MSG_ANY 2 // any player can do messaging
5979 #define MULTI_HO_MSG_HOST 3 // only the host can do messaging
5980 #define MULTI_HO_END_RANK 4 // highest rank can and host can end mission
5981 #define MULTI_HO_END_LEADER 5 // wing/team leaders and host can end the mission
5982 #define MULTI_HO_END_ANY 6 // any player can end the mission
5983 #define MULTI_HO_END_HOST 7 // only host can end the mission
5984 #define MULTI_HO_VOICE_ON 8 // voice toggled on
5985 #define MULTI_HO_VOICE_OFF 9 // voice toggled off
5986 #define MULTI_HO_HOST_MODIFIES 10 // only the host or team captains can modify ships/weapons in briefing
5987 #define MULTI_HO_ACCEPT 11 // accept button
5989 ui_button_info Multi_ho_buttons[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_BUTTONS] = {
5992 // who is allowed to message
5993 ui_button_info("MH_00", 13, 157, -1, -1, 0), // highest rank
5994 ui_button_info("MH_01", 13, 179, -1, -1, 1), // team/wing leader
5995 ui_button_info("MH_02", 13, 200, -1, -1, 2), // any
5996 ui_button_info("MH_03", 13, 223, -1, -1, 3), // host
5998 // who is allowed to end the mission
5999 ui_button_info("MH_09", 13, 273, -1, -1, 9), // highest rank
6000 ui_button_info("MH_10", 13, 295, -1, -1, 10), // team/wing leader
6001 ui_button_info("MH_11", 13, 317, -1, -1, 11), // any
6002 ui_button_info("MH_12", 13, 339, -1, -1, 12), // host
6004 // voice on/off button
6005 ui_button_info("MH_14", 396, 156, -1, -1, 14),
6006 ui_button_info("MH_15", 453, 156, -1, -1, 15),
6008 // host modifies ships
6009 ui_button_info("MH_19", 215, 410, -1, -1, 19),
6012 ui_button_info("MH_18", 560, 411, -1, -1, 18),
6014 // who is allowed to message
6015 ui_button_info("MH_00", 3, 160, 46, 166, 0), // highest rank
6016 ui_button_info("MH_01", 3, 179, 46, 185, 1), // team/wing leader
6017 ui_button_info("MH_02", 3, 196, 46, 203, 2), // any
6018 ui_button_info("MH_03", 3, 214, 46, 220, 3), // host
6020 // who is allowed to end the mission
6021 ui_button_info("MH_04", 3, 257, 46, 265, 4), // highest rank
6022 ui_button_info("MH_05", 3, 276, 46, 283, 5), // team/wing leader
6023 ui_button_info("MH_06", 3, 294, 46, 300, 6), // any
6024 ui_button_info("MH_07", 3, 311, 46, 317, 7), // host
6026 // voice on/off button
6027 ui_button_info("MH_09", 542, 158, 545, 185, 9),
6028 ui_button_info("MH_10", 598, 158, 604, 185, 10),
6030 // host modifies ships
6031 ui_button_info("MH_13", 542, 377, 437, 363, 13),
6034 ui_button_info("MH_14", 572, 428, 580, 414, 14),
6038 // who is allowed to message
6039 ui_button_info("2_MH_00", 5, 256, 73, 269, 0), // highest rank
6040 ui_button_info("2_MH_01", 5, 286, 73, 297, 1), // team/wing leader
6041 ui_button_info("2_MH_02", 5, 314, 73, 325, 2), // any
6042 ui_button_info("2_MH_03", 5, 341, 73, 352, 3), // host
6044 // who is allowed to end the mission
6045 ui_button_info("2_MH_04", 5, 412, 73, 425, 4), // highest rank
6046 ui_button_info("2_MH_05", 5, 442, 73, 452, 5), // team/wing leader
6047 ui_button_info("2_MH_06", 5, 470, 73, 480, 6), // any
6048 ui_button_info("2_MH_07", 5, 497, 73, 508, 7), // host
6050 // voice on/off button
6051 ui_button_info("2_MH_09", 867, 253, 872, 296, 9),
6052 ui_button_info("2_MH_10", 957, 253, 966, 296, 10),
6054 // host modifies ships
6055 ui_button_info("2_MH_13", 867, 603, 784, 581, 13),
6058 ui_button_info("2_MH_14", 916, 685, 925, 665, 14),
6061 UI_XSTR Multi_ho_text[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_BUTTONS] = {
6063 // not needed for FS1
6065 {"Highest rank", 1280, 46, 166, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_RANK].button},
6066 {"Team / wing-leader", 1281, 46, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_LEADER].button},
6067 {"Any", 1282, 46, 203, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_ANY].button},
6068 {"Host", 1283, 46, 220, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_HOST].button},
6069 {"Highest rank", 1280, 46, 265, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_RANK].button},
6070 {"Team / wing-leader", 1281, 46, 283, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_LEADER].button},
6071 {"Any", 1282, 46, 300, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_ANY].button},
6072 {"Host", 1283, 46, 317, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_HOST].button},
6073 {"On", 1285, 545, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_VOICE_ON].button},
6074 {"Off", 1286, 604, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_VOICE_OFF].button},
6075 {"Host modifies ships", 1287, 437, 363, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_HOST_MODIFIES].button},
6076 {"Exit", 1417, 572, 418, UI_XSTR_COLOR_PINK, -1, &Multi_ho_buttons[0][MULTI_HO_ACCEPT].button},
6080 // not needed for FS1
6082 {"Highest rank", 1280, 62, 269, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_RANK].button},
6083 {"Team / wing-leader", 1281, 62, 297, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_LEADER].button},
6084 {"Any", 1282, 62, 325, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_ANY].button},
6085 {"Host", 1283, 62, 352, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_HOST].button},
6086 {"Highest rank", 1280, 62, 425, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_RANK].button},
6087 {"Team / wing-leader", 1281, 62, 452, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_LEADER].button},
6088 {"Any", 1282, 62, 480, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_ANY].button},
6089 {"Host", 1283, 62, 508, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_HOST].button},
6090 {"On", 1285, 877, 294, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_VOICE_ON].button},
6091 {"Off", 1286, 967, 293, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_VOICE_OFF].button},
6092 {"Host modifies ships", 1287, 869, 589, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_HOST_MODIFIES].button},
6093 {"Exit", 1417, 953, 672, UI_XSTR_COLOR_PINK, -1, &Multi_ho_buttons[1][MULTI_HO_ACCEPT].button},
6098 // radio button controls
6099 #define MULTI_HO_NUM_RADIO_GROUPS 3
6100 #define MULTI_HO_MSG_GROUP 0 // group dealing with squadmate messaging
6101 #define MULTI_HO_END_GROUP 1 // group dealing with ending the mission
6102 #define MULTI_HO_VOICE_GROUP 2 // group dealing with voice stuff
6103 int Multi_ho_radio_groups[MULTI_HO_NUM_RADIO_GROUPS] = { // currently selected button in the radio button group
6106 int Multi_ho_radio_info[MULTI_HO_NUM_RADIO_BUTTONS][3] = { // info related to each of the radio buttons themselves
6107 // { group #, value, button id# }
6108 {0, 0, 0}, // highest ranking players can do messaging
6109 {0, 1, 1}, // wing/team leaders can do messaging
6110 {0, 2, 2}, // any player can do messaging
6111 {0, 3, 3}, // only host can do messaging
6112 {1, 0, 4}, // highest rank and host can end the mission
6113 {1, 1, 5}, // team/wing leader can end the mission
6114 {1, 2, 6}, // any player can end the mission
6115 {1, 3, 7}, // only the host can end the mission
6116 {2, 0, 8}, // voice toggled on
6117 {2, 1, 9} // voice toggled off
6121 #define MULTI_HO_NUM_SLIDERS 3
6122 #define MULTI_HO_SLIDER_VOICE_QOS 0 // voice quality of sound
6123 #define MULTI_HO_SLIDER_VOICE_DUR 1 // max duration of voice recording
6124 #define MULTI_HO_SLIDER_SKILL 2 // skill level
6126 const char *filename;
6131 UI_DOT_SLIDER_NEW slider; // because we have a class inside this struct, we need the constructor below..
6133 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){}
6135 ho_sliders Multi_ho_sliders[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_SLIDERS] = {
6138 ho_sliders("MH_16", 396, 210, -1, -1, 16, 19, 10), // voice qos
6139 ho_sliders("MH_17", 396, 263, -1, -1, 17, 19, 10), // voice duration
6140 ho_sliders("MH_13", 10, 387, -1, -1, 13, 36, 5), // skill level
6142 ho_sliders("MH_11", 428, 214, 437, 199, 11, 19, 10), // voice qos
6143 ho_sliders("MH_12", 428, 261, 437, 246, 12, 19, 10), // voice duration
6144 ho_sliders("MH_08", 237, 454, 230, 411, 8, 36, 5), // skill level
6148 ho_sliders("2_MH_11", 684, 343, 690, 323, 11, 32, 10), // voice qos
6149 ho_sliders("2_MH_12", 685, 418, 837, 468, 12, 32, 10), // voice duration
6150 ho_sliders("2_MH_08", 379, 727, 369, 663, 8, 60, 5), // skill level
6154 int Multi_ho_mission_respawn;
6156 int Multi_ho_host_modifies;
6158 // whether or not any of the inputboxes on this screen had focus last frame
6159 int Multi_ho_lastframe_input = 0;
6161 // game information text areas
6165 #define MULTI_HO_NUM_TITLES 14
6167 UI_XSTR Multi_ho_titles[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_TITLES] = {
6169 { "AI Orders", 1289, 32, 144, UI_XSTR_COLOR_GREEN, -1, NULL },
6170 { "End Mission", 1290, 32, 242, UI_XSTR_COLOR_GREEN, -1, NULL },
6171 { "Time Limit", 1291, 32, 347, UI_XSTR_COLOR_GREEN, -1, NULL },
6172 { "Min", 1292, 74, 362, UI_XSTR_COLOR_GREEN, -1, NULL },
6173 { "Respawn Limit", 1288, 32, 378, UI_XSTR_COLOR_GREEN, -1, NULL },
6174 { "Kill Limit", 1293, 32, 409, UI_XSTR_COLOR_GREEN, -1, NULL },
6175 { "Observers", 1294, 32, 441, UI_XSTR_COLOR_GREEN, -1, NULL },
6176 { "Skill Level", 1284, 230, 411, UI_XSTR_COLOR_GREEN, -1, NULL },
6177 { "Voice Transmission", 1295, 437, 144, UI_XSTR_COLOR_GREEN, -1, NULL },
6178 { "Voice Quality", 1296, 437, 199, UI_XSTR_COLOR_GREEN, -1, NULL },
6179 { "Message Duration", 1297, 437, 246, UI_XSTR_COLOR_GREEN, -1, NULL },
6180 { "sec", 1522, 523, 292, UI_XSTR_COLOR_GREEN, -1, NULL },
6181 { "sec", 1523, 523, 332, UI_XSTR_COLOR_GREEN, -1, NULL },
6182 { "Voice Wait", 1298, 437, 313, UI_XSTR_COLOR_GREEN, -1, NULL },
6185 { "AI Orders", 1289, 48, 238, UI_XSTR_COLOR_GREEN, -1, NULL },
6186 { "End Mission", 1290, 48, 394, UI_XSTR_COLOR_GREEN, -1, NULL },
6187 { "Time Limit", 1291, 50, 568, UI_XSTR_COLOR_GREEN, -1, NULL },
6188 { "Min", 1292, 119, 581, UI_XSTR_COLOR_GREEN, -1, NULL },
6189 { "Respawn Limit", 1288, 50, 618, UI_XSTR_COLOR_GREEN, -1, NULL },
6190 { "Kill Limit", 1293, 50, 668, UI_XSTR_COLOR_GREEN, -1, NULL },
6191 { "Observers", 1294, 50, 718, UI_XSTR_COLOR_GREEN, -1, NULL },
6192 { "Skill Level", 1284, 398, 670, UI_XSTR_COLOR_GREEN, -1, NULL },
6193 { "Voice Transmission", 1295, 869, 239, UI_XSTR_COLOR_GREEN, -1, NULL },
6194 { "Voice Quality", 1296, 690, 331, UI_XSTR_COLOR_GREEN, -1, NULL },
6195 { "Message Duration", 1297, 690, 405, UI_XSTR_COLOR_GREEN, -1, NULL },
6196 { "sec", 1522, 837, 467, UI_XSTR_COLOR_GREEN, -1, NULL },
6197 { "sec", 1523, 837, 534, UI_XSTR_COLOR_GREEN, -1, NULL },
6198 { "Voice Wait", 1298, 742, 510, UI_XSTR_COLOR_GREEN, -1, NULL },
6203 // mission time limit input box
6204 int Ho_time_coords[GR_NUM_RESOLUTIONS][4] = {
6217 // furball kill limit input box
6218 int Ho_kill_coords[GR_NUM_RESOLUTIONS][4] = {
6231 // voice recording duration text display area
6232 int Ho_vd_coords[GR_NUM_RESOLUTIONS][4] = {
6245 // voice token wait input box
6246 int Ho_vw_coords[GR_NUM_RESOLUTIONS][6] = {
6259 // observer count input box
6260 int Ho_obs_coords[GR_NUM_RESOLUTIONS][4] = {
6273 // skill text description area
6274 int Ho_st_coords[GR_NUM_RESOLUTIONS][4] = {
6287 // respawn input box
6288 int Ho_rsp_coords[GR_NUM_RESOLUTIONS][6] = {
6301 // respawn max text area
6302 int Ho_max_rsp_coords[GR_NUM_RESOLUTIONS][2] = {
6315 // maximum values for various input boxes (to notify user of overruns)
6316 #define MULTI_HO_MAX_TIME_LIMIT 500
6317 #define MULTI_HO_MAX_TOKEN_WAIT 5
6318 #define MULTI_HO_MAX_KILL_LIMIT 9999
6319 #define MULTI_HO_MAX_OBS 4
6321 // LOCAL function definitions
6322 void multi_ho_check_buttons();
6323 void multi_ho_button_pressed(int n);
6324 void multi_ho_draw_radio_groups();
6325 void multi_ho_accept_hit();
6326 void multi_ho_get_options();
6327 void multi_ho_apply_options();
6328 void multi_ho_display_record_time();
6329 int multi_ho_check_values();
6330 void multi_ho_check_focus();
6331 void multi_ho_blit_max_respawns();
6332 void multi_ho_display_skill_level();
6334 void multi_host_options_init()
6338 // create the interface window
6339 Multi_ho_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
6340 Multi_ho_window.set_mask_bmap(Multi_ho_bitmap_mask_fname[gr_screen.res]);
6342 // load the background bitmap
6343 Multi_ho_bitmap = bm_load(Multi_ho_bitmap_fname[gr_screen.res]);
6344 if(Multi_ho_bitmap < 0){
6345 // we failed to load the bitmap - this is very bad
6349 // initialize the common notification messaging
6350 multi_common_notify_init();
6352 // use the common interface palette
6353 multi_common_set_palette();
6355 // create the interface buttons
6356 for(idx=0;idx<MULTI_HO_NUM_BUTTONS;idx++){
6357 // create the object
6358 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);
6360 // set the sound to play when highlighted
6361 Multi_ho_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
6363 // set the ani for the button
6364 Multi_ho_buttons[gr_screen.res][idx].button.set_bmaps(Multi_ho_buttons[gr_screen.res][idx].filename);
6366 // set the hotspot, ignoring the skill level button
6367 Multi_ho_buttons[gr_screen.res][idx].button.link_hotspot(Multi_ho_buttons[gr_screen.res][idx].hotspot);
6371 Multi_ho_window.add_XSTR(&Multi_ho_text[gr_screen.res][idx]);
6377 for(idx=0; idx<MULTI_HO_NUM_TITLES; idx++){
6378 Multi_ho_window.add_XSTR(&Multi_ho_titles[gr_screen.res][idx]);
6382 // create the interface sliders
6383 for(idx=0; idx<MULTI_HO_NUM_SLIDERS; idx++){
6384 // create the object
6385 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);
6388 // create the respawn count input box
6389 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);
6390 // if we're in campaign mode, disable it
6391 if(Netgame.campaign_mode == MP_CAMPAIGN){
6392 Multi_ho_respawns.set_text(XSTR("NA",795)); // [[ Not applicable ]]
6393 Multi_ho_respawns.disable();
6395 Multi_ho_respawns.set_valid_chars("0123456789");
6398 // create the time limit input box
6399 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);
6400 Multi_ho_time_limit.set_valid_chars("-0123456789");
6402 // create the voice token wait input box
6403 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);
6404 Multi_ho_voice_wait.set_valid_chars("01243456789");
6406 // create the furball kill limit input box
6407 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);
6408 Multi_ho_kill_limit.set_valid_chars("0123456789");
6410 // create the observer limit input box
6411 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);
6412 Multi_ho_obs.set_valid_chars("01234");
6414 // load in the current netgame defaults
6415 multi_ho_get_options();
6417 // whether or not any of the inputboxes on this screen had focus last frame
6418 Multi_ho_lastframe_input = 0;
6420 // get the # of respawns for the currently selected mission (if any)
6421 if(Multi_create_list_select != -1){
6422 int abs_index = multi_create_select_to_index(Multi_create_list_select);
6424 // if he has a valid mission selected
6426 Multi_ho_mission_respawn = (int)Multi_create_file_list[abs_index].respawn;
6428 Multi_ho_mission_respawn = -1;
6431 Multi_ho_mission_respawn = -1;
6435 void multi_ho_update_sliders()
6437 // game skill slider
6438 if (Game_skill_level != Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos) {
6439 if ( !(Netgame.type_flags & NG_TYPE_TEAM) ){
6440 Game_skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6441 gamesnd_play_iface(SND_USER_SELECT);
6443 Game_skill_level = NUM_SKILL_LEVELS / 2;
6447 // get the voice qos options
6448 if (Netgame.options.voice_qos != (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1)) {
6449 Netgame.options.voice_qos = (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1);
6450 gamesnd_play_iface(SND_USER_SELECT);
6453 // get the voice duration options
6454 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)) {
6455 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);
6456 gamesnd_play_iface(SND_USER_SELECT);
6461 void multi_host_options_do()
6466 k = Multi_ho_window.process();
6469 // process any keypresses
6472 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
6475 case KEY_CTRLED + SDLK_RETURN :
6476 gamesnd_play_iface(SND_COMMIT_PRESSED);
6477 multi_ho_accept_hit();
6481 // process any button clicks
6482 multi_ho_check_buttons();
6484 // update the sliders
6485 multi_ho_update_sliders();
6487 // make sure that the chatbox inputbox and any inputbox on this screen are mutually exclusive in terms of focus
6488 multi_ho_check_focus();
6490 // draw the background, etc
6492 GR_MAYBE_CLEAR_RES(Multi_ho_bitmap);
6493 if(Multi_ho_bitmap != -1){
6494 gr_set_bitmap(Multi_ho_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
6497 Multi_ho_window.draw();
6499 // draw all the radio buttons properly
6500 multi_ho_draw_radio_groups();
6502 // display any pending notification messages
6503 multi_common_notify_do();
6505 // display the voice record time settings
6506 multi_ho_display_record_time();
6508 // maybe display the max # of respawns next to the respawn input box
6509 multi_ho_blit_max_respawns();
6511 // blit the proper skill level
6512 multi_ho_display_skill_level();
6514 // blit the "host modifies button"
6515 if(Multi_ho_host_modifies){
6516 Multi_ho_buttons[gr_screen.res][MULTI_HO_HOST_MODIFIES].button.draw_forced(2);
6519 // process and show the chatbox thingie
6523 Multi_ho_window.draw_tooltip();
6525 // display the voice status indicator
6526 multi_common_voice_display_status();
6532 void multi_host_options_close()
6534 // unload any bitmaps
6535 if(!bm_unload(Multi_ho_bitmap)){
6536 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_ho_bitmap_fname[gr_screen.res]));
6539 // destroy the UI_WINDOW
6540 Multi_ho_window.destroy();
6543 void multi_ho_check_buttons()
6546 for(idx=0;idx<MULTI_HO_NUM_BUTTONS;idx++){
6547 // we only really need to check for one button pressed at a time, so we can break after
6549 if(Multi_ho_buttons[gr_screen.res][idx].button.pressed()){
6550 multi_ho_button_pressed(idx);
6556 void multi_ho_button_pressed(int n)
6558 int radio_index,idx;
6559 int x_pixel,y_pixel;
6561 // get the pixel position of the click
6562 Multi_ho_buttons[gr_screen.res][n].button.get_mouse_pos(&x_pixel,&y_pixel);
6565 // clicked on the accept button
6566 case MULTI_HO_ACCEPT:
6567 gamesnd_play_iface(SND_COMMIT_PRESSED);
6568 multi_ho_accept_hit();
6571 // clicked on the host/captains only modify button
6572 case MULTI_HO_HOST_MODIFIES:
6573 // toggle it on or off
6574 Multi_ho_host_modifies = !Multi_ho_host_modifies;
6575 gamesnd_play_iface(SND_USER_SELECT);
6579 // look through the radio buttons and see which one this corresponds to
6581 for(idx=0;idx<MULTI_HO_NUM_RADIO_BUTTONS;idx++){
6582 if(Multi_ho_radio_info[idx][2] == n){
6587 SDL_assert(radio_index != -1);
6589 // check to see if a radio button was pressed
6590 if(radio_index < MULTI_HO_NUM_RADIO_BUTTONS){
6591 // see if this value is already picked for this radio group
6592 if(Multi_ho_radio_groups[Multi_ho_radio_info[radio_index][0]] != Multi_ho_radio_info[radio_index][1]){
6593 gamesnd_play_iface(SND_USER_SELECT);
6594 Multi_ho_radio_groups[Multi_ho_radio_info[radio_index][0]] = Multi_ho_radio_info[radio_index][1];
6596 gamesnd_play_iface(SND_GENERAL_FAIL);
6601 void multi_ho_draw_radio_groups()
6605 // go through each item and draw it if it is the selected button in its respective group
6606 for(idx=0;idx<MULTI_HO_NUM_RADIO_BUTTONS;idx++){
6607 /// if this button is the currently selected one in its group
6608 if(Multi_ho_radio_info[idx][1] == Multi_ho_radio_groups[Multi_ho_radio_info[idx][0]]){
6609 Multi_ho_buttons[gr_screen.res][Multi_ho_radio_info[idx][2]].button.draw_forced(2);
6614 void multi_ho_accept_hit()
6618 // check the values in the input boxes
6619 if(!multi_ho_check_values()){
6623 // zero out the netgame flags
6626 // set default options
6627 Netgame.options.flags = (MSO_FLAG_INGAME_XFER | MSO_FLAG_ACCEPT_PIX);
6629 // set the squadmate messaging flags
6630 switch(Multi_ho_radio_groups[MULTI_HO_MSG_GROUP]){
6632 Netgame.options.squad_set = MSO_SQUAD_RANK;
6635 Netgame.options.squad_set = MSO_SQUAD_LEADER;
6638 Netgame.options.squad_set = MSO_SQUAD_ANY;
6641 Netgame.options.squad_set = MSO_SQUAD_HOST;
6647 // set the end mission flags
6648 switch(Multi_ho_radio_groups[MULTI_HO_END_GROUP]){
6650 Netgame.options.endgame_set = MSO_END_RANK;
6653 Netgame.options.endgame_set = MSO_END_LEADER;
6656 Netgame.options.endgame_set = MSO_END_ANY;
6659 Netgame.options.endgame_set = MSO_END_HOST;
6665 // set the voice toggle
6666 switch(Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP]){
6668 Netgame.options.flags &= ~(MSO_FLAG_NO_VOICE);
6671 Netgame.options.flags |= MSO_FLAG_NO_VOICE;
6677 // get the voice qos options
6678 Netgame.options.voice_qos = (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1);
6680 // get the voice duration options
6681 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);
6683 // set the skill level. If in team vs. team mode, preserve the old setting before saving
6684 // the pilot file. I'll bet that this doesn't work though because the pilot file gets
6685 // written in a bunch of locations....sigh.
6686 if ( !(Netgame.type_flags & NG_TYPE_TEAM) ){
6687 Game_skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6689 Game_skill_level = NUM_SKILL_LEVELS / 2;
6692 // set the netgame respawn count
6693 // maybe warn the user that respawns will not be used for a campaign mission
6694 if(Netgame.campaign_mode == MP_SINGLE){
6695 Multi_ho_respawns.get_text(resp_str);
6696 uint temp_respawn = (uint)atoi(resp_str);
6697 // if he currently has no mission selected, let the user set any # of respawns
6698 if((int)temp_respawn > Multi_ho_mission_respawn){
6699 if(Multi_ho_mission_respawn == -1){
6700 Netgame.respawn = temp_respawn;
6701 Netgame.options.respawn = temp_respawn;
6703 // this should have been taken care of by the interface code
6708 Netgame.options.respawn = temp_respawn;
6709 Netgame.respawn = temp_respawn;
6713 // get the mission time limit
6714 Multi_ho_time_limit.get_text(resp_str);
6715 int temp_time = atoi(resp_str);
6717 Netgame.options.mission_time_limit = fl2f(-1.0f);
6718 } else if(temp_time > MULTI_HO_MAX_TIME_LIMIT){
6721 Netgame.options.mission_time_limit = fl2f(60.0f * (float)temp_time);
6724 // get observer count options
6725 Multi_ho_obs.get_text(resp_str);
6726 int temp_obs = atoi(resp_str);
6727 if(temp_obs > MULTI_HO_MAX_OBS){
6730 Netgame.options.max_observers = (ubyte)temp_obs;
6732 // get the furball kill limit
6733 Multi_ho_kill_limit.get_text(resp_str);
6734 int temp_kills = atoi(resp_str);
6735 if(temp_kills > MULTI_HO_MAX_KILL_LIMIT){
6738 Netgame.options.kill_limit = temp_kills;
6740 // get the token wait limit
6741 Multi_ho_voice_wait.get_text(resp_str);
6742 int temp_wait = atoi(resp_str);
6743 if(temp_wait > MULTI_HO_MAX_TOKEN_WAIT){
6746 Netgame.options.voice_token_wait = (temp_wait * 1000);
6748 // set the netgame option
6749 Netgame.options.skill_level = (ubyte)Game_skill_level;
6751 // get whether we're in host/captains only modify mode
6752 Netgame.options.flags &= ~(MSO_FLAG_SS_LEADERS);
6753 if(Multi_ho_host_modifies){
6754 Netgame.options.flags |= MSO_FLAG_SS_LEADERS;
6757 // store these values locally
6758 memcpy(&Player->m_local_options,&Net_player->p_info.options,sizeof(multi_local_options));
6759 memcpy(&Player->m_server_options,&Netgame.options,sizeof(multi_server_options));
6760 write_pilot_file(Player);
6762 // apply any changes in settings (notify everyone of voice qos changes, etc)
6763 multi_ho_apply_options();
6765 // move back to the create game screen
6766 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
6769 void multi_ho_get_options()
6773 // set the squadmate messaging buttons
6774 switch(Netgame.options.squad_set){
6775 case MSO_SQUAD_RANK :
6776 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 0;
6778 case MSO_SQUAD_LEADER:
6779 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 1;
6782 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 2;
6784 case MSO_SQUAD_HOST:
6785 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 3;
6791 // set the mission end buttons
6792 switch(Netgame.options.endgame_set){
6794 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 0;
6796 case MSO_END_LEADER:
6797 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 1;
6800 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 2;
6803 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 3;
6809 // set the voice toggle buttons
6810 if(Netgame.options.flags & MSO_FLAG_NO_VOICE){
6811 Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP] = 1;
6813 Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP] = 0;
6816 // get the voice qos options
6817 SDL_assert((Netgame.options.voice_qos >= 1) && (Netgame.options.voice_qos <= 10));
6818 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos = (Netgame.options.voice_qos - 1);
6820 // get the voice duration options
6821 SDL_assert((Netgame.options.voice_record_time > 0) && (Netgame.options.voice_record_time <= MULTI_VOICE_MAX_TIME));
6822 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos = ((int)((float)Netgame.options.voice_record_time / 500.0f)) - 1;
6824 // get the current skill level
6825 SDL_assert((Game_skill_level >= 0) && (Game_skill_level < NUM_SKILL_LEVELS));
6826 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos = Game_skill_level;
6828 // get the # of observers
6829 memset(resp_str,0,10);
6830 sprintf(resp_str,"%d",Netgame.options.max_observers);
6831 Multi_ho_obs.set_text(resp_str);
6833 // set the respawn count
6834 if(Netgame.campaign_mode == MP_SINGLE){
6835 memset(resp_str,0,10);
6836 sprintf(resp_str,"%d",Netgame.respawn);
6837 Multi_ho_respawns.set_text(resp_str);
6840 // set the mission time limit
6841 memset(resp_str,0,10);
6842 float tl = f2fl(Netgame.options.mission_time_limit);
6843 sprintf(resp_str,"%d",(int)(tl / 60.0f));
6844 Multi_ho_time_limit.set_text(resp_str);
6846 // set the furball kill limit
6847 memset(resp_str,0,10);
6848 sprintf(resp_str,"%d",Netgame.options.kill_limit);
6849 Multi_ho_kill_limit.set_text(resp_str);
6851 // set the token wait time
6852 memset(resp_str,0,10);
6853 sprintf(resp_str,"%d",Netgame.options.voice_token_wait / 1000);
6854 Multi_ho_voice_wait.set_text(resp_str);
6856 // get whether we're in host/captains only modify mode
6857 if(Netgame.options.flags & MSO_FLAG_SS_LEADERS){
6858 Multi_ho_host_modifies = 1;
6860 Multi_ho_host_modifies = 0;
6864 void multi_ho_apply_options()
6866 // if the voice qos or duration has changed, apply the change
6867 multi_voice_maybe_update_vars(Netgame.options.voice_qos,Netgame.options.voice_record_time);
6869 // send an options update
6870 multi_options_update_netgame();
6873 // display the voice record time settings
6874 void multi_ho_display_record_time()
6877 int full_seconds, half_seconds;
6880 memset(time_str,0,30);
6883 full_seconds = (((Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 500) / 1000);
6885 // get the half-seconds
6886 half_seconds = ((((Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 500) % 1000) / 500) * 5;
6888 // format the string
6889 sprintf(time_str,"%d.%d",full_seconds,half_seconds);
6890 gr_set_color_fast(&Color_bright);
6891 gr_string(Ho_vd_coords[gr_screen.res][MULTI_HO_X_COORD],Ho_vd_coords[gr_screen.res][MULTI_HO_Y_COORD],time_str);
6894 int multi_ho_check_values()
6898 memset(val_txt,0,255);
6900 // check against respawn settings
6901 if(Multi_ho_mission_respawn != -1){
6902 Multi_ho_respawns.get_text(val_txt);
6903 // if the value is invalid, let the user know
6904 if(atoi(val_txt) > Multi_ho_mission_respawn){
6905 memset(val_txt,0,255);
6906 sprintf(val_txt,XSTR("Warning\nRespawn count in greater than mission specified max (%d)",796),Multi_ho_mission_respawn);
6907 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6912 // check against mission time limit max
6913 Multi_ho_time_limit.get_text(val_txt);
6914 // if the value is invalid, force it to be valid
6915 if(atoi(val_txt) > MULTI_HO_MAX_TIME_LIMIT){
6916 memset(val_txt,0,255);
6917 sprintf(val_txt,XSTR("Warning\nMission time limit is greater than max allowed (%d)",797),MULTI_HO_MAX_TIME_LIMIT);
6918 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6922 // check against max observer limit
6923 Multi_ho_obs.get_text(val_txt);
6924 // if the value is invalid, force it to be valid
6925 if(atoi(val_txt) > MULTI_HO_MAX_OBS){
6926 memset(val_txt,0,255);
6927 sprintf(val_txt,XSTR("Warning\nObserver count is greater than max allowed (%d)",798),MULTI_HO_MAX_OBS);
6928 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6932 // check against furball kill limit
6933 Multi_ho_kill_limit.get_text(val_txt);
6934 // if the value is invalid, force it to be valid
6935 if(atoi(val_txt) > MULTI_HO_MAX_KILL_LIMIT){
6936 memset(val_txt,0,255);
6937 sprintf(val_txt,XSTR("Warning\nMission kill limit is greater than max allowed (%d)",799),MULTI_HO_MAX_KILL_LIMIT);
6938 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6942 // check against the token wait limit
6943 Multi_ho_voice_wait.get_text(val_txt);
6944 if(atoi(val_txt) > MULTI_HO_MAX_TOKEN_WAIT){
6945 memset(val_txt,0,255);
6946 sprintf(val_txt,XSTR("Warning\nvoice wait time is greater than max allowed (%d)",800),MULTI_HO_MAX_TOKEN_WAIT);
6947 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6951 // all values are valid
6955 void multi_ho_check_focus()
6957 // if an inputbox has been pressed (hit enter), lose its focus
6958 if (Multi_ho_respawns.pressed() || Multi_ho_time_limit.pressed() || Multi_ho_voice_wait.pressed() || Multi_ho_kill_limit.pressed() || Multi_ho_obs.pressed()) {
6959 Multi_ho_respawns.clear_focus();
6960 Multi_ho_time_limit.clear_focus();
6961 Multi_ho_voice_wait.clear_focus();
6962 Multi_ho_kill_limit.clear_focus();
6963 Multi_ho_obs.clear_focus();
6964 gamesnd_play_iface(SND_COMMIT_PRESSED);
6965 chatbox_set_focus();
6966 Multi_ho_lastframe_input = 0;
6968 } else if(!Multi_ho_lastframe_input) {
6969 // if we didn't have focus last frame
6970 if(Multi_ho_respawns.has_focus() || Multi_ho_time_limit.has_focus() || Multi_ho_kill_limit.has_focus() || Multi_ho_voice_wait.has_focus() ){
6971 chatbox_lose_focus();
6973 Multi_ho_lastframe_input = 1;
6976 // if we _did_ have focus last frame
6978 // if we no longer have focus on any of the input boxes, set the focus on the chatbox
6979 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()){
6980 chatbox_set_focus();
6982 // if the chatbox now has focus, clear all focus from our inputboxes
6983 else if (chatbox_has_focus()) {
6984 Multi_ho_respawns.clear_focus();
6985 Multi_ho_time_limit.clear_focus();
6986 Multi_ho_kill_limit.clear_focus();
6987 Multi_ho_voice_wait.clear_focus();
6989 Multi_ho_lastframe_input = 0;
6994 void multi_ho_blit_max_respawns()
6998 // if we're in campaign mode, do nothing
6999 if(Netgame.campaign_mode == MP_CAMPAIGN){
7003 // otherwise blit the max as specified by the current mission file
7004 sprintf(string,"(%d)",Multi_ho_mission_respawn);
7005 gr_set_color_fast(&Color_normal);
7006 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);
7009 void multi_ho_display_skill_level()
7011 int skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
7014 SDL_assert((skill_level >= 0) && (skill_level < NUM_SKILL_LEVELS));
7015 if((skill_level < 0) || (skill_level >= NUM_SKILL_LEVELS)){
7019 gr_set_color_fast(&Color_bright);
7020 gr_string(Ho_st_coords[gr_screen.res][0], Ho_st_coords[gr_screen.res][1], Skill_level_names(skill_level, 1));
7023 // -------------------------------------------------------------------------------------------------------------
7025 // MULTIPLAYER JOIN SCREEN
7028 #define MULTI_JW_NUM_BUTTONS 8
7032 #define MULTI_JW_PALETTE "InterfacePalette"
7034 static const char *Multi_jw_bitmap_fname[GR_NUM_RESOLUTIONS] = {
7035 "MultiJoinWait", // GR_640
7036 "2_MultiJoinWait" // GR_1024
7039 static const char *Multi_jw_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
7040 "MultiJoinWait-M", // GR_640
7041 "2_MultiJoinWait-M" // GR_1024
7047 #define MJW_SCROLL_PLAYERS_UP 0
7048 #define MJW_SCROLL_PLAYERS_DOWN 1
7051 #define MJW_PILOT_INFO 4
7052 #define MJW_SCROLL_INFO_UP 5
7053 #define MJW_SCROLL_INFO_DOWN 6
7054 #define MJW_CANCEL 7
7056 UI_WINDOW Multi_jw_window; // the window object for the join screen
7057 int Multi_jw_bitmap; // the background bitmap
7059 // constants for coordinate lookup
7060 #define MJW_X_COORD 0
7061 #define MJW_Y_COORD 1
7062 #define MJW_W_COORD 2
7063 #define MJW_H_COORD 3
7065 ui_button_info Multi_jw_buttons[GR_NUM_RESOLUTIONS][MULTI_JW_NUM_BUTTONS] = {
7068 ui_button_info("MJW_00", 0, 50, -1, -1, 0),
7069 ui_button_info("MJW_01", 0, 87, -1, -1, 1),
7070 ui_button_info("MJW_02", 20, 219, -1, -1, 2),
7071 ui_button_info("MJW_03", 73, 219, -1, -1, 3),
7072 ui_button_info("MJW_09", 131, 213, -1, -1, 9),
7073 ui_button_info("MJW_05", 0, 398, -1, -1, 5),
7074 ui_button_info("MJW_06", 0, 435, -1, -1, 6),
7075 ui_button_info("MJW_04", 559, 411, -1, -1, 4),
7077 ui_button_info("MJW_00", 1, 24, -1, -1, 0),
7078 ui_button_info("MJW_01", 1, 66, -1, -1, 1),
7079 ui_button_info("MJW_02", 30, 244, 20, 272, 2),
7080 ui_button_info("MJW_03", 84, 244, 73, 272, 3),
7081 ui_button_info("MJW_04", 139, 242, 134, 272, 4),
7082 ui_button_info("MJW_05", 1, 406, -1, -1, 5),
7083 ui_button_info("MJW_06", 1, 447, -1, -1, 6),
7084 ui_button_info("MJW_07", 577, 428, 570, 414, 7),
7088 ui_button_info("2_MJW_00", 2, 38, -1, -1, 0),
7089 ui_button_info("2_MJW_01", 2, 106, -1, -1, 1),
7090 ui_button_info("2_MJW_02", 48, 390, 47, 435, 2),
7091 ui_button_info("2_MJW_03", 134, 390, 133, 435, 3),
7092 ui_button_info("2_MJW_04", 223, 388, 225, 435, 4),
7093 ui_button_info("2_MJW_05", 2, 649, -1, -1, 5),
7094 ui_button_info("2_MJW_06", 2, 715, -1, -1, 6),
7095 ui_button_info("2_MJW_07", 923, 685, 931, 667, 7),
7100 #define MULTI_JW_NUM_TEXT 7
7102 UI_XSTR Multi_jw_text[GR_NUM_RESOLUTIONS][MULTI_JW_NUM_TEXT] = {
7104 { "Team 1", 1308, 20, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_TEAM0].button },
7105 { "Team 2", 1309, 73, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_TEAM1].button },
7106 { "Pilot", 1310, 134, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_PILOT_INFO].button },
7107 { "Info", 1311, 134, 283, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_PILOT_INFO].button },
7108 { "Cancel", 387, 570, 414, UI_XSTR_COLOR_PINK, -1, &Multi_jw_buttons[0][MJW_CANCEL].button },
7109 { "Players", 1269, 38, 8, UI_XSTR_COLOR_GREEN, -1, NULL },
7110 { "Choose Team", 1312, 27, 231, UI_XSTR_COLOR_GREEN, -1, NULL },
7113 { "Team 1", 1308, 47, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_TEAM0].button },
7114 { "Team 2", 1309, 133, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_TEAM1].button },
7115 { "Pilot", 1310, 225, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_PILOT_INFO].button },
7116 { "Info", 1311, 225, 446, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_PILOT_INFO].button },
7117 { "Cancel", 387, 931, 667, UI_XSTR_COLOR_PINK, -1, &Multi_jw_buttons[1][MJW_CANCEL].button },
7118 { "Players", 1269, 165, 12, UI_XSTR_COLOR_GREEN, -1, NULL },
7119 { "Choose Team", 1312, 45, 373, UI_XSTR_COLOR_GREEN, -1, NULL },
7124 int Mjw_players_coords[GR_NUM_RESOLUTIONS][4] = {
7137 int Mjw_mission_name_coords[GR_NUM_RESOLUTIONS][2] = {
7146 // squad war checkbox
7147 UI_CHECKBOX Multi_jw_sw_checkbox;
7148 const char *Multi_jw_sw_checkbox_fname[GR_NUM_RESOLUTIONS] = {
7152 int Multi_jw_sw_checkbox_coords[GR_NUM_RESOLUTIONS][2] = {
7160 int Multi_jw_sw_checkbox_text[GR_NUM_RESOLUTIONS][2] = {
7170 // player list control thingie defs
7171 #define MULTI_JW_PLIST_MAX_DISPLAY 19
7172 int Multi_jw_plist_select_flag; // indicates whether we currently have a selected player
7173 short Multi_jw_plist_select_id; // id of the current selected player
7174 UI_BUTTON Multi_jw_plist_select_button; // for selecting a player
7176 int Multi_jw_should_show_popup = 0;
7178 // LOCAL function definitions
7179 void multi_jw_check_buttons();
7180 void multi_jw_button_pressed(int n);
7181 void multi_jw_do_netstuff();
7182 void multi_jw_scroll_players_up();
7183 void multi_jw_scroll_players_down();
7184 void multi_jw_plist_process();
7185 void multi_jw_plist_blit_normal();
7186 void multi_jw_plist_blit_team();
7187 short multi_jw_get_mouse_id();
7189 void multi_game_client_setup_init()
7193 // create the interface window
7194 Multi_jw_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
7195 Multi_jw_window.set_mask_bmap(Multi_jw_bitmap_mask_fname[gr_screen.res]);
7197 // load the background bitmap
7198 Multi_jw_bitmap = bm_load(Multi_jw_bitmap_fname[gr_screen.res]);
7199 if(Multi_jw_bitmap < 0){
7200 // we failed to load the bitmap - this is very bad
7204 // initialize the player list data
7205 Multi_jw_plist_select_flag = 0;
7206 Multi_jw_plist_select_id = -1;
7208 // kill any old instances of the chatbox and create a new one
7210 chatbox_create(CHATBOX_FLAG_BIG | CHATBOX_FLAG_DRAW_BOX | CHATBOX_FLAG_BUTTONS);
7212 // initialize the common notification messaging
7213 multi_common_notify_init();
7215 // initialize the common mission info display area.
7216 multi_common_set_text("");
7218 // use the common interface palette
7219 multi_common_set_palette();
7221 // create the interface buttons
7222 for(idx=0; idx<MULTI_JW_NUM_BUTTONS; idx++){
7223 // create the object
7224 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);
7226 // set the sound to play when highlighted
7227 Multi_jw_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
7229 // set the ani for the button
7230 Multi_jw_buttons[gr_screen.res][idx].button.set_bmaps(Multi_jw_buttons[gr_screen.res][idx].filename);
7233 Multi_jw_buttons[gr_screen.res][idx].button.link_hotspot(Multi_jw_buttons[gr_screen.res][idx].hotspot);
7236 // if this is a PXO game, enable the squadwar checkbox
7237 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);
7238 Multi_jw_sw_checkbox.set_bmaps(Multi_jw_sw_checkbox_fname[gr_screen.res], 6, 0);
7239 Multi_jw_sw_checkbox.disable();
7240 if(!MULTI_IS_TRACKER_GAME){
7241 Multi_jw_sw_checkbox.hide();
7246 for(idx=0; idx<MULTI_JW_NUM_TEXT; idx++){
7247 Multi_jw_window.add_XSTR(&Multi_jw_text[gr_screen.res][idx]);
7251 // create the player select list button and hide it
7252 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);
7253 Multi_jw_plist_select_button.hide();
7256 Multi_jw_buttons[gr_screen.res][MJW_CANCEL].button.set_hotkey(SDLK_ESCAPE);
7258 // remove campaign flags
7259 Game_mode &= ~(GM_CAMPAIGN_MODE);
7261 // tell the server we have finished joining
7262 Net_player->state = NETPLAYER_STATE_JOINED;
7263 send_netplayer_update_packet();
7266 ml_printf(NOX("Joined netgame %s"), Netgame.name);
7268 // send any appropriate files
7269 multi_data_send_my_junk();
7272 void multi_game_client_setup_do_frame()
7275 int k = chatbox_process();
7276 char mission_text[255];
7277 k = Multi_jw_window.process(k,0);
7279 Multi_jw_should_show_popup = 0;
7281 // process any button clicks
7282 multi_jw_check_buttons();
7284 // do any network related stuff
7285 multi_jw_do_netstuff();
7287 // draw the background, etc
7289 GR_MAYBE_CLEAR_RES(Multi_jw_bitmap);
7290 if(Multi_jw_bitmap != -1){
7291 gr_set_bitmap(Multi_jw_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7295 // if we're not in team vs. team mode, don't draw the team buttons
7296 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
7297 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.hide();
7298 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.hide();
7299 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.disable();
7300 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.disable();
7302 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.enable();
7303 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.enable();
7304 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.unhide();
7305 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.unhide();
7308 if(MULTI_IS_TRACKER_GAME){
7309 // maybe check the squadwar button
7310 if(Netgame.type_flags & NG_TYPE_SW){
7311 Multi_jw_sw_checkbox.set_state(1);
7312 gr_set_color_fast(&Color_bright);
7314 Multi_jw_sw_checkbox.set_state(0);
7315 gr_set_color_fast(&Color_normal);
7318 gr_string(Multi_jw_sw_checkbox_text[gr_screen.res][0], Multi_jw_sw_checkbox_text[gr_screen.res][1], "SquadWar");
7321 // draw the UI window
7322 Multi_jw_window.draw();
7324 // process and display the player list
7325 // NOTE : this must be done before the buttons are checked to insure that a player hasn't dropped
7326 multi_jw_plist_process();
7327 if(Netgame.type_flags & NG_TYPE_TEAM){
7328 multi_jw_plist_blit_team();
7330 multi_jw_plist_blit_normal();
7333 // display any text in the info area
7334 multi_common_render_text();
7336 // display any pending notification messages
7337 multi_common_notify_do();
7339 // blit the mission filename if possible
7340 if(Netgame.campaign_mode){
7341 if(strlen(Netgame.campaign_name) > 0){
7342 strcpy(mission_text,Netgame.campaign_name);
7344 if(strlen(Netgame.title) > 0){
7345 strcat(mission_text,", ");
7346 strcat(mission_text,Netgame.title);
7349 gr_set_color_fast(&Color_bright_white);
7350 gr_string(Mjw_mission_name_coords[gr_screen.res][MJW_X_COORD],Mjw_mission_name_coords[gr_screen.res][MJW_Y_COORD],mission_text);
7353 if(strlen(Netgame.mission_name) > 0){
7354 strcpy(mission_text,Netgame.mission_name);
7356 if(strlen(Netgame.title) > 0){
7357 strcat(mission_text,", ");
7358 strcat(mission_text,Netgame.title);
7361 gr_set_color_fast(&Color_bright_white);
7362 gr_string(Mjw_mission_name_coords[gr_screen.res][MJW_X_COORD],Mjw_mission_name_coords[gr_screen.res][MJW_Y_COORD],mission_text);
7366 // process and show the chatbox thingie
7370 Multi_jw_window.draw_tooltip();
7372 // display the voice status indicator
7373 multi_common_voice_display_status();
7378 // if we're supposed to be displaying a pilot info popup
7379 if(Multi_jw_should_show_popup){
7380 player_index = find_player_id(Multi_jw_plist_select_id);
7381 if(player_index != -1){
7382 multi_pinfo_popup(&Net_players[player_index]);
7387 void multi_game_client_setup_close()
7389 // unload any bitmaps
7390 if(!bm_unload(Multi_jw_bitmap)){
7391 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_jw_bitmap_fname[gr_screen.res]));
7394 // destroy the chatbox
7397 // destroy the UI_WINDOW
7398 Multi_jw_window.destroy();
7401 if(Netgame.game_state == NETGAME_STATE_MISSION_SYNC){
7402 gamesnd_play_iface(SND_COMMIT_PRESSED);
7407 void multi_jw_check_buttons()
7410 for(idx=0;idx<MULTI_JW_NUM_BUTTONS;idx++){
7411 // we only really need to check for one button pressed at a time, so we can break after
7413 if(Multi_jw_buttons[gr_screen.res][idx].button.pressed()){
7414 multi_jw_button_pressed(idx);
7420 void multi_jw_button_pressed(int n)
7424 gamesnd_play_iface(SND_USER_SELECT);
7425 multi_quit_game(PROMPT_CLIENT);
7427 case MJW_SCROLL_PLAYERS_UP:
7428 multi_jw_scroll_players_up();
7430 case MJW_SCROLL_PLAYERS_DOWN:
7431 multi_jw_scroll_players_down();
7433 case MJW_SCROLL_INFO_UP:
7434 multi_common_scroll_text_up();
7436 case MJW_SCROLL_INFO_DOWN:
7437 multi_common_scroll_text_down();
7440 // request to set myself to team 0
7442 gamesnd_play_iface(SND_USER_SELECT);
7443 multi_team_set_team(Net_player,0);
7446 // request to set myself to team 1
7448 gamesnd_play_iface(SND_USER_SELECT);
7449 multi_team_set_team(Net_player,1);
7453 case MJW_PILOT_INFO:
7454 Multi_jw_should_show_popup = 1;
7458 multi_common_add_notify(XSTR("Not implemented yet!",760));
7463 // do stuff like pinging servers, sending out requests, etc
7464 void multi_jw_do_netstuff()
7468 void multi_jw_scroll_players_up()
7470 gamesnd_play_iface(SND_GENERAL_FAIL);
7473 // scroll down through the player list
7474 void multi_jw_scroll_players_down()
7476 gamesnd_play_iface(SND_GENERAL_FAIL);
7479 void multi_jw_plist_process()
7481 int test_count,player_index,idx;
7483 // first determine if there are 0 players in the game. This should never happen since the host is _always_ in the game
7485 for(idx=0;idx<MAX_PLAYERS;idx++){
7486 // count anyone except the standalone server (if applicable)
7487 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7491 if(test_count <= 0){
7495 // if we had a selected item but that player has left, select myself instead
7496 if(Multi_jw_plist_select_flag){
7497 player_index = find_player_id(Multi_jw_plist_select_id);
7498 if(player_index == -1){
7499 Multi_jw_plist_select_id = Net_player->player_id;
7502 Multi_jw_plist_select_flag = 1;
7503 Multi_jw_plist_select_id = Net_player->player_id;
7506 // if the player has clicked somewhere in the player list area
7507 if(Multi_jw_plist_select_button.pressed()){
7511 player_id = multi_jw_get_mouse_id();
7512 player_index = find_player_id(player_id);
7513 if(player_index != -1){
7514 Multi_jw_plist_select_id = player_id;
7515 Multi_jw_plist_select_flag = 1;
7520 void multi_jw_plist_blit_normal()
7523 char str[CALLSIGN_LEN+1];
7524 int y_start = Mjw_players_coords[gr_screen.res][MJW_Y_COORD];
7527 // display all the players
7528 for(idx=0;idx<MAX_PLAYERS;idx++){
7529 // reset total offset
7532 // count anyone except the standalone server (if applicable)
7533 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7534 // highlight him if he's the host
7535 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7536 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7537 gr_set_color_fast(&Color_text_active_hi);
7539 gr_set_color_fast(&Color_bright);
7542 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7543 gr_set_color_fast(&Color_text_active);
7545 gr_set_color_fast(&Color_text_normal);
7549 // optionally draw his CD status
7550 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7551 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7552 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7554 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7557 // make sure the string will fit, then display it
7558 strcpy(str,Net_players[idx].player->callsign);
7559 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
7562 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7563 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7570 void multi_jw_plist_blit_team()
7573 char str[CALLSIGN_LEN+1];
7574 int y_start = Mjw_players_coords[gr_screen.res][MJW_Y_COORD];
7577 // always blit the proper team button based on _my_ team status
7578 if(Net_player->p_info.team == 0){
7579 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.draw_forced(2);
7581 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.draw_forced(2);
7584 // display all the red players first
7585 for(idx=0;idx<MAX_PLAYERS;idx++){
7586 // reset total offset
7589 // count anyone except the standalone server (if applicable)
7590 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
7591 // highlight him if he's the host
7592 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7593 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7594 gr_set_color_fast(&Color_text_active_hi);
7596 gr_set_color_fast(&Color_bright);
7599 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7600 gr_set_color_fast(&Color_text_active);
7602 gr_set_color_fast(&Color_text_normal);
7606 // optionally draw his CD status
7607 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7608 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7609 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7611 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7614 // blit the red team indicator
7615 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
7616 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
7617 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7618 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7620 total_offset += Multi_common_icon_dims[MICON_TEAM0_SELECT][0] + 1;
7623 if(Multi_common_icons[MICON_TEAM0] != -1){
7624 gr_set_bitmap(Multi_common_icons[MICON_TEAM0], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7625 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7627 total_offset += Multi_common_icon_dims[MICON_TEAM0][0] + 1;
7631 // make sure the string will fit
7632 strcpy(str,Net_players[idx].player->callsign);
7633 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7635 // display him in the correct half of the list depending on his team
7636 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7641 // display all the green players next
7642 for(idx=0;idx<MAX_PLAYERS;idx++){
7643 // reset total offset
7646 // count anyone except the standalone server (if applicable)
7647 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
7648 // highlight him if he's the host
7649 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7650 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7651 gr_set_color_fast(&Color_text_active_hi);
7653 gr_set_color_fast(&Color_bright);
7656 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7657 gr_set_color_fast(&Color_text_active);
7659 gr_set_color_fast(&Color_text_normal);
7663 // optionally draw his CD status
7664 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7665 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7666 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7668 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7671 // blit the red team indicator
7672 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
7673 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
7674 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7675 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7677 total_offset += Multi_common_icon_dims[MICON_TEAM1_SELECT][0] + 1;
7680 if(Multi_common_icons[MICON_TEAM1] != -1){
7681 gr_set_bitmap(Multi_common_icons[MICON_TEAM1], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7682 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7684 total_offset += Multi_common_icon_dims[MICON_TEAM1][0] + 1;
7688 // make sure the string will fit
7689 strcpy(str,Net_players[idx].player->callsign);
7690 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
7693 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7695 // display him in the correct half of the list depending on his team
7696 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7702 void multi_jw_handle_join(net_player *pl)
7704 // for now just play a bloop sound
7705 gamesnd_play_iface(SND_ICON_DROP_ON_WING);
7708 short multi_jw_get_mouse_id()
7710 // determine where he clicked (y pixel value)
7712 Multi_jw_plist_select_button.get_mouse_pos(NULL,&y);
7714 // select things a little differently if we're in team vs. team or non-team vs. team mode
7716 if(Netgame.type_flags & NG_TYPE_TEAM){
7717 int player_index = -1;
7719 // look through all of team red first
7720 for(idx=0;idx<MAX_PLAYERS;idx++){
7721 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
7724 // if this is the _nth_ guy
7732 // if we still haven't found him yet, look through the green team
7733 if(player_index == -1){
7734 for(idx=0;idx<MAX_PLAYERS;idx++){
7735 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
7737 // if this is the _nth_ guy
7745 if(player_index != -1){
7746 return Net_players[idx].player_id;
7749 // select the nth active player if possible, disregarding the standalone server
7750 for(idx=0;idx<MAX_PLAYERS;idx++){
7751 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7754 // if this is the _nth_ guy
7756 return Net_players[idx].player_id;
7767 // -------------------------------------------------------------------------------------------------------------
7769 // MULTIPLAYER WAIT/SYNCH SCREEN
7772 #define MULTI_SYNC_HOST_COUNT 4 // host uses 4 buttons (and sometimes 5)
7773 #define MULTI_SYNC_CLIENT_COUNT 3 // client only uses 3 buttons
7775 const char *Multi_sync_bitmap_fname[GR_NUM_RESOLUTIONS] = {
7776 "MultiSynch", // GR_640
7777 "2_MultiSynch" // GR_1024
7780 const char *Multi_sync_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
7781 "MultiSynch-M", // GR_640
7782 "2_MultiSynch-M" // GR_1024
7787 // constants for coordinate lookup
7788 #define MS_X_COORD 0
7789 #define MS_Y_COORD 1
7790 #define MS_W_COORD 2
7791 #define MS_H_COORD 3
7793 UI_WINDOW Multi_sync_window; // the window object for the join screen
7794 int Multi_sync_button_count; // the # of buttons to use for this instance of mission sync
7795 int Multi_sync_bitmap; // the background bitmap
7798 #define MULTI_SYNC_NUM_BUTTONS 5
7799 #define MS_SCROLL_INFO_UP 0
7800 #define MS_SCROLL_INFO_DOWN 1
7804 ui_button_info Multi_sync_buttons[GR_NUM_RESOLUTIONS][MULTI_SYNC_NUM_BUTTONS] = {
7807 ui_button_info("MS_00", 0, 396, -1, -1, 0),
7808 ui_button_info("MS_01", 0, 435, -1, -1, 1),
7809 ui_button_info("MS_02", 496, 411, -1, -1, 2),
7810 ui_button_info("MS_04", 436, 403, -1, -1, 4),
7811 ui_button_info("MS_03", 556, 398, -1, -1, 3),
7813 ui_button_info("MS_00", 1, 404, -1, -1, 0),
7814 ui_button_info("MS_01", 1, 446, -1, -1, 1),
7815 ui_button_info("MS_03", 518, 426, 519, 416, 3),
7816 ui_button_info("MS_02", 469, 426, 479, 416, 2),
7817 ui_button_info("MS_04", 571, 420, 577, 416, 4),
7821 ui_button_info("2_MS_00", 2, 647, -1, -1, 0),
7822 ui_button_info("2_MS_01", 2, 713, -1, -1, 1),
7823 ui_button_info("2_MS_03", 829, 682, 831, 667, 3),
7824 ui_button_info("2_MS_02", 751, 682, 766, 667, 2),
7825 ui_button_info("2_MS_04", 914, 672, 924, 667, 4),
7831 #define MULTI_SYNC_NUM_TEXT 5
7833 #define MST_LAUNCH 2
7834 UI_XSTR Multi_sync_text[GR_NUM_RESOLUTIONS][MULTI_SYNC_NUM_TEXT] = {
7836 { "Kick", 1266, 479, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_KICK].button },
7837 { "Cancel", 387, 519, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_CANCEL].button },
7838 { "Launch", 801, 577, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_LAUNCH].button },
7839 { "Players", 1269, 23, 133, UI_XSTR_COLOR_GREEN, -1, NULL },
7840 { "Status", 1304, 228, 133, UI_XSTR_COLOR_GREEN, -1, NULL }
7843 { "Kick", 1266, 766, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_KICK].button },
7844 { "Cancel", 387, 831, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_CANCEL].button },
7845 { "Launch", 801, 924, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_LAUNCH].button },
7846 { "Players", 1269, 38, 214, UI_XSTR_COLOR_GREEN, -1, NULL },
7847 { "Status", 1304, 366, 214, UI_XSTR_COLOR_GREEN, -1, NULL }
7853 int Ms_status_coords[GR_NUM_RESOLUTIONS][4] = {
7862 // player status coords
7863 int Ms_status2_coords[GR_NUM_RESOLUTIONS][4] = {
7872 int Ms_cd_icon_offset[GR_NUM_RESOLUTIONS] = {
7877 int Ms_team_icon_offset[GR_NUM_RESOLUTIONS] = {
7882 // player currently selected, index into Net_players[]
7883 int Multi_sync_player_select = -1;
7885 // player list control thingie defs
7886 #define MULTI_SYNC_PLIST_MAX_DISPLAY 15
7887 int Multi_sync_plist_start; // where to start displaying from
7888 int Multi_sync_plist_count; // how many we have
7890 // list select button
7891 UI_BUTTON Multi_sync_plist_button;
7893 int Multi_sync_mode = -1;
7895 #define MULTI_SYNC_COUNTDOWN_TIME 5 // in seconds
7896 float Multi_sync_countdown_timer;
7897 int Multi_sync_countdown = -1;
7899 int Multi_launch_button_created;
7902 // countdown animation timer
7903 const char* Multi_sync_countdown_fname[GR_NUM_RESOLUTIONS] = {
7905 "2_Count" // GR_1024
7908 int Multi_sync_countdown_coords[GR_NUM_RESOLUTIONS][2] = {
7919 anim *Multi_sync_countdown_anim = NULL;
7920 anim_instance *Multi_sync_countdown_instance = NULL;
7923 // PREBRIEFING STUFF
7924 // syncing flags used by the server
7925 int Mission_sync_flags = 0;
7926 #define MS_FLAG_SENT_FILESIG (1<<0) // sent filesig requests
7927 #define MS_FLAG_SENT_LOAD (1<<1) // sent load packets
7928 #define MS_FLAG_PUSHED_BRIEFING (1<<2) // pushed everyone else into the briefing
7929 #define MS_FLAG_POST_DATA (1<<3) // sent the post data block
7930 #define MS_FLAG_WSS_SLOTS (1<<4) // all players have received wss slot data
7931 #define MS_FLAG_PSETTINGS (1<<5) // send the player settings packet
7932 #define MS_FLAG_MT_STATS_START (1<<6) // server has started getting player stats from the tracker
7933 #define MS_FLAG_MT_STATS_DONE (1<<7) // server has finished getting player stats from the tracker (success or fail)
7934 #define MS_FLAG_TS_SLOTS (1<<8) // team/ship slots have been sent
7935 #define MS_FLAG_DATA_DONE (1<<9) // done transferring all necessary data
7936 #define MS_FLAG_CAMP_DONE (1<<10) // send campaign pool/goal/event stuff
7938 // POSTBRIEFING STUFF
7939 int Multi_state_timestamp;
7940 int Multi_sync_launch_pressed;
7942 // LOCAL function definitions
7943 void multi_sync_check_buttons();
7944 void multi_sync_button_pressed(int n);
7945 void multi_sync_scroll_info_up();
7946 void multi_sync_scroll_info_down();
7947 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
7948 void multi_sync_display_status(const char *status, int index); // display info on the right hand portion of the status window thingie
7949 void multi_sync_force_start_pre();
7950 void multi_sync_force_start_post();
7951 void multi_sync_launch();
7952 void multi_sync_create_launch_button();
7953 void multi_sync_blit_screen_all();
7954 void multi_sync_handle_plist();
7956 void multi_sync_common_init();
7957 void multi_sync_common_do();
7958 void multi_sync_common_close();
7960 void multi_sync_pre_init();
7961 void multi_sync_pre_do();
7962 void multi_sync_pre_close();
7964 void multi_sync_post_init();
7965 void multi_sync_post_do();
7966 void multi_sync_post_close();
7971 // perform the correct init functions
7972 void multi_sync_init()
7974 Multi_sync_countdown = -1;
7978 // reset all timestamp
7979 multi_reset_timestamps();
7981 extern int Player_multi_died_check;
7982 Player_multi_died_check = -1;
7984 if(!(Game_mode & GM_STANDALONE_SERVER)){
7985 multi_sync_common_init();
7988 switch(Multi_sync_mode){
7989 case MULTI_SYNC_PRE_BRIEFING:
7990 multi_sync_pre_init();
7992 case MULTI_SYNC_POST_BRIEFING:
7993 multi_sync_post_init();
7995 case MULTI_SYNC_INGAME:
7996 multi_ingame_sync_init();
8001 // perform the correct do frame functions
8002 void multi_sync_do()
8004 if(!(Game_mode & GM_STANDALONE_SERVER)){
8005 multi_sync_common_do();
8008 // if the netgame is ending, don't do any sync processing
8009 if(multi_endgame_ending()){
8013 // process appropriateliy
8014 switch(Multi_sync_mode){
8015 case MULTI_SYNC_PRE_BRIEFING:
8016 multi_sync_pre_do();
8018 case MULTI_SYNC_POST_BRIEFING:
8019 multi_sync_post_do();
8021 case MULTI_SYNC_INGAME:
8022 multi_ingame_sync_do();
8025 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8026 if(Multi_sync_bitmap != -1){
8027 gr_set_bitmap(Multi_sync_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8030 Multi_sync_window.draw();
8032 multi_sync_blit_screen_all();
8039 // perform the correct close functions
8040 void multi_sync_close()
8042 switch(Multi_sync_mode){
8043 case MULTI_SYNC_PRE_BRIEFING:
8044 multi_sync_pre_close();
8046 case MULTI_SYNC_POST_BRIEFING:
8047 multi_sync_post_close();
8049 case MULTI_SYNC_INGAME:
8050 multi_ingame_sync_close();
8054 if(!(Game_mode & GM_STANDALONE_SERVER)){
8055 multi_sync_common_close();
8059 const char *multi_sync_tooltip_handler(const char *str)
8061 if (!SDL_strcasecmp(str, NOX("@launch"))) {
8062 if (Multi_launch_button_created){
8063 return XSTR("Launch",801);
8070 void multi_sync_common_init()
8074 // create the interface window
8075 Multi_sync_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
8076 Multi_sync_window.set_mask_bmap(Multi_sync_bitmap_mask_fname[gr_screen.res]);
8077 Multi_sync_window.tooltip_handler = multi_sync_tooltip_handler;
8079 // load the background bitmap
8080 Multi_sync_bitmap = bm_load(Multi_sync_bitmap_fname[gr_screen.res]);
8081 if (Multi_sync_bitmap < 0) {
8082 // we failed to load the bitmap - this is very bad
8086 // initialize the player list data
8087 Multi_sync_plist_start = 0;
8088 Multi_sync_plist_count = 1; // we can pretty safely assume that there's one player in the game - me.
8090 Multi_launch_button_created = 0;
8092 // create the chatbox thingie (shouldn't be necesary to do this, but we'll put it in for good measure)
8095 // force the chatbox to be small
8096 chatbox_force_small();
8098 // initialize the common notification messaging
8099 multi_common_notify_init();
8101 // initialize the common mission info display area.
8102 multi_common_set_text("");
8104 // use the common interface palette
8105 multi_common_set_palette();
8107 // don't select any player yet.
8108 Multi_sync_player_select = -1;
8110 // determine how many of the 5 buttons to create
8111 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8112 Multi_sync_button_count = MULTI_SYNC_HOST_COUNT;
8114 Multi_sync_button_count = MULTI_SYNC_CLIENT_COUNT;
8116 // create the interface buttons
8117 for(idx=0; idx<Multi_sync_button_count; idx++){
8118 // create the object
8119 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);
8121 // set the sound to play when highlighted
8122 Multi_sync_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
8124 // set the ani for the button
8125 // this wierdness is necessary because cancel and kick buttons aren't drawn on the background bitmap,
8126 // so we have to load in frame 0, too (the file should exist)
8127 if ((idx == MS_CANCEL) || (idx == MS_KICK) || (idx == MS_LAUNCH)) {
8128 Multi_sync_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sync_buttons[gr_screen.res][idx].filename, 3, 0);
8130 Multi_sync_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sync_buttons[gr_screen.res][idx].filename);
8134 Multi_sync_buttons[gr_screen.res][idx].button.link_hotspot(Multi_sync_buttons[gr_screen.res][idx].hotspot);
8139 for(idx=0; idx<MULTI_SYNC_NUM_TEXT; idx++) {
8140 // don't create the "launch" button text just yet
8141 if(idx == MST_LAUNCH) {
8144 // multiplayer clients should ignore the kick button
8145 if(!MULTIPLAYER_MASTER && !MULTIPLAYER_HOST && (idx == MST_KICK)) {
8149 Multi_sync_window.add_XSTR(&Multi_sync_text[gr_screen.res][idx]);
8153 // create the player list select button and hide it
8154 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);
8155 Multi_sync_plist_button.hide();
8157 // set up hotkeys for certain common functions
8158 Multi_sync_buttons[gr_screen.res][MS_CANCEL].button.set_hotkey(SDLK_ESCAPE);
8161 void multi_sync_common_do()
8163 int k = chatbox_process();
8164 k = Multi_sync_window.process(k);
8166 // process the player list
8167 multi_sync_handle_plist();
8169 // process any button clicks
8170 multi_sync_check_buttons();
8172 // process any keypresses
8176 gamesnd_play_iface(SND_USER_SELECT);
8177 multi_quit_game(PROMPT_ALL);
8182 void multi_sync_common_close()
8184 // unload any bitmaps
8185 if(!bm_unload(Multi_sync_bitmap)){
8186 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_sync_bitmap_fname[gr_screen.res]));
8189 extern int Player_multi_died_check;
8190 Player_multi_died_check = -1;
8192 // destroy the UI_WINDOW
8193 Multi_sync_window.destroy();
8196 void multi_sync_blit_screen_all()
8203 // display any text in the info area
8204 multi_common_render_text();
8206 // display any pending notification messages
8207 multi_common_notify_do();
8209 // display any info about visible players
8211 for(idx=0;idx<MAX_PLAYERS;idx++){
8212 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
8213 // display his name and status
8214 multi_sync_display_name(Net_players[idx].player->callsign,count,idx);
8216 // get the player state
8217 state = Net_players[idx].state;
8219 // if we're ingame joining, show all other players except myself as "playing"
8220 if((Net_player != NULL) && (&Net_players[idx] != Net_player) && ((Multi_sync_mode == MULTI_SYNC_INGAME) || (Net_player->flags & NETINFO_FLAG_INGAME_JOIN)) ){
8221 state = NETPLAYER_STATE_IN_MISSION;
8225 case NETPLAYER_STATE_MISSION_LOADING:
8226 multi_sync_display_status(XSTR("Mission Loading",802),count);
8228 case NETPLAYER_STATE_INGAME_SHIP_SELECT: // I don't think its possible to see this state, but...
8229 multi_sync_display_status(XSTR("Ingame Ship Select",803),count);
8231 case NETPLAYER_STATE_DEBRIEF:
8232 multi_sync_display_status(XSTR("Debriefing",804),count);
8234 case NETPLAYER_STATE_MISSION_SYNC:
8235 multi_sync_display_status(XSTR("Mission Sync",805),count);
8237 case NETPLAYER_STATE_JOINING:
8238 multi_sync_display_status(XSTR("Joining",806),count);
8240 case NETPLAYER_STATE_JOINED:
8241 multi_sync_display_status(XSTR("Joined",807),count);
8243 case NETPLAYER_STATE_SLOT_ACK :
8244 multi_sync_display_status(XSTR("Slot Ack",808),count);
8246 case NETPLAYER_STATE_BRIEFING:
8247 multi_sync_display_status(XSTR("Briefing",765),count);
8249 case NETPLAYER_STATE_SHIP_SELECT:
8250 multi_sync_display_status(XSTR("Ship Select",809),count);
8252 case NETPLAYER_STATE_WEAPON_SELECT:
8253 multi_sync_display_status(XSTR("Weapon Select",810),count);
8255 case NETPLAYER_STATE_WAITING:
8256 multi_sync_display_status(XSTR("Waiting",811),count);
8258 case NETPLAYER_STATE_IN_MISSION:
8259 multi_sync_display_status(XSTR("In Mission",812),count);
8261 case NETPLAYER_STATE_MISSION_LOADED:
8262 multi_sync_display_status(XSTR("Mission Loaded",813),count);
8264 case NETPLAYER_STATE_DATA_LOAD:
8265 multi_sync_display_status(XSTR("Data loading",814),count);
8267 case NETPLAYER_STATE_SETTINGS_ACK:
8268 multi_sync_display_status(XSTR("Ready To Enter Mission",815),count);
8270 case NETPLAYER_STATE_INGAME_SHIPS:
8271 multi_sync_display_status(XSTR("Ingame Ships Packet Ack",816),count);
8273 case NETPLAYER_STATE_INGAME_WINGS:
8274 multi_sync_display_status(XSTR("Ingame Wings Packet Ack",817),count);
8276 case NETPLAYER_STATE_INGAME_RPTS:
8277 multi_sync_display_status(XSTR("Ingame Respawn Points Ack",818),count);
8279 case NETPLAYER_STATE_SLOTS_ACK:
8280 multi_sync_display_status(XSTR("Ingame Weapon Slots Ack",819),count);
8282 case NETPLAYER_STATE_POST_DATA_ACK:
8283 multi_sync_display_status(XSTR("Post Briefing Data Block Ack",820),count);
8285 case NETPLAYER_STATE_FLAG_ACK :
8286 multi_sync_display_status(XSTR("Flags Ack",821),count);
8288 case NETPLAYER_STATE_MT_STATS :
8289 multi_sync_display_status(XSTR("Parallax Online Stats Updating",822),count);
8291 case NETPLAYER_STATE_WSS_ACK :
8292 multi_sync_display_status(XSTR("Weapon Slots Ack",823),count);
8294 case NETPLAYER_STATE_HOST_SETUP :
8295 multi_sync_display_status(XSTR("Host setup",824),count);
8297 case NETPLAYER_STATE_DEBRIEF_ACCEPT:
8298 multi_sync_display_status(XSTR("Debrief accept",825),count);
8300 case NETPLAYER_STATE_DEBRIEF_REPLAY:
8301 multi_sync_display_status(XSTR("Debrief replay",826),count);
8303 case NETPLAYER_STATE_CPOOL_ACK:
8304 multi_sync_display_status(XSTR("Campaign ship/weapon ack",827),count);
8306 case NETPLAYER_STATE_MISSION_XFER :
8308 // server should display the pct completion of all clients
8309 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8310 if(Net_players[idx].s_info.xfer_handle != -1){
8311 pct_complete = multi_xfer_pct_complete(Net_players[idx].s_info.xfer_handle);
8313 // if we've got a valid xfer handle
8314 if((pct_complete >= 0.0) && (pct_complete <= 1.0)){
8315 sprintf(txt,XSTR("Mission file xfer %d%%",828),(int)(pct_complete * 100.0f));
8319 strcpy(txt,XSTR("Mission file xfer",829));
8322 strcpy(txt,XSTR("Mission file xfer",829));
8325 // clients should display only for themselves (which is the only thing they know)
8327 // if we've got a valid file xfer handle
8328 if((&Net_players[idx] == Net_player) && (Net_player->s_info.xfer_handle != -1)){
8329 pct_complete = multi_xfer_pct_complete(Net_player->s_info.xfer_handle);
8331 // if we've got a valid xfer handle
8332 if((pct_complete >= 0.0) && (pct_complete <= 1.0)){
8333 sprintf(txt,XSTR("Mission file xfer %d%%",828),(int)(pct_complete * 100.0f));
8337 strcpy(txt,XSTR("Mission file xfer",829));
8342 strcpy(txt,XSTR("Mission file xfer",829));
8347 multi_sync_display_status(txt,count);
8350 nprintf(("Network","Unhandled player state : %d !\n",Net_players[idx].state));
8357 // display the mission start countdown timer (if any)
8358 anim_render_all(GS_STATE_MULTI_MISSION_SYNC,flFrametime);
8360 // process and show the chatbox thingie
8364 Multi_sync_window.draw_tooltip();
8366 // display the voice status indicator
8367 multi_common_voice_display_status();
8370 void multi_sync_check_buttons()
8373 for(idx=0;idx<Multi_sync_button_count;idx++){
8374 // we only really need to check for one button pressed at a time, so we can break after
8376 if(Multi_sync_buttons[gr_screen.res][idx].button.pressed()){
8377 multi_sync_button_pressed(idx);
8383 void multi_sync_button_pressed(int n)
8388 gamesnd_play_iface(SND_USER_SELECT);
8389 multi_quit_game(PROMPT_ALL);
8392 // scroll the info box up
8393 case MS_SCROLL_INFO_UP:
8394 multi_common_scroll_text_up();
8397 // scroll the info box down
8398 case MS_SCROLL_INFO_DOWN:
8399 multi_common_scroll_text_down();
8404 // if we have a currently selected player, kick him
8405 if(Multi_sync_player_select >= 0){
8406 multi_kick_player(Multi_sync_player_select);
8410 // start the final launch countdown (post-sync only)
8412 multi_sync_start_countdown();
8415 // doesn't do anything
8421 void multi_sync_pre_init()
8425 Netgame.game_state = NETGAME_STATE_MISSION_SYNC;
8427 // if we're in teamplay mode, always force skill level to be medium
8428 if((Netgame.type_flags & NG_TYPE_TEAM) && (Net_player->flags & NETINFO_FLAG_GAME_HOST)){
8429 Netgame.options.skill_level = NUM_SKILL_LEVELS / 2;
8430 Game_skill_level = NUM_SKILL_LEVELS / 2;
8431 multi_options_update_netgame();
8434 // notify everyone of when we get here
8435 if(!(Game_mode & GM_STANDALONE_SERVER)){
8436 Net_player->state = NETPLAYER_STATE_MISSION_SYNC;
8437 send_netplayer_update_packet();
8440 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8442 ml_string(NOX("Server performing pre-briefing data sync"));
8444 if(!(Game_mode & GM_STANDALONE_SERVER)){
8445 multi_common_set_text(XSTR("Server performing sync\n",830),1);
8448 // maybe initialize tvt and squad war stuff
8449 if(Netgame.type_flags & NG_TYPE_TEAM){
8450 multi_team_level_init();
8453 // force everyone into this state
8454 send_netgame_update_packet();
8456 if(!(Game_mode & GM_STANDALONE_SERVER)){
8457 multi_common_add_text(XSTR("Send update packet\n",831),1);
8460 // setup some of my own data
8461 Net_player->flags |= NETINFO_FLAG_MISSION_OK;
8463 // do any output stuff
8464 if(Game_mode & GM_STANDALONE_SERVER){
8465 std_debug_set_standalone_state_string("Mission Sync");
8468 // do this here to insure we have the most up to date file checksum info
8469 multi_get_mission_checksum(Game_current_mission_filename);
8470 // parse_get_file_signature(Game_current_mission_filename);
8472 if(!(Game_mode & GM_STANDALONE_SERVER)){
8473 multi_common_add_text(XSTR("Got file signatures\n",832),1);
8476 if(!(Game_mode & GM_STANDALONE_SERVER)){
8477 multi_common_add_text(XSTR("Sending update state packet\n",833),1);
8481 // if we're not in team vs. team mode - set all player teams to be 0, and unset all captaincy bits
8482 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
8483 for(idx=0;idx<MAX_PLAYERS;idx++){
8484 Net_players[idx].p_info.team = 0;
8485 Net_players[idx].flags &= ~(NETINFO_FLAG_TEAM_CAPTAIN);
8489 // we aren't necessarily xferring the mission file yet
8490 SDL_assert(Net_player->s_info.xfer_handle == -1);
8492 // always call this for good measure
8493 multi_campaign_flush_data();
8495 Mission_sync_flags = 0;
8496 Multi_mission_loaded = 0;
8499 void multi_sync_pre_do()
8503 // If I'm the server, wait for everyone to arrive in this state, then begin transferring data, etc.
8504 // all servers (standalone or no, go through this)
8505 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8506 // wait for everyone to arrive, then request filesig from all of them
8507 if(multi_netplayer_state_check(NETPLAYER_STATE_MISSION_SYNC) && !(Mission_sync_flags & MS_FLAG_SENT_FILESIG) && !multi_endgame_ending()){
8508 send_file_sig_request(Netgame.mission_name);
8509 Mission_sync_flags |= MS_FLAG_SENT_FILESIG;
8511 if(!(Game_mode & GM_STANDALONE_SERVER)){
8512 multi_common_add_text(XSTR("Sent filesig request\n",834),1);
8516 // if we're waiting for players to receive files, then check on their status
8517 if((Mission_sync_flags & MS_FLAG_SENT_FILESIG) && !multi_netplayer_flag_check(NETINFO_FLAG_MISSION_OK) && !multi_endgame_ending()){
8518 for(idx=0;idx<MAX_PLAYERS;idx++){
8519 // if this player is in the process of xferring a file
8520 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].s_info.xfer_handle != -1)){
8521 switch(multi_xfer_get_status(Net_players[idx].s_info.xfer_handle)){
8522 // if it has successfully completed, set his ok flag
8523 case MULTI_XFER_SUCCESS :
8525 Net_players[idx].flags |= NETINFO_FLAG_MISSION_OK;
8527 // release the xfer instance handle
8528 multi_xfer_release_handle(Net_players[idx].s_info.xfer_handle);
8529 Net_players[idx].s_info.xfer_handle = -1;
8531 // if it has failed or timed-out, kick the player
8532 case MULTI_XFER_TIMEDOUT:
8533 case MULTI_XFER_FAIL:
8534 // release the xfer handle
8535 multi_xfer_release_handle(Net_players[idx].s_info.xfer_handle);
8536 Net_players[idx].s_info.xfer_handle = -1;
8539 multi_kick_player(idx, 0, KICK_REASON_BAD_XFER);
8546 // NOTE : this is now obsolete
8547 // once everyone is verified, do any data transfer necessary
8548 if(multi_netplayer_flag_check(NETINFO_FLAG_MISSION_OK) && !(Mission_sync_flags & MS_FLAG_DATA_DONE) && !multi_endgame_ending()){
8549 // do nothing for now
8550 Mission_sync_flags |= MS_FLAG_DATA_DONE;
8552 // send campaign pool data
8553 multi_campaign_send_pool_status();
8556 // wait for everyone to ack on campaign pool data (even in non-campaign situations)
8557 if((Mission_sync_flags & MS_FLAG_DATA_DONE) && !(Mission_sync_flags & MS_FLAG_CAMP_DONE) && !multi_endgame_ending()){
8558 // check to see if everyone has acked the campaign pool data
8559 if(multi_netplayer_state_check(NETPLAYER_STATE_CPOOL_ACK)){
8560 Mission_sync_flags |= MS_FLAG_CAMP_DONE;
8564 // once everyone is verified, tell them to load the mission
8565 // also make sure to load the mission myself _AFTER_ telling everyone to do so. This makes the whole process
8566 // move along faster
8567 if((Mission_sync_flags & MS_FLAG_CAMP_DONE) && !(Mission_sync_flags & MS_FLAG_SENT_LOAD) && !multi_endgame_ending()){
8568 send_netplayer_load_packet(NULL);
8569 Mission_sync_flags |= MS_FLAG_SENT_LOAD;
8571 if(!(Game_mode & GM_STANDALONE_SERVER)){
8572 multi_common_add_text(XSTR("Sent load packet\n",835),1);
8575 // load the mission myself, as soon as possible
8576 if(!Multi_mission_loaded){
8577 nprintf(("Network","Server loading mission..."));
8579 // update everyone about my status
8580 Net_player->state = NETPLAYER_STATE_MISSION_LOADING;
8581 send_netplayer_update_packet();
8583 game_start_mission();
8585 nprintf(("Network","Done\n"));
8586 Multi_mission_loaded = 1;
8588 // update everyone about my status
8589 Net_player->state = NETPLAYER_STATE_MISSION_LOADED;
8590 send_netplayer_update_packet();
8592 if(!(Game_mode & GM_STANDALONE_SERVER)){
8593 multi_common_add_text(XSTR("Loaded mission locally\n",836),1);
8598 // if everyone has loaded the mission, randomly assign players to ships
8599 if(multi_netplayer_state_check(NETPLAYER_STATE_MISSION_LOADED) && !(Mission_sync_flags & MS_FLAG_TS_SLOTS) && !multi_endgame_ending()){
8600 // call the team select function to assign players to their ships, wings, etc
8601 multi_ts_assign_players_all();
8602 send_netplayer_slot_packet();
8605 Mission_sync_flags |= MS_FLAG_TS_SLOTS;
8608 // if everyone has loaded the mission, move to the team select stage
8609 if(Sync_test && multi_netplayer_state_check(NETPLAYER_STATE_SLOT_ACK) && !(Mission_sync_flags & MS_FLAG_PUSHED_BRIEFING) && !multi_endgame_ending()){
8610 Netgame.game_state = NETGAME_STATE_BRIEFING;
8611 send_netgame_update_packet(); // this will push everyone into the next state
8613 // the standalone moves to his own wait state, whereas in the normal game mode, the server/host moves in to the
8614 // team select state
8615 if(Game_mode & GM_STANDALONE_SERVER){
8616 gameseq_post_event(GS_EVENT_MULTI_STD_WAIT);
8618 gameseq_post_event(GS_EVENT_START_GAME);
8621 Mission_sync_flags |= MS_FLAG_PUSHED_BRIEFING;
8623 if(!(Game_mode & GM_STANDALONE_SERVER)){
8624 multi_common_add_text(XSTR("Moving to team select\n",837),1);
8628 // clients should detect here if they are doing a file xfer and do error processing
8629 if((Net_player->state == NETPLAYER_STATE_MISSION_XFER) && (Net_player->s_info.xfer_handle != -1) && !multi_endgame_ending()){
8630 switch(multi_xfer_get_status(Net_player->s_info.xfer_handle)){
8631 // if it has successfully completed, set his ok flag
8632 case MULTI_XFER_SUCCESS :
8633 // release my xfer handle
8634 multi_xfer_release_handle(Net_player->s_info.xfer_handle);
8635 Net_player->s_info.xfer_handle = -1;
8638 // if it has failed or timed-out, kick the player
8639 case MULTI_XFER_TIMEDOUT:
8640 case MULTI_XFER_FAIL:
8641 // release my xfer handle
8642 multi_xfer_release_handle(Net_player->s_info.xfer_handle);
8643 Net_player->s_info.xfer_handle = -1;
8645 // leave the game qith an error code
8646 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_XFER_FAIL);
8653 if(!(Game_mode & GM_STANDALONE_SERVER)){
8655 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8656 if(Multi_sync_bitmap != -1){
8657 gr_set_bitmap(Multi_sync_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8660 Multi_sync_window.draw();
8662 multi_sync_blit_screen_all();
8668 void multi_sync_pre_close()
8670 // at this point, we should shut down any file xfers...
8671 if(Net_player->s_info.xfer_handle != -1){
8672 nprintf(("Network","WARNING - killing file xfer while leaving mission sync state!!!\n"));
8674 multi_xfer_abort(Net_player->s_info.xfer_handle);
8675 Net_player->s_info.xfer_handle = -1;
8679 void multi_sync_post_init()
8681 multi_reset_timestamps();
8683 Multi_state_timestamp = timestamp(0);
8686 ml_string(NOX("Performing post-briefing data sync"));
8688 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8689 multi_common_add_text(XSTR("Server performing sync\n",830),1);
8691 multi_common_add_text(XSTR("Client performing sync\n",838),1);
8694 // everyone should re-initialize these
8695 init_multiplayer_stats();
8697 // reset all sequencing info
8698 multi_oo_reset_sequencing();
8700 // if I am not the master of the game, then send the firing information for my ship
8702 if ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) ){
8703 send_firing_info_packet();
8706 // if I'm not a standalone server, load up the countdown stuff
8707 if(!(Game_mode & GM_STANDALONE_SERVER)){
8708 Multi_sync_countdown_anim = NULL;
8709 Multi_sync_countdown_instance = NULL;
8710 Multi_sync_countdown_anim = anim_load(Multi_sync_countdown_fname[gr_screen.res]);
8711 if(Multi_sync_countdown_anim == NULL){
8712 nprintf(("General","WARNING!, Could not load countdown animation %s!\n",Multi_sync_countdown_fname[gr_screen.res]));
8716 // create objects for all permanent observers
8717 multi_obs_level_init();
8719 // clear the game start countdown timer
8720 Multi_sync_countdown_timer = -1.0f;
8721 Multi_sync_countdown = -1;
8723 // if this is a team vs. team mission, mark all ship teams appropriately
8724 if(Netgame.type_flags & NG_TYPE_TEAM){
8725 multi_team_mark_all_ships();
8728 Mission_sync_flags = 0;
8729 Multi_sync_launch_pressed = 0;
8732 #define MULTI_POST_TIMESTAMP 7000
8734 extern int create_wings();
8736 void multi_sync_post_do()
8740 // only if the host is also the master should he be doing this (non-standalone situation)
8741 if ( Net_player->flags & NETINFO_FLAG_AM_MASTER ) {
8743 // once everyone gets to this screen, send them the ship classes of all ships.
8744 if(multi_netplayer_state_check(NETPLAYER_STATE_WAITING) && !(Mission_sync_flags & MS_FLAG_POST_DATA)) {
8745 // only the host should ever do this
8746 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8747 // at this point we want to delete all necessary ships, change all necessary ship classes, and set all weapons up
8748 multi_ts_create_wings();
8750 // update player ets settings
8751 for(idx=0;idx<MAX_PLAYERS;idx++){
8752 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].player->objnum != -1)){
8753 multi_server_update_player_weapons(&Net_players[idx],&Ships[Objects[Net_players[idx].player->objnum].instance]);
8758 // note that this is done a little differently for standalones and nonstandalones
8759 send_post_sync_data_packet();
8761 multi_common_add_text(XSTR("Sending post briefing block information\n",839),1);
8763 Mission_sync_flags |= MS_FLAG_POST_DATA;
8766 // send weapon slots data
8767 if(multi_netplayer_state_check(NETPLAYER_STATE_POST_DATA_ACK) && !(Mission_sync_flags & MS_FLAG_WSS_SLOTS)) {
8768 // note that this is done a little differently for standalones and nonstandalones
8769 if(Netgame.type_flags & NG_TYPE_TEAM){
8770 send_wss_slots_data_packet(0,0);
8771 send_wss_slots_data_packet(1,1);
8773 send_wss_slots_data_packet(0,1);
8776 multi_common_add_text(XSTR("Sending weapon slots information\n",840),1);
8778 Mission_sync_flags |= MS_FLAG_WSS_SLOTS;
8781 // once weapon information is received, send player settings info
8782 if ( multi_netplayer_state_check(NETPLAYER_STATE_WSS_ACK) && !(Mission_sync_flags & MS_FLAG_PSETTINGS)) {
8783 send_player_settings_packet();
8785 // server (specifically, the standalone), should set this here
8786 Net_player->state = NETPLAYER_STATE_SETTINGS_ACK;
8787 send_netplayer_update_packet();
8789 multi_common_add_text(XSTR("Sending player settings packets\n",841),1);
8791 Mission_sync_flags |= MS_FLAG_PSETTINGS;
8794 // check to see if the countdown timer has started and act appropriately
8795 if( Multi_sync_countdown_timer > -1.0f ) {
8797 // increment by frametime.
8798 Multi_sync_countdown_timer += flFrametime;
8800 // if the animation is not playing, start it
8801 if(!(Game_mode & GM_STANDALONE_SERVER) && (Multi_sync_countdown_instance == NULL) && (Multi_sync_countdown_anim != NULL)){
8802 anim_play_struct aps;
8804 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]);
8805 aps.screen_id = GS_STATE_MULTI_MISSION_SYNC;
8806 aps.framerate_independent = 1;
8808 Multi_sync_countdown_instance = anim_play(&aps);
8811 // if the next second has expired
8812 if( Multi_sync_countdown_timer >= 1.0f ) {
8814 Multi_sync_countdown--;
8815 Multi_sync_countdown_timer = 0.0f;
8817 // if the countdown has reached 0, launch the mission
8818 if(Multi_sync_countdown == 0){
8819 Multi_sync_countdown_timer = -1.0f;
8821 Multi_sync_launch_pressed = 0;
8822 multi_sync_launch();
8824 // otherwise send a countdown packet
8826 send_countdown_packet(Multi_sync_countdown);
8831 // jump into the mission myself
8832 if((Multi_sync_countdown == 0) && multi_netplayer_state_check(NETPLAYER_STATE_IN_MISSION)){
8833 if(!((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !Multi_sync_launch_pressed)){
8834 Net_player->state = NETPLAYER_STATE_IN_MISSION;
8835 Netgame.game_state = NETGAME_STATE_IN_MISSION;
8836 gameseq_post_event(GS_EVENT_ENTER_GAME);
8838 multi_common_add_text(XSTR("Moving into game\n",842),1);
8842 // maybe start the animation countdown
8843 if((Multi_sync_countdown >= 0) && (Multi_sync_countdown_instance == NULL) && (Multi_sync_countdown_anim != NULL)){
8844 anim_play_struct aps;
8846 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]);
8847 aps.screen_id = GS_STATE_MULTI_MISSION_SYNC;
8848 aps.framerate_independent = 1;
8850 Multi_sync_countdown_instance = anim_play(&aps);
8854 // host - specific stuff
8855 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8856 // create the launch button so the host can click
8857 if( Sync_test && multi_netplayer_state_check(NETPLAYER_STATE_SETTINGS_ACK) ){
8858 multi_sync_create_launch_button();
8863 if(!(Game_mode & GM_STANDALONE_SERVER)){
8865 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8866 if(Multi_sync_bitmap != -1){
8867 gr_set_bitmap(Multi_sync_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8870 Multi_sync_window.draw();
8872 multi_sync_blit_screen_all();
8878 void multi_sync_post_close()
8882 // if I'm not a standalone server, unload up the countdown stuff
8883 if(!(Game_mode & GM_STANDALONE_SERVER)){
8884 // release all rendering animation instances (should only be 1)
8885 anim_release_all_instances(GS_STATE_MULTI_MISSION_SYNC);
8886 Multi_sync_countdown_instance = NULL;
8888 // free up the countdown animation
8889 if(Multi_sync_countdown_anim != NULL){
8890 anim_free(Multi_sync_countdown_anim);
8891 Multi_sync_countdown_anim = NULL;
8895 // all players should reset sequencing
8896 for(idx=0;idx<MAX_PLAYERS;idx++){
8897 if(Net_player->flags & NETINFO_FLAG_CONNECTED){
8898 Net_players[idx].client_cinfo_seq = 0;
8899 Net_players[idx].client_server_seq = 0;
8903 // multiplayer dogfight
8904 multi_df_level_pre_enter();
8906 // clients should clear obj_pair array and add pair for themselves
8908 if ( MULTIPLAYER_CLIENT ) {
8910 obj_add_pairs( OBJ_INDEX(Player_obj) );
8915 void multi_sync_display_name(const char *name, int index, int np_index)
8917 char fit[CALLSIGN_LEN];
8919 // make sure the string actually fits
8922 // if we're in team vs. team mode
8923 if(Netgame.type_flags & NG_TYPE_TEAM){
8924 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]);
8926 // if this is the currently selected player, draw him highlighted
8927 if(np_index == Multi_sync_player_select){
8928 gr_set_color_fast(&Color_text_selected);
8930 gr_set_color_fast(&Color_text_normal);
8934 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);
8936 // blit his team icon
8938 if(Net_players[np_index].p_info.team == 0){
8939 // blit the team captain icon
8940 if(Net_players[np_index].flags & NETINFO_FLAG_TEAM_CAPTAIN){
8941 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
8942 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8943 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);
8946 // normal team member icon
8948 if(Multi_common_icons[MICON_TEAM0] != -1){
8949 gr_set_bitmap(Multi_common_icons[MICON_TEAM0], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8950 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);
8955 else if(Net_players[np_index].p_info.team == 1){
8956 // blit the team captain icon
8957 if(Net_players[np_index].flags & NETINFO_FLAG_TEAM_CAPTAIN){
8958 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
8959 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8960 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);
8963 // normal team member icon
8965 if(Multi_common_icons[MICON_TEAM1] != -1){
8966 gr_set_bitmap(Multi_common_icons[MICON_TEAM1], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8967 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);
8972 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]);
8974 // if this is the currently selected player, draw him highlighted
8975 if(np_index == Multi_sync_player_select){
8976 gr_set_color_fast(&Color_text_selected);
8978 gr_set_color_fast(&Color_text_normal);
8982 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);
8985 // maybe blit his CD status icon
8986 if((Net_players[np_index].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
8987 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8988 gr_bitmap(Ms_status_coords[gr_screen.res][MS_X_COORD], Ms_status_coords[gr_screen.res][MS_Y_COORD] + (index * 10));
8992 void multi_sync_display_status(const char *status, int index)
8996 // make sure the string actually fits
8997 strcpy(fit, status);
8998 gr_force_fit_string(fit, 250, Ms_status2_coords[gr_screen.res][MS_W_COORD] - 20);
8999 gr_set_color_fast(&Color_bright);
9000 gr_string(Ms_status2_coords[gr_screen.res][MS_X_COORD], Ms_status2_coords[gr_screen.res][MS_Y_COORD] + (index * 10), fit);
9003 void multi_sync_force_start_pre()
9006 int want_state = NETPLAYER_STATE_SLOT_ACK; // kick any players who are still in this state
9008 // go through the player list and boot anyone who isn't in the right state
9009 for(idx=0;idx<MAX_PLAYERS;idx++){
9010 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx]) && (Net_players[idx].state == want_state)){
9011 multi_kick_player(idx,0);
9016 void multi_sync_force_start_post()
9020 int num_kill_states;
9022 // determine the state we want all players in so that we can find those who are not in the state
9023 kill_state[0] = NETPLAYER_STATE_BRIEFING;
9024 kill_state[1] = NETPLAYER_STATE_SHIP_SELECT;
9025 kill_state[2] = NETPLAYER_STATE_WEAPON_SELECT;
9026 num_kill_states = 3;
9028 // go through the player list and boot anyone who isn't in the right state
9029 for(idx=0;idx<MAX_PLAYERS;idx++){
9030 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx])){
9031 // check against all kill state
9032 for(idx2 = 0;idx2<num_kill_states;idx2++){
9033 if(Net_players[idx].state == kill_state[idx2]){
9034 multi_kick_player(idx,0);
9042 void multi_sync_start_countdown()
9044 // don't allow repeat button presses
9045 if(Multi_sync_launch_pressed){
9049 Multi_sync_launch_pressed = 1;
9051 // if I'm the server, begin the countdown
9052 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
9053 gamesnd_play_iface(SND_COMMIT_PRESSED);
9054 Multi_sync_countdown_timer = 0.0f;
9055 Multi_sync_countdown = MULTI_SYNC_COUNTDOWN_TIME;
9057 // send an initial countdown value
9058 send_countdown_packet(Multi_sync_countdown);
9060 // otherwise send the "start countdown" packet to the standalone
9062 SDL_assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
9063 send_countdown_packet(-1);
9067 void multi_sync_launch()
9069 // don't allow repeat button presses
9070 if(Multi_sync_launch_pressed){
9074 Multi_sync_launch_pressed = 1;
9077 ml_printf(NOX("Entering mission %s"), Game_current_mission_filename);
9079 // tell everyone to jump into the mission
9080 send_jump_into_mission_packet();
9081 Multi_state_timestamp = timestamp(MULTI_POST_TIMESTAMP);
9083 // set the # of players at the start of the mission
9084 Multi_num_players_at_start = multi_num_players();
9085 nprintf(("Network","# of players at start of mission : %d\n", Multi_num_players_at_start));
9087 // initialize datarate limiting for all clients
9088 multi_oo_rate_init_all();
9090 multi_common_add_text(XSTR("Sending mission start packet\n",843),1);
9093 void multi_sync_create_launch_button()
9095 if (!Multi_launch_button_created) {
9096 // create the object
9097 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);
9099 // set the sound to play when highlighted
9100 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_highlight_action(common_play_highlight_sound);
9102 // set the ani for the button
9103 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_bmaps(Multi_sync_buttons[gr_screen.res][MS_LAUNCH].filename, 3, 0);
9106 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.link_hotspot(Multi_sync_buttons[gr_screen.res][MS_LAUNCH].hotspot);
9109 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_hotkey(KEY_CTRLED+SDLK_RETURN);
9112 // create the text for the button
9113 Multi_sync_window.add_XSTR(&Multi_sync_text[gr_screen.res][MST_LAUNCH]);
9116 // increment the button count so we start checking this one
9117 Multi_sync_button_count++;
9119 Multi_launch_button_created = 1;
9123 void multi_sync_handle_plist()
9129 // if we don't have a currently selected player, select one
9130 if((Multi_sync_player_select < 0) || !MULTI_CONNECTED(Net_players[Multi_sync_player_select])){
9131 for(idx=0;idx<MAX_PLAYERS;idx++){
9132 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
9133 Multi_sync_player_select = idx;
9139 // check for button list presses
9140 if(Multi_sync_plist_button.pressed()){
9141 // get the y mouse coords
9142 Multi_sync_plist_button.get_mouse_pos(NULL,&my);
9144 // get the index of the item selected
9145 select_index = my / 10;
9147 // if the index is greater than the current # connections, do nothing
9148 if(select_index > (multi_num_connections() - 1)){
9152 // translate into an absolute Net_players[] index (get the Nth net player)
9153 Multi_sync_player_select = -1;
9154 for(idx=0;idx<MAX_PLAYERS;idx++){
9155 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
9159 // if we've found the item we're looking for
9160 if(select_index < 0){
9161 Multi_sync_player_select = idx;
9166 // if for some bizarre reason, this is an invalid player, unselect him and wait for the next interation
9168 if((Multi_sync_player_select >= 0) && (!MULTI_CONNECTED(Net_players[Multi_sync_player_select]) || MULTI_STANDALONE(Net_players[Multi_sync_player_select])) ){
9169 Multi_sync_player_select = -1;
9175 // -------------------------------------------------------------------------------------------------------------
9177 // MULTIPLAYER DEBRIEF SCREEN
9180 // other relevant data
9181 int Multi_debrief_accept_hit;
9182 int Multi_debrief_replay_hit;
9184 // set if the server has left the game
9185 int Multi_debrief_server_left = 0;
9187 // if we've reported on TvT status all players are in the debrief
9188 int Multi_debrief_reported_tvt = 0;
9190 // whether stats are being accepted
9191 // -1 == no decision yet
9194 int Multi_debrief_stats_accept_code = -1;
9196 int Multi_debrief_server_framecount = 0;
9198 float Multi_debrief_time = 0.0f;
9199 float Multi_debrief_resend_time = 10.0f;
9201 void multi_debrief_init()
9205 Multi_debrief_time = 0.0f;
9206 Multi_debrief_resend_time = 10.0f;
9208 // do this to notify the standalone or the normal server that we're in the debrief state and ready to receive packets
9209 if (!(Net_player->flags & NETINFO_FLAG_AM_MASTER)) {
9210 Net_player->state = NETPLAYER_STATE_DEBRIEF;
9211 send_netplayer_update_packet();
9214 // unflag some stuff
9215 for(idx=0;idx<MAX_PLAYERS;idx++){
9216 if(MULTI_CONNECTED(Net_players[idx])){
9217 Net_players[idx].flags &= ~(NETINFO_FLAG_RESPAWNING | NETINFO_FLAG_LIMBO | NETINFO_FLAG_WARPING_OUT);
9221 // if text input mode is active, clear it
9222 multi_msg_text_flush();
9224 // the server has not left yet
9225 Multi_debrief_server_left = 0;
9227 // have not hit accept or replay yet
9228 Multi_debrief_accept_hit = 0;
9229 Multi_debrief_replay_hit = 0;
9231 // stats have not been accepted yet
9232 Multi_debrief_stats_accept_code = -1;
9234 // mark stats as not being store yet
9235 Netgame.flags &= ~(NG_FLAG_STORED_MT_STATS);
9237 // no report on TvT yet
9238 Multi_debrief_reported_tvt = 0;
9240 Multi_debrief_server_framecount = 0;
9243 void multi_debrief_do_frame()
9245 Multi_debrief_time += flFrametime;
9247 // set the netgame state to be debriefing when appropriate
9248 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)){
9249 Netgame.game_state = NETGAME_STATE_DEBRIEF;
9250 send_netgame_update_packet();
9253 // evaluate all server stuff
9254 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
9255 multi_debrief_server_process();
9259 void multi_debrief_close()
9261 if ( MULTIPLAYER_CLIENT && (Netgame.game_state == NETGAME_STATE_MISSION_SYNC) ){
9262 gamesnd_play_iface( SND_COMMIT_PRESSED );
9266 // handle optional mission loop
9267 void multi_maybe_set_mission_loop()
9269 int cur = Campaign.current_mission;
9270 if (Campaign.missions[cur].has_mission_loop) {
9271 SDL_assert(Campaign.loop_mission != CAMPAIGN_LOOP_MISSION_UNINITIALIZED);
9273 bool require_repeat_mission = (Campaign.current_mission == Campaign.next_mission);
9275 // check for (1) mission loop available, (2) dont have to repeat last mission
9276 if ( (Campaign.missions[cur].has_mission_loop && (Campaign.loop_mission != -1)) && !require_repeat_mission ) {
9279 debrief_assemble_optional_mission_popup_text(buffer, Campaign.missions[cur].mission_loop_desc);
9281 int choice = popup(0 , 2, POPUP_NO, POPUP_YES, buffer);
9283 Campaign.loop_enabled = 1;
9284 Campaign.next_mission = Campaign.loop_mission;
9289 // handle all cases for when the accept key is hit in a multiplayer debriefing
9290 void multi_debrief_accept_hit()
9292 // if we already accepted, do nothing
9293 // but he may need to hit accept again after the server has left the game, so allow this
9294 if(Multi_debrief_accept_hit){
9298 // mark this so that we don't hit it again
9299 Multi_debrief_accept_hit = 1;
9301 gamesnd_play_iface(SND_COMMIT_PRESSED);
9303 // if the server has left the game, always just end the game.
9304 if(Multi_debrief_server_left){
9305 if(!multi_quit_game(PROMPT_ALL)){
9306 Multi_debrief_server_left = 1;
9307 Multi_debrief_accept_hit = 0;
9309 Multi_debrief_server_left = 0;
9312 // query the host and see if he wants to accept stats
9313 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
9314 // if we're on a tracker game, he gets no choice for storing stats
9315 if(MULTI_IS_TRACKER_GAME){
9316 multi_maybe_set_mission_loop();
9318 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));
9320 // evaluate the result
9325 Multi_debrief_accept_hit = 0;
9328 // set the accept code to be "not accepting"
9330 multi_debrief_stats_toss();
9331 multi_maybe_set_mission_loop();
9334 // accept the stats and continue
9336 multi_debrief_stats_accept();
9337 multi_maybe_set_mission_loop();
9343 // set my netplayer state to be "debrief_accept", and be done with it
9344 Net_player->state = NETPLAYER_STATE_DEBRIEF_ACCEPT;
9345 send_netplayer_update_packet();
9349 // handle all cases for when the escape key is hit in a multiplayer debriefing
9350 void multi_debrief_esc_hit()
9354 // if the server has left
9355 if(Multi_debrief_server_left){
9356 multi_quit_game(PROMPT_ALL);
9361 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
9362 // if the stats have already been accepted
9363 if((Multi_debrief_stats_accept_code != -1) || (MULTI_IS_TRACKER_GAME)){
9364 multi_quit_game(PROMPT_HOST);
9366 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));
9368 // evaluate the result
9375 // set the accept code to be "not accepting"
9377 multi_debrief_stats_toss();
9378 multi_quit_game(PROMPT_NONE);
9381 // accept the stats and continue
9383 multi_debrief_stats_accept();
9384 multi_quit_game(PROMPT_NONE);
9389 // if the stats haven't been accepted yet, or this is a tracker game
9390 if((Multi_debrief_stats_accept_code == -1) && !(MULTI_IS_TRACKER_GAME)){
9391 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));
9393 // evaluate the result
9395 multi_quit_game(PROMPT_NONE);
9398 // otherwise go through the normal endgame channels
9400 multi_quit_game(PROMPT_ALL);
9405 void multi_debrief_replay_hit()
9407 // only the host should ever get here
9408 SDL_assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
9410 // if the button was already pressed, do nothing
9411 if(Multi_debrief_accept_hit){
9415 // same as hittin the except button except no stats are kept
9416 Multi_debrief_accept_hit = 1;
9418 // mark myself as being in the replay state so we know what to do next
9419 Net_player->state = NETPLAYER_STATE_DEBRIEF_REPLAY;
9420 send_netplayer_update_packet();
9423 // call this when the server has left and we would otherwise be saying "contact lost with server
9424 void multi_debrief_server_left()
9427 Multi_debrief_server_left = 1;
9429 // undo any "accept" hit so that clients can hit accept again to leave
9430 Multi_debrief_accept_hit = 0;
9433 void multi_debrief_stats_accept()
9435 // don't do anything if we've already accepted
9436 if(Multi_debrief_stats_accept_code != -1){
9440 Multi_debrief_stats_accept_code = 1;
9442 // if we're the host, and we're on a standalone, tell the standalone to begin the stats storing process
9443 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) || (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
9444 // send a packet to the players telling them to store their stats
9445 send_store_stats_packet(1);
9448 // add a chat line saying "stats have been accepted"
9449 multi_display_chat_msg(XSTR("<stats have been accepted>",850),0,0);
9452 ml_string(NOX("Stats stored"));
9455 void multi_debrief_stats_toss()
9457 // don't do anything if we've already accepted
9458 if(Multi_debrief_stats_accept_code != -1){
9462 Multi_debrief_stats_accept_code = 0;
9464 // if we're the host, and we're on a standalone, tell everyone to "toss" stats
9465 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) || (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
9466 // send a packet to the players telling them to store their stats
9467 send_store_stats_packet(0);
9470 // add a chat line saying "stats have been accepted"
9471 multi_display_chat_msg(XSTR("<stats have been tossed>",851),0,0);
9474 ml_string(NOX("Stats tossed"));
9477 int multi_debrief_stats_accept_code()
9479 return Multi_debrief_stats_accept_code;
9482 void multi_debrief_server_process()
9485 int player_status,other_status;
9487 Multi_debrief_server_framecount++;
9489 // if we're > 10 seconds into the debrief and not everyone is here, try warping everyone out again
9490 if((Multi_debrief_time >= Multi_debrief_resend_time) && !multi_netplayer_state_check3(NETPLAYER_STATE_DEBRIEF, NETPLAYER_STATE_DEBRIEF_ACCEPT, NETPLAYER_STATE_DEBRIEF_REPLAY, 1)){
9491 // find all players who are not in the debrief state and hit them with the endgame packet
9492 for(idx=0; idx<MAX_PLAYERS; idx++){
9493 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)) ){
9494 send_endgame_packet(&Net_players[idx]);
9499 Multi_debrief_resend_time += 7.0f;
9502 // evaluate the status of all players in the game (0 == not ready, 1 == ready to continue, 2 == ready to replay)
9505 // check all players
9506 for(idx=0;idx<MAX_PLAYERS;idx++){
9507 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_HOST(Net_players[idx])){
9508 if(Net_players[idx].state != NETPLAYER_STATE_DEBRIEF_ACCEPT){
9515 // if we haven't already reported TvT results
9516 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)){
9517 multi_team_report();
9518 Multi_debrief_reported_tvt = 1;
9521 // if all other players are good to go, check the host
9523 // if he is ready to continue
9524 if(Netgame.host->state == NETPLAYER_STATE_DEBRIEF_ACCEPT){
9527 // if he wants to replay the mission
9528 else if(Netgame.host->state == NETPLAYER_STATE_DEBRIEF_REPLAY){
9531 // if he is not ready
9536 // if all players are _not_ good to go
9541 // if we're in the debriefing state in a campaign mode, process accordingly
9542 if(Netgame.campaign_mode == MP_CAMPAIGN){
9543 multi_campaign_do_debrief(player_status);
9545 // otherwise process as normal (looking for all players to be ready to go to the next mission
9547 if(player_status == 1){
9548 multi_flush_mission_stuff();
9550 // set the netgame state to be forming and continue
9551 Netgame.game_state = NETGAME_STATE_FORMING;
9552 send_netgame_update_packet();
9554 // move to the proper state
9555 if(Game_mode & GM_STANDALONE_SERVER){
9556 gameseq_post_event(GS_EVENT_STANDALONE_MAIN);
9558 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
9561 multi_reset_timestamps();
9562 } else if(player_status == 2){
9563 multi_flush_mission_stuff();
9565 // tell everyone to move into the pre-briefing sync state
9566 Netgame.game_state = NETGAME_STATE_MISSION_SYNC;
9567 send_netgame_update_packet();
9569 // move back to the mission sync screen for the same mission again
9570 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
9571 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
9573 multi_reset_timestamps();
9579 // -------------------------------------------------------------------------------------------------------------
9581 // MULTIPLAYER PASSWORD POPUP
9586 static const char *Multi_pwd_bitmap_fname[GR_NUM_RESOLUTIONS] = {
9587 "Password", // GR_640
9588 "2_Password" // GR_1024
9591 static const char *Multi_pwd_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
9592 "Password-M", // GR_640
9593 "2_Password-M" // GR_1024
9598 // constants for coordinate lookup
9599 #define MPWD_X_COORD 0
9600 #define MPWD_Y_COORD 1
9601 #define MPWD_W_COORD 2
9602 #define MPWD_H_COORD 3
9605 #define MULTI_PWD_NUM_BUTTONS 2
9606 #define MPWD_CANCEL 0
9607 #define MPWD_COMMIT 1
9609 // password area defs
9610 int Mpwd_coords[GR_NUM_RESOLUTIONS][4] = {
9623 UI_WINDOW Multi_pwd_window; // the window object for the join screen
9624 UI_INPUTBOX Multi_pwd_passwd; // for Netgame.passwd
9625 int Multi_pwd_bitmap; // the background bitmap
9626 int Multi_passwd_background = -1;
9627 int Multi_passwd_done = -1;
9628 int Multi_passwd_running = 0;
9631 ui_button_info Multi_pwd_buttons[GR_NUM_RESOLUTIONS][MULTI_PWD_NUM_BUTTONS] = {
9634 ui_button_info("PWB_00", 402, 134, -1, -1, 0),
9635 ui_button_info("PWB_01", 450, 134, -1, -1, 1),
9637 ui_button_info("PWB_00", 411, 151, 405, 141, 0),
9638 ui_button_info("PWB_01", 460, 151, 465, 141, 1),
9642 ui_button_info("2_PWB_00", 659, 242, 649, 225, 0),
9643 ui_button_info("2_PWB_01", 737, 242, 736, 225, 1),
9649 #define MULTI_PWD_NUM_TEXT 3
9651 UI_XSTR Multi_pwd_text[GR_NUM_RESOLUTIONS][MULTI_PWD_NUM_TEXT] = {
9653 { "Cancel", 387, 400, 141, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[0][MPWD_CANCEL].button},
9654 { "Commit", 1062, 455, 141, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[0][MPWD_COMMIT].button},
9655 { "Enter Password", 1332, 149, 92, UI_XSTR_COLOR_GREEN, -1, NULL},
9658 { "Cancel", 387, 649, 225, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[1][MPWD_CANCEL].button},
9659 { "Commit", 1062, 736, 225, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[1][MPWD_COMMIT].button},
9660 { "Enter Password", 1332, 239, 148, UI_XSTR_COLOR_GREEN, -1, NULL},
9665 // initialize all graphics, etc
9666 void multi_passwd_init()
9670 // store the background as it currently is
9671 Multi_passwd_background = gr_save_screen();
9673 // create the interface window
9674 Multi_pwd_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
9675 Multi_pwd_window.set_mask_bmap(Multi_pwd_bitmap_mask_fname[gr_screen.res]);
9677 // load the background bitmap
9678 Multi_pwd_bitmap = bm_load(Multi_pwd_bitmap_fname[gr_screen.res]);
9679 if(Multi_pwd_bitmap < 0){
9680 // we failed to load the bitmap - this is very bad
9684 // create the interface buttons
9685 for(idx=0; idx<MULTI_PWD_NUM_BUTTONS; idx++){
9686 // create the object
9687 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);
9689 // set the sound to play when highlighted
9690 Multi_pwd_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
9692 // set the ani for the button
9693 Multi_pwd_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pwd_buttons[gr_screen.res][idx].filename);
9696 Multi_pwd_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pwd_buttons[gr_screen.res][idx].hotspot);
9701 for(idx=0; idx<MULTI_PWD_NUM_TEXT; idx++){
9702 Multi_pwd_window.add_XSTR(&Multi_pwd_text[gr_screen.res][idx]);
9706 // create the password input box
9707 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);
9708 Multi_pwd_passwd.set_focus();
9710 // link the enter key to ACCEPT
9711 Multi_pwd_buttons[gr_screen.res][MPWD_COMMIT].button.set_hotkey(SDLK_RETURN);
9713 Multi_passwd_done = -1;
9714 Multi_passwd_running = 1;
9717 // close down all graphics, etc
9718 void multi_passwd_close()
9720 // unload any bitmaps
9721 bm_release(Multi_pwd_bitmap);
9723 // destroy the UI_WINDOW
9724 Multi_pwd_window.destroy();
9726 // free up the saved background screen
9727 if(Multi_passwd_background >= 0){
9728 gr_free_screen(Multi_passwd_background);
9729 Multi_passwd_background = -1;
9732 Multi_passwd_running = 0;
9735 // process any button pressed
9736 void multi_passwd_process_buttons()
9738 // if the accept button was pressed
9739 if(Multi_pwd_buttons[gr_screen.res][MPWD_COMMIT].button.pressed()){
9740 gamesnd_play_iface(SND_USER_SELECT);
9741 Multi_passwd_done = 1;
9744 // if the cancel button was pressed
9745 if(Multi_pwd_buttons[gr_screen.res][MPWD_CANCEL].button.pressed()){
9746 gamesnd_play_iface(SND_USER_SELECT);
9747 Multi_passwd_done = 0;
9751 // run the passwd popup
9752 void multi_passwd_do(char *passwd)
9756 while(Multi_passwd_done == -1){
9757 // set frametime and run background stuff
9758 game_set_frametime(-1);
9759 game_do_state_common(gameseq_get_state());
9761 k = Multi_pwd_window.process();
9763 // process any keypresses
9766 // set this to indicate the user has cancelled for one reason or another
9767 Multi_passwd_done = 0;
9771 // if the input box text has changed
9772 if(Multi_pwd_passwd.changed()){
9774 Multi_pwd_passwd.get_text(passwd);
9777 // process any button pressed
9778 multi_passwd_process_buttons();
9780 // draw the background, etc
9783 if(Multi_passwd_background >= 0){
9784 gr_restore_screen(Multi_passwd_background);
9786 gr_set_bitmap(Multi_pwd_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
9788 Multi_pwd_window.draw();
9795 // bring up the password string popup, fill in passwd (return 1 if accept was pressed, 0 if cancel was pressed)
9796 int multi_passwd_popup(char *passwd)
9798 // if the popup is already running for some reason, don't do anything
9799 if(Multi_passwd_running){
9803 // initialize all graphics
9804 multi_passwd_init();
9807 multi_passwd_do(passwd);
9809 // shut everything down
9810 multi_passwd_close();
9812 return Multi_passwd_done;