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.10 2004/09/20 01:31:44 theoddone33
21 * Revision 1.9 2004/07/04 11:39:06 taylor
22 * fix missing debrief text, crash on exit, path separator's, warning fixes, no GR_SOFT
24 * Revision 1.8 2003/05/25 02:30:43 taylor
27 * Revision 1.7 2002/06/09 04:41:24 relnev
28 * added copyright header
30 * Revision 1.6 2002/06/02 06:02:59 relnev
33 * Revision 1.5 2002/06/02 00:31:35 relnev
34 * implemented osregistry
36 * Revision 1.4 2002/06/01 07:12:33 relnev
37 * a few NDEBUG updates.
39 * removed a few warnings.
41 * Revision 1.3 2002/05/26 20:49:54 theoddone33
44 * Revision 1.2 2002/05/07 03:16:47 theoddone33
45 * The Great Newline Fix
47 * Revision 1.1.1.1 2002/05/03 03:28:10 root
51 * 94 6/16/00 3:16p Jefff
52 * sim of the year dvd version changes, a few german soty localization
55 * 93 10/14/99 2:51p Jefff
58 * 92 10/13/99 3:50p Jefff
59 * fixed unnumbered XSTRs
61 * 91 9/15/99 1:45a Dave
62 * Don't init joystick on standalone. Fixed campaign mode on standalone.
63 * Fixed no-score-report problem in TvT
65 * 90 9/14/99 12:51a Jefff
68 * 89 9/13/99 4:52p Dave
71 * 88 9/13/99 11:30a Dave
72 * Added checkboxes and functionality for disabling PXO banners as well as
73 * disabling d3d zbuffer biasing.
75 * 87 9/12/99 10:06p Jefff
76 * changed instances of "Squad War" to "SquadWar"
78 * 86 9/03/99 1:32a Dave
79 * CD checking by act. Added support to play 2 cutscenes in a row
80 * seamlessly. Fixed super low level cfile bug related to files in the
81 * root directory of a CD. Added cheat code to set campaign mission # in
84 * 85 9/01/99 10:49p Dave
85 * Added nice SquadWar checkbox to the client join wait screen.
87 * 84 8/30/99 2:49p Jefff
89 * 83 8/26/99 8:49p Jefff
90 * Updated medals screen and about everything that ever touches medals in
91 * one way or another. Sheesh.
93 * 82 8/25/99 4:38p Dave
94 * Updated PXO stuff. Make squad war report stuff much more nicely.
96 * 81 8/20/99 2:09p Dave
99 * 80 8/20/99 10:06a Jefff
100 * removed closed/rstricted buttons from multi start screen
102 * 79 8/18/99 11:30a Jefff
104 * 78 8/18/99 10:38a Jefff
106 * 77 8/16/99 4:06p Dave
107 * Big honking checkin.
109 * 76 8/16/99 1:08p Jefff
110 * added sounds to a few controls, made input boxes lose focus on ENTER
112 * 75 8/16/99 9:52a Jefff
113 * fixed bitmap loading on buttons in multi-sync screen
115 * 74 8/11/99 5:54p Dave
116 * Fixed collision problem. Fixed standalone ghost problem.
118 * 73 8/10/99 4:35p Jefff
119 * fixed hi-res coords
121 * 72 8/06/99 12:29a Dave
122 * Multiple bug fixes.
124 * 71 8/05/99 3:13p Jasenw
125 * tweaked some text placement coords.
127 * 70 8/04/99 1:38p Jefff
128 * moved some text in multi join wait
130 * 69 8/03/99 12:45p Dave
133 * 68 7/25/99 5:17p Jefff
134 * campaign descriptions show up on multicreate screen
136 * 67 7/20/99 1:49p Dave
137 * Peter Drake build. Fixed some release build warnings.
139 * 66 7/19/99 2:13p Dave
140 * Added some new strings for Heiko.
142 * 65 7/15/99 9:20a Andsager
143 * FS2_DEMO initial checkin
145 * 64 7/08/99 10:53a Dave
146 * New multiplayer interpolation scheme. Not 100% done yet, but still
147 * better than the old way.
149 * 63 6/30/99 10:49a Jasenw
150 * Fixed coords for new launch countdown ani
152 * 62 6/29/99 7:39p Dave
153 * Lots of small bug fixes.
155 * 61 6/25/99 11:59a Dave
156 * Multi options screen.
158 * 60 6/09/99 2:17p Dave
159 * Fixed up pleasewait bitmap rendering.
161 * 59 6/05/99 3:42p Dave
162 * New multi sync screen.
164 * 58 6/01/99 6:07p Dave
165 * New loading/pause/please wait bar.
167 * 57 5/21/99 6:45p Dave
168 * Sped up ui loading a bit. Sped up localization disk access stuff. Multi
169 * start game screen, multi password, and multi pxo-help screen.
171 * 56 5/06/99 11:10a Dave
172 * Fixed coord on multi create screen.
174 * 55 5/04/99 6:38p Dave
175 * Finished multi join-wait screen.
177 * 54 5/04/99 5:20p Dave
178 * Fixed up multiplayer join screen and host options screen. Should both
181 * 53 5/03/99 11:04p Dave
182 * Most of the way done with the multi join screen.
184 * 52 5/03/99 8:32p Dave
185 * New version of multi host options screen.
187 * 51 4/29/99 2:15p Neilk
188 * slider2 code got modified; changed parameters for create
190 * 50 4/25/99 3:02p Dave
191 * Build defines for the E3 build.
193 * 49 4/21/99 6:15p Dave
194 * Did some serious housecleaning in the beam code. Made it ready to go
195 * for anti-fighter "pulse" weapons. Fixed collision pair creation. Added
196 * a handy macro for recalculating collision pairs for a given object.
198 * 48 4/16/99 5:27p Neilk
199 * added slider support and hir res for multi_create
201 * 47 4/14/99 6:37p Dave
202 * Fixed scroll button bug on host create screen.
204 * 46 4/14/99 5:28p Dave
207 * 45 4/12/99 10:07p Dave
208 * Made network startup more forgiving. Added checkmarks to dogfight
209 * screen for players who hit commit.
211 * 44 4/09/99 2:21p Dave
212 * Multiplayer beta stuff. CD checking.
214 * 43 4/08/99 1:28p Dave
215 * Small bug fixes for refresh button on the multi create screen.
217 * 42 4/08/99 11:55a Neilk
218 * Converted Multi_Create to new artwork (just lowres)
220 * 41 4/08/99 2:10a Dave
221 * Numerous bug fixes for the beta. Added builtin mission info for the
224 * 40 3/20/99 3:48p Andsager
225 * Do mission_loop stuff for PXO
227 * 39 3/10/99 6:50p Dave
228 * Changed the way we buffer packets for all clients. Optimized turret
229 * fired packets. Did some weapon firing optimizations.
231 * 38 3/09/99 6:24p Dave
232 * More work on object update revamping. Identified several sources of
233 * unnecessary bandwidth.
235 * 37 3/08/99 7:03p Dave
236 * First run of new object update system. Looks very promising.
238 * 36 2/25/99 4:19p Dave
239 * Added multiplayer_beta defines. Added cd_check define. Fixed a few
240 * release build warnings. Added more data to the squad war request and
243 * 35 2/24/99 3:26p Anoop
244 * Make sure the host is the only guy who bashes skill level for TvT.
246 * 34 2/24/99 2:25p Dave
247 * Fixed up chatbox bugs. Made squad war reporting better. Fixed a respawn
248 * bug for dogfight more.
250 * 33 2/23/99 2:29p Dave
251 * First run of oldschool dogfight mode.
253 * 32 2/17/99 2:11p Dave
254 * First full run of squad war. All freespace and tracker side stuff
257 * 31 2/12/99 6:16p Dave
258 * Pre-mission Squad War code is 95% done.
260 * 30 2/11/99 3:08p Dave
261 * PXO refresh button. Very preliminary squad war support.
263 * 29 2/08/99 5:07p Dave
264 * FS2 chat server support. FS2 specific validated missions.
266 * 28 2/04/99 6:29p Dave
267 * First full working rev of FS2 PXO support. Fixed Glide lighting
270 * 27 1/30/99 5:08p Dave
271 * More new hi-res stuff.Support for nice D3D textures.
273 * 26 1/29/99 2:08a Dave
274 * Fixed beam weapon collisions with players. Reduced size of scoring
275 * struct for multiplayer. Disabled PXO.
277 * 25 1/15/99 2:36p Neilk
278 * fixed multi_jw coordinates
280 * 24 1/13/99 7:19p Neilk
281 * Converted Mission Brief, Barracks, Synch to high res support
283 * 23 1/12/99 7:17p Neilk
285 * 22 1/12/99 5:45p Dave
286 * Moved weapon pipeline in multiplayer to almost exclusively client side.
287 * Very good results. Bandwidth goes down, playability goes up for crappy
288 * connections. Fixed object update problem for ship subsystems.
290 * 21 1/12/99 4:07a Dave
291 * Put in barracks code support for selecting squad logos. Properly
292 * distribute squad logos in a multiplayer game.
294 * 20 1/11/99 7:19p Neilk
295 * Converted multi_join interface to support multiple resolutions
297 * 19 12/18/98 1:13a Dave
298 * Rough 1024x768 support for Direct3D. Proper detection and usage through
301 * 18 12/17/98 4:50p Andsager
302 * Added debrief_assemble_optional_mission_popup_text() for single and
305 * 17 12/14/98 12:13p Dave
306 * Spiffed up xfer system a bit. Put in support for squad logo file xfer.
309 * 16 12/10/98 10:19a Andsager
310 * Fix mission loop assert
312 * 15 12/10/98 9:59a Andsager
313 * Fix some bugs with mission loops
315 * 14 12/09/98 1:56p Andsager
316 * Initial checkin of mission loop
318 * 13 12/03/98 5:22p Dave
319 * Ported over Freespace 1 multiplayer ships.tbl and weapons.tbl
322 * 12 11/30/98 1:07p Dave
323 * 16 bit conversion, first run.
325 * 11 11/20/98 11:16a Dave
326 * Fixed up IPX support a bit. Making sure that switching modes and
327 * loading/saving pilot files maintains proper state.
329 * 10 11/19/98 4:57p Dave
330 * Ignore PXO option if IPX is selected.
332 * 9 11/19/98 4:19p Dave
333 * Put IPX sockets back in psnet. Consolidated all multiplayer config
336 * 8 11/19/98 8:04a Dave
337 * Full support for D3-style reliable sockets. Revamped packet lag/loss
338 * system, made it receiver side and at the lowest possible level.
340 * 7 11/17/98 11:12a Dave
341 * Removed player identification by address. Now assign explicit id #'s.
343 * 6 10/19/98 11:15a Dave
344 * Changed requirements for stats storing in PXO mode.
346 * 5 10/16/98 9:40a Andsager
347 * Remove ".h" files from model.h
349 * 4 10/13/98 9:29a Dave
350 * Started neatening up freespace.h. Many variables renamed and
351 * reorganized. Added AlphaColors.[h,cpp]
353 * 3 10/07/98 6:27p Dave
354 * Globalized mission and campaign file extensions. Removed Silent Threat
355 * special code. Moved \cache \players and \multidata into the \data
358 * 2 10/07/98 10:53a Dave
361 * 1 10/07/98 10:50a Dave
363 * 333 10/02/98 3:22p Allender
364 * fix up the -connect option and fix the -port option
366 * 332 9/17/98 9:26p Dave
367 * Externalized new string.
369 * 331 9/17/98 3:08p Dave
370 * PXO to non-pxo game warning popup. Player icon stuff in create and join
371 * game screens. Upped server count refresh time in PXO to 35 secs (from
374 * 330 9/17/98 9:43a Allender
375 * removed an Assert that Dave called bogus.
377 * 329 9/16/98 6:54p Dave
378 * Upped max sexpression nodes to 1800 (from 1600). Changed FRED to sort
379 * the ship list box. Added code so that tracker stats are not stored with
382 * 328 9/15/98 7:24p Dave
383 * Minor UI changes. Localized bunch of new text.
385 * 327 9/15/98 4:03p Dave
386 * Changed readyroom and multi screens to display "st" icon for all
387 * missions with mission disk content (not necessarily just those that
388 * come with Silent Threat).
390 * 326 9/15/98 11:44a Dave
391 * Renamed builtin ships and wepaons appropriately in FRED. Put in scoring
392 * scale factors. Fixed standalone filtering of MD missions to non-MD
395 * 325 9/13/98 9:36p Dave
396 * Support for new info icons for multiplayer missions (from-volition,
397 * valid, mission disk, etc).
399 * 324 9/11/98 4:14p Dave
400 * Fixed file checksumming of < file_size. Put in more verbose kicking and
401 * PXO stats store reporting.
403 * 323 9/10/98 1:17p Dave
404 * Put in code to flag missions and campaigns as being MD or not in Fred
405 * and Freespace. Put in multiplayer support for filtering out MD
406 * missions. Put in multiplayer popups for warning of non-valid missions.
408 * 322 9/04/98 3:51p Dave
409 * Put in validated mission updating and application during stats
412 * 321 8/31/98 2:06p Dave
413 * Make cfile sort the ordering or vp files. Added support/checks for
414 * recognizing "mission disk" players.
416 * 320 8/21/98 1:15p Dave
417 * Put in log system hooks in useful places.
419 * 319 8/20/98 5:31p Dave
420 * Put in handy multiplayer logfile system. Now need to put in useful
421 * applications of it all over the code.
423 * 318 8/12/98 4:53p Dave
424 * Put in 32 bit checksumming for PXO missions. No validation on the
425 * actual tracker yet, though.
427 * 317 8/07/98 10:40a Allender
428 * new command line flags for starting netgames. Only starting currently
429 * works, and PXO isn't implemented yet
431 * 316 7/24/98 9:27a Dave
432 * Tidied up endgame sequencing by removing several old flags and
433 * standardizing _all_ endgame stuff with a single function call.
435 * 315 7/14/98 10:04a Allender
436 * fixed the countdown code to not be reliant on timer_get_fixed_seconds
438 * 314 7/10/98 5:04p Dave
439 * Fix connection speed bug on standalone server.
441 * 313 7/09/98 6:01p Dave
442 * Firsts full version of PXO updater. Put in stub for displaying
445 * 312 7/07/98 2:49p Dave
448 * 311 6/30/98 2:17p Dave
449 * Revised object update system. Removed updates for all weapons. Put
450 * button info back into control info packet.
452 * 310 6/13/98 9:32p Mike
453 * Kill last character in file which caused "Find in Files" to report the
454 * file as "not a text file."
461 #include <winsock.h> // for inet_addr()
463 #include <sys/types.h>
464 #include <sys/socket.h>
465 #include <netinet/in.h>
466 #include <arpa/inet.h>
471 #include "multiutil.h"
472 #include "multimsgs.h"
478 #include "gamesequence.h"
479 #include "freespace.h"
480 #include "contexthelp.h"
485 #include "missionshipchoice.h"
486 #include "multi_xfer.h"
488 #include "stand_gui.h"
489 #include "linklist.h"
490 #include "multiteamselect.h"
491 #include "missioncampaign.h"
498 #include "missiondebrief.h"
499 #include "multi_ingame.h"
500 #include "multi_kick.h"
501 #include "multi_data.h"
502 #include "multi_campaign.h"
503 #include "multi_team.h"
504 #include "multi_pinfo.h"
505 #include "multi_observer.h"
506 #include "multi_voice.h"
507 #include "multi_endgame.h"
508 #include "managepilot.h"
511 #include "objcollide.h"
513 #include "multi_pmsg.h"
514 #include "multi_obj.h"
515 #include "multi_log.h"
516 #include "alphacolors.h"
517 #include "animplay.h"
518 #include "multi_dogfight.h"
519 #include "missionpause.h"
521 // -------------------------------------------------------------------------------------------------------------
523 // MULTIPLAYER COMMON interface controls
526 // the common text info box stuff. This is lifted almost directly from Alans briefing code (minus the spiffy colored, scrolling
528 int Multi_common_text_coords[GR_NUM_RESOLUTIONS][4] = {
541 int Multi_common_text_max_display[GR_NUM_RESOLUTIONS] = {
550 #define MULTI_COMMON_TEXT_META_CHAR '$'
551 #define MULTI_COMMON_TEXT_MAX_LINE_LENGTH 100
552 #define MULTI_COMMON_TEXT_MAX_LINES 20
553 #define MULTI_COMMON_MAX_TEXT (MULTI_COMMON_TEXT_MAX_LINES * MULTI_COMMON_TEXT_MAX_LINE_LENGTH)
555 char Multi_common_all_text[MULTI_COMMON_MAX_TEXT];
556 char Multi_common_text[MULTI_COMMON_TEXT_MAX_LINES][MULTI_COMMON_TEXT_MAX_LINE_LENGTH];
558 int Multi_common_top_text_line = -1; // where to start displaying from
559 int Multi_common_num_text_lines = 0; // how many lines we have
561 void multi_common_scroll_text_up();
562 void multi_common_scroll_text_down();
563 void multi_common_move_to_bottom();
564 void multi_common_render_text();
565 void multi_common_split_text();
567 #define MAX_IP_STRING 255 // maximum length for ip string
569 void multi_common_scroll_text_up()
571 Multi_common_top_text_line--;
572 if ( Multi_common_top_text_line < 0 ) {
573 Multi_common_top_text_line = 0;
574 if ( !mouse_down(MOUSE_LEFT_BUTTON) )
575 gamesnd_play_iface(SND_GENERAL_FAIL);
578 gamesnd_play_iface(SND_SCROLL);
582 void multi_common_scroll_text_down()
584 Multi_common_top_text_line++;
585 if ( (Multi_common_num_text_lines - Multi_common_top_text_line) < Multi_common_text_max_display[gr_screen.res] ) {
586 Multi_common_top_text_line--;
587 if ( !mouse_down(MOUSE_LEFT_BUTTON) ){
588 gamesnd_play_iface(SND_GENERAL_FAIL);
591 gamesnd_play_iface(SND_SCROLL);
595 void multi_common_move_to_bottom()
597 // if there's nowhere to scroll down, do nothing
598 if(Multi_common_num_text_lines <= Multi_common_text_max_display[gr_screen.res]){
602 Multi_common_top_text_line = Multi_common_num_text_lines - Multi_common_text_max_display[gr_screen.res];
605 void multi_common_set_text(char *str,int auto_scroll)
608 // store the entire string as well
609 if(strlen(str) > MULTI_COMMON_MAX_TEXT){
612 strcpy(Multi_common_all_text,str);
615 // split the whole thing up
616 multi_common_split_text();
618 // scroll to the bottom if we're supposed to
620 multi_common_move_to_bottom();
624 void multi_common_add_text(char *str,int auto_scroll)
627 // store the entire string as well
628 if((strlen(str) + strlen(Multi_common_all_text)) > MULTI_COMMON_MAX_TEXT){
631 strcat(Multi_common_all_text,str);
634 // split the whole thing up
635 multi_common_split_text();
637 // scroll to the bottom if we're supposed to
639 multi_common_move_to_bottom();
643 void multi_common_split_text()
646 int n_chars[MAX_BRIEF_LINES];
647 char *p_str[MAX_BRIEF_LINES];
649 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);
650 Assert(n_lines != -1);
652 for ( i = 0; i < n_lines; i++ ) {
653 Assert(n_chars[i] < MULTI_COMMON_TEXT_MAX_LINE_LENGTH);
654 strncpy(Multi_common_text[i], p_str[i], n_chars[i]);
655 Multi_common_text[i][n_chars[i]] = 0;
656 drop_leading_white_space(Multi_common_text[i]);
659 Multi_common_top_text_line = 0;
660 Multi_common_num_text_lines = n_lines;
663 void multi_common_render_text()
665 int i, fh, line_count;
667 fh = gr_get_font_height();
670 gr_set_color_fast(&Color_text_normal);
671 for ( i = Multi_common_top_text_line; i < Multi_common_num_text_lines; i++ ) {
672 if ( line_count >= Multi_common_text_max_display[gr_screen.res] ){
675 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]);
679 if ( (Multi_common_num_text_lines - Multi_common_top_text_line) > Multi_common_text_max_display[gr_screen.res] ) {
680 gr_set_color_fast(&Color_bright_red);
681 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));
685 // common notification messaging stuff
686 #define MULTI_COMMON_NOTIFY_TIME 3500
687 int Multi_common_join_y[GR_NUM_RESOLUTIONS] = {
691 int Multi_common_create_y[GR_NUM_RESOLUTIONS] = {
696 int Multi_common_jw_y[GR_NUM_RESOLUTIONS] = {
701 int Multi_common_msg_y[GR_NUM_RESOLUTIONS] = {
706 char Multi_common_notify_text[200];
707 int Multi_common_notify_stamp;
709 void multi_common_notify_init()
711 strcpy(Multi_common_notify_text,"");
712 Multi_common_notify_stamp = -1;
715 // add a notification string, drawing appropriately depending on the state/screen we're in
716 void multi_common_add_notify(char *str)
719 strcpy(Multi_common_notify_text,str);
720 Multi_common_notify_stamp = timestamp(MULTI_COMMON_NOTIFY_TIME);
724 // process/display notification messages
725 void multi_common_notify_do()
727 if(Multi_common_notify_stamp != -1){
728 if(timestamp_elapsed(Multi_common_notify_stamp)){
729 Multi_common_notify_stamp = -1;
732 gr_get_string_size(&w,&h,Multi_common_notify_text);
733 gr_set_color_fast(&Color_white);
735 // determine where it should be placed based upon which screen we're on
737 switch(gameseq_get_state()){
738 case GS_STATE_MULTI_JOIN_GAME :
739 y = Multi_common_join_y[gr_screen.res];
741 case GS_STATE_MULTI_HOST_SETUP :
742 y = Multi_common_create_y[gr_screen.res];
744 case GS_STATE_MULTI_CLIENT_SETUP :
745 y = Multi_common_jw_y[gr_screen.res];
747 case GS_STATE_MULTI_START_GAME :
748 y = Multi_common_msg_y[gr_screen.res];
752 gr_string((gr_screen.max_w - w)/2, y, Multi_common_notify_text);
759 int Multi_common_icons[MULTI_NUM_COMMON_ICONS];
761 char *Multi_common_icon_names[MULTI_NUM_COMMON_ICONS] = {
762 "DotRed", // voice denied
763 "DotGreen", // voice recording
764 "OvalGreen", // team 0
765 "OvalGreen01", // team 0 select
767 "OvalRed01", // team 1 select
768 "mp_coop", // coop mission
769 "mp_teams", // TvT mission
770 "mp_furball", // furball mission
771 "icon-volition", // volition mission
772 "icon-valid", // mission is valid
777 "icon-silent" // SilentThreat
781 // width and height of the icons
782 int Multi_common_icon_dims[MULTI_NUM_COMMON_ICONS][2] = {
783 {11, 11}, // voice denied
784 {11, 11}, // voice recording
786 {11, 11}, // team 0 select
788 {11, 11}, // team 1 select
791 {18, 11}, // mp furball
792 {9, 9}, // volition mission
793 {8, 8}, // mission is valid
798 {16, 7} // silent threat
802 void multi_load_common_icons()
807 for(idx=0; idx<MULTI_NUM_COMMON_ICONS; idx++){
808 Multi_common_icons[idx] = -1;
809 Multi_common_icons[idx] = bm_load(Multi_common_icon_names[idx]);
813 void multi_unload_common_icons()
818 for(idx=0; idx<MULTI_NUM_COMMON_ICONS; idx++){
819 if(Multi_common_icons[idx] != -1){
820 bm_unload(Multi_common_icons[idx]);
821 Multi_common_icons[idx] = -1;
826 // display any relevant voice status icons
827 void multi_common_voice_display_status()
829 switch(multi_voice_status()){
830 // i have been denied the voice token
831 case MULTI_VOICE_STATUS_DENIED:
832 if(Multi_common_icons[MICON_VOICE_DENIED] != -1){
833 gr_set_bitmap(Multi_common_icons[MICON_VOICE_DENIED], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
838 // i am currently recording
839 case MULTI_VOICE_STATUS_RECORDING:
840 if(Multi_common_icons[MICON_VOICE_RECORDING] != -1){
841 gr_set_bitmap(Multi_common_icons[MICON_VOICE_RECORDING], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
846 // i am currently playing back sound
847 case MULTI_VOICE_STATUS_PLAYING:
850 // the system is currently idle
851 case MULTI_VOICE_STATUS_IDLE:
857 // palette initialization stuff
858 #define MULTI_COMMON_PALETTE_FNAME "InterfacePalette"
861 int Multi_common_interface_palette = -1;
863 void multi_common_load_palette();
864 void multi_common_set_palette();
865 void multi_common_unload_palette();
867 // load in the palette if it doesn't already exist
868 void multi_common_load_palette()
870 if(Multi_common_interface_palette != -1){
874 Multi_common_interface_palette = bm_load(MULTI_COMMON_PALETTE_FNAME);
875 if(Multi_common_interface_palette == -1){
876 nprintf(("Network","Error loading multiplayer common palette!\n"));
880 // set the common palette to be the active one
881 void multi_common_set_palette()
883 // if the palette is not loaded yet, do so now
884 if(Multi_common_interface_palette == -1){
885 multi_common_load_palette();
888 if(Multi_common_interface_palette != -1){
889 #ifndef HARDWARE_ONLY
890 palette_use_bm_palette(Multi_common_interface_palette);
895 // unload the bitmap palette
896 void multi_common_unload_palette()
898 if(Multi_common_interface_palette != -1){
899 bm_unload(Multi_common_interface_palette);
900 Multi_common_interface_palette = -1;
904 void multi_common_verify_cd()
907 // otherwise, call the freespace function to determine if we have a cd
910 if((find_freespace_cd(FS_CDROM_VOLUME_1) >= 0) || (find_freespace_cd(FS_CDROM_VOLUME_2) >= 0) ){
912 if((find_freespace_cd(FS_CDROM_VOLUME_1) >= 0) || (find_freespace_cd(FS_CDROM_VOLUME_2) >= 0) || (find_freespace_cd(FS_CDROM_VOLUME_3) >= 0) ){
922 // -------------------------------------------------------------------------------------------------------------
924 // MULTIPLAYER JOIN SCREEN
927 #define MULTI_JOIN_NUM_BUTTONS 11
931 #define MULTI_JOIN_PALETTE "InterfacePalette"
933 static char *Multi_join_bitmap_fname[GR_NUM_RESOLUTIONS] = {
934 "MultiJoin", // GR_640
935 "2_MultiJoin" // GR_1024
938 static char *Multi_join_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
939 "MultiJoin-M", // GR_640
940 "2_MultiJoin-M" // GR_1024
946 char *Mj_slider_name[GR_NUM_RESOLUTIONS] = {
950 int Mj_slider_coords[GR_NUM_RESOLUTIONS][4] = {
961 #define MJ_SCROLL_UP 0
962 #define MJ_SCROLL_DOWN 1
964 #define MJ_SCROLL_INFO_UP 3
965 #define MJ_SCROLL_INFO_DOWN 4
966 #define MJ_JOIN_OBSERVER 5
967 #define MJ_START_GAME 6
973 // uses MULTI_JOIN_REFRESH_TIME as its timestamp
974 int Multi_join_glr_stamp;
976 #define MULTI_JOIN_PING_TIME 15000 // how often we ping all the known servers
977 int Multi_join_ping_stamp;
978 UI_WINDOW Multi_join_window; // the window object for the join screen
979 UI_BUTTON Multi_join_select_button; // for selecting list items
981 UI_SLIDER2 Multi_join_slider; // handy dandy slider
983 int Multi_join_bitmap; // the background bitmap
985 ui_button_info Multi_join_buttons[GR_NUM_RESOLUTIONS][MULTI_JOIN_NUM_BUTTONS] = {
988 ui_button_info("MJ_00", 0, 85, -1, -1, 0),
989 ui_button_info("MJ_01", 0, 125, -1, -1, 1),
990 ui_button_info("MJ_03", 20, 324, -1, -1, 3),
991 ui_button_info("MJ_04", 0, 399, -1, -1, 4),
992 ui_button_info("MJ_05", 0, 436, -1, -1, 5),
993 ui_button_info("MJ_15", 450, 323, -1, -1, 15),
994 ui_button_info("MJ_10", 519, 323, -1, -1, 10),
995 ui_button_info("MJ_06", 574, 323, -1, -1, 6),
996 ui_button_info("MJ_08", 470, 427, -1, -1, 8),
997 ui_button_info("MJ_09", 448, 454, -1, -1, 9),
998 ui_button_info("MJ_07", 563, 411, -1, -1, 7),
1000 ui_button_info( "MJ_00", 1, 57, -1, -1, 0 ), // scroll up
1001 ui_button_info( "MJ_02", 1, 297, -1, -1, 2 ), // scroll down
1002 ui_button_info( "MJ_03", 10, 338, 65, 364, 3 ), // refresh
1003 ui_button_info( "MJ_04", 1, 405, -1, -1, 4 ), // scroll info up
1004 ui_button_info( "MJ_05", 1, 446, -1, -1, 5 ), // scroll info down
1005 ui_button_info( "MJ_06", 489, 339, -1, -1, 6 ), // join as observer
1006 ui_button_info( "MJ_07", 538, 339, -1, -1, 7 ), // create game
1007 ui_button_info( "MJ_08", 583, 339, 588, 376, 8 ), // cancel
1008 ui_button_info( "MJ_09", 534, 426, -1, -1, 9 ), // help
1009 ui_button_info( "MJ_10", 534, 454, -1, -1, 10 ), // options
1010 ui_button_info( "MJ_11", 571, 426, 589, 416, 11 ), // join
1014 ui_button_info( "2_MJ_00", 2, 92, -1, -1, 0 ), // scroll up
1015 ui_button_info( "2_MJ_02", 2, 475, -1, -1, 2 ), // scroll down
1016 ui_button_info( "2_MJ_03", 16, 541, 104, 582, 3 ), // refresh
1017 ui_button_info( "2_MJ_04", 2, 648, -1, -1, 4 ), // scroll info up
1018 ui_button_info( "2_MJ_05", 2, 713, -1, -1, 5 ), // scroll info down
1019 ui_button_info( "2_MJ_06", 783, 542, -1, -1, 6 ), // join as observer
1020 ui_button_info( "2_MJ_07", 861, 542, -1, -1, 7 ), // create game
1021 ui_button_info( "2_MJ_08", 933, 542, 588, 376, 8 ), // cancel
1022 ui_button_info( "2_MJ_09", 854, 681, -1, -1, 9 ), // help
1023 ui_button_info( "2_MJ_10", 854, 727, -1, -1, 10 ), // options
1024 ui_button_info( "2_MJ_11", 914, 681, 937, 668, 11 ), // join
1029 #define MULTI_JOIN_NUM_TEXT 0
1031 #define MULTI_JOIN_NUM_TEXT 13
1034 UI_XSTR Multi_join_text[GR_NUM_RESOLUTIONS][MULTI_JOIN_NUM_TEXT] = {
1036 // not needed for FS1
1038 {"Refresh", 1299, 65, 364, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_REFRESH].button},
1039 {"Join as", 1300, 476, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_JOIN_OBSERVER].button},
1040 {"Observer", 1301, 467, 385, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_JOIN_OBSERVER].button},
1041 {"Create", 1408, 535, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_START_GAME].button},
1042 {"Game", 1302, 541, 385, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_START_GAME].button},
1043 {"Cancel", 387, 588, 376, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[0][MJ_CANCEL].button},
1044 {"Help", 928, 479, 436, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_HELP].button},
1045 {"Options", 1036, 479, 460, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_OPTIONS].button},
1046 {"Join", 1303, 589, 416, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[0][MJ_ACCEPT].button},
1047 {"Status", 1304, 37, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
1048 {"Server", 1305, 116, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
1049 {"Players", 1306, 471, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
1050 {"Ping", 1307, 555, 37, UI_XSTR_COLOR_GREEN, -1, NULL}
1054 // not needed for FS1
1056 {"Refresh", 1299, 104, 582, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_REFRESH].button},
1057 {"Join as", 1300, 783, 602, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_JOIN_OBSERVER].button},
1058 {"Observer", 1301, 774, 611, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_JOIN_OBSERVER].button},
1059 {"Create", 1408, 868, 602, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_START_GAME].button},
1060 {"Game", 1302, 872, 611, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_START_GAME].button},
1061 {"Cancel", 387, 941, 602, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[1][MJ_CANCEL].button},
1062 {"Help", 928, 782, 699, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_HELP].button},
1063 {"Options", 1036, 782, 736, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_OPTIONS].button},
1064 {"Join", 1303, 937, 668, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[1][MJ_ACCEPT].button},
1065 {"Status", 1304, 60, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
1066 {"Server", 1305, 186, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
1067 {"Players", 1306, 753, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
1068 {"Ping", 1307, 888, 60, UI_XSTR_COLOR_GREEN, -1, NULL}
1073 // constants for coordinate look ups
1074 #define MJ_X_COORD 0
1075 #define MJ_Y_COORD 1
1076 #define MJ_W_COORD 2
1077 #define MJ_H_COORD 3
1079 #define MULTI_JOIN_SENT_WAIT 10000 // wait this long since a join was sent to allow another
1080 int Multi_join_sent_stamp;
1082 // game information text areas
1083 int Mj_max_game_items[GR_NUM_RESOLUTIONS] = {
1088 int Mj_list_y[GR_NUM_RESOLUTIONS] = {
1093 int Mj_status_coords[GR_NUM_RESOLUTIONS][4] = {
1102 int Mj_game_icon_coords[GR_NUM_RESOLUTIONS][3] = {
1111 int Mj_speed_coords[GR_NUM_RESOLUTIONS][4] = {
1120 int Mj_game_name_coords[GR_NUM_RESOLUTIONS][4] = {
1129 int Mj_players_coords[GR_NUM_RESOLUTIONS][4] = {
1138 int Mj_ping_coords[GR_NUM_RESOLUTIONS][4] = {
1147 // game speed labels
1148 #define MJ_NUM_SPEED_LABELS 5
1149 char *Multi_join_speed_labels[MJ_NUM_SPEED_LABELS] = {
1156 color *Multi_join_speed_colors[MJ_NUM_SPEED_LABELS] = {
1159 &Color_bright_green,
1160 &Color_bright_green,
1164 int Mj_cd_coords[GR_NUM_RESOLUTIONS] = {
1169 // extents of the entire boundable game info region
1170 // NOTE : these numbers are completely empirical
1171 #define MJ_PING_GREEN 160
1172 #define MJ_PING_YELLOW 300
1174 int Mj_list_area_coords[GR_NUM_RESOLUTIONS][4] = {
1183 // PXO channel filter
1184 #define MJ_PXO_FILTER_Y 0
1186 // special chars to indicate various status modes for servers
1187 #define MJ_CHAR_STANDALONE "*"
1188 #define MJ_CHAR_CAMPAIGN "c"
1191 // various interface indices
1192 int Multi_join_list_start; // where to start displaying from
1193 active_game *Multi_join_list_start_item; // a pointer to the corresponding active_game
1194 int Multi_join_list_selected; // which item we have selected
1195 active_game *Multi_join_selected_item; // a pointer to the corresponding active_game
1197 // use this macro to modify the list start
1198 #define MJ_LIST_START_INC() do { Multi_join_list_start++; } while(0);
1199 #define MJ_LIST_START_DEC() do { Multi_join_list_start--; } while(0);
1200 #define MJ_LIST_START_SET(vl) do { Multi_join_list_start = vl; } while(0);
1202 // if we should be sending a join request at the end of the frame
1203 int Multi_join_should_send = -1;
1205 // master tracker details
1206 int Multi_join_frame_count; // keep a count of frames displayed
1207 int Multi_join_mt_tried_verify; // already tried verifying the pilot with the tracker
1209 // data stuff for auto joining a game
1210 #define MULTI_AUTOJOIN_JOIN_STAMP 2000
1211 #define MULTI_AUTOJOIN_QUERY_STAMP 2000
1213 int Multi_did_autojoin;
1214 net_addr Multi_autojoin_addr;
1215 int Multi_autojoin_join_stamp;
1216 int Multi_autojoin_query_stamp;
1219 join_request Multi_join_request;
1221 // LOCAL function definitions
1222 void multi_join_check_buttons();
1223 void multi_join_button_pressed(int n);
1224 void multi_join_display_games();
1225 void multi_join_blit_game_status(active_game *game, int y);
1226 void multi_join_load_tcp_addrs();
1227 void multi_join_do_netstuff();
1228 void multi_join_ping_all();
1229 void multi_join_process_select();
1230 void multi_join_list_scroll_up();
1231 void multi_join_list_scroll_down();
1232 void multi_join_list_page_up();
1233 void multi_join_list_page_down();
1234 active_game *multi_join_get_game(int n);
1235 void multi_join_cull_timeouts();
1236 void multi_join_handle_item_cull(active_game *item, int item_index);
1237 void multi_join_send_join_request(int as_observer);
1238 void multi_join_create_game();
1239 void multi_join_blit_top_stuff();
1240 int multi_join_maybe_warn();
1241 int multi_join_warn_pxo();
1242 void multi_join_blit_protocol();
1246 active_game ag, *newitem;;
1249 dc_get_arg(ARG_INT);
1250 for(idx=0; idx<Dc_arg_int; idx++){
1251 // stuff some fake info
1252 memset(&ag, 0, sizeof(active_game));
1253 sprintf(ag.name, "Game %d", idx);
1254 ag.version = MULTI_FS_SERVER_VERSION;
1255 ag.comp_version = MULTI_FS_SERVER_VERSION;
1256 ag.server_addr.addr[0] = (char)idx;
1257 ag.flags = (AG_FLAG_COOP | AG_FLAG_FORMING | AG_FLAG_STANDALONE);
1260 newitem = multi_update_active_games(&ag);
1262 // timestamp it so we get random timeouts
1263 if(newitem != NULL){
1264 // newitem->heard_from_timer = timestamp((int)frand_range(500.0f, 10000.0f));
1269 void multi_join_notify_new_game()
1272 // reset the # of items
1273 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);
1274 Multi_join_slider.force_currentItem(Multi_join_list_start);
1278 int multi_join_autojoin_do()
1280 // if we have an active game on the list, then return a positive value so that we
1281 // can join the game
1282 if ( Active_game_head && (Active_game_count > 0) ) {
1283 Multi_join_selected_item = Active_game_head;
1287 // send out a server_query again
1288 if ( timestamp_elapsed(Multi_autojoin_query_stamp) ) {
1289 send_server_query(&Multi_autojoin_addr);
1290 Multi_autojoin_query_stamp = timestamp(MULTI_AUTOJOIN_QUERY_STAMP);
1296 void multi_join_game_init()
1300 // do the multiplayer init stuff - multi_level_init() now does all net_player zeroing.
1301 // setup various multiplayer things
1302 Assert( Game_mode & GM_MULTIPLAYER );
1303 Assert( Net_player != NULL );
1305 switch (Multi_options_g.protocol) {
1307 ADDRESS_LENGTH = IPX_ADDRESS_LENGTH;
1308 PORT_LENGTH = IPX_PORT_LENGTH;
1312 ADDRESS_LENGTH = IP_ADDRESS_LENGTH;
1313 PORT_LENGTH = IP_PORT_LENGTH;
1322 memset( &Netgame, 0, sizeof(Netgame) );
1325 Net_player->flags |= NETINFO_FLAG_DO_NETWORKING;
1326 Net_player->player = Player;
1327 memcpy(&Net_player->p_info.addr,&Psnet_my_addr,sizeof(net_addr));
1329 // check for the existence of a CD
1330 multi_common_verify_cd();
1332 // load my local netplayer options
1333 multi_options_local_load(&Net_player->p_info.options, Net_player);
1339 common_set_interface_palette(MULTI_JOIN_PALETTE);
1342 // destroy any chatbox contents which previously existed (from another game)
1345 // create the interface window
1346 Multi_join_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
1347 Multi_join_window.set_mask_bmap(Multi_join_bitmap_mask_fname[gr_screen.res]);
1349 // load the background bitmap
1350 Multi_join_bitmap = bm_load(Multi_join_bitmap_fname[gr_screen.res]);
1351 if(Multi_join_bitmap < 0){
1352 // we failed to load the bitmap - this is very bad
1356 // intialize the endgame system
1357 multi_endgame_init();
1359 // initialize the common notification messaging
1360 multi_common_notify_init();
1362 // initialize the common text area
1363 multi_common_set_text("");
1365 // load and use the common interface palette
1366 multi_common_load_palette();
1367 multi_common_set_palette();
1369 // load the help overlay
1370 help_overlay_load(MULTI_JOIN_OVERLAY);
1371 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1373 // do TCP and VMT specific initialization
1374 if(Multi_options_g.protocol == NET_TCP){
1375 // if this is a TCP (non tracker) game, we'll load up our default address list right now
1376 multi_join_load_tcp_addrs();
1379 // initialize any and all timestamps
1380 Multi_join_glr_stamp = -1;
1381 Multi_join_ping_stamp = -1;
1382 Multi_join_sent_stamp = -1;
1384 // reset frame count
1385 Multi_join_frame_count = 0;
1387 // haven't tried to verify on the tracker yet.
1388 Multi_join_mt_tried_verify = 0;
1390 // clear our all game lists to save hassles
1391 multi_join_clear_game_list();
1393 // create the interface buttons
1394 for(idx=0; idx<MULTI_JOIN_NUM_BUTTONS; idx++){
1395 // create the object
1396 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);
1398 // set the sound to play when highlighted
1399 Multi_join_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
1401 // set the ani for the button
1402 Multi_join_buttons[gr_screen.res][idx].button.set_bmaps(Multi_join_buttons[gr_screen.res][idx].filename);
1405 Multi_join_buttons[gr_screen.res][idx].button.link_hotspot(Multi_join_buttons[gr_screen.res][idx].hotspot);
1409 for(idx=0; idx<MULTI_JOIN_NUM_TEXT; idx++){
1410 Multi_join_window.add_XSTR(&Multi_join_text[gr_screen.res][idx]);
1413 Multi_join_should_send = -1;
1415 // close any previously open chatbox
1418 // create the list item select button
1419 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);
1420 Multi_join_select_button.hide();
1424 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);
1427 // if starting a network game, then go to the create game screen
1428 if ( Cmdline_start_netgame ) {
1429 multi_join_create_game();
1430 } else if ( Cmdline_connect_addr != NULL ) {
1435 // joining a game. Send a join request to the given IP address, and wait for the return.
1436 memset( &Multi_autojoin_addr, 0, sizeof(net_addr) );
1437 Multi_autojoin_addr.type = NET_TCP;
1439 // create the address, looking out for port number at the end
1440 port_num = DEFAULT_GAME_PORT;
1441 p = strrchr(Cmdline_connect_addr, ':');
1445 port_num = (short)atoi(p);
1447 ip_addr = inet_addr(Cmdline_connect_addr);
1448 memcpy(Multi_autojoin_addr.addr, &ip_addr, 4);
1449 Multi_autojoin_addr.port = port_num;
1451 send_server_query(&Multi_autojoin_addr);
1452 Multi_autojoin_query_stamp = timestamp(MULTI_AUTOJOIN_QUERY_STAMP);
1453 Multi_did_autojoin = 0;
1457 void multi_join_clear_game_list()
1460 Multi_join_list_selected = -1;
1461 Multi_join_selected_item = NULL;
1462 MJ_LIST_START_SET(-1);
1463 Multi_join_list_start_item = NULL;
1465 // free up the active game list
1466 multi_free_active_games();
1468 // initialize the active game list
1469 Active_game_head = NULL;
1470 Active_game_count = 0;
1473 void multi_join_game_do_frame()
1475 // check the status of our reliable socket. If not valid, popup error and return to main menu
1476 // I put this code here to avoid nasty gameseq issues with states. Also, we will have nice
1477 // background for the popup
1478 if ( !psnet_rel_check() ) {
1479 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));
1480 gameseq_post_event(GS_EVENT_MAIN_MENU);
1484 // return here since we will be moving to the next stage anyway -- I don't want to see the backgrounds of
1485 // all the screens for < 1 second for every screen we automatically move to.
1486 if ( Cmdline_start_netgame ) {
1490 // when joining a network game, wait for the server query to come back, and then join the game
1491 if ( Cmdline_connect_addr != NULL ) {
1494 if ( !Multi_did_autojoin ) {
1495 rval = popup_till_condition(multi_join_autojoin_do, XSTR("&Cancel", 779), XSTR("Joining netgame", 1500) );
1497 // cancel was hit. Send the user back to the main hall
1498 gameseq_post_event(GS_EVENT_MAIN_MENU);
1499 Cmdline_connect_addr = NULL; // reset this value.
1502 // when we get here, we have the data -- join the game.
1503 multi_join_send_join_request(0);
1504 Multi_autojoin_join_stamp = timestamp(MULTI_AUTOJOIN_JOIN_STAMP);
1505 Multi_did_autojoin = 1;
1508 if ( timestamp_elapsed(Multi_autojoin_join_stamp) ) {
1509 multi_join_send_join_request(0);
1510 Multi_autojoin_join_stamp = timestamp(MULTI_AUTOJOIN_JOIN_STAMP);
1516 // reset the should send var
1517 Multi_join_should_send = -1;
1519 int k = Multi_join_window.process();
1521 // process any keypresses
1524 if(help_overlay_active(MULTI_JOIN_OVERLAY)){
1525 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1527 gameseq_post_event(GS_EVENT_MAIN_MENU);
1528 gamesnd_play_iface(SND_USER_SELECT);
1532 // page up the game list
1534 multi_join_list_page_up();
1536 Multi_join_slider.force_currentItem(Multi_join_list_start);
1541 multi_pinfo_popup(Net_player);
1544 // page down the game list
1546 multi_join_list_page_down();
1548 Multi_join_slider.force_currentItem(Multi_join_list_start);
1552 // send out a ping-all
1554 multi_join_ping_all();
1555 Multi_join_ping_stamp = timestamp(MULTI_JOIN_PING_TIME);
1558 // shortcut to start a game
1560 multi_join_create_game();
1563 // scroll the game list up
1565 multi_join_list_scroll_up();
1567 Multi_join_slider.force_currentItem(Multi_join_list_start);
1571 // scroll the game list down
1573 multi_join_list_scroll_down();
1575 Multi_join_slider.force_currentItem(Multi_join_list_start);
1580 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
1581 help_overlay_set_state(MULTI_JOIN_OVERLAY, 0);
1584 // do any network related stuff
1585 multi_join_do_netstuff();
1587 // process any button clicks
1588 multi_join_check_buttons();
1590 // process any list selection stuff
1591 multi_join_process_select();
1593 // draw the background, etc
1595 GR_MAYBE_CLEAR_RES(Multi_join_bitmap);
1596 if(Multi_join_bitmap != -1){
1597 gr_set_bitmap(Multi_join_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1600 Multi_join_window.draw();
1602 // display the active games
1603 multi_join_display_games();
1605 // display any text in the info area
1606 multi_common_render_text();
1608 // display any pending notification messages
1609 multi_common_notify_do();
1611 // blit the CD icon and any PXO filter stuff
1612 multi_join_blit_top_stuff();
1614 // draw the help overlay
1615 help_overlay_maybe_blit(MULTI_JOIN_OVERLAY);
1620 // if we are supposed to be sending a join request
1621 if(Multi_join_should_send != -1){
1622 multi_join_send_join_request(Multi_join_should_send);
1624 Multi_join_should_send = -1;
1626 // increment the frame count
1627 Multi_join_frame_count++;
1630 void multi_join_game_close()
1632 // unload any bitmaps
1633 if(!bm_unload(Multi_join_bitmap)){
1634 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_join_bitmap_fname[gr_screen.res]));
1637 // unload the help overlay
1638 help_overlay_unload(MULTI_JOIN_OVERLAY);
1640 // free up the active game list
1641 multi_free_active_games();
1643 // destroy the UI_WINDOW
1644 Multi_join_window.destroy();
1647 common_free_interface_palette();
1651 void multi_join_check_buttons()
1654 for(idx=0;idx<MULTI_JOIN_NUM_BUTTONS;idx++){
1655 // we only really need to check for one button pressed at a time, so we can break after
1657 if(Multi_join_buttons[gr_screen.res][idx].button.pressed()){
1658 multi_join_button_pressed(idx);
1664 void multi_join_button_pressed(int n)
1668 // if we're player PXO, go back there
1669 gameseq_post_event(GS_EVENT_MAIN_MENU);
1670 gamesnd_play_iface(SND_USER_SELECT);
1673 if(Active_game_count <= 0){
1674 multi_common_add_notify(XSTR("No games found!",757));
1675 gamesnd_play_iface(SND_GENERAL_FAIL);
1676 } else if(Multi_join_list_selected == -1){
1677 multi_common_add_notify(XSTR("No game selected!",758));
1678 gamesnd_play_iface(SND_GENERAL_FAIL);
1679 } else if((Multi_join_sent_stamp != -1) && !timestamp_elapsed(Multi_join_sent_stamp)){
1680 multi_common_add_notify(XSTR("Still waiting on previous join request!",759));
1681 gamesnd_play_iface(SND_GENERAL_FAIL);
1683 // otherwise, if he's already played PXO games, warn him
1685 if(Player->flags & PLAYER_FLAGS_HAS_PLAYED_PXO){
1686 if(!multi_join_warn_pxo()){
1692 // send the join request here
1693 Assert(Multi_join_selected_item != NULL);
1695 // send a join request packet
1696 Multi_join_should_send = 0;
1698 gamesnd_play_iface(SND_COMMIT_PRESSED);
1704 if(!help_overlay_active(MULTI_JOIN_OVERLAY)){
1705 help_overlay_set_state(MULTI_JOIN_OVERLAY,1);
1707 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1711 // scroll the game list up
1713 multi_join_list_scroll_up();
1715 Multi_join_slider.force_currentItem(Multi_join_list_start);
1719 // scroll the game list down
1720 case MJ_SCROLL_DOWN:
1721 multi_join_list_scroll_down();
1723 Multi_join_slider.force_currentItem(Multi_join_list_start);
1727 // scroll the info text box up
1728 case MJ_SCROLL_INFO_UP:
1729 multi_common_scroll_text_up();
1732 // scroll the info text box down
1733 case MJ_SCROLL_INFO_DOWN:
1734 multi_common_scroll_text_down();
1737 // go to the options screen
1739 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
1742 // go to the start game screen
1744 multi_join_create_game();
1747 // refresh the game/server list
1749 gamesnd_play_iface(SND_USER_SELECT);
1750 broadcast_game_query();
1753 // join a game as an observer
1754 case MJ_JOIN_OBSERVER:
1755 if(Active_game_count <= 0){
1756 multi_common_add_notify(XSTR("No games found!",757));
1757 gamesnd_play_iface(SND_GENERAL_FAIL);
1758 } else if(Multi_join_list_selected == -1){
1759 multi_common_add_notify(XSTR("No game selected!",758));
1760 gamesnd_play_iface(SND_GENERAL_FAIL);
1761 } else if((Multi_join_sent_stamp != -1) && !timestamp_elapsed(Multi_join_sent_stamp)){
1762 multi_common_add_notify(XSTR("Still waiting on previous join request!",759));
1763 gamesnd_play_iface(SND_GENERAL_FAIL);
1765 // send the join request here
1766 Assert(Multi_join_selected_item != NULL);
1768 Multi_join_should_send = 1;
1770 gamesnd_play_iface(SND_COMMIT_PRESSED);
1775 multi_common_add_notify(XSTR("Not implemented yet!",760));
1776 gamesnd_play_iface(SND_GENERAL_FAIL);
1781 // display all relevant info for active games
1782 void multi_join_display_games()
1784 active_game *moveup = Multi_join_list_start_item;
1788 int y_start = Mj_list_y[gr_screen.res];
1793 // blit the game status (including text and type icon)
1794 multi_join_blit_game_status(moveup,y_start);
1796 // get the connection type
1797 con_type = (moveup->flags & AG_FLAG_CONNECTION_SPEED_MASK) >> AG_FLAG_CONNECTION_BIT;
1798 if((con_type > 4) || (con_type < 0)){
1802 // display the connection speed
1804 strcpy(str, Multi_join_speed_labels[con_type]);
1805 gr_set_color_fast(Multi_join_speed_colors[con_type]);
1806 gr_string(Mj_speed_coords[gr_screen.res][MJ_X_COORD], y_start, str);
1808 // we'll want to have different colors for highlighted items, etc.
1809 if(moveup == Multi_join_selected_item){
1810 gr_set_color_fast(&Color_text_selected);
1812 gr_set_color_fast(&Color_text_normal);
1815 // display the game name, adding appropriate status chars
1817 if(moveup->flags & AG_FLAG_STANDALONE){
1818 strcat(str,MJ_CHAR_STANDALONE);
1820 if(moveup->flags & AG_FLAG_CAMPAIGN){
1821 strcat(str,MJ_CHAR_CAMPAIGN);
1824 // tack on the actual server name
1826 strcat(str,moveup->name);
1827 if(strlen(moveup->mission_name) > 0){
1829 strcat(str,moveup->mission_name);
1832 // make sure the string fits in the display area and draw it
1833 gr_force_fit_string(str,200,Mj_game_name_coords[gr_screen.res][MJ_W_COORD]);
1834 gr_string(Mj_game_name_coords[gr_screen.res][MJ_X_COORD],y_start,str);
1836 // display the ping time
1837 if(moveup->ping.ping_avg > 0){
1838 if(moveup->ping.ping_avg > 1000){
1839 gr_set_color_fast(&Color_bright_red);
1840 strcpy(str,XSTR("> 1 sec",761));
1842 // set the appropriate ping time color indicator
1843 if(moveup->ping.ping_avg > MJ_PING_YELLOW){
1844 gr_set_color_fast(&Color_bright_red);
1845 } else if(moveup->ping.ping_avg > MJ_PING_YELLOW){
1846 gr_set_color_fast(&Color_bright_yellow);
1848 gr_set_color_fast(&Color_bright_green);
1851 sprintf(str,"%d",moveup->ping.ping_avg);
1852 strcat(str,XSTR(" ms",762)); // [[ Milliseconds ]]
1855 gr_string(Mj_ping_coords[gr_screen.res][MJ_X_COORD],y_start,str);
1858 // display the number of players (be sure to center it)
1859 if(moveup == Multi_join_selected_item){
1860 gr_set_color_fast(&Color_text_selected);
1862 gr_set_color_fast(&Color_text_normal);
1864 sprintf(str,"%d",moveup->num_players);
1865 gr_get_string_size(&w,&h,str);
1866 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);
1870 moveup = moveup->next;
1871 } while((moveup != Active_game_head) && (count < Mj_max_game_items[gr_screen.res]));
1873 // if there are no items on the list, display this info
1875 gr_set_color_fast(&Color_bright);
1876 gr_string(Mj_game_name_coords[gr_screen.res][MJ_X_COORD] - 30,y_start,XSTR("<No game servers found>",763));
1880 void multi_join_blit_game_status(active_game *game, int y)
1883 char status_text[25];
1885 // blit the proper icon
1887 switch( game->flags & AG_FLAG_TYPE_MASK ){
1890 if(Multi_common_icons[MICON_COOP] != -1){
1891 gr_set_bitmap(Multi_common_icons[MICON_COOP], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1896 // team vs. team game
1898 if(Multi_common_icons[MICON_TVT] != -1){
1899 gr_set_bitmap(Multi_common_icons[MICON_TVT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1906 case AG_FLAG_DOGFIGHT:
1907 if(Multi_common_icons[MICON_DOGFIGHT] != -1){
1908 gr_set_bitmap(Multi_common_icons[MICON_DOGFIGHT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1914 // if we're supposed to draw a bitmap
1916 gr_bitmap(Mj_game_icon_coords[gr_screen.res][MJ_X_COORD],y-1);
1919 // blit the proper status text
1920 memset(status_text,0,25);
1922 switch( game->flags & AG_FLAG_STATE_MASK ){
1923 case AG_FLAG_FORMING:
1924 gr_set_color_fast(&Color_bright_green);
1925 strcpy(status_text,XSTR("Forming",764));
1927 case AG_FLAG_BRIEFING:
1928 gr_set_color_fast(&Color_bright_red);
1929 strcpy(status_text,XSTR("Briefing",765));
1931 case AG_FLAG_DEBRIEF:
1932 gr_set_color_fast(&Color_bright_red);
1933 strcpy(status_text,XSTR("Debrief",766));
1936 gr_set_color_fast(&Color_bright_red);
1937 strcpy(status_text,XSTR("Paused",767));
1939 case AG_FLAG_IN_MISSION:
1940 gr_set_color_fast(&Color_bright_red);
1941 strcpy(status_text,XSTR("Playing",768));
1944 gr_set_color_fast(&Color_bright);
1945 strcpy(status_text,XSTR("Unknown",769));
1948 gr_get_string_size(&str_w,NULL,status_text);
1949 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);
1952 // load in a list of active games from our tcp.cfg file
1953 void multi_join_load_tcp_addrs()
1955 char line[MAX_IP_STRING];
1960 // attempt to open the ip list file
1961 file = cfopen(IP_CONFIG_FNAME,"rt",CFILE_NORMAL,CF_TYPE_DATA);
1963 nprintf(("Network","Error loading tcp.cfg file!\n"));
1967 // free up any existing server list
1968 multi_free_server_list();
1970 // read in all the strings in the file
1971 while(!cfeof(file)){
1973 cfgets(line,MAX_IP_STRING,file);
1975 // strip off any newline character
1976 if(line[strlen(line) - 1] == '\n'){
1977 line[strlen(line) - 1] = '\0';
1980 // empty lines don't get processed
1981 if( (line[0] == '\0') || (line[0] == '\n') ){
1985 if ( !psnet_is_valid_ip_string(line) ) {
1986 nprintf(("Network","Invalid ip string (%s)\n",line));
1988 // copy the server ip address
1989 memset(&addr,0,sizeof(net_addr));
1990 addr.type = NET_TCP;
1991 psnet_string_to_addr(&addr,line);
1992 if ( addr.port == 0 ){
1993 addr.port = DEFAULT_GAME_PORT;
1996 // create a new server item on the list
1997 item = multi_new_server_item();
1999 memcpy(&item->server_addr,&addr,sizeof(net_addr));
2007 // do stuff like pinging servers, sending out requests, etc
2008 void multi_join_do_netstuff()
2010 // handle game query stuff
2011 if(Multi_join_glr_stamp == -1){
2012 broadcast_game_query();
2014 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
2015 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME_LOCAL);
2017 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME);
2020 // otherwise send out game query and restamp
2021 else if(timestamp_elapsed(Multi_join_glr_stamp)){
2022 broadcast_game_query();
2024 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
2025 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME_LOCAL);
2027 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME);
2031 // check to see if we've been accepted. If so, put up message saying so
2032 if ( Net_player->flags & (NETINFO_FLAG_ACCEPT_INGAME|NETINFO_FLAG_ACCEPT_CLIENT|NETINFO_FLAG_ACCEPT_HOST|NETINFO_FLAG_ACCEPT_OBSERVER) ) {
2033 multi_common_add_notify(XSTR("Accepted. Waiting for player data.",770));
2036 // check to see if any join packets we have sent have timed out
2037 if((Multi_join_sent_stamp != -1) && (timestamp_elapsed(Multi_join_sent_stamp))){
2038 Multi_join_sent_stamp = -1;
2039 multi_common_add_notify(XSTR("Join request timed out!",771));
2042 // check to see if we should be pinging everyone
2043 if((Multi_join_ping_stamp == -1) || (timestamp_elapsed(Multi_join_ping_stamp))){
2044 multi_join_ping_all();
2045 Multi_join_ping_stamp = timestamp(MULTI_JOIN_PING_TIME);
2049 multi_join_cull_timeouts();
2052 // evaluate a returned pong.
2053 void multi_join_eval_pong(net_addr *addr, fix pong_time)
2056 active_game *moveup = Active_game_head;
2061 if(psnet_same(&moveup->server_addr,addr)){
2063 multi_ping_eval_pong(&moveup->ping);
2067 moveup = moveup->next;
2069 } while(moveup != Active_game_head);
2072 // update the game's ping
2074 if(found && (moveup->ping_end > moveup->ping_start)){
2075 moveup->ping_time = f2fl(moveup->ping_end - moveup->ping_start);
2076 moveup->ping_start = -1;
2077 moveup->ping_end = -1;
2082 // ping all the server on the list
2083 void multi_join_ping_all()
2085 active_game *moveup = Active_game_head;
2090 moveup->ping_start = timer_get_fixed_seconds();
2091 moveup->ping_end = -1;
2092 send_ping(&moveup->server_addr);
2094 multi_ping_send(&moveup->server_addr,&moveup->ping);
2096 moveup = moveup->next;
2097 } while(moveup != Active_game_head);
2101 void multi_join_process_select()
2103 // if we don't have anything selected and there are items on the list - select the first one
2104 if((Multi_join_list_selected == -1) && (Active_game_count > 0)){
2105 Multi_join_list_selected = 0;
2106 Multi_join_selected_item = multi_join_get_game(0);
2107 MJ_LIST_START_SET(0);
2108 Multi_join_list_start_item = Multi_join_selected_item;
2110 // send a mission description request to this guy
2111 send_netgame_descript_packet(&Multi_join_selected_item->server_addr,0);
2112 multi_common_set_text("");
2114 // I sure hope this doesn't happen
2115 Assert(Multi_join_selected_item != NULL);
2118 // otherwise see if he's clicked on an item
2119 else if(Multi_join_select_button.pressed() && (Active_game_count > 0)){
2121 Multi_join_select_button.get_mouse_pos(NULL,&y);
2123 if(item + Multi_join_list_start < Active_game_count){
2124 gamesnd_play_iface(SND_IFACE_MOUSE_CLICK);
2126 Multi_join_list_selected = item + Multi_join_list_start;
2127 Multi_join_selected_item = multi_join_get_game(Multi_join_list_selected);
2129 // I sure hope this doesn't happen
2130 Assert(Multi_join_selected_item != NULL);
2132 // send a mission description request to this guy
2133 send_netgame_descript_packet(&Multi_join_selected_item->server_addr,0);
2134 multi_common_set_text("");
2138 // if he's double clicked, then select it and accept
2139 if(Multi_join_select_button.double_clicked()){
2141 Multi_join_select_button.get_mouse_pos(NULL,&y);
2143 if(item == Multi_join_list_selected){
2144 multi_join_button_pressed(MJ_ACCEPT);
2149 // return game n (0 based index)
2150 active_game *multi_join_get_game(int n)
2152 active_game *moveup = Active_game_head;
2159 moveup = moveup->next;
2160 while((moveup != Active_game_head) && (count != n)){
2161 moveup = moveup->next;
2164 if(moveup == Active_game_head){
2165 nprintf(("Network","Warning, couldn't find game item %d!\n",n));
2175 // scroll through the game list
2176 void multi_join_list_scroll_up()
2178 // if we're not at the beginning of the list, scroll up
2179 if(Multi_join_list_start_item != Active_game_head){
2180 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2182 MJ_LIST_START_DEC();
2184 gamesnd_play_iface(SND_SCROLL);
2186 gamesnd_play_iface(SND_GENERAL_FAIL);
2190 // scroll through the game list
2191 void multi_join_list_scroll_down()
2193 if((Active_game_count - Multi_join_list_start) > Mj_max_game_items[gr_screen.res]){
2194 Multi_join_list_start_item = Multi_join_list_start_item->next;
2196 MJ_LIST_START_INC();
2198 gamesnd_play_iface(SND_SCROLL);
2200 gamesnd_play_iface(SND_GENERAL_FAIL);
2204 void multi_join_list_page_up()
2206 // in this case, just set us to the beginning of the list
2207 if((Multi_join_list_start - Mj_max_game_items[gr_screen.res]) < 0){
2208 Multi_join_list_start_item = Active_game_head;
2210 MJ_LIST_START_SET(0);
2212 gamesnd_play_iface(SND_SCROLL);
2214 // otherwise page the whole thing up
2216 for(idx=0; idx<Mj_max_game_items[gr_screen.res]; idx++){
2217 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2219 MJ_LIST_START_DEC();
2221 gamesnd_play_iface(SND_SCROLL);
2225 void multi_join_list_page_down()
2229 // page the whole thing down
2230 while((count < Mj_max_game_items[gr_screen.res]) && ((Active_game_count - Multi_join_list_start) > Mj_max_game_items[gr_screen.res])){
2231 Multi_join_list_start_item = Multi_join_list_start_item->next;
2232 MJ_LIST_START_INC();
2237 gamesnd_play_iface(SND_SCROLL);
2240 void multi_join_cull_timeouts()
2242 active_game *backup;
2244 active_game *moveup = Active_game_head;
2246 // traverse through the entire list if any items exist
2250 if((moveup->heard_from_timer != -1) && (timestamp_elapsed(moveup->heard_from_timer))){
2251 Active_game_count--;
2253 // if this is the head of the list
2254 if(moveup == Active_game_head){
2255 // if this is the _only_ item on the list
2256 if(moveup->next == Active_game_head){
2257 // handle any gui details related to deleting this item
2258 multi_join_handle_item_cull(Active_game_head, count);
2260 free(Active_game_head);
2261 Active_game_head = NULL;
2264 // if there are other items on the list
2266 // handle any gui details related to deleting this item
2267 multi_join_handle_item_cull(moveup, count);
2269 Active_game_head = moveup->next;
2270 Active_game_head->prev = moveup->prev;
2271 Active_game_head->prev->next = Active_game_head;
2273 moveup = Active_game_head;
2276 // if its somewhere else on the list
2278 // handle any gui details related to deleting this item
2279 multi_join_handle_item_cull(moveup, count);
2281 // if its the last item on the list
2282 moveup->next->prev = moveup->prev;
2283 moveup->prev->next = moveup->next;
2285 // if it was the last element on the list, return
2286 if(moveup->next == Active_game_head){
2290 backup = moveup->next;
2296 moveup = moveup->next;
2299 } while(moveup != Active_game_head);
2303 // deep magic begins here.
2304 void multi_join_handle_item_cull(active_game *item, int item_index)
2306 // if this is the only item on the list, unset everything
2307 if(item->next == item){
2308 Multi_join_list_selected = -1;
2309 Multi_join_selected_item = NULL;
2312 Multi_join_slider.set_numberItems(0);
2314 MJ_LIST_START_SET(-1);
2315 Multi_join_list_start_item = NULL;
2321 // see if we should be adjusting our currently selected item
2322 if(item_index <= Multi_join_list_selected){
2323 // the selected item is the head of the list
2324 if(Multi_join_selected_item == Active_game_head){
2325 // move the pointer up since this item is about to be destroyed
2326 Multi_join_selected_item = Multi_join_selected_item->next;
2328 // if this is the item being deleted, select the previous one
2329 if(item == Multi_join_selected_item){
2331 Multi_join_selected_item = Multi_join_selected_item->prev;
2333 // decrement the selected index by 1
2334 Multi_join_list_selected--;
2336 // now we know its a previous item, so our pointer stays the same but our index goes down by one, since there will be
2337 // 1 less item on the list
2339 // decrement the selected index by 1
2340 Multi_join_list_selected--;
2345 // see if we should be adjusting out current start position
2346 if(item_index <= Multi_join_list_start){
2347 // the start position is the head of the list
2348 if(Multi_join_list_start_item == Active_game_head){
2349 // move the pointer up since this item is about to be destroyed
2350 Multi_join_list_start_item = Multi_join_list_start_item->next;
2352 // if this is the item being deleted, select the previous one
2353 if(item == Multi_join_list_start_item){
2354 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2356 // decrement the starting index by 1
2357 MJ_LIST_START_DEC();
2359 // but decrement the starting index by 1
2360 MJ_LIST_START_DEC();
2365 // maybe go back up a bit so that we always have a full page of items
2366 if(Active_game_count > Mj_max_game_items[gr_screen.res]){
2367 while((Active_game_count - Multi_join_list_start) < Mj_max_game_items[gr_screen.res]){
2368 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2369 MJ_LIST_START_DEC();
2373 // set slider location
2375 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);
2376 Multi_join_slider.force_currentItem(Multi_join_list_start);
2380 void multi_join_send_join_request(int as_observer)
2382 // don't do anything if we have no items selected
2383 if(Multi_join_selected_item == NULL){
2387 // 5/26/98 -- for team v team games, don't allow ingame joining :-(
2388 if ( (Multi_join_selected_item->flags & AG_FLAG_TEAMS) && (Multi_join_selected_item->flags & (AG_FLAG_PAUSE|AG_FLAG_IN_MISSION)) ) {
2389 popup(0, 1, POPUP_OK, XSTR("Joining ingame is currently not allowed for team vs. team games",772));
2393 memset(&Multi_join_request,0,sizeof(join_request));
2395 // if the netgame is in password mode, put up a request for the password
2396 if(Multi_join_selected_item->flags & AG_FLAG_PASSWD){
2397 if(!multi_passwd_popup(Multi_join_request.passwd)){
2401 nprintf(("Password : %s\n",Multi_join_request.passwd));
2404 // fill out the join request struct
2405 strcpy(Multi_join_request.callsign,Player->callsign);
2406 if(strlen(Player->image_filename) > 0){
2407 strcpy(Multi_join_request.image_filename, Player->image_filename);
2410 if(strlen(Player->squad_filename) > 0){
2411 strcpy(Multi_join_request.squad_filename, Player->squad_filename);
2415 // tracker id (if any)
2416 Multi_join_request.tracker_id = Multi_tracker_id;
2418 // player's rank (at least, what he wants you to _believe_)
2419 Multi_join_request.player_rank = (ubyte)Player->stats.rank;
2422 Multi_join_request.flags = 0;
2424 Multi_join_request.flags |= JOIN_FLAG_AS_OBSERVER;
2427 // if the player has hacked data
2428 if(game_hacked_data()){
2429 Multi_join_request.flags |= JOIN_FLAG_HAXOR;
2434 strncpy(Multi_join_request.pxo_squad_name, Multi_tracker_squad_name, LOGIN_LEN);
2437 // version of this server
2438 Multi_join_request.version = MULTI_FS_SERVER_VERSION;
2440 // server compatible version
2441 Multi_join_request.comp_version = MULTI_FS_SERVER_COMPATIBLE_VERSION;
2443 // his local player options
2444 memcpy(&Multi_join_request.player_options,&Player->m_local_options,sizeof(multi_local_options));
2446 // set the server address for the netgame
2447 memcpy(&Netgame.server_addr,&Multi_join_selected_item->server_addr,sizeof(net_addr));
2449 // send a join request to the guy
2450 send_join_packet(&Multi_join_selected_item->server_addr,&Multi_join_request);
2453 Multi_join_sent_stamp = timestamp(MULTI_JOIN_SENT_WAIT);
2456 multi_common_add_notify(XSTR("Sending join request...",773));
2459 void multi_join_create_game()
2461 // maybe warn the player about possible crappy server conditions
2462 if(!multi_join_maybe_warn()){
2466 // make sure to flag ourself as being the master
2467 Net_player->flags |= (NETINFO_FLAG_AM_MASTER | NETINFO_FLAG_GAME_HOST);
2468 Net_player->state = NETPLAYER_STATE_HOST_SETUP;
2470 // if we're in PXO mode, mark it down in our player struct
2471 if(MULTI_IS_TRACKER_GAME){
2472 Player->flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
2473 Player->save_flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
2475 // otherwise, if he's already played PXO games, warn him
2478 if(Player->flags & PLAYER_FLAGS_HAS_PLAYED_PXO){
2479 if(!multi_join_warn_pxo()){
2486 gameseq_post_event(GS_EVENT_MULTI_START_GAME);
2487 gamesnd_play_iface(SND_USER_SELECT);
2490 void multi_join_reset_join_stamp()
2492 // unset the timestamp here so the user can immediately send another join request
2493 Multi_join_sent_stamp = -1;
2494 multi_common_add_notify("");
2497 void multi_join_blit_top_stuff()
2499 // blit the cd icon if he has one
2500 if(Multi_has_cd && (Multi_common_icons[MICON_CD] != -1)){
2503 bm_get_info(Multi_common_icons[MICON_CD], &cd_w, NULL, NULL, NULL, NULL);
2505 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
2506 gr_bitmap((gr_screen.max_w / 2) - (cd_w / 2), Mj_cd_coords[gr_screen.res]);
2510 #define CW_CODE_CANCEL 0 // cancel the action
2511 #define CW_CODE_OK 1 // continue anyway
2512 #define CW_CODE_INFO 2 // gimme some more information
2514 #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)
2515 #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)
2517 #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)
2518 #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)
2520 int multi_join_warn_update_low(int code)
2524 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),LOW_WARN_TEXT);
2527 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),LOW_INFO_TEXT);
2530 return CW_CODE_CANCEL;
2533 int multi_join_warn_update_medium(int code)
2537 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),MED_WARN_TEXT);
2540 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),MED_INFO_TEXT);
2543 return CW_CODE_CANCEL;
2546 int multi_join_maybe_warn()
2550 // if the player is set for low updates
2551 if(Player->m_local_options.obj_update_level == OBJ_UPDATE_LOW){
2554 code = multi_join_warn_update_low(code);
2555 } while((code != CW_CODE_CANCEL) && (code != CW_CODE_OK));
2560 // if the player is set for medium updates
2561 else if(Player->m_local_options.obj_update_level == OBJ_UPDATE_MEDIUM){
2564 code = multi_join_warn_update_medium(code);
2565 } while((code != CW_CODE_CANCEL) && (code != CW_CODE_OK));
2573 int multi_join_warn_pxo()
2575 // 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;
2579 void multi_join_blit_protocol()
2581 gr_set_color_fast(&Color_bright);
2583 switch(Socket_type){
2586 gr_string(5, 2, "TCP");
2590 gr_string(5, 2, "IPX");
2596 // -------------------------------------------------------------------------------------------------
2598 // MULTIPLAYER START GAME screen
2603 #define MULTI_SG_PALETTE "InterfacePalette"
2605 static char *Multi_sg_bitmap_fname[GR_NUM_RESOLUTIONS] = {
2606 "MultiStartGame", // GR_640
2607 "2_MultiStartGame" // GR_1024
2610 static char *Multi_sg_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
2611 "MultiStartGame-M", // GR_640
2612 "2_MultiStartGame-M" // GR_1024
2617 int Multi_sg_rank_max_display[GR_NUM_RESOLUTIONS] = {
2622 // constants for coordinate look ups
2623 #define MSG_X_COORD 0
2624 #define MSG_Y_COORD 1
2625 #define MSG_W_COORD 2
2626 #define MSG_H_COORD 3
2630 // input password field
2631 int Msg_passwd_coords[GR_NUM_RESOLUTIONS][4] = {
2644 // input game title field
2645 int Msg_title_coords[GR_NUM_RESOLUTIONS][4] = {
2658 // rank selected field
2659 int Msg_rank_sel_coords[GR_NUM_RESOLUTIONS][4] = {
2673 int Msg_rank_list_coords[GR_NUM_RESOLUTIONS][4] = {
2689 #define MULTI_SG_NUM_BUTTONS 12
2690 #define MSG_OPEN_GAME 0
2691 #define MSG_CLOSED_GAME 1
2692 #define MSG_PASSWD_GAME 2
2693 #define MSG_RESTRICTED_GAME 3
2694 #define MSG_RANK_SET_GAME 4
2695 #define MSG_RANK_SCROLL_UP 5
2696 #define MSG_RANK_SCROLL_DOWN 6
2697 #define MSG_RANK_ABOVE 7
2698 #define MSG_RANK_BELOW 8
2700 #define MSG_OPTIONS 10
2701 #define MSG_ACCEPT 11
2703 #define MULTI_SG_NUM_BUTTONS 10
2704 #define MSG_OPEN_GAME 0
2705 //#define MSG_CLOSED_GAME 1
2706 //#define MSG_RESTRICTED_GAME 2
2707 #define MSG_PASSWD_GAME 1
2708 #define MSG_RANK_SET_GAME 2
2709 #define MSG_RANK_SCROLL_UP 3
2710 #define MSG_RANK_SCROLL_DOWN 4
2711 #define MSG_RANK_ABOVE 5
2712 #define MSG_RANK_BELOW 6
2714 #define MSG_OPTIONS 8
2715 #define MSG_ACCEPT 9
2718 UI_WINDOW Multi_sg_window; // the window object for the join screen
2719 UI_BUTTON Multi_sg_rank_button; // for selecting the rank marker
2720 UI_INPUTBOX Multi_sg_game_name; // for Netgame.name
2721 UI_INPUTBOX Multi_sg_game_passwd; // for Netgame.passwd
2722 int Multi_sg_bitmap; // the background bitmap
2724 ui_button_info Multi_sg_buttons[GR_NUM_RESOLUTIONS][MULTI_SG_NUM_BUTTONS] = {
2727 ui_button_info("MSG_00", 75, 111, -1, -1, 0),
2728 ui_button_info("MSG_01", 75, 139, -1, -1, 1),
2729 ui_button_info("MSG_02", 75, 164, -1, -1, 2),
2730 ui_button_info("MSG_03", 75, 199, -1, -1, 3),
2731 ui_button_info("MSG_04", 75, 243, -1, -1, 4),
2732 ui_button_info("MSG_05", 376, 231, -1, -1, 5),
2733 ui_button_info("MSG_06", 376, 258, -1, -1, 6),
2734 ui_button_info("MSG_07", 376, 291, -1, -1, 7),
2735 ui_button_info("MSG_08", 376, 320, -1, -1, 8),
2736 ui_button_info("MSG_09", 469, 427, -1, -1, 9),
2737 ui_button_info("MSG_10", 447, 452, -1, -1, 10),
2738 ui_button_info("MSG_11", 561, 411, -1, -1, 11),
2740 ui_button_info("MSG_00", 1, 184, 34, 191, 2), // open
2741 // ui_button_info("MSG_01", 1, 159, 34, 166, 1), // closed
2742 // ui_button_info("MSG_02", 1, 184, 34, 191, 2), // restricted
2743 ui_button_info("MSG_03", 1, 209, 34, 218, 3), // password
2744 ui_button_info("MSG_04", 1, 257, 34, 266, 4), // rank set
2745 ui_button_info("MSG_05", 1, 282, -1, -1, 5), // rank scroll up
2746 ui_button_info("MSG_06", 1, 307, -1, -1, 6), // rank scroll down
2747 ui_button_info("MSG_07", 177, 282, 210, 290, 7), // rank above
2748 ui_button_info("MSG_08", 177, 307, 210, 315, 8), // rank below
2749 ui_button_info("MSG_09", 536, 429, 500, 440, 9), // help
2750 ui_button_info("MSG_10", 536, 454, 479, 464, 10), // options
2751 ui_button_info("MSG_11", 576, 432, 571, 415, 11), // accept
2755 ui_button_info("2_MSG_00", 2, 295, 51, 307, 2), // open
2756 // ui_button_info("2_MSG_01", 2, 254, 51, 267, 1), // closed
2757 // ui_button_info("2_MSG_02", 2, 295, 51, 307, 2), // restricted
2758 ui_button_info("2_MSG_03", 2, 335, 51, 350, 3), // password
2759 ui_button_info("2_MSG_04", 2, 412, 51, 426, 4), // rank set
2760 ui_button_info("2_MSG_05", 2, 452, -1, -1, 5), // rank scroll up
2761 ui_button_info("2_MSG_06", 2, 492, -1, -1, 6), // rank scroll down
2762 ui_button_info("2_MSG_07", 284, 452, 335, 465, 7), // rank above
2763 ui_button_info("2_MSG_08", 284, 492, 335, 505, 8), // rank below
2764 ui_button_info("2_MSG_09", 858, 687, 817, 706, 9), // help
2765 ui_button_info("2_MSG_10", 858, 728, 797, 743, 10), // options
2766 ui_button_info("2_MSG_11", 921, 692, 921, 664, 11), // accept
2767 #ifdef MAKE_FS1 // filler for extra FS1 buttons
2768 ui_button_info("none", -1, -1, -1, -1, -1),
2769 ui_button_info("none", -1, -1, -1, -1, -1),
2775 #define MULTI_SG_NUM_TEXT 0
2777 #define MULTI_SG_NUM_TEXT 11
2779 UI_XSTR Multi_sg_text[GR_NUM_RESOLUTIONS][MULTI_SG_NUM_TEXT] = {
2781 // not needed for FS1
2783 {"Open", 1322, 34, 191, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_OPEN_GAME].button},
2784 // {"Closed", 1323, 34, 166, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_CLOSED_GAME].button},
2785 // {"Restricted", 1324, 34, 191, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RESTRICTED_GAME].button},
2786 {"Password Protected", 1325, 34, 218, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_PASSWD_GAME].button},
2787 {"Allow Rank", 1326, 34, 266, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_SET_GAME].button},
2788 {"Above", 1327, 210, 290, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_ABOVE].button},
2789 {"Below", 1328, 210, 315, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_BELOW].button},
2790 {"Help", 928, 500, 440, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_HELP].button},
2791 {"Options", 1036, 479, 464, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_OPTIONS].button},
2792 {"Accept", 1035, 571, 415, UI_XSTR_COLOR_PINK, -1, &Multi_sg_buttons[0][MSG_ACCEPT].button},
2793 {"Start Game", 1329, 26, 10, UI_XSTR_COLOR_GREEN, -1, NULL},
2794 {"Title", 1330, 26, 31, UI_XSTR_COLOR_GREEN, -1, NULL},
2795 {"Game Type", 1331, 12, 165, UI_XSTR_COLOR_GREEN, -1, NULL},
2799 // not needed for FS1
2801 {"Open", 1322, 51, 307, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_OPEN_GAME].button},
2802 // {"Closed", 1323, 51, 267, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_CLOSED_GAME].button},
2803 // {"Restricted", 1324, 51, 307, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RESTRICTED_GAME].button},
2804 {"Password Protected", 1325, 51, 350, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_PASSWD_GAME].button},
2805 {"Allow Rank", 1326, 51, 426, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_SET_GAME].button},
2806 {"Above", 1327, 335, 465, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_ABOVE].button},
2807 {"Below", 1328, 335, 505, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_BELOW].button},
2808 {"Help", 928, 817, 706, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_HELP].button},
2809 {"Options", 1036, 797, 743, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_OPTIONS].button},
2810 {"Accept", 1035, 921, 664, UI_XSTR_COLOR_PINK, -1, &Multi_sg_buttons[1][MSG_ACCEPT].button},
2811 {"Start Game", 1329, 42, 22, UI_XSTR_COLOR_GREEN, -1, NULL},
2812 {"Title", 1330, 42, 50, UI_XSTR_COLOR_GREEN, -1, NULL},
2813 {"Game Type", 1331, 20, 264, UI_XSTR_COLOR_GREEN, -1, NULL},
2818 // starting index for displaying ranks
2819 int Multi_sg_rank_start;
2820 int Multi_sg_rank_select;
2822 // netgame pointer to indirect through
2823 netgame_info *Multi_sg_netgame;
2825 // hold temporary values in this structure when on a standalone server
2826 netgame_info Multi_sg_netgame_temp;
2828 // forward declarations
2829 void multi_sg_check_buttons();
2830 void multi_sg_button_pressed(int n);
2831 void multi_sg_init_gamenet();
2832 void multi_sg_draw_radio_buttons();
2833 void multi_sg_rank_scroll_up();
2834 void multi_sg_rank_scroll_down();
2835 void multi_sg_rank_display_stuff();
2836 void multi_sg_rank_process_select();
2837 void multi_sg_rank_build_name(char *in,char *out);
2838 void multi_sg_check_passwd();
2839 void multi_sg_check_name();
2840 void multi_sg_release_passwd();
2841 int multi_sg_rank_select_valid(int rank);
2842 void multi_sg_select_rank_default();
2844 // function which takes a rank name and returns the index. Useful for commandline options
2845 // for above and below rank. We return the index of the rank in the Ranks[] array. If
2846 // the rank isn't found, we return -1
2847 int multi_start_game_rank_from_name( char *rank ) {
2851 for ( i = 0; i <= MAX_FREESPACE1_RANK; i++ ) {
2853 for ( i = 0; i <= MAX_FREESPACE2_RANK; i++ ) {
2855 if ( !stricmp(Ranks[i].name, rank) ) {
2863 void multi_start_game_init()
2867 // initialize the gamenet
2868 multi_sg_init_gamenet();
2870 // create the interface window
2871 Multi_sg_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
2872 Multi_sg_window.set_mask_bmap(Multi_sg_bitmap_mask_fname[gr_screen.res]);
2874 // load the background bitmap
2875 Multi_sg_bitmap = bm_load(Multi_sg_bitmap_fname[gr_screen.res]);
2876 if(Multi_sg_bitmap < 0){
2877 // we failed to load the bitmap - this is very bad
2881 // initialize the common notification messaging
2882 multi_common_notify_init();
2884 // initialize the common text area
2885 multi_common_set_text("");
2887 // use the common interface palette
2888 multi_common_set_palette();
2890 // create the interface buttons
2891 for(idx=0; idx<MULTI_SG_NUM_BUTTONS; idx++){
2892 // create the object
2893 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);
2895 // set the sound to play when highlighted
2896 Multi_sg_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
2898 // set the ani for the button
2899 Multi_sg_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sg_buttons[gr_screen.res][idx].filename);
2902 Multi_sg_buttons[gr_screen.res][idx].button.link_hotspot(Multi_sg_buttons[gr_screen.res][idx].hotspot);
2906 for(idx=0; idx<MULTI_SG_NUM_TEXT; idx++){
2907 Multi_sg_window.add_XSTR(&Multi_sg_text[gr_screen.res][idx]);
2910 // load the help overlay
2911 help_overlay_load(MULTI_START_OVERLAY);
2912 help_overlay_set_state(MULTI_START_OVERLAY,0);
2914 // intiialize the rank selection items
2915 multi_sg_select_rank_default();
2916 Multi_sg_rank_start = Multi_sg_rank_select;
2918 // create the rank select button
2919 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);
2920 Multi_sg_rank_button.hide();
2922 // create the netgame name input box
2923 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);
2925 // create the netgame password input box, and disable it by default
2926 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);
2927 Multi_sg_game_passwd.hide();
2928 Multi_sg_game_passwd.disable();
2930 // set the netgame text to this gadget and make it have focus
2931 Multi_sg_game_name.set_text(Multi_sg_netgame->name);
2932 Multi_sg_game_name.set_focus();
2934 // if starting a netgame, set the name of the game and any other options that are appropriate
2935 if ( Cmdline_start_netgame ) {
2936 if ( Cmdline_game_name != NULL ) {
2937 strcpy( Multi_sg_netgame->name, Cmdline_game_name );
2938 Multi_sg_game_name.set_text(Multi_sg_netgame->name);
2941 // deal with the different game types -- only one should even be active, so we will just go down
2942 // the line. Last one wins.
2943 if ( Cmdline_closed_game ) {
2944 Multi_sg_netgame->mode = NG_MODE_CLOSED;
2945 } else if ( Cmdline_restricted_game ) {
2946 Multi_sg_netgame->mode = NG_MODE_RESTRICTED;
2947 } else if ( Cmdline_game_password != NULL ) {
2948 Multi_sg_netgame->mode = NG_MODE_PASSWORD;
2949 strcpy(Multi_sg_netgame->passwd, Cmdline_game_password);
2950 Multi_sg_game_passwd.set_text(Multi_sg_netgame->passwd);
2953 // deal with rank above and rank below
2954 if ( (Cmdline_rank_above != NULL) || (Cmdline_rank_below != NULL) ) {
2958 if ( Cmdline_rank_above != NULL ) {
2959 rank_str = Cmdline_rank_above;
2961 rank_str = Cmdline_rank_below;
2964 // try and get the rank index from the name -- if found, then set the rank base
2965 // and the game type. apparently we only support either above or below, not both
2966 // together, so I make a random choice
2967 rank = multi_start_game_rank_from_name( rank_str );
2969 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
2971 // now an arbitrary decision
2972 if ( Cmdline_rank_above != NULL ) {
2973 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
2975 Multi_sg_netgame->mode = NG_MODE_RANK_BELOW;
2980 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
2984 void multi_start_game_do()
2986 // return here since we will be moving to the next stage anyway -- I don't want to see the backgrounds of
2987 // all the screens for < 1 second for every screen we automatically move to.
2988 if ( Cmdline_start_netgame ) {
2992 int k = Multi_sg_window.process();
2994 // process any keypresses
2997 if(help_overlay_active(MULTI_START_OVERLAY)){
2998 help_overlay_set_state(MULTI_START_OVERLAY,0);
3000 gamesnd_play_iface(SND_USER_SELECT);
3001 multi_quit_game(PROMPT_NONE);
3006 case KEY_LCTRL + KEY_ENTER :
3007 case KEY_RCTRL + KEY_ENTER :
3008 gamesnd_play_iface(SND_COMMIT_PRESSED);
3009 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
3013 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
3014 help_overlay_set_state(MULTI_START_OVERLAY, 0);
3017 // check to see if the user has selected a different rank
3018 multi_sg_rank_process_select();
3020 // check any button presses
3021 multi_sg_check_buttons();
3023 // check to see if any of the input boxes have changed, and update the appropriate Netgame fields if necessary
3024 multi_sg_check_passwd();
3025 multi_sg_check_name();
3027 // draw the background, etc
3029 GR_MAYBE_CLEAR_RES(Multi_sg_bitmap);
3030 if(Multi_sg_bitmap != -1){
3031 gr_set_bitmap(Multi_sg_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
3034 Multi_sg_window.draw();
3036 // display rank stuff
3037 multi_sg_rank_display_stuff();
3039 // display any pending notification messages
3040 multi_common_notify_do();
3042 // draw all radio button
3043 multi_sg_draw_radio_buttons();
3045 // draw the help overlay
3046 help_overlay_maybe_blit(MULTI_START_OVERLAY);
3052 void multi_start_game_close()
3054 // if i'm the host on a standalone server, send him my start game options (passwd, mode, etc)
3055 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
3056 multi_options_update_start_game(Multi_sg_netgame);
3059 // unload any bitmaps
3060 if(!bm_unload(Multi_sg_bitmap)){
3061 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_sg_bitmap_fname[gr_screen.res]));
3064 // unload the help overlay
3065 help_overlay_unload(MULTI_START_OVERLAY);
3067 // destroy the UI_WINDOW
3068 Multi_sg_window.destroy();
3071 void multi_sg_check_buttons()
3074 for(idx=0;idx<MULTI_SG_NUM_BUTTONS;idx++){
3075 // we only really need to check for one button pressed at a time, so we can break after
3077 if(Multi_sg_buttons[gr_screen.res][idx].button.pressed()){
3078 multi_sg_button_pressed(idx);
3084 void multi_sg_button_pressed(int n)
3087 // go to the options screen
3089 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
3094 if(!help_overlay_active(MULTI_START_OVERLAY)){
3095 help_overlay_set_state(MULTI_START_OVERLAY,1);
3097 help_overlay_set_state(MULTI_START_OVERLAY,0);
3101 // the open button was pressed
3103 // if the closed option is selected
3104 if(Multi_sg_netgame->mode != NG_MODE_OPEN){
3105 Multi_sg_netgame->mode = NG_MODE_OPEN;
3107 gamesnd_play_iface(SND_USER_SELECT);
3109 // release the password control if necessary
3110 multi_sg_release_passwd();
3112 // if its already selected
3114 gamesnd_play_iface(SND_GENERAL_FAIL);
3119 // the open button was pressed
3120 case MSG_CLOSED_GAME:
3121 // if the closed option is selected
3122 if(Multi_sg_netgame->mode != NG_MODE_CLOSED){
3123 Multi_sg_netgame->mode = NG_MODE_CLOSED;
3125 gamesnd_play_iface(SND_USER_SELECT);
3127 // release the password control if necessary
3128 multi_sg_release_passwd();
3130 // if its already selected
3132 gamesnd_play_iface(SND_GENERAL_FAIL);
3137 // toggle password protection
3138 case MSG_PASSWD_GAME:
3139 // if we selected it
3140 if(Multi_sg_netgame->mode != NG_MODE_PASSWORD){
3141 gamesnd_play_iface(SND_USER_SELECT);
3143 Multi_sg_game_passwd.enable();
3144 Multi_sg_game_passwd.unhide();
3145 Multi_sg_game_passwd.set_focus();
3147 Multi_sg_netgame->mode = NG_MODE_PASSWORD;
3149 // copy in the current network password
3150 Multi_sg_game_passwd.set_text(Multi_sg_netgame->passwd);
3152 gamesnd_play_iface(SND_GENERAL_FAIL);
3157 // toggle "restricted" on or off
3158 case MSG_RESTRICTED_GAME:
3159 if(Multi_sg_netgame->mode != NG_MODE_RESTRICTED){
3160 gamesnd_play_iface(SND_USER_SELECT);
3161 Multi_sg_netgame->mode = NG_MODE_RESTRICTED;
3163 // release the password control if necessary
3164 multi_sg_release_passwd();
3166 gamesnd_play_iface(SND_GENERAL_FAIL);
3171 // turn off all rank requirements
3172 case MSG_RANK_SET_GAME:
3173 // if either is set, then turn then both off
3174 if((Multi_sg_netgame->mode != NG_MODE_RANK_BELOW) && (Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE)){
3175 gamesnd_play_iface(SND_USER_SELECT);
3177 // set it to the default case if we're turning it off
3178 multi_sg_select_rank_default();
3179 Multi_sg_rank_start = Multi_sg_rank_select;
3181 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
3183 // release the password control if necessary
3184 multi_sg_release_passwd();
3186 gamesnd_play_iface(SND_GENERAL_FAIL);
3190 // rank above was pressed
3191 case MSG_RANK_ABOVE :
3192 if((Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE) || (Multi_sg_netgame->mode == NG_MODE_RANK_BELOW)){
3193 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
3195 // select the first item
3196 multi_sg_select_rank_default();
3197 Multi_sg_rank_start = Multi_sg_rank_select;
3200 gamesnd_play_iface(SND_USER_SELECT);
3202 gamesnd_play_iface(SND_GENERAL_FAIL);
3206 // rank below was pressed
3207 case MSG_RANK_BELOW :
3208 if((Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE) || (Multi_sg_netgame->mode == NG_MODE_RANK_BELOW)){
3209 Multi_sg_netgame->mode = NG_MODE_RANK_BELOW;
3211 // select the first item
3212 multi_sg_select_rank_default();
3213 Multi_sg_rank_start = Multi_sg_rank_select;
3216 gamesnd_play_iface(SND_USER_SELECT);
3218 gamesnd_play_iface(SND_GENERAL_FAIL);
3222 // scroll the rank list up
3223 case MSG_RANK_SCROLL_UP:
3224 multi_sg_rank_scroll_up();
3227 // scroll the rank list down
3228 case MSG_RANK_SCROLL_DOWN:
3229 multi_sg_rank_scroll_down();
3232 // move to the create game screen
3234 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
3235 gamesnd_play_iface(SND_COMMIT_PRESSED);
3239 gamesnd_play_iface(SND_GENERAL_FAIL);
3240 multi_common_add_notify(XSTR("Not implemented yet!",760));
3245 // NOTE : this is where all Netgame initialization should take place on the host
3246 void multi_sg_init_gamenet()
3248 char buf[128],out_name[128];
3250 net_player *server_save;
3252 // back this data up in case we are already connected to a standalone
3253 memcpy(&save,&Netgame.server_addr,sizeof(net_addr));
3254 server_save = Netgame.server;
3256 // remove campaign flags
3257 Game_mode &= ~(GM_CAMPAIGN_MODE);
3259 // clear out the Netgame structure and start filling in the values
3260 memset( &Netgame, 0, sizeof(Netgame) );
3261 memset( &Multi_sg_netgame_temp, 0, sizeof(netgame_info) );
3263 // if we're on the standalone, we're not the server, so we don't care about setting the netgame state
3264 if(Net_player->state != NETPLAYER_STATE_STD_HOST_SETUP){
3265 Netgame.game_state = NETGAME_STATE_HOST_SETUP;
3266 Multi_sg_netgame = &Netgame;
3269 ml_string(NOX("Starting netgame as Host/Server"));
3271 Multi_sg_netgame = &Multi_sg_netgame_temp;
3275 memcpy(&temp_addr.s_addr, &Netgame.server_addr, 4);
3276 char *server_addr = inet_ntoa(temp_addr);
3277 ml_printf(NOX("Starting netgame as Host on Standalone server : %s"), (server_addr == NULL) ? NOX("Unknown") : server_addr);
3280 Net_player->tracker_player_id = Multi_tracker_id;
3282 Multi_sg_netgame->security = (rand() % 32766) + 1; // get some random security number
3283 Multi_sg_netgame->mode = NG_MODE_OPEN;
3284 Multi_sg_netgame->rank_base = RANK_ENSIGN;
3285 if(Multi_sg_netgame->security < 16){
3286 Multi_sg_netgame->security += 16;
3289 // set the version_info field
3290 Multi_sg_netgame->version_info = NG_VERSION_ID;
3292 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
3293 Netgame.host = Net_player;
3296 // set the default netgame flags
3297 Multi_sg_netgame->flags = 0;
3299 // intialize endgame stuff
3300 multi_endgame_init();
3302 // load in my netgame options
3303 multi_options_netgame_load(&Netgame.options);
3305 // load my local netplayer options
3306 multi_options_local_load(&Net_player->p_info.options, Net_player);
3308 // setup the default game name, taking care of string length and player callsigns
3309 memset(out_name,0,128);
3311 pilot_format_callsign_personal(Player->callsign,out_name);
3312 sprintf(buf, XSTR("%s game",782), out_name); // [[ %s will be a pilot's name ]]
3313 if ( strlen(buf) > MAX_GAMENAME_LEN ){
3314 strcpy(buf, XSTR("Temporary name",783));
3316 strcpy(Multi_sg_netgame->name, buf);
3318 // set the default qos and duration
3319 multi_voice_maybe_update_vars(Netgame.options.voice_qos,Netgame.options.voice_record_time);
3321 // make sure to set the server correctly (me or the standalone)
3322 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3323 memcpy(&Netgame.server_addr, &Psnet_my_addr,sizeof(net_addr));
3324 Netgame.server = Net_player;
3325 Net_player->player_id = multi_get_new_id();
3327 // setup debug flags
3328 Netgame.debug_flags = 0;
3330 if(!Cmdline_server_firing){
3331 Netgame.debug_flags |= NETD_FLAG_CLIENT_FIRING;
3333 if(!Cmdline_client_dodamage){
3334 Netgame.debug_flags |= NETD_FLAG_CLIENT_NODAMAGE;
3338 memcpy(&Netgame.server_addr,&save,sizeof(net_addr));
3339 Netgame.server = server_save;
3342 // if I have a cd or not
3344 Net_player->flags |= NETINFO_FLAG_HAS_CD;
3347 // if I have hacked data
3348 if(game_hacked_data()){
3349 Net_player->flags |= NETINFO_FLAG_HAXOR;
3352 // assign my player struct and other data
3353 Net_player->flags |= (NETINFO_FLAG_CONNECTED | NETINFO_FLAG_DO_NETWORKING);
3354 Net_player->s_info.voice_token_timestamp = -1;
3356 // if we're supposed to flush our cache directory, do so now
3357 if(Net_player->p_info.options.flags & MLO_FLAG_FLUSH_CACHE){
3358 multi_flush_multidata_cache();
3361 ml_string(NOX("Flushing multi-data cache"));
3367 void multi_sg_draw_radio_buttons()
3369 // draw the appropriate radio button
3370 switch(Multi_sg_netgame->mode){
3372 Multi_sg_buttons[gr_screen.res][MSG_OPEN_GAME].button.draw_forced(2);
3376 case NG_MODE_CLOSED:
3377 Multi_sg_buttons[gr_screen.res][MSG_CLOSED_GAME].button.draw_forced(2);
3381 case NG_MODE_PASSWORD:
3382 Multi_sg_buttons[gr_screen.res][MSG_PASSWD_GAME].button.draw_forced(2);
3386 case NG_MODE_RESTRICTED:
3387 Multi_sg_buttons[gr_screen.res][MSG_RESTRICTED_GAME].button.draw_forced(2);
3391 case NG_MODE_RANK_ABOVE:
3392 Multi_sg_buttons[gr_screen.res][MSG_RANK_SET_GAME].button.draw_forced(2);
3393 Multi_sg_buttons[gr_screen.res][MSG_RANK_ABOVE].button.draw_forced(2);
3395 case NG_MODE_RANK_BELOW:
3396 Multi_sg_buttons[gr_screen.res][MSG_RANK_SET_GAME].button.draw_forced(2);
3397 Multi_sg_buttons[gr_screen.res][MSG_RANK_BELOW].button.draw_forced(2);
3402 void multi_sg_rank_scroll_up()
3404 // if he doesn't have either of the rank flags set, then ignore this
3405 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3409 if(Multi_sg_rank_start > 0){
3410 Multi_sg_rank_start--;
3411 gamesnd_play_iface(SND_SCROLL);
3413 gamesnd_play_iface(SND_GENERAL_FAIL);
3417 void multi_sg_rank_scroll_down()
3419 // if he doesn't have either of the rank flags set, then ignore this
3420 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3424 if((NUM_RANKS - Multi_sg_rank_start) > Multi_sg_rank_max_display[gr_screen.res]){
3425 Multi_sg_rank_start++;
3426 gamesnd_play_iface(SND_SCROLL);
3428 gamesnd_play_iface(SND_GENERAL_FAIL);
3432 void multi_sg_rank_display_stuff()
3437 // if he doesn't have either of the rank flags set, then ignore this
3438 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3442 // display the list of ranks
3443 y = Msg_rank_list_coords[gr_screen.res][MSG_Y_COORD];
3444 idx = Multi_sg_rank_start;
3446 while((count < NUM_RANKS) && (count < Multi_sg_rank_max_display[gr_screen.res]) && (idx < NUM_RANKS)){
3447 // if its the selected item, then color it differently
3448 if(idx == Multi_sg_rank_select){
3449 gr_set_color_fast(&Color_text_selected);
3451 gr_set_color_fast(&Color_text_normal);
3455 multi_sg_rank_build_name(Ranks[idx].name,rank_name);
3456 gr_string(Msg_rank_list_coords[gr_screen.res][MSG_X_COORD],y,rank_name);
3464 // display the selected rank
3466 gr_set_color_fast(&Color_bright);
3467 multi_sg_rank_build_name(Ranks[Multi_sg_netgame->rank_base].name,rank_name);
3468 gr_string(Msg_rank_sel_coords[gr_screen.res][MSG_X_COORD],Msg_rank_sel_coords[gr_screen.res][MSG_Y_COORD],rank_name);
3472 void multi_sg_rank_process_select()
3476 // if he doesn't have either of the rank flags set, then ignore this
3477 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3481 // see if he's clicked on an item on the rank list
3482 if(Multi_sg_rank_button.pressed()){
3484 Multi_sg_rank_button.get_mouse_pos(NULL,&y);
3487 if(item + Multi_sg_rank_start < NUM_RANKS){
3488 // evaluate whether this rank is valid for the guy to pick
3489 if(multi_sg_rank_select_valid(item + Multi_sg_rank_start)){
3490 gamesnd_play_iface(SND_USER_SELECT);
3492 Multi_sg_rank_select = item + Multi_sg_rank_start;
3494 // set the Netgame rank
3495 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
3497 gamesnd_play_iface(SND_GENERAL_FAIL);
3499 memset(string,0,255);
3500 sprintf(string,XSTR("Illegal value for a host of your rank (%s)\n",784),Ranks[Net_player->player->stats.rank].name);
3501 multi_common_add_notify(string);
3507 void multi_sg_rank_build_name(char *in,char *out)
3513 first = strtok(use," ");
3515 // just copy the string
3520 // if the first part of the string is lieutenant, then abbreivate it and tack on the rest of the string
3521 if (stricmp(first,XSTR("lieutenant",785)) == 0) {
3522 first = strtok(NULL, NOX("\n"));
3524 // if he's not just a plain lieutenant
3526 strcpy(out,XSTR("Lt. ",786)); // [[ lieutenant ]]
3529 // if he _is_ just a plain lieutenant
3538 void multi_sg_check_passwd()
3540 // check to see if the password input box has been pressed
3541 if(Multi_sg_game_passwd.changed()){
3542 Multi_sg_game_passwd.get_text(Multi_sg_netgame->passwd);
3546 void multi_sg_check_name()
3548 // check to see if the game name input box has been pressed
3549 if(Multi_sg_game_name.changed()){
3550 Multi_sg_game_name.get_text(Multi_sg_netgame->name);
3554 void multi_sg_release_passwd()
3556 // hide and disable the password input box
3557 Multi_sg_game_passwd.hide();
3558 Multi_sg_game_passwd.disable();
3560 // set the focus back to the name input box
3561 Multi_sg_game_name.set_focus();
3564 int multi_sg_rank_select_valid(int rank)
3567 if(Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE){
3568 if(Net_player->player->stats.rank >= rank){
3574 if(Net_player->player->stats.rank <= rank){
3582 void multi_sg_select_rank_default()
3584 // pick our rank for now
3585 Multi_sg_rank_select = Net_player->player->stats.rank;
3587 // set the Netgame rank
3588 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
3591 // -------------------------------------------------------------------------------------------------
3593 // MULTIPLAYER CREATE GAME screen
3598 char *Multi_create_bitmap_fname[GR_NUM_RESOLUTIONS] = {
3599 "MultiCreate", // GR_640
3600 "2_MultiCreate" // GR_1024
3603 char *Multi_create_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
3604 "MultiCreate-M", // GR_640
3605 "2_MultiCreate-M" // GR_1024
3608 char *Multi_create_loading_fname[GR_NUM_RESOLUTIONS] = {
3609 "PleaseWait", // GR_640
3610 "2_PleaseWait" // GR_1024
3614 #define MULTI_CREATE_NUM_BUTTONS 23
3617 #define MC_SHOW_ALL 0
3618 #define MC_SHOW_COOP 1
3619 #define MC_SHOW_TEAM 2
3620 #define MC_SHOW_DOGFIGHT 3
3621 #define MC_PXO_REFRESH 4
3622 #define MC_PILOT_INFO 5
3623 #define MC_SCROLL_LIST_UP 6
3624 #define MC_SCROLL_LIST_DOWN 7
3625 #define MC_SCROLL_PLAYERS_UP 8
3626 #define MC_SCROLL_PLAYERS_DOWN 9
3627 #define MC_MISSION_FILTER 10
3628 #define MC_CAMPAIGN_FILTER 11
3629 #define MC_CANCEL 12
3634 #define MC_SCROLL_INFO_UP 17
3635 #define MC_SCROLL_INFO_DOWN 18
3636 #define MC_HOST_OPTIONS 19
3638 #define MC_OPTIONS 21
3639 #define MC_ACCEPT 22
3642 UI_WINDOW Multi_create_window; // the window object for the create screen
3643 UI_BUTTON Multi_create_player_select_button; // for selecting players
3644 UI_BUTTON Multi_create_list_select_button; // for selecting missions/campaigns
3645 int Multi_create_bitmap; // the background bitmap
3646 UI_SLIDER2 Multi_create_slider; // for create list
3648 // constants for coordinate look ups
3649 #define MC_X_COORD 0
3650 #define MC_Y_COORD 1
3651 #define MC_W_COORD 2
3652 #define MC_H_COORD 3
3654 ui_button_info Multi_create_buttons[GR_NUM_RESOLUTIONS][MULTI_CREATE_NUM_BUTTONS] = {
3657 ui_button_info("MC_18", 34, 131, -1, -1, 18), // all
3658 ui_button_info("MC_19", 72, 131, -1, -1, 19), // coop
3659 ui_button_info("MC_20", 120, 131, -1, -1, 20), // team
3660 // ui_button_info("MC_21", 166, 131, -1, -1, 21), // dogfight
3661 ui_button_info("none", -1, -1, -1, -1, -1), // dogfight (not used)
3662 ui_button_info("none", -1, -1, -1, -1, -1), // pxo?
3663 ui_button_info("MC_26", 540, 114, -1, -1, 26), // pilot info
3664 ui_button_info("MC_03", 0, 187, -1, -1, 2), // scroll list up
3665 ui_button_info("MC_02", 0, 227, -1, -1, 3), // scroll list down
3666 ui_button_info("MC_04", 611, 182, -1, -1, 4), // scroll players up
3667 ui_button_info("MC_05", 611, 221, -1, -1, 5), // scroll players down
3668 ui_button_info("MC_06", 18, 322, -1, -1, 6), // mission filter
3669 ui_button_info("MC_07", 18, 344, -1, -1, 7), // campaign filter
3670 ui_button_info("MC_10", 317, 339, -1, -1, 10), // cancel
3671 ui_button_info("MC_14", 464, 350, -1, -1, 14), // team 1
3672 ui_button_info("MC_15", 498, 350, -1, -1, 15), // team 2
3673 ui_button_info("MC_16", 527, 346, -1, -1, 16), // kick
3674 ui_button_info("MC_17", 572, 346, -1, -1, 17), // close
3675 ui_button_info("MC_08", 0, 398, -1, -1, 8), // scroll mission info up
3676 ui_button_info("MC_09", 0, 435, -1, -1, 9), // scroll mission info down
3677 ui_button_info("MC_27", 447, 402, -1, -1, 27), // host options
3678 ui_button_info("MC_11", 510, 428, -1, -1, 11), // help
3679 ui_button_info("MC_12", 510, 453, -1, -1, 12), // options
3680 ui_button_info("Mc_13", 562, 412, -1, -1, 13), // commit
3682 ui_button_info("MC_00", 32, 129, 36, 158, 0), // show all missions
3683 ui_button_info("MC_01", 76, 129, 71, 158, 1), // show coop missions
3684 ui_button_info("MC_02", 121, 129, 119, 158, 2), // show team missions
3685 ui_button_info("MC_03", 164, 129, 166, 158, 3), // show dogfight missions
3686 ui_button_info("MC_04", 399, 129, 229, 130, 4), // pxo mission refresh
3687 ui_button_info("MC_05", 567, 123, 467, 132, 5), // pilot info
3688 ui_button_info("MC_06", 1, 161, -1, -1, 6), // scroll mission info up
3689 ui_button_info("MC_08", 1, 304, -1, -1, 8), // scroll mission info down
3690 ui_button_info("MC_09", 613, 160, -1, -1, 9), // scroll players up
3691 ui_button_info("MC_10", 613, 202, -1, -1, 10), // scroll players down
3692 ui_button_info("MC_11", 22, 346, 27, 376, 11), // mission filter
3693 ui_button_info("MC_12", 104, 346, 110, 376, 12), // campaign filter
3694 ui_button_info("MC_13", 392, 341, 328, 364, 13), // cancel
3695 ui_button_info("MC_14", 472, 352, 482, 381, 14), // team 0
3696 ui_button_info("MC_15", 506, 352, 514, 381, 15), // team 1
3697 ui_button_info("MC_16", 539, 346, 539, 381, 16), // kick
3698 ui_button_info("MC_17", 589, 346, 582, 381, 17), // close
3699 ui_button_info("MC_18", 1, 406, -1, -1, 18), // scroll list up
3700 ui_button_info("MC_19", 1, 447, -1, -1, 19), // scroll list down
3701 ui_button_info("MC_20", 499, 434, 436, 423, 20), // host options
3702 ui_button_info("MC_21", 534, 426, -1, -1, 21), // help
3703 ui_button_info("MC_22", 534, 452, -1, -1, 22), // options
3704 ui_button_info("MC_23", 571, 426, 572, 413, 23), // commit
3708 ui_button_info("2_MC_00", 51, 207, 61, 253, 0), // show all missions
3709 ui_button_info("2_MC_01", 122, 207, 124, 253, 1), // show coop missions
3710 ui_button_info("2_MC_02", 193, 207, 194, 253, 2), // show team missions
3711 ui_button_info("2_MC_03", 263, 207, 261, 253, 3), // show dogfight missions
3712 ui_button_info("2_MC_04", 639, 207, 479, 218, 4), // pxo mission refresh
3713 ui_button_info("2_MC_05", 907, 197, 748, 216, 5), // pilot info
3714 ui_button_info("2_MC_06", 1, 258, -1, -1, 6), // scroll mission info up
3715 ui_button_info("2_MC_08", 1, 487, -1, -1, 8), // scroll mission info down
3716 ui_button_info("2_MC_09", 981, 256, -1, -1, 9), // scroll players up
3717 ui_button_info("2_MC_10", 981, 323, -1, -1, 10), // scroll players down
3718 ui_button_info("2_MC_11", 35, 554, 46, 601, 11), // mission filter
3719 ui_button_info("2_MC_12", 166, 554, 174, 601, 12), // campaign filter
3720 ui_button_info("2_MC_13", 628, 545, 559, 582, 13), // cancel
3721 ui_button_info("2_MC_14", 756, 564, 772, 610, 14), // team 0
3722 ui_button_info("2_MC_15", 810, 564, 826, 610, 15), // team 1
3723 ui_button_info("2_MC_16", 862, 554, 872, 610, 16), // kick
3724 ui_button_info("2_MC_17", 943, 554, 949, 610, 17), // close
3725 ui_button_info("2_MC_18", 1, 649, -1, -1, 18), // scroll list up
3726 ui_button_info("2_MC_19", 1, 716, -1, -1, 19), // scroll list down
3727 ui_button_info("2_MC_20", 798, 695, 726, 667, 20), // host options
3728 ui_button_info("2_MC_21", 854, 681, -1, -1, 21), // help
3729 ui_button_info("2_MC_22", 854, 724, -1, -1, 22), // options
3730 ui_button_info("2_MC_23", 914, 681, 932, 667, 23), // commit
3735 #define MULTI_CREATE_NUM_TEXT 0
3737 #define MULTI_CREATE_NUM_TEXT 15
3739 UI_XSTR Multi_create_text[GR_NUM_RESOLUTIONS][MULTI_CREATE_NUM_BUTTONS] = {
3741 // not needed for FS1
3743 {"All", 1256, 36, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_ALL].button},
3744 {"Coop", 1257, 71, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_COOP].button},
3745 {"Team", 1258, 119, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_TEAM].button},
3746 {"Dogfight", 1259, 166, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_DOGFIGHT].button},
3747 {"Refresh Missions", 1260, 229, 130, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_PXO_REFRESH].button},
3748 {"Pilot Info", 1261, 467, 132, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_PILOT_INFO].button},
3749 {"Missions", 1262, 27, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_MISSION_FILTER].button},
3750 {"Campaigns", 1263, 110, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_CAMPAIGN_FILTER].button},
3751 {"Cancel", 387, 328, 364, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_CANCEL].button},
3752 {"1", 1264, 482, 381, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_TEAM0].button},
3753 {"2", 1265, 514, 381, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_TEAM1].button},
3754 {"Kick", 1266, 539, 381, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_KICK].button},
3755 {"Close", 1508, 582, 381, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_CLOSE].button},
3756 {"Host Options", 1267, 436, 423, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_HOST_OPTIONS].button},
3757 {"Commit", 1062, 572, 413, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_ACCEPT].button}
3761 // not needed for FS1
3763 {"All", 1256, 61, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_ALL].button},
3764 {"Coop", 1257, 124, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_COOP].button},
3765 {"Team", 1258, 194, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_TEAM].button},
3766 {"Dogfight", 1259, 261, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_DOGFIGHT].button},
3767 {"Refresh Missions", 1260, 501, 218, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_PXO_REFRESH].button},
3768 {"Pilot Info", 1261, 814, 216, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_PILOT_INFO].button},
3769 {"Missions", 1262, 46, 601, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_MISSION_FILTER].button},
3770 {"Campaigns", 1263, 174, 601, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_CAMPAIGN_FILTER].button},
3771 {"Cancel", 387, 559, 582, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_CANCEL].button},
3772 {"1", 1264, 772, 610, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_TEAM0].button},
3773 {"2", 1265, 826, 610, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_TEAM1].button},
3774 {"Kick", 1266, 872, 610, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_KICK].button},
3775 {"Close", 1508, 949, 610, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_CLOSE].button},
3776 {"Host Options", 1267, 755, 683, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_HOST_OPTIONS].button},
3777 {"Commit", 1062, 932, 667, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_ACCEPT].button}
3782 // squad war checkbox
3783 UI_CHECKBOX Multi_create_sw_checkbox;
3784 char *Multi_create_sw_checkbox_fname[GR_NUM_RESOLUTIONS] = {
3788 int Multi_create_sw_checkbox_coords[GR_NUM_RESOLUTIONS][2] = {
3796 int Multi_create_sw_checkbox_text[GR_NUM_RESOLUTIONS][2] = {
3805 // game information text areas
3806 int Mc_list_coords[GR_NUM_RESOLUTIONS][4] = {
3819 int Mc_players_coords[GR_NUM_RESOLUTIONS][4] = {
3832 int Mc_info_coords[GR_NUM_RESOLUTIONS][4] = {
3845 // mission icon stuff
3846 int Mc_icon_type_coords[GR_NUM_RESOLUTIONS][2] = {
3848 38, -2 // y is an offset
3851 61, -2 // y is an offset
3855 int Mc_icon_volition_coords[GR_NUM_RESOLUTIONS][2] = {
3857 61, -1 // y is an offset
3860 98, 1 // y is an offset
3864 int Mc_icon_silent_coords[GR_NUM_RESOLUTIONS][2] = {
3866 72, 0 // y is an offset
3869 115, 0 // y is an offset
3873 int Mc_icon_valid_coords[GR_NUM_RESOLUTIONS][2] = {
3875 91, 0 // y is an offset
3878 146, 0 // y is an offset
3882 // mission/campaign list column areas
3883 int Mc_column1_w[GR_NUM_RESOLUTIONS] = {
3888 int Mc_column2_w[GR_NUM_RESOLUTIONS] = {
3893 int Mc_column3_w[GR_NUM_RESOLUTIONS] = {
3898 int Mc_mission_name_x[GR_NUM_RESOLUTIONS] = {
3903 int Mc_mission_count_x[GR_NUM_RESOLUTIONS] = {
3908 int Mc_mission_fname_x[GR_NUM_RESOLUTIONS] = {
3913 int Mc_create_game_text[GR_NUM_RESOLUTIONS][2] = {
3914 {13, 116}, // GR_640
3915 {21, 186} // GR_1024
3918 int Mc_players_text[GR_NUM_RESOLUTIONS][2] = {
3919 {467, 150}, // GR_640
3920 {747, 240} // GR_1024
3923 int Mc_team_text[GR_NUM_RESOLUTIONS][2] = {
3924 {484, 342}, // GR_640
3925 {774, 547} // GR_1024
3928 int Mc_slider_coords[GR_NUM_RESOLUTIONS][4] = {
3930 3, 197, 13, 105 // GR_640
3933 5, 316, 20, 168 // GR_1024
3937 char *Mc_slider_bitmap[GR_NUM_RESOLUTIONS] = {
3942 // player list control thingie defs
3943 #define MULTI_CREATE_PLIST_MAX_DISPLAY 20
3944 int Multi_create_plist_select_flag; // flag indicating if we have a play selected
3945 short Multi_create_plist_select_id; // the net address of the currently selected player (for lookup)
3947 // master tracker details
3948 int Multi_create_frame_count; // framecount
3949 int Multi_create_mt_tried_login; // attempted to login this server on the MT
3951 // mission filter settings
3952 int Multi_create_filter; // what mode we're in
3954 // game/campaign list control defs
3955 int Multi_create_list_max_display[GR_NUM_RESOLUTIONS] = {
3961 int Multi_create_list_count; // number of items in listbox
3962 int Multi_create_list_mode; // 0 == mission mode, 1 == campaign mode
3963 int Multi_create_list_start; // where to start displaying from
3964 int Multi_create_list_select; // which item is currently highlighted
3965 int Multi_create_files_loaded;
3967 char Multi_create_files_array[MULTI_CREATE_MAX_LIST_ITEMS][MAX_FILENAME_LEN];
3969 int Multi_create_mission_count; // how many we have
3970 int Multi_create_campaign_count;
3971 multi_create_info Multi_create_mission_list[MULTI_CREATE_MAX_LIST_ITEMS];
3972 multi_create_info Multi_create_campaign_list[MULTI_CREATE_MAX_LIST_ITEMS];
3974 // use a pointer for the file list. Will point to either the missions or the campaigns
3975 multi_create_info *Multi_create_file_list;
3977 // LOCAL function definitions
3978 void multi_create_check_buttons();
3979 void multi_create_button_pressed(int n);
3980 void multi_create_init_as_server();
3981 void multi_create_init_as_client();
3982 void multi_create_do_netstuff();
3983 void multi_create_plist_scroll_up();
3984 void multi_create_plist_scroll_down();
3985 void multi_create_plist_process();
3986 void multi_create_plist_blit_normal();
3987 void multi_create_plist_blit_team();
3988 void multi_create_list_scroll_up();
3989 void multi_create_list_scroll_down();
3990 void multi_create_list_do();
3991 void multi_create_list_select_item(int n);
3992 void multi_create_list_blit_icons(int list_index, int y_start);
3993 void multi_create_accept_hit();
3994 void multi_create_draw_filter_buttons();
3995 void multi_create_set_selected_team(int team);
3996 short multi_create_get_mouse_id();
3997 int multi_create_ok_to_commit();
3998 int multi_create_verify_cds();
3999 void multi_create_refresh_pxo();
4000 void multi_create_sw_clicked();
4002 // since we can selectively filter out mission/campaign types we always need to map a selected index (which is relative
4003 // to the displayed list), to an absolute index (which is relative to the total file list - some of which may filtered out)
4004 void multi_create_select_to_filename(int select_index,char *filename);
4005 int multi_create_select_to_index(int select_index);
4007 int Multi_create_should_show_popup = 0;
4010 // sorting function to sort mission lists.. Basic sorting on mission name
4011 int multi_create_sort_func(const void *a, const void *b)
4013 multi_create_info *m1, *m2;
4015 m1 = (multi_create_info *)a;
4016 m2 = (multi_create_info *)b;
4018 return ( strcmp(m1->name, m2->name) );
4021 void multi_create_setup_list_data(int mode)
4023 int idx,should_sort,switched_modes;
4025 // set the current mode
4028 if((Multi_create_list_mode != mode) && (mode != -1)){
4029 Multi_create_list_mode = mode;
4032 // set up the list pointers
4033 if ( Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ) {
4034 Multi_create_file_list = Multi_create_mission_list;
4035 } else if ( Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ) {
4036 Multi_create_file_list = Multi_create_campaign_list;
4042 // get the mission count based upon the filter selected
4043 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
4044 switch(Multi_create_filter){
4045 case MISSION_TYPE_MULTI:
4046 Multi_create_list_count = Multi_create_mission_count;
4049 Multi_create_list_count = 0;
4050 // find all missions which match
4051 for(idx=0;idx<Multi_create_mission_count;idx++){
4052 if(Multi_create_mission_list[idx].flags & Multi_create_filter){
4053 Multi_create_list_count++;
4057 // if we switched modes and we have more than 0 items, sort them
4058 if(switched_modes && (Multi_create_list_count > 0)){
4063 } else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
4064 switch(Multi_create_filter){
4065 case MISSION_TYPE_MULTI:
4066 Multi_create_list_count = Multi_create_campaign_count;
4069 Multi_create_list_count = 0;
4070 // find all missions which match
4071 for(idx=0;idx<Multi_create_campaign_count;idx++){
4072 if(Multi_create_campaign_list[idx].flags & Multi_create_filter){
4073 Multi_create_list_count++;
4077 // if we switched modes and we have more than 0 items, sort them
4078 if(switched_modes && (Multi_create_list_count > 0)){
4085 // reset the list start and selected indices
4086 Multi_create_list_start = 0;
4087 Multi_create_list_select = -1;
4088 multi_create_list_select_item(Multi_create_list_start);
4090 // sort the list of missions if necessary
4092 qsort(Multi_create_file_list, Multi_create_list_count, sizeof(multi_create_info), multi_create_sort_func);
4097 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);
4101 void multi_create_game_init()
4106 // now make sure to initialze various netgame stuff based upon whether we're on a standalone or not
4107 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4108 multi_create_init_as_server();
4110 multi_create_init_as_client();
4113 // initialize the player list data
4114 Multi_create_plist_select_flag = 0;
4115 Multi_create_plist_select_id = -1;
4117 // create the interface window
4118 Multi_create_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
4119 Multi_create_window.set_mask_bmap(Multi_create_bitmap_mask_fname[gr_screen.res]);
4121 // load the background bitmap
4122 Multi_create_bitmap = bm_load(Multi_create_bitmap_fname[gr_screen.res]);
4123 if(Multi_create_bitmap < 0){
4124 // we failed to load the bitmap - this is very bad
4128 // close any previous existing instances of the chatbox and create a new one
4132 // load the help overlay
4133 help_overlay_load(MULTI_CREATE_OVERLAY);
4134 help_overlay_set_state(MULTI_CREATE_OVERLAY, 0);
4136 // initialize the common notification messaging
4137 multi_common_notify_init();
4139 // use the common interface palette
4140 multi_common_set_palette();
4142 // create the interface buttons
4143 for(idx=0; idx<MULTI_CREATE_NUM_BUTTONS; idx++){
4144 b = &Multi_create_buttons[gr_screen.res][idx];
4146 // create the object
4147 b->button.create(&Multi_create_window, "", b->x, b->y, 1, 1, ((idx == MC_SCROLL_LIST_UP) || (idx == MC_SCROLL_LIST_DOWN)), 1);
4149 // set the sound to play when highlighted
4150 b->button.set_highlight_action(common_play_highlight_sound);
4152 // set the ani for the button
4153 b->button.set_bmaps(b->filename);
4156 b->button.link_hotspot(b->hotspot);
4158 // some special case stuff for the pxo refresh button
4159 if(idx == MC_PXO_REFRESH){
4160 // if not a PXO game, or if I'm not a server disable and hide the button
4161 if(!MULTI_IS_TRACKER_GAME || !MULTIPLAYER_MASTER){
4163 b->button.disable();
4169 for(idx=0; idx<MULTI_CREATE_NUM_TEXT; idx++){
4170 Multi_create_window.add_XSTR(&Multi_create_text[gr_screen.res][idx]);
4173 // if this is a PXO game, enable the squadwar checkbox
4174 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);
4175 Multi_create_sw_checkbox.set_bmaps(Multi_create_sw_checkbox_fname[gr_screen.res], 6, 0);
4176 if(!MULTI_IS_TRACKER_GAME){
4177 Multi_create_sw_checkbox.hide();
4178 Multi_create_sw_checkbox.disable();
4182 // disable squad war button in demo
4183 Multi_create_sw_checkbox.hide();
4184 Multi_create_sw_checkbox.disable();
4187 // initialize the mission type filtering mode
4188 Multi_create_filter = MISSION_TYPE_MULTI;
4190 // initialize the list mode, and load in a list
4191 memset(Multi_create_mission_list, 0, sizeof(multi_create_info) * MULTI_CREATE_MAX_LIST_ITEMS);
4192 memset(Multi_create_campaign_list, 0, sizeof(multi_create_info) * MULTI_CREATE_MAX_LIST_ITEMS);
4193 for(idx=0; idx<MULTI_CREATE_MAX_LIST_ITEMS; idx++){
4194 Multi_create_mission_list[idx].valid_status = MVALID_STATUS_UNKNOWN;
4195 Multi_create_campaign_list[idx].valid_status = MVALID_STATUS_UNKNOWN;
4197 Multi_create_list_mode = MULTI_CREATE_SHOW_MISSIONS;
4198 Multi_create_list_start = -1;
4199 Multi_create_list_select = -1;
4200 Multi_create_list_count = 0;
4203 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);
4206 // create the player list select button
4207 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);
4208 Multi_create_player_select_button.hide();
4210 // create the mission/campaign list select button
4211 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);
4212 Multi_create_list_select_button.hide();
4214 // set hotkeys for a couple of things.
4215 Multi_create_buttons[gr_screen.res][MC_ACCEPT].button.set_hotkey(KEY_CTRLED+KEY_ENTER);
4217 // init some master tracker stuff
4218 Multi_create_frame_count = 0;
4219 Multi_create_mt_tried_login = 0;
4221 // remove campaign flags
4222 Game_mode &= ~(GM_CAMPAIGN_MODE);
4224 // send any pilots as appropriate
4225 multi_data_send_my_junk();
4226 Multi_create_file_list = Multi_create_mission_list;
4228 Multi_create_campaign_count = 0;
4229 Multi_create_mission_count = 0;
4230 Multi_create_files_loaded = 0;
4233 void multi_create_game_do()
4237 char *loading_str = XSTR("Loading", 1336);
4241 // set this if we want to show the pilot info popup
4242 Multi_create_should_show_popup = 0;
4244 // first thing is to load the files
4245 if ( !Multi_create_files_loaded ) {
4246 // if I am a client, send a list request to the server for the missions
4247 if ( MULTIPLAYER_CLIENT ) {
4248 send_mission_list_request( MISSION_LIST_REQUEST );
4252 loading_bitmap = bm_load(Multi_create_loading_fname[gr_screen.res]);
4254 // draw the background, etc
4256 GR_MAYBE_CLEAR_RES(Multi_create_bitmap);
4257 if(Multi_create_bitmap != -1){
4258 gr_set_bitmap(Multi_create_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4262 if ( loading_bitmap > -1 ){
4263 gr_set_bitmap(loading_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4265 gr_bitmap( Please_wait_coords[gr_screen.res][MC_X_COORD], Please_wait_coords[gr_screen.res][MC_Y_COORD] );
4267 // draw "Loading" on it
4269 gr_set_color_fast(&Color_normal);
4271 gr_get_string_size(&str_w, &str_h, loading_str);
4272 gr_string((gr_screen.max_w - str_w) / 2, (gr_screen.max_h - str_h) / 2, loading_str);
4278 multi_create_list_load_missions();
4279 multi_create_list_load_campaigns();
4281 // if this is a tracker game, validate missions
4282 if(MULTI_IS_TRACKER_GAME){
4283 multi_update_valid_missions();
4286 // update the file list
4287 multi_create_setup_list_data(MULTI_CREATE_SHOW_MISSIONS);
4290 // don't bother setting netgame state if ont the server
4291 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4292 Netgame.game_state = NETGAME_STATE_FORMING;
4293 send_netgame_update_packet();
4296 // if we're on the standalone we have to tell him that we're now in the host setup screen
4297 Net_player->state = NETPLAYER_STATE_HOST_SETUP;
4298 send_netplayer_update_packet();
4300 Multi_create_files_loaded = 1;
4303 int k = chatbox_process();
4304 k = Multi_create_window.process(k,0);
4307 // same as the cancel button
4309 if(help_overlay_active(MULTI_CREATE_OVERLAY)){
4310 help_overlay_set_state(MULTI_CREATE_OVERLAY,0);
4312 gamesnd_play_iface(SND_USER_SELECT);
4313 multi_quit_game(PROMPT_HOST);
4318 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
4319 help_overlay_set_state(MULTI_CREATE_OVERLAY, 0);
4322 // process any button clicks
4323 multi_create_check_buttons();
4325 // do any network related stuff
4326 multi_create_do_netstuff();
4328 // draw the background, etc
4330 GR_MAYBE_CLEAR_RES(Multi_create_bitmap);
4331 if(Multi_create_bitmap != -1){
4332 gr_set_bitmap(Multi_create_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4336 // if we're not in team vs. team mode, don't draw the team buttons
4337 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
4338 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.hide();
4339 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.hide();
4340 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.disable();
4341 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.disable();
4343 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.enable();
4344 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.enable();
4345 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.unhide();
4346 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.unhide();
4349 // draw the window itself
4350 Multi_create_window.draw();
4352 gr_set_color_fast(&Color_normal);
4355 // draw Create Game text
4356 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));
4358 // draw players text
4359 gr_string(Mc_players_text[gr_screen.res][MC_X_COORD], Mc_players_text[gr_screen.res][MC_Y_COORD], XSTR("Players", 1269));
4361 // draw players text
4362 gr_string(Mc_team_text[gr_screen.res][MC_X_COORD], Mc_team_text[gr_screen.res][MC_Y_COORD], XSTR("Team", 1258));
4365 // process and display the player list
4366 // NOTE : this must be done before the buttons are checked to insure that a player hasn't dropped
4367 multi_create_plist_process();
4368 if(Netgame.type_flags & NG_TYPE_TEAM){
4369 multi_create_plist_blit_team();
4371 multi_create_plist_blit_normal();
4374 // process and display the game/campaign list
4375 multi_create_list_do();
4377 // draw the correct mission filter button
4378 multi_create_draw_filter_buttons();
4380 // display any text in the info area
4381 multi_common_render_text();
4383 // display any pending notification messages
4384 multi_common_notify_do();
4386 // force the correct mission/campaign button to light up
4387 if( Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ){
4388 Multi_create_buttons[gr_screen.res][MC_MISSION_FILTER].button.draw_forced(2);
4390 Multi_create_buttons[gr_screen.res][MC_CAMPAIGN_FILTER].button.draw_forced(2);
4393 // force draw the closed button if it is toggled on
4394 if(Netgame.flags & NG_FLAG_TEMP_CLOSED){
4395 Multi_create_buttons[gr_screen.res][MC_CLOSE].button.draw_forced(2);
4398 // process and show the chatbox thingie
4402 Multi_create_window.draw_tooltip();
4404 // display the voice status indicator
4405 multi_common_voice_display_status();
4407 // blit the help overlay if necessary
4408 help_overlay_maybe_blit(MULTI_CREATE_OVERLAY);
4411 if(MULTI_IS_TRACKER_GAME){
4412 if(Netgame.type_flags & NG_TYPE_SW){
4413 gr_set_color_fast(&Color_bright);
4415 gr_set_color_fast(&Color_normal);
4417 gr_string(Multi_create_sw_checkbox_text[gr_screen.res][0], Multi_create_sw_checkbox_text[gr_screen.res][1], "SquadWar");
4423 // if we're supposed to show the pilot info popup, do it now
4424 if(Multi_create_should_show_popup){
4425 // get the player index and address of the player item the mouse is currently over
4426 if(Multi_create_plist_select_flag){
4427 player_index = find_player_id(Multi_create_plist_select_id);
4428 if(player_index != -1){
4429 multi_pinfo_popup(&Net_players[player_index]);
4434 // increment the frame count
4435 Multi_create_frame_count++;
4438 void multi_create_game_close()
4440 // unload any bitmaps
4441 if(!bm_unload(Multi_create_bitmap)){
4442 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_create_bitmap_fname[gr_screen.res]));
4445 // unload the help overlay
4446 help_overlay_unload(MULTI_CREATE_OVERLAY);
4448 // destroy the chatbox
4451 // destroy the UI_WINDOW
4452 Multi_create_window.destroy();
4455 void multi_create_check_buttons()
4458 for(idx=0;idx<MULTI_CREATE_NUM_BUTTONS;idx++){
4459 // we only really need to check for one button pressed at a time, so we can break after
4461 if(Multi_create_buttons[gr_screen.res][idx].button.pressed()){
4462 multi_create_button_pressed(idx);
4467 // if the squad war checkbox was clicked
4468 if(Multi_create_sw_checkbox.changed()){
4469 multi_create_sw_clicked();
4473 void multi_create_button_pressed(int n)
4479 gamesnd_play_iface(SND_USER_SELECT);
4480 multi_quit_game(PROMPT_HOST);
4483 // if valid commit conditions have not been met
4484 if(!multi_create_ok_to_commit()){
4489 multi_create_accept_hit();
4494 if(!help_overlay_active(MULTI_CREATE_OVERLAY)){
4495 help_overlay_set_state(MULTI_CREATE_OVERLAY,1);
4497 help_overlay_set_state(MULTI_CREATE_OVERLAY,0);
4501 // scroll the info text box up
4502 case MC_SCROLL_INFO_UP:
4503 multi_common_scroll_text_up();
4506 // scroll the info text box down
4507 case MC_SCROLL_INFO_DOWN:
4508 multi_common_scroll_text_down();
4511 // scroll the player list up
4512 case MC_SCROLL_PLAYERS_UP:
4513 multi_create_plist_scroll_up();
4516 // scroll the player list down
4517 case MC_SCROLL_PLAYERS_DOWN:
4518 multi_create_plist_scroll_down();
4521 // scroll the game/campaign list up
4522 case MC_SCROLL_LIST_UP:
4523 multi_create_list_scroll_up();
4525 Multi_create_slider.forceUp(); // move slider up
4529 // scroll the game/campaign list down
4530 case MC_SCROLL_LIST_DOWN:
4531 multi_create_list_scroll_down();
4533 Multi_create_slider.forceDown(); // move slider down
4537 // go to the options screen
4539 gamesnd_play_iface(SND_USER_SELECT);
4540 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
4543 // show all missions
4545 if(Multi_create_filter != MISSION_TYPE_MULTI){
4546 gamesnd_play_iface(SND_USER_SELECT);
4547 Multi_create_filter = MISSION_TYPE_MULTI;
4548 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4550 gamesnd_play_iface(SND_GENERAL_FAIL);
4554 // show cooperative missions
4556 if(Multi_create_filter != MISSION_TYPE_MULTI_COOP){
4557 gamesnd_play_iface(SND_USER_SELECT);
4558 Multi_create_filter = MISSION_TYPE_MULTI_COOP;
4559 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4561 gamesnd_play_iface(SND_GENERAL_FAIL);
4565 // show team vs. team missions
4567 if(Multi_create_filter != MISSION_TYPE_MULTI_TEAMS){
4568 gamesnd_play_iface(SND_USER_SELECT);
4569 Multi_create_filter = MISSION_TYPE_MULTI_TEAMS;
4570 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4572 gamesnd_play_iface(SND_GENERAL_FAIL);
4576 // show dogfight missions
4578 case MC_SHOW_DOGFIGHT:
4579 if (Multi_create_filter != MISSION_TYPE_MULTI_DOGFIGHT){
4580 gamesnd_play_iface(SND_USER_SELECT);
4581 Multi_create_filter = MISSION_TYPE_MULTI_DOGFIGHT;
4582 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4584 gamesnd_play_iface(SND_GENERAL_FAIL);
4589 // toggle temporary netgame closed on/off
4591 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4592 Netgame.flags ^= NG_FLAG_TEMP_CLOSED;
4594 Netgame.options.flags |= MLO_FLAG_TEMP_CLOSED;
4595 multi_options_update_netgame();
4597 gamesnd_play_iface(SND_USER_SELECT);
4600 // kick the currently selected player (if possible)
4602 // lookup the player at the specified index
4603 if(Multi_create_plist_select_flag){
4604 idx = find_player_id(Multi_create_plist_select_id);
4605 // kick him - but don't ban him
4607 multi_kick_player(idx,0);
4612 // switch to individual mission mode and load in a list
4613 case MC_MISSION_FILTER:
4614 if(Multi_create_list_mode != MULTI_CREATE_SHOW_MISSIONS){
4615 Netgame.campaign_mode = MP_SINGLE;
4617 gamesnd_play_iface(SND_USER_SELECT);
4619 // update the file list
4620 multi_create_setup_list_data(MULTI_CREATE_SHOW_MISSIONS);
4622 gamesnd_play_iface(SND_GENERAL_FAIL);
4626 // switch to campaign mode and load in a list
4627 case MC_CAMPAIGN_FILTER:
4628 // switch off squad war
4629 Multi_create_sw_checkbox.set_state(0);
4630 Netgame.type_flags = NG_TYPE_COOP;
4632 if(Multi_create_list_mode != MULTI_CREATE_SHOW_CAMPAIGNS){
4633 Netgame.campaign_mode = MP_CAMPAIGN;
4635 gamesnd_play_iface(SND_USER_SELECT);
4637 // update the file list
4638 multi_create_setup_list_data(MULTI_CREATE_SHOW_CAMPAIGNS);
4640 gamesnd_play_iface(SND_GENERAL_FAIL);
4644 // attempt to set the selected player's team
4646 multi_create_set_selected_team(0);
4647 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4648 multi_team_send_update();
4652 // attempt to set the selected player's team
4654 multi_create_set_selected_team(1);
4655 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4656 multi_team_send_update();
4660 // popup the pilot info dialog for the currently selected pilot (will occur at the end of the frame)
4662 Multi_create_should_show_popup = 1;
4665 // go to the host options screen
4666 case MC_HOST_OPTIONS:
4667 gamesnd_play_iface(SND_USER_SELECT);
4668 gameseq_post_event(GS_EVENT_MULTI_HOST_OPTIONS);
4671 // refresh PXO file list
4672 case MC_PXO_REFRESH:
4673 if(!MULTI_IS_TRACKER_GAME){
4676 multi_create_refresh_pxo();
4680 gamesnd_play_iface(SND_GENERAL_FAIL);
4681 multi_common_add_notify(XSTR("Not implemented yet!",760));
4686 // do stuff like pinging servers, sending out requests, etc
4687 void multi_create_do_netstuff()
4691 // if not on a standalone
4692 void multi_create_init_as_server()
4694 // set me up as the host and master
4695 Net_player->flags |= (NETINFO_FLAG_AM_MASTER | NETINFO_FLAG_GAME_HOST);
4698 // if on a standalone
4699 void multi_create_init_as_client()
4701 Net_player->flags |= NETINFO_FLAG_GAME_HOST;
4704 // scroll up through the player list
4705 void multi_create_plist_scroll_up()
4707 gamesnd_play_iface(SND_GENERAL_FAIL);
4710 // scroll down through the player list
4711 void multi_create_plist_scroll_down()
4713 gamesnd_play_iface(SND_GENERAL_FAIL);
4716 void multi_create_plist_process()
4718 int test_count,idx,player_index;
4720 // first determine if there are 0 players in the game. This should never happen since the host is _always_ in the game
4722 for(idx=0;idx<MAX_PLAYERS;idx++){
4723 // count anyone except the standalone server (if applicable)
4724 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
4728 if(test_count <= 0){
4732 // if we had a selected item but that player has left, select myself instead
4733 if(Multi_create_plist_select_flag){
4734 player_index = find_player_id(Multi_create_plist_select_id);
4735 if(player_index == -1){
4736 Multi_create_plist_select_id = Net_player->player_id;
4739 Multi_create_plist_select_flag = 1;
4740 Multi_create_plist_select_id = Net_player->player_id;
4743 // if the player has clicked somewhere in the player list area
4744 if(Multi_create_player_select_button.pressed()){
4747 // get the player index and address of the player item the mouse is currently over
4748 player_id = multi_create_get_mouse_id();
4749 player_index = find_player_id(player_id);
4750 if(player_index != -1){
4751 Multi_create_plist_select_flag = 1;
4752 Multi_create_plist_select_id = player_id;
4757 void multi_create_plist_blit_normal()
4760 char str[CALLSIGN_LEN+5];
4761 int y_start = Mc_players_coords[gr_screen.res][MC_Y_COORD];
4764 // display all the players
4765 for(idx=0;idx<MAX_PLAYERS;idx++){
4766 // count anyone except the standalone server (if applicable)
4767 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
4771 // highlight him if he's the host
4772 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4773 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4774 gr_set_color_fast(&Color_text_active_hi);
4776 gr_set_color_fast(&Color_bright);
4779 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4780 gr_set_color_fast(&Color_text_active);
4782 gr_set_color_fast(&Color_text_normal);
4786 // optionally draw his CD status
4787 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4788 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4789 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4791 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4794 // make sure the string will fit, then display it
4795 strcpy(str,Net_players[idx].player->callsign);
4796 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4797 strcat(str,XSTR("(O)",787)); // [[ Observer ]]
4799 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4800 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4807 void multi_create_plist_blit_team()
4810 char str[CALLSIGN_LEN+1];
4811 int y_start = Mc_players_coords[gr_screen.res][MC_Y_COORD];
4814 // display all the red players first
4815 for(idx=0;idx<MAX_PLAYERS;idx++){
4816 // count anyone except the standalone server (if applicable)
4817 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
4818 // reset total offset
4821 // highlight him if he's the host
4822 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4823 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4824 gr_set_color_fast(&Color_text_active_hi);
4826 // be sure to blit the correct team button
4827 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.draw_forced(2);
4829 gr_set_color_fast(&Color_bright);
4832 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4833 gr_set_color_fast(&Color_text_active);
4835 // be sure to blit the correct team button
4836 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.draw_forced(2);
4838 gr_set_color_fast(&Color_text_normal);
4842 // optionally draw his CD status
4843 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4844 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4845 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4847 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4850 // blit the red team indicator
4851 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
4852 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
4853 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4854 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4856 total_offset += Multi_common_icon_dims[MICON_TEAM0_SELECT][0] + 1;
4859 if(Multi_common_icons[MICON_TEAM0] != -1){
4860 gr_set_bitmap(Multi_common_icons[MICON_TEAM0], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4861 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4863 total_offset += Multi_common_icon_dims[MICON_TEAM0][0] + 1;
4867 // make sure the string will fit
4868 strcpy(str,Net_players[idx].player->callsign);
4869 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4870 strcat(str,XSTR("(O)",787));
4872 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4874 // display him in the correct half of the list depending on his team
4875 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4880 // display all the green players next
4881 for(idx=0;idx<MAX_PLAYERS;idx++){
4882 // count anyone except the standalone server (if applicable)
4883 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
4884 // reset total offset
4887 // highlight him if he's the host
4888 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4889 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4890 gr_set_color_fast(&Color_text_active_hi);
4892 // be sure to blit the correct team button
4893 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.draw_forced(2);
4895 gr_set_color_fast(&Color_bright);
4898 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4899 gr_set_color_fast(&Color_text_active);
4901 // be sure to blit the correct team button
4902 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.draw_forced(2);
4904 gr_set_color_fast(&Color_text_normal);
4908 // optionally draw his CD status
4909 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4910 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4911 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4913 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4916 // blit the red team indicator
4917 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
4918 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
4919 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4920 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4922 total_offset += Multi_common_icon_dims[MICON_TEAM1_SELECT][0] + 1;
4925 if(Multi_common_icons[MICON_TEAM1] != -1){
4926 gr_set_bitmap(Multi_common_icons[MICON_TEAM1], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4927 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4929 total_offset += Multi_common_icon_dims[MICON_TEAM1][0] + 1;
4933 // make sure the string will fit
4934 strcpy(str,Net_players[idx].player->callsign);
4935 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4936 strcat(str,XSTR("(O)",787));
4938 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4940 // display him in the correct half of the list depending on his team
4941 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4947 void multi_create_list_scroll_up()
4949 if(Multi_create_list_start > 0){
4950 Multi_create_list_start--;
4952 gamesnd_play_iface(SND_SCROLL);
4954 gamesnd_play_iface(SND_GENERAL_FAIL);
4958 void multi_create_list_scroll_down()
4960 if((Multi_create_list_count - Multi_create_list_start) > Multi_create_list_max_display[gr_screen.res]){
4961 Multi_create_list_start++;
4963 gamesnd_play_iface(SND_SCROLL);
4965 gamesnd_play_iface(SND_GENERAL_FAIL);
4969 // gets a list of multiplayer misisons
4970 void multi_create_list_load_missions()
4972 char *fname, mission_name[NAME_LENGTH+1];
4973 char wild_card[256];
4976 memset(wild_card, 0, 256);
4977 strcpy(wild_card, NOX("*"));
4978 strcat(wild_card, FS_MISSION_FILE_EXT);
4979 file_count = cf_get_file_list_preallocated(MULTI_CREATE_MAX_LIST_ITEMS, Multi_create_files_array, NULL, CF_TYPE_MISSIONS, wild_card);
4980 Multi_create_mission_count = 0;
4982 // maybe create a standalone dialog
4983 if(Game_mode & GM_STANDALONE_SERVER){
4984 std_create_gen_dialog("Loading missions");
4985 std_gen_set_text("Mission:", 1);
4988 for(idx = 0; idx < file_count; idx++){
4989 int flags,max_players;
4993 fname = Multi_create_files_array[idx];
4995 // tack on any necessary file extension
4996 filename = cf_add_ext( fname, FS_MISSION_FILE_EXT );
4998 // for multiplayer beta builds, only accept builtin missions
4999 #if defined(MULTIPLAYER_BETA_BUILD) || defined(FS2_DEMO)
5000 if(game_find_builtin_mission(filename) == NULL){
5003 #elif defined(PD_BUILD)
5004 if((game_find_builtin_mission(filename) == NULL) && !strstr(filename, "peterdrake")){
5009 if(Game_mode & GM_STANDALONE_SERVER){
5010 std_gen_set_text(filename, 2);
5013 flags = mission_parse_is_multi(filename, mission_name);
5015 // if the mission is a multiplayer mission, then add it to the mission list
5017 max_players = mission_parse_get_multi_mission_info( filename );
5018 m_respawn = The_mission.num_respawns;
5020 if ( Multi_create_mission_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
5021 multi_create_info *mcip;
5023 mcip = &Multi_create_mission_list[Multi_create_mission_count];
5024 strcpy(mcip->filename, filename );
5025 strcpy(mcip->name, mission_name );
5026 mcip->flags = flags;
5027 mcip->respawn = m_respawn;
5028 mcip->max_players = (ubyte)max_players;
5030 // get any additional information for possibly builtin missions
5031 fs_builtin_mission *fb = game_find_builtin_mission(filename);
5035 Multi_create_mission_count++;
5041 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);
5044 // maybe create a standalone dialog
5045 if(Game_mode & GM_STANDALONE_SERVER){
5046 std_destroy_gen_dialog();
5050 void multi_create_list_load_campaigns()
5053 int idx, file_count;
5054 int campaign_type,max_players;
5056 char wild_card[256];
5058 // maybe create a standalone dialog
5059 if(Game_mode & GM_STANDALONE_SERVER){
5060 std_create_gen_dialog("Loading campaigns");
5061 std_gen_set_text("Campaign:", 1);
5064 Multi_create_campaign_count = 0;
5065 memset(wild_card, 0, 256);
5066 strcpy(wild_card, NOX("*"));
5067 strcat(wild_card, FS_CAMPAIGN_FILE_EXT);
5068 file_count = cf_get_file_list_preallocated(MULTI_CREATE_MAX_LIST_ITEMS, Multi_create_files_array, NULL, CF_TYPE_MISSIONS, wild_card);
5069 for(idx = 0; idx < file_count; idx++){
5071 char *filename, name[NAME_LENGTH];
5073 fname = Multi_create_files_array[idx];
5075 // tack on any necessary file extension
5076 filename = cf_add_ext( fname, FS_CAMPAIGN_FILE_EXT );
5078 // for multiplayer beta builds, only accept builtin missions
5079 #if defined(MULTIPLAYER_BETA_BUILD) || defined(FS2_DEMO)
5080 if(game_find_builtin_mission(filename) == NULL){
5083 #elif defined(PD_BUILD)
5084 if((game_find_builtin_mission(filename) == NULL) && !strstr(filename, "peterdrake")){
5089 if(Game_mode & GM_STANDALONE_SERVER){
5090 std_gen_set_text(filename, 2);
5093 // if the campaign is a multiplayer campaign, then add the data to the campaign list items
5094 flags = mission_campaign_parse_is_multi( filename, name );
5095 if( flags != CAMPAIGN_TYPE_SINGLE && mission_campaign_get_info(filename,title,&campaign_type,&max_players)) {
5096 if ( Multi_create_campaign_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
5097 multi_create_info *mcip;
5099 mcip = &Multi_create_campaign_list[Multi_create_campaign_count];
5100 strcpy(mcip->filename, filename );
5101 strcpy(mcip->name, name );
5103 // setup various flags
5104 if ( flags == CAMPAIGN_TYPE_MULTI_COOP ){
5105 mcip->flags = MISSION_TYPE_MULTI_COOP | MISSION_TYPE_MULTI;
5106 } else if ( flags == CAMPAIGN_TYPE_MULTI_TEAMS ) {
5107 mcip->flags = MISSION_TYPE_MULTI_TEAMS | MISSION_TYPE_MULTI;
5109 Int3(); // bogus campaign multi type -- find allender
5112 // 0 respawns for campaign files (should be contained within the mission files themselves)
5115 // 0 max players for campaign files
5116 mcip->max_players = (unsigned char)max_players;
5118 // get any additional information for possibly builtin missions
5119 fs_builtin_mission *fb = game_find_builtin_mission(filename);
5123 Multi_create_campaign_count++;
5128 // maybe create a standalone dialog
5129 if(Game_mode & GM_STANDALONE_SERVER){
5130 std_destroy_gen_dialog();
5134 void multi_create_list_do()
5137 int start_index,stop_index;
5138 char selected_name[255];
5140 // bail early if there aren't any selectable items
5141 if(Multi_create_list_count == 0){
5145 // first check to see if the user has clicked on an item
5146 if(Multi_create_list_select_button.pressed()){
5148 Multi_create_list_select_button.get_mouse_pos(NULL,&y);
5151 // make sure we are selectedin valid indices
5152 if((item < Multi_create_list_max_display[gr_screen.res]) && (item >= 0)){
5153 item += Multi_create_list_start;
5155 if(item < Multi_create_list_count){
5156 multi_create_list_select_item(item);
5157 gamesnd_play_iface(SND_IFACE_MOUSE_CLICK);
5162 // bail early if we don't have a start position
5163 if(Multi_create_list_start == -1){
5167 // display the list of individual campaigns/missions
5169 int y_start = Mc_list_coords[gr_screen.res][MC_Y_COORD];
5171 start_index = multi_create_select_to_index(Multi_create_list_start);
5172 stop_index = Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ? Multi_create_mission_count : Multi_create_campaign_count;
5173 for(idx=start_index; idx<stop_index; idx++){
5174 // see if we should drop out
5175 if(count == Multi_create_list_max_display[gr_screen.res]){
5179 // see if we should filter out this mission
5180 if ( !(Multi_create_file_list[idx].flags & Multi_create_filter) ){
5184 // highlight the selected item
5185 multi_create_select_to_filename(Multi_create_list_select,selected_name);
5186 if(!strcmp(selected_name,Multi_create_file_list[idx].filename)){
5187 gr_set_color_fast(&Color_text_selected);
5189 gr_set_color_fast(&Color_text_normal);
5192 // draw the type icon
5193 multi_create_list_blit_icons(idx, y_start);
5195 // force fit the mission name string
5196 strcpy(selected_name,Multi_create_file_list[idx].name);
5197 gr_force_fit_string(selected_name,255,Mc_column1_w[gr_screen.res]);
5198 gr_string(Mc_mission_name_x[gr_screen.res],y_start,selected_name);
5200 // draw the max players if in mission mode
5201 sprintf(selected_name,"%d",(int)Multi_create_file_list[idx].max_players);
5202 gr_string(Mc_mission_count_x[gr_screen.res],y_start,selected_name);
5204 // force fit the mission filename string
5205 strcpy(selected_name,Multi_create_file_list[idx].filename);
5206 gr_force_fit_string(selected_name,255,Mc_column3_w[gr_screen.res]);
5207 gr_string(Mc_mission_fname_x[gr_screen.res],y_start,selected_name);
5214 // takes care of stuff like changing indices around and setting up the netgame structure
5215 void multi_create_list_select_item(int n)
5217 int abs_index,campaign_type,max_players;
5218 char title[NAME_LENGTH+1];
5219 netgame_info ng_temp;
5222 char *campaign_desc;
5224 // if not on the standalone server
5225 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5228 // on the standalone
5230 memset(&ng_temp,0,sizeof(netgame_info));
5234 if ( n != Multi_create_list_select ) {
5235 // check to see if this is a valid index, and bail if it is not
5236 abs_index = multi_create_select_to_index(n);
5237 if(abs_index == -1){
5241 Multi_create_list_select = n;
5243 // set the mission name
5244 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5245 multi_create_select_to_filename(n,ng->mission_name);
5247 multi_create_select_to_filename(n,ng->campaign_name);
5250 // make sure the netgame type is properly set
5251 int old_type = Netgame.type_flags;
5252 abs_index = multi_create_select_to_index(n);
5253 if(abs_index != -1){
5254 if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_TEAMS){
5255 // if we're in squad war mode, leave it as squad war
5256 if(old_type & NG_TYPE_SW){
5257 ng->type_flags = NG_TYPE_SW;
5259 ng->type_flags = NG_TYPE_TVT;
5261 } else if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_COOP){
5262 ng->type_flags = NG_TYPE_COOP;
5263 } else if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_DOGFIGHT){
5264 ng->type_flags = NG_TYPE_DOGFIGHT;
5268 // if we're no longer in a TvT game, just uncheck the squadwar checkbox
5269 if(!(ng->type_flags & NG_TYPE_TEAM)){
5270 Multi_create_sw_checkbox.set_state(0);
5273 // if we switched from something else to team vs. team mode, do some special processing
5274 if((ng->type_flags & NG_TYPE_TEAM) && (ng->type_flags != old_type) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
5278 switch(Multi_create_list_mode){
5279 case MULTI_CREATE_SHOW_MISSIONS:
5280 // don't forget to update the info box window thingie
5281 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5282 ship_init(); // mwa -- 10/15/97. Call this function to reset number of ships in mission
5283 ng->max_players = mission_parse_get_multi_mission_info( ng->mission_name );
5285 Assert(ng->max_players > 0);
5286 strcpy(ng->title,The_mission.name);
5288 // set the information area text
5289 multi_common_set_text(The_mission.mission_desc);
5291 // if we're on the standalone, send a request for the description
5293 send_netgame_descript_packet(&Netgame.server_addr,0);
5294 multi_common_set_text("");
5297 // set the respawns as appropriate
5298 if(Netgame.options.respawn <= Multi_create_file_list[abs_index].respawn){
5299 ng->respawn = Netgame.options.respawn;
5300 nprintf(("Network","Using netgame options for respawn count (%d %d)\n",Netgame.options.respawn,Multi_create_file_list[abs_index].respawn));
5302 ng->respawn = Multi_create_file_list[abs_index].respawn;
5303 nprintf(("Network","Using mission settings for respawn count (%d %d)\n",Netgame.options.respawn,Multi_create_file_list[abs_index].respawn));
5306 case MULTI_CREATE_SHOW_CAMPAIGNS:
5307 // if not on the standalone server
5308 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5309 // get the campaign info
5310 memset(title,0,NAME_LENGTH+1);
5311 if(!mission_campaign_get_info(ng->campaign_name,title,&campaign_type,&max_players, &campaign_desc)) {
5312 memset(ng->campaign_name,0,NAME_LENGTH+1);
5313 ng->max_players = 0;
5315 // if we successfully got the # of players
5317 memset(ng->title,0,NAME_LENGTH+1);
5318 strcpy(ng->title,title);
5319 ng->max_players = max_players;
5322 nprintf(("Network","MC MAX PLAYERS : %d\n",ng->max_players));
5324 // set the information area text
5325 // multi_common_set_text(ng->title);
5326 multi_common_set_text(campaign_desc);
5328 // if on the standalone server, send a request for the description
5330 // no descriptions currently kept for campaigns
5333 // netgame respawns are always 0 for campaigns (until the first mission is loaded)
5338 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5340 send_netgame_update_packet();
5342 // update all machines about stuff like respawns, etc.
5343 multi_options_update_netgame();
5345 multi_options_update_mission(ng, Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ? 1 : 0);
5350 void multi_create_list_blit_icons(int list_index, int y_start)
5352 multi_create_info *mcip;
5353 fs_builtin_mission *fb;
5356 // get a pointer to the list item
5357 max_index = Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ? Multi_create_mission_count - 1 : Multi_create_campaign_count - 1;
5358 if((list_index < 0) || (list_index > max_index)){
5361 mcip = &Multi_create_file_list[list_index];
5363 // blit the multiplayer type icons
5364 if(mcip->flags & MISSION_TYPE_MULTI_COOP){
5365 if(Multi_common_icons[MICON_COOP] >= 0){
5366 gr_set_bitmap(Multi_common_icons[MICON_COOP], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5367 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5369 } else if(mcip->flags & MISSION_TYPE_MULTI_TEAMS){
5370 if(Multi_common_icons[MICON_TVT] >= 0){
5371 gr_set_bitmap(Multi_common_icons[MICON_TVT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5372 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5374 } else if(mcip->flags & MISSION_TYPE_MULTI_DOGFIGHT){
5375 if(Multi_common_icons[MICON_DOGFIGHT] >= 0){
5376 gr_set_bitmap(Multi_common_icons[MICON_DOGFIGHT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5377 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5381 // if its a valid mission, blit the valid mission icon
5382 if(MULTI_IS_TRACKER_GAME && (mcip->valid_status == MVALID_STATUS_VALID)){
5383 if(Multi_common_icons[MICON_VALID] >= 0){
5384 gr_set_bitmap(Multi_common_icons[MICON_VALID], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5385 gr_bitmap(Mc_icon_valid_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_valid_coords[gr_screen.res][MC_Y_COORD]);
5389 // now see if its a builtin mission
5390 fb = game_find_builtin_mission(mcip->filename);
5391 // if the mission is from volition, blit the volition icon
5392 if((fb != NULL) && (fb->flags & FSB_FROM_VOLITION)){
5393 if(Multi_common_icons[MICON_VOLITION] >= 0){
5394 gr_set_bitmap(Multi_common_icons[MICON_VOLITION], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5395 gr_bitmap(Mc_icon_volition_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_volition_coords[gr_screen.res][MC_Y_COORD]);
5400 // check for mdisk mission
5401 fb = game_find_builtin_mission(mcip->filename);
5402 if((fb != NULL) && (fb->flags & FSB_FROM_MDISK)){
5403 if(Multi_common_icons[MICON_MDISK] >= 0){
5404 gr_set_bitmap(Multi_common_icons[MICON_MDISK]);
5405 gr_bitmap(Mc_icon_silent_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_silent_coords[gr_screen.res][MC_Y_COORD]);
5411 void multi_create_accept_hit()
5413 char selected_name[255];
5414 int start_campaign = 0;
5416 // make sure all players have finished joining
5417 if(!multi_netplayer_state_check(NETPLAYER_STATE_JOINED,1)){
5418 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("Please wait until all clients have finished joining",788));
5419 gamesnd_play_iface(SND_GENERAL_FAIL);
5422 gamesnd_play_iface(SND_COMMIT_PRESSED);
5425 // do single mission stuff
5426 switch(Multi_create_list_mode){
5427 case MULTI_CREATE_SHOW_MISSIONS:
5428 if(Multi_create_list_select != -1){
5429 // set the netgame mode
5430 Netgame.campaign_mode = MP_SINGLE;
5432 // setup various filenames and mission names
5433 multi_create_select_to_filename(Multi_create_list_select,selected_name);
5434 strncpy( Game_current_mission_filename, selected_name, MAX_FILENAME_LEN );
5435 strncpy(Netgame.mission_name,selected_name,MAX_FILENAME_LEN);
5438 ml_printf(NOX("Starting single mission %s, with %d players"), Game_current_mission_filename, multi_num_players());
5440 multi_common_add_notify(XSTR("No mission selected!",789));
5445 case MULTI_CREATE_SHOW_CAMPAIGNS:
5446 // do campaign related stuff
5447 if(Multi_create_list_select != -1){
5448 // set the netgame mode
5449 Netgame.campaign_mode = MP_CAMPAIGN;
5451 // start a campaign instead of a single mission
5452 multi_create_select_to_filename(Multi_create_list_select,selected_name);
5453 multi_campaign_start(selected_name);
5457 ml_printf(NOX("Starting campaign %s, with %d players"), selected_name, multi_num_players());
5459 multi_common_add_notify(XSTR("No campaign selected!",790));
5465 // if this is a team vs team situation, lock the players send a final team update
5466 if((Netgame.type_flags & NG_TYPE_TEAM) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
5467 multi_team_host_lock_all();
5468 multi_team_send_update();
5471 // if not on the standalone, move to the mission sync state which will take care of everything
5472 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5473 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
5474 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
5476 // otherwise tell the standalone to do so
5478 // when the standalone receives this, he'll do the mission syncing himself
5479 send_mission_sync_packet(MULTI_SYNC_PRE_BRIEFING,start_campaign);
5483 void multi_create_draw_filter_buttons()
5485 // highlight the correct filter button
5486 if ( Multi_create_filter == MISSION_TYPE_MULTI ){
5487 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL].button.draw_forced(2);
5488 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_COOP ) {
5489 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 1].button.draw_forced(2);
5490 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_TEAMS ) {
5491 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 2].button.draw_forced(2);
5492 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_DOGFIGHT ){
5493 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 3].button.draw_forced(2);
5499 void multi_create_set_selected_team(int team)
5503 // if we don't currently have a player selected, don't do anything
5504 if(!Multi_create_plist_select_flag){
5505 gamesnd_play_iface(SND_GENERAL_FAIL);
5508 gamesnd_play_iface(SND_USER_SELECT);
5510 // otherwise attempt to set the team for this guy
5511 player_index = find_player_id(Multi_create_plist_select_id);
5512 if(player_index != -1){
5513 multi_team_set_team(&Net_players[player_index],team);
5517 void multi_create_handle_join(net_player *pl)
5519 // for now just play a bloop sound
5520 gamesnd_play_iface(SND_ICON_DROP_ON_WING);
5523 // fill in net address of player the mouse is over, return player index (or -1 if none)
5524 short multi_create_get_mouse_id()
5526 // determine where he clicked (y pixel value)
5528 Multi_create_player_select_button.get_mouse_pos(NULL,&y);
5530 // select things a little differently if we're in team vs. team or non-team vs. team mode
5532 if(Netgame.type_flags & NG_TYPE_TEAM){
5533 int player_index = -1;
5535 // look through all of team red first
5536 for(idx=0;idx<MAX_PLAYERS;idx++){
5537 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
5540 // if this is the _nth_ guy
5548 // if we still haven't found him yet, look through the green team
5549 if(player_index == -1){
5550 for(idx=0;idx<MAX_PLAYERS;idx++){
5551 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
5553 // if this is the _nth_ guy
5562 if(player_index != -1){
5563 return Net_players[player_index].player_id;
5566 // select the nth active player if possible, disregarding the standalone server
5567 for(idx=0;idx<MAX_PLAYERS;idx++){
5568 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
5571 // if this is the _nth_ guy
5573 return Net_players[idx].player_id;
5582 void multi_create_select_to_filename(int select_index,char *filename)
5586 // look through the mission list
5587 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5588 for(idx=0;idx<Multi_create_mission_count;idx++){
5589 if(Multi_create_file_list[idx].flags & Multi_create_filter){
5593 // if we found the item
5594 if(select_index < 0){
5595 strcpy(filename,Multi_create_file_list[idx].filename);
5600 // look through the campaign list
5601 else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
5602 for(idx=0;idx<Multi_create_campaign_count;idx++){
5605 // if we found the item
5606 if(select_index < 0){
5607 strcpy(filename,Multi_create_file_list[idx].filename);
5613 strcpy(filename,"");
5616 int multi_create_select_to_index(int select_index)
5619 int lookup_index = 0;
5621 // look through the mission list
5622 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5623 for(idx=0;idx<Multi_create_mission_count;idx++){
5624 if(Multi_create_file_list[idx].flags & Multi_create_filter){
5628 // if we found the item
5629 if(select_index < lookup_index){
5634 // look through the campaign list
5635 else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
5636 for(idx=0;idx<Multi_create_campaign_count;idx++){
5639 // if we found the item
5640 if(select_index < 0){
5649 int multi_create_ok_to_commit()
5651 int player_count, observer_count, idx;
5652 int notify_of_hacked_ships_tbl = 0;
5653 int notify_of_hacked_weapons_tbl = 0;
5654 char err_string[255];
5658 // make sure we have a valid mission selected
5659 if(Multi_create_list_select < 0){
5663 // if this is not a valid mission, let the player know
5664 abs_index = multi_create_select_to_index(Multi_create_list_select);
5669 // if we're playing with a hacked ships.tbl (on PXO)
5670 notify_of_hacked_ships_tbl = 0;
5671 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5672 if(!Game_ships_tbl_valid){
5673 notify_of_hacked_ships_tbl = 1;
5676 if(Netgame.flags & NG_FLAG_HACKED_SHIPS_TBL){
5677 notify_of_hacked_ships_tbl = 1;
5680 if(!MULTI_IS_TRACKER_GAME){
5681 notify_of_hacked_ships_tbl = 0;
5683 if(notify_of_hacked_ships_tbl){
5684 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){
5689 // if we're playing with a hacked weapons.tbl (on PXO)
5690 notify_of_hacked_weapons_tbl = 0;
5691 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5692 if(!Game_weapons_tbl_valid){
5693 notify_of_hacked_weapons_tbl = 1;
5696 if(Netgame.flags & NG_FLAG_HACKED_WEAPONS_TBL){
5697 notify_of_hacked_weapons_tbl = 1;
5700 if(!MULTI_IS_TRACKER_GAME){
5701 notify_of_hacked_weapons_tbl = 0;
5703 if(notify_of_hacked_weapons_tbl){
5704 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){
5709 // if any of the players have hacked data
5711 for(idx=0; idx<MAX_PLAYERS; idx++){
5712 // look for hacked players
5713 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAXOR)){
5717 // message everyone - haha
5718 if(Net_players[idx].player != NULL){
5719 sprintf(err_string, "%s %s", Net_players[idx].player->callsign, XSTR("has hacked tables/data", 1271));
5721 sprintf(err_string, "somebody %s", XSTR("has hacked tables/data", 1271));
5723 send_game_chat_packet(Net_player, err_string, MULTI_MSG_ALL, NULL, NULL, 1);
5726 // if we found a hacked set of data
5729 if(MULTI_IS_TRACKER_GAME){
5730 // don't allow squad war matches to continue
5731 if(Netgame.type_flags & NG_TYPE_SW){
5733 // if this is squad war, don't allow it to continue
5734 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));
5739 // otherwise, warn the players that stats will not saved
5741 // if this is squad war, don't allow it to continue
5742 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){
5747 // non-pxo, just give a notice
5749 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){
5755 // check to see that we don't have too many observers
5756 observer_count = multi_num_observers();
5757 if(observer_count > Netgame.options.max_observers){
5758 // print up the error string
5759 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);
5761 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, err_string);
5765 // check to see that we have a valid # of players for the the # of ships in the game
5766 player_count = multi_num_players();
5767 if(player_count > Netgame.max_players){
5768 // print up the error string
5769 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);
5771 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, err_string);
5775 // check to see if teams are assigned properly in a team vs. team situation
5776 if(Netgame.type_flags & NG_TYPE_TEAM){
5777 if(!multi_team_ok_to_commit()){
5778 gamesnd_play_iface(SND_GENERAL_FAIL);
5779 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("Teams and/or team captains are not assigned properly", 793));
5785 if(!multi_create_verify_cds()){
5786 gamesnd_play_iface(SND_GENERAL_FAIL);
5788 #ifdef MULTIPLAYER_BETA_BUILD
5789 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, "You need 1 CD for every player!");
5791 #ifdef DVD_MESSAGE_HACK
5792 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You need 1 DVD for every 4 players!", 794));
5794 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You need 1 CD for every 4 players!", 794));
5800 // if we're playing on the tracker
5801 if(MULTI_IS_TRACKER_GAME){
5802 #ifdef PXO_CHECK_VALID_MISSIONS
5803 if((Multi_create_file_list == Multi_create_mission_list) && (Multi_create_file_list[abs_index].valid_status != MVALID_STATUS_VALID)){
5804 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){
5811 if(!(Netgame.type_flags & NG_TYPE_SW)){
5812 // if he is playing by himself, tell him stats will not be accepted
5813 if(multi_num_players() == 1){
5814 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){
5827 int multi_create_verify_cds()
5829 int player_count = multi_num_players();
5833 // count how many cds we have
5835 for(idx=0;idx<MAX_PLAYERS;idx++){
5836 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAS_CD)){
5841 // for the beta, everyone must have a CD
5842 #ifdef MULTIPLAYER_BETA_BUILD
5843 if(multi_cd_count < player_count){
5847 // determine if we have enough
5848 float ratio = (float)player_count / (float)multi_cd_count;
5849 // greater than a 4 to 1 ratio
5855 // we meet the conditions
5859 // returns an index into Multi_create_mission_list
5860 int multi_create_lookup_mission(char *fname)
5864 for(idx=0; idx<Multi_create_mission_count; idx++){
5865 if(!stricmp(fname, Multi_create_mission_list[idx].filename)){
5870 // couldn't find the mission
5874 // returns an index into Multi_create_campaign_list
5875 int multi_create_lookup_campaign(char *fname)
5879 for(idx=0; idx<Multi_create_campaign_count; idx++){
5880 if(!stricmp(fname, Multi_create_campaign_list[idx].filename)){
5885 // couldn't find the campaign
5889 void multi_create_refresh_pxo()
5891 // delete mvalid.cfg if it exists
5892 cf_delete(MULTI_VALID_MISSION_FILE, CF_TYPE_DATA);
5894 // refresh missions from the tracker
5895 multi_update_valid_missions();
5898 void multi_create_sw_clicked()
5900 netgame_info ng_temp;
5903 int file_index = multi_create_select_to_index(Multi_create_list_select);
5905 // either a temporary netgame or the real one
5906 if(MULTIPLAYER_MASTER){
5913 // maybe switch squad war off
5914 if(!Multi_create_sw_checkbox.checked()){
5915 // if the mission selected is a coop mission, go back to coop mode
5916 Assert(file_index != -1);
5917 if(file_index == -1){
5918 ng->type_flags = NG_TYPE_COOP;
5920 if(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_TEAMS){
5921 ng->type_flags = NG_TYPE_TVT;
5922 } else if(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_DOGFIGHT){
5923 ng->type_flags = NG_TYPE_DOGFIGHT;
5925 ng->type_flags = NG_TYPE_COOP;
5928 // switch squad war on
5930 Assert(file_index != -1);
5931 if((file_index == -1) || !(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_TEAMS)){
5932 Multi_create_sw_checkbox.set_state(0);
5934 // at this point we know its safe to switch squad war on
5935 ng->type_flags = NG_TYPE_SW;
5940 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5942 send_netgame_update_packet();
5944 // update all machines about stuff like respawns, etc.
5945 multi_options_update_netgame();
5947 // on the standalone
5949 // standalone will take care of polling the usertracker
5950 multi_options_update_mission(ng, Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ? 1 : 0);
5955 // -------------------------------------------------------------------------------------------------------------
5957 // MULTIPLAYER HOST OPTIONS SCREEN
5960 #define MULTI_HO_NUM_BUTTONS 12
5961 #define MULTI_HO_NUM_RADIO_BUTTONS 10
5965 #define MULTI_HO_PALETTE "InterfacePalette"
5967 static char *Multi_ho_bitmap_fname[GR_NUM_RESOLUTIONS] = {
5968 "MultiHost", // GR_640
5969 "2_MultiHost" // GR_1024
5972 static char *Multi_ho_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
5973 "MultiHost-M", // GR_640
5974 "2_MultiHost-M" // GR_1024
5978 UI_WINDOW Multi_ho_window; // the window object for the join screen
5979 UI_INPUTBOX Multi_ho_respawns; // the # of respawns allowed in the game
5980 UI_INPUTBOX Multi_ho_time_limit; // mission time limit
5981 UI_INPUTBOX Multi_ho_voice_wait; // wait time between tokens
5982 UI_INPUTBOX Multi_ho_kill_limit; // kill limit in a furball mission
5983 UI_INPUTBOX Multi_ho_obs; // # of observers we'll allow
5984 int Multi_ho_bitmap; // the background bitmap
5986 // constants for coordinate lookup
5987 #define MULTI_HO_X_COORD 0
5988 #define MULTI_HO_Y_COORD 1
5989 #define MULTI_HO_W_COORD 2
5990 #define MULTI_HO_H_COORD 3
5991 #define MULTI_HO_TEXT_X_COORD 4
5992 #define MULTI_HO_TEXT_Y_COORD 5
5995 #define MULTI_HO_MSG_RANK 0 // highest ranking players can do messaging
5996 #define MULTI_HO_MSG_LEADER 1 // wing/team leaders can do messaging
5997 #define MULTI_HO_MSG_ANY 2 // any player can do messaging
5998 #define MULTI_HO_MSG_HOST 3 // only the host can do messaging
5999 #define MULTI_HO_END_RANK 4 // highest rank can and host can end mission
6000 #define MULTI_HO_END_LEADER 5 // wing/team leaders and host can end the mission
6001 #define MULTI_HO_END_ANY 6 // any player can end the mission
6002 #define MULTI_HO_END_HOST 7 // only host can end the mission
6003 #define MULTI_HO_VOICE_ON 8 // voice toggled on
6004 #define MULTI_HO_VOICE_OFF 9 // voice toggled off
6005 #define MULTI_HO_HOST_MODIFIES 10 // only the host or team captains can modify ships/weapons in briefing
6006 #define MULTI_HO_ACCEPT 11 // accept button
6008 ui_button_info Multi_ho_buttons[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_BUTTONS] = {
6011 // who is allowed to message
6012 ui_button_info("MH_00", 13, 157, -1, -1, 0), // highest rank
6013 ui_button_info("MH_01", 13, 179, -1, -1, 1), // team/wing leader
6014 ui_button_info("MH_02", 13, 200, -1, -1, 2), // any
6015 ui_button_info("MH_03", 13, 223, -1, -1, 3), // host
6017 // who is allowed to end the mission
6018 ui_button_info("MH_09", 13, 273, -1, -1, 9), // highest rank
6019 ui_button_info("MH_10", 13, 295, -1, -1, 10), // team/wing leader
6020 ui_button_info("MH_11", 13, 317, -1, -1, 11), // any
6021 ui_button_info("MH_12", 13, 339, -1, -1, 12), // host
6023 // voice on/off button
6024 ui_button_info("MH_14", 396, 156, -1, -1, 14),
6025 ui_button_info("MH_15", 453, 156, -1, -1, 15),
6027 // host modifies ships
6028 ui_button_info("MH_19", 215, 410, -1, -1, 19),
6031 ui_button_info("MH_18", 560, 411, -1, -1, 18),
6033 // who is allowed to message
6034 ui_button_info("MH_00", 3, 160, 46, 166, 0), // highest rank
6035 ui_button_info("MH_01", 3, 179, 46, 185, 1), // team/wing leader
6036 ui_button_info("MH_02", 3, 196, 46, 203, 2), // any
6037 ui_button_info("MH_03", 3, 214, 46, 220, 3), // host
6039 // who is allowed to end the mission
6040 ui_button_info("MH_04", 3, 257, 46, 265, 4), // highest rank
6041 ui_button_info("MH_05", 3, 276, 46, 283, 5), // team/wing leader
6042 ui_button_info("MH_06", 3, 294, 46, 300, 6), // any
6043 ui_button_info("MH_07", 3, 311, 46, 317, 7), // host
6045 // voice on/off button
6046 ui_button_info("MH_09", 542, 158, 545, 185, 9),
6047 ui_button_info("MH_10", 598, 158, 604, 185, 10),
6049 // host modifies ships
6050 ui_button_info("MH_13", 542, 377, 437, 363, 13),
6053 ui_button_info("MH_14", 572, 428, 580, 414, 14),
6057 // who is allowed to message
6058 ui_button_info("2_MH_00", 5, 256, 73, 269, 0), // highest rank
6059 ui_button_info("2_MH_01", 5, 286, 73, 297, 1), // team/wing leader
6060 ui_button_info("2_MH_02", 5, 314, 73, 325, 2), // any
6061 ui_button_info("2_MH_03", 5, 341, 73, 352, 3), // host
6063 // who is allowed to end the mission
6064 ui_button_info("2_MH_04", 5, 412, 73, 425, 4), // highest rank
6065 ui_button_info("2_MH_05", 5, 442, 73, 452, 5), // team/wing leader
6066 ui_button_info("2_MH_06", 5, 470, 73, 480, 6), // any
6067 ui_button_info("2_MH_07", 5, 497, 73, 508, 7), // host
6069 // voice on/off button
6070 ui_button_info("2_MH_09", 867, 253, 872, 296, 9),
6071 ui_button_info("2_MH_10", 957, 253, 966, 296, 10),
6073 // host modifies ships
6074 ui_button_info("2_MH_13", 867, 603, 784, 581, 13),
6077 ui_button_info("2_MH_14", 916, 685, 925, 665, 14),
6080 UI_XSTR Multi_ho_text[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_BUTTONS] = {
6082 // not needed for FS1
6084 {"Highest rank", 1280, 46, 166, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_RANK].button},
6085 {"Team / wing-leader", 1281, 46, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_LEADER].button},
6086 {"Any", 1282, 46, 203, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_ANY].button},
6087 {"Host", 1283, 46, 220, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_HOST].button},
6088 {"Highest rank", 1280, 46, 265, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_RANK].button},
6089 {"Team / wing-leader", 1281, 46, 283, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_LEADER].button},
6090 {"Any", 1282, 46, 300, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_ANY].button},
6091 {"Host", 1283, 46, 317, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_HOST].button},
6092 {"On", 1285, 545, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_VOICE_ON].button},
6093 {"Off", 1286, 604, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_VOICE_OFF].button},
6094 {"Host modifies ships", 1287, 437, 363, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_HOST_MODIFIES].button},
6095 {"Exit", 1417, 572, 418, UI_XSTR_COLOR_PINK, -1, &Multi_ho_buttons[0][MULTI_HO_ACCEPT].button},
6099 // not needed for FS1
6101 {"Highest rank", 1280, 62, 269, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_RANK].button},
6102 {"Team / wing-leader", 1281, 62, 297, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_LEADER].button},
6103 {"Any", 1282, 62, 325, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_ANY].button},
6104 {"Host", 1283, 62, 352, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_HOST].button},
6105 {"Highest rank", 1280, 62, 425, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_RANK].button},
6106 {"Team / wing-leader", 1281, 62, 452, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_LEADER].button},
6107 {"Any", 1282, 62, 480, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_ANY].button},
6108 {"Host", 1283, 62, 508, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_HOST].button},
6109 {"On", 1285, 877, 294, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_VOICE_ON].button},
6110 {"Off", 1286, 967, 293, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_VOICE_OFF].button},
6111 {"Host modifies ships", 1287, 869, 589, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_HOST_MODIFIES].button},
6112 {"Exit", 1417, 953, 672, UI_XSTR_COLOR_PINK, -1, &Multi_ho_buttons[1][MULTI_HO_ACCEPT].button},
6117 // radio button controls
6118 #define MULTI_HO_NUM_RADIO_GROUPS 3
6119 #define MULTI_HO_MSG_GROUP 0 // group dealing with squadmate messaging
6120 #define MULTI_HO_END_GROUP 1 // group dealing with ending the mission
6121 #define MULTI_HO_VOICE_GROUP 2 // group dealing with voice stuff
6122 int Multi_ho_radio_groups[MULTI_HO_NUM_RADIO_GROUPS] = { // currently selected button in the radio button group
6125 int Multi_ho_radio_info[MULTI_HO_NUM_RADIO_BUTTONS][3] = { // info related to each of the radio buttons themselves
6126 // { group #, value, button id# }
6127 {0, 0, 0}, // highest ranking players can do messaging
6128 {0, 1, 1}, // wing/team leaders can do messaging
6129 {0, 2, 2}, // any player can do messaging
6130 {0, 3, 3}, // only host can do messaging
6131 {1, 0, 4}, // highest rank and host can end the mission
6132 {1, 1, 5}, // team/wing leader can end the mission
6133 {1, 2, 6}, // any player can end the mission
6134 {1, 3, 7}, // only the host can end the mission
6135 {2, 0, 8}, // voice toggled on
6136 {2, 1, 9} // voice toggled off
6140 #define MULTI_HO_NUM_SLIDERS 3
6141 #define MULTI_HO_SLIDER_VOICE_QOS 0 // voice quality of sound
6142 #define MULTI_HO_SLIDER_VOICE_DUR 1 // max duration of voice recording
6143 #define MULTI_HO_SLIDER_SKILL 2 // skill level
6150 UI_DOT_SLIDER_NEW slider; // because we have a class inside this struct, we need the constructor below..
6152 ho_sliders(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){}
6154 ho_sliders Multi_ho_sliders[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_SLIDERS] = {
6157 ho_sliders("MH_16", 396, 210, -1, -1, 16, 19, 10), // voice qos
6158 ho_sliders("MH_17", 396, 263, -1, -1, 17, 19, 10), // voice duration
6159 ho_sliders("MH_13", 10, 387, -1, -1, 13, 36, 5), // skill level
6161 ho_sliders("MH_11", 428, 214, 437, 199, 11, 19, 10), // voice qos
6162 ho_sliders("MH_12", 428, 261, 437, 246, 12, 19, 10), // voice duration
6163 ho_sliders("MH_08", 237, 454, 230, 411, 8, 36, 5), // skill level
6167 ho_sliders("2_MH_11", 684, 343, 690, 323, 11, 32, 10), // voice qos
6168 ho_sliders("2_MH_12", 685, 418, 837, 468, 12, 32, 10), // voice duration
6169 ho_sliders("2_MH_08", 379, 727, 369, 663, 8, 60, 5), // skill level
6173 int Multi_ho_mission_respawn;
6175 int Multi_ho_host_modifies;
6177 // whether or not any of the inputboxes on this screen had focus last frame
6178 int Multi_ho_lastframe_input = 0;
6180 // game information text areas
6184 #define MULTI_HO_NUM_TITLES 0
6186 #define MULTI_HO_NUM_TITLES 14
6188 UI_XSTR Multi_ho_titles[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_TITLES] = {
6190 // not needed for FS1
6192 { "AI Orders", 1289, 32, 144, UI_XSTR_COLOR_GREEN, -1, NULL },
6193 { "End Mission", 1290, 32, 242, UI_XSTR_COLOR_GREEN, -1, NULL },
6194 { "Time Limit", 1291, 32, 347, UI_XSTR_COLOR_GREEN, -1, NULL },
6195 { "Min", 1292, 74, 362, UI_XSTR_COLOR_GREEN, -1, NULL },
6196 { "Respawn Limit", 1288, 32, 378, UI_XSTR_COLOR_GREEN, -1, NULL },
6197 { "Kill Limit", 1293, 32, 409, UI_XSTR_COLOR_GREEN, -1, NULL },
6198 { "Observers", 1294, 32, 441, UI_XSTR_COLOR_GREEN, -1, NULL },
6199 { "Skill Level", 1284, 230, 411, UI_XSTR_COLOR_GREEN, -1, NULL },
6200 { "Voice Transmission", 1295, 437, 144, UI_XSTR_COLOR_GREEN, -1, NULL },
6201 { "Voice Quality", 1296, 437, 199, UI_XSTR_COLOR_GREEN, -1, NULL },
6202 { "Message Duration", 1297, 437, 246, UI_XSTR_COLOR_GREEN, -1, NULL },
6203 { "sec", 1522, 523, 292, UI_XSTR_COLOR_GREEN, -1, NULL },
6204 { "sec", 1523, 523, 332, UI_XSTR_COLOR_GREEN, -1, NULL },
6205 { "Voice Wait", 1298, 437, 313, UI_XSTR_COLOR_GREEN, -1, NULL },
6209 // not needed for FS1
6211 { "AI Orders", 1289, 48, 238, UI_XSTR_COLOR_GREEN, -1, NULL },
6212 { "End Mission", 1290, 48, 394, UI_XSTR_COLOR_GREEN, -1, NULL },
6213 { "Time Limit", 1291, 50, 568, UI_XSTR_COLOR_GREEN, -1, NULL },
6214 { "Min", 1292, 119, 581, UI_XSTR_COLOR_GREEN, -1, NULL },
6215 { "Respawn Limit", 1288, 50, 618, UI_XSTR_COLOR_GREEN, -1, NULL },
6216 { "Kill Limit", 1293, 50, 668, UI_XSTR_COLOR_GREEN, -1, NULL },
6217 { "Observers", 1294, 50, 718, UI_XSTR_COLOR_GREEN, -1, NULL },
6218 { "Skill Level", 1284, 398, 670, UI_XSTR_COLOR_GREEN, -1, NULL },
6219 { "Voice Transmission", 1295, 869, 239, UI_XSTR_COLOR_GREEN, -1, NULL },
6220 { "Voice Quality", 1296, 690, 331, UI_XSTR_COLOR_GREEN, -1, NULL },
6221 { "Message Duration", 1297, 690, 405, UI_XSTR_COLOR_GREEN, -1, NULL },
6222 { "sec", 1522, 837, 467, UI_XSTR_COLOR_GREEN, -1, NULL },
6223 { "sec", 1523, 837, 534, UI_XSTR_COLOR_GREEN, -1, NULL },
6224 { "Voice Wait", 1298, 742, 510, UI_XSTR_COLOR_GREEN, -1, NULL },
6229 // mission time limit input box
6230 int Ho_time_coords[GR_NUM_RESOLUTIONS][4] = {
6243 // furball kill limit input box
6244 int Ho_kill_coords[GR_NUM_RESOLUTIONS][4] = {
6257 // voice recording duration text display area
6258 int Ho_vd_coords[GR_NUM_RESOLUTIONS][4] = {
6271 // voice token wait input box
6272 int Ho_vw_coords[GR_NUM_RESOLUTIONS][6] = {
6285 // observer count input box
6286 int Ho_obs_coords[GR_NUM_RESOLUTIONS][4] = {
6299 // skill text description area
6300 int Ho_st_coords[GR_NUM_RESOLUTIONS][4] = {
6313 // respawn input box
6314 int Ho_rsp_coords[GR_NUM_RESOLUTIONS][6] = {
6327 // respawn max text area
6328 int Ho_max_rsp_coords[GR_NUM_RESOLUTIONS][2] = {
6341 // maximum values for various input boxes (to notify user of overruns)
6342 #define MULTI_HO_MAX_TIME_LIMIT 500
6343 #define MULTI_HO_MAX_TOKEN_WAIT 5
6344 #define MULTI_HO_MAX_KILL_LIMIT 9999
6345 #define MULTI_HO_MAX_OBS 4
6347 // LOCAL function definitions
6348 void multi_ho_check_buttons();
6349 void multi_ho_button_pressed(int n);
6350 void multi_ho_draw_radio_groups();
6351 void multi_ho_accept_hit();
6352 void multi_ho_get_options();
6353 void multi_ho_apply_options();
6354 void multi_ho_display_record_time();
6355 int multi_ho_check_values();
6356 void multi_ho_check_focus();
6357 void multi_ho_blit_max_respawns();
6358 void multi_ho_display_skill_level();
6360 void multi_host_options_init()
6364 // create the interface window
6365 Multi_ho_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
6366 Multi_ho_window.set_mask_bmap(Multi_ho_bitmap_mask_fname[gr_screen.res]);
6368 // load the background bitmap
6369 Multi_ho_bitmap = bm_load(Multi_ho_bitmap_fname[gr_screen.res]);
6370 if(Multi_ho_bitmap < 0){
6371 // we failed to load the bitmap - this is very bad
6375 // initialize the common notification messaging
6376 multi_common_notify_init();
6378 // use the common interface palette
6379 multi_common_set_palette();
6381 // create the interface buttons
6382 for(idx=0;idx<MULTI_HO_NUM_BUTTONS;idx++){
6383 // create the object
6384 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);
6386 // set the sound to play when highlighted
6387 Multi_ho_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
6389 // set the ani for the button
6390 Multi_ho_buttons[gr_screen.res][idx].button.set_bmaps(Multi_ho_buttons[gr_screen.res][idx].filename);
6392 // set the hotspot, ignoring the skill level button
6393 Multi_ho_buttons[gr_screen.res][idx].button.link_hotspot(Multi_ho_buttons[gr_screen.res][idx].hotspot);
6397 Multi_ho_window.add_XSTR(&Multi_ho_text[gr_screen.res][idx]);
6402 for(idx=0; idx<MULTI_HO_NUM_TITLES; idx++){
6403 Multi_ho_window.add_XSTR(&Multi_ho_titles[gr_screen.res][idx]);
6406 // create the interface sliders
6407 for(idx=0; idx<MULTI_HO_NUM_SLIDERS; idx++){
6408 // create the object
6409 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);
6412 // create the respawn count input box
6413 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);
6414 // if we're in campaign mode, disable it
6415 if(Netgame.campaign_mode == MP_CAMPAIGN){
6416 Multi_ho_respawns.set_text(XSTR("NA",795)); // [[ Not applicable ]]
6417 Multi_ho_respawns.disable();
6419 Multi_ho_respawns.set_valid_chars("0123456789");
6422 // create the time limit input box
6423 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);
6424 Multi_ho_time_limit.set_valid_chars("-0123456789");
6426 // create the voice token wait input box
6427 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);
6428 Multi_ho_voice_wait.set_valid_chars("01243456789");
6430 // create the furball kill limit input box
6431 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);
6432 Multi_ho_kill_limit.set_valid_chars("0123456789");
6434 // create the observer limit input box
6435 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);
6436 Multi_ho_obs.set_valid_chars("01234");
6438 // load in the current netgame defaults
6439 multi_ho_get_options();
6441 // whether or not any of the inputboxes on this screen had focus last frame
6442 Multi_ho_lastframe_input = 0;
6444 // get the # of respawns for the currently selected mission (if any)
6445 if(Multi_create_list_select != -1){
6446 int abs_index = multi_create_select_to_index(Multi_create_list_select);
6448 // if he has a valid mission selected
6450 Multi_ho_mission_respawn = (int)Multi_create_file_list[abs_index].respawn;
6452 Multi_ho_mission_respawn = -1;
6455 Multi_ho_mission_respawn = -1;
6459 void multi_ho_update_sliders()
6461 // game skill slider
6462 if (Game_skill_level != Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos) {
6463 if ( !(Netgame.type_flags & NG_TYPE_TEAM) ){
6464 Game_skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6465 gamesnd_play_iface(SND_USER_SELECT);
6467 Game_skill_level = NUM_SKILL_LEVELS / 2;
6471 // get the voice qos options
6472 if (Netgame.options.voice_qos != (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1)) {
6473 Netgame.options.voice_qos = (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1);
6474 gamesnd_play_iface(SND_USER_SELECT);
6477 // get the voice duration options
6478 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)) {
6479 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);
6480 gamesnd_play_iface(SND_USER_SELECT);
6485 void multi_host_options_do()
6490 k = Multi_ho_window.process();
6493 // process any keypresses
6496 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
6499 case KEY_CTRLED + KEY_ENTER :
6500 gamesnd_play_iface(SND_COMMIT_PRESSED);
6501 multi_ho_accept_hit();
6505 // process any button clicks
6506 multi_ho_check_buttons();
6508 // update the sliders
6509 multi_ho_update_sliders();
6511 // make sure that the chatbox inputbox and any inputbox on this screen are mutually exclusive in terms of focus
6512 multi_ho_check_focus();
6514 // draw the background, etc
6516 GR_MAYBE_CLEAR_RES(Multi_ho_bitmap);
6517 if(Multi_ho_bitmap != -1){
6518 gr_set_bitmap(Multi_ho_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
6521 Multi_ho_window.draw();
6523 // draw all the radio buttons properly
6524 multi_ho_draw_radio_groups();
6526 // display any pending notification messages
6527 multi_common_notify_do();
6529 // display the voice record time settings
6530 multi_ho_display_record_time();
6532 // maybe display the max # of respawns next to the respawn input box
6533 multi_ho_blit_max_respawns();
6535 // blit the proper skill level
6536 multi_ho_display_skill_level();
6538 // blit the "host modifies button"
6539 if(Multi_ho_host_modifies){
6540 Multi_ho_buttons[gr_screen.res][MULTI_HO_HOST_MODIFIES].button.draw_forced(2);
6543 // process and show the chatbox thingie
6547 Multi_ho_window.draw_tooltip();
6549 // display the voice status indicator
6550 multi_common_voice_display_status();
6556 void multi_host_options_close()
6558 // unload any bitmaps
6559 if(!bm_unload(Multi_ho_bitmap)){
6560 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_ho_bitmap_fname[gr_screen.res]));
6563 // destroy the UI_WINDOW
6564 Multi_ho_window.destroy();
6567 void multi_ho_check_buttons()
6570 for(idx=0;idx<MULTI_HO_NUM_BUTTONS;idx++){
6571 // we only really need to check for one button pressed at a time, so we can break after
6573 if(Multi_ho_buttons[gr_screen.res][idx].button.pressed()){
6574 multi_ho_button_pressed(idx);
6580 void multi_ho_button_pressed(int n)
6582 int radio_index,idx;
6583 int x_pixel,y_pixel;
6585 // get the pixel position of the click
6586 Multi_ho_buttons[gr_screen.res][n].button.get_mouse_pos(&x_pixel,&y_pixel);
6589 // clicked on the accept button
6590 case MULTI_HO_ACCEPT:
6591 gamesnd_play_iface(SND_COMMIT_PRESSED);
6592 multi_ho_accept_hit();
6595 // clicked on the host/captains only modify button
6596 case MULTI_HO_HOST_MODIFIES:
6597 // toggle it on or off
6598 Multi_ho_host_modifies = !Multi_ho_host_modifies;
6599 gamesnd_play_iface(SND_USER_SELECT);
6603 // look through the radio buttons and see which one this corresponds to
6605 for(idx=0;idx<MULTI_HO_NUM_RADIO_BUTTONS;idx++){
6606 if(Multi_ho_radio_info[idx][2] == n){
6611 Assert(radio_index != -1);
6613 // check to see if a radio button was pressed
6614 if(radio_index < MULTI_HO_NUM_RADIO_BUTTONS){
6615 // see if this value is already picked for this radio group
6616 if(Multi_ho_radio_groups[Multi_ho_radio_info[radio_index][0]] != Multi_ho_radio_info[radio_index][1]){
6617 gamesnd_play_iface(SND_USER_SELECT);
6618 Multi_ho_radio_groups[Multi_ho_radio_info[radio_index][0]] = Multi_ho_radio_info[radio_index][1];
6620 gamesnd_play_iface(SND_GENERAL_FAIL);
6625 void multi_ho_draw_radio_groups()
6629 // go through each item and draw it if it is the selected button in its respective group
6630 for(idx=0;idx<MULTI_HO_NUM_RADIO_BUTTONS;idx++){
6631 /// if this button is the currently selected one in its group
6632 if(Multi_ho_radio_info[idx][1] == Multi_ho_radio_groups[Multi_ho_radio_info[idx][0]]){
6633 Multi_ho_buttons[gr_screen.res][Multi_ho_radio_info[idx][2]].button.draw_forced(2);
6638 void multi_ho_accept_hit()
6642 // check the values in the input boxes
6643 if(!multi_ho_check_values()){
6647 // zero out the netgame flags
6650 // set default options
6651 Netgame.options.flags = (MSO_FLAG_INGAME_XFER | MSO_FLAG_ACCEPT_PIX);
6653 // set the squadmate messaging flags
6654 switch(Multi_ho_radio_groups[MULTI_HO_MSG_GROUP]){
6656 Netgame.options.squad_set = MSO_SQUAD_RANK;
6659 Netgame.options.squad_set = MSO_SQUAD_LEADER;
6662 Netgame.options.squad_set = MSO_SQUAD_ANY;
6665 Netgame.options.squad_set = MSO_SQUAD_HOST;
6671 // set the end mission flags
6672 switch(Multi_ho_radio_groups[MULTI_HO_END_GROUP]){
6674 Netgame.options.endgame_set = MSO_END_RANK;
6677 Netgame.options.endgame_set = MSO_END_LEADER;
6680 Netgame.options.endgame_set = MSO_END_ANY;
6683 Netgame.options.endgame_set = MSO_END_HOST;
6689 // set the voice toggle
6690 switch(Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP]){
6692 Netgame.options.flags &= ~(MSO_FLAG_NO_VOICE);
6695 Netgame.options.flags |= MSO_FLAG_NO_VOICE;
6701 // get the voice qos options
6702 Netgame.options.voice_qos = (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1);
6704 // get the voice duration options
6705 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);
6707 // set the skill level. If in team vs. team mode, preserve the old setting before saving
6708 // the pilot file. I'll bet that this doesn't work though because the pilot file gets
6709 // written in a bunch of locations....sigh.
6710 if ( !(Netgame.type_flags & NG_TYPE_TEAM) ){
6711 Game_skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6713 Game_skill_level = NUM_SKILL_LEVELS / 2;
6716 // set the netgame respawn count
6717 // maybe warn the user that respawns will not be used for a campaign mission
6718 if(Netgame.campaign_mode == MP_SINGLE){
6719 Multi_ho_respawns.get_text(resp_str);
6720 uint temp_respawn = (uint)atoi(resp_str);
6721 // if he currently has no mission selected, let the user set any # of respawns
6722 if((int)temp_respawn > Multi_ho_mission_respawn){
6723 if(Multi_ho_mission_respawn == -1){
6724 Netgame.respawn = temp_respawn;
6725 Netgame.options.respawn = temp_respawn;
6727 // this should have been taken care of by the interface code
6732 Netgame.options.respawn = temp_respawn;
6733 Netgame.respawn = temp_respawn;
6737 // get the mission time limit
6738 Multi_ho_time_limit.get_text(resp_str);
6739 int temp_time = atoi(resp_str);
6741 Netgame.options.mission_time_limit = fl2f(-1.0f);
6742 } else if(temp_time > MULTI_HO_MAX_TIME_LIMIT){
6745 Netgame.options.mission_time_limit = fl2f(60.0f * (float)temp_time);
6748 // get observer count options
6749 Multi_ho_obs.get_text(resp_str);
6750 int temp_obs = atoi(resp_str);
6751 if(temp_obs > MULTI_HO_MAX_OBS){
6754 Netgame.options.max_observers = (ubyte)temp_obs;
6756 // get the furball kill limit
6757 Multi_ho_kill_limit.get_text(resp_str);
6758 int temp_kills = atoi(resp_str);
6759 if(temp_kills > MULTI_HO_MAX_KILL_LIMIT){
6762 Netgame.options.kill_limit = temp_kills;
6764 // get the token wait limit
6765 Multi_ho_voice_wait.get_text(resp_str);
6766 int temp_wait = atoi(resp_str);
6767 if(temp_wait > MULTI_HO_MAX_TOKEN_WAIT){
6770 Netgame.options.voice_token_wait = (temp_wait * 1000);
6772 // set the netgame option
6773 Netgame.options.skill_level = (ubyte)Game_skill_level;
6775 // get whether we're in host/captains only modify mode
6776 Netgame.options.flags &= ~(MSO_FLAG_SS_LEADERS);
6777 if(Multi_ho_host_modifies){
6778 Netgame.options.flags |= MSO_FLAG_SS_LEADERS;
6781 // store these values locally
6782 memcpy(&Player->m_local_options,&Net_player->p_info.options,sizeof(multi_local_options));
6783 memcpy(&Player->m_server_options,&Netgame.options,sizeof(multi_server_options));
6784 write_pilot_file(Player);
6786 // apply any changes in settings (notify everyone of voice qos changes, etc)
6787 multi_ho_apply_options();
6789 // move back to the create game screen
6790 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
6793 void multi_ho_get_options()
6797 // set the squadmate messaging buttons
6798 switch(Netgame.options.squad_set){
6799 case MSO_SQUAD_RANK :
6800 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 0;
6802 case MSO_SQUAD_LEADER:
6803 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 1;
6806 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 2;
6808 case MSO_SQUAD_HOST:
6809 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 3;
6815 // set the mission end buttons
6816 switch(Netgame.options.endgame_set){
6818 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 0;
6820 case MSO_END_LEADER:
6821 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 1;
6824 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 2;
6827 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 3;
6833 // set the voice toggle buttons
6834 if(Netgame.options.flags & MSO_FLAG_NO_VOICE){
6835 Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP] = 1;
6837 Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP] = 0;
6840 // get the voice qos options
6841 Assert((Netgame.options.voice_qos >= 1) && (Netgame.options.voice_qos <= 10));
6842 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos = (Netgame.options.voice_qos - 1);
6844 // get the voice duration options
6845 Assert((Netgame.options.voice_record_time > 0) && (Netgame.options.voice_record_time <= MULTI_VOICE_MAX_TIME));
6846 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos = ((int)((float)Netgame.options.voice_record_time / 500.0f)) - 1;
6848 // get the current skill level
6849 Assert((Game_skill_level >= 0) && (Game_skill_level < NUM_SKILL_LEVELS));
6850 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos = Game_skill_level;
6852 // get the # of observers
6853 memset(resp_str,0,10);
6854 sprintf(resp_str,"%d",Netgame.options.max_observers);
6855 Multi_ho_obs.set_text(resp_str);
6857 // set the respawn count
6858 if(Netgame.campaign_mode == MP_SINGLE){
6859 memset(resp_str,0,10);
6860 sprintf(resp_str,"%d",Netgame.respawn);
6861 Multi_ho_respawns.set_text(resp_str);
6864 // set the mission time limit
6865 memset(resp_str,0,10);
6866 float tl = f2fl(Netgame.options.mission_time_limit);
6867 sprintf(resp_str,"%d",(int)(tl / 60.0f));
6868 Multi_ho_time_limit.set_text(resp_str);
6870 // set the furball kill limit
6871 memset(resp_str,0,10);
6872 sprintf(resp_str,"%d",Netgame.options.kill_limit);
6873 Multi_ho_kill_limit.set_text(resp_str);
6875 // set the token wait time
6876 memset(resp_str,0,10);
6877 sprintf(resp_str,"%d",Netgame.options.voice_token_wait / 1000);
6878 Multi_ho_voice_wait.set_text(resp_str);
6880 // get whether we're in host/captains only modify mode
6881 if(Netgame.options.flags & MSO_FLAG_SS_LEADERS){
6882 Multi_ho_host_modifies = 1;
6884 Multi_ho_host_modifies = 0;
6888 void multi_ho_apply_options()
6890 // if the voice qos or duration has changed, apply the change
6891 multi_voice_maybe_update_vars(Netgame.options.voice_qos,Netgame.options.voice_record_time);
6893 // send an options update
6894 multi_options_update_netgame();
6897 // display the voice record time settings
6898 void multi_ho_display_record_time()
6901 int full_seconds, half_seconds;
6904 memset(time_str,0,30);
6907 full_seconds = (((Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 500) / 1000);
6909 // get the half-seconds
6910 half_seconds = ((((Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 500) % 1000) / 500) * 5;
6912 // format the string
6913 sprintf(time_str,"%d.%d",full_seconds,half_seconds);
6914 gr_set_color_fast(&Color_bright);
6915 gr_string(Ho_vd_coords[gr_screen.res][MULTI_HO_X_COORD],Ho_vd_coords[gr_screen.res][MULTI_HO_Y_COORD],time_str);
6918 int multi_ho_check_values()
6922 memset(val_txt,0,255);
6924 // check against respawn settings
6925 if(Multi_ho_mission_respawn != -1){
6926 Multi_ho_respawns.get_text(val_txt);
6927 // if the value is invalid, let the user know
6928 if(atoi(val_txt) > Multi_ho_mission_respawn){
6929 memset(val_txt,0,255);
6930 sprintf(val_txt,XSTR("Warning\nRespawn count in greater than mission specified max (%d)",796),Multi_ho_mission_respawn);
6931 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6936 // check against mission time limit max
6937 Multi_ho_time_limit.get_text(val_txt);
6938 // if the value is invalid, force it to be valid
6939 if(atoi(val_txt) > MULTI_HO_MAX_TIME_LIMIT){
6940 memset(val_txt,0,255);
6941 sprintf(val_txt,XSTR("Warning\nMission time limit is greater than max allowed (%d)",797),MULTI_HO_MAX_TIME_LIMIT);
6942 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6946 // check against max observer limit
6947 Multi_ho_obs.get_text(val_txt);
6948 // if the value is invalid, force it to be valid
6949 if(atoi(val_txt) > MULTI_HO_MAX_OBS){
6950 memset(val_txt,0,255);
6951 sprintf(val_txt,XSTR("Warning\nObserver count is greater than max allowed (%d)",798),MULTI_HO_MAX_OBS);
6952 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6956 // check against furball kill limit
6957 Multi_ho_kill_limit.get_text(val_txt);
6958 // if the value is invalid, force it to be valid
6959 if(atoi(val_txt) > MULTI_HO_MAX_KILL_LIMIT){
6960 memset(val_txt,0,255);
6961 sprintf(val_txt,XSTR("Warning\nMission kill limit is greater than max allowed (%d)",799),MULTI_HO_MAX_KILL_LIMIT);
6962 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6966 // check against the token wait limit
6967 Multi_ho_voice_wait.get_text(val_txt);
6968 if(atoi(val_txt) > MULTI_HO_MAX_TOKEN_WAIT){
6969 memset(val_txt,0,255);
6970 sprintf(val_txt,XSTR("Warning\nvoice wait time is greater than max allowed (%d)",800),MULTI_HO_MAX_TOKEN_WAIT);
6971 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6975 // all values are valid
6979 void multi_ho_check_focus()
6981 // if an inputbox has been pressed (hit enter), lose its focus
6982 if (Multi_ho_respawns.pressed() || Multi_ho_time_limit.pressed() || Multi_ho_voice_wait.pressed() || Multi_ho_kill_limit.pressed() || Multi_ho_obs.pressed()) {
6983 Multi_ho_respawns.clear_focus();
6984 Multi_ho_time_limit.clear_focus();
6985 Multi_ho_voice_wait.clear_focus();
6986 Multi_ho_kill_limit.clear_focus();
6987 Multi_ho_obs.clear_focus();
6988 gamesnd_play_iface(SND_COMMIT_PRESSED);
6989 chatbox_set_focus();
6990 Multi_ho_lastframe_input = 0;
6992 } else if(!Multi_ho_lastframe_input) {
6993 // if we didn't have focus last frame
6994 if(Multi_ho_respawns.has_focus() || Multi_ho_time_limit.has_focus() || Multi_ho_kill_limit.has_focus() || Multi_ho_voice_wait.has_focus() ){
6995 chatbox_lose_focus();
6997 Multi_ho_lastframe_input = 1;
7000 // if we _did_ have focus last frame
7002 // if we no longer have focus on any of the input boxes, set the focus on the chatbox
7003 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()){
7004 chatbox_set_focus();
7006 // if the chatbox now has focus, clear all focus from our inputboxes
7007 else if (chatbox_has_focus()) {
7008 Multi_ho_respawns.clear_focus();
7009 Multi_ho_time_limit.clear_focus();
7010 Multi_ho_kill_limit.clear_focus();
7011 Multi_ho_voice_wait.clear_focus();
7013 Multi_ho_lastframe_input = 0;
7018 void multi_ho_blit_max_respawns()
7022 // if we're in campaign mode, do nothing
7023 if(Netgame.campaign_mode == MP_CAMPAIGN){
7027 // otherwise blit the max as specified by the current mission file
7028 sprintf(string,"(%d)",Multi_ho_mission_respawn);
7029 gr_set_color_fast(&Color_normal);
7030 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);
7033 void multi_ho_display_skill_level()
7035 int skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
7038 Assert((skill_level >= 0) && (skill_level < NUM_SKILL_LEVELS));
7039 if((skill_level < 0) || (skill_level >= NUM_SKILL_LEVELS)){
7043 gr_set_color_fast(&Color_bright);
7044 gr_string(Ho_st_coords[gr_screen.res][0], Ho_st_coords[gr_screen.res][1], Skill_level_names(skill_level, 1));
7047 // -------------------------------------------------------------------------------------------------------------
7049 // MULTIPLAYER JOIN SCREEN
7052 #define MULTI_JW_NUM_BUTTONS 8
7056 #define MULTI_JW_PALETTE "InterfacePalette"
7058 static char *Multi_jw_bitmap_fname[GR_NUM_RESOLUTIONS] = {
7059 "MultiJoinWait", // GR_640
7060 "2_MultiJoinWait" // GR_1024
7063 static char *Multi_jw_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
7064 "MultiJoinWait-M", // GR_640
7065 "2_MultiJoinWait-M" // GR_1024
7071 #define MJW_SCROLL_PLAYERS_UP 0
7072 #define MJW_SCROLL_PLAYERS_DOWN 1
7075 #define MJW_PILOT_INFO 4
7076 #define MJW_SCROLL_INFO_UP 5
7077 #define MJW_SCROLL_INFO_DOWN 6
7078 #define MJW_CANCEL 7
7080 UI_WINDOW Multi_jw_window; // the window object for the join screen
7081 int Multi_jw_bitmap; // the background bitmap
7083 // constants for coordinate lookup
7084 #define MJW_X_COORD 0
7085 #define MJW_Y_COORD 1
7086 #define MJW_W_COORD 2
7087 #define MJW_H_COORD 3
7089 ui_button_info Multi_jw_buttons[GR_NUM_RESOLUTIONS][MULTI_JW_NUM_BUTTONS] = {
7091 ui_button_info("MJW_00", 1, 24, -1, -1, 0),
7092 ui_button_info("MJW_01", 1, 66, -1, -1, 1),
7093 ui_button_info("MJW_02", 30, 244, 20, 272, 2),
7094 ui_button_info("MJW_03", 84, 244, 73, 272, 3),
7095 ui_button_info("MJW_04", 139, 242, 134, 272, 4),
7096 ui_button_info("MJW_05", 1, 406, -1, -1, 5),
7097 ui_button_info("MJW_06", 1, 447, -1, -1, 6),
7098 ui_button_info("MJW_07", 577, 428, 570, 414, 7),
7101 ui_button_info("2_MJW_00", 2, 38, -1, -1, 0),
7102 ui_button_info("2_MJW_01", 2, 106, -1, -1, 1),
7103 ui_button_info("2_MJW_02", 48, 390, 47, 435, 2),
7104 ui_button_info("2_MJW_03", 134, 390, 133, 435, 3),
7105 ui_button_info("2_MJW_04", 223, 388, 225, 435, 4),
7106 ui_button_info("2_MJW_05", 2, 649, -1, -1, 5),
7107 ui_button_info("2_MJW_06", 2, 715, -1, -1, 6),
7108 ui_button_info("2_MJW_07", 923, 685, 931, 667, 7),
7113 #define MULTI_JW_NUM_TEXT 0
7115 #define MULTI_JW_NUM_TEXT 7
7118 UI_XSTR Multi_jw_text[GR_NUM_RESOLUTIONS][MULTI_JW_NUM_TEXT] = {
7120 // not needed for FS1
7122 { "Team 1", 1308, 20, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_TEAM0].button },
7123 { "Team 2", 1309, 73, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_TEAM1].button },
7124 { "Pilot", 1310, 134, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_PILOT_INFO].button },
7125 { "Info", 1311, 134, 283, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_PILOT_INFO].button },
7126 { "Cancel", 387, 570, 414, UI_XSTR_COLOR_PINK, -1, &Multi_jw_buttons[0][MJW_CANCEL].button },
7127 { "Players", 1269, 38, 8, UI_XSTR_COLOR_GREEN, -1, NULL },
7128 { "Choose Team", 1312, 27, 231, UI_XSTR_COLOR_GREEN, -1, NULL },
7132 // not needed for FS1
7134 { "Team 1", 1308, 47, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_TEAM0].button },
7135 { "Team 2", 1309, 133, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_TEAM1].button },
7136 { "Pilot", 1310, 225, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_PILOT_INFO].button },
7137 { "Info", 1311, 225, 446, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_PILOT_INFO].button },
7138 { "Cancel", 387, 931, 667, UI_XSTR_COLOR_PINK, -1, &Multi_jw_buttons[1][MJW_CANCEL].button },
7139 { "Players", 1269, 165, 12, UI_XSTR_COLOR_GREEN, -1, NULL },
7140 { "Choose Team", 1312, 45, 373, UI_XSTR_COLOR_GREEN, -1, NULL },
7145 int Mjw_players_coords[GR_NUM_RESOLUTIONS][4] = {
7154 int Mjw_mission_name_coords[GR_NUM_RESOLUTIONS][2] = {
7163 // squad war checkbox
7164 UI_CHECKBOX Multi_jw_sw_checkbox;
7165 char *Multi_jw_sw_checkbox_fname[GR_NUM_RESOLUTIONS] = {
7169 int Multi_jw_sw_checkbox_coords[GR_NUM_RESOLUTIONS][2] = {
7177 int Multi_jw_sw_checkbox_text[GR_NUM_RESOLUTIONS][2] = {
7187 // player list control thingie defs
7188 #define MULTI_JW_PLIST_MAX_DISPLAY 19
7189 int Multi_jw_plist_select_flag; // indicates whether we currently have a selected player
7190 short Multi_jw_plist_select_id; // id of the current selected player
7191 UI_BUTTON Multi_jw_plist_select_button; // for selecting a player
7193 int Multi_jw_should_show_popup = 0;
7195 // LOCAL function definitions
7196 void multi_jw_check_buttons();
7197 void multi_jw_button_pressed(int n);
7198 void multi_jw_do_netstuff();
7199 void multi_jw_scroll_players_up();
7200 void multi_jw_scroll_players_down();
7201 void multi_jw_plist_process();
7202 void multi_jw_plist_blit_normal();
7203 void multi_jw_plist_blit_team();
7204 short multi_jw_get_mouse_id();
7206 void multi_game_client_setup_init()
7210 // create the interface window
7211 Multi_jw_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
7212 Multi_jw_window.set_mask_bmap(Multi_jw_bitmap_mask_fname[gr_screen.res]);
7214 // load the background bitmap
7215 Multi_jw_bitmap = bm_load(Multi_jw_bitmap_fname[gr_screen.res]);
7216 if(Multi_jw_bitmap < 0){
7217 // we failed to load the bitmap - this is very bad
7221 // initialize the player list data
7222 Multi_jw_plist_select_flag = 0;
7223 Multi_jw_plist_select_id = -1;
7225 // kill any old instances of the chatbox and create a new one
7227 chatbox_create(CHATBOX_FLAG_BIG | CHATBOX_FLAG_DRAW_BOX | CHATBOX_FLAG_BUTTONS);
7229 // initialize the common notification messaging
7230 multi_common_notify_init();
7232 // initialize the common mission info display area.
7233 multi_common_set_text("");
7235 // use the common interface palette
7236 multi_common_set_palette();
7238 // create the interface buttons
7239 for(idx=0; idx<MULTI_JW_NUM_BUTTONS; idx++){
7240 // create the object
7241 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);
7243 // set the sound to play when highlighted
7244 Multi_jw_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
7246 // set the ani for the button
7247 Multi_jw_buttons[gr_screen.res][idx].button.set_bmaps(Multi_jw_buttons[gr_screen.res][idx].filename);
7250 Multi_jw_buttons[gr_screen.res][idx].button.link_hotspot(Multi_jw_buttons[gr_screen.res][idx].hotspot);
7253 // if this is a PXO game, enable the squadwar checkbox
7254 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);
7255 Multi_jw_sw_checkbox.set_bmaps(Multi_jw_sw_checkbox_fname[gr_screen.res], 6, 0);
7256 Multi_jw_sw_checkbox.disable();
7257 if(!MULTI_IS_TRACKER_GAME){
7258 Multi_jw_sw_checkbox.hide();
7262 for(idx=0; idx<MULTI_JW_NUM_TEXT; idx++){
7263 Multi_jw_window.add_XSTR(&Multi_jw_text[gr_screen.res][idx]);
7266 // create the player select list button and hide it
7267 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);
7268 Multi_jw_plist_select_button.hide();
7271 Multi_jw_buttons[gr_screen.res][MJW_CANCEL].button.set_hotkey(KEY_ESC);
7273 // remove campaign flags
7274 Game_mode &= ~(GM_CAMPAIGN_MODE);
7276 // tell the server we have finished joining
7277 Net_player->state = NETPLAYER_STATE_JOINED;
7278 send_netplayer_update_packet();
7281 ml_printf(NOX("Joined netgame %s"), Netgame.name);
7283 // send any appropriate files
7284 multi_data_send_my_junk();
7287 void multi_game_client_setup_do_frame()
7290 int k = chatbox_process();
7291 char mission_text[255];
7292 k = Multi_jw_window.process(k,0);
7294 Multi_jw_should_show_popup = 0;
7296 // process any button clicks
7297 multi_jw_check_buttons();
7299 // do any network related stuff
7300 multi_jw_do_netstuff();
7302 // draw the background, etc
7304 GR_MAYBE_CLEAR_RES(Multi_jw_bitmap);
7305 if(Multi_jw_bitmap != -1){
7306 gr_set_bitmap(Multi_jw_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7310 // if we're not in team vs. team mode, don't draw the team buttons
7311 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
7312 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.hide();
7313 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.hide();
7314 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.disable();
7315 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.disable();
7317 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.enable();
7318 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.enable();
7319 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.unhide();
7320 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.unhide();
7323 if(MULTI_IS_TRACKER_GAME){
7324 // maybe check the squadwar button
7325 if(Netgame.type_flags & NG_TYPE_SW){
7326 Multi_jw_sw_checkbox.set_state(1);
7327 gr_set_color_fast(&Color_bright);
7329 Multi_jw_sw_checkbox.set_state(0);
7330 gr_set_color_fast(&Color_normal);
7333 gr_string(Multi_jw_sw_checkbox_text[gr_screen.res][0], Multi_jw_sw_checkbox_text[gr_screen.res][1], "SquadWar");
7336 // draw the UI window
7337 Multi_jw_window.draw();
7339 // process and display the player list
7340 // NOTE : this must be done before the buttons are checked to insure that a player hasn't dropped
7341 multi_jw_plist_process();
7342 if(Netgame.type_flags & NG_TYPE_TEAM){
7343 multi_jw_plist_blit_team();
7345 multi_jw_plist_blit_normal();
7348 // display any text in the info area
7349 multi_common_render_text();
7351 // display any pending notification messages
7352 multi_common_notify_do();
7354 // blit the mission filename if possible
7355 if(Netgame.campaign_mode){
7356 if(strlen(Netgame.campaign_name) > 0){
7357 strcpy(mission_text,Netgame.campaign_name);
7359 if(strlen(Netgame.title) > 0){
7360 strcat(mission_text,", ");
7361 strcat(mission_text,Netgame.title);
7364 gr_set_color_fast(&Color_bright_white);
7365 gr_string(Mjw_mission_name_coords[gr_screen.res][MJW_X_COORD],Mjw_mission_name_coords[gr_screen.res][MJW_Y_COORD],mission_text);
7368 if(strlen(Netgame.mission_name) > 0){
7369 strcpy(mission_text,Netgame.mission_name);
7371 if(strlen(Netgame.title) > 0){
7372 strcat(mission_text,", ");
7373 strcat(mission_text,Netgame.title);
7376 gr_set_color_fast(&Color_bright_white);
7377 gr_string(Mjw_mission_name_coords[gr_screen.res][MJW_X_COORD],Mjw_mission_name_coords[gr_screen.res][MJW_Y_COORD],mission_text);
7381 // process and show the chatbox thingie
7385 Multi_jw_window.draw_tooltip();
7387 // display the voice status indicator
7388 multi_common_voice_display_status();
7393 // if we're supposed to be displaying a pilot info popup
7394 if(Multi_jw_should_show_popup){
7395 player_index = find_player_id(Multi_jw_plist_select_id);
7396 if(player_index != -1){
7397 multi_pinfo_popup(&Net_players[player_index]);
7402 void multi_game_client_setup_close()
7404 // unload any bitmaps
7405 if(!bm_unload(Multi_jw_bitmap)){
7406 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_jw_bitmap_fname[gr_screen.res]));
7409 // destroy the chatbox
7412 // destroy the UI_WINDOW
7413 Multi_jw_window.destroy();
7416 if(Netgame.game_state == NETGAME_STATE_MISSION_SYNC){
7417 gamesnd_play_iface(SND_COMMIT_PRESSED);
7422 void multi_jw_check_buttons()
7425 for(idx=0;idx<MULTI_JW_NUM_BUTTONS;idx++){
7426 // we only really need to check for one button pressed at a time, so we can break after
7428 if(Multi_jw_buttons[gr_screen.res][idx].button.pressed()){
7429 multi_jw_button_pressed(idx);
7435 void multi_jw_button_pressed(int n)
7439 gamesnd_play_iface(SND_USER_SELECT);
7440 multi_quit_game(PROMPT_CLIENT);
7442 case MJW_SCROLL_PLAYERS_UP:
7443 multi_jw_scroll_players_up();
7445 case MJW_SCROLL_PLAYERS_DOWN:
7446 multi_jw_scroll_players_down();
7448 case MJW_SCROLL_INFO_UP:
7449 multi_common_scroll_text_up();
7451 case MJW_SCROLL_INFO_DOWN:
7452 multi_common_scroll_text_down();
7455 // request to set myself to team 0
7457 gamesnd_play_iface(SND_USER_SELECT);
7458 multi_team_set_team(Net_player,0);
7461 // request to set myself to team 1
7463 gamesnd_play_iface(SND_USER_SELECT);
7464 multi_team_set_team(Net_player,1);
7468 case MJW_PILOT_INFO:
7469 Multi_jw_should_show_popup = 1;
7473 multi_common_add_notify(XSTR("Not implemented yet!",760));
7478 // do stuff like pinging servers, sending out requests, etc
7479 void multi_jw_do_netstuff()
7483 void multi_jw_scroll_players_up()
7485 gamesnd_play_iface(SND_GENERAL_FAIL);
7488 // scroll down through the player list
7489 void multi_jw_scroll_players_down()
7491 gamesnd_play_iface(SND_GENERAL_FAIL);
7494 void multi_jw_plist_process()
7496 int test_count,player_index,idx;
7498 // first determine if there are 0 players in the game. This should never happen since the host is _always_ in the game
7500 for(idx=0;idx<MAX_PLAYERS;idx++){
7501 // count anyone except the standalone server (if applicable)
7502 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7506 if(test_count <= 0){
7510 // if we had a selected item but that player has left, select myself instead
7511 if(Multi_jw_plist_select_flag){
7512 player_index = find_player_id(Multi_jw_plist_select_id);
7513 if(player_index == -1){
7514 Multi_jw_plist_select_id = Net_player->player_id;
7517 Multi_jw_plist_select_flag = 1;
7518 Multi_jw_plist_select_id = Net_player->player_id;
7521 // if the player has clicked somewhere in the player list area
7522 if(Multi_jw_plist_select_button.pressed()){
7526 player_id = multi_jw_get_mouse_id();
7527 player_index = find_player_id(player_id);
7528 if(player_index != -1){
7529 Multi_jw_plist_select_id = player_id;
7530 Multi_jw_plist_select_flag = 1;
7535 void multi_jw_plist_blit_normal()
7538 char str[CALLSIGN_LEN+1];
7539 int y_start = Mjw_players_coords[gr_screen.res][MJW_Y_COORD];
7542 // display all the players
7543 for(idx=0;idx<MAX_PLAYERS;idx++){
7544 // reset total offset
7547 // count anyone except the standalone server (if applicable)
7548 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7549 // highlight him if he's the host
7550 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7551 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7552 gr_set_color_fast(&Color_text_active_hi);
7554 gr_set_color_fast(&Color_bright);
7557 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7558 gr_set_color_fast(&Color_text_active);
7560 gr_set_color_fast(&Color_text_normal);
7564 // optionally draw his CD status
7565 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7566 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7567 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7569 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7572 // make sure the string will fit, then display it
7573 strcpy(str,Net_players[idx].player->callsign);
7574 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
7577 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7578 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7585 void multi_jw_plist_blit_team()
7588 char str[CALLSIGN_LEN+1];
7589 int y_start = Mjw_players_coords[gr_screen.res][MJW_Y_COORD];
7592 // always blit the proper team button based on _my_ team status
7593 if(Net_player->p_info.team == 0){
7594 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.draw_forced(2);
7596 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.draw_forced(2);
7599 // display all the red players first
7600 for(idx=0;idx<MAX_PLAYERS;idx++){
7601 // reset total offset
7604 // count anyone except the standalone server (if applicable)
7605 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
7606 // highlight him if he's the host
7607 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7608 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7609 gr_set_color_fast(&Color_text_active_hi);
7611 gr_set_color_fast(&Color_bright);
7614 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7615 gr_set_color_fast(&Color_text_active);
7617 gr_set_color_fast(&Color_text_normal);
7621 // optionally draw his CD status
7622 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7623 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7624 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7626 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7629 // blit the red team indicator
7630 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
7631 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
7632 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7633 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7635 total_offset += Multi_common_icon_dims[MICON_TEAM0_SELECT][0] + 1;
7638 if(Multi_common_icons[MICON_TEAM0] != -1){
7639 gr_set_bitmap(Multi_common_icons[MICON_TEAM0], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7640 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7642 total_offset += Multi_common_icon_dims[MICON_TEAM0][0] + 1;
7646 // make sure the string will fit
7647 strcpy(str,Net_players[idx].player->callsign);
7648 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7650 // display him in the correct half of the list depending on his team
7651 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7656 // display all the green players next
7657 for(idx=0;idx<MAX_PLAYERS;idx++){
7658 // reset total offset
7661 // count anyone except the standalone server (if applicable)
7662 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
7663 // highlight him if he's the host
7664 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7665 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7666 gr_set_color_fast(&Color_text_active_hi);
7668 gr_set_color_fast(&Color_bright);
7671 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7672 gr_set_color_fast(&Color_text_active);
7674 gr_set_color_fast(&Color_text_normal);
7678 // optionally draw his CD status
7679 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7680 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7681 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7683 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7686 // blit the red team indicator
7687 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
7688 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
7689 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7690 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7692 total_offset += Multi_common_icon_dims[MICON_TEAM1_SELECT][0] + 1;
7695 if(Multi_common_icons[MICON_TEAM1] != -1){
7696 gr_set_bitmap(Multi_common_icons[MICON_TEAM1], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7697 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7699 total_offset += Multi_common_icon_dims[MICON_TEAM1][0] + 1;
7703 // make sure the string will fit
7704 strcpy(str,Net_players[idx].player->callsign);
7705 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
7708 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7710 // display him in the correct half of the list depending on his team
7711 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7717 void multi_jw_handle_join(net_player *pl)
7719 // for now just play a bloop sound
7720 gamesnd_play_iface(SND_ICON_DROP_ON_WING);
7723 short multi_jw_get_mouse_id()
7725 // determine where he clicked (y pixel value)
7727 Multi_jw_plist_select_button.get_mouse_pos(NULL,&y);
7729 // select things a little differently if we're in team vs. team or non-team vs. team mode
7731 if(Netgame.type_flags & NG_TYPE_TEAM){
7732 int player_index = -1;
7734 // look through all of team red first
7735 for(idx=0;idx<MAX_PLAYERS;idx++){
7736 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
7739 // if this is the _nth_ guy
7747 // if we still haven't found him yet, look through the green team
7748 if(player_index == -1){
7749 for(idx=0;idx<MAX_PLAYERS;idx++){
7750 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
7752 // if this is the _nth_ guy
7760 if(player_index != -1){
7761 return Net_players[idx].player_id;
7764 // select the nth active player if possible, disregarding the standalone server
7765 for(idx=0;idx<MAX_PLAYERS;idx++){
7766 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7769 // if this is the _nth_ guy
7771 return Net_players[idx].player_id;
7782 // -------------------------------------------------------------------------------------------------------------
7784 // MULTIPLAYER WAIT/SYNCH SCREEN
7787 #define MULTI_SYNC_HOST_COUNT 4 // host uses 4 buttons (and sometimes 5)
7788 #define MULTI_SYNC_CLIENT_COUNT 3 // client only uses 3 buttons
7790 char *Multi_sync_bitmap_fname[GR_NUM_RESOLUTIONS] = {
7791 "MultiSynch", // GR_640
7792 "2_MultiSynch" // GR_1024
7795 char *Multi_sync_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
7796 "MultiSynch-M", // GR_640
7797 "2_MultiSynch-M" // GR_1024
7802 // constants for coordinate lookup
7803 #define MS_X_COORD 0
7804 #define MS_Y_COORD 1
7805 #define MS_W_COORD 2
7806 #define MS_H_COORD 3
7808 UI_WINDOW Multi_sync_window; // the window object for the join screen
7809 int Multi_sync_button_count; // the # of buttons to use for this instance of mission sync
7810 int Multi_sync_bitmap; // the background bitmap
7813 #define MULTI_SYNC_NUM_BUTTONS 5
7814 #define MS_SCROLL_INFO_UP 0
7815 #define MS_SCROLL_INFO_DOWN 1
7819 ui_button_info Multi_sync_buttons[GR_NUM_RESOLUTIONS][MULTI_SYNC_NUM_BUTTONS] = {
7822 ui_button_info("MS_00", 0, 396, -1, -1, 0),
7823 ui_button_info("MS_01", 0, 435, -1, -1, 1),
7824 ui_button_info("MS_02", 496, 411, -1, -1, 2),
7825 ui_button_info("MS_04", 436, 403, -1, -1, 4),
7826 ui_button_info("MS_03", 556, 398, -1, -1, 3),
7828 ui_button_info("MS_00", 1, 404, -1, -1, 0),
7829 ui_button_info("MS_01", 1, 446, -1, -1, 1),
7830 ui_button_info("MS_03", 518, 426, 519, 416, 3),
7831 ui_button_info("MS_02", 469, 426, 479, 416, 2),
7832 ui_button_info("MS_04", 571, 420, 577, 416, 4),
7836 ui_button_info("2_MS_00", 2, 647, -1, -1, 0),
7837 ui_button_info("2_MS_01", 2, 713, -1, -1, 1),
7838 ui_button_info("2_MS_03", 829, 682, 831, 667, 3),
7839 ui_button_info("2_MS_02", 751, 682, 766, 667, 2),
7840 ui_button_info("2_MS_04", 914, 672, 924, 667, 4),
7846 #define MULTI_SYNC_NUM_TEXT 0
7848 #define MULTI_SYNC_NUM_TEXT 5
7851 #define MST_LAUNCH 2
7852 UI_XSTR Multi_sync_text[GR_NUM_RESOLUTIONS][MULTI_SYNC_NUM_TEXT] = {
7854 // not needed for FS1
7856 { "Kick", 1266, 479, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_KICK].button },
7857 { "Cancel", 387, 519, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_CANCEL].button },
7858 { "Launch", 801, 577, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_LAUNCH].button },
7859 { "Players", 1269, 23, 133, UI_XSTR_COLOR_GREEN, -1, NULL },
7860 { "Status", 1304, 228, 133, UI_XSTR_COLOR_GREEN, -1, NULL }
7864 // not needed for FS1
7866 { "Kick", 1266, 766, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_KICK].button },
7867 { "Cancel", 387, 831, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_CANCEL].button },
7868 { "Launch", 801, 924, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_LAUNCH].button },
7869 { "Players", 1269, 38, 214, UI_XSTR_COLOR_GREEN, -1, NULL },
7870 { "Status", 1304, 366, 214, UI_XSTR_COLOR_GREEN, -1, NULL }
7876 int Ms_status_coords[GR_NUM_RESOLUTIONS][4] = {
7885 // player status coords
7886 int Ms_status2_coords[GR_NUM_RESOLUTIONS][4] = {
7895 int Ms_cd_icon_offset[GR_NUM_RESOLUTIONS] = {
7900 int Ms_team_icon_offset[GR_NUM_RESOLUTIONS] = {
7905 // player currently selected, index into Net_players[]
7906 int Multi_sync_player_select = -1;
7908 // player list control thingie defs
7909 #define MULTI_SYNC_PLIST_MAX_DISPLAY 15
7910 int Multi_sync_plist_start; // where to start displaying from
7911 int Multi_sync_plist_count; // how many we have
7913 // list select button
7914 UI_BUTTON Multi_sync_plist_button;
7916 int Multi_sync_mode = -1;
7918 #define MULTI_SYNC_COUNTDOWN_TIME 5 // in seconds
7919 float Multi_sync_countdown_timer;
7920 int Multi_sync_countdown = -1;
7922 int Multi_launch_button_created;
7925 // countdown animation timer
7926 char* Multi_sync_countdown_fname[GR_NUM_RESOLUTIONS] = {
7928 "2_Count" // GR_1024
7931 int Multi_sync_countdown_coords[GR_NUM_RESOLUTIONS][2] = {
7942 anim *Multi_sync_countdown_anim = NULL;
7943 anim_instance *Multi_sync_countdown_instance = NULL;
7946 // PREBRIEFING STUFF
7947 // syncing flags used by the server
7948 int Mission_sync_flags = 0;
7949 #define MS_FLAG_SENT_FILESIG (1<<0) // sent filesig requests
7950 #define MS_FLAG_SENT_LOAD (1<<1) // sent load packets
7951 #define MS_FLAG_PUSHED_BRIEFING (1<<2) // pushed everyone else into the briefing
7952 #define MS_FLAG_POST_DATA (1<<3) // sent the post data block
7953 #define MS_FLAG_WSS_SLOTS (1<<4) // all players have received wss slot data
7954 #define MS_FLAG_PSETTINGS (1<<5) // send the player settings packet
7955 #define MS_FLAG_MT_STATS_START (1<<6) // server has started getting player stats from the tracker
7956 #define MS_FLAG_MT_STATS_DONE (1<<7) // server has finished getting player stats from the tracker (success or fail)
7957 #define MS_FLAG_TS_SLOTS (1<<8) // team/ship slots have been sent
7958 #define MS_FLAG_DATA_DONE (1<<9) // done transferring all necessary data
7959 #define MS_FLAG_CAMP_DONE (1<<10) // send campaign pool/goal/event stuff
7961 // POSTBRIEFING STUFF
7962 int Multi_state_timestamp;
7963 int Multi_sync_launch_pressed;
7965 // LOCAL function definitions
7966 void multi_sync_check_buttons();
7967 void multi_sync_button_pressed(int n);
7968 void multi_sync_scroll_info_up();
7969 void multi_sync_scroll_info_down();
7970 void multi_sync_display_name(char *name,int index,int np_index); // display info on the left hand portion of the status window thingie
7971 void multi_sync_display_status(char *status,int index); // display info on the right hand portion of the status window thingie
7972 void multi_sync_force_start_pre();
7973 void multi_sync_force_start_post();
7974 void multi_sync_launch();
7975 void multi_sync_create_launch_button();
7976 void multi_sync_blit_screen_all();
7977 void multi_sync_handle_plist();
7979 void multi_sync_common_init();
7980 void multi_sync_common_do();
7981 void multi_sync_common_close();
7983 void multi_sync_pre_init();
7984 void multi_sync_pre_do();
7985 void multi_sync_pre_close();
7987 void multi_sync_post_init();
7988 void multi_sync_post_do();
7989 void multi_sync_post_close();
7994 // perform the correct init functions
7995 void multi_sync_init()
7997 Multi_sync_countdown = -1;
8001 // reset all timestamp
8002 multi_reset_timestamps();
8004 extern int Player_multi_died_check;
8005 Player_multi_died_check = -1;
8007 if(!(Game_mode & GM_STANDALONE_SERVER)){
8008 multi_sync_common_init();
8011 switch(Multi_sync_mode){
8012 case MULTI_SYNC_PRE_BRIEFING:
8013 multi_sync_pre_init();
8015 case MULTI_SYNC_POST_BRIEFING:
8016 multi_sync_post_init();
8018 case MULTI_SYNC_INGAME:
8019 multi_ingame_sync_init();
8024 // perform the correct do frame functions
8025 void multi_sync_do()
8027 if(!(Game_mode & GM_STANDALONE_SERVER)){
8028 multi_sync_common_do();
8031 // if the netgame is ending, don't do any sync processing
8032 if(multi_endgame_ending()){
8036 // process appropriateliy
8037 switch(Multi_sync_mode){
8038 case MULTI_SYNC_PRE_BRIEFING:
8039 multi_sync_pre_do();
8041 case MULTI_SYNC_POST_BRIEFING:
8042 multi_sync_post_do();
8044 case MULTI_SYNC_INGAME:
8045 multi_ingame_sync_do();
8048 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8049 if(Multi_sync_bitmap != -1){
8050 gr_set_bitmap(Multi_sync_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8053 Multi_sync_window.draw();
8055 multi_sync_blit_screen_all();
8062 // perform the correct close functions
8063 void multi_sync_close()
8065 switch(Multi_sync_mode){
8066 case MULTI_SYNC_PRE_BRIEFING:
8067 multi_sync_pre_close();
8069 case MULTI_SYNC_POST_BRIEFING:
8070 multi_sync_post_close();
8072 case MULTI_SYNC_INGAME:
8073 multi_ingame_sync_close();
8077 if(!(Game_mode & GM_STANDALONE_SERVER)){
8078 multi_sync_common_close();
8082 char *multi_sync_tooltip_handler(char *str)
8084 if (!stricmp(str, NOX("@launch"))) {
8085 if (Multi_launch_button_created){
8086 return XSTR("Launch",801);
8093 void multi_sync_common_init()
8097 // create the interface window
8098 Multi_sync_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
8099 Multi_sync_window.set_mask_bmap(Multi_sync_bitmap_mask_fname[gr_screen.res]);
8100 Multi_sync_window.tooltip_handler = multi_sync_tooltip_handler;
8102 // load the background bitmap
8103 Multi_sync_bitmap = bm_load(Multi_sync_bitmap_fname[gr_screen.res]);
8104 if (Multi_sync_bitmap < 0) {
8105 // we failed to load the bitmap - this is very bad
8109 // initialize the player list data
8110 Multi_sync_plist_start = 0;
8111 Multi_sync_plist_count = 1; // we can pretty safely assume that there's one player in the game - me.
8113 Multi_launch_button_created = 0;
8115 // create the chatbox thingie (shouldn't be necesary to do this, but we'll put it in for good measure)
8118 // force the chatbox to be small
8119 chatbox_force_small();
8121 // initialize the common notification messaging
8122 multi_common_notify_init();
8124 // initialize the common mission info display area.
8125 multi_common_set_text("");
8127 // use the common interface palette
8128 multi_common_set_palette();
8130 // don't select any player yet.
8131 Multi_sync_player_select = -1;
8133 // determine how many of the 5 buttons to create
8134 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8135 Multi_sync_button_count = MULTI_SYNC_HOST_COUNT;
8137 Multi_sync_button_count = MULTI_SYNC_CLIENT_COUNT;
8139 // create the interface buttons
8140 for(idx=0; idx<Multi_sync_button_count; idx++){
8141 // create the object
8142 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);
8144 // set the sound to play when highlighted
8145 Multi_sync_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
8147 // set the ani for the button
8148 // this wierdness is necessary because cancel and kick buttons aren't drawn on the background bitmap,
8149 // so we have to load in frame 0, too (the file should exist)
8150 if ((idx == MS_CANCEL) || (idx == MS_KICK) || (idx == MS_LAUNCH)) {
8151 Multi_sync_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sync_buttons[gr_screen.res][idx].filename, 3, 0);
8153 Multi_sync_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sync_buttons[gr_screen.res][idx].filename);
8157 Multi_sync_buttons[gr_screen.res][idx].button.link_hotspot(Multi_sync_buttons[gr_screen.res][idx].hotspot);
8161 for(idx=0; idx<MULTI_SYNC_NUM_TEXT; idx++) {
8162 // don't create the "launch" button text just yet
8163 if(idx == MST_LAUNCH) {
8166 // multiplayer clients should ignore the kick button
8167 if(!MULTIPLAYER_MASTER && !MULTIPLAYER_HOST && (idx == MST_KICK)) {
8171 Multi_sync_window.add_XSTR(&Multi_sync_text[gr_screen.res][idx]);
8174 // create the player list select button and hide it
8175 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);
8176 Multi_sync_plist_button.hide();
8178 // set up hotkeys for certain common functions
8179 Multi_sync_buttons[gr_screen.res][MS_CANCEL].button.set_hotkey(KEY_ESC);
8182 void multi_sync_common_do()
8184 int k = chatbox_process();
8185 k = Multi_sync_window.process(k);
8187 // process the player list
8188 multi_sync_handle_plist();
8190 // process any button clicks
8191 multi_sync_check_buttons();
8193 // process any keypresses
8197 gamesnd_play_iface(SND_USER_SELECT);
8198 multi_quit_game(PROMPT_ALL);
8203 void multi_sync_common_close()
8205 // unload any bitmaps
8206 if(!bm_unload(Multi_sync_bitmap)){
8207 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_sync_bitmap_fname[gr_screen.res]));
8210 extern int Player_multi_died_check;
8211 Player_multi_died_check = -1;
8213 // destroy the UI_WINDOW
8214 Multi_sync_window.destroy();
8217 void multi_sync_blit_screen_all()
8224 // display any text in the info area
8225 multi_common_render_text();
8227 // display any pending notification messages
8228 multi_common_notify_do();
8230 // display any info about visible players
8232 for(idx=0;idx<MAX_PLAYERS;idx++){
8233 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
8234 // display his name and status
8235 multi_sync_display_name(Net_players[idx].player->callsign,count,idx);
8237 // get the player state
8238 state = Net_players[idx].state;
8240 // if we're ingame joining, show all other players except myself as "playing"
8241 if((Net_player != NULL) && (&Net_players[idx] != Net_player) && ((Multi_sync_mode == MULTI_SYNC_INGAME) || (Net_player->flags & NETINFO_FLAG_INGAME_JOIN)) ){
8242 state = NETPLAYER_STATE_IN_MISSION;
8246 case NETPLAYER_STATE_MISSION_LOADING:
8247 multi_sync_display_status(XSTR("Mission Loading",802),count);
8249 case NETPLAYER_STATE_INGAME_SHIP_SELECT: // I don't think its possible to see this state, but...
8250 multi_sync_display_status(XSTR("Ingame Ship Select",803),count);
8252 case NETPLAYER_STATE_DEBRIEF:
8253 multi_sync_display_status(XSTR("Debriefing",804),count);
8255 case NETPLAYER_STATE_MISSION_SYNC:
8256 multi_sync_display_status(XSTR("Mission Sync",805),count);
8258 case NETPLAYER_STATE_JOINING:
8259 multi_sync_display_status(XSTR("Joining",806),count);
8261 case NETPLAYER_STATE_JOINED:
8262 multi_sync_display_status(XSTR("Joined",807),count);
8264 case NETPLAYER_STATE_SLOT_ACK :
8265 multi_sync_display_status(XSTR("Slot Ack",808),count);
8267 case NETPLAYER_STATE_BRIEFING:
8268 multi_sync_display_status(XSTR("Briefing",765),count);
8270 case NETPLAYER_STATE_SHIP_SELECT:
8271 multi_sync_display_status(XSTR("Ship Select",809),count);
8273 case NETPLAYER_STATE_WEAPON_SELECT:
8274 multi_sync_display_status(XSTR("Weapon Select",810),count);
8276 case NETPLAYER_STATE_WAITING:
8277 multi_sync_display_status(XSTR("Waiting",811),count);
8279 case NETPLAYER_STATE_IN_MISSION:
8280 multi_sync_display_status(XSTR("In Mission",812),count);
8282 case NETPLAYER_STATE_MISSION_LOADED:
8283 multi_sync_display_status(XSTR("Mission Loaded",813),count);
8285 case NETPLAYER_STATE_DATA_LOAD:
8286 multi_sync_display_status(XSTR("Data loading",814),count);
8288 case NETPLAYER_STATE_SETTINGS_ACK:
8289 multi_sync_display_status(XSTR("Ready To Enter Mission",815),count);
8291 case NETPLAYER_STATE_INGAME_SHIPS:
8292 multi_sync_display_status(XSTR("Ingame Ships Packet Ack",816),count);
8294 case NETPLAYER_STATE_INGAME_WINGS:
8295 multi_sync_display_status(XSTR("Ingame Wings Packet Ack",817),count);
8297 case NETPLAYER_STATE_INGAME_RPTS:
8298 multi_sync_display_status(XSTR("Ingame Respawn Points Ack",818),count);
8300 case NETPLAYER_STATE_SLOTS_ACK:
8301 multi_sync_display_status(XSTR("Ingame Weapon Slots Ack",819),count);
8303 case NETPLAYER_STATE_POST_DATA_ACK:
8304 multi_sync_display_status(XSTR("Post Briefing Data Block Ack",820),count);
8306 case NETPLAYER_STATE_FLAG_ACK :
8307 multi_sync_display_status(XSTR("Flags Ack",821),count);
8309 case NETPLAYER_STATE_MT_STATS :
8310 multi_sync_display_status(XSTR("Parallax Online Stats Updating",822),count);
8312 case NETPLAYER_STATE_WSS_ACK :
8313 multi_sync_display_status(XSTR("Weapon Slots Ack",823),count);
8315 case NETPLAYER_STATE_HOST_SETUP :
8316 multi_sync_display_status(XSTR("Host setup",824),count);
8318 case NETPLAYER_STATE_DEBRIEF_ACCEPT:
8319 multi_sync_display_status(XSTR("Debrief accept",825),count);
8321 case NETPLAYER_STATE_DEBRIEF_REPLAY:
8322 multi_sync_display_status(XSTR("Debrief replay",826),count);
8324 case NETPLAYER_STATE_CPOOL_ACK:
8325 multi_sync_display_status(XSTR("Campaign ship/weapon ack",827),count);
8327 case NETPLAYER_STATE_MISSION_XFER :
8329 // server should display the pct completion of all clients
8330 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8331 if(Net_players[idx].s_info.xfer_handle != -1){
8332 pct_complete = multi_xfer_pct_complete(Net_players[idx].s_info.xfer_handle);
8334 // if we've got a valid xfer handle
8335 if((pct_complete >= 0.0) && (pct_complete <= 1.0)){
8336 sprintf(txt,XSTR("Mission file xfer %d%%",828),(int)(pct_complete * 100.0f));
8340 strcpy(txt,XSTR("Mission file xfer",829));
8343 strcpy(txt,XSTR("Mission file xfer",829));
8346 // clients should display only for themselves (which is the only thing they know)
8348 // if we've got a valid file xfer handle
8349 if((&Net_players[idx] == Net_player) && (Net_player->s_info.xfer_handle != -1)){
8350 pct_complete = multi_xfer_pct_complete(Net_player->s_info.xfer_handle);
8352 // if we've got a valid xfer handle
8353 if((pct_complete >= 0.0) && (pct_complete <= 1.0)){
8354 sprintf(txt,XSTR("Mission file xfer %d%%",828),(int)(pct_complete * 100.0f));
8358 strcpy(txt,XSTR("Mission file xfer",829));
8363 strcpy(txt,XSTR("Mission file xfer",829));
8368 multi_sync_display_status(txt,count);
8371 nprintf(("Network","Unhandled player state : %d !\n",Net_players[idx].state));
8378 // display the mission start countdown timer (if any)
8379 anim_render_all(GS_STATE_MULTI_MISSION_SYNC,flFrametime);
8381 // process and show the chatbox thingie
8385 Multi_sync_window.draw_tooltip();
8387 // display the voice status indicator
8388 multi_common_voice_display_status();
8391 void multi_sync_check_buttons()
8394 for(idx=0;idx<Multi_sync_button_count;idx++){
8395 // we only really need to check for one button pressed at a time, so we can break after
8397 if(Multi_sync_buttons[gr_screen.res][idx].button.pressed()){
8398 multi_sync_button_pressed(idx);
8404 void multi_sync_button_pressed(int n)
8409 gamesnd_play_iface(SND_USER_SELECT);
8410 multi_quit_game(PROMPT_ALL);
8413 // scroll the info box up
8414 case MS_SCROLL_INFO_UP:
8415 multi_common_scroll_text_up();
8418 // scroll the info box down
8419 case MS_SCROLL_INFO_DOWN:
8420 multi_common_scroll_text_down();
8425 // if we have a currently selected player, kick him
8426 if(Multi_sync_player_select >= 0){
8427 multi_kick_player(Multi_sync_player_select);
8431 // start the final launch countdown (post-sync only)
8433 multi_sync_start_countdown();
8436 // doesn't do anything
8442 void multi_sync_pre_init()
8446 Netgame.game_state = NETGAME_STATE_MISSION_SYNC;
8448 // if we're in teamplay mode, always force skill level to be medium
8449 if((Netgame.type_flags & NG_TYPE_TEAM) && (Net_player->flags & NETINFO_FLAG_GAME_HOST)){
8450 Netgame.options.skill_level = NUM_SKILL_LEVELS / 2;
8451 Game_skill_level = NUM_SKILL_LEVELS / 2;
8452 multi_options_update_netgame();
8455 // notify everyone of when we get here
8456 if(!(Game_mode & GM_STANDALONE_SERVER)){
8457 Net_player->state = NETPLAYER_STATE_MISSION_SYNC;
8458 send_netplayer_update_packet();
8461 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8463 ml_string(NOX("Server performing pre-briefing data sync"));
8465 if(!(Game_mode & GM_STANDALONE_SERVER)){
8466 multi_common_set_text(XSTR("Server performing sync\n",830),1);
8469 // maybe initialize tvt and squad war stuff
8470 if(Netgame.type_flags & NG_TYPE_TEAM){
8471 multi_team_level_init();
8474 // force everyone into this state
8475 send_netgame_update_packet();
8477 if(!(Game_mode & GM_STANDALONE_SERVER)){
8478 multi_common_add_text(XSTR("Send update packet\n",831),1);
8481 // setup some of my own data
8482 Net_player->flags |= NETINFO_FLAG_MISSION_OK;
8484 // do any output stuff
8485 if(Game_mode & GM_STANDALONE_SERVER){
8486 std_debug_set_standalone_state_string("Mission Sync");
8489 // do this here to insure we have the most up to date file checksum info
8490 multi_get_mission_checksum(Game_current_mission_filename);
8491 // parse_get_file_signature(Game_current_mission_filename);
8493 if(!(Game_mode & GM_STANDALONE_SERVER)){
8494 multi_common_add_text(XSTR("Got file signatures\n",832),1);
8497 if(!(Game_mode & GM_STANDALONE_SERVER)){
8498 multi_common_add_text(XSTR("Sending update state packet\n",833),1);
8502 // if we're not in team vs. team mode - set all player teams to be 0, and unset all captaincy bits
8503 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
8504 for(idx=0;idx<MAX_PLAYERS;idx++){
8505 Net_players[idx].p_info.team = 0;
8506 Net_players[idx].flags &= ~(NETINFO_FLAG_TEAM_CAPTAIN);
8510 // we aren't necessarily xferring the mission file yet
8511 Assert(Net_player->s_info.xfer_handle == -1);
8513 // always call this for good measure
8514 multi_campaign_flush_data();
8516 Mission_sync_flags = 0;
8517 Multi_mission_loaded = 0;
8520 void multi_sync_pre_do()
8524 // If I'm the server, wait for everyone to arrive in this state, then begin transferring data, etc.
8525 // all servers (standalone or no, go through this)
8526 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8527 // wait for everyone to arrive, then request filesig from all of them
8528 if(multi_netplayer_state_check(NETPLAYER_STATE_MISSION_SYNC) && !(Mission_sync_flags & MS_FLAG_SENT_FILESIG) && !multi_endgame_ending()){
8529 send_file_sig_request(Netgame.mission_name);
8530 Mission_sync_flags |= MS_FLAG_SENT_FILESIG;
8532 if(!(Game_mode & GM_STANDALONE_SERVER)){
8533 multi_common_add_text(XSTR("Sent filesig request\n",834),1);
8537 // if we're waiting for players to receive files, then check on their status
8538 if((Mission_sync_flags & MS_FLAG_SENT_FILESIG) && !multi_netplayer_flag_check(NETINFO_FLAG_MISSION_OK) && !multi_endgame_ending()){
8539 for(idx=0;idx<MAX_PLAYERS;idx++){
8540 // if this player is in the process of xferring a file
8541 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].s_info.xfer_handle != -1)){
8542 switch(multi_xfer_get_status(Net_players[idx].s_info.xfer_handle)){
8543 // if it has successfully completed, set his ok flag
8544 case MULTI_XFER_SUCCESS :
8546 Net_players[idx].flags |= NETINFO_FLAG_MISSION_OK;
8548 // release the xfer instance handle
8549 multi_xfer_release_handle(Net_players[idx].s_info.xfer_handle);
8550 Net_players[idx].s_info.xfer_handle = -1;
8552 // if it has failed or timed-out, kick the player
8553 case MULTI_XFER_TIMEDOUT:
8554 case MULTI_XFER_FAIL:
8555 // release the xfer handle
8556 multi_xfer_release_handle(Net_players[idx].s_info.xfer_handle);
8557 Net_players[idx].s_info.xfer_handle = -1;
8560 multi_kick_player(idx, 0, KICK_REASON_BAD_XFER);
8567 // NOTE : this is now obsolete
8568 // once everyone is verified, do any data transfer necessary
8569 if(multi_netplayer_flag_check(NETINFO_FLAG_MISSION_OK) && !(Mission_sync_flags & MS_FLAG_DATA_DONE) && !multi_endgame_ending()){
8570 // do nothing for now
8571 Mission_sync_flags |= MS_FLAG_DATA_DONE;
8573 // send campaign pool data
8574 multi_campaign_send_pool_status();
8577 // wait for everyone to ack on campaign pool data (even in non-campaign situations)
8578 if((Mission_sync_flags & MS_FLAG_DATA_DONE) && !(Mission_sync_flags & MS_FLAG_CAMP_DONE) && !multi_endgame_ending()){
8579 // check to see if everyone has acked the campaign pool data
8580 if(multi_netplayer_state_check(NETPLAYER_STATE_CPOOL_ACK)){
8581 Mission_sync_flags |= MS_FLAG_CAMP_DONE;
8585 // once everyone is verified, tell them to load the mission
8586 // also make sure to load the mission myself _AFTER_ telling everyone to do so. This makes the whole process
8587 // move along faster
8588 if((Mission_sync_flags & MS_FLAG_CAMP_DONE) && !(Mission_sync_flags & MS_FLAG_SENT_LOAD) && !multi_endgame_ending()){
8589 send_netplayer_load_packet(NULL);
8590 Mission_sync_flags |= MS_FLAG_SENT_LOAD;
8592 if(!(Game_mode & GM_STANDALONE_SERVER)){
8593 multi_common_add_text(XSTR("Sent load packet\n",835),1);
8596 // load the mission myself, as soon as possible
8597 if(!Multi_mission_loaded){
8598 nprintf(("Network","Server loading mission..."));
8600 // update everyone about my status
8601 Net_player->state = NETPLAYER_STATE_MISSION_LOADING;
8602 send_netplayer_update_packet();
8604 game_start_mission();
8606 nprintf(("Network","Done\n"));
8607 Multi_mission_loaded = 1;
8609 // update everyone about my status
8610 Net_player->state = NETPLAYER_STATE_MISSION_LOADED;
8611 send_netplayer_update_packet();
8613 if(!(Game_mode & GM_STANDALONE_SERVER)){
8614 multi_common_add_text(XSTR("Loaded mission locally\n",836),1);
8619 // if everyone has loaded the mission, randomly assign players to ships
8620 if(multi_netplayer_state_check(NETPLAYER_STATE_MISSION_LOADED) && !(Mission_sync_flags & MS_FLAG_TS_SLOTS) && !multi_endgame_ending()){
8621 // call the team select function to assign players to their ships, wings, etc
8622 multi_ts_assign_players_all();
8623 send_netplayer_slot_packet();
8626 Mission_sync_flags |= MS_FLAG_TS_SLOTS;
8629 // if everyone has loaded the mission, move to the team select stage
8630 if(Sync_test && multi_netplayer_state_check(NETPLAYER_STATE_SLOT_ACK) && !(Mission_sync_flags & MS_FLAG_PUSHED_BRIEFING) && !multi_endgame_ending()){
8631 Netgame.game_state = NETGAME_STATE_BRIEFING;
8632 send_netgame_update_packet(); // this will push everyone into the next state
8634 // the standalone moves to his own wait state, whereas in the normal game mode, the server/host moves in to the
8635 // team select state
8636 if(Game_mode & GM_STANDALONE_SERVER){
8637 gameseq_post_event(GS_EVENT_MULTI_STD_WAIT);
8639 gameseq_post_event(GS_EVENT_START_GAME);
8642 Mission_sync_flags |= MS_FLAG_PUSHED_BRIEFING;
8644 if(!(Game_mode & GM_STANDALONE_SERVER)){
8645 multi_common_add_text(XSTR("Moving to team select\n",837),1);
8649 // clients should detect here if they are doing a file xfer and do error processing
8650 if((Net_player->state == NETPLAYER_STATE_MISSION_XFER) && (Net_player->s_info.xfer_handle != -1) && !multi_endgame_ending()){
8651 switch(multi_xfer_get_status(Net_player->s_info.xfer_handle)){
8652 // if it has successfully completed, set his ok flag
8653 case MULTI_XFER_SUCCESS :
8654 // release my xfer handle
8655 multi_xfer_release_handle(Net_player->s_info.xfer_handle);
8656 Net_player->s_info.xfer_handle = -1;
8659 // if it has failed or timed-out, kick the player
8660 case MULTI_XFER_TIMEDOUT:
8661 case MULTI_XFER_FAIL:
8662 // release my xfer handle
8663 multi_xfer_release_handle(Net_player->s_info.xfer_handle);
8664 Net_player->s_info.xfer_handle = -1;
8666 // leave the game qith an error code
8667 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_XFER_FAIL);
8674 if(!(Game_mode & GM_STANDALONE_SERVER)){
8676 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8677 if(Multi_sync_bitmap != -1){
8678 gr_set_bitmap(Multi_sync_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8681 Multi_sync_window.draw();
8683 multi_sync_blit_screen_all();
8689 void multi_sync_pre_close()
8691 // at this point, we should shut down any file xfers...
8692 if(Net_player->s_info.xfer_handle != -1){
8693 nprintf(("Network","WARNING - killing file xfer while leaving mission sync state!!!\n"));
8695 multi_xfer_abort(Net_player->s_info.xfer_handle);
8696 Net_player->s_info.xfer_handle = -1;
8700 void multi_sync_post_init()
8702 multi_reset_timestamps();
8704 Multi_state_timestamp = timestamp(0);
8707 ml_string(NOX("Performing post-briefing data sync"));
8709 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8710 multi_common_add_text(XSTR("Server performing sync\n",830),1);
8712 multi_common_add_text(XSTR("Client performing sync\n",838),1);
8715 // everyone should re-initialize these
8716 init_multiplayer_stats();
8718 // reset all sequencing info
8719 multi_oo_reset_sequencing();
8721 // if I am not the master of the game, then send the firing information for my ship
8723 if ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) ){
8724 send_firing_info_packet();
8727 // if I'm not a standalone server, load up the countdown stuff
8728 if(!(Game_mode & GM_STANDALONE_SERVER)){
8729 Multi_sync_countdown_anim = NULL;
8730 Multi_sync_countdown_instance = NULL;
8731 Multi_sync_countdown_anim = anim_load(Multi_sync_countdown_fname[gr_screen.res]);
8732 if(Multi_sync_countdown_anim == NULL){
8733 nprintf(("General","WARNING!, Could not load countdown animation %s!\n",Multi_sync_countdown_fname[gr_screen.res]));
8737 // create objects for all permanent observers
8738 multi_obs_level_init();
8740 // clear the game start countdown timer
8741 Multi_sync_countdown_timer = -1.0f;
8742 Multi_sync_countdown = -1;
8744 // if this is a team vs. team mission, mark all ship teams appropriately
8745 if(Netgame.type_flags & NG_TYPE_TEAM){
8746 multi_team_mark_all_ships();
8749 Mission_sync_flags = 0;
8750 Multi_sync_launch_pressed = 0;
8753 #define MULTI_POST_TIMESTAMP 7000
8755 extern int create_wings();
8757 void multi_sync_post_do()
8761 // only if the host is also the master should he be doing this (non-standalone situation)
8762 if ( Net_player->flags & NETINFO_FLAG_AM_MASTER ) {
8764 // once everyone gets to this screen, send them the ship classes of all ships.
8765 if(multi_netplayer_state_check(NETPLAYER_STATE_WAITING) && !(Mission_sync_flags & MS_FLAG_POST_DATA)) {
8766 // only the host should ever do this
8767 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8768 // at this point we want to delete all necessary ships, change all necessary ship classes, and set all weapons up
8769 multi_ts_create_wings();
8771 // update player ets settings
8772 for(idx=0;idx<MAX_PLAYERS;idx++){
8773 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].player->objnum != -1)){
8774 multi_server_update_player_weapons(&Net_players[idx],&Ships[Objects[Net_players[idx].player->objnum].instance]);
8779 // note that this is done a little differently for standalones and nonstandalones
8780 send_post_sync_data_packet();
8782 multi_common_add_text(XSTR("Sending post briefing block information\n",839),1);
8784 Mission_sync_flags |= MS_FLAG_POST_DATA;
8787 // send weapon slots data
8788 if(multi_netplayer_state_check(NETPLAYER_STATE_POST_DATA_ACK) && !(Mission_sync_flags & MS_FLAG_WSS_SLOTS)) {
8789 // note that this is done a little differently for standalones and nonstandalones
8790 if(Netgame.type_flags & NG_TYPE_TEAM){
8791 send_wss_slots_data_packet(0,0);
8792 send_wss_slots_data_packet(1,1);
8794 send_wss_slots_data_packet(0,1);
8797 multi_common_add_text(XSTR("Sending weapon slots information\n",840),1);
8799 Mission_sync_flags |= MS_FLAG_WSS_SLOTS;
8802 // once weapon information is received, send player settings info
8803 if ( multi_netplayer_state_check(NETPLAYER_STATE_WSS_ACK) && !(Mission_sync_flags & MS_FLAG_PSETTINGS)) {
8804 send_player_settings_packet();
8806 // server (specifically, the standalone), should set this here
8807 Net_player->state = NETPLAYER_STATE_SETTINGS_ACK;
8808 send_netplayer_update_packet();
8810 multi_common_add_text(XSTR("Sending player settings packets\n",841),1);
8812 Mission_sync_flags |= MS_FLAG_PSETTINGS;
8815 // check to see if the countdown timer has started and act appropriately
8816 if( Multi_sync_countdown_timer > -1.0f ) {
8818 // increment by frametime.
8819 Multi_sync_countdown_timer += flFrametime;
8821 // if the animation is not playing, start it
8822 if(!(Game_mode & GM_STANDALONE_SERVER) && (Multi_sync_countdown_instance == NULL) && (Multi_sync_countdown_anim != NULL)){
8823 anim_play_struct aps;
8825 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]);
8826 aps.screen_id = GS_STATE_MULTI_MISSION_SYNC;
8827 aps.framerate_independent = 1;
8829 Multi_sync_countdown_instance = anim_play(&aps);
8832 // if the next second has expired
8833 if( Multi_sync_countdown_timer >= 1.0f ) {
8835 Multi_sync_countdown--;
8836 Multi_sync_countdown_timer = 0.0f;
8838 // if the countdown has reached 0, launch the mission
8839 if(Multi_sync_countdown == 0){
8840 Multi_sync_countdown_timer = -1.0f;
8842 Multi_sync_launch_pressed = 0;
8843 multi_sync_launch();
8845 // otherwise send a countdown packet
8847 send_countdown_packet(Multi_sync_countdown);
8852 // jump into the mission myself
8853 if((Multi_sync_countdown == 0) && multi_netplayer_state_check(NETPLAYER_STATE_IN_MISSION)){
8854 if(!((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !Multi_sync_launch_pressed)){
8855 Net_player->state = NETPLAYER_STATE_IN_MISSION;
8856 Netgame.game_state = NETGAME_STATE_IN_MISSION;
8857 gameseq_post_event(GS_EVENT_ENTER_GAME);
8859 multi_common_add_text(XSTR("Moving into game\n",842),1);
8863 // maybe start the animation countdown
8864 if((Multi_sync_countdown >= 0) && (Multi_sync_countdown_instance == NULL) && (Multi_sync_countdown_anim != NULL)){
8865 anim_play_struct aps;
8867 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]);
8868 aps.screen_id = GS_STATE_MULTI_MISSION_SYNC;
8869 aps.framerate_independent = 1;
8871 Multi_sync_countdown_instance = anim_play(&aps);
8875 // host - specific stuff
8876 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8877 // create the launch button so the host can click
8878 if( Sync_test && multi_netplayer_state_check(NETPLAYER_STATE_SETTINGS_ACK) ){
8879 multi_sync_create_launch_button();
8884 if(!(Game_mode & GM_STANDALONE_SERVER)){
8886 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8887 if(Multi_sync_bitmap != -1){
8888 gr_set_bitmap(Multi_sync_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8891 Multi_sync_window.draw();
8893 multi_sync_blit_screen_all();
8899 void multi_sync_post_close()
8903 // if I'm not a standalone server, unload up the countdown stuff
8904 if(!(Game_mode & GM_STANDALONE_SERVER)){
8905 // release all rendering animation instances (should only be 1)
8906 anim_release_all_instances(GS_STATE_MULTI_MISSION_SYNC);
8907 Multi_sync_countdown_instance = NULL;
8909 // free up the countdown animation
8910 if(Multi_sync_countdown_anim != NULL){
8911 anim_free(Multi_sync_countdown_anim);
8912 Multi_sync_countdown_anim = NULL;
8916 // all players should reset sequencing
8917 for(idx=0;idx<MAX_PLAYERS;idx++){
8918 if(Net_player->flags & NETINFO_FLAG_CONNECTED){
8919 Net_players[idx].client_cinfo_seq = 0;
8920 Net_players[idx].client_server_seq = 0;
8924 // multiplayer dogfight
8925 multi_df_level_pre_enter();
8927 // clients should clear obj_pair array and add pair for themselves
8929 if ( MULTIPLAYER_CLIENT ) {
8931 obj_add_pairs( OBJ_INDEX(Player_obj) );
8936 void multi_sync_display_name(char *name,int index,int np_index)
8938 char fit[CALLSIGN_LEN];
8940 // make sure the string actually fits
8943 // if we're in team vs. team mode
8944 if(Netgame.type_flags & NG_TYPE_TEAM){
8945 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]);
8947 // if this is the currently selected player, draw him highlighted
8948 if(np_index == Multi_sync_player_select){
8949 gr_set_color_fast(&Color_text_selected);
8951 gr_set_color_fast(&Color_text_normal);
8955 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);
8957 // blit his team icon
8959 if(Net_players[np_index].p_info.team == 0){
8960 // blit the team captain icon
8961 if(Net_players[np_index].flags & NETINFO_FLAG_TEAM_CAPTAIN){
8962 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
8963 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8964 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);
8967 // normal team member icon
8969 if(Multi_common_icons[MICON_TEAM0] != -1){
8970 gr_set_bitmap(Multi_common_icons[MICON_TEAM0], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8971 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);
8976 else if(Net_players[np_index].p_info.team == 1){
8977 // blit the team captain icon
8978 if(Net_players[np_index].flags & NETINFO_FLAG_TEAM_CAPTAIN){
8979 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
8980 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8981 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);
8984 // normal team member icon
8986 if(Multi_common_icons[MICON_TEAM1] != -1){
8987 gr_set_bitmap(Multi_common_icons[MICON_TEAM1], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8988 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);
8993 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]);
8995 // if this is the currently selected player, draw him highlighted
8996 if(np_index == Multi_sync_player_select){
8997 gr_set_color_fast(&Color_text_selected);
8999 gr_set_color_fast(&Color_text_normal);
9003 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);
9006 // maybe blit his CD status icon
9007 if((Net_players[np_index].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
9008 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
9009 gr_bitmap(Ms_status_coords[gr_screen.res][MS_X_COORD], Ms_status_coords[gr_screen.res][MS_Y_COORD] + (index * 10));
9013 void multi_sync_display_status(char *status,int index)
9017 // make sure the string actually fits
9018 strcpy(fit, status);
9019 gr_force_fit_string(fit, 250, Ms_status2_coords[gr_screen.res][MS_W_COORD] - 20);
9020 gr_set_color_fast(&Color_bright);
9021 gr_string(Ms_status2_coords[gr_screen.res][MS_X_COORD], Ms_status2_coords[gr_screen.res][MS_Y_COORD] + (index * 10), fit);
9024 void multi_sync_force_start_pre()
9027 int want_state = NETPLAYER_STATE_SLOT_ACK; // kick any players who are still in this state
9029 // go through the player list and boot anyone who isn't in the right state
9030 for(idx=0;idx<MAX_PLAYERS;idx++){
9031 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx]) && (Net_players[idx].state == want_state)){
9032 multi_kick_player(idx,0);
9037 void multi_sync_force_start_post()
9041 int num_kill_states;
9043 // determine the state we want all players in so that we can find those who are not in the state
9044 kill_state[0] = NETPLAYER_STATE_BRIEFING;
9045 kill_state[1] = NETPLAYER_STATE_SHIP_SELECT;
9046 kill_state[2] = NETPLAYER_STATE_WEAPON_SELECT;
9047 num_kill_states = 3;
9049 // go through the player list and boot anyone who isn't in the right state
9050 for(idx=0;idx<MAX_PLAYERS;idx++){
9051 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx])){
9052 // check against all kill state
9053 for(idx2 = 0;idx2<num_kill_states;idx2++){
9054 if(Net_players[idx].state == kill_state[idx2]){
9055 multi_kick_player(idx,0);
9063 void multi_sync_start_countdown()
9065 // don't allow repeat button presses
9066 if(Multi_sync_launch_pressed){
9070 Multi_sync_launch_pressed = 1;
9072 // if I'm the server, begin the countdown
9073 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
9074 gamesnd_play_iface(SND_COMMIT_PRESSED);
9075 Multi_sync_countdown_timer = 0.0f;
9076 Multi_sync_countdown = MULTI_SYNC_COUNTDOWN_TIME;
9078 // send an initial countdown value
9079 send_countdown_packet(Multi_sync_countdown);
9081 // otherwise send the "start countdown" packet to the standalone
9083 Assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
9084 send_countdown_packet(-1);
9088 void multi_sync_launch()
9090 // don't allow repeat button presses
9091 if(Multi_sync_launch_pressed){
9095 Multi_sync_launch_pressed = 1;
9098 ml_printf(NOX("Entering mission %s"), Game_current_mission_filename);
9100 // tell everyone to jump into the mission
9101 send_jump_into_mission_packet();
9102 Multi_state_timestamp = timestamp(MULTI_POST_TIMESTAMP);
9104 // set the # of players at the start of the mission
9105 Multi_num_players_at_start = multi_num_players();
9106 nprintf(("Network","# of players at start of mission : %d\n", Multi_num_players_at_start));
9108 // initialize datarate limiting for all clients
9109 multi_oo_rate_init_all();
9111 multi_common_add_text(XSTR("Sending mission start packet\n",843),1);
9114 void multi_sync_create_launch_button()
9116 if (!Multi_launch_button_created) {
9117 // create the object
9118 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);
9120 // set the sound to play when highlighted
9121 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_highlight_action(common_play_highlight_sound);
9123 // set the ani for the button
9124 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_bmaps(Multi_sync_buttons[gr_screen.res][MS_LAUNCH].filename, 3, 0);
9127 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.link_hotspot(Multi_sync_buttons[gr_screen.res][MS_LAUNCH].hotspot);
9130 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_hotkey(KEY_CTRLED+KEY_ENTER);
9133 // create the text for the button
9134 Multi_sync_window.add_XSTR(&Multi_sync_text[gr_screen.res][MST_LAUNCH]);
9137 // increment the button count so we start checking this one
9138 Multi_sync_button_count++;
9140 Multi_launch_button_created = 1;
9144 void multi_sync_handle_plist()
9150 // if we don't have a currently selected player, select one
9151 if((Multi_sync_player_select < 0) || !MULTI_CONNECTED(Net_players[Multi_sync_player_select])){
9152 for(idx=0;idx<MAX_PLAYERS;idx++){
9153 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
9154 Multi_sync_player_select = idx;
9160 // check for button list presses
9161 if(Multi_sync_plist_button.pressed()){
9162 // get the y mouse coords
9163 Multi_sync_plist_button.get_mouse_pos(NULL,&my);
9165 // get the index of the item selected
9166 select_index = my / 10;
9168 // if the index is greater than the current # connections, do nothing
9169 if(select_index > (multi_num_connections() - 1)){
9173 // translate into an absolute Net_players[] index (get the Nth net player)
9174 Multi_sync_player_select = -1;
9175 for(idx=0;idx<MAX_PLAYERS;idx++){
9176 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
9180 // if we've found the item we're looking for
9181 if(select_index < 0){
9182 Multi_sync_player_select = idx;
9187 // if for some bizarre reason, this is an invalid player, unselect him and wait for the next interation
9189 if((Multi_sync_player_select >= 0) && (!MULTI_CONNECTED(Net_players[Multi_sync_player_select]) || MULTI_STANDALONE(Net_players[Multi_sync_player_select])) ){
9190 Multi_sync_player_select = -1;
9196 // -------------------------------------------------------------------------------------------------------------
9198 // MULTIPLAYER DEBRIEF SCREEN
9201 // other relevant data
9202 int Multi_debrief_accept_hit;
9203 int Multi_debrief_replay_hit;
9205 // set if the server has left the game
9206 int Multi_debrief_server_left = 0;
9208 // if we've reported on TvT status all players are in the debrief
9209 int Multi_debrief_reported_tvt = 0;
9211 // whether stats are being accepted
9212 // -1 == no decision yet
9215 int Multi_debrief_stats_accept_code = -1;
9217 int Multi_debrief_server_framecount = 0;
9219 float Multi_debrief_time = 0.0f;
9220 float Multi_debrief_resend_time = 10.0f;
9222 void multi_debrief_init()
9226 Multi_debrief_time = 0.0f;
9227 Multi_debrief_resend_time = 10.0f;
9229 // do this to notify the standalone or the normal server that we're in the debrief state and ready to receive packets
9230 if (!(Net_player->flags & NETINFO_FLAG_AM_MASTER)) {
9231 Net_player->state = NETPLAYER_STATE_DEBRIEF;
9232 send_netplayer_update_packet();
9235 // unflag some stuff
9236 for(idx=0;idx<MAX_PLAYERS;idx++){
9237 if(MULTI_CONNECTED(Net_players[idx])){
9238 Net_players[idx].flags &= ~(NETINFO_FLAG_RESPAWNING | NETINFO_FLAG_LIMBO | NETINFO_FLAG_WARPING_OUT);
9242 // if text input mode is active, clear it
9243 multi_msg_text_flush();
9245 // the server has not left yet
9246 Multi_debrief_server_left = 0;
9248 // have not hit accept or replay yet
9249 Multi_debrief_accept_hit = 0;
9250 Multi_debrief_replay_hit = 0;
9252 // stats have not been accepted yet
9253 Multi_debrief_stats_accept_code = -1;
9255 // mark stats as not being store yet
9256 Netgame.flags &= ~(NG_FLAG_STORED_MT_STATS);
9258 // no report on TvT yet
9259 Multi_debrief_reported_tvt = 0;
9261 Multi_debrief_server_framecount = 0;
9264 void multi_debrief_do_frame()
9266 Multi_debrief_time += flFrametime;
9268 // set the netgame state to be debriefing when appropriate
9269 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)){
9270 Netgame.game_state = NETGAME_STATE_DEBRIEF;
9271 send_netgame_update_packet();
9274 // evaluate all server stuff
9275 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
9276 multi_debrief_server_process();
9280 void multi_debrief_close()
9282 if ( MULTIPLAYER_CLIENT && (Netgame.game_state == NETGAME_STATE_MISSION_SYNC) ){
9283 gamesnd_play_iface( SND_COMMIT_PRESSED );
9287 // handle optional mission loop
9288 void multi_maybe_set_mission_loop()
9290 int cur = Campaign.current_mission;
9291 if (Campaign.missions[cur].has_mission_loop) {
9292 Assert(Campaign.loop_mission != CAMPAIGN_LOOP_MISSION_UNINITIALIZED);
9294 bool require_repeat_mission = (Campaign.current_mission == Campaign.next_mission);
9296 // check for (1) mission loop available, (2) dont have to repeat last mission
9297 if ( (Campaign.missions[cur].has_mission_loop && (Campaign.loop_mission != -1)) && !require_repeat_mission ) {
9300 debrief_assemble_optional_mission_popup_text(buffer, Campaign.missions[cur].mission_loop_desc);
9302 int choice = popup(0 , 2, POPUP_NO, POPUP_YES, buffer);
9304 Campaign.loop_enabled = 1;
9305 Campaign.next_mission = Campaign.loop_mission;
9310 // handle all cases for when the accept key is hit in a multiplayer debriefing
9311 void multi_debrief_accept_hit()
9313 // if we already accepted, do nothing
9314 // but he may need to hit accept again after the server has left the game, so allow this
9315 if(Multi_debrief_accept_hit){
9319 // mark this so that we don't hit it again
9320 Multi_debrief_accept_hit = 1;
9322 gamesnd_play_iface(SND_COMMIT_PRESSED);
9324 // if the server has left the game, always just end the game.
9325 if(Multi_debrief_server_left){
9326 if(!multi_quit_game(PROMPT_ALL)){
9327 Multi_debrief_server_left = 1;
9328 Multi_debrief_accept_hit = 0;
9330 Multi_debrief_server_left = 0;
9333 // query the host and see if he wants to accept stats
9334 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
9335 // if we're on a tracker game, he gets no choice for storing stats
9336 if(MULTI_IS_TRACKER_GAME){
9337 multi_maybe_set_mission_loop();
9339 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));
9341 // evaluate the result
9346 Multi_debrief_accept_hit = 0;
9349 // set the accept code to be "not accepting"
9351 multi_debrief_stats_toss();
9352 multi_maybe_set_mission_loop();
9355 // accept the stats and continue
9357 multi_debrief_stats_accept();
9358 multi_maybe_set_mission_loop();
9364 // set my netplayer state to be "debrief_accept", and be done with it
9365 Net_player->state = NETPLAYER_STATE_DEBRIEF_ACCEPT;
9366 send_netplayer_update_packet();
9370 // handle all cases for when the escape key is hit in a multiplayer debriefing
9371 void multi_debrief_esc_hit()
9375 // if the server has left
9376 if(Multi_debrief_server_left){
9377 multi_quit_game(PROMPT_ALL);
9382 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
9383 // if the stats have already been accepted
9384 if((Multi_debrief_stats_accept_code != -1) || (MULTI_IS_TRACKER_GAME)){
9385 multi_quit_game(PROMPT_HOST);
9387 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));
9389 // evaluate the result
9396 // set the accept code to be "not accepting"
9398 multi_debrief_stats_toss();
9399 multi_quit_game(PROMPT_NONE);
9402 // accept the stats and continue
9404 multi_debrief_stats_accept();
9405 multi_quit_game(PROMPT_NONE);
9410 // if the stats haven't been accepted yet, or this is a tracker game
9411 if((Multi_debrief_stats_accept_code == -1) && !(MULTI_IS_TRACKER_GAME)){
9412 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));
9414 // evaluate the result
9416 multi_quit_game(PROMPT_NONE);
9419 // otherwise go through the normal endgame channels
9421 multi_quit_game(PROMPT_ALL);
9426 void multi_debrief_replay_hit()
9428 // only the host should ever get here
9429 Assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
9431 // if the button was already pressed, do nothing
9432 if(Multi_debrief_accept_hit){
9436 // same as hittin the except button except no stats are kept
9437 Multi_debrief_accept_hit = 1;
9439 // mark myself as being in the replay state so we know what to do next
9440 Net_player->state = NETPLAYER_STATE_DEBRIEF_REPLAY;
9441 send_netplayer_update_packet();
9444 // call this when the server has left and we would otherwise be saying "contact lost with server
9445 void multi_debrief_server_left()
9448 Multi_debrief_server_left = 1;
9450 // undo any "accept" hit so that clients can hit accept again to leave
9451 Multi_debrief_accept_hit = 0;
9454 void multi_debrief_stats_accept()
9456 // don't do anything if we've already accepted
9457 if(Multi_debrief_stats_accept_code != -1){
9461 Multi_debrief_stats_accept_code = 1;
9463 // if we're the host, and we're on a standalone, tell the standalone to begin the stats storing process
9464 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) || (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
9465 // send a packet to the players telling them to store their stats
9466 send_store_stats_packet(1);
9469 // add a chat line saying "stats have been accepted"
9470 multi_display_chat_msg(XSTR("<stats have been accepted>",850),0,0);
9473 ml_string(NOX("Stats stored"));
9476 void multi_debrief_stats_toss()
9478 // don't do anything if we've already accepted
9479 if(Multi_debrief_stats_accept_code != -1){
9483 Multi_debrief_stats_accept_code = 0;
9485 // if we're the host, and we're on a standalone, tell everyone to "toss" stats
9486 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) || (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
9487 // send a packet to the players telling them to store their stats
9488 send_store_stats_packet(0);
9491 // add a chat line saying "stats have been accepted"
9492 multi_display_chat_msg(XSTR("<stats have been tossed>",851),0,0);
9495 ml_string(NOX("Stats tossed"));
9498 int multi_debrief_stats_accept_code()
9500 return Multi_debrief_stats_accept_code;
9503 void multi_debrief_server_process()
9506 int player_status,other_status;
9508 Multi_debrief_server_framecount++;
9510 // if we're > 10 seconds into the debrief and not everyone is here, try warping everyone out again
9511 if((Multi_debrief_time >= Multi_debrief_resend_time) && !multi_netplayer_state_check3(NETPLAYER_STATE_DEBRIEF, NETPLAYER_STATE_DEBRIEF_ACCEPT, NETPLAYER_STATE_DEBRIEF_REPLAY, 1)){
9512 // find all players who are not in the debrief state and hit them with the endgame packet
9513 for(idx=0; idx<MAX_PLAYERS; idx++){
9514 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)) ){
9515 send_endgame_packet(&Net_players[idx]);
9520 Multi_debrief_resend_time += 7.0f;
9523 // evaluate the status of all players in the game (0 == not ready, 1 == ready to continue, 2 == ready to replay)
9526 // check all players
9527 for(idx=0;idx<MAX_PLAYERS;idx++){
9528 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_HOST(Net_players[idx])){
9529 if(Net_players[idx].state != NETPLAYER_STATE_DEBRIEF_ACCEPT){
9536 // if we haven't already reported TvT results
9537 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)){
9538 multi_team_report();
9539 Multi_debrief_reported_tvt = 1;
9542 // if all other players are good to go, check the host
9544 // if he is ready to continue
9545 if(Netgame.host->state == NETPLAYER_STATE_DEBRIEF_ACCEPT){
9548 // if he wants to replay the mission
9549 else if(Netgame.host->state == NETPLAYER_STATE_DEBRIEF_REPLAY){
9552 // if he is not ready
9557 // if all players are _not_ good to go
9562 // if we're in the debriefing state in a campaign mode, process accordingly
9563 if(Netgame.campaign_mode == MP_CAMPAIGN){
9564 multi_campaign_do_debrief(player_status);
9566 // otherwise process as normal (looking for all players to be ready to go to the next mission
9568 if(player_status == 1){
9569 multi_flush_mission_stuff();
9571 // set the netgame state to be forming and continue
9572 Netgame.game_state = NETGAME_STATE_FORMING;
9573 send_netgame_update_packet();
9575 // move to the proper state
9576 if(Game_mode & GM_STANDALONE_SERVER){
9577 gameseq_post_event(GS_EVENT_STANDALONE_MAIN);
9579 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
9582 multi_reset_timestamps();
9583 } else if(player_status == 2){
9584 multi_flush_mission_stuff();
9586 // tell everyone to move into the pre-briefing sync state
9587 Netgame.game_state = NETGAME_STATE_MISSION_SYNC;
9588 send_netgame_update_packet();
9590 // move back to the mission sync screen for the same mission again
9591 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
9592 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
9594 multi_reset_timestamps();
9600 // -------------------------------------------------------------------------------------------------------------
9602 // MULTIPLAYER PASSWORD POPUP
9607 static char *Multi_pwd_bitmap_fname[GR_NUM_RESOLUTIONS] = {
9608 "Password", // GR_640
9609 "2_Password" // GR_1024
9612 static char *Multi_pwd_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
9613 "Password-M", // GR_640
9614 "2_Password-M" // GR_1024
9619 // constants for coordinate lookup
9620 #define MPWD_X_COORD 0
9621 #define MPWD_Y_COORD 1
9622 #define MPWD_W_COORD 2
9623 #define MPWD_H_COORD 3
9626 #define MULTI_PWD_NUM_BUTTONS 2
9627 #define MPWD_CANCEL 0
9628 #define MPWD_COMMIT 1
9630 // password area defs
9631 int Mpwd_coords[GR_NUM_RESOLUTIONS][4] = {
9644 UI_WINDOW Multi_pwd_window; // the window object for the join screen
9645 UI_INPUTBOX Multi_pwd_passwd; // for Netgame.passwd
9646 int Multi_pwd_bitmap; // the background bitmap
9647 int Multi_passwd_background = -1;
9648 int Multi_passwd_done = -1;
9649 int Multi_passwd_running = 0;
9652 ui_button_info Multi_pwd_buttons[GR_NUM_RESOLUTIONS][MULTI_PWD_NUM_BUTTONS] = {
9655 ui_button_info("PWB_00", 402, 134, -1, -1, 0),
9656 ui_button_info("PWB_01", 450, 134, -1, -1, 1),
9658 ui_button_info("PWB_00", 411, 151, 405, 141, 0),
9659 ui_button_info("PWB_01", 460, 151, 465, 141, 1),
9663 ui_button_info("2_PWB_00", 659, 242, 649, 225, 0),
9664 ui_button_info("2_PWB_01", 737, 242, 736, 225, 1),
9670 #define MULTI_PWD_NUM_TEXT 0
9672 #define MULTI_PWD_NUM_TEXT 3
9674 UI_XSTR Multi_pwd_text[GR_NUM_RESOLUTIONS][MULTI_PWD_NUM_TEXT] = {
9676 // not needed for FS1
9678 { "Cancel", 387, 400, 141, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[0][MPWD_CANCEL].button},
9679 { "Commit", 1062, 455, 141, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[0][MPWD_COMMIT].button},
9680 { "Enter Password", 1332, 149, 92, UI_XSTR_COLOR_GREEN, -1, NULL},
9684 // not needed for FS1
9686 { "Cancel", 387, 649, 225, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[1][MPWD_CANCEL].button},
9687 { "Commit", 1062, 736, 225, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[1][MPWD_COMMIT].button},
9688 { "Enter Password", 1332, 239, 148, UI_XSTR_COLOR_GREEN, -1, NULL},
9693 // initialize all graphics, etc
9694 void multi_passwd_init()
9698 // store the background as it currently is
9699 Multi_passwd_background = gr_save_screen();
9701 // create the interface window
9702 Multi_pwd_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
9703 Multi_pwd_window.set_mask_bmap(Multi_pwd_bitmap_mask_fname[gr_screen.res]);
9705 // load the background bitmap
9706 Multi_pwd_bitmap = bm_load(Multi_pwd_bitmap_fname[gr_screen.res]);
9707 if(Multi_pwd_bitmap < 0){
9708 // we failed to load the bitmap - this is very bad
9712 // create the interface buttons
9713 for(idx=0; idx<MULTI_PWD_NUM_BUTTONS; idx++){
9714 // create the object
9715 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);
9717 // set the sound to play when highlighted
9718 Multi_pwd_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
9720 // set the ani for the button
9721 Multi_pwd_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pwd_buttons[gr_screen.res][idx].filename);
9724 Multi_pwd_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pwd_buttons[gr_screen.res][idx].hotspot);
9728 for(idx=0; idx<MULTI_PWD_NUM_TEXT; idx++){
9729 Multi_pwd_window.add_XSTR(&Multi_pwd_text[gr_screen.res][idx]);
9732 // create the password input box
9733 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);
9734 Multi_pwd_passwd.set_focus();
9736 // link the enter key to ACCEPT
9737 Multi_pwd_buttons[gr_screen.res][MPWD_COMMIT].button.set_hotkey(KEY_ENTER);
9739 Multi_passwd_done = -1;
9740 Multi_passwd_running = 1;
9743 // close down all graphics, etc
9744 void multi_passwd_close()
9746 // unload any bitmaps
9747 bm_release(Multi_pwd_bitmap);
9749 // destroy the UI_WINDOW
9750 Multi_pwd_window.destroy();
9752 // free up the saved background screen
9753 if(Multi_passwd_background >= 0){
9754 gr_free_screen(Multi_passwd_background);
9755 Multi_passwd_background = -1;
9758 Multi_passwd_running = 0;
9761 // process any button pressed
9762 void multi_passwd_process_buttons()
9764 // if the accept button was pressed
9765 if(Multi_pwd_buttons[gr_screen.res][MPWD_COMMIT].button.pressed()){
9766 gamesnd_play_iface(SND_USER_SELECT);
9767 Multi_passwd_done = 1;
9770 // if the cancel button was pressed
9771 if(Multi_pwd_buttons[gr_screen.res][MPWD_CANCEL].button.pressed()){
9772 gamesnd_play_iface(SND_USER_SELECT);
9773 Multi_passwd_done = 0;
9777 // run the passwd popup
9778 void multi_passwd_do(char *passwd)
9782 while(Multi_passwd_done == -1){
9783 // set frametime and run background stuff
9784 game_set_frametime(-1);
9785 game_do_state_common(gameseq_get_state());
9787 k = Multi_pwd_window.process();
9789 // process any keypresses
9792 // set this to indicate the user has cancelled for one reason or another
9793 Multi_passwd_done = 0;
9797 // if the input box text has changed
9798 if(Multi_pwd_passwd.changed()){
9800 Multi_pwd_passwd.get_text(passwd);
9803 // process any button pressed
9804 multi_passwd_process_buttons();
9806 // draw the background, etc
9809 if(Multi_passwd_background >= 0){
9810 gr_restore_screen(Multi_passwd_background);
9812 gr_set_bitmap(Multi_pwd_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
9814 Multi_pwd_window.draw();
9821 // bring up the password string popup, fill in passwd (return 1 if accept was pressed, 0 if cancel was pressed)
9822 int multi_passwd_popup(char *passwd)
9824 // if the popup is already running for some reason, don't do anything
9825 if(Multi_passwd_running){
9829 // initialize all graphics
9830 multi_passwd_init();
9833 multi_passwd_do(passwd);
9835 // shut everything down
9836 multi_passwd_close();
9838 return Multi_passwd_done;