2 * $Logfile: /Freespace2/code/Network/MultiUI.cpp $
7 * C file for all the UI controls of the mulitiplayer screens
10 * Revision 1.6 2002/06/02 06:02:59 relnev
13 * Revision 1.5 2002/06/02 00:31:35 relnev
14 * implemented osregistry
16 * Revision 1.4 2002/06/01 07:12:33 relnev
17 * a few NDEBUG updates.
19 * removed a few warnings.
21 * Revision 1.3 2002/05/26 20:49:54 theoddone33
24 * Revision 1.2 2002/05/07 03:16:47 theoddone33
25 * The Great Newline Fix
27 * Revision 1.1.1.1 2002/05/03 03:28:10 root
31 * 94 6/16/00 3:16p Jefff
32 * sim of the year dvd version changes, a few german soty localization
35 * 93 10/14/99 2:51p Jefff
38 * 92 10/13/99 3:50p Jefff
39 * fixed unnumbered XSTRs
41 * 91 9/15/99 1:45a Dave
42 * Don't init joystick on standalone. Fixed campaign mode on standalone.
43 * Fixed no-score-report problem in TvT
45 * 90 9/14/99 12:51a Jefff
48 * 89 9/13/99 4:52p Dave
51 * 88 9/13/99 11:30a Dave
52 * Added checkboxes and functionality for disabling PXO banners as well as
53 * disabling d3d zbuffer biasing.
55 * 87 9/12/99 10:06p Jefff
56 * changed instances of "Squad War" to "SquadWar"
58 * 86 9/03/99 1:32a Dave
59 * CD checking by act. Added support to play 2 cutscenes in a row
60 * seamlessly. Fixed super low level cfile bug related to files in the
61 * root directory of a CD. Added cheat code to set campaign mission # in
64 * 85 9/01/99 10:49p Dave
65 * Added nice SquadWar checkbox to the client join wait screen.
67 * 84 8/30/99 2:49p Jefff
69 * 83 8/26/99 8:49p Jefff
70 * Updated medals screen and about everything that ever touches medals in
71 * one way or another. Sheesh.
73 * 82 8/25/99 4:38p Dave
74 * Updated PXO stuff. Make squad war report stuff much more nicely.
76 * 81 8/20/99 2:09p Dave
79 * 80 8/20/99 10:06a Jefff
80 * removed closed/rstricted buttons from multi start screen
82 * 79 8/18/99 11:30a Jefff
84 * 78 8/18/99 10:38a Jefff
86 * 77 8/16/99 4:06p Dave
87 * Big honking checkin.
89 * 76 8/16/99 1:08p Jefff
90 * added sounds to a few controls, made input boxes lose focus on ENTER
92 * 75 8/16/99 9:52a Jefff
93 * fixed bitmap loading on buttons in multi-sync screen
95 * 74 8/11/99 5:54p Dave
96 * Fixed collision problem. Fixed standalone ghost problem.
98 * 73 8/10/99 4:35p Jefff
101 * 72 8/06/99 12:29a Dave
102 * Multiple bug fixes.
104 * 71 8/05/99 3:13p Jasenw
105 * tweaked some text placement coords.
107 * 70 8/04/99 1:38p Jefff
108 * moved some text in multi join wait
110 * 69 8/03/99 12:45p Dave
113 * 68 7/25/99 5:17p Jefff
114 * campaign descriptions show up on multicreate screen
116 * 67 7/20/99 1:49p Dave
117 * Peter Drake build. Fixed some release build warnings.
119 * 66 7/19/99 2:13p Dave
120 * Added some new strings for Heiko.
122 * 65 7/15/99 9:20a Andsager
123 * FS2_DEMO initial checkin
125 * 64 7/08/99 10:53a Dave
126 * New multiplayer interpolation scheme. Not 100% done yet, but still
127 * better than the old way.
129 * 63 6/30/99 10:49a Jasenw
130 * Fixed coords for new launch countdown ani
132 * 62 6/29/99 7:39p Dave
133 * Lots of small bug fixes.
135 * 61 6/25/99 11:59a Dave
136 * Multi options screen.
138 * 60 6/09/99 2:17p Dave
139 * Fixed up pleasewait bitmap rendering.
141 * 59 6/05/99 3:42p Dave
142 * New multi sync screen.
144 * 58 6/01/99 6:07p Dave
145 * New loading/pause/please wait bar.
147 * 57 5/21/99 6:45p Dave
148 * Sped up ui loading a bit. Sped up localization disk access stuff. Multi
149 * start game screen, multi password, and multi pxo-help screen.
151 * 56 5/06/99 11:10a Dave
152 * Fixed coord on multi create screen.
154 * 55 5/04/99 6:38p Dave
155 * Finished multi join-wait screen.
157 * 54 5/04/99 5:20p Dave
158 * Fixed up multiplayer join screen and host options screen. Should both
161 * 53 5/03/99 11:04p Dave
162 * Most of the way done with the multi join screen.
164 * 52 5/03/99 8:32p Dave
165 * New version of multi host options screen.
167 * 51 4/29/99 2:15p Neilk
168 * slider2 code got modified; changed parameters for create
170 * 50 4/25/99 3:02p Dave
171 * Build defines for the E3 build.
173 * 49 4/21/99 6:15p Dave
174 * Did some serious housecleaning in the beam code. Made it ready to go
175 * for anti-fighter "pulse" weapons. Fixed collision pair creation. Added
176 * a handy macro for recalculating collision pairs for a given object.
178 * 48 4/16/99 5:27p Neilk
179 * added slider support and hir res for multi_create
181 * 47 4/14/99 6:37p Dave
182 * Fixed scroll button bug on host create screen.
184 * 46 4/14/99 5:28p Dave
187 * 45 4/12/99 10:07p Dave
188 * Made network startup more forgiving. Added checkmarks to dogfight
189 * screen for players who hit commit.
191 * 44 4/09/99 2:21p Dave
192 * Multiplayer beta stuff. CD checking.
194 * 43 4/08/99 1:28p Dave
195 * Small bug fixes for refresh button on the multi create screen.
197 * 42 4/08/99 11:55a Neilk
198 * Converted Multi_Create to new artwork (just lowres)
200 * 41 4/08/99 2:10a Dave
201 * Numerous bug fixes for the beta. Added builtin mission info for the
204 * 40 3/20/99 3:48p Andsager
205 * Do mission_loop stuff for PXO
207 * 39 3/10/99 6:50p Dave
208 * Changed the way we buffer packets for all clients. Optimized turret
209 * fired packets. Did some weapon firing optimizations.
211 * 38 3/09/99 6:24p Dave
212 * More work on object update revamping. Identified several sources of
213 * unnecessary bandwidth.
215 * 37 3/08/99 7:03p Dave
216 * First run of new object update system. Looks very promising.
218 * 36 2/25/99 4:19p Dave
219 * Added multiplayer_beta defines. Added cd_check define. Fixed a few
220 * release build warnings. Added more data to the squad war request and
223 * 35 2/24/99 3:26p Anoop
224 * Make sure the host is the only guy who bashes skill level for TvT.
226 * 34 2/24/99 2:25p Dave
227 * Fixed up chatbox bugs. Made squad war reporting better. Fixed a respawn
228 * bug for dogfight more.
230 * 33 2/23/99 2:29p Dave
231 * First run of oldschool dogfight mode.
233 * 32 2/17/99 2:11p Dave
234 * First full run of squad war. All freespace and tracker side stuff
237 * 31 2/12/99 6:16p Dave
238 * Pre-mission Squad War code is 95% done.
240 * 30 2/11/99 3:08p Dave
241 * PXO refresh button. Very preliminary squad war support.
243 * 29 2/08/99 5:07p Dave
244 * FS2 chat server support. FS2 specific validated missions.
246 * 28 2/04/99 6:29p Dave
247 * First full working rev of FS2 PXO support. Fixed Glide lighting
250 * 27 1/30/99 5:08p Dave
251 * More new hi-res stuff.Support for nice D3D textures.
253 * 26 1/29/99 2:08a Dave
254 * Fixed beam weapon collisions with players. Reduced size of scoring
255 * struct for multiplayer. Disabled PXO.
257 * 25 1/15/99 2:36p Neilk
258 * fixed multi_jw coordinates
260 * 24 1/13/99 7:19p Neilk
261 * Converted Mission Brief, Barracks, Synch to high res support
263 * 23 1/12/99 7:17p Neilk
265 * 22 1/12/99 5:45p Dave
266 * Moved weapon pipeline in multiplayer to almost exclusively client side.
267 * Very good results. Bandwidth goes down, playability goes up for crappy
268 * connections. Fixed object update problem for ship subsystems.
270 * 21 1/12/99 4:07a Dave
271 * Put in barracks code support for selecting squad logos. Properly
272 * distribute squad logos in a multiplayer game.
274 * 20 1/11/99 7:19p Neilk
275 * Converted multi_join interface to support multiple resolutions
277 * 19 12/18/98 1:13a Dave
278 * Rough 1024x768 support for Direct3D. Proper detection and usage through
281 * 18 12/17/98 4:50p Andsager
282 * Added debrief_assemble_optional_mission_popup_text() for single and
285 * 17 12/14/98 12:13p Dave
286 * Spiffed up xfer system a bit. Put in support for squad logo file xfer.
289 * 16 12/10/98 10:19a Andsager
290 * Fix mission loop assert
292 * 15 12/10/98 9:59a Andsager
293 * Fix some bugs with mission loops
295 * 14 12/09/98 1:56p Andsager
296 * Initial checkin of mission loop
298 * 13 12/03/98 5:22p Dave
299 * Ported over Freespace 1 multiplayer ships.tbl and weapons.tbl
302 * 12 11/30/98 1:07p Dave
303 * 16 bit conversion, first run.
305 * 11 11/20/98 11:16a Dave
306 * Fixed up IPX support a bit. Making sure that switching modes and
307 * loading/saving pilot files maintains proper state.
309 * 10 11/19/98 4:57p Dave
310 * Ignore PXO option if IPX is selected.
312 * 9 11/19/98 4:19p Dave
313 * Put IPX sockets back in psnet. Consolidated all multiplayer config
316 * 8 11/19/98 8:04a Dave
317 * Full support for D3-style reliable sockets. Revamped packet lag/loss
318 * system, made it receiver side and at the lowest possible level.
320 * 7 11/17/98 11:12a Dave
321 * Removed player identification by address. Now assign explicit id #'s.
323 * 6 10/19/98 11:15a Dave
324 * Changed requirements for stats storing in PXO mode.
326 * 5 10/16/98 9:40a Andsager
327 * Remove ".h" files from model.h
329 * 4 10/13/98 9:29a Dave
330 * Started neatening up freespace.h. Many variables renamed and
331 * reorganized. Added AlphaColors.[h,cpp]
333 * 3 10/07/98 6:27p Dave
334 * Globalized mission and campaign file extensions. Removed Silent Threat
335 * special code. Moved \cache \players and \multidata into the \data
338 * 2 10/07/98 10:53a Dave
341 * 1 10/07/98 10:50a Dave
343 * 333 10/02/98 3:22p Allender
344 * fix up the -connect option and fix the -port option
346 * 332 9/17/98 9:26p Dave
347 * Externalized new string.
349 * 331 9/17/98 3:08p Dave
350 * PXO to non-pxo game warning popup. Player icon stuff in create and join
351 * game screens. Upped server count refresh time in PXO to 35 secs (from
354 * 330 9/17/98 9:43a Allender
355 * removed an Assert that Dave called bogus.
357 * 329 9/16/98 6:54p Dave
358 * Upped max sexpression nodes to 1800 (from 1600). Changed FRED to sort
359 * the ship list box. Added code so that tracker stats are not stored with
362 * 328 9/15/98 7:24p Dave
363 * Minor UI changes. Localized bunch of new text.
365 * 327 9/15/98 4:03p Dave
366 * Changed readyroom and multi screens to display "st" icon for all
367 * missions with mission disk content (not necessarily just those that
368 * come with Silent Threat).
370 * 326 9/15/98 11:44a Dave
371 * Renamed builtin ships and wepaons appropriately in FRED. Put in scoring
372 * scale factors. Fixed standalone filtering of MD missions to non-MD
375 * 325 9/13/98 9:36p Dave
376 * Support for new info icons for multiplayer missions (from-volition,
377 * valid, mission disk, etc).
379 * 324 9/11/98 4:14p Dave
380 * Fixed file checksumming of < file_size. Put in more verbose kicking and
381 * PXO stats store reporting.
383 * 323 9/10/98 1:17p Dave
384 * Put in code to flag missions and campaigns as being MD or not in Fred
385 * and Freespace. Put in multiplayer support for filtering out MD
386 * missions. Put in multiplayer popups for warning of non-valid missions.
388 * 322 9/04/98 3:51p Dave
389 * Put in validated mission updating and application during stats
392 * 321 8/31/98 2:06p Dave
393 * Make cfile sort the ordering or vp files. Added support/checks for
394 * recognizing "mission disk" players.
396 * 320 8/21/98 1:15p Dave
397 * Put in log system hooks in useful places.
399 * 319 8/20/98 5:31p Dave
400 * Put in handy multiplayer logfile system. Now need to put in useful
401 * applications of it all over the code.
403 * 318 8/12/98 4:53p Dave
404 * Put in 32 bit checksumming for PXO missions. No validation on the
405 * actual tracker yet, though.
407 * 317 8/07/98 10:40a Allender
408 * new command line flags for starting netgames. Only starting currently
409 * works, and PXO isn't implemented yet
411 * 316 7/24/98 9:27a Dave
412 * Tidied up endgame sequencing by removing several old flags and
413 * standardizing _all_ endgame stuff with a single function call.
415 * 315 7/14/98 10:04a Allender
416 * fixed the countdown code to not be reliant on timer_get_fixed_seconds
418 * 314 7/10/98 5:04p Dave
419 * Fix connection speed bug on standalone server.
421 * 313 7/09/98 6:01p Dave
422 * Firsts full version of PXO updater. Put in stub for displaying
425 * 312 7/07/98 2:49p Dave
428 * 311 6/30/98 2:17p Dave
429 * Revised object update system. Removed updates for all weapons. Put
430 * button info back into control info packet.
432 * 310 6/13/98 9:32p Mike
433 * Kill last character in file which caused "Find in Files" to report the
434 * file as "not a text file."
441 #include <winsock.h> // for inet_addr()
443 #include <sys/types.h>
444 #include <sys/socket.h>
445 #include <netinet/in.h>
446 #include <arpa/inet.h>
451 #include "multiutil.h"
452 #include "multimsgs.h"
458 #include "gamesequence.h"
459 #include "freespace.h"
460 #include "contexthelp.h"
465 #include "missionshipchoice.h"
466 #include "multi_xfer.h"
468 #include "stand_gui.h"
469 #include "linklist.h"
470 #include "multiteamselect.h"
471 #include "missioncampaign.h"
478 #include "missiondebrief.h"
479 #include "multi_ingame.h"
480 #include "multi_kick.h"
481 #include "multi_data.h"
482 #include "multi_campaign.h"
483 #include "multi_team.h"
484 #include "multi_pinfo.h"
485 #include "multi_observer.h"
486 #include "multi_voice.h"
487 #include "multi_endgame.h"
488 #include "managepilot.h"
491 #include "objcollide.h"
493 #include "multi_pmsg.h"
494 #include "multi_obj.h"
495 #include "multi_log.h"
496 #include "alphacolors.h"
497 #include "animplay.h"
498 #include "multi_dogfight.h"
499 #include "missionpause.h"
501 // -------------------------------------------------------------------------------------------------------------
503 // MULTIPLAYER COMMON interface controls
506 // the common text info box stuff. This is lifted almost directly from Alans briefing code (minus the spiffy colored, scrolling
508 int Multi_common_text_coords[GR_NUM_RESOLUTIONS][4] = {
517 int Multi_common_text_max_display[GR_NUM_RESOLUTIONS] = {
522 #define MULTI_COMMON_TEXT_META_CHAR '$'
523 #define MULTI_COMMON_TEXT_MAX_LINE_LENGTH 100
524 #define MULTI_COMMON_TEXT_MAX_LINES 20
525 #define MULTI_COMMON_MAX_TEXT (MULTI_COMMON_TEXT_MAX_LINES * MULTI_COMMON_TEXT_MAX_LINE_LENGTH)
527 char Multi_common_all_text[MULTI_COMMON_MAX_TEXT];
528 char Multi_common_text[MULTI_COMMON_TEXT_MAX_LINES][MULTI_COMMON_TEXT_MAX_LINE_LENGTH];
530 int Multi_common_top_text_line = -1; // where to start displaying from
531 int Multi_common_num_text_lines = 0; // how many lines we have
533 void multi_common_scroll_text_up();
534 void multi_common_scroll_text_down();
535 void multi_common_move_to_bottom();
536 void multi_common_render_text();
537 void multi_common_split_text();
539 #define MAX_IP_STRING 255 // maximum length for ip string
541 void multi_common_scroll_text_up()
543 Multi_common_top_text_line--;
544 if ( Multi_common_top_text_line < 0 ) {
545 Multi_common_top_text_line = 0;
546 if ( !mouse_down(MOUSE_LEFT_BUTTON) )
547 gamesnd_play_iface(SND_GENERAL_FAIL);
550 gamesnd_play_iface(SND_SCROLL);
554 void multi_common_scroll_text_down()
556 Multi_common_top_text_line++;
557 if ( (Multi_common_num_text_lines - Multi_common_top_text_line) < Multi_common_text_max_display[gr_screen.res] ) {
558 Multi_common_top_text_line--;
559 if ( !mouse_down(MOUSE_LEFT_BUTTON) ){
560 gamesnd_play_iface(SND_GENERAL_FAIL);
563 gamesnd_play_iface(SND_SCROLL);
567 void multi_common_move_to_bottom()
569 // if there's nowhere to scroll down, do nothing
570 if(Multi_common_num_text_lines <= Multi_common_text_max_display[gr_screen.res]){
574 Multi_common_top_text_line = Multi_common_num_text_lines - Multi_common_text_max_display[gr_screen.res];
577 void multi_common_set_text(char *str,int auto_scroll)
580 // store the entire string as well
581 if(strlen(str) > MULTI_COMMON_MAX_TEXT){
584 strcpy(Multi_common_all_text,str);
587 // split the whole thing up
588 multi_common_split_text();
590 // scroll to the bottom if we're supposed to
592 multi_common_move_to_bottom();
596 void multi_common_add_text(char *str,int auto_scroll)
599 // store the entire string as well
600 if((strlen(str) + strlen(Multi_common_all_text)) > MULTI_COMMON_MAX_TEXT){
603 strcat(Multi_common_all_text,str);
606 // split the whole thing up
607 multi_common_split_text();
609 // scroll to the bottom if we're supposed to
611 multi_common_move_to_bottom();
615 void multi_common_split_text()
618 int n_chars[MAX_BRIEF_LINES];
619 char *p_str[MAX_BRIEF_LINES];
621 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);
622 Assert(n_lines != -1);
624 for ( i = 0; i < n_lines; i++ ) {
625 Assert(n_chars[i] < MULTI_COMMON_TEXT_MAX_LINE_LENGTH);
626 strncpy(Multi_common_text[i], p_str[i], n_chars[i]);
627 Multi_common_text[i][n_chars[i]] = 0;
628 drop_leading_white_space(Multi_common_text[i]);
631 Multi_common_top_text_line = 0;
632 Multi_common_num_text_lines = n_lines;
635 void multi_common_render_text()
637 int i, fh, line_count;
639 fh = gr_get_font_height();
642 gr_set_color_fast(&Color_text_normal);
643 for ( i = Multi_common_top_text_line; i < Multi_common_num_text_lines; i++ ) {
644 if ( line_count >= Multi_common_text_max_display[gr_screen.res] ){
647 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]);
651 if ( (Multi_common_num_text_lines - Multi_common_top_text_line) > Multi_common_text_max_display[gr_screen.res] ) {
652 gr_set_color_fast(&Color_bright_red);
653 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));
657 // common notification messaging stuff
658 #define MULTI_COMMON_NOTIFY_TIME 3500
659 int Multi_common_join_y[GR_NUM_RESOLUTIONS] = {
663 int Multi_common_create_y[GR_NUM_RESOLUTIONS] = {
668 int Multi_common_jw_y[GR_NUM_RESOLUTIONS] = {
673 int Multi_common_msg_y[GR_NUM_RESOLUTIONS] = {
678 char Multi_common_notify_text[200];
679 int Multi_common_notify_stamp;
681 void multi_common_notify_init()
683 strcpy(Multi_common_notify_text,"");
684 Multi_common_notify_stamp = -1;
687 // add a notification string, drawing appropriately depending on the state/screen we're in
688 void multi_common_add_notify(char *str)
691 strcpy(Multi_common_notify_text,str);
692 Multi_common_notify_stamp = timestamp(MULTI_COMMON_NOTIFY_TIME);
696 // process/display notification messages
697 void multi_common_notify_do()
699 if(Multi_common_notify_stamp != -1){
700 if(timestamp_elapsed(Multi_common_notify_stamp)){
701 Multi_common_notify_stamp = -1;
704 gr_get_string_size(&w,&h,Multi_common_notify_text);
705 gr_set_color_fast(&Color_white);
707 // determine where it should be placed based upon which screen we're on
709 switch(gameseq_get_state()){
710 case GS_STATE_MULTI_JOIN_GAME :
711 y = Multi_common_join_y[gr_screen.res];
713 case GS_STATE_MULTI_HOST_SETUP :
714 y = Multi_common_create_y[gr_screen.res];
716 case GS_STATE_MULTI_CLIENT_SETUP :
717 y = Multi_common_jw_y[gr_screen.res];
719 case GS_STATE_MULTI_START_GAME :
720 y = Multi_common_msg_y[gr_screen.res];
724 gr_string((gr_screen.max_w - w)/2, y, Multi_common_notify_text);
731 int Multi_common_icons[MULTI_NUM_COMMON_ICONS];
733 char *Multi_common_icon_names[MULTI_NUM_COMMON_ICONS] = {
734 "DotRed", // voice denied
735 "DotGreen", // voice recording
736 "OvalGreen", // team 0
737 "OvalGreen01", // team 0 select
739 "OvalRed01", // team 1 select
740 "mp_coop", // coop mission
741 "mp_teams", // TvT mission
742 "mp_furball", // furball mission
743 "icon-volition", // volition mission
744 "icon-valid", // mission is valid
748 // width and height of the icons
749 int Multi_common_icon_dims[MULTI_NUM_COMMON_ICONS][2] = {
750 {11, 11}, // voice denied
751 {11, 11}, // voice recording
753 {11, 11}, // team 0 select
755 {11, 11}, // team 1 select
758 {18, 11}, // mp furball
759 {9, 9}, // volition mission
760 {8, 8}, // mission is valid
764 void multi_load_common_icons()
769 for(idx=0; idx<MULTI_NUM_COMMON_ICONS; idx++){
770 Multi_common_icons[idx] = -1;
771 Multi_common_icons[idx] = bm_load(Multi_common_icon_names[idx]);
775 void multi_unload_common_icons()
780 for(idx=0; idx<MULTI_NUM_COMMON_ICONS; idx++){
781 if(Multi_common_icons[idx] != -1){
782 bm_unload(Multi_common_icons[idx]);
783 Multi_common_icons[idx] = -1;
788 // display any relevant voice status icons
789 void multi_common_voice_display_status()
791 switch(multi_voice_status()){
792 // i have been denied the voice token
793 case MULTI_VOICE_STATUS_DENIED:
794 if(Multi_common_icons[MICON_VOICE_DENIED] != -1){
795 gr_set_bitmap(Multi_common_icons[MICON_VOICE_DENIED]);
800 // i am currently recording
801 case MULTI_VOICE_STATUS_RECORDING:
802 if(Multi_common_icons[MICON_VOICE_RECORDING] != -1){
803 gr_set_bitmap(Multi_common_icons[MICON_VOICE_RECORDING]);
808 // i am currently playing back sound
809 case MULTI_VOICE_STATUS_PLAYING:
812 // the system is currently idle
813 case MULTI_VOICE_STATUS_IDLE:
819 // palette initialization stuff
820 #define MULTI_COMMON_PALETTE_FNAME "InterfacePalette"
823 int Multi_common_interface_palette = -1;
825 void multi_common_load_palette();
826 void multi_common_set_palette();
827 void multi_common_unload_palette();
829 // load in the palette if it doesn't already exist
830 void multi_common_load_palette()
832 if(Multi_common_interface_palette != -1){
836 Multi_common_interface_palette = bm_load(MULTI_COMMON_PALETTE_FNAME);
837 if(Multi_common_interface_palette == -1){
838 nprintf(("Network","Error loading multiplayer common palette!\n"));
842 // set the common palette to be the active one
843 void multi_common_set_palette()
845 // if the palette is not loaded yet, do so now
846 if(Multi_common_interface_palette == -1){
847 multi_common_load_palette();
850 if(Multi_common_interface_palette != -1){
851 #ifndef HARDWARE_ONLY
852 palette_use_bm_palette(Multi_common_interface_palette);
857 // unload the bitmap palette
858 void multi_common_unload_palette()
860 if(Multi_common_interface_palette != -1){
861 bm_unload(Multi_common_interface_palette);
862 Multi_common_interface_palette = -1;
866 void multi_common_verify_cd()
869 // otherwise, call the freespace function to determine if we have a cd
871 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) ){
880 // -------------------------------------------------------------------------------------------------------------
882 // MULTIPLAYER JOIN SCREEN
885 #define MULTI_JOIN_NUM_BUTTONS 11
889 #define MULTI_JOIN_PALETTE "InterfacePalette"
891 static char *Multi_join_bitmap_fname[GR_NUM_RESOLUTIONS] = {
892 "MultiJoin", // GR_640
893 "2_MultiJoin" // GR_1024
896 static char *Multi_join_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
897 "MultiJoin-M", // GR_640
898 "2_MultiJoin-M" // GR_1024
903 char *Mj_slider_name[GR_NUM_RESOLUTIONS] = {
907 int Mj_slider_coords[GR_NUM_RESOLUTIONS][4] = {
917 #define MJ_SCROLL_UP 0
918 #define MJ_SCROLL_DOWN 1
920 #define MJ_SCROLL_INFO_UP 3
921 #define MJ_SCROLL_INFO_DOWN 4
922 #define MJ_JOIN_OBSERVER 5
923 #define MJ_START_GAME 6
929 // uses MULTI_JOIN_REFRESH_TIME as its timestamp
930 int Multi_join_glr_stamp;
932 #define MULTI_JOIN_PING_TIME 15000 // how often we ping all the known servers
933 int Multi_join_ping_stamp;
934 UI_WINDOW Multi_join_window; // the window object for the join screen
935 UI_BUTTON Multi_join_select_button; // for selecting list items
936 UI_SLIDER2 Multi_join_slider; // handy dandy slider
937 int Multi_join_bitmap; // the background bitmap
939 ui_button_info Multi_join_buttons[GR_NUM_RESOLUTIONS][MULTI_JOIN_NUM_BUTTONS] = {
941 ui_button_info( "MJ_00", 1, 57, -1, -1, 0 ), // scroll up
942 ui_button_info( "MJ_02", 1, 297, -1, -1, 2 ), // scroll down
943 ui_button_info( "MJ_03", 10, 338, 65, 364, 3 ), // refresh
944 ui_button_info( "MJ_04", 1, 405, -1, -1, 4 ), // scroll info up
945 ui_button_info( "MJ_05", 1, 446, -1, -1, 5 ), // scroll info down
946 ui_button_info( "MJ_06", 489, 339, -1, -1, 6 ), // join as observer
947 ui_button_info( "MJ_07", 538, 339, -1, -1, 7 ), // create game
948 ui_button_info( "MJ_08", 583, 339, 588, 376, 8 ), // cancel
949 ui_button_info( "MJ_09", 534, 426, -1, -1, 9 ), // help
950 ui_button_info( "MJ_10", 534, 454, -1, -1, 10 ), // options
951 ui_button_info( "MJ_11", 571, 426, 589, 416, 11 ), // join
954 ui_button_info( "2_MJ_00", 2, 92, -1, -1, 0 ), // scroll up
955 ui_button_info( "2_MJ_02", 2, 475, -1, -1, 2 ), // scroll down
956 ui_button_info( "2_MJ_03", 16, 541, 104, 582, 3 ), // refresh
957 ui_button_info( "2_MJ_04", 2, 648, -1, -1, 4 ), // scroll info up
958 ui_button_info( "2_MJ_05", 2, 713, -1, -1, 5 ), // scroll info down
959 ui_button_info( "2_MJ_06", 783, 542, -1, -1, 6 ), // join as observer
960 ui_button_info( "2_MJ_07", 861, 542, -1, -1, 7 ), // create game
961 ui_button_info( "2_MJ_08", 933, 542, 588, 376, 8 ), // cancel
962 ui_button_info( "2_MJ_09", 854, 681, -1, -1, 9 ), // help
963 ui_button_info( "2_MJ_10", 854, 727, -1, -1, 10 ), // options
964 ui_button_info( "2_MJ_11", 914, 681, 937, 668, 11 ), // join
968 #define MULTI_JOIN_NUM_TEXT 13
970 UI_XSTR Multi_join_text[GR_NUM_RESOLUTIONS][MULTI_JOIN_NUM_TEXT] = {
972 {"Refresh", 1299, 65, 364, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_REFRESH].button},
973 {"Join as", 1300, 476, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_JOIN_OBSERVER].button},
974 {"Observer", 1301, 467, 385, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_JOIN_OBSERVER].button},
975 {"Create", 1408, 535, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_START_GAME].button},
976 {"Game", 1302, 541, 385, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_START_GAME].button},
977 {"Cancel", 387, 588, 376, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[0][MJ_CANCEL].button},
978 {"Help", 928, 479, 436, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_HELP].button},
979 {"Options", 1036, 479, 460, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_OPTIONS].button},
980 {"Join", 1303, 589, 416, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[0][MJ_ACCEPT].button},
981 {"Status", 1304, 37, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
982 {"Server", 1305, 116, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
983 {"Players", 1306, 471, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
984 {"Ping", 1307, 555, 37, UI_XSTR_COLOR_GREEN, -1, NULL}
987 {"Refresh", 1299, 104, 582, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_REFRESH].button},
988 {"Join as", 1300, 783, 602, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_JOIN_OBSERVER].button},
989 {"Observer", 1301, 774, 611, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_JOIN_OBSERVER].button},
990 {"Create", 1408, 868, 602, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_START_GAME].button},
991 {"Game", 1302, 872, 611, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_START_GAME].button},
992 {"Cancel", 387, 941, 602, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[1][MJ_CANCEL].button},
993 {"Help", 928, 782, 699, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_HELP].button},
994 {"Options", 1036, 782, 736, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_OPTIONS].button},
995 {"Join", 1303, 937, 668, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[1][MJ_ACCEPT].button},
996 {"Status", 1304, 60, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
997 {"Server", 1305, 186, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
998 {"Players", 1306, 753, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
999 {"Ping", 1307, 888, 60, UI_XSTR_COLOR_GREEN, -1, NULL}
1003 // constants for coordinate look ups
1004 #define MJ_X_COORD 0
1005 #define MJ_Y_COORD 1
1006 #define MJ_W_COORD 2
1007 #define MJ_H_COORD 3
1009 #define MULTI_JOIN_SENT_WAIT 10000 // wait this long since a join was sent to allow another
1010 int Multi_join_sent_stamp;
1012 // game information text areas
1013 int Mj_max_game_items[GR_NUM_RESOLUTIONS] = {
1018 int Mj_list_y[GR_NUM_RESOLUTIONS] = {
1023 int Mj_status_coords[GR_NUM_RESOLUTIONS][4] = {
1032 int Mj_game_icon_coords[GR_NUM_RESOLUTIONS][3] = {
1041 int Mj_speed_coords[GR_NUM_RESOLUTIONS][4] = {
1050 int Mj_game_name_coords[GR_NUM_RESOLUTIONS][4] = {
1059 int Mj_players_coords[GR_NUM_RESOLUTIONS][4] = {
1068 int Mj_ping_coords[GR_NUM_RESOLUTIONS][4] = {
1077 // game speed labels
1078 #define MJ_NUM_SPEED_LABELS 5
1079 char *Multi_join_speed_labels[MJ_NUM_SPEED_LABELS] = {
1086 color *Multi_join_speed_colors[MJ_NUM_SPEED_LABELS] = {
1089 &Color_bright_green,
1090 &Color_bright_green,
1094 int Mj_cd_coords[GR_NUM_RESOLUTIONS] = {
1099 // extents of the entire boundable game info region
1100 // NOTE : these numbers are completely empirical
1101 #define MJ_PING_GREEN 160
1102 #define MJ_PING_YELLOW 300
1104 int Mj_list_area_coords[GR_NUM_RESOLUTIONS][4] = {
1113 // PXO channel filter
1114 #define MJ_PXO_FILTER_Y 0
1116 // special chars to indicate various status modes for servers
1117 #define MJ_CHAR_STANDALONE "*"
1118 #define MJ_CHAR_CAMPAIGN "c"
1121 // various interface indices
1122 int Multi_join_list_start; // where to start displaying from
1123 active_game *Multi_join_list_start_item; // a pointer to the corresponding active_game
1124 int Multi_join_list_selected; // which item we have selected
1125 active_game *Multi_join_selected_item; // a pointer to the corresponding active_game
1127 // use this macro to modify the list start
1128 #define MJ_LIST_START_INC() do { Multi_join_list_start++; } while(0);
1129 #define MJ_LIST_START_DEC() do { Multi_join_list_start--; } while(0);
1130 #define MJ_LIST_START_SET(vl) do { Multi_join_list_start = vl; } while(0);
1132 // if we should be sending a join request at the end of the frame
1133 int Multi_join_should_send = -1;
1135 // master tracker details
1136 int Multi_join_frame_count; // keep a count of frames displayed
1137 int Multi_join_mt_tried_verify; // already tried verifying the pilot with the tracker
1139 // data stuff for auto joining a game
1140 #define MULTI_AUTOJOIN_JOIN_STAMP 2000
1141 #define MULTI_AUTOJOIN_QUERY_STAMP 2000
1143 int Multi_did_autojoin;
1144 net_addr Multi_autojoin_addr;
1145 int Multi_autojoin_join_stamp;
1146 int Multi_autojoin_query_stamp;
1149 join_request Multi_join_request;
1151 // LOCAL function definitions
1152 void multi_join_check_buttons();
1153 void multi_join_button_pressed(int n);
1154 void multi_join_display_games();
1155 void multi_join_blit_game_status(active_game *game, int y);
1156 void multi_join_load_tcp_addrs();
1157 void multi_join_do_netstuff();
1158 void multi_join_ping_all();
1159 void multi_join_process_select();
1160 void multi_join_list_scroll_up();
1161 void multi_join_list_scroll_down();
1162 void multi_join_list_page_up();
1163 void multi_join_list_page_down();
1164 active_game *multi_join_get_game(int n);
1165 void multi_join_cull_timeouts();
1166 void multi_join_handle_item_cull(active_game *item, int item_index);
1167 void multi_join_send_join_request(int as_observer);
1168 void multi_join_create_game();
1169 void multi_join_blit_top_stuff();
1170 int multi_join_maybe_warn();
1171 int multi_join_warn_pxo();
1172 void multi_join_blit_protocol();
1176 active_game ag, *newitem;;
1179 dc_get_arg(ARG_INT);
1180 for(idx=0; idx<Dc_arg_int; idx++){
1181 // stuff some fake info
1182 memset(&ag, 0, sizeof(active_game));
1183 sprintf(ag.name, "Game %d", idx);
1184 ag.version = MULTI_FS_SERVER_VERSION;
1185 ag.comp_version = MULTI_FS_SERVER_VERSION;
1186 ag.server_addr.addr[0] = (char)idx;
1187 ag.flags = (AG_FLAG_COOP | AG_FLAG_FORMING | AG_FLAG_STANDALONE);
1190 newitem = multi_update_active_games(&ag);
1192 // timestamp it so we get random timeouts
1193 if(newitem != NULL){
1194 // newitem->heard_from_timer = timestamp((int)frand_range(500.0f, 10000.0f));
1199 void multi_join_notify_new_game()
1201 // reset the # of items
1202 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);
1203 Multi_join_slider.force_currentItem(Multi_join_list_start);
1206 int multi_join_autojoin_do()
1208 // if we have an active game on the list, then return a positive value so that we
1209 // can join the game
1210 if ( Active_game_head && (Active_game_count > 0) ) {
1211 Multi_join_selected_item = Active_game_head;
1215 // send out a server_query again
1216 if ( timestamp_elapsed(Multi_autojoin_query_stamp) ) {
1217 send_server_query(&Multi_autojoin_addr);
1218 Multi_autojoin_query_stamp = timestamp(MULTI_AUTOJOIN_QUERY_STAMP);
1224 void multi_join_game_init()
1228 // do the multiplayer init stuff - multi_level_init() now does all net_player zeroing.
1229 // setup various multiplayer things
1230 Assert( Game_mode & GM_MULTIPLAYER );
1231 Assert( Net_player != NULL );
1233 switch (Multi_options_g.protocol) {
1235 ADDRESS_LENGTH = IPX_ADDRESS_LENGTH;
1236 PORT_LENGTH = IPX_PORT_LENGTH;
1240 ADDRESS_LENGTH = IP_ADDRESS_LENGTH;
1241 PORT_LENGTH = IP_PORT_LENGTH;
1250 memset( &Netgame, 0, sizeof(Netgame) );
1253 Net_player->flags |= NETINFO_FLAG_DO_NETWORKING;
1254 Net_player->player = Player;
1255 memcpy(&Net_player->p_info.addr,&Psnet_my_addr,sizeof(net_addr));
1257 // check for the existence of a CD
1258 multi_common_verify_cd();
1260 // load my local netplayer options
1261 multi_options_local_load(&Net_player->p_info.options, Net_player);
1266 // common_set_interface_palette(MULTI_JOIN_PALETTE);
1268 // destroy any chatbox contents which previously existed (from another game)
1271 // create the interface window
1272 Multi_join_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
1273 Multi_join_window.set_mask_bmap(Multi_join_bitmap_mask_fname[gr_screen.res]);
1275 // load the background bitmap
1276 Multi_join_bitmap = bm_load(Multi_join_bitmap_fname[gr_screen.res]);
1277 if(Multi_join_bitmap < 0){
1278 // we failed to load the bitmap - this is very bad
1282 // intialize the endgame system
1283 multi_endgame_init();
1285 // initialize the common notification messaging
1286 multi_common_notify_init();
1288 // initialize the common text area
1289 multi_common_set_text("");
1291 // load and use the common interface palette
1292 multi_common_load_palette();
1293 multi_common_set_palette();
1295 // load the help overlay
1296 help_overlay_load(MULTI_JOIN_OVERLAY);
1297 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1299 // do TCP and VMT specific initialization
1300 if(Multi_options_g.protocol == NET_TCP){
1301 // if this is a TCP (non tracker) game, we'll load up our default address list right now
1302 multi_join_load_tcp_addrs();
1305 // initialize any and all timestamps
1306 Multi_join_glr_stamp = -1;
1307 Multi_join_ping_stamp = -1;
1308 Multi_join_sent_stamp = -1;
1310 // reset frame count
1311 Multi_join_frame_count = 0;
1313 // haven't tried to verify on the tracker yet.
1314 Multi_join_mt_tried_verify = 0;
1316 // clear our all game lists to save hassles
1317 multi_join_clear_game_list();
1319 // create the interface buttons
1320 for(idx=0; idx<MULTI_JOIN_NUM_BUTTONS; idx++){
1321 // create the object
1322 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);
1324 // set the sound to play when highlighted
1325 Multi_join_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
1327 // set the ani for the button
1328 Multi_join_buttons[gr_screen.res][idx].button.set_bmaps(Multi_join_buttons[gr_screen.res][idx].filename);
1331 Multi_join_buttons[gr_screen.res][idx].button.link_hotspot(Multi_join_buttons[gr_screen.res][idx].hotspot);
1335 for(idx=0; idx<MULTI_JOIN_NUM_TEXT; idx++){
1336 Multi_join_window.add_XSTR(&Multi_join_text[gr_screen.res][idx]);
1339 Multi_join_should_send = -1;
1341 // close any previously open chatbox
1344 // create the list item select button
1345 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);
1346 Multi_join_select_button.hide();
1349 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);
1351 // if starting a network game, then go to the create game screen
1352 if ( Cmdline_start_netgame ) {
1353 multi_join_create_game();
1354 } else if ( Cmdline_connect_addr != NULL ) {
1359 // joining a game. Send a join request to the given IP address, and wait for the return.
1360 memset( &Multi_autojoin_addr, 0, sizeof(net_addr) );
1361 Multi_autojoin_addr.type = NET_TCP;
1363 // create the address, looking out for port number at the end
1364 port_num = DEFAULT_GAME_PORT;
1365 p = strrchr(Cmdline_connect_addr, ':');
1369 port_num = (short)atoi(p);
1371 ip_addr = inet_addr(Cmdline_connect_addr);
1372 memcpy(Multi_autojoin_addr.addr, &ip_addr, 4);
1373 Multi_autojoin_addr.port = port_num;
1375 send_server_query(&Multi_autojoin_addr);
1376 Multi_autojoin_query_stamp = timestamp(MULTI_AUTOJOIN_QUERY_STAMP);
1377 Multi_did_autojoin = 0;
1381 void multi_join_clear_game_list()
1384 Multi_join_list_selected = -1;
1385 Multi_join_selected_item = NULL;
1386 MJ_LIST_START_SET(-1);
1387 Multi_join_list_start_item = NULL;
1389 // free up the active game list
1390 multi_free_active_games();
1392 // initialize the active game list
1393 Active_game_head = NULL;
1394 Active_game_count = 0;
1397 void multi_join_game_do_frame()
1399 // check the status of our reliable socket. If not valid, popup error and return to main menu
1400 // I put this code here to avoid nasty gameseq issues with states. Also, we will have nice
1401 // background for the popup
1402 if ( !psnet_rel_check() ) {
1403 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));
1404 gameseq_post_event(GS_EVENT_MAIN_MENU);
1408 // return here since we will be moving to the next stage anyway -- I don't want to see the backgrounds of
1409 // all the screens for < 1 second for every screen we automatically move to.
1410 if ( Cmdline_start_netgame ) {
1414 // when joining a network game, wait for the server query to come back, and then join the game
1415 if ( Cmdline_connect_addr != NULL ) {
1418 if ( !Multi_did_autojoin ) {
1419 rval = popup_till_condition(multi_join_autojoin_do, XSTR("&Cancel", 779), XSTR("Joining netgame", 1500) );
1421 // cancel was hit. Send the user back to the main hall
1422 gameseq_post_event(GS_EVENT_MAIN_MENU);
1423 Cmdline_connect_addr = NULL; // reset this value.
1426 // when we get here, we have the data -- join the game.
1427 multi_join_send_join_request(0);
1428 Multi_autojoin_join_stamp = timestamp(MULTI_AUTOJOIN_JOIN_STAMP);
1429 Multi_did_autojoin = 1;
1432 if ( timestamp_elapsed(Multi_autojoin_join_stamp) ) {
1433 multi_join_send_join_request(0);
1434 Multi_autojoin_join_stamp = timestamp(MULTI_AUTOJOIN_JOIN_STAMP);
1440 // reset the should send var
1441 Multi_join_should_send = -1;
1443 int k = Multi_join_window.process();
1445 // process any keypresses
1448 if(help_overlay_active(MULTI_JOIN_OVERLAY)){
1449 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1451 gameseq_post_event(GS_EVENT_MAIN_MENU);
1452 gamesnd_play_iface(SND_USER_SELECT);
1456 // page up the game list
1458 multi_join_list_page_up();
1459 Multi_join_slider.force_currentItem(Multi_join_list_start);
1463 multi_pinfo_popup(Net_player);
1466 // page down the game list
1468 multi_join_list_page_down();
1469 Multi_join_slider.force_currentItem(Multi_join_list_start);
1472 // send out a ping-all
1474 multi_join_ping_all();
1475 Multi_join_ping_stamp = timestamp(MULTI_JOIN_PING_TIME);
1478 // shortcut to start a game
1480 multi_join_create_game();
1483 // scroll the game list up
1485 multi_join_list_scroll_up();
1486 Multi_join_slider.force_currentItem(Multi_join_list_start);
1489 // scroll the game list down
1491 multi_join_list_scroll_down();
1492 Multi_join_slider.force_currentItem(Multi_join_list_start);
1496 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
1497 help_overlay_set_state(MULTI_JOIN_OVERLAY, 0);
1500 // do any network related stuff
1501 multi_join_do_netstuff();
1503 // process any button clicks
1504 multi_join_check_buttons();
1506 // process any list selection stuff
1507 multi_join_process_select();
1509 // draw the background, etc
1511 GR_MAYBE_CLEAR_RES(Multi_join_bitmap);
1512 if(Multi_join_bitmap != -1){
1513 gr_set_bitmap(Multi_join_bitmap);
1516 Multi_join_window.draw();
1518 // display the active games
1519 multi_join_display_games();
1521 // display any text in the info area
1522 multi_common_render_text();
1524 // display any pending notification messages
1525 multi_common_notify_do();
1527 // blit the CD icon and any PXO filter stuff
1528 multi_join_blit_top_stuff();
1530 // draw the help overlay
1531 help_overlay_maybe_blit(MULTI_JOIN_OVERLAY);
1536 // if we are supposed to be sending a join request
1537 if(Multi_join_should_send != -1){
1538 multi_join_send_join_request(Multi_join_should_send);
1540 Multi_join_should_send = -1;
1542 // increment the frame count
1543 Multi_join_frame_count++;
1546 void multi_join_game_close()
1548 // unload any bitmaps
1549 if(!bm_unload(Multi_join_bitmap)){
1550 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_join_bitmap_fname[gr_screen.res]));
1553 // unload the help overlay
1554 help_overlay_unload(MULTI_JOIN_OVERLAY);
1556 // free up the active game list
1557 multi_free_active_games();
1559 // destroy the UI_WINDOW
1560 Multi_join_window.destroy();
1563 void multi_join_check_buttons()
1566 for(idx=0;idx<MULTI_JOIN_NUM_BUTTONS;idx++){
1567 // we only really need to check for one button pressed at a time, so we can break after
1569 if(Multi_join_buttons[gr_screen.res][idx].button.pressed()){
1570 multi_join_button_pressed(idx);
1576 void multi_join_button_pressed(int n)
1580 // if we're player PXO, go back there
1581 gameseq_post_event(GS_EVENT_MAIN_MENU);
1582 gamesnd_play_iface(SND_USER_SELECT);
1585 if(Active_game_count <= 0){
1586 multi_common_add_notify(XSTR("No games found!",757));
1587 gamesnd_play_iface(SND_GENERAL_FAIL);
1588 } else if(Multi_join_list_selected == -1){
1589 multi_common_add_notify(XSTR("No game selected!",758));
1590 gamesnd_play_iface(SND_GENERAL_FAIL);
1591 } else if((Multi_join_sent_stamp != -1) && !timestamp_elapsed(Multi_join_sent_stamp)){
1592 multi_common_add_notify(XSTR("Still waiting on previous join request!",759));
1593 gamesnd_play_iface(SND_GENERAL_FAIL);
1595 // otherwise, if he's already played PXO games, warn him
1597 if(Player->flags & PLAYER_FLAGS_HAS_PLAYED_PXO){
1598 if(!multi_join_warn_pxo()){
1604 // send the join request here
1605 Assert(Multi_join_selected_item != NULL);
1607 // send a join request packet
1608 Multi_join_should_send = 0;
1610 gamesnd_play_iface(SND_COMMIT_PRESSED);
1616 if(!help_overlay_active(MULTI_JOIN_OVERLAY)){
1617 help_overlay_set_state(MULTI_JOIN_OVERLAY,1);
1619 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1623 // scroll the game list up
1625 multi_join_list_scroll_up();
1626 Multi_join_slider.force_currentItem(Multi_join_list_start);
1629 // scroll the game list down
1630 case MJ_SCROLL_DOWN:
1631 multi_join_list_scroll_down();
1632 Multi_join_slider.force_currentItem(Multi_join_list_start);
1635 // scroll the info text box up
1636 case MJ_SCROLL_INFO_UP:
1637 multi_common_scroll_text_up();
1640 // scroll the info text box down
1641 case MJ_SCROLL_INFO_DOWN:
1642 multi_common_scroll_text_down();
1645 // go to the options screen
1647 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
1650 // go to the start game screen
1652 multi_join_create_game();
1655 // refresh the game/server list
1657 gamesnd_play_iface(SND_USER_SELECT);
1658 broadcast_game_query();
1661 // join a game as an observer
1662 case MJ_JOIN_OBSERVER:
1663 if(Active_game_count <= 0){
1664 multi_common_add_notify(XSTR("No games found!",757));
1665 gamesnd_play_iface(SND_GENERAL_FAIL);
1666 } else if(Multi_join_list_selected == -1){
1667 multi_common_add_notify(XSTR("No game selected!",758));
1668 gamesnd_play_iface(SND_GENERAL_FAIL);
1669 } else if((Multi_join_sent_stamp != -1) && !timestamp_elapsed(Multi_join_sent_stamp)){
1670 multi_common_add_notify(XSTR("Still waiting on previous join request!",759));
1671 gamesnd_play_iface(SND_GENERAL_FAIL);
1673 // send the join request here
1674 Assert(Multi_join_selected_item != NULL);
1676 Multi_join_should_send = 1;
1678 gamesnd_play_iface(SND_COMMIT_PRESSED);
1683 multi_common_add_notify(XSTR("Not implemented yet!",760));
1684 gamesnd_play_iface(SND_GENERAL_FAIL);
1689 // display all relevant info for active games
1690 void multi_join_display_games()
1692 active_game *moveup = Multi_join_list_start_item;
1696 int y_start = Mj_list_y[gr_screen.res];
1701 // blit the game status (including text and type icon)
1702 multi_join_blit_game_status(moveup,y_start);
1704 // get the connection type
1705 con_type = (moveup->flags & AG_FLAG_CONNECTION_SPEED_MASK) >> AG_FLAG_CONNECTION_BIT;
1706 if((con_type > 4) || (con_type < 0)){
1710 // display the connection speed
1712 strcpy(str, Multi_join_speed_labels[con_type]);
1713 gr_set_color_fast(Multi_join_speed_colors[con_type]);
1714 gr_string(Mj_speed_coords[gr_screen.res][MJ_X_COORD], y_start, str);
1716 // we'll want to have different colors for highlighted items, etc.
1717 if(moveup == Multi_join_selected_item){
1718 gr_set_color_fast(&Color_text_selected);
1720 gr_set_color_fast(&Color_text_normal);
1723 // display the game name, adding appropriate status chars
1725 if(moveup->flags & AG_FLAG_STANDALONE){
1726 strcat(str,MJ_CHAR_STANDALONE);
1728 if(moveup->flags & AG_FLAG_CAMPAIGN){
1729 strcat(str,MJ_CHAR_CAMPAIGN);
1732 // tack on the actual server name
1734 strcat(str,moveup->name);
1735 if(strlen(moveup->mission_name) > 0){
1737 strcat(str,moveup->mission_name);
1740 // make sure the string fits in the display area and draw it
1741 gr_force_fit_string(str,200,Mj_game_name_coords[gr_screen.res][MJ_W_COORD]);
1742 gr_string(Mj_game_name_coords[gr_screen.res][MJ_X_COORD],y_start,str);
1744 // display the ping time
1745 if(moveup->ping.ping_avg > 0){
1746 if(moveup->ping.ping_avg > 1000){
1747 gr_set_color_fast(&Color_bright_red);
1748 strcpy(str,XSTR("> 1 sec",761));
1750 // set the appropriate ping time color indicator
1751 if(moveup->ping.ping_avg > MJ_PING_YELLOW){
1752 gr_set_color_fast(&Color_bright_red);
1753 } else if(moveup->ping.ping_avg > MJ_PING_YELLOW){
1754 gr_set_color_fast(&Color_bright_yellow);
1756 gr_set_color_fast(&Color_bright_green);
1759 sprintf(str,"%d",moveup->ping.ping_avg);
1760 strcat(str,XSTR(" ms",762)); // [[ Milliseconds ]]
1763 gr_string(Mj_ping_coords[gr_screen.res][MJ_X_COORD],y_start,str);
1766 // display the number of players (be sure to center it)
1767 if(moveup == Multi_join_selected_item){
1768 gr_set_color_fast(&Color_text_selected);
1770 gr_set_color_fast(&Color_text_normal);
1772 sprintf(str,"%d",moveup->num_players);
1773 gr_get_string_size(&w,&h,str);
1774 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);
1778 moveup = moveup->next;
1779 } while((moveup != Active_game_head) && (count < Mj_max_game_items[gr_screen.res]));
1781 // if there are no items on the list, display this info
1783 gr_set_color_fast(&Color_bright);
1784 gr_string(Mj_game_name_coords[gr_screen.res][MJ_X_COORD] - 30,y_start,XSTR("<No game servers found>",763));
1788 void multi_join_blit_game_status(active_game *game, int y)
1791 char status_text[25];
1793 // blit the proper icon
1795 switch( game->flags & AG_FLAG_TYPE_MASK ){
1798 if(Multi_common_icons[MICON_COOP] != -1){
1799 gr_set_bitmap(Multi_common_icons[MICON_COOP]);
1804 // team vs. team game
1806 if(Multi_common_icons[MICON_TVT] != -1){
1807 gr_set_bitmap(Multi_common_icons[MICON_TVT]);
1813 case AG_FLAG_DOGFIGHT:
1814 if(Multi_common_icons[MICON_DOGFIGHT] != -1){
1815 gr_set_bitmap(Multi_common_icons[MICON_DOGFIGHT]);
1820 // if we're supposed to draw a bitmap
1822 gr_bitmap(Mj_game_icon_coords[gr_screen.res][MJ_X_COORD],y-1);
1825 // blit the proper status text
1826 memset(status_text,0,25);
1828 switch( game->flags & AG_FLAG_STATE_MASK ){
1829 case AG_FLAG_FORMING:
1830 gr_set_color_fast(&Color_bright_green);
1831 strcpy(status_text,XSTR("Forming",764));
1833 case AG_FLAG_BRIEFING:
1834 gr_set_color_fast(&Color_bright_red);
1835 strcpy(status_text,XSTR("Briefing",765));
1837 case AG_FLAG_DEBRIEF:
1838 gr_set_color_fast(&Color_bright_red);
1839 strcpy(status_text,XSTR("Debrief",766));
1842 gr_set_color_fast(&Color_bright_red);
1843 strcpy(status_text,XSTR("Paused",767));
1845 case AG_FLAG_IN_MISSION:
1846 gr_set_color_fast(&Color_bright_red);
1847 strcpy(status_text,XSTR("Playing",768));
1850 gr_set_color_fast(&Color_bright);
1851 strcpy(status_text,XSTR("Unknown",769));
1854 gr_get_string_size(&str_w,NULL,status_text);
1855 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);
1858 // load in a list of active games from our tcp.cfg file
1859 void multi_join_load_tcp_addrs()
1861 char line[MAX_IP_STRING];
1866 // attempt to open the ip list file
1867 file = cfopen(IP_CONFIG_FNAME,"rt",CFILE_NORMAL,CF_TYPE_DATA);
1869 nprintf(("Network","Error loading tcp.cfg file!\n"));
1873 // free up any existing server list
1874 multi_free_server_list();
1876 // read in all the strings in the file
1877 while(!cfeof(file)){
1879 cfgets(line,MAX_IP_STRING,file);
1881 // strip off any newline character
1882 if(line[strlen(line) - 1] == '\n'){
1883 line[strlen(line) - 1] = '\0';
1886 // empty lines don't get processed
1887 if( (line[0] == '\0') || (line[0] == '\n') ){
1891 if ( !psnet_is_valid_ip_string(line) ) {
1892 nprintf(("Network","Invalid ip string (%s)\n",line));
1894 // copy the server ip address
1895 memset(&addr,0,sizeof(net_addr));
1896 addr.type = NET_TCP;
1897 psnet_string_to_addr(&addr,line);
1898 if ( addr.port == 0 ){
1899 addr.port = DEFAULT_GAME_PORT;
1902 // create a new server item on the list
1903 item = multi_new_server_item();
1905 memcpy(&item->server_addr,&addr,sizeof(net_addr));
1913 // do stuff like pinging servers, sending out requests, etc
1914 void multi_join_do_netstuff()
1916 // handle game query stuff
1917 if(Multi_join_glr_stamp == -1){
1918 broadcast_game_query();
1920 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
1921 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME_LOCAL);
1923 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME);
1926 // otherwise send out game query and restamp
1927 else if(timestamp_elapsed(Multi_join_glr_stamp)){
1928 broadcast_game_query();
1930 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
1931 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME_LOCAL);
1933 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME);
1937 // check to see if we've been accepted. If so, put up message saying so
1938 if ( Net_player->flags & (NETINFO_FLAG_ACCEPT_INGAME|NETINFO_FLAG_ACCEPT_CLIENT|NETINFO_FLAG_ACCEPT_HOST|NETINFO_FLAG_ACCEPT_OBSERVER) ) {
1939 multi_common_add_notify(XSTR("Accepted. Waiting for player data.",770));
1942 // check to see if any join packets we have sent have timed out
1943 if((Multi_join_sent_stamp != -1) && (timestamp_elapsed(Multi_join_sent_stamp))){
1944 Multi_join_sent_stamp = -1;
1945 multi_common_add_notify(XSTR("Join request timed out!",771));
1948 // check to see if we should be pinging everyone
1949 if((Multi_join_ping_stamp == -1) || (timestamp_elapsed(Multi_join_ping_stamp))){
1950 multi_join_ping_all();
1951 Multi_join_ping_stamp = timestamp(MULTI_JOIN_PING_TIME);
1955 multi_join_cull_timeouts();
1958 // evaluate a returned pong.
1959 void multi_join_eval_pong(net_addr *addr, fix pong_time)
1962 active_game *moveup = Active_game_head;
1967 if(psnet_same(&moveup->server_addr,addr)){
1969 multi_ping_eval_pong(&moveup->ping);
1973 moveup = moveup->next;
1975 } while(moveup != Active_game_head);
1978 // update the game's ping
1980 if(found && (moveup->ping_end > moveup->ping_start)){
1981 moveup->ping_time = f2fl(moveup->ping_end - moveup->ping_start);
1982 moveup->ping_start = -1;
1983 moveup->ping_end = -1;
1988 // ping all the server on the list
1989 void multi_join_ping_all()
1991 active_game *moveup = Active_game_head;
1996 moveup->ping_start = timer_get_fixed_seconds();
1997 moveup->ping_end = -1;
1998 send_ping(&moveup->server_addr);
2000 multi_ping_send(&moveup->server_addr,&moveup->ping);
2002 moveup = moveup->next;
2003 } while(moveup != Active_game_head);
2007 void multi_join_process_select()
2009 // if we don't have anything selected and there are items on the list - select the first one
2010 if((Multi_join_list_selected == -1) && (Active_game_count > 0)){
2011 Multi_join_list_selected = 0;
2012 Multi_join_selected_item = multi_join_get_game(0);
2013 MJ_LIST_START_SET(0);
2014 Multi_join_list_start_item = Multi_join_selected_item;
2016 // send a mission description request to this guy
2017 send_netgame_descript_packet(&Multi_join_selected_item->server_addr,0);
2018 multi_common_set_text("");
2020 // I sure hope this doesn't happen
2021 Assert(Multi_join_selected_item != NULL);
2024 // otherwise see if he's clicked on an item
2025 else if(Multi_join_select_button.pressed() && (Active_game_count > 0)){
2027 Multi_join_select_button.get_mouse_pos(NULL,&y);
2029 if(item + Multi_join_list_start < Active_game_count){
2030 gamesnd_play_iface(SND_IFACE_MOUSE_CLICK);
2032 Multi_join_list_selected = item + Multi_join_list_start;
2033 Multi_join_selected_item = multi_join_get_game(Multi_join_list_selected);
2035 // I sure hope this doesn't happen
2036 Assert(Multi_join_selected_item != NULL);
2038 // send a mission description request to this guy
2039 send_netgame_descript_packet(&Multi_join_selected_item->server_addr,0);
2040 multi_common_set_text("");
2044 // if he's double clicked, then select it and accept
2045 if(Multi_join_select_button.double_clicked()){
2047 Multi_join_select_button.get_mouse_pos(NULL,&y);
2049 if(item == Multi_join_list_selected){
2050 multi_join_button_pressed(MJ_ACCEPT);
2055 // return game n (0 based index)
2056 active_game *multi_join_get_game(int n)
2058 active_game *moveup = Active_game_head;
2065 moveup = moveup->next;
2066 while((moveup != Active_game_head) && (count != n)){
2067 moveup = moveup->next;
2070 if(moveup == Active_game_head){
2071 nprintf(("Network","Warning, couldn't find game item %d!\n",n));
2081 // scroll through the game list
2082 void multi_join_list_scroll_up()
2084 // if we're not at the beginning of the list, scroll up
2085 if(Multi_join_list_start_item != Active_game_head){
2086 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2088 MJ_LIST_START_DEC();
2090 gamesnd_play_iface(SND_SCROLL);
2092 gamesnd_play_iface(SND_GENERAL_FAIL);
2096 // scroll through the game list
2097 void multi_join_list_scroll_down()
2099 if((Active_game_count - Multi_join_list_start) > Mj_max_game_items[gr_screen.res]){
2100 Multi_join_list_start_item = Multi_join_list_start_item->next;
2102 MJ_LIST_START_INC();
2104 gamesnd_play_iface(SND_SCROLL);
2106 gamesnd_play_iface(SND_GENERAL_FAIL);
2110 void multi_join_list_page_up()
2112 // in this case, just set us to the beginning of the list
2113 if((Multi_join_list_start - Mj_max_game_items[gr_screen.res]) < 0){
2114 Multi_join_list_start_item = Active_game_head;
2116 MJ_LIST_START_SET(0);
2118 gamesnd_play_iface(SND_SCROLL);
2120 // otherwise page the whole thing up
2122 for(idx=0; idx<Mj_max_game_items[gr_screen.res]; idx++){
2123 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2125 MJ_LIST_START_DEC();
2127 gamesnd_play_iface(SND_SCROLL);
2131 void multi_join_list_page_down()
2135 // page the whole thing down
2136 while((count < Mj_max_game_items[gr_screen.res]) && ((Active_game_count - Multi_join_list_start) > Mj_max_game_items[gr_screen.res])){
2137 Multi_join_list_start_item = Multi_join_list_start_item->next;
2138 MJ_LIST_START_INC();
2143 gamesnd_play_iface(SND_SCROLL);
2146 void multi_join_cull_timeouts()
2148 active_game *backup;
2150 active_game *moveup = Active_game_head;
2152 // traverse through the entire list if any items exist
2156 if((moveup->heard_from_timer != -1) && (timestamp_elapsed(moveup->heard_from_timer))){
2157 Active_game_count--;
2159 // if this is the head of the list
2160 if(moveup == Active_game_head){
2161 // if this is the _only_ item on the list
2162 if(moveup->next == Active_game_head){
2163 // handle any gui details related to deleting this item
2164 multi_join_handle_item_cull(Active_game_head, count);
2166 free(Active_game_head);
2167 Active_game_head = NULL;
2170 // if there are other items on the list
2172 // handle any gui details related to deleting this item
2173 multi_join_handle_item_cull(moveup, count);
2175 Active_game_head = moveup->next;
2176 Active_game_head->prev = moveup->prev;
2177 Active_game_head->prev->next = Active_game_head;
2179 moveup = Active_game_head;
2182 // if its somewhere else on the list
2184 // handle any gui details related to deleting this item
2185 multi_join_handle_item_cull(moveup, count);
2187 // if its the last item on the list
2188 moveup->next->prev = moveup->prev;
2189 moveup->prev->next = moveup->next;
2191 // if it was the last element on the list, return
2192 if(moveup->next == Active_game_head){
2196 backup = moveup->next;
2202 moveup = moveup->next;
2205 } while(moveup != Active_game_head);
2209 // deep magic begins here.
2210 void multi_join_handle_item_cull(active_game *item, int item_index)
2212 // if this is the only item on the list, unset everything
2213 if(item->next == item){
2214 Multi_join_list_selected = -1;
2215 Multi_join_selected_item = NULL;
2217 Multi_join_slider.set_numberItems(0);
2218 MJ_LIST_START_SET(-1);
2219 Multi_join_list_start_item = NULL;
2225 // see if we should be adjusting our currently selected item
2226 if(item_index <= Multi_join_list_selected){
2227 // the selected item is the head of the list
2228 if(Multi_join_selected_item == Active_game_head){
2229 // move the pointer up since this item is about to be destroyed
2230 Multi_join_selected_item = Multi_join_selected_item->next;
2232 // if this is the item being deleted, select the previous one
2233 if(item == Multi_join_selected_item){
2235 Multi_join_selected_item = Multi_join_selected_item->prev;
2237 // decrement the selected index by 1
2238 Multi_join_list_selected--;
2240 // now we know its a previous item, so our pointer stays the same but our index goes down by one, since there will be
2241 // 1 less item on the list
2243 // decrement the selected index by 1
2244 Multi_join_list_selected--;
2249 // see if we should be adjusting out current start position
2250 if(item_index <= Multi_join_list_start){
2251 // the start position is the head of the list
2252 if(Multi_join_list_start_item == Active_game_head){
2253 // move the pointer up since this item is about to be destroyed
2254 Multi_join_list_start_item = Multi_join_list_start_item->next;
2256 // if this is the item being deleted, select the previous one
2257 if(item == Multi_join_list_start_item){
2258 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2260 // decrement the starting index by 1
2261 MJ_LIST_START_DEC();
2263 // but decrement the starting index by 1
2264 MJ_LIST_START_DEC();
2269 // maybe go back up a bit so that we always have a full page of items
2270 if(Active_game_count > Mj_max_game_items[gr_screen.res]){
2271 while((Active_game_count - Multi_join_list_start) < Mj_max_game_items[gr_screen.res]){
2272 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2273 MJ_LIST_START_DEC();
2277 // set slider location
2278 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);
2279 Multi_join_slider.force_currentItem(Multi_join_list_start);
2282 void multi_join_send_join_request(int as_observer)
2284 // don't do anything if we have no items selected
2285 if(Multi_join_selected_item == NULL){
2289 // 5/26/98 -- for team v team games, don't allow ingame joining :-(
2290 if ( (Multi_join_selected_item->flags & AG_FLAG_TEAMS) && (Multi_join_selected_item->flags & (AG_FLAG_PAUSE|AG_FLAG_IN_MISSION)) ) {
2291 popup(0, 1, POPUP_OK, XSTR("Joining ingame is currently not allowed for team vs. team games",772));
2295 memset(&Multi_join_request,0,sizeof(join_request));
2297 // if the netgame is in password mode, put up a request for the password
2298 if(Multi_join_selected_item->flags & AG_FLAG_PASSWD){
2299 if(!multi_passwd_popup(Multi_join_request.passwd)){
2303 nprintf(("Password : %s\n",Multi_join_request.passwd));
2306 // fill out the join request struct
2307 strcpy(Multi_join_request.callsign,Player->callsign);
2308 if(strlen(Player->image_filename) > 0){
2309 strcpy(Multi_join_request.image_filename, Player->image_filename);
2311 if(strlen(Player->squad_filename) > 0){
2312 strcpy(Multi_join_request.squad_filename, Player->squad_filename);
2315 // tracker id (if any)
2316 Multi_join_request.tracker_id = Multi_tracker_id;
2318 // player's rank (at least, what he wants you to _believe_)
2319 Multi_join_request.player_rank = (ubyte)Player->stats.rank;
2322 Multi_join_request.flags = 0;
2324 Multi_join_request.flags |= JOIN_FLAG_AS_OBSERVER;
2327 // if the player has hacked data
2328 if(game_hacked_data()){
2329 Multi_join_request.flags |= JOIN_FLAG_HAXOR;
2333 strncpy(Multi_join_request.pxo_squad_name, Multi_tracker_squad_name, LOGIN_LEN);
2335 // version of this server
2336 Multi_join_request.version = MULTI_FS_SERVER_VERSION;
2338 // server compatible version
2339 Multi_join_request.comp_version = MULTI_FS_SERVER_COMPATIBLE_VERSION;
2341 // his local player options
2342 memcpy(&Multi_join_request.player_options,&Player->m_local_options,sizeof(multi_local_options));
2344 // set the server address for the netgame
2345 memcpy(&Netgame.server_addr,&Multi_join_selected_item->server_addr,sizeof(net_addr));
2347 // send a join request to the guy
2348 send_join_packet(&Multi_join_selected_item->server_addr,&Multi_join_request);
2351 Multi_join_sent_stamp = timestamp(MULTI_JOIN_SENT_WAIT);
2354 multi_common_add_notify(XSTR("Sending join request...",773));
2357 void multi_join_create_game()
2359 // maybe warn the player about possible crappy server conditions
2360 if(!multi_join_maybe_warn()){
2364 // make sure to flag ourself as being the master
2365 Net_player->flags |= (NETINFO_FLAG_AM_MASTER | NETINFO_FLAG_GAME_HOST);
2366 Net_player->state = NETPLAYER_STATE_HOST_SETUP;
2368 // if we're in PXO mode, mark it down in our player struct
2369 if(MULTI_IS_TRACKER_GAME){
2370 Player->flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
2371 Player->save_flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
2373 // otherwise, if he's already played PXO games, warn him
2376 if(Player->flags & PLAYER_FLAGS_HAS_PLAYED_PXO){
2377 if(!multi_join_warn_pxo()){
2384 gameseq_post_event(GS_EVENT_MULTI_START_GAME);
2385 gamesnd_play_iface(SND_USER_SELECT);
2388 void multi_join_reset_join_stamp()
2390 // unset the timestamp here so the user can immediately send another join request
2391 Multi_join_sent_stamp = -1;
2392 multi_common_add_notify("");
2395 void multi_join_blit_top_stuff()
2397 // blit the cd icon if he has one
2398 if(Multi_has_cd && (Multi_common_icons[MICON_CD] != -1)){
2401 bm_get_info(Multi_common_icons[MICON_CD], &cd_w, NULL, NULL, NULL, NULL);
2403 gr_set_bitmap(Multi_common_icons[MICON_CD]);
2404 gr_bitmap((gr_screen.max_w / 2) - (cd_w / 2), Mj_cd_coords[gr_screen.res]);
2408 #define CW_CODE_CANCEL 0 // cancel the action
2409 #define CW_CODE_OK 1 // continue anyway
2410 #define CW_CODE_INFO 2 // gimme some more information
2412 #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)
2413 #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)
2415 #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)
2416 #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)
2418 int multi_join_warn_update_low(int code)
2422 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),LOW_WARN_TEXT);
2425 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),LOW_INFO_TEXT);
2428 return CW_CODE_CANCEL;
2431 int multi_join_warn_update_medium(int code)
2435 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),MED_WARN_TEXT);
2438 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),MED_INFO_TEXT);
2441 return CW_CODE_CANCEL;
2444 int multi_join_maybe_warn()
2448 // if the player is set for low updates
2449 if(Player->m_local_options.obj_update_level == OBJ_UPDATE_LOW){
2452 code = multi_join_warn_update_low(code);
2453 } while((code != CW_CODE_CANCEL) && (code != CW_CODE_OK));
2458 // if the player is set for medium updates
2459 else if(Player->m_local_options.obj_update_level == OBJ_UPDATE_MEDIUM){
2462 code = multi_join_warn_update_medium(code);
2463 } while((code != CW_CODE_CANCEL) && (code != CW_CODE_OK));
2471 int multi_join_warn_pxo()
2473 // 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;
2477 void multi_join_blit_protocol()
2479 gr_set_color_fast(&Color_bright);
2481 switch(Socket_type){
2484 gr_string(5, 2, "TCP");
2488 gr_string(5, 2, "IPX");
2494 // -------------------------------------------------------------------------------------------------
2496 // MULTIPLAYER START GAME screen
2501 #define MULTI_SG_PALETTE "InterfacePalette"
2503 static char *Multi_sg_bitmap_fname[GR_NUM_RESOLUTIONS] = {
2504 "MultiStartGame", // GR_640
2505 "2_MultiStartGame" // GR_1024
2508 static char *Multi_sg_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
2509 "MultiStartGame-M", // GR_640
2510 "2_MultiStartGame-M" // GR_1024
2515 int Multi_sg_rank_max_display[GR_NUM_RESOLUTIONS] = {
2520 // constants for coordinate look ups
2521 #define MSG_X_COORD 0
2522 #define MSG_Y_COORD 1
2523 #define MSG_W_COORD 2
2524 #define MSG_H_COORD 3
2528 // input password field
2529 int Msg_passwd_coords[GR_NUM_RESOLUTIONS][4] = {
2538 // input game title field
2539 int Msg_title_coords[GR_NUM_RESOLUTIONS][4] = {
2548 // rank selected field
2549 int Msg_rank_sel_coords[GR_NUM_RESOLUTIONS][4] = {
2559 int Msg_rank_list_coords[GR_NUM_RESOLUTIONS][4] = {
2570 #define MULTI_SG_NUM_BUTTONS 10
2571 #define MSG_OPEN_GAME 0
2572 //#define MSG_CLOSED_GAME 1
2573 //#define MSG_RESTRICTED_GAME 2
2574 #define MSG_PASSWD_GAME 1
2575 #define MSG_RANK_SET_GAME 2
2576 #define MSG_RANK_SCROLL_UP 3
2577 #define MSG_RANK_SCROLL_DOWN 4
2578 #define MSG_RANK_ABOVE 5
2579 #define MSG_RANK_BELOW 6
2581 #define MSG_OPTIONS 8
2582 #define MSG_ACCEPT 9
2584 UI_WINDOW Multi_sg_window; // the window object for the join screen
2585 UI_BUTTON Multi_sg_rank_button; // for selecting the rank marker
2586 UI_INPUTBOX Multi_sg_game_name; // for Netgame.name
2587 UI_INPUTBOX Multi_sg_game_passwd; // for Netgame.passwd
2588 int Multi_sg_bitmap; // the background bitmap
2590 ui_button_info Multi_sg_buttons[GR_NUM_RESOLUTIONS][MULTI_SG_NUM_BUTTONS] = {
2592 ui_button_info("MSG_00", 1, 184, 34, 191, 2), // open
2593 // ui_button_info("MSG_01", 1, 159, 34, 166, 1), // closed
2594 // ui_button_info("MSG_02", 1, 184, 34, 191, 2), // restricted
2595 ui_button_info("MSG_03", 1, 209, 34, 218, 3), // password
2596 ui_button_info("MSG_04", 1, 257, 34, 266, 4), // rank set
2597 ui_button_info("MSG_05", 1, 282, -1, -1, 5), // rank scroll up
2598 ui_button_info("MSG_06", 1, 307, -1, -1, 6), // rank scroll down
2599 ui_button_info("MSG_07", 177, 282, 210, 290, 7), // rank above
2600 ui_button_info("MSG_08", 177, 307, 210, 315, 8), // rank below
2601 ui_button_info("MSG_09", 536, 429, 500, 440, 9), // help
2602 ui_button_info("MSG_10", 536, 454, 479, 464, 10), // options
2603 ui_button_info("MSG_11", 576, 432, 571, 415, 11), // accept
2606 ui_button_info("2_MSG_00", 2, 295, 51, 307, 2), // open
2607 // ui_button_info("2_MSG_01", 2, 254, 51, 267, 1), // closed
2608 // ui_button_info("2_MSG_02", 2, 295, 51, 307, 2), // restricted
2609 ui_button_info("2_MSG_03", 2, 335, 51, 350, 3), // password
2610 ui_button_info("2_MSG_04", 2, 412, 51, 426, 4), // rank set
2611 ui_button_info("2_MSG_05", 2, 452, -1, -1, 5), // rank scroll up
2612 ui_button_info("2_MSG_06", 2, 492, -1, -1, 6), // rank scroll down
2613 ui_button_info("2_MSG_07", 284, 452, 335, 465, 7), // rank above
2614 ui_button_info("2_MSG_08", 284, 492, 335, 505, 8), // rank below
2615 ui_button_info("2_MSG_09", 858, 687, 817, 706, 9), // help
2616 ui_button_info("2_MSG_10", 858, 728, 797, 743, 10), // options
2617 ui_button_info("2_MSG_11", 921, 692, 921, 664, 11), // accept
2621 #define MULTI_SG_NUM_TEXT 11
2622 UI_XSTR Multi_sg_text[GR_NUM_RESOLUTIONS][MULTI_SG_NUM_TEXT] = {
2624 {"Open", 1322, 34, 191, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_OPEN_GAME].button},
2625 // {"Closed", 1323, 34, 166, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_CLOSED_GAME].button},
2626 // {"Restricted", 1324, 34, 191, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RESTRICTED_GAME].button},
2627 {"Password Protected", 1325, 34, 218, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_PASSWD_GAME].button},
2628 {"Allow Rank", 1326, 34, 266, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_SET_GAME].button},
2629 {"Above", 1327, 210, 290, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_ABOVE].button},
2630 {"Below", 1328, 210, 315, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_BELOW].button},
2631 {"Help", 928, 500, 440, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_HELP].button},
2632 {"Options", 1036, 479, 464, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_OPTIONS].button},
2633 {"Accept", 1035, 571, 415, UI_XSTR_COLOR_PINK, -1, &Multi_sg_buttons[0][MSG_ACCEPT].button},
2634 {"Start Game", 1329, 26, 10, UI_XSTR_COLOR_GREEN, -1, NULL},
2635 {"Title", 1330, 26, 31, UI_XSTR_COLOR_GREEN, -1, NULL},
2636 {"Game Type", 1331, 12, 165, UI_XSTR_COLOR_GREEN, -1, NULL},
2639 {"Open", 1322, 51, 307, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_OPEN_GAME].button},
2640 // {"Closed", 1323, 51, 267, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_CLOSED_GAME].button},
2641 // {"Restricted", 1324, 51, 307, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RESTRICTED_GAME].button},
2642 {"Password Protected", 1325, 51, 350, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_PASSWD_GAME].button},
2643 {"Allow Rank", 1326, 51, 426, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_SET_GAME].button},
2644 {"Above", 1327, 335, 465, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_ABOVE].button},
2645 {"Below", 1328, 335, 505, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_BELOW].button},
2646 {"Help", 928, 817, 706, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_HELP].button},
2647 {"Options", 1036, 797, 743, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_OPTIONS].button},
2648 {"Accept", 1035, 921, 664, UI_XSTR_COLOR_PINK, -1, &Multi_sg_buttons[1][MSG_ACCEPT].button},
2649 {"Start Game", 1329, 42, 22, UI_XSTR_COLOR_GREEN, -1, NULL},
2650 {"Title", 1330, 42, 50, UI_XSTR_COLOR_GREEN, -1, NULL},
2651 {"Game Type", 1331, 20, 264, UI_XSTR_COLOR_GREEN, -1, NULL},
2655 // starting index for displaying ranks
2656 int Multi_sg_rank_start;
2657 int Multi_sg_rank_select;
2659 // netgame pointer to indirect through
2660 netgame_info *Multi_sg_netgame;
2662 // hold temporary values in this structure when on a standalone server
2663 netgame_info Multi_sg_netgame_temp;
2665 // forward declarations
2666 void multi_sg_check_buttons();
2667 void multi_sg_button_pressed(int n);
2668 void multi_sg_init_gamenet();
2669 void multi_sg_draw_radio_buttons();
2670 void multi_sg_rank_scroll_up();
2671 void multi_sg_rank_scroll_down();
2672 void multi_sg_rank_display_stuff();
2673 void multi_sg_rank_process_select();
2674 void multi_sg_rank_build_name(char *in,char *out);
2675 void multi_sg_check_passwd();
2676 void multi_sg_check_name();
2677 void multi_sg_release_passwd();
2678 int multi_sg_rank_select_valid(int rank);
2679 void multi_sg_select_rank_default();
2681 // function which takes a rank name and returns the index. Useful for commandline options
2682 // for above and below rank. We return the index of the rank in the Ranks[] array. If
2683 // the rank isn't found, we return -1
2684 int multi_start_game_rank_from_name( char *rank ) {
2687 for ( i = 0; i <= MAX_FREESPACE2_RANK; i++ ) {
2688 if ( !stricmp(Ranks[i].name, rank) ) {
2696 void multi_start_game_init()
2700 // initialize the gamenet
2701 multi_sg_init_gamenet();
2703 // create the interface window
2704 Multi_sg_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
2705 Multi_sg_window.set_mask_bmap(Multi_sg_bitmap_mask_fname[gr_screen.res]);
2707 // load the background bitmap
2708 Multi_sg_bitmap = bm_load(Multi_sg_bitmap_fname[gr_screen.res]);
2709 if(Multi_sg_bitmap < 0){
2710 // we failed to load the bitmap - this is very bad
2714 // initialize the common notification messaging
2715 multi_common_notify_init();
2717 // initialize the common text area
2718 multi_common_set_text("");
2720 // use the common interface palette
2721 multi_common_set_palette();
2723 // create the interface buttons
2724 for(idx=0; idx<MULTI_SG_NUM_BUTTONS; idx++){
2725 // create the object
2726 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);
2728 // set the sound to play when highlighted
2729 Multi_sg_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
2731 // set the ani for the button
2732 Multi_sg_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sg_buttons[gr_screen.res][idx].filename);
2735 Multi_sg_buttons[gr_screen.res][idx].button.link_hotspot(Multi_sg_buttons[gr_screen.res][idx].hotspot);
2739 for(idx=0; idx<MULTI_SG_NUM_TEXT; idx++){
2740 Multi_sg_window.add_XSTR(&Multi_sg_text[gr_screen.res][idx]);
2743 // load the help overlay
2744 help_overlay_load(MULTI_START_OVERLAY);
2745 help_overlay_set_state(MULTI_START_OVERLAY,0);
2747 // intiialize the rank selection items
2748 multi_sg_select_rank_default();
2749 Multi_sg_rank_start = Multi_sg_rank_select;
2751 // create the rank select button
2752 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);
2753 Multi_sg_rank_button.hide();
2755 // create the netgame name input box
2756 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);
2758 // create the netgame password input box, and disable it by default
2759 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);
2760 Multi_sg_game_passwd.hide();
2761 Multi_sg_game_passwd.disable();
2763 // set the netgame text to this gadget and make it have focus
2764 Multi_sg_game_name.set_text(Multi_sg_netgame->name);
2765 Multi_sg_game_name.set_focus();
2767 // if starting a netgame, set the name of the game and any other options that are appropriate
2768 if ( Cmdline_start_netgame ) {
2769 if ( Cmdline_game_name != NULL ) {
2770 strcpy( Multi_sg_netgame->name, Cmdline_game_name );
2771 Multi_sg_game_name.set_text(Multi_sg_netgame->name);
2774 // deal with the different game types -- only one should even be active, so we will just go down
2775 // the line. Last one wins.
2776 if ( Cmdline_closed_game ) {
2777 Multi_sg_netgame->mode = NG_MODE_CLOSED;
2778 } else if ( Cmdline_restricted_game ) {
2779 Multi_sg_netgame->mode = NG_MODE_RESTRICTED;
2780 } else if ( Cmdline_game_password != NULL ) {
2781 Multi_sg_netgame->mode = NG_MODE_PASSWORD;
2782 strcpy(Multi_sg_netgame->passwd, Cmdline_game_password);
2783 Multi_sg_game_passwd.set_text(Multi_sg_netgame->passwd);
2786 // deal with rank above and rank below
2787 if ( (Cmdline_rank_above != NULL) || (Cmdline_rank_below != NULL) ) {
2791 if ( Cmdline_rank_above != NULL ) {
2792 rank_str = Cmdline_rank_above;
2794 rank_str = Cmdline_rank_below;
2797 // try and get the rank index from the name -- if found, then set the rank base
2798 // and the game type. apparently we only support either above or below, not both
2799 // together, so I make a random choice
2800 rank = multi_start_game_rank_from_name( rank_str );
2802 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
2804 // now an arbitrary decision
2805 if ( Cmdline_rank_above != NULL ) {
2806 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
2808 Multi_sg_netgame->mode = NG_MODE_RANK_BELOW;
2813 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
2817 void multi_start_game_do()
2819 // return here since we will be moving to the next stage anyway -- I don't want to see the backgrounds of
2820 // all the screens for < 1 second for every screen we automatically move to.
2821 if ( Cmdline_start_netgame ) {
2825 int k = Multi_sg_window.process();
2827 // process any keypresses
2830 if(help_overlay_active(MULTI_START_OVERLAY)){
2831 help_overlay_set_state(MULTI_START_OVERLAY,0);
2833 gamesnd_play_iface(SND_USER_SELECT);
2834 multi_quit_game(PROMPT_NONE);
2839 case KEY_LCTRL + KEY_ENTER :
2840 case KEY_RCTRL + KEY_ENTER :
2841 gamesnd_play_iface(SND_COMMIT_PRESSED);
2842 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
2846 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
2847 help_overlay_set_state(MULTI_START_OVERLAY, 0);
2850 // check to see if the user has selected a different rank
2851 multi_sg_rank_process_select();
2853 // check any button presses
2854 multi_sg_check_buttons();
2856 // check to see if any of the input boxes have changed, and update the appropriate Netgame fields if necessary
2857 multi_sg_check_passwd();
2858 multi_sg_check_name();
2860 // draw the background, etc
2862 GR_MAYBE_CLEAR_RES(Multi_sg_bitmap);
2863 if(Multi_sg_bitmap != -1){
2864 gr_set_bitmap(Multi_sg_bitmap);
2867 Multi_sg_window.draw();
2869 // display rank stuff
2870 multi_sg_rank_display_stuff();
2872 // display any pending notification messages
2873 multi_common_notify_do();
2875 // draw all radio button
2876 multi_sg_draw_radio_buttons();
2878 // draw the help overlay
2879 help_overlay_maybe_blit(MULTI_START_OVERLAY);
2885 void multi_start_game_close()
2887 // if i'm the host on a standalone server, send him my start game options (passwd, mode, etc)
2888 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
2889 multi_options_update_start_game(Multi_sg_netgame);
2892 // unload any bitmaps
2893 if(!bm_unload(Multi_sg_bitmap)){
2894 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_sg_bitmap_fname[gr_screen.res]));
2897 // unload the help overlay
2898 help_overlay_unload(MULTI_START_OVERLAY);
2900 // destroy the UI_WINDOW
2901 Multi_sg_window.destroy();
2904 void multi_sg_check_buttons()
2907 for(idx=0;idx<MULTI_SG_NUM_BUTTONS;idx++){
2908 // we only really need to check for one button pressed at a time, so we can break after
2910 if(Multi_sg_buttons[gr_screen.res][idx].button.pressed()){
2911 multi_sg_button_pressed(idx);
2917 void multi_sg_button_pressed(int n)
2920 // go to the options screen
2922 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
2927 if(!help_overlay_active(MULTI_START_OVERLAY)){
2928 help_overlay_set_state(MULTI_START_OVERLAY,1);
2930 help_overlay_set_state(MULTI_START_OVERLAY,0);
2934 // the open button was pressed
2936 // if the closed option is selected
2937 if(Multi_sg_netgame->mode != NG_MODE_OPEN){
2938 Multi_sg_netgame->mode = NG_MODE_OPEN;
2940 gamesnd_play_iface(SND_USER_SELECT);
2942 // release the password control if necessary
2943 multi_sg_release_passwd();
2945 // if its already selected
2947 gamesnd_play_iface(SND_GENERAL_FAIL);
2952 // the open button was pressed
2953 case MSG_CLOSED_GAME:
2954 // if the closed option is selected
2955 if(Multi_sg_netgame->mode != NG_MODE_CLOSED){
2956 Multi_sg_netgame->mode = NG_MODE_CLOSED;
2958 gamesnd_play_iface(SND_USER_SELECT);
2960 // release the password control if necessary
2961 multi_sg_release_passwd();
2963 // if its already selected
2965 gamesnd_play_iface(SND_GENERAL_FAIL);
2969 // toggle password protection
2970 case MSG_PASSWD_GAME:
2971 // if we selected it
2972 if(Multi_sg_netgame->mode != NG_MODE_PASSWORD){
2973 gamesnd_play_iface(SND_USER_SELECT);
2975 Multi_sg_game_passwd.enable();
2976 Multi_sg_game_passwd.unhide();
2977 Multi_sg_game_passwd.set_focus();
2979 Multi_sg_netgame->mode = NG_MODE_PASSWORD;
2981 // copy in the current network password
2982 Multi_sg_game_passwd.set_text(Multi_sg_netgame->passwd);
2984 gamesnd_play_iface(SND_GENERAL_FAIL);
2989 // toggle "restricted" on or off
2990 case MSG_RESTRICTED_GAME:
2991 if(Multi_sg_netgame->mode != NG_MODE_RESTRICTED){
2992 gamesnd_play_iface(SND_USER_SELECT);
2993 Multi_sg_netgame->mode = NG_MODE_RESTRICTED;
2995 // release the password control if necessary
2996 multi_sg_release_passwd();
2998 gamesnd_play_iface(SND_GENERAL_FAIL);
3002 // turn off all rank requirements
3003 case MSG_RANK_SET_GAME:
3004 // if either is set, then turn then both off
3005 if((Multi_sg_netgame->mode != NG_MODE_RANK_BELOW) && (Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE)){
3006 gamesnd_play_iface(SND_USER_SELECT);
3008 // set it to the default case if we're turning it off
3009 multi_sg_select_rank_default();
3010 Multi_sg_rank_start = Multi_sg_rank_select;
3012 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
3014 // release the password control if necessary
3015 multi_sg_release_passwd();
3017 gamesnd_play_iface(SND_GENERAL_FAIL);
3021 // rank above was pressed
3022 case MSG_RANK_ABOVE :
3023 if((Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE) || (Multi_sg_netgame->mode == NG_MODE_RANK_BELOW)){
3024 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
3026 // select the first item
3027 multi_sg_select_rank_default();
3028 Multi_sg_rank_start = Multi_sg_rank_select;
3031 gamesnd_play_iface(SND_USER_SELECT);
3033 gamesnd_play_iface(SND_GENERAL_FAIL);
3037 // rank below was pressed
3038 case MSG_RANK_BELOW :
3039 if((Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE) || (Multi_sg_netgame->mode == NG_MODE_RANK_BELOW)){
3040 Multi_sg_netgame->mode = NG_MODE_RANK_BELOW;
3042 // select the first item
3043 multi_sg_select_rank_default();
3044 Multi_sg_rank_start = Multi_sg_rank_select;
3047 gamesnd_play_iface(SND_USER_SELECT);
3049 gamesnd_play_iface(SND_GENERAL_FAIL);
3053 // scroll the rank list up
3054 case MSG_RANK_SCROLL_UP:
3055 multi_sg_rank_scroll_up();
3058 // scroll the rank list down
3059 case MSG_RANK_SCROLL_DOWN:
3060 multi_sg_rank_scroll_down();
3063 // move to the create game screen
3065 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
3066 gamesnd_play_iface(SND_COMMIT_PRESSED);
3070 gamesnd_play_iface(SND_GENERAL_FAIL);
3071 multi_common_add_notify(XSTR("Not implemented yet!",760));
3076 // NOTE : this is where all Netgame initialization should take place on the host
3077 void multi_sg_init_gamenet()
3079 char buf[128],out_name[128];
3081 net_player *server_save;
3083 // back this data up in case we are already connected to a standalone
3084 memcpy(&save,&Netgame.server_addr,sizeof(net_addr));
3085 server_save = Netgame.server;
3087 // remove campaign flags
3088 Game_mode &= ~(GM_CAMPAIGN_MODE);
3090 // clear out the Netgame structure and start filling in the values
3091 memset( &Netgame, 0, sizeof(Netgame) );
3092 memset( &Multi_sg_netgame_temp, 0, sizeof(netgame_info) );
3094 // if we're on the standalone, we're not the server, so we don't care about setting the netgame state
3095 if(Net_player->state != NETPLAYER_STATE_STD_HOST_SETUP){
3096 Netgame.game_state = NETGAME_STATE_HOST_SETUP;
3097 Multi_sg_netgame = &Netgame;
3100 ml_string(NOX("Starting netgame as Host/Server"));
3102 Multi_sg_netgame = &Multi_sg_netgame_temp;
3106 memcpy(&temp_addr.s_addr, &Netgame.server_addr, 4);
3107 char *server_addr = inet_ntoa(temp_addr);
3108 ml_printf(NOX("Starting netgame as Host on Standalone server : %s"), (server_addr == NULL) ? NOX("Unknown") : server_addr);
3111 Net_player->tracker_player_id = Multi_tracker_id;
3113 Multi_sg_netgame->security = (rand() % 32766) + 1; // get some random security number
3114 Multi_sg_netgame->mode = NG_MODE_OPEN;
3115 Multi_sg_netgame->rank_base = RANK_ENSIGN;
3116 if(Multi_sg_netgame->security < 16){
3117 Multi_sg_netgame->security += 16;
3120 // set the version_info field
3121 Multi_sg_netgame->version_info = NG_VERSION_ID;
3123 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
3124 Netgame.host = Net_player;
3127 // set the default netgame flags
3128 Multi_sg_netgame->flags = 0;
3130 // intialize endgame stuff
3131 multi_endgame_init();
3133 // load in my netgame options
3134 multi_options_netgame_load(&Netgame.options);
3136 // load my local netplayer options
3137 multi_options_local_load(&Net_player->p_info.options, Net_player);
3139 // setup the default game name, taking care of string length and player callsigns
3140 memset(out_name,0,128);
3142 pilot_format_callsign_personal(Player->callsign,out_name);
3143 sprintf(buf, XSTR("%s game",782), out_name); // [[ %s will be a pilot's name ]]
3144 if ( strlen(buf) > MAX_GAMENAME_LEN ){
3145 strcpy(buf, XSTR("Temporary name",783));
3147 strcpy(Multi_sg_netgame->name, buf);
3149 // set the default qos and duration
3150 multi_voice_maybe_update_vars(Netgame.options.voice_qos,Netgame.options.voice_record_time);
3152 // make sure to set the server correctly (me or the standalone)
3153 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3154 memcpy(&Netgame.server_addr, &Psnet_my_addr,sizeof(net_addr));
3155 Netgame.server = Net_player;
3156 Net_player->player_id = multi_get_new_id();
3158 // setup debug flags
3159 Netgame.debug_flags = 0;
3161 if(!Cmdline_server_firing){
3162 Netgame.debug_flags |= NETD_FLAG_CLIENT_FIRING;
3164 if(!Cmdline_client_dodamage){
3165 Netgame.debug_flags |= NETD_FLAG_CLIENT_NODAMAGE;
3169 memcpy(&Netgame.server_addr,&save,sizeof(net_addr));
3170 Netgame.server = server_save;
3173 // if I have a cd or not
3175 Net_player->flags |= NETINFO_FLAG_HAS_CD;
3178 // if I have hacked data
3179 if(game_hacked_data()){
3180 Net_player->flags |= NETINFO_FLAG_HAXOR;
3183 // assign my player struct and other data
3184 Net_player->flags |= (NETINFO_FLAG_CONNECTED | NETINFO_FLAG_DO_NETWORKING);
3185 Net_player->s_info.voice_token_timestamp = -1;
3187 // if we're supposed to flush our cache directory, do so now
3188 if(Net_player->p_info.options.flags & MLO_FLAG_FLUSH_CACHE){
3189 multi_flush_multidata_cache();
3192 ml_string(NOX("Flushing multi-data cache"));
3198 void multi_sg_draw_radio_buttons()
3200 // draw the appropriate radio button
3201 switch(Multi_sg_netgame->mode){
3203 Multi_sg_buttons[gr_screen.res][MSG_OPEN_GAME].button.draw_forced(2);
3206 case NG_MODE_CLOSED:
3207 Multi_sg_buttons[gr_screen.res][MSG_CLOSED_GAME].button.draw_forced(2);
3210 case NG_MODE_PASSWORD:
3211 Multi_sg_buttons[gr_screen.res][MSG_PASSWD_GAME].button.draw_forced(2);
3213 /* case NG_MODE_RESTRICTED:
3214 Multi_sg_buttons[gr_screen.res][MSG_RESTRICTED_GAME].button.draw_forced(2);
3217 case NG_MODE_RANK_ABOVE:
3218 Multi_sg_buttons[gr_screen.res][MSG_RANK_SET_GAME].button.draw_forced(2);
3219 Multi_sg_buttons[gr_screen.res][MSG_RANK_ABOVE].button.draw_forced(2);
3221 case NG_MODE_RANK_BELOW:
3222 Multi_sg_buttons[gr_screen.res][MSG_RANK_SET_GAME].button.draw_forced(2);
3223 Multi_sg_buttons[gr_screen.res][MSG_RANK_BELOW].button.draw_forced(2);
3228 void multi_sg_rank_scroll_up()
3230 // if he doesn't have either of the rank flags set, then ignore this
3231 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3235 if(Multi_sg_rank_start > 0){
3236 Multi_sg_rank_start--;
3237 gamesnd_play_iface(SND_SCROLL);
3239 gamesnd_play_iface(SND_GENERAL_FAIL);
3243 void multi_sg_rank_scroll_down()
3245 // if he doesn't have either of the rank flags set, then ignore this
3246 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3250 if((NUM_RANKS - Multi_sg_rank_start) > Multi_sg_rank_max_display[gr_screen.res]){
3251 Multi_sg_rank_start++;
3252 gamesnd_play_iface(SND_SCROLL);
3254 gamesnd_play_iface(SND_GENERAL_FAIL);
3258 void multi_sg_rank_display_stuff()
3263 // if he doesn't have either of the rank flags set, then ignore this
3264 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3268 // display the list of ranks
3269 y = Msg_rank_list_coords[gr_screen.res][MSG_Y_COORD];
3270 idx = Multi_sg_rank_start;
3272 while((count < NUM_RANKS) && (count < Multi_sg_rank_max_display[gr_screen.res]) && (idx < NUM_RANKS)){
3273 // if its the selected item, then color it differently
3274 if(idx == Multi_sg_rank_select){
3275 gr_set_color_fast(&Color_text_selected);
3277 gr_set_color_fast(&Color_text_normal);
3281 multi_sg_rank_build_name(Ranks[idx].name,rank_name);
3282 gr_string(Msg_rank_list_coords[gr_screen.res][MSG_X_COORD],y,rank_name);
3290 // display the selected rank
3292 gr_set_color_fast(&Color_bright);
3293 multi_sg_rank_build_name(Ranks[Multi_sg_netgame->rank_base].name,rank_name);
3294 gr_string(Msg_rank_sel_coords[gr_screen.res][MSG_X_COORD],Msg_rank_sel_coords[gr_screen.res][MSG_Y_COORD],rank_name);
3298 void multi_sg_rank_process_select()
3302 // if he doesn't have either of the rank flags set, then ignore this
3303 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3307 // see if he's clicked on an item on the rank list
3308 if(Multi_sg_rank_button.pressed()){
3310 Multi_sg_rank_button.get_mouse_pos(NULL,&y);
3313 if(item + Multi_sg_rank_start < NUM_RANKS){
3314 // evaluate whether this rank is valid for the guy to pick
3315 if(multi_sg_rank_select_valid(item + Multi_sg_rank_start)){
3316 gamesnd_play_iface(SND_USER_SELECT);
3318 Multi_sg_rank_select = item + Multi_sg_rank_start;
3320 // set the Netgame rank
3321 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
3323 gamesnd_play_iface(SND_GENERAL_FAIL);
3325 memset(string,0,255);
3326 sprintf(string,XSTR("Illegal value for a host of your rank (%s)\n",784),Ranks[Net_player->player->stats.rank].name);
3327 multi_common_add_notify(string);
3333 void multi_sg_rank_build_name(char *in,char *out)
3339 first = strtok(use," ");
3341 // just copy the string
3346 // if the first part of the string is lieutenant, then abbreivate it and tack on the rest of the string
3347 if (stricmp(first,XSTR("lieutenant",785)) == 0) {
3348 first = strtok(NULL, NOX("\n"));
3350 // if he's not just a plain lieutenant
3352 strcpy(out,XSTR("Lt. ",786)); // [[ lieutenant ]]
3355 // if he _is_ just a plain lieutenant
3364 void multi_sg_check_passwd()
3366 // check to see if the password input box has been pressed
3367 if(Multi_sg_game_passwd.changed()){
3368 Multi_sg_game_passwd.get_text(Multi_sg_netgame->passwd);
3372 void multi_sg_check_name()
3374 // check to see if the game name input box has been pressed
3375 if(Multi_sg_game_name.changed()){
3376 Multi_sg_game_name.get_text(Multi_sg_netgame->name);
3380 void multi_sg_release_passwd()
3382 // hide and disable the password input box
3383 Multi_sg_game_passwd.hide();
3384 Multi_sg_game_passwd.disable();
3386 // set the focus back to the name input box
3387 Multi_sg_game_name.set_focus();
3390 int multi_sg_rank_select_valid(int rank)
3393 if(Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE){
3394 if(Net_player->player->stats.rank >= rank){
3400 if(Net_player->player->stats.rank <= rank){
3408 void multi_sg_select_rank_default()
3410 // pick our rank for now
3411 Multi_sg_rank_select = Net_player->player->stats.rank;
3413 // set the Netgame rank
3414 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
3417 // -------------------------------------------------------------------------------------------------
3419 // MULTIPLAYER CREATE GAME screen
3424 char *Multi_create_bitmap_fname[GR_NUM_RESOLUTIONS] = {
3425 "MultiCreate", // GR_640
3426 "2_MultiCreate" // GR_1024
3429 char *Multi_create_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
3430 "MultiCreate-M", // GR_640
3431 "2_MultiCreate-M" // GR_1024
3434 char *Multi_create_loading_fname[GR_NUM_RESOLUTIONS] = {
3435 "PleaseWait", // GR_640
3436 "2_PleaseWait" // GR_1024
3440 #define MULTI_CREATE_NUM_BUTTONS 23
3443 #define MC_SHOW_ALL 0
3444 #define MC_SHOW_COOP 1
3445 #define MC_SHOW_TEAM 2
3446 #define MC_SHOW_DOGFIGHT 3
3447 #define MC_PXO_REFRESH 4
3448 #define MC_PILOT_INFO 5
3449 #define MC_SCROLL_LIST_UP 6
3450 #define MC_SCROLL_LIST_DOWN 7
3451 #define MC_SCROLL_PLAYERS_UP 8
3452 #define MC_SCROLL_PLAYERS_DOWN 9
3453 #define MC_MISSION_FILTER 10
3454 #define MC_CAMPAIGN_FILTER 11
3455 #define MC_CANCEL 12
3460 #define MC_SCROLL_INFO_UP 17
3461 #define MC_SCROLL_INFO_DOWN 18
3462 #define MC_HOST_OPTIONS 19
3464 #define MC_OPTIONS 21
3465 #define MC_ACCEPT 22
3468 UI_WINDOW Multi_create_window; // the window object for the create screen
3469 UI_BUTTON Multi_create_player_select_button; // for selecting players
3470 UI_BUTTON Multi_create_list_select_button; // for selecting missions/campaigns
3471 int Multi_create_bitmap; // the background bitmap
3472 UI_SLIDER2 Multi_create_slider; // for create list
3474 // constants for coordinate look ups
3475 #define MC_X_COORD 0
3476 #define MC_Y_COORD 1
3477 #define MC_W_COORD 2
3478 #define MC_H_COORD 3
3480 ui_button_info Multi_create_buttons[GR_NUM_RESOLUTIONS][MULTI_CREATE_NUM_BUTTONS] = {
3482 ui_button_info("MC_00", 32, 129, 36, 158, 0), // show all missions
3483 ui_button_info("MC_01", 76, 129, 71, 158, 1), // show coop missions
3484 ui_button_info("MC_02", 121, 129, 119, 158, 2), // show team missions
3485 ui_button_info("MC_03", 164, 129, 166, 158, 3), // show dogfight missions
3486 ui_button_info("MC_04", 399, 129, 229, 130, 4), // pxo mission refresh
3487 ui_button_info("MC_05", 567, 123, 467, 132, 5), // pilot info
3488 ui_button_info("MC_06", 1, 161, -1, -1, 6), // scroll mission info up
3489 ui_button_info("MC_08", 1, 304, -1, -1, 8), // scroll mission info down
3490 ui_button_info("MC_09", 613, 160, -1, -1, 9), // scroll players up
3491 ui_button_info("MC_10", 613, 202, -1, -1, 10), // scroll players down
3492 ui_button_info("MC_11", 22, 346, 27, 376, 11), // mission filter
3493 ui_button_info("MC_12", 104, 346, 110, 376, 12), // campaign filter
3494 ui_button_info("MC_13", 392, 341, 328, 364, 13), // cancel
3495 ui_button_info("MC_14", 472, 352, 482, 381, 14), // team 0
3496 ui_button_info("MC_15", 506, 352, 514, 381, 15), // team 1
3497 ui_button_info("MC_16", 539, 346, 539, 381, 16), // kick
3498 ui_button_info("MC_17", 589, 346, 582, 381, 17), // close
3499 ui_button_info("MC_18", 1, 406, -1, -1, 18), // scroll list up
3500 ui_button_info("MC_19", 1, 447, -1, -1, 19), // scroll list down
3501 ui_button_info("MC_20", 499, 434, 436, 423, 20), // host options
3502 ui_button_info("MC_21", 534, 426, -1, -1, 21), // help
3503 ui_button_info("MC_22", 534, 452, -1, -1, 22), // options
3504 ui_button_info("MC_23", 571, 426, 572, 413, 23), // commit
3507 ui_button_info("2_MC_00", 51, 207, 61, 253, 0), // show all missions
3508 ui_button_info("2_MC_01", 122, 207, 124, 253, 1), // show coop missions
3509 ui_button_info("2_MC_02", 193, 207, 194, 253, 2), // show team missions
3510 ui_button_info("2_MC_03", 263, 207, 261, 253, 3), // show dogfight missions
3511 ui_button_info("2_MC_04", 639, 207, 479, 218, 4), // pxo mission refresh
3512 ui_button_info("2_MC_05", 907, 197, 748, 216, 5), // pilot info
3513 ui_button_info("2_MC_06", 1, 258, -1, -1, 6), // scroll mission info up
3514 ui_button_info("2_MC_08", 1, 487, -1, -1, 8), // scroll mission info down
3515 ui_button_info("2_MC_09", 981, 256, -1, -1, 9), // scroll players up
3516 ui_button_info("2_MC_10", 981, 323, -1, -1, 10), // scroll players down
3517 ui_button_info("2_MC_11", 35, 554, 46, 601, 11), // mission filter
3518 ui_button_info("2_MC_12", 166, 554, 174, 601, 12), // campaign filter
3519 ui_button_info("2_MC_13", 628, 545, 559, 582, 13), // cancel
3520 ui_button_info("2_MC_14", 756, 564, 772, 610, 14), // team 0
3521 ui_button_info("2_MC_15", 810, 564, 826, 610, 15), // team 1
3522 ui_button_info("2_MC_16", 862, 554, 872, 610, 16), // kick
3523 ui_button_info("2_MC_17", 943, 554, 949, 610, 17), // close
3524 ui_button_info("2_MC_18", 1, 649, -1, -1, 18), // scroll list up
3525 ui_button_info("2_MC_19", 1, 716, -1, -1, 19), // scroll list down
3526 ui_button_info("2_MC_20", 798, 695, 726, 667, 20), // host options
3527 ui_button_info("2_MC_21", 854, 681, -1, -1, 21), // help
3528 ui_button_info("2_MC_22", 854, 724, -1, -1, 22), // options
3529 ui_button_info("2_MC_23", 914, 681, 932, 667, 23), // commit
3533 #define MULTI_CREATE_NUM_TEXT 15
3534 UI_XSTR Multi_create_text[GR_NUM_RESOLUTIONS][MULTI_CREATE_NUM_BUTTONS] = {
3536 {"All", 1256, 36, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_ALL].button},
3537 {"Coop", 1257, 71, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_COOP].button},
3538 {"Team", 1258, 119, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_TEAM].button},
3539 {"Dogfight", 1259, 166, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_DOGFIGHT].button},
3540 {"Refresh Missions", 1260, 229, 130, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_PXO_REFRESH].button},
3541 {"Pilot Info", 1261, 467, 132, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_PILOT_INFO].button},
3542 {"Missions", 1262, 27, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_MISSION_FILTER].button},
3543 {"Campaigns", 1263, 110, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_CAMPAIGN_FILTER].button},
3544 {"Cancel", 387, 328, 364, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_CANCEL].button},
3545 {"1", 1264, 482, 381, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_TEAM0].button},
3546 {"2", 1265, 514, 381, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_TEAM1].button},
3547 {"Kick", 1266, 539, 381, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_KICK].button},
3548 {"Close", 1508, 582, 381, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_CLOSE].button},
3549 {"Host Options", 1267, 436, 423, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_HOST_OPTIONS].button},
3550 {"Commit", 1062, 572, 413, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_ACCEPT].button}
3553 {"All", 1256, 61, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_ALL].button},
3554 {"Coop", 1257, 124, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_COOP].button},
3555 {"Team", 1258, 194, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_TEAM].button},
3556 {"Dogfight", 1259, 261, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_DOGFIGHT].button},
3557 {"Refresh Missions", 1260, 501, 218, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_PXO_REFRESH].button},
3558 {"Pilot Info", 1261, 814, 216, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_PILOT_INFO].button},
3559 {"Missions", 1262, 46, 601, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_MISSION_FILTER].button},
3560 {"Campaigns", 1263, 174, 601, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_CAMPAIGN_FILTER].button},
3561 {"Cancel", 387, 559, 582, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_CANCEL].button},
3562 {"1", 1264, 772, 610, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_TEAM0].button},
3563 {"2", 1265, 826, 610, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_TEAM1].button},
3564 {"Kick", 1266, 872, 610, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_KICK].button},
3565 {"Close", 1508, 949, 610, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_CLOSE].button},
3566 {"Host Options", 1267, 755, 683, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_HOST_OPTIONS].button},
3567 {"Commit", 1062, 932, 667, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_ACCEPT].button}
3571 // squad war checkbox
3572 UI_CHECKBOX Multi_create_sw_checkbox;
3573 char *Multi_create_sw_checkbox_fname[GR_NUM_RESOLUTIONS] = {
3577 int Multi_create_sw_checkbox_coords[GR_NUM_RESOLUTIONS][2] = {
3585 int Multi_create_sw_checkbox_text[GR_NUM_RESOLUTIONS][2] = {
3594 // game information text areas
3595 int Mc_list_coords[GR_NUM_RESOLUTIONS][4] = {
3604 int Mc_players_coords[GR_NUM_RESOLUTIONS][4] = {
3613 int Mc_info_coords[GR_NUM_RESOLUTIONS][4] = {
3622 // mission icon stuff
3623 int Mc_icon_type_coords[GR_NUM_RESOLUTIONS][2] = {
3625 38, -2 // y is an offset
3628 61, -2 // y is an offset
3632 int Mc_icon_volition_coords[GR_NUM_RESOLUTIONS][2] = {
3634 61, -1 // y is an offset
3637 98, 1 // y is an offset
3641 int Mc_icon_silent_coords[GR_NUM_RESOLUTIONS][2] = {
3643 72, 0 // y is an offset
3646 115, 0 // y is an offset
3650 int Mc_icon_valid_coords[GR_NUM_RESOLUTIONS][2] = {
3652 91, 0 // y is an offset
3655 146, 0 // y is an offset
3659 // mission/campaign list column areas
3660 int Mc_column1_w[GR_NUM_RESOLUTIONS] = {
3665 int Mc_column2_w[GR_NUM_RESOLUTIONS] = {
3670 int Mc_column3_w[GR_NUM_RESOLUTIONS] = {
3675 int Mc_mission_name_x[GR_NUM_RESOLUTIONS] = {
3680 int Mc_mission_count_x[GR_NUM_RESOLUTIONS] = {
3685 int Mc_mission_fname_x[GR_NUM_RESOLUTIONS] = {
3690 int Mc_create_game_text[GR_NUM_RESOLUTIONS][2] = {
3691 {13, 116}, // GR_640
3692 {21, 186} // GR_1024
3695 int Mc_players_text[GR_NUM_RESOLUTIONS][2] = {
3696 {467, 150}, // GR_640
3697 {747, 240} // GR_1024
3700 int Mc_team_text[GR_NUM_RESOLUTIONS][2] = {
3701 {484, 342}, // GR_640
3702 {774, 547} // GR_1024
3705 int Mc_slider_coords[GR_NUM_RESOLUTIONS][4] = {
3707 3, 197, 13, 105 // GR_640
3710 5, 316, 20, 168 // GR_1024
3714 char *Mc_slider_bitmap[GR_NUM_RESOLUTIONS] = {
3719 // player list control thingie defs
3720 #define MULTI_CREATE_PLIST_MAX_DISPLAY 20
3721 int Multi_create_plist_select_flag; // flag indicating if we have a play selected
3722 short Multi_create_plist_select_id; // the net address of the currently selected player (for lookup)
3724 // master tracker details
3725 int Multi_create_frame_count; // framecount
3726 int Multi_create_mt_tried_login; // attempted to login this server on the MT
3728 // mission filter settings
3729 int Multi_create_filter; // what mode we're in
3731 // game/campaign list control defs
3732 int Multi_create_list_max_display[GR_NUM_RESOLUTIONS] = {
3738 int Multi_create_list_count; // number of items in listbox
3739 int Multi_create_list_mode; // 0 == mission mode, 1 == campaign mode
3740 int Multi_create_list_start; // where to start displaying from
3741 int Multi_create_list_select; // which item is currently highlighted
3742 int Multi_create_files_loaded;
3744 char Multi_create_files_array[MULTI_CREATE_MAX_LIST_ITEMS][MAX_FILENAME_LEN];
3746 int Multi_create_mission_count; // how many we have
3747 int Multi_create_campaign_count;
3748 multi_create_info Multi_create_mission_list[MULTI_CREATE_MAX_LIST_ITEMS];
3749 multi_create_info Multi_create_campaign_list[MULTI_CREATE_MAX_LIST_ITEMS];
3751 // use a pointer for the file list. Will point to either the missions or the campaigns
3752 multi_create_info *Multi_create_file_list;
3754 // LOCAL function definitions
3755 void multi_create_check_buttons();
3756 void multi_create_button_pressed(int n);
3757 void multi_create_init_as_server();
3758 void multi_create_init_as_client();
3759 void multi_create_do_netstuff();
3760 void multi_create_plist_scroll_up();
3761 void multi_create_plist_scroll_down();
3762 void multi_create_plist_process();
3763 void multi_create_plist_blit_normal();
3764 void multi_create_plist_blit_team();
3765 void multi_create_list_scroll_up();
3766 void multi_create_list_scroll_down();
3767 void multi_create_list_do();
3768 void multi_create_list_select_item(int n);
3769 void multi_create_list_blit_icons(int list_index, int y_start);
3770 void multi_create_accept_hit();
3771 void multi_create_draw_filter_buttons();
3772 void multi_create_set_selected_team(int team);
3773 short multi_create_get_mouse_id();
3774 int multi_create_ok_to_commit();
3775 int multi_create_verify_cds();
3776 void multi_create_refresh_pxo();
3777 void multi_create_sw_clicked();
3779 // since we can selectively filter out mission/campaign types we always need to map a selected index (which is relative
3780 // to the displayed list), to an absolute index (which is relative to the total file list - some of which may filtered out)
3781 void multi_create_select_to_filename(int select_index,char *filename);
3782 int multi_create_select_to_index(int select_index);
3784 int Multi_create_should_show_popup = 0;
3787 // sorting function to sort mission lists.. Basic sorting on mission name
3788 int multi_create_sort_func(const void *a, const void *b)
3790 multi_create_info *m1, *m2;
3792 m1 = (multi_create_info *)a;
3793 m2 = (multi_create_info *)b;
3795 return ( strcmp(m1->name, m2->name) );
3798 void multi_create_setup_list_data(int mode)
3800 int idx,should_sort,switched_modes;
3802 // set the current mode
3805 if((Multi_create_list_mode != mode) && (mode != -1)){
3806 Multi_create_list_mode = mode;
3809 // set up the list pointers
3810 if ( Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ) {
3811 Multi_create_file_list = Multi_create_mission_list;
3812 } else if ( Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ) {
3813 Multi_create_file_list = Multi_create_campaign_list;
3819 // get the mission count based upon the filter selected
3820 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
3821 switch(Multi_create_filter){
3822 case MISSION_TYPE_MULTI:
3823 Multi_create_list_count = Multi_create_mission_count;
3826 Multi_create_list_count = 0;
3827 // find all missions which match
3828 for(idx=0;idx<Multi_create_mission_count;idx++){
3829 if(Multi_create_mission_list[idx].flags & Multi_create_filter){
3830 Multi_create_list_count++;
3834 // if we switched modes and we have more than 0 items, sort them
3835 if(switched_modes && (Multi_create_list_count > 0)){
3840 } else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
3841 switch(Multi_create_filter){
3842 case MISSION_TYPE_MULTI:
3843 Multi_create_list_count = Multi_create_campaign_count;
3846 Multi_create_list_count = 0;
3847 // find all missions which match
3848 for(idx=0;idx<Multi_create_campaign_count;idx++){
3849 if(Multi_create_campaign_list[idx].flags & Multi_create_filter){
3850 Multi_create_list_count++;
3854 // if we switched modes and we have more than 0 items, sort them
3855 if(switched_modes && (Multi_create_list_count > 0)){
3862 // reset the list start and selected indices
3863 Multi_create_list_start = 0;
3864 Multi_create_list_select = -1;
3865 multi_create_list_select_item(Multi_create_list_start);
3867 // sort the list of missions if necessary
3869 qsort(Multi_create_file_list, Multi_create_list_count, sizeof(multi_create_info), multi_create_sort_func);
3873 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);
3876 void multi_create_game_init()
3881 // now make sure to initialze various netgame stuff based upon whether we're on a standalone or not
3882 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3883 multi_create_init_as_server();
3885 multi_create_init_as_client();
3888 // initialize the player list data
3889 Multi_create_plist_select_flag = 0;
3890 Multi_create_plist_select_id = -1;
3892 // create the interface window
3893 Multi_create_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
3894 Multi_create_window.set_mask_bmap(Multi_create_bitmap_mask_fname[gr_screen.res]);
3896 // load the background bitmap
3897 Multi_create_bitmap = bm_load(Multi_create_bitmap_fname[gr_screen.res]);
3898 if(Multi_create_bitmap < 0){
3899 // we failed to load the bitmap - this is very bad
3903 // close any previous existing instances of the chatbox and create a new one
3907 // load the help overlay
3908 help_overlay_load(MULTI_CREATE_OVERLAY);
3909 help_overlay_set_state(MULTI_CREATE_OVERLAY, 0);
3911 // initialize the common notification messaging
3912 multi_common_notify_init();
3914 // use the common interface palette
3915 multi_common_set_palette();
3917 // create the interface buttons
3918 for(idx=0; idx<MULTI_CREATE_NUM_BUTTONS; idx++){
3919 b = &Multi_create_buttons[gr_screen.res][idx];
3921 // create the object
3922 b->button.create(&Multi_create_window, "", b->x, b->y, 1, 1, ((idx == MC_SCROLL_LIST_UP) || (idx == MC_SCROLL_LIST_DOWN)), 1);
3924 // set the sound to play when highlighted
3925 b->button.set_highlight_action(common_play_highlight_sound);
3927 // set the ani for the button
3928 b->button.set_bmaps(b->filename);
3931 b->button.link_hotspot(b->hotspot);
3933 // some special case stuff for the pxo refresh button
3934 if(idx == MC_PXO_REFRESH){
3935 // if not a PXO game, or if I'm not a server disable and hide the button
3936 if(!MULTI_IS_TRACKER_GAME || !MULTIPLAYER_MASTER){
3938 b->button.disable();
3944 for(idx=0; idx<MULTI_CREATE_NUM_TEXT; idx++){
3945 Multi_create_window.add_XSTR(&Multi_create_text[gr_screen.res][idx]);
3948 // if this is a PXO game, enable the squadwar checkbox
3949 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);
3950 Multi_create_sw_checkbox.set_bmaps(Multi_create_sw_checkbox_fname[gr_screen.res], 6, 0);
3951 if(!MULTI_IS_TRACKER_GAME){
3952 Multi_create_sw_checkbox.hide();
3953 Multi_create_sw_checkbox.disable();
3957 // disable squad war button in demo
3958 Multi_create_sw_checkbox.hide();
3959 Multi_create_sw_checkbox.disable();
3962 // initialize the mission type filtering mode
3963 Multi_create_filter = MISSION_TYPE_MULTI;
3965 // initialize the list mode, and load in a list
3966 memset(Multi_create_mission_list, 0, sizeof(multi_create_info) * MULTI_CREATE_MAX_LIST_ITEMS);
3967 memset(Multi_create_campaign_list, 0, sizeof(multi_create_info) * MULTI_CREATE_MAX_LIST_ITEMS);
3968 for(idx=0; idx<MULTI_CREATE_MAX_LIST_ITEMS; idx++){
3969 Multi_create_mission_list[idx].valid_status = MVALID_STATUS_UNKNOWN;
3970 Multi_create_campaign_list[idx].valid_status = MVALID_STATUS_UNKNOWN;
3972 Multi_create_list_mode = MULTI_CREATE_SHOW_MISSIONS;
3973 Multi_create_list_start = -1;
3974 Multi_create_list_select = -1;
3975 Multi_create_list_count = 0;
3977 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);
3979 // create the player list select button
3980 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);
3981 Multi_create_player_select_button.hide();
3983 // create the mission/campaign list select button
3984 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);
3985 Multi_create_list_select_button.hide();
3987 // set hotkeys for a couple of things.
3988 Multi_create_buttons[gr_screen.res][MC_ACCEPT].button.set_hotkey(KEY_CTRLED+KEY_ENTER);
3990 // init some master tracker stuff
3991 Multi_create_frame_count = 0;
3992 Multi_create_mt_tried_login = 0;
3994 // remove campaign flags
3995 Game_mode &= ~(GM_CAMPAIGN_MODE);
3997 // send any pilots as appropriate
3998 multi_data_send_my_junk();
3999 Multi_create_file_list = Multi_create_mission_list;
4001 Multi_create_campaign_count = 0;
4002 Multi_create_mission_count = 0;
4003 Multi_create_files_loaded = 0;
4006 void multi_create_game_do()
4009 char *loading_str = XSTR("Loading", 1336);
4012 // set this if we want to show the pilot info popup
4013 Multi_create_should_show_popup = 0;
4015 // first thing is to load the files
4016 if ( !Multi_create_files_loaded ) {
4017 // if I am a client, send a list request to the server for the missions
4018 if ( MULTIPLAYER_CLIENT ) {
4019 send_mission_list_request( MISSION_LIST_REQUEST );
4023 loading_bitmap = bm_load(Multi_create_loading_fname[gr_screen.res]);
4025 // draw the background, etc
4027 GR_MAYBE_CLEAR_RES(Multi_create_bitmap);
4028 if(Multi_create_bitmap != -1){
4029 gr_set_bitmap(Multi_create_bitmap);
4033 if ( loading_bitmap > -1 ){
4034 gr_set_bitmap(loading_bitmap);
4036 gr_bitmap( Please_wait_coords[gr_screen.res][MC_X_COORD], Please_wait_coords[gr_screen.res][MC_Y_COORD] );
4038 // draw "Loading" on it
4039 gr_set_color_fast(&Color_normal);
4041 gr_get_string_size(&str_w, &str_h, loading_str);
4042 gr_string((gr_screen.max_w - str_w) / 2, (gr_screen.max_h - str_h) / 2, loading_str);
4047 multi_create_list_load_missions();
4048 multi_create_list_load_campaigns();
4050 // if this is a tracker game, validate missions
4051 if(MULTI_IS_TRACKER_GAME){
4052 multi_update_valid_missions();
4055 // update the file list
4056 multi_create_setup_list_data(MULTI_CREATE_SHOW_MISSIONS);
4059 // don't bother setting netgame state if ont the server
4060 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4061 Netgame.game_state = NETGAME_STATE_FORMING;
4062 send_netgame_update_packet();
4065 // if we're on the standalone we have to tell him that we're now in the host setup screen
4066 Net_player->state = NETPLAYER_STATE_HOST_SETUP;
4067 send_netplayer_update_packet();
4069 Multi_create_files_loaded = 1;
4072 int k = chatbox_process();
4073 k = Multi_create_window.process(k,0);
4076 // same as the cancel button
4078 if(help_overlay_active(MULTI_CREATE_OVERLAY)){
4079 help_overlay_set_state(MULTI_CREATE_OVERLAY,0);
4081 gamesnd_play_iface(SND_USER_SELECT);
4082 multi_quit_game(PROMPT_HOST);
4087 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
4088 help_overlay_set_state(MULTI_CREATE_OVERLAY, 0);
4091 // process any button clicks
4092 multi_create_check_buttons();
4094 // do any network related stuff
4095 multi_create_do_netstuff();
4097 // draw the background, etc
4099 GR_MAYBE_CLEAR_RES(Multi_create_bitmap);
4100 if(Multi_create_bitmap != -1){
4101 gr_set_bitmap(Multi_create_bitmap);
4105 // if we're not in team vs. team mode, don't draw the team buttons
4106 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
4107 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.hide();
4108 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.hide();
4109 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.disable();
4110 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.disable();
4112 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.enable();
4113 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.enable();
4114 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.unhide();
4115 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.unhide();
4118 // draw the window itself
4119 Multi_create_window.draw();
4121 gr_set_color_fast(&Color_normal);
4123 // draw Create Game text
4124 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));
4126 // draw players text
4127 gr_string(Mc_players_text[gr_screen.res][MC_X_COORD], Mc_players_text[gr_screen.res][MC_Y_COORD], XSTR("Players", 1269));
4129 // draw players text
4130 gr_string(Mc_team_text[gr_screen.res][MC_X_COORD], Mc_team_text[gr_screen.res][MC_Y_COORD], XSTR("Team", 1258));
4132 // process and display the player list
4133 // NOTE : this must be done before the buttons are checked to insure that a player hasn't dropped
4134 multi_create_plist_process();
4135 if(Netgame.type_flags & NG_TYPE_TEAM){
4136 multi_create_plist_blit_team();
4138 multi_create_plist_blit_normal();
4141 // process and display the game/campaign list
4142 multi_create_list_do();
4144 // draw the correct mission filter button
4145 multi_create_draw_filter_buttons();
4147 // display any text in the info area
4148 multi_common_render_text();
4150 // display any pending notification messages
4151 multi_common_notify_do();
4153 // force the correct mission/campaign button to light up
4154 if( Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ){
4155 Multi_create_buttons[gr_screen.res][MC_MISSION_FILTER].button.draw_forced(2);
4157 Multi_create_buttons[gr_screen.res][MC_CAMPAIGN_FILTER].button.draw_forced(2);
4160 // force draw the closed button if it is toggled on
4161 if(Netgame.flags & NG_FLAG_TEMP_CLOSED){
4162 Multi_create_buttons[gr_screen.res][MC_CLOSE].button.draw_forced(2);
4165 // process and show the chatbox thingie
4169 Multi_create_window.draw_tooltip();
4171 // display the voice status indicator
4172 multi_common_voice_display_status();
4174 // blit the help overlay if necessary
4175 help_overlay_maybe_blit(MULTI_CREATE_OVERLAY);
4178 if(MULTI_IS_TRACKER_GAME){
4179 if(Netgame.type_flags & NG_TYPE_SW){
4180 gr_set_color_fast(&Color_bright);
4182 gr_set_color_fast(&Color_normal);
4184 gr_string(Multi_create_sw_checkbox_text[gr_screen.res][0], Multi_create_sw_checkbox_text[gr_screen.res][1], "SquadWar");
4190 // if we're supposed to show the pilot info popup, do it now
4191 if(Multi_create_should_show_popup){
4192 // get the player index and address of the player item the mouse is currently over
4193 if(Multi_create_plist_select_flag){
4194 player_index = find_player_id(Multi_create_plist_select_id);
4195 if(player_index != -1){
4196 multi_pinfo_popup(&Net_players[player_index]);
4201 // increment the frame count
4202 Multi_create_frame_count++;
4205 void multi_create_game_close()
4207 // unload any bitmaps
4208 if(!bm_unload(Multi_create_bitmap)){
4209 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_create_bitmap_fname[gr_screen.res]));
4212 // unload the help overlay
4213 help_overlay_unload(MULTI_CREATE_OVERLAY);
4215 // destroy the chatbox
4218 // destroy the UI_WINDOW
4219 Multi_create_window.destroy();
4222 void multi_create_check_buttons()
4225 for(idx=0;idx<MULTI_CREATE_NUM_BUTTONS;idx++){
4226 // we only really need to check for one button pressed at a time, so we can break after
4228 if(Multi_create_buttons[gr_screen.res][idx].button.pressed()){
4229 multi_create_button_pressed(idx);
4234 // if the squad war checkbox was clicked
4235 if(Multi_create_sw_checkbox.changed()){
4236 multi_create_sw_clicked();
4240 void multi_create_button_pressed(int n)
4246 gamesnd_play_iface(SND_USER_SELECT);
4247 multi_quit_game(PROMPT_HOST);
4250 // if valid commit conditions have not been met
4251 if(!multi_create_ok_to_commit()){
4256 multi_create_accept_hit();
4261 if(!help_overlay_active(MULTI_CREATE_OVERLAY)){
4262 help_overlay_set_state(MULTI_CREATE_OVERLAY,1);
4264 help_overlay_set_state(MULTI_CREATE_OVERLAY,0);
4268 // scroll the info text box up
4269 case MC_SCROLL_INFO_UP:
4270 multi_common_scroll_text_up();
4273 // scroll the info text box down
4274 case MC_SCROLL_INFO_DOWN:
4275 multi_common_scroll_text_down();
4278 // scroll the player list up
4279 case MC_SCROLL_PLAYERS_UP:
4280 multi_create_plist_scroll_up();
4283 // scroll the player list down
4284 case MC_SCROLL_PLAYERS_DOWN:
4285 multi_create_plist_scroll_down();
4288 // scroll the game/campaign list up
4289 case MC_SCROLL_LIST_UP:
4290 multi_create_list_scroll_up();
4291 Multi_create_slider.forceUp(); // move slider up
4294 // scroll the game/campaign list down
4295 case MC_SCROLL_LIST_DOWN:
4296 multi_create_list_scroll_down();
4297 Multi_create_slider.forceDown(); // move slider down
4300 // go to the options screen
4302 gamesnd_play_iface(SND_USER_SELECT);
4303 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
4306 // show all missions
4308 if(Multi_create_filter != MISSION_TYPE_MULTI){
4309 gamesnd_play_iface(SND_USER_SELECT);
4310 Multi_create_filter = MISSION_TYPE_MULTI;
4311 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4313 gamesnd_play_iface(SND_GENERAL_FAIL);
4317 // show cooperative missions
4319 if(Multi_create_filter != MISSION_TYPE_MULTI_COOP){
4320 gamesnd_play_iface(SND_USER_SELECT);
4321 Multi_create_filter = MISSION_TYPE_MULTI_COOP;
4322 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4324 gamesnd_play_iface(SND_GENERAL_FAIL);
4328 // show team vs. team missions
4330 if(Multi_create_filter != MISSION_TYPE_MULTI_TEAMS){
4331 gamesnd_play_iface(SND_USER_SELECT);
4332 Multi_create_filter = MISSION_TYPE_MULTI_TEAMS;
4333 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4335 gamesnd_play_iface(SND_GENERAL_FAIL);
4339 // show dogfight missions
4340 case MC_SHOW_DOGFIGHT:
4341 if (Multi_create_filter != MISSION_TYPE_MULTI_DOGFIGHT){
4342 gamesnd_play_iface(SND_USER_SELECT);
4343 Multi_create_filter = MISSION_TYPE_MULTI_DOGFIGHT;
4344 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4346 gamesnd_play_iface(SND_GENERAL_FAIL);
4350 // toggle temporary netgame closed on/off
4352 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4353 Netgame.flags ^= NG_FLAG_TEMP_CLOSED;
4355 Netgame.options.flags |= MLO_FLAG_TEMP_CLOSED;
4356 multi_options_update_netgame();
4358 gamesnd_play_iface(SND_USER_SELECT);
4361 // kick the currently selected player (if possible)
4363 // lookup the player at the specified index
4364 if(Multi_create_plist_select_flag){
4365 idx = find_player_id(Multi_create_plist_select_id);
4366 // kick him - but don't ban him
4368 multi_kick_player(idx,0);
4373 // switch to individual mission mode and load in a list
4374 case MC_MISSION_FILTER:
4375 if(Multi_create_list_mode != MULTI_CREATE_SHOW_MISSIONS){
4376 Netgame.campaign_mode = MP_SINGLE;
4378 gamesnd_play_iface(SND_USER_SELECT);
4380 // update the file list
4381 multi_create_setup_list_data(MULTI_CREATE_SHOW_MISSIONS);
4383 gamesnd_play_iface(SND_GENERAL_FAIL);
4387 // switch to campaign mode and load in a list
4388 case MC_CAMPAIGN_FILTER:
4389 // switch off squad war
4390 Multi_create_sw_checkbox.set_state(0);
4391 Netgame.type_flags = NG_TYPE_COOP;
4393 if(Multi_create_list_mode != MULTI_CREATE_SHOW_CAMPAIGNS){
4394 Netgame.campaign_mode = MP_CAMPAIGN;
4396 gamesnd_play_iface(SND_USER_SELECT);
4398 // update the file list
4399 multi_create_setup_list_data(MULTI_CREATE_SHOW_CAMPAIGNS);
4401 gamesnd_play_iface(SND_GENERAL_FAIL);
4405 // attempt to set the selected player's team
4407 multi_create_set_selected_team(0);
4408 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4409 multi_team_send_update();
4413 // attempt to set the selected player's team
4415 multi_create_set_selected_team(1);
4416 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4417 multi_team_send_update();
4421 // popup the pilot info dialog for the currently selected pilot (will occur at the end of the frame)
4423 Multi_create_should_show_popup = 1;
4426 // go to the host options screen
4427 case MC_HOST_OPTIONS:
4428 gamesnd_play_iface(SND_USER_SELECT);
4429 gameseq_post_event(GS_EVENT_MULTI_HOST_OPTIONS);
4432 // refresh PXO file list
4433 case MC_PXO_REFRESH:
4434 if(!MULTI_IS_TRACKER_GAME){
4437 multi_create_refresh_pxo();
4441 gamesnd_play_iface(SND_GENERAL_FAIL);
4442 multi_common_add_notify(XSTR("Not implemented yet!",760));
4447 // do stuff like pinging servers, sending out requests, etc
4448 void multi_create_do_netstuff()
4452 // if not on a standalone
4453 void multi_create_init_as_server()
4455 // set me up as the host and master
4456 Net_player->flags |= (NETINFO_FLAG_AM_MASTER | NETINFO_FLAG_GAME_HOST);
4459 // if on a standalone
4460 void multi_create_init_as_client()
4462 Net_player->flags |= NETINFO_FLAG_GAME_HOST;
4465 // scroll up through the player list
4466 void multi_create_plist_scroll_up()
4468 gamesnd_play_iface(SND_GENERAL_FAIL);
4471 // scroll down through the player list
4472 void multi_create_plist_scroll_down()
4474 gamesnd_play_iface(SND_GENERAL_FAIL);
4477 void multi_create_plist_process()
4479 int test_count,idx,player_index;
4481 // first determine if there are 0 players in the game. This should never happen since the host is _always_ in the game
4483 for(idx=0;idx<MAX_PLAYERS;idx++){
4484 // count anyone except the standalone server (if applicable)
4485 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
4489 if(test_count <= 0){
4493 // if we had a selected item but that player has left, select myself instead
4494 if(Multi_create_plist_select_flag){
4495 player_index = find_player_id(Multi_create_plist_select_id);
4496 if(player_index == -1){
4497 Multi_create_plist_select_id = Net_player->player_id;
4500 Multi_create_plist_select_flag = 1;
4501 Multi_create_plist_select_id = Net_player->player_id;
4504 // if the player has clicked somewhere in the player list area
4505 if(Multi_create_player_select_button.pressed()){
4508 // get the player index and address of the player item the mouse is currently over
4509 player_id = multi_create_get_mouse_id();
4510 player_index = find_player_id(player_id);
4511 if(player_index != -1){
4512 Multi_create_plist_select_flag = 1;
4513 Multi_create_plist_select_id = player_id;
4518 void multi_create_plist_blit_normal()
4521 char str[CALLSIGN_LEN+5];
4522 int y_start = Mc_players_coords[gr_screen.res][MC_Y_COORD];
4525 // display all the players
4526 for(idx=0;idx<MAX_PLAYERS;idx++){
4527 // count anyone except the standalone server (if applicable)
4528 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
4532 // highlight him if he's the host
4533 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4534 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4535 gr_set_color_fast(&Color_text_active_hi);
4537 gr_set_color_fast(&Color_bright);
4540 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4541 gr_set_color_fast(&Color_text_active);
4543 gr_set_color_fast(&Color_text_normal);
4547 // optionally draw his CD status
4548 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4549 gr_set_bitmap(Multi_common_icons[MICON_CD]);
4550 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4552 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4555 // make sure the string will fit, then display it
4556 strcpy(str,Net_players[idx].player->callsign);
4557 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4558 strcat(str,XSTR("(O)",787)); // [[ Observer ]]
4560 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4561 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4568 void multi_create_plist_blit_team()
4571 char str[CALLSIGN_LEN+1];
4572 int y_start = Mc_players_coords[gr_screen.res][MC_Y_COORD];
4575 // display all the red players first
4576 for(idx=0;idx<MAX_PLAYERS;idx++){
4577 // count anyone except the standalone server (if applicable)
4578 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
4579 // reset total offset
4582 // highlight him if he's the host
4583 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4584 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4585 gr_set_color_fast(&Color_text_active_hi);
4587 // be sure to blit the correct team button
4588 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.draw_forced(2);
4590 gr_set_color_fast(&Color_bright);
4593 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4594 gr_set_color_fast(&Color_text_active);
4596 // be sure to blit the correct team button
4597 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.draw_forced(2);
4599 gr_set_color_fast(&Color_text_normal);
4603 // optionally draw his CD status
4604 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4605 gr_set_bitmap(Multi_common_icons[MICON_CD]);
4606 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4608 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4611 // blit the red team indicator
4612 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
4613 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
4614 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT]);
4615 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4617 total_offset += Multi_common_icon_dims[MICON_TEAM0_SELECT][0] + 1;
4620 if(Multi_common_icons[MICON_TEAM0] != -1){
4621 gr_set_bitmap(Multi_common_icons[MICON_TEAM0]);
4622 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4624 total_offset += Multi_common_icon_dims[MICON_TEAM0][0] + 1;
4628 // make sure the string will fit
4629 strcpy(str,Net_players[idx].player->callsign);
4630 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4631 strcat(str,XSTR("(O)",787));
4633 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4635 // display him in the correct half of the list depending on his team
4636 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4641 // display all the green players next
4642 for(idx=0;idx<MAX_PLAYERS;idx++){
4643 // count anyone except the standalone server (if applicable)
4644 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
4645 // reset total offset
4648 // highlight him if he's the host
4649 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4650 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4651 gr_set_color_fast(&Color_text_active_hi);
4653 // be sure to blit the correct team button
4654 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.draw_forced(2);
4656 gr_set_color_fast(&Color_bright);
4659 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4660 gr_set_color_fast(&Color_text_active);
4662 // be sure to blit the correct team button
4663 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.draw_forced(2);
4665 gr_set_color_fast(&Color_text_normal);
4669 // optionally draw his CD status
4670 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4671 gr_set_bitmap(Multi_common_icons[MICON_CD]);
4672 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4674 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4677 // blit the red team indicator
4678 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
4679 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
4680 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT]);
4681 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4683 total_offset += Multi_common_icon_dims[MICON_TEAM1_SELECT][0] + 1;
4686 if(Multi_common_icons[MICON_TEAM1] != -1){
4687 gr_set_bitmap(Multi_common_icons[MICON_TEAM1]);
4688 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4690 total_offset += Multi_common_icon_dims[MICON_TEAM1][0] + 1;
4694 // make sure the string will fit
4695 strcpy(str,Net_players[idx].player->callsign);
4696 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4697 strcat(str,XSTR("(O)",787));
4699 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4701 // display him in the correct half of the list depending on his team
4702 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4708 void multi_create_list_scroll_up()
4710 if(Multi_create_list_start > 0){
4711 Multi_create_list_start--;
4713 gamesnd_play_iface(SND_SCROLL);
4715 gamesnd_play_iface(SND_GENERAL_FAIL);
4719 void multi_create_list_scroll_down()
4721 if((Multi_create_list_count - Multi_create_list_start) > Multi_create_list_max_display[gr_screen.res]){
4722 Multi_create_list_start++;
4724 gamesnd_play_iface(SND_SCROLL);
4726 gamesnd_play_iface(SND_GENERAL_FAIL);
4730 // gets a list of multiplayer misisons
4731 void multi_create_list_load_missions()
4733 char *fname, mission_name[NAME_LENGTH+1];
4734 char wild_card[256];
4737 memset(wild_card, 0, 256);
4738 strcpy(wild_card, NOX("*"));
4739 strcat(wild_card, FS_MISSION_FILE_EXT);
4740 file_count = cf_get_file_list_preallocated(MULTI_CREATE_MAX_LIST_ITEMS, Multi_create_files_array, NULL, CF_TYPE_MISSIONS, wild_card);
4741 Multi_create_mission_count = 0;
4743 // maybe create a standalone dialog
4744 if(Game_mode & GM_STANDALONE_SERVER){
4745 std_create_gen_dialog("Loading missions");
4746 std_gen_set_text("Mission:", 1);
4749 for(idx = 0; idx < file_count; idx++){
4750 int flags,max_players;
4754 fname = Multi_create_files_array[idx];
4756 // tack on any necessary file extension
4757 filename = cf_add_ext( fname, FS_MISSION_FILE_EXT );
4759 // for multiplayer beta builds, only accept builtin missions
4760 #if defined(MULTIPLAYER_BETA_BUILD) || defined(FS2_DEMO)
4761 if(game_find_builtin_mission(filename) == NULL){
4764 #elif defined(PD_BUILD)
4765 if((game_find_builtin_mission(filename) == NULL) && !strstr(filename, "peterdrake")){
4770 if(Game_mode & GM_STANDALONE_SERVER){
4771 std_gen_set_text(filename, 2);
4774 flags = mission_parse_is_multi(filename, mission_name);
4776 // if the mission is a multiplayer mission, then add it to the mission list
4778 max_players = mission_parse_get_multi_mission_info( filename );
4779 m_respawn = The_mission.num_respawns;
4781 if ( Multi_create_mission_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
4782 multi_create_info *mcip;
4784 mcip = &Multi_create_mission_list[Multi_create_mission_count];
4785 strcpy(mcip->filename, filename );
4786 strcpy(mcip->name, mission_name );
4787 mcip->flags = flags;
4788 mcip->respawn = m_respawn;
4789 mcip->max_players = (ubyte)max_players;
4791 // get any additional information for possibly builtin missions
4792 fs_builtin_mission *fb = game_find_builtin_mission(filename);
4796 Multi_create_mission_count++;
4801 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);
4803 // maybe create a standalone dialog
4804 if(Game_mode & GM_STANDALONE_SERVER){
4805 std_destroy_gen_dialog();
4809 void multi_create_list_load_campaigns()
4812 int idx, file_count;
4813 int campaign_type,max_players;
4815 char wild_card[256];
4817 // maybe create a standalone dialog
4818 if(Game_mode & GM_STANDALONE_SERVER){
4819 std_create_gen_dialog("Loading campaigns");
4820 std_gen_set_text("Campaign:", 1);
4823 Multi_create_campaign_count = 0;
4824 memset(wild_card, 0, 256);
4825 strcpy(wild_card, NOX("*"));
4826 strcat(wild_card, FS_CAMPAIGN_FILE_EXT);
4827 file_count = cf_get_file_list_preallocated(MULTI_CREATE_MAX_LIST_ITEMS, Multi_create_files_array, NULL, CF_TYPE_MISSIONS, wild_card);
4828 for(idx = 0; idx < file_count; idx++){
4830 char *filename, name[NAME_LENGTH];
4832 fname = Multi_create_files_array[idx];
4834 // tack on any necessary file extension
4835 filename = cf_add_ext( fname, FS_CAMPAIGN_FILE_EXT );
4837 // for multiplayer beta builds, only accept builtin missions
4838 #if defined(MULTIPLAYER_BETA_BUILD) || defined(FS2_DEMO)
4839 if(game_find_builtin_mission(filename) == NULL){
4842 #elif defined(PD_BUILD)
4843 if((game_find_builtin_mission(filename) == NULL) && !strstr(filename, "peterdrake")){
4848 if(Game_mode & GM_STANDALONE_SERVER){
4849 std_gen_set_text(filename, 2);
4852 // if the campaign is a multiplayer campaign, then add the data to the campaign list items
4853 flags = mission_campaign_parse_is_multi( filename, name );
4854 if( flags != CAMPAIGN_TYPE_SINGLE && mission_campaign_get_info(filename,title,&campaign_type,&max_players)) {
4855 if ( Multi_create_campaign_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
4856 multi_create_info *mcip;
4858 mcip = &Multi_create_campaign_list[Multi_create_campaign_count];
4859 strcpy(mcip->filename, filename );
4860 strcpy(mcip->name, name );
4862 // setup various flags
4863 if ( flags == CAMPAIGN_TYPE_MULTI_COOP ){
4864 mcip->flags = MISSION_TYPE_MULTI_COOP | MISSION_TYPE_MULTI;
4865 } else if ( flags == CAMPAIGN_TYPE_MULTI_TEAMS ) {
4866 mcip->flags = MISSION_TYPE_MULTI_TEAMS | MISSION_TYPE_MULTI;
4868 Int3(); // bogus campaign multi type -- find allender
4871 // 0 respawns for campaign files (should be contained within the mission files themselves)
4874 // 0 max players for campaign files
4875 mcip->max_players = (unsigned char)max_players;
4877 // get any additional information for possibly builtin missions
4878 fs_builtin_mission *fb = game_find_builtin_mission(filename);
4882 Multi_create_campaign_count++;
4887 // maybe create a standalone dialog
4888 if(Game_mode & GM_STANDALONE_SERVER){
4889 std_destroy_gen_dialog();
4893 void multi_create_list_do()
4896 int start_index,stop_index;
4897 char selected_name[255];
4899 // bail early if there aren't any selectable items
4900 if(Multi_create_list_count == 0){
4904 // first check to see if the user has clicked on an item
4905 if(Multi_create_list_select_button.pressed()){
4907 Multi_create_list_select_button.get_mouse_pos(NULL,&y);
4910 // make sure we are selectedin valid indices
4911 if((item < Multi_create_list_max_display[gr_screen.res]) && (item >= 0)){
4912 item += Multi_create_list_start;
4914 if(item < Multi_create_list_count){
4915 multi_create_list_select_item(item);
4916 gamesnd_play_iface(SND_IFACE_MOUSE_CLICK);
4921 // bail early if we don't have a start position
4922 if(Multi_create_list_start == -1){
4926 // display the list of individual campaigns/missions
4928 int y_start = Mc_list_coords[gr_screen.res][MC_Y_COORD];
4930 start_index = multi_create_select_to_index(Multi_create_list_start);
4931 stop_index = Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ? Multi_create_mission_count : Multi_create_campaign_count;
4932 for(idx=start_index; idx<stop_index; idx++){
4933 // see if we should drop out
4934 if(count == Multi_create_list_max_display[gr_screen.res]){
4938 // see if we should filter out this mission
4939 if ( !(Multi_create_file_list[idx].flags & Multi_create_filter) ){
4943 // highlight the selected item
4944 multi_create_select_to_filename(Multi_create_list_select,selected_name);
4945 if(!strcmp(selected_name,Multi_create_file_list[idx].filename)){
4946 gr_set_color_fast(&Color_text_selected);
4948 gr_set_color_fast(&Color_text_normal);
4951 // draw the type icon
4952 multi_create_list_blit_icons(idx, y_start);
4954 // force fit the mission name string
4955 strcpy(selected_name,Multi_create_file_list[idx].name);
4956 gr_force_fit_string(selected_name,255,Mc_column1_w[gr_screen.res]);
4957 gr_string(Mc_mission_name_x[gr_screen.res],y_start,selected_name);
4959 // draw the max players if in mission mode
4960 sprintf(selected_name,"%d",(int)Multi_create_file_list[idx].max_players);
4961 gr_string(Mc_mission_count_x[gr_screen.res],y_start,selected_name);
4963 // force fit the mission filename string
4964 strcpy(selected_name,Multi_create_file_list[idx].filename);
4965 gr_force_fit_string(selected_name,255,Mc_column3_w[gr_screen.res]);
4966 gr_string(Mc_mission_fname_x[gr_screen.res],y_start,selected_name);
4973 // takes care of stuff like changing indices around and setting up the netgame structure
4974 void multi_create_list_select_item(int n)
4976 int abs_index,campaign_type,max_players;
4977 char title[NAME_LENGTH+1];
4978 netgame_info ng_temp;
4981 char *campaign_desc;
4983 // if not on the standalone server
4984 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4987 // on the standalone
4989 memset(&ng_temp,0,sizeof(netgame_info));
4993 if ( n != Multi_create_list_select ) {
4994 // check to see if this is a valid index, and bail if it is not
4995 abs_index = multi_create_select_to_index(n);
4996 if(abs_index == -1){
5000 Multi_create_list_select = n;
5002 // set the mission name
5003 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5004 multi_create_select_to_filename(n,ng->mission_name);
5006 multi_create_select_to_filename(n,ng->campaign_name);
5009 // make sure the netgame type is properly set
5010 int old_type = Netgame.type_flags;
5011 abs_index = multi_create_select_to_index(n);
5012 if(abs_index != -1){
5013 if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_TEAMS){
5014 // if we're in squad war mode, leave it as squad war
5015 if(old_type & NG_TYPE_SW){
5016 ng->type_flags = NG_TYPE_SW;
5018 ng->type_flags = NG_TYPE_TVT;
5020 } else if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_COOP){
5021 ng->type_flags = NG_TYPE_COOP;
5022 } else if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_DOGFIGHT){
5023 ng->type_flags = NG_TYPE_DOGFIGHT;
5027 // if we're no longer in a TvT game, just uncheck the squadwar checkbox
5028 if(!(ng->type_flags & NG_TYPE_TEAM)){
5029 Multi_create_sw_checkbox.set_state(0);
5032 // if we switched from something else to team vs. team mode, do some special processing
5033 if((ng->type_flags & NG_TYPE_TEAM) && (ng->type_flags != old_type) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
5037 switch(Multi_create_list_mode){
5038 case MULTI_CREATE_SHOW_MISSIONS:
5039 // don't forget to update the info box window thingie
5040 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5041 ship_init(); // mwa -- 10/15/97. Call this function to reset number of ships in mission
5042 ng->max_players = mission_parse_get_multi_mission_info( ng->mission_name );
5044 Assert(ng->max_players > 0);
5045 strcpy(ng->title,The_mission.name);
5047 // set the information area text
5048 multi_common_set_text(The_mission.mission_desc);
5050 // if we're on the standalone, send a request for the description
5052 send_netgame_descript_packet(&Netgame.server_addr,0);
5053 multi_common_set_text("");
5056 // set the respawns as appropriate
5057 if(Netgame.options.respawn <= Multi_create_file_list[abs_index].respawn){
5058 ng->respawn = Netgame.options.respawn;
5059 nprintf(("Network","Using netgame options for respawn count (%d %d)\n",Netgame.options.respawn,Multi_create_file_list[abs_index].respawn));
5061 ng->respawn = Multi_create_file_list[abs_index].respawn;
5062 nprintf(("Network","Using mission settings for respawn count (%d %d)\n",Netgame.options.respawn,Multi_create_file_list[abs_index].respawn));
5065 case MULTI_CREATE_SHOW_CAMPAIGNS:
5066 // if not on the standalone server
5067 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5068 // get the campaign info
5069 memset(title,0,NAME_LENGTH+1);
5070 if(!mission_campaign_get_info(ng->campaign_name,title,&campaign_type,&max_players, &campaign_desc)) {
5071 memset(ng->campaign_name,0,NAME_LENGTH+1);
5072 ng->max_players = 0;
5074 // if we successfully got the # of players
5076 memset(ng->title,0,NAME_LENGTH+1);
5077 strcpy(ng->title,title);
5078 ng->max_players = max_players;
5081 nprintf(("Network","MC MAX PLAYERS : %d\n",ng->max_players));
5083 // set the information area text
5084 // multi_common_set_text(ng->title);
5085 multi_common_set_text(campaign_desc);
5087 // if on the standalone server, send a request for the description
5089 // no descriptions currently kept for campaigns
5092 // netgame respawns are always 0 for campaigns (until the first mission is loaded)
5097 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5099 send_netgame_update_packet();
5101 // update all machines about stuff like respawns, etc.
5102 multi_options_update_netgame();
5104 multi_options_update_mission(ng, Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ? 1 : 0);
5109 void multi_create_list_blit_icons(int list_index, int y_start)
5111 multi_create_info *mcip;
5112 fs_builtin_mission *fb;
5115 // get a pointer to the list item
5116 max_index = Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ? Multi_create_mission_count - 1 : Multi_create_campaign_count - 1;
5117 if((list_index < 0) || (list_index > max_index)){
5120 mcip = &Multi_create_file_list[list_index];
5122 // blit the multiplayer type icons
5123 if(mcip->flags & MISSION_TYPE_MULTI_COOP){
5124 if(Multi_common_icons[MICON_COOP] >= 0){
5125 gr_set_bitmap(Multi_common_icons[MICON_COOP]);
5126 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5128 } else if(mcip->flags & MISSION_TYPE_MULTI_TEAMS){
5129 if(Multi_common_icons[MICON_TVT] >= 0){
5130 gr_set_bitmap(Multi_common_icons[MICON_TVT]);
5131 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5133 } else if(mcip->flags & MISSION_TYPE_MULTI_DOGFIGHT){
5134 if(Multi_common_icons[MICON_DOGFIGHT] >= 0){
5135 gr_set_bitmap(Multi_common_icons[MICON_DOGFIGHT]);
5136 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5140 // if its a valid mission, blit the valid mission icon
5141 if(MULTI_IS_TRACKER_GAME && (mcip->valid_status == MVALID_STATUS_VALID)){
5142 if(Multi_common_icons[MICON_VALID] >= 0){
5143 gr_set_bitmap(Multi_common_icons[MICON_VALID]);
5144 gr_bitmap(Mc_icon_valid_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_valid_coords[gr_screen.res][MC_Y_COORD]);
5148 // now see if its a builtin mission
5149 fb = game_find_builtin_mission(mcip->filename);
5150 // if the mission is from volition, blit the volition icon
5151 if((fb != NULL) && (fb->flags & FSB_FROM_VOLITION)){
5152 if(Multi_common_icons[MICON_VOLITION] >= 0){
5153 gr_set_bitmap(Multi_common_icons[MICON_VOLITION]);
5154 gr_bitmap(Mc_icon_volition_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_volition_coords[gr_screen.res][MC_Y_COORD]);
5159 void multi_create_accept_hit()
5161 char selected_name[255];
5162 int start_campaign = 0;
5164 // make sure all players have finished joining
5165 if(!multi_netplayer_state_check(NETPLAYER_STATE_JOINED,1)){
5166 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("Please wait until all clients have finished joining",788));
5167 gamesnd_play_iface(SND_GENERAL_FAIL);
5170 gamesnd_play_iface(SND_COMMIT_PRESSED);
5173 // do single mission stuff
5174 switch(Multi_create_list_mode){
5175 case MULTI_CREATE_SHOW_MISSIONS:
5176 if(Multi_create_list_select != -1){
5177 // set the netgame mode
5178 Netgame.campaign_mode = MP_SINGLE;
5180 // setup various filenames and mission names
5181 multi_create_select_to_filename(Multi_create_list_select,selected_name);
5182 strncpy( Game_current_mission_filename, selected_name, MAX_FILENAME_LEN );
5183 strncpy(Netgame.mission_name,selected_name,MAX_FILENAME_LEN);
5186 ml_printf(NOX("Starting single mission %s, with %d players"), Game_current_mission_filename, multi_num_players());
5188 multi_common_add_notify(XSTR("No mission selected!",789));
5193 case MULTI_CREATE_SHOW_CAMPAIGNS:
5194 // do campaign related stuff
5195 if(Multi_create_list_select != -1){
5196 // set the netgame mode
5197 Netgame.campaign_mode = MP_CAMPAIGN;
5199 // start a campaign instead of a single mission
5200 multi_create_select_to_filename(Multi_create_list_select,selected_name);
5201 multi_campaign_start(selected_name);
5205 ml_printf(NOX("Starting campaign %s, with %d players"), selected_name, multi_num_players());
5207 multi_common_add_notify(XSTR("No campaign selected!",790));
5213 // if this is a team vs team situation, lock the players send a final team update
5214 if((Netgame.type_flags & NG_TYPE_TEAM) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
5215 multi_team_host_lock_all();
5216 multi_team_send_update();
5219 // if not on the standalone, move to the mission sync state which will take care of everything
5220 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5221 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
5222 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
5224 // otherwise tell the standalone to do so
5226 // when the standalone receives this, he'll do the mission syncing himself
5227 send_mission_sync_packet(MULTI_SYNC_PRE_BRIEFING,start_campaign);
5231 void multi_create_draw_filter_buttons()
5233 // highlight the correct filter button
5234 if ( Multi_create_filter == MISSION_TYPE_MULTI ){
5235 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL].button.draw_forced(2);
5236 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_COOP ) {
5237 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 1].button.draw_forced(2);
5238 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_TEAMS ) {
5239 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 2].button.draw_forced(2);
5240 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_DOGFIGHT ){
5241 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 3].button.draw_forced(2);
5247 void multi_create_set_selected_team(int team)
5251 // if we don't currently have a player selected, don't do anything
5252 if(!Multi_create_plist_select_flag){
5253 gamesnd_play_iface(SND_GENERAL_FAIL);
5256 gamesnd_play_iface(SND_USER_SELECT);
5258 // otherwise attempt to set the team for this guy
5259 player_index = find_player_id(Multi_create_plist_select_id);
5260 if(player_index != -1){
5261 multi_team_set_team(&Net_players[player_index],team);
5265 void multi_create_handle_join(net_player *pl)
5267 // for now just play a bloop sound
5268 gamesnd_play_iface(SND_ICON_DROP_ON_WING);
5271 // fill in net address of player the mouse is over, return player index (or -1 if none)
5272 short multi_create_get_mouse_id()
5274 // determine where he clicked (y pixel value)
5276 Multi_create_player_select_button.get_mouse_pos(NULL,&y);
5278 // select things a little differently if we're in team vs. team or non-team vs. team mode
5280 if(Netgame.type_flags & NG_TYPE_TEAM){
5281 int player_index = -1;
5283 // look through all of team red first
5284 for(idx=0;idx<MAX_PLAYERS;idx++){
5285 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
5288 // if this is the _nth_ guy
5296 // if we still haven't found him yet, look through the green team
5297 if(player_index == -1){
5298 for(idx=0;idx<MAX_PLAYERS;idx++){
5299 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
5301 // if this is the _nth_ guy
5310 if(player_index != -1){
5311 return Net_players[player_index].player_id;
5314 // select the nth active player if possible, disregarding the standalone server
5315 for(idx=0;idx<MAX_PLAYERS;idx++){
5316 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
5319 // if this is the _nth_ guy
5321 return Net_players[idx].player_id;
5330 void multi_create_select_to_filename(int select_index,char *filename)
5334 // look through the mission list
5335 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5336 for(idx=0;idx<Multi_create_mission_count;idx++){
5337 if(Multi_create_file_list[idx].flags & Multi_create_filter){
5341 // if we found the item
5342 if(select_index < 0){
5343 strcpy(filename,Multi_create_file_list[idx].filename);
5348 // look through the campaign list
5349 else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
5350 for(idx=0;idx<Multi_create_campaign_count;idx++){
5353 // if we found the item
5354 if(select_index < 0){
5355 strcpy(filename,Multi_create_file_list[idx].filename);
5361 strcpy(filename,"");
5364 int multi_create_select_to_index(int select_index)
5367 int lookup_index = 0;
5369 // look through the mission list
5370 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5371 for(idx=0;idx<Multi_create_mission_count;idx++){
5372 if(Multi_create_file_list[idx].flags & Multi_create_filter){
5376 // if we found the item
5377 if(select_index < lookup_index){
5382 // look through the campaign list
5383 else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
5384 for(idx=0;idx<Multi_create_campaign_count;idx++){
5387 // if we found the item
5388 if(select_index < 0){
5397 int multi_create_ok_to_commit()
5399 int player_count, observer_count, idx;
5400 int notify_of_hacked_ships_tbl = 0;
5401 int notify_of_hacked_weapons_tbl = 0;
5402 char err_string[255];
5406 // make sure we have a valid mission selected
5407 if(Multi_create_list_select < 0){
5411 // if this is not a valid mission, let the player know
5412 abs_index = multi_create_select_to_index(Multi_create_list_select);
5417 // if we're playing with a hacked ships.tbl (on PXO)
5418 notify_of_hacked_ships_tbl = 0;
5419 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5420 if(!Game_ships_tbl_valid){
5421 notify_of_hacked_ships_tbl = 1;
5424 if(Netgame.flags & NG_FLAG_HACKED_SHIPS_TBL){
5425 notify_of_hacked_ships_tbl = 1;
5428 if(!MULTI_IS_TRACKER_GAME){
5429 notify_of_hacked_ships_tbl = 0;
5431 if(notify_of_hacked_ships_tbl){
5432 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){
5437 // if we're playing with a hacked weapons.tbl (on PXO)
5438 notify_of_hacked_weapons_tbl = 0;
5439 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5440 if(!Game_weapons_tbl_valid){
5441 notify_of_hacked_weapons_tbl = 1;
5444 if(Netgame.flags & NG_FLAG_HACKED_WEAPONS_TBL){
5445 notify_of_hacked_weapons_tbl = 1;
5448 if(!MULTI_IS_TRACKER_GAME){
5449 notify_of_hacked_weapons_tbl = 0;
5451 if(notify_of_hacked_weapons_tbl){
5452 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){
5457 // if any of the players have hacked data
5459 for(idx=0; idx<MAX_PLAYERS; idx++){
5460 // look for hacked players
5461 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAXOR)){
5465 // message everyone - haha
5466 if(Net_players[idx].player != NULL){
5467 sprintf(err_string, "%s %s", Net_players[idx].player->callsign, XSTR("has hacked tables/data", 1271));
5469 sprintf(err_string, "somebody %s", XSTR("has hacked tables/data", 1271));
5471 send_game_chat_packet(Net_player, err_string, MULTI_MSG_ALL, NULL, NULL, 1);
5474 // if we found a hacked set of data
5477 if(MULTI_IS_TRACKER_GAME){
5478 // don't allow squad war matches to continue
5479 if(Netgame.type_flags & NG_TYPE_SW){
5481 // if this is squad war, don't allow it to continue
5482 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));
5487 // otherwise, warn the players that stats will not saved
5489 // if this is squad war, don't allow it to continue
5490 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){
5495 // non-pxo, just give a notice
5497 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){
5503 // check to see that we don't have too many observers
5504 observer_count = multi_num_observers();
5505 if(observer_count > Netgame.options.max_observers){
5506 // print up the error string
5507 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);
5509 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, err_string);
5513 // check to see that we have a valid # of players for the the # of ships in the game
5514 player_count = multi_num_players();
5515 if(player_count > Netgame.max_players){
5516 // print up the error string
5517 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);
5519 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, err_string);
5523 // check to see if teams are assigned properly in a team vs. team situation
5524 if(Netgame.type_flags & NG_TYPE_TEAM){
5525 if(!multi_team_ok_to_commit()){
5526 gamesnd_play_iface(SND_GENERAL_FAIL);
5527 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("Teams and/or team captains are not assigned properly", 793));
5533 if(!multi_create_verify_cds()){
5534 gamesnd_play_iface(SND_GENERAL_FAIL);
5536 #ifdef MULTIPLAYER_BETA_BUILD
5537 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, "You need 1 CD for every player!");
5539 #ifdef DVD_MESSAGE_HACK
5540 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You need 1 DVD for every 4 players!", 794));
5542 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You need 1 CD for every 4 players!", 794));
5548 // if we're playing on the tracker
5549 if(MULTI_IS_TRACKER_GAME){
5550 #ifdef PXO_CHECK_VALID_MISSIONS
5551 if((Multi_create_file_list == Multi_create_mission_list) && (Multi_create_file_list[abs_index].valid_status != MVALID_STATUS_VALID)){
5552 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){
5559 if(!(Netgame.type_flags & NG_TYPE_SW)){
5560 // if he is playing by himself, tell him stats will not be accepted
5561 if(multi_num_players() == 1){
5562 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){
5575 int multi_create_verify_cds()
5577 int player_count = multi_num_players();
5581 // count how many cds we have
5583 for(idx=0;idx<MAX_PLAYERS;idx++){
5584 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAS_CD)){
5589 // for the beta, everyone must have a CD
5590 #ifdef MULTIPLAYER_BETA_BUILD
5591 if(multi_cd_count < player_count){
5595 // determine if we have enough
5596 float ratio = (float)player_count / (float)multi_cd_count;
5597 // greater than a 4 to 1 ratio
5603 // we meet the conditions
5607 // returns an index into Multi_create_mission_list
5608 int multi_create_lookup_mission(char *fname)
5612 for(idx=0; idx<Multi_create_mission_count; idx++){
5613 if(!stricmp(fname, Multi_create_mission_list[idx].filename)){
5618 // couldn't find the mission
5622 // returns an index into Multi_create_campaign_list
5623 int multi_create_lookup_campaign(char *fname)
5627 for(idx=0; idx<Multi_create_campaign_count; idx++){
5628 if(!stricmp(fname, Multi_create_campaign_list[idx].filename)){
5633 // couldn't find the campaign
5637 void multi_create_refresh_pxo()
5639 // delete mvalid.cfg if it exists
5640 cf_delete(MULTI_VALID_MISSION_FILE, CF_TYPE_DATA);
5642 // refresh missions from the tracker
5643 multi_update_valid_missions();
5646 void multi_create_sw_clicked()
5648 netgame_info ng_temp;
5651 int file_index = multi_create_select_to_index(Multi_create_list_select);
5653 // either a temporary netgame or the real one
5654 if(MULTIPLAYER_MASTER){
5661 // maybe switch squad war off
5662 if(!Multi_create_sw_checkbox.checked()){
5663 // if the mission selected is a coop mission, go back to coop mode
5664 Assert(file_index != -1);
5665 if(file_index == -1){
5666 ng->type_flags = NG_TYPE_COOP;
5668 if(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_TEAMS){
5669 ng->type_flags = NG_TYPE_TVT;
5670 } else if(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_DOGFIGHT){
5671 ng->type_flags = NG_TYPE_DOGFIGHT;
5673 ng->type_flags = NG_TYPE_COOP;
5676 // switch squad war on
5678 Assert(file_index != -1);
5679 if((file_index == -1) || !(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_TEAMS)){
5680 Multi_create_sw_checkbox.set_state(0);
5682 // at this point we know its safe to switch squad war on
5683 ng->type_flags = NG_TYPE_SW;
5688 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5690 send_netgame_update_packet();
5692 // update all machines about stuff like respawns, etc.
5693 multi_options_update_netgame();
5695 // on the standalone
5697 // standalone will take care of polling the usertracker
5698 multi_options_update_mission(ng, Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ? 1 : 0);
5703 // -------------------------------------------------------------------------------------------------------------
5705 // MULTIPLAYER HOST OPTIONS SCREEN
5708 #define MULTI_HO_NUM_BUTTONS 12
5709 #define MULTI_HO_NUM_RADIO_BUTTONS 10
5713 #define MULTI_HO_PALETTE "InterfacePalette"
5715 static char *Multi_ho_bitmap_fname[GR_NUM_RESOLUTIONS] = {
5716 "MultiHost", // GR_640
5717 "2_MultiHost" // GR_1024
5720 static char *Multi_ho_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
5721 "MultiHost-M", // GR_640
5722 "2_MultiHost-M" // GR_1024
5726 UI_WINDOW Multi_ho_window; // the window object for the join screen
5727 UI_INPUTBOX Multi_ho_respawns; // the # of respawns allowed in the game
5728 UI_INPUTBOX Multi_ho_time_limit; // mission time limit
5729 UI_INPUTBOX Multi_ho_voice_wait; // wait time between tokens
5730 UI_INPUTBOX Multi_ho_kill_limit; // kill limit in a furball mission
5731 UI_INPUTBOX Multi_ho_obs; // # of observers we'll allow
5732 int Multi_ho_bitmap; // the background bitmap
5734 // constants for coordinate lookup
5735 #define MULTI_HO_X_COORD 0
5736 #define MULTI_HO_Y_COORD 1
5737 #define MULTI_HO_W_COORD 2
5738 #define MULTI_HO_H_COORD 3
5739 #define MULTI_HO_TEXT_X_COORD 4
5740 #define MULTI_HO_TEXT_Y_COORD 5
5743 #define MULTI_HO_MSG_RANK 0 // highest ranking players can do messaging
5744 #define MULTI_HO_MSG_LEADER 1 // wing/team leaders can do messaging
5745 #define MULTI_HO_MSG_ANY 2 // any player can do messaging
5746 #define MULTI_HO_MSG_HOST 3 // only the host can do messaging
5747 #define MULTI_HO_END_RANK 4 // highest rank can and host can end mission
5748 #define MULTI_HO_END_LEADER 5 // wing/team leaders and host can end the mission
5749 #define MULTI_HO_END_ANY 6 // any player can end the mission
5750 #define MULTI_HO_END_HOST 7 // only host can end the mission
5751 #define MULTI_HO_VOICE_ON 8 // voice toggled on
5752 #define MULTI_HO_VOICE_OFF 9 // voice toggled off
5753 #define MULTI_HO_HOST_MODIFIES 10 // only the host or team captains can modify ships/weapons in briefing
5754 #define MULTI_HO_ACCEPT 11 // accept button
5756 ui_button_info Multi_ho_buttons[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_BUTTONS] = {
5758 // who is allowed to message
5759 ui_button_info("MH_00", 3, 160, 46, 166, 0), // highest rank
5760 ui_button_info("MH_01", 3, 179, 46, 185, 1), // team/wing leader
5761 ui_button_info("MH_02", 3, 196, 46, 203, 2), // any
5762 ui_button_info("MH_03", 3, 214, 46, 220, 3), // host
5764 // who is allowed to end the mission
5765 ui_button_info("MH_04", 3, 257, 46, 265, 4), // highest rank
5766 ui_button_info("MH_05", 3, 276, 46, 283, 5), // team/wing leader
5767 ui_button_info("MH_06", 3, 294, 46, 300, 6), // any
5768 ui_button_info("MH_07", 3, 311, 46, 317, 7), // host
5770 // voice on/off button
5771 ui_button_info("MH_09", 542, 158, 545, 185, 9),
5772 ui_button_info("MH_10", 598, 158, 604, 185, 10),
5774 // host modifies ships
5775 ui_button_info("MH_13", 542, 377, 437, 363, 13),
5778 ui_button_info("MH_14", 572, 428, 580, 414, 14),
5781 // who is allowed to message
5782 ui_button_info("2_MH_00", 5, 256, 73, 269, 0), // highest rank
5783 ui_button_info("2_MH_01", 5, 286, 73, 297, 1), // team/wing leader
5784 ui_button_info("2_MH_02", 5, 314, 73, 325, 2), // any
5785 ui_button_info("2_MH_03", 5, 341, 73, 352, 3), // host
5787 // who is allowed to end the mission
5788 ui_button_info("2_MH_04", 5, 412, 73, 425, 4), // highest rank
5789 ui_button_info("2_MH_05", 5, 442, 73, 452, 5), // team/wing leader
5790 ui_button_info("2_MH_06", 5, 470, 73, 480, 6), // any
5791 ui_button_info("2_MH_07", 5, 497, 73, 508, 7), // host
5793 // voice on/off button
5794 ui_button_info("2_MH_09", 867, 253, 872, 296, 9),
5795 ui_button_info("2_MH_10", 957, 253, 966, 296, 10),
5797 // host modifies ships
5798 ui_button_info("2_MH_13", 867, 603, 784, 581, 13),
5801 ui_button_info("2_MH_14", 916, 685, 925, 665, 14),
5804 UI_XSTR Multi_ho_text[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_BUTTONS] = {
5806 {"Highest rank", 1280, 46, 166, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_RANK].button},
5807 {"Team / wing-leader", 1281, 46, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_LEADER].button},
5808 {"Any", 1282, 46, 203, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_ANY].button},
5809 {"Host", 1283, 46, 220, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_HOST].button},
5810 {"Highest rank", 1280, 46, 265, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_RANK].button},
5811 {"Team / wing-leader", 1281, 46, 283, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_LEADER].button},
5812 {"Any", 1282, 46, 300, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_ANY].button},
5813 {"Host", 1283, 46, 317, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_HOST].button},
5814 {"On", 1285, 545, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_VOICE_ON].button},
5815 {"Off", 1286, 604, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_VOICE_OFF].button},
5816 {"Host modifies ships", 1287, 437, 363, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_HOST_MODIFIES].button},
5817 {"Exit", 1417, 572, 418, UI_XSTR_COLOR_PINK, -1, &Multi_ho_buttons[0][MULTI_HO_ACCEPT].button},
5820 {"Highest rank", 1280, 62, 269, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_RANK].button},
5821 {"Team / wing-leader", 1281, 62, 297, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_LEADER].button},
5822 {"Any", 1282, 62, 325, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_ANY].button},
5823 {"Host", 1283, 62, 352, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_HOST].button},
5824 {"Highest rank", 1280, 62, 425, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_RANK].button},
5825 {"Team / wing-leader", 1281, 62, 452, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_LEADER].button},
5826 {"Any", 1282, 62, 480, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_ANY].button},
5827 {"Host", 1283, 62, 508, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_HOST].button},
5828 {"On", 1285, 877, 294, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_VOICE_ON].button},
5829 {"Off", 1286, 967, 293, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_VOICE_OFF].button},
5830 {"Host modifies ships", 1287, 869, 589, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_HOST_MODIFIES].button},
5831 {"Exit", 1417, 953, 672, UI_XSTR_COLOR_PINK, -1, &Multi_ho_buttons[1][MULTI_HO_ACCEPT].button},
5835 // radio button controls
5836 #define MULTI_HO_NUM_RADIO_GROUPS 3
5837 #define MULTI_HO_MSG_GROUP 0 // group dealing with squadmate messaging
5838 #define MULTI_HO_END_GROUP 1 // group dealing with ending the mission
5839 #define MULTI_HO_VOICE_GROUP 2 // group dealing with voice stuff
5840 int Multi_ho_radio_groups[MULTI_HO_NUM_RADIO_GROUPS] = { // currently selected button in the radio button group
5843 int Multi_ho_radio_info[MULTI_HO_NUM_RADIO_BUTTONS][3] = { // info related to each of the radio buttons themselves
5844 // { group #, value, button id# }
5845 {0, 0, 0}, // highest ranking players can do messaging
5846 {0, 1, 1}, // wing/team leaders can do messaging
5847 {0, 2, 2}, // any player can do messaging
5848 {0, 3, 3}, // only host can do messaging
5849 {1, 0, 4}, // highest rank and host can end the mission
5850 {1, 1, 5}, // team/wing leader can end the mission
5851 {1, 2, 6}, // any player can end the mission
5852 {1, 3, 7}, // only the host can end the mission
5853 {2, 0, 8}, // voice toggled on
5854 {2, 1, 9} // voice toggled off
5858 #define MULTI_HO_NUM_SLIDERS 3
5859 #define MULTI_HO_SLIDER_VOICE_QOS 0 // voice quality of sound
5860 #define MULTI_HO_SLIDER_VOICE_DUR 1 // max duration of voice recording
5861 #define MULTI_HO_SLIDER_SKILL 2 // skill level
5868 UI_DOT_SLIDER_NEW slider; // because we have a class inside this struct, we need the constructor below..
5870 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){}
5872 ho_sliders Multi_ho_sliders[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_SLIDERS] = {
5874 ho_sliders("MH_11", 428, 214, 437, 199, 11, 19, 10), // voice qos
5875 ho_sliders("MH_12", 428, 261, 437, 246, 12, 19, 10), // voice duration
5876 ho_sliders("MH_08", 237, 454, 230, 411, 8, 36, 5), // skill level
5879 ho_sliders("2_MH_11", 684, 343, 690, 323, 11, 32, 10), // voice qos
5880 ho_sliders("2_MH_12", 685, 418, 837, 468, 12, 32, 10), // voice duration
5881 ho_sliders("2_MH_08", 379, 727, 369, 663, 8, 60, 5), // skill level
5885 int Multi_ho_mission_respawn;
5887 int Multi_ho_host_modifies;
5889 // whether or not any of the inputboxes on this screen had focus last frame
5890 int Multi_ho_lastframe_input = 0;
5892 // game information text areas
5895 #define MULTI_HO_NUM_TITLES 14
5896 UI_XSTR Multi_ho_titles[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_TITLES] = {
5898 { "AI Orders", 1289, 32, 144, UI_XSTR_COLOR_GREEN, -1, NULL },
5899 { "End Mission", 1290, 32, 242, UI_XSTR_COLOR_GREEN, -1, NULL },
5900 { "Time Limit", 1291, 32, 347, UI_XSTR_COLOR_GREEN, -1, NULL },
5901 { "Min", 1292, 74, 362, UI_XSTR_COLOR_GREEN, -1, NULL },
5902 { "Respawn Limit", 1288, 32, 378, UI_XSTR_COLOR_GREEN, -1, NULL },
5903 { "Kill Limit", 1293, 32, 409, UI_XSTR_COLOR_GREEN, -1, NULL },
5904 { "Observers", 1294, 32, 441, UI_XSTR_COLOR_GREEN, -1, NULL },
5905 { "Skill Level", 1284, 230, 411, UI_XSTR_COLOR_GREEN, -1, NULL },
5906 { "Voice Transmission", 1295, 437, 144, UI_XSTR_COLOR_GREEN, -1, NULL },
5907 { "Voice Quality", 1296, 437, 199, UI_XSTR_COLOR_GREEN, -1, NULL },
5908 { "Message Duration", 1297, 437, 246, UI_XSTR_COLOR_GREEN, -1, NULL },
5909 { "sec", 1522, 523, 292, UI_XSTR_COLOR_GREEN, -1, NULL },
5910 { "sec", 1523, 523, 332, UI_XSTR_COLOR_GREEN, -1, NULL },
5911 { "Voice Wait", 1298, 437, 313, UI_XSTR_COLOR_GREEN, -1, NULL },
5914 { "AI Orders", 1289, 48, 238, UI_XSTR_COLOR_GREEN, -1, NULL },
5915 { "End Mission", 1290, 48, 394, UI_XSTR_COLOR_GREEN, -1, NULL },
5916 { "Time Limit", 1291, 50, 568, UI_XSTR_COLOR_GREEN, -1, NULL },
5917 { "Min", 1292, 119, 581, UI_XSTR_COLOR_GREEN, -1, NULL },
5918 { "Respawn Limit", 1288, 50, 618, UI_XSTR_COLOR_GREEN, -1, NULL },
5919 { "Kill Limit", 1293, 50, 668, UI_XSTR_COLOR_GREEN, -1, NULL },
5920 { "Observers", 1294, 50, 718, UI_XSTR_COLOR_GREEN, -1, NULL },
5921 { "Skill Level", 1284, 398, 670, UI_XSTR_COLOR_GREEN, -1, NULL },
5922 { "Voice Transmission", 1295, 869, 239, UI_XSTR_COLOR_GREEN, -1, NULL },
5923 { "Voice Quality", 1296, 690, 331, UI_XSTR_COLOR_GREEN, -1, NULL },
5924 { "Message Duration", 1297, 690, 405, UI_XSTR_COLOR_GREEN, -1, NULL },
5925 { "sec", 1522, 837, 467, UI_XSTR_COLOR_GREEN, -1, NULL },
5926 { "sec", 1523, 837, 534, UI_XSTR_COLOR_GREEN, -1, NULL },
5927 { "Voice Wait", 1298, 742, 510, UI_XSTR_COLOR_GREEN, -1, NULL },
5931 // mission time limit input box
5932 int Ho_time_coords[GR_NUM_RESOLUTIONS][4] = {
5941 // furball kill limit input box
5942 int Ho_kill_coords[GR_NUM_RESOLUTIONS][4] = {
5951 // voice recording duration text display area
5952 int Ho_vd_coords[GR_NUM_RESOLUTIONS][4] = {
5961 // voice token wait input box
5962 int Ho_vw_coords[GR_NUM_RESOLUTIONS][6] = {
5971 // observer count input box
5972 int Ho_obs_coords[GR_NUM_RESOLUTIONS][4] = {
5981 // skill text description area
5982 int Ho_st_coords[GR_NUM_RESOLUTIONS][4] = {
5991 // respawn input box
5992 int Ho_rsp_coords[GR_NUM_RESOLUTIONS][6] = {
6001 // respawn max text area
6002 int Ho_max_rsp_coords[GR_NUM_RESOLUTIONS][2] = {
6011 // maximum values for various input boxes (to notify user of overruns)
6012 #define MULTI_HO_MAX_TIME_LIMIT 500
6013 #define MULTI_HO_MAX_TOKEN_WAIT 5
6014 #define MULTI_HO_MAX_KILL_LIMIT 9999
6015 #define MULTI_HO_MAX_OBS 4
6017 // LOCAL function definitions
6018 void multi_ho_check_buttons();
6019 void multi_ho_button_pressed(int n);
6020 void multi_ho_draw_radio_groups();
6021 void multi_ho_accept_hit();
6022 void multi_ho_get_options();
6023 void multi_ho_apply_options();
6024 void multi_ho_display_record_time();
6025 int multi_ho_check_values();
6026 void multi_ho_check_focus();
6027 void multi_ho_blit_max_respawns();
6028 void multi_ho_display_skill_level();
6030 void multi_host_options_init()
6034 // create the interface window
6035 Multi_ho_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
6036 Multi_ho_window.set_mask_bmap(Multi_ho_bitmap_mask_fname[gr_screen.res]);
6038 // load the background bitmap
6039 Multi_ho_bitmap = bm_load(Multi_ho_bitmap_fname[gr_screen.res]);
6040 if(Multi_ho_bitmap < 0){
6041 // we failed to load the bitmap - this is very bad
6045 // initialize the common notification messaging
6046 multi_common_notify_init();
6048 // use the common interface palette
6049 multi_common_set_palette();
6051 // create the interface buttons
6052 for(idx=0;idx<MULTI_HO_NUM_BUTTONS;idx++){
6053 // create the object
6054 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);
6056 // set the sound to play when highlighted
6057 Multi_ho_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
6059 // set the ani for the button
6060 Multi_ho_buttons[gr_screen.res][idx].button.set_bmaps(Multi_ho_buttons[gr_screen.res][idx].filename);
6062 // set the hotspot, ignoring the skill level button
6063 Multi_ho_buttons[gr_screen.res][idx].button.link_hotspot(Multi_ho_buttons[gr_screen.res][idx].hotspot);
6066 Multi_ho_window.add_XSTR(&Multi_ho_text[gr_screen.res][idx]);
6070 for(idx=0; idx<MULTI_HO_NUM_TITLES; idx++){
6071 Multi_ho_window.add_XSTR(&Multi_ho_titles[gr_screen.res][idx]);
6074 // create the interface sliders
6075 for(idx=0; idx<MULTI_HO_NUM_SLIDERS; idx++){
6076 // create the object
6077 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);
6080 // create the respawn count input box
6081 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);
6082 // if we're in campaign mode, disable it
6083 if(Netgame.campaign_mode == MP_CAMPAIGN){
6084 Multi_ho_respawns.set_text(XSTR("NA",795)); // [[ Not applicable ]]
6085 Multi_ho_respawns.disable();
6087 Multi_ho_respawns.set_valid_chars("0123456789");
6090 // create the time limit input box
6091 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);
6092 Multi_ho_time_limit.set_valid_chars("-0123456789");
6094 // create the voice token wait input box
6095 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);
6096 Multi_ho_voice_wait.set_valid_chars("01243456789");
6098 // create the furball kill limit input box
6099 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);
6100 Multi_ho_kill_limit.set_valid_chars("0123456789");
6102 // create the observer limit input box
6103 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);
6104 Multi_ho_obs.set_valid_chars("01234");
6106 // load in the current netgame defaults
6107 multi_ho_get_options();
6109 // whether or not any of the inputboxes on this screen had focus last frame
6110 Multi_ho_lastframe_input = 0;
6112 // get the # of respawns for the currently selected mission (if any)
6113 if(Multi_create_list_select != -1){
6114 int abs_index = multi_create_select_to_index(Multi_create_list_select);
6116 // if he has a valid mission selected
6118 Multi_ho_mission_respawn = (int)Multi_create_file_list[abs_index].respawn;
6120 Multi_ho_mission_respawn = -1;
6123 Multi_ho_mission_respawn = -1;
6127 void multi_ho_update_sliders()
6129 // game skill slider
6130 if (Game_skill_level != Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos) {
6131 if ( !(Netgame.type_flags & NG_TYPE_TEAM) ){
6132 Game_skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6133 gamesnd_play_iface(SND_USER_SELECT);
6135 Game_skill_level = NUM_SKILL_LEVELS / 2;
6139 // get the voice qos options
6140 if (Netgame.options.voice_qos != (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1)) {
6141 Netgame.options.voice_qos = (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1);
6142 gamesnd_play_iface(SND_USER_SELECT);
6145 // get the voice duration options
6146 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)) {
6147 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);
6148 gamesnd_play_iface(SND_USER_SELECT);
6153 void multi_host_options_do()
6158 k = Multi_ho_window.process();
6161 // process any keypresses
6164 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
6167 case KEY_CTRLED + KEY_ENTER :
6168 gamesnd_play_iface(SND_COMMIT_PRESSED);
6169 multi_ho_accept_hit();
6173 // process any button clicks
6174 multi_ho_check_buttons();
6176 // update the sliders
6177 multi_ho_update_sliders();
6179 // make sure that the chatbox inputbox and any inputbox on this screen are mutually exclusive in terms of focus
6180 multi_ho_check_focus();
6182 // draw the background, etc
6184 GR_MAYBE_CLEAR_RES(Multi_ho_bitmap);
6185 if(Multi_ho_bitmap != -1){
6186 gr_set_bitmap(Multi_ho_bitmap);
6189 Multi_ho_window.draw();
6191 // draw all the radio buttons properly
6192 multi_ho_draw_radio_groups();
6194 // display any pending notification messages
6195 multi_common_notify_do();
6197 // display the voice record time settings
6198 multi_ho_display_record_time();
6200 // maybe display the max # of respawns next to the respawn input box
6201 multi_ho_blit_max_respawns();
6203 // blit the proper skill level
6204 multi_ho_display_skill_level();
6206 // blit the "host modifies button"
6207 if(Multi_ho_host_modifies){
6208 Multi_ho_buttons[gr_screen.res][MULTI_HO_HOST_MODIFIES].button.draw_forced(2);
6211 // process and show the chatbox thingie
6215 Multi_ho_window.draw_tooltip();
6217 // display the voice status indicator
6218 multi_common_voice_display_status();
6224 void multi_host_options_close()
6226 // unload any bitmaps
6227 if(!bm_unload(Multi_ho_bitmap)){
6228 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_ho_bitmap_fname[gr_screen.res]));
6231 // destroy the UI_WINDOW
6232 Multi_ho_window.destroy();
6235 void multi_ho_check_buttons()
6238 for(idx=0;idx<MULTI_HO_NUM_BUTTONS;idx++){
6239 // we only really need to check for one button pressed at a time, so we can break after
6241 if(Multi_ho_buttons[gr_screen.res][idx].button.pressed()){
6242 multi_ho_button_pressed(idx);
6248 void multi_ho_button_pressed(int n)
6250 int radio_index,idx;
6251 int x_pixel,y_pixel;
6253 // get the pixel position of the click
6254 Multi_ho_buttons[gr_screen.res][n].button.get_mouse_pos(&x_pixel,&y_pixel);
6257 // clicked on the accept button
6258 case MULTI_HO_ACCEPT:
6259 gamesnd_play_iface(SND_COMMIT_PRESSED);
6260 multi_ho_accept_hit();
6263 // clicked on the host/captains only modify button
6264 case MULTI_HO_HOST_MODIFIES:
6265 // toggle it on or off
6266 Multi_ho_host_modifies = !Multi_ho_host_modifies;
6267 gamesnd_play_iface(SND_USER_SELECT);
6271 // look through the radio buttons and see which one this corresponds to
6273 for(idx=0;idx<MULTI_HO_NUM_RADIO_BUTTONS;idx++){
6274 if(Multi_ho_radio_info[idx][2] == n){
6279 Assert(radio_index != -1);
6281 // check to see if a radio button was pressed
6282 if(radio_index < MULTI_HO_NUM_RADIO_BUTTONS){
6283 // see if this value is already picked for this radio group
6284 if(Multi_ho_radio_groups[Multi_ho_radio_info[radio_index][0]] != Multi_ho_radio_info[radio_index][1]){
6285 gamesnd_play_iface(SND_USER_SELECT);
6286 Multi_ho_radio_groups[Multi_ho_radio_info[radio_index][0]] = Multi_ho_radio_info[radio_index][1];
6288 gamesnd_play_iface(SND_GENERAL_FAIL);
6293 void multi_ho_draw_radio_groups()
6297 // go through each item and draw it if it is the selected button in its respective group
6298 for(idx=0;idx<MULTI_HO_NUM_RADIO_BUTTONS;idx++){
6299 /// if this button is the currently selected one in its group
6300 if(Multi_ho_radio_info[idx][1] == Multi_ho_radio_groups[Multi_ho_radio_info[idx][0]]){
6301 Multi_ho_buttons[gr_screen.res][Multi_ho_radio_info[idx][2]].button.draw_forced(2);
6306 void multi_ho_accept_hit()
6310 // check the values in the input boxes
6311 if(!multi_ho_check_values()){
6315 // zero out the netgame flags
6318 // set default options
6319 Netgame.options.flags = (MSO_FLAG_INGAME_XFER | MSO_FLAG_ACCEPT_PIX);
6321 // set the squadmate messaging flags
6322 switch(Multi_ho_radio_groups[MULTI_HO_MSG_GROUP]){
6324 Netgame.options.squad_set = MSO_SQUAD_RANK;
6327 Netgame.options.squad_set = MSO_SQUAD_LEADER;
6330 Netgame.options.squad_set = MSO_SQUAD_ANY;
6333 Netgame.options.squad_set = MSO_SQUAD_HOST;
6339 // set the end mission flags
6340 switch(Multi_ho_radio_groups[MULTI_HO_END_GROUP]){
6342 Netgame.options.endgame_set = MSO_END_RANK;
6345 Netgame.options.endgame_set = MSO_END_LEADER;
6348 Netgame.options.endgame_set = MSO_END_ANY;
6351 Netgame.options.endgame_set = MSO_END_HOST;
6357 // set the voice toggle
6358 switch(Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP]){
6360 Netgame.options.flags &= ~(MSO_FLAG_NO_VOICE);
6363 Netgame.options.flags |= MSO_FLAG_NO_VOICE;
6369 // get the voice qos options
6370 Netgame.options.voice_qos = (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1);
6372 // get the voice duration options
6373 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);
6375 // set the skill level. If in team vs. team mode, preserve the old setting before saving
6376 // the pilot file. I'll bet that this doesn't work though because the pilot file gets
6377 // written in a bunch of locations....sigh.
6378 if ( !(Netgame.type_flags & NG_TYPE_TEAM) ){
6379 Game_skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6381 Game_skill_level = NUM_SKILL_LEVELS / 2;
6384 // set the netgame respawn count
6385 // maybe warn the user that respawns will not be used for a campaign mission
6386 if(Netgame.campaign_mode == MP_SINGLE){
6387 Multi_ho_respawns.get_text(resp_str);
6388 uint temp_respawn = (uint)atoi(resp_str);
6389 // if he currently has no mission selected, let the user set any # of respawns
6390 if((int)temp_respawn > Multi_ho_mission_respawn){
6391 if(Multi_ho_mission_respawn == -1){
6392 Netgame.respawn = temp_respawn;
6393 Netgame.options.respawn = temp_respawn;
6395 // this should have been taken care of by the interface code
6400 Netgame.options.respawn = temp_respawn;
6401 Netgame.respawn = temp_respawn;
6405 // get the mission time limit
6406 Multi_ho_time_limit.get_text(resp_str);
6407 int temp_time = atoi(resp_str);
6409 Netgame.options.mission_time_limit = fl2f(-1.0f);
6410 } else if(temp_time > MULTI_HO_MAX_TIME_LIMIT){
6413 Netgame.options.mission_time_limit = fl2f(60.0f * (float)temp_time);
6416 // get observer count options
6417 Multi_ho_obs.get_text(resp_str);
6418 int temp_obs = atoi(resp_str);
6419 if(temp_obs > MULTI_HO_MAX_OBS){
6422 Netgame.options.max_observers = (ubyte)temp_obs;
6424 // get the furball kill limit
6425 Multi_ho_kill_limit.get_text(resp_str);
6426 int temp_kills = atoi(resp_str);
6427 if(temp_kills > MULTI_HO_MAX_KILL_LIMIT){
6430 Netgame.options.kill_limit = temp_kills;
6432 // get the token wait limit
6433 Multi_ho_voice_wait.get_text(resp_str);
6434 int temp_wait = atoi(resp_str);
6435 if(temp_wait > MULTI_HO_MAX_TOKEN_WAIT){
6438 Netgame.options.voice_token_wait = (temp_wait * 1000);
6440 // set the netgame option
6441 Netgame.options.skill_level = (ubyte)Game_skill_level;
6443 // get whether we're in host/captains only modify mode
6444 Netgame.options.flags &= ~(MSO_FLAG_SS_LEADERS);
6445 if(Multi_ho_host_modifies){
6446 Netgame.options.flags |= MSO_FLAG_SS_LEADERS;
6449 // store these values locally
6450 memcpy(&Player->m_local_options,&Net_player->p_info.options,sizeof(multi_local_options));
6451 memcpy(&Player->m_server_options,&Netgame.options,sizeof(multi_server_options));
6452 write_pilot_file(Player);
6454 // apply any changes in settings (notify everyone of voice qos changes, etc)
6455 multi_ho_apply_options();
6457 // move back to the create game screen
6458 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
6461 void multi_ho_get_options()
6465 // set the squadmate messaging buttons
6466 switch(Netgame.options.squad_set){
6467 case MSO_SQUAD_RANK :
6468 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 0;
6470 case MSO_SQUAD_LEADER:
6471 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 1;
6474 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 2;
6476 case MSO_SQUAD_HOST:
6477 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 3;
6483 // set the mission end buttons
6484 switch(Netgame.options.endgame_set){
6486 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 0;
6488 case MSO_END_LEADER:
6489 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 1;
6492 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 2;
6495 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 3;
6501 // set the voice toggle buttons
6502 if(Netgame.options.flags & MSO_FLAG_NO_VOICE){
6503 Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP] = 1;
6505 Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP] = 0;
6508 // get the voice qos options
6509 Assert((Netgame.options.voice_qos >= 1) && (Netgame.options.voice_qos <= 10));
6510 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos = (Netgame.options.voice_qos - 1);
6512 // get the voice duration options
6513 Assert((Netgame.options.voice_record_time > 0) && (Netgame.options.voice_record_time <= MULTI_VOICE_MAX_TIME));
6514 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos = ((int)((float)Netgame.options.voice_record_time / 500.0f)) - 1;
6516 // get the current skill level
6517 Assert((Game_skill_level >= 0) && (Game_skill_level < NUM_SKILL_LEVELS));
6518 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos = Game_skill_level;
6520 // get the # of observers
6521 memset(resp_str,0,10);
6522 sprintf(resp_str,"%d",Netgame.options.max_observers);
6523 Multi_ho_obs.set_text(resp_str);
6525 // set the respawn count
6526 if(Netgame.campaign_mode == MP_SINGLE){
6527 memset(resp_str,0,10);
6528 sprintf(resp_str,"%d",Netgame.respawn);
6529 Multi_ho_respawns.set_text(resp_str);
6532 // set the mission time limit
6533 memset(resp_str,0,10);
6534 float tl = f2fl(Netgame.options.mission_time_limit);
6535 sprintf(resp_str,"%d",(int)(tl / 60.0f));
6536 Multi_ho_time_limit.set_text(resp_str);
6538 // set the furball kill limit
6539 memset(resp_str,0,10);
6540 sprintf(resp_str,"%d",Netgame.options.kill_limit);
6541 Multi_ho_kill_limit.set_text(resp_str);
6543 // set the token wait time
6544 memset(resp_str,0,10);
6545 sprintf(resp_str,"%d",Netgame.options.voice_token_wait / 1000);
6546 Multi_ho_voice_wait.set_text(resp_str);
6548 // get whether we're in host/captains only modify mode
6549 if(Netgame.options.flags & MSO_FLAG_SS_LEADERS){
6550 Multi_ho_host_modifies = 1;
6552 Multi_ho_host_modifies = 0;
6556 void multi_ho_apply_options()
6558 // if the voice qos or duration has changed, apply the change
6559 multi_voice_maybe_update_vars(Netgame.options.voice_qos,Netgame.options.voice_record_time);
6561 // send an options update
6562 multi_options_update_netgame();
6565 // display the voice record time settings
6566 void multi_ho_display_record_time()
6569 int full_seconds, half_seconds;
6572 memset(time_str,0,30);
6575 full_seconds = (((Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 500) / 1000);
6577 // get the half-seconds
6578 half_seconds = ((((Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 500) % 1000) / 500) * 5;
6580 // format the string
6581 sprintf(time_str,"%d.%d",full_seconds,half_seconds);
6582 gr_set_color_fast(&Color_bright);
6583 gr_string(Ho_vd_coords[gr_screen.res][MULTI_HO_X_COORD],Ho_vd_coords[gr_screen.res][MULTI_HO_Y_COORD],time_str);
6586 int multi_ho_check_values()
6590 memset(val_txt,0,255);
6592 // check against respawn settings
6593 if(Multi_ho_mission_respawn != -1){
6594 Multi_ho_respawns.get_text(val_txt);
6595 // if the value is invalid, let the user know
6596 if(atoi(val_txt) > Multi_ho_mission_respawn){
6597 memset(val_txt,0,255);
6598 sprintf(val_txt,XSTR("Warning\nRespawn count in greater than mission specified max (%d)",796),Multi_ho_mission_respawn);
6599 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6604 // check against mission time limit max
6605 Multi_ho_time_limit.get_text(val_txt);
6606 // if the value is invalid, force it to be valid
6607 if(atoi(val_txt) > MULTI_HO_MAX_TIME_LIMIT){
6608 memset(val_txt,0,255);
6609 sprintf(val_txt,XSTR("Warning\nMission time limit is greater than max allowed (%d)",797),MULTI_HO_MAX_TIME_LIMIT);
6610 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6614 // check against max observer limit
6615 Multi_ho_obs.get_text(val_txt);
6616 // if the value is invalid, force it to be valid
6617 if(atoi(val_txt) > MULTI_HO_MAX_OBS){
6618 memset(val_txt,0,255);
6619 sprintf(val_txt,XSTR("Warning\nObserver count is greater than max allowed (%d)",798),MULTI_HO_MAX_OBS);
6620 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6624 // check against furball kill limit
6625 Multi_ho_kill_limit.get_text(val_txt);
6626 // if the value is invalid, force it to be valid
6627 if(atoi(val_txt) > MULTI_HO_MAX_KILL_LIMIT){
6628 memset(val_txt,0,255);
6629 sprintf(val_txt,XSTR("Warning\nMission kill limit is greater than max allowed (%d)",799),MULTI_HO_MAX_KILL_LIMIT);
6630 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6634 // check against the token wait limit
6635 Multi_ho_voice_wait.get_text(val_txt);
6636 if(atoi(val_txt) > MULTI_HO_MAX_TOKEN_WAIT){
6637 memset(val_txt,0,255);
6638 sprintf(val_txt,XSTR("Warning\nvoice wait time is greater than max allowed (%d)",800),MULTI_HO_MAX_TOKEN_WAIT);
6639 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6643 // all values are valid
6647 void multi_ho_check_focus()
6649 // if an inputbox has been pressed (hit enter), lose its focus
6650 if (Multi_ho_respawns.pressed() || Multi_ho_time_limit.pressed() || Multi_ho_voice_wait.pressed() || Multi_ho_kill_limit.pressed() || Multi_ho_obs.pressed()) {
6651 Multi_ho_respawns.clear_focus();
6652 Multi_ho_time_limit.clear_focus();
6653 Multi_ho_voice_wait.clear_focus();
6654 Multi_ho_kill_limit.clear_focus();
6655 Multi_ho_obs.clear_focus();
6656 gamesnd_play_iface(SND_COMMIT_PRESSED);
6657 chatbox_set_focus();
6658 Multi_ho_lastframe_input = 0;
6660 } else if(!Multi_ho_lastframe_input) {
6661 // if we didn't have focus last frame
6662 if(Multi_ho_respawns.has_focus() || Multi_ho_time_limit.has_focus() || Multi_ho_kill_limit.has_focus() || Multi_ho_voice_wait.has_focus() ){
6663 chatbox_lose_focus();
6665 Multi_ho_lastframe_input = 1;
6668 // if we _did_ have focus last frame
6670 // if we no longer have focus on any of the input boxes, set the focus on the chatbox
6671 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()){
6672 chatbox_set_focus();
6674 // if the chatbox now has focus, clear all focus from our inputboxes
6675 else if (chatbox_has_focus()) {
6676 Multi_ho_respawns.clear_focus();
6677 Multi_ho_time_limit.clear_focus();
6678 Multi_ho_kill_limit.clear_focus();
6679 Multi_ho_voice_wait.clear_focus();
6681 Multi_ho_lastframe_input = 0;
6686 void multi_ho_blit_max_respawns()
6690 // if we're in campaign mode, do nothing
6691 if(Netgame.campaign_mode == MP_CAMPAIGN){
6695 // otherwise blit the max as specified by the current mission file
6696 sprintf(string,"(%d)",Multi_ho_mission_respawn);
6697 gr_set_color_fast(&Color_normal);
6698 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);
6701 void multi_ho_display_skill_level()
6703 int skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6706 Assert((skill_level >= 0) && (skill_level < NUM_SKILL_LEVELS));
6707 if((skill_level < 0) || (skill_level >= NUM_SKILL_LEVELS)){
6711 gr_set_color_fast(&Color_bright);
6712 gr_string(Ho_st_coords[gr_screen.res][0], Ho_st_coords[gr_screen.res][1], Skill_level_names(skill_level, 1));
6715 // -------------------------------------------------------------------------------------------------------------
6717 // MULTIPLAYER JOIN SCREEN
6720 #define MULTI_JW_NUM_BUTTONS 8
6724 #define MULTI_JW_PALETTE "InterfacePalette"
6726 static char *Multi_jw_bitmap_fname[GR_NUM_RESOLUTIONS] = {
6727 "MultiJoinWait", // GR_640
6728 "2_MultiJoinWait" // GR_1024
6731 static char *Multi_jw_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
6732 "MultiJoinWait-M", // GR_640
6733 "2_MultiJoinWait-M" // GR_1024
6739 #define MJW_SCROLL_PLAYERS_UP 0
6740 #define MJW_SCROLL_PLAYERS_DOWN 1
6743 #define MJW_PILOT_INFO 4
6744 #define MJW_SCROLL_INFO_UP 5
6745 #define MJW_SCROLL_INFO_DOWN 6
6746 #define MJW_CANCEL 7
6748 UI_WINDOW Multi_jw_window; // the window object for the join screen
6749 int Multi_jw_bitmap; // the background bitmap
6751 // constants for coordinate lookup
6752 #define MJW_X_COORD 0
6753 #define MJW_Y_COORD 1
6754 #define MJW_W_COORD 2
6755 #define MJW_H_COORD 3
6757 ui_button_info Multi_jw_buttons[GR_NUM_RESOLUTIONS][MULTI_JW_NUM_BUTTONS] = {
6759 ui_button_info("MJW_00", 1, 24, -1, -1, 0),
6760 ui_button_info("MJW_01", 1, 66, -1, -1, 1),
6761 ui_button_info("MJW_02", 30, 244, 20, 272, 2),
6762 ui_button_info("MJW_03", 84, 244, 73, 272, 3),
6763 ui_button_info("MJW_04", 139, 242, 134, 272, 4),
6764 ui_button_info("MJW_05", 1, 406, -1, -1, 5),
6765 ui_button_info("MJW_06", 1, 447, -1, -1, 6),
6766 ui_button_info("MJW_07", 577, 428, 570, 414, 7),
6769 ui_button_info("2_MJW_00", 2, 38, -1, -1, 0),
6770 ui_button_info("2_MJW_01", 2, 106, -1, -1, 1),
6771 ui_button_info("2_MJW_02", 48, 390, 47, 435, 2),
6772 ui_button_info("2_MJW_03", 134, 390, 133, 435, 3),
6773 ui_button_info("2_MJW_04", 223, 388, 225, 435, 4),
6774 ui_button_info("2_MJW_05", 2, 649, -1, -1, 5),
6775 ui_button_info("2_MJW_06", 2, 715, -1, -1, 6),
6776 ui_button_info("2_MJW_07", 923, 685, 931, 667, 7),
6780 #define MULTI_JW_NUM_TEXT 7
6782 UI_XSTR Multi_jw_text[GR_NUM_RESOLUTIONS][MULTI_JW_NUM_TEXT] = {
6784 { "Team 1", 1308, 20, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_TEAM0].button },
6785 { "Team 2", 1309, 73, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_TEAM1].button },
6786 { "Pilot", 1310, 134, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_PILOT_INFO].button },
6787 { "Info", 1311, 134, 283, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_PILOT_INFO].button },
6788 { "Cancel", 387, 570, 414, UI_XSTR_COLOR_PINK, -1, &Multi_jw_buttons[0][MJW_CANCEL].button },
6789 { "Players", 1269, 38, 8, UI_XSTR_COLOR_GREEN, -1, NULL },
6790 { "Choose Team", 1312, 27, 231, UI_XSTR_COLOR_GREEN, -1, NULL },
6793 { "Team 1", 1308, 47, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_TEAM0].button },
6794 { "Team 2", 1309, 133, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_TEAM1].button },
6795 { "Pilot", 1310, 225, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_PILOT_INFO].button },
6796 { "Info", 1311, 225, 446, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_PILOT_INFO].button },
6797 { "Cancel", 387, 931, 667, UI_XSTR_COLOR_PINK, -1, &Multi_jw_buttons[1][MJW_CANCEL].button },
6798 { "Players", 1269, 165, 12, UI_XSTR_COLOR_GREEN, -1, NULL },
6799 { "Choose Team", 1312, 45, 373, UI_XSTR_COLOR_GREEN, -1, NULL },
6803 int Mjw_players_coords[GR_NUM_RESOLUTIONS][4] = {
6812 int Mjw_mission_name_coords[GR_NUM_RESOLUTIONS][2] = {
6821 // squad war checkbox
6822 UI_CHECKBOX Multi_jw_sw_checkbox;
6823 char *Multi_jw_sw_checkbox_fname[GR_NUM_RESOLUTIONS] = {
6827 int Multi_jw_sw_checkbox_coords[GR_NUM_RESOLUTIONS][2] = {
6835 int Multi_jw_sw_checkbox_text[GR_NUM_RESOLUTIONS][2] = {
6845 // player list control thingie defs
6846 #define MULTI_JW_PLIST_MAX_DISPLAY 19
6847 int Multi_jw_plist_select_flag; // indicates whether we currently have a selected player
6848 short Multi_jw_plist_select_id; // id of the current selected player
6849 UI_BUTTON Multi_jw_plist_select_button; // for selecting a player
6851 int Multi_jw_should_show_popup = 0;
6853 // LOCAL function definitions
6854 void multi_jw_check_buttons();
6855 void multi_jw_button_pressed(int n);
6856 void multi_jw_do_netstuff();
6857 void multi_jw_scroll_players_up();
6858 void multi_jw_scroll_players_down();
6859 void multi_jw_plist_process();
6860 void multi_jw_plist_blit_normal();
6861 void multi_jw_plist_blit_team();
6862 short multi_jw_get_mouse_id();
6864 void multi_game_client_setup_init()
6868 // create the interface window
6869 Multi_jw_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
6870 Multi_jw_window.set_mask_bmap(Multi_jw_bitmap_mask_fname[gr_screen.res]);
6872 // load the background bitmap
6873 Multi_jw_bitmap = bm_load(Multi_jw_bitmap_fname[gr_screen.res]);
6874 if(Multi_jw_bitmap < 0){
6875 // we failed to load the bitmap - this is very bad
6879 // initialize the player list data
6880 Multi_jw_plist_select_flag = 0;
6881 Multi_jw_plist_select_id = -1;
6883 // kill any old instances of the chatbox and create a new one
6885 chatbox_create(CHATBOX_FLAG_BIG | CHATBOX_FLAG_DRAW_BOX | CHATBOX_FLAG_BUTTONS);
6887 // initialize the common notification messaging
6888 multi_common_notify_init();
6890 // initialize the common mission info display area.
6891 multi_common_set_text("");
6893 // use the common interface palette
6894 multi_common_set_palette();
6896 // create the interface buttons
6897 for(idx=0; idx<MULTI_JW_NUM_BUTTONS; idx++){
6898 // create the object
6899 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);
6901 // set the sound to play when highlighted
6902 Multi_jw_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
6904 // set the ani for the button
6905 Multi_jw_buttons[gr_screen.res][idx].button.set_bmaps(Multi_jw_buttons[gr_screen.res][idx].filename);
6908 Multi_jw_buttons[gr_screen.res][idx].button.link_hotspot(Multi_jw_buttons[gr_screen.res][idx].hotspot);
6911 // if this is a PXO game, enable the squadwar checkbox
6912 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);
6913 Multi_jw_sw_checkbox.set_bmaps(Multi_jw_sw_checkbox_fname[gr_screen.res], 6, 0);
6914 Multi_jw_sw_checkbox.disable();
6915 if(!MULTI_IS_TRACKER_GAME){
6916 Multi_jw_sw_checkbox.hide();
6920 for(idx=0; idx<MULTI_JW_NUM_TEXT; idx++){
6921 Multi_jw_window.add_XSTR(&Multi_jw_text[gr_screen.res][idx]);
6924 // create the player select list button and hide it
6925 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);
6926 Multi_jw_plist_select_button.hide();
6929 Multi_jw_buttons[gr_screen.res][MJW_CANCEL].button.set_hotkey(KEY_ESC);
6931 // remove campaign flags
6932 Game_mode &= ~(GM_CAMPAIGN_MODE);
6934 // tell the server we have finished joining
6935 Net_player->state = NETPLAYER_STATE_JOINED;
6936 send_netplayer_update_packet();
6939 ml_printf(NOX("Joined netgame %s"), Netgame.name);
6941 // send any appropriate files
6942 multi_data_send_my_junk();
6945 void multi_game_client_setup_do_frame()
6948 int k = chatbox_process();
6949 char mission_text[255];
6950 k = Multi_jw_window.process(k,0);
6952 Multi_jw_should_show_popup = 0;
6954 // process any button clicks
6955 multi_jw_check_buttons();
6957 // do any network related stuff
6958 multi_jw_do_netstuff();
6960 // draw the background, etc
6962 GR_MAYBE_CLEAR_RES(Multi_jw_bitmap);
6963 if(Multi_jw_bitmap != -1){
6964 gr_set_bitmap(Multi_jw_bitmap);
6968 // if we're not in team vs. team mode, don't draw the team buttons
6969 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
6970 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.hide();
6971 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.hide();
6972 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.disable();
6973 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.disable();
6975 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.enable();
6976 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.enable();
6977 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.unhide();
6978 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.unhide();
6981 if(MULTI_IS_TRACKER_GAME){
6982 // maybe check the squadwar button
6983 if(Netgame.type_flags & NG_TYPE_SW){
6984 Multi_jw_sw_checkbox.set_state(1);
6985 gr_set_color_fast(&Color_bright);
6987 Multi_jw_sw_checkbox.set_state(0);
6988 gr_set_color_fast(&Color_normal);
6991 gr_string(Multi_jw_sw_checkbox_text[gr_screen.res][0], Multi_jw_sw_checkbox_text[gr_screen.res][1], "SquadWar");
6994 // draw the UI window
6995 Multi_jw_window.draw();
6997 // process and display the player list
6998 // NOTE : this must be done before the buttons are checked to insure that a player hasn't dropped
6999 multi_jw_plist_process();
7000 if(Netgame.type_flags & NG_TYPE_TEAM){
7001 multi_jw_plist_blit_team();
7003 multi_jw_plist_blit_normal();
7006 // display any text in the info area
7007 multi_common_render_text();
7009 // display any pending notification messages
7010 multi_common_notify_do();
7012 // blit the mission filename if possible
7013 if(Netgame.campaign_mode){
7014 if(strlen(Netgame.campaign_name) > 0){
7015 strcpy(mission_text,Netgame.campaign_name);
7017 if(strlen(Netgame.title) > 0){
7018 strcat(mission_text,", ");
7019 strcat(mission_text,Netgame.title);
7022 gr_set_color_fast(&Color_bright_white);
7023 gr_string(Mjw_mission_name_coords[gr_screen.res][MJW_X_COORD],Mjw_mission_name_coords[gr_screen.res][MJW_Y_COORD],mission_text);
7026 if(strlen(Netgame.mission_name) > 0){
7027 strcpy(mission_text,Netgame.mission_name);
7029 if(strlen(Netgame.title) > 0){
7030 strcat(mission_text,", ");
7031 strcat(mission_text,Netgame.title);
7034 gr_set_color_fast(&Color_bright_white);
7035 gr_string(Mjw_mission_name_coords[gr_screen.res][MJW_X_COORD],Mjw_mission_name_coords[gr_screen.res][MJW_Y_COORD],mission_text);
7039 // process and show the chatbox thingie
7043 Multi_jw_window.draw_tooltip();
7045 // display the voice status indicator
7046 multi_common_voice_display_status();
7051 // if we're supposed to be displaying a pilot info popup
7052 if(Multi_jw_should_show_popup){
7053 player_index = find_player_id(Multi_jw_plist_select_id);
7054 if(player_index != -1){
7055 multi_pinfo_popup(&Net_players[player_index]);
7060 void multi_game_client_setup_close()
7062 // unload any bitmaps
7063 if(!bm_unload(Multi_jw_bitmap)){
7064 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_jw_bitmap_fname[gr_screen.res]));
7067 // destroy the chatbox
7070 // destroy the UI_WINDOW
7071 Multi_jw_window.destroy();
7074 if(Netgame.game_state == NETGAME_STATE_MISSION_SYNC){
7075 gamesnd_play_iface(SND_COMMIT_PRESSED);
7080 void multi_jw_check_buttons()
7083 for(idx=0;idx<MULTI_JW_NUM_BUTTONS;idx++){
7084 // we only really need to check for one button pressed at a time, so we can break after
7086 if(Multi_jw_buttons[gr_screen.res][idx].button.pressed()){
7087 multi_jw_button_pressed(idx);
7093 void multi_jw_button_pressed(int n)
7097 gamesnd_play_iface(SND_USER_SELECT);
7098 multi_quit_game(PROMPT_CLIENT);
7100 case MJW_SCROLL_PLAYERS_UP:
7101 multi_jw_scroll_players_up();
7103 case MJW_SCROLL_PLAYERS_DOWN:
7104 multi_jw_scroll_players_down();
7106 case MJW_SCROLL_INFO_UP:
7107 multi_common_scroll_text_up();
7109 case MJW_SCROLL_INFO_DOWN:
7110 multi_common_scroll_text_down();
7113 // request to set myself to team 0
7115 gamesnd_play_iface(SND_USER_SELECT);
7116 multi_team_set_team(Net_player,0);
7119 // request to set myself to team 1
7121 gamesnd_play_iface(SND_USER_SELECT);
7122 multi_team_set_team(Net_player,1);
7126 case MJW_PILOT_INFO:
7127 Multi_jw_should_show_popup = 1;
7131 multi_common_add_notify(XSTR("Not implemented yet!",760));
7136 // do stuff like pinging servers, sending out requests, etc
7137 void multi_jw_do_netstuff()
7141 void multi_jw_scroll_players_up()
7143 gamesnd_play_iface(SND_GENERAL_FAIL);
7146 // scroll down through the player list
7147 void multi_jw_scroll_players_down()
7149 gamesnd_play_iface(SND_GENERAL_FAIL);
7152 void multi_jw_plist_process()
7154 int test_count,player_index,idx;
7156 // first determine if there are 0 players in the game. This should never happen since the host is _always_ in the game
7158 for(idx=0;idx<MAX_PLAYERS;idx++){
7159 // count anyone except the standalone server (if applicable)
7160 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7164 if(test_count <= 0){
7168 // if we had a selected item but that player has left, select myself instead
7169 if(Multi_jw_plist_select_flag){
7170 player_index = find_player_id(Multi_jw_plist_select_id);
7171 if(player_index == -1){
7172 Multi_jw_plist_select_id = Net_player->player_id;
7175 Multi_jw_plist_select_flag = 1;
7176 Multi_jw_plist_select_id = Net_player->player_id;
7179 // if the player has clicked somewhere in the player list area
7180 if(Multi_jw_plist_select_button.pressed()){
7184 player_id = multi_jw_get_mouse_id();
7185 player_index = find_player_id(player_id);
7186 if(player_index != -1){
7187 Multi_jw_plist_select_id = player_id;
7188 Multi_jw_plist_select_flag = 1;
7193 void multi_jw_plist_blit_normal()
7196 char str[CALLSIGN_LEN+1];
7197 int y_start = Mjw_players_coords[gr_screen.res][MJW_Y_COORD];
7200 // display all the players
7201 for(idx=0;idx<MAX_PLAYERS;idx++){
7202 // reset total offset
7205 // count anyone except the standalone server (if applicable)
7206 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7207 // highlight him if he's the host
7208 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7209 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7210 gr_set_color_fast(&Color_text_active_hi);
7212 gr_set_color_fast(&Color_bright);
7215 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7216 gr_set_color_fast(&Color_text_active);
7218 gr_set_color_fast(&Color_text_normal);
7222 // optionally draw his CD status
7223 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7224 gr_set_bitmap(Multi_common_icons[MICON_CD]);
7225 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7227 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7230 // make sure the string will fit, then display it
7231 strcpy(str,Net_players[idx].player->callsign);
7232 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
7235 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7236 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7243 void multi_jw_plist_blit_team()
7246 char str[CALLSIGN_LEN+1];
7247 int y_start = Mjw_players_coords[gr_screen.res][MJW_Y_COORD];
7250 // always blit the proper team button based on _my_ team status
7251 if(Net_player->p_info.team == 0){
7252 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.draw_forced(2);
7254 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.draw_forced(2);
7257 // display all the red players first
7258 for(idx=0;idx<MAX_PLAYERS;idx++){
7259 // reset total offset
7262 // count anyone except the standalone server (if applicable)
7263 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
7264 // highlight him if he's the host
7265 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7266 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7267 gr_set_color_fast(&Color_text_active_hi);
7269 gr_set_color_fast(&Color_bright);
7272 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7273 gr_set_color_fast(&Color_text_active);
7275 gr_set_color_fast(&Color_text_normal);
7279 // optionally draw his CD status
7280 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7281 gr_set_bitmap(Multi_common_icons[MICON_CD]);
7282 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7284 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7287 // blit the red team indicator
7288 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
7289 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
7290 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT]);
7291 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7293 total_offset += Multi_common_icon_dims[MICON_TEAM0_SELECT][0] + 1;
7296 if(Multi_common_icons[MICON_TEAM0] != -1){
7297 gr_set_bitmap(Multi_common_icons[MICON_TEAM0]);
7298 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7300 total_offset += Multi_common_icon_dims[MICON_TEAM0][0] + 1;
7304 // make sure the string will fit
7305 strcpy(str,Net_players[idx].player->callsign);
7306 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7308 // display him in the correct half of the list depending on his team
7309 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7314 // display all the green players next
7315 for(idx=0;idx<MAX_PLAYERS;idx++){
7316 // reset total offset
7319 // count anyone except the standalone server (if applicable)
7320 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
7321 // highlight him if he's the host
7322 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7323 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7324 gr_set_color_fast(&Color_text_active_hi);
7326 gr_set_color_fast(&Color_bright);
7329 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7330 gr_set_color_fast(&Color_text_active);
7332 gr_set_color_fast(&Color_text_normal);
7336 // optionally draw his CD status
7337 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7338 gr_set_bitmap(Multi_common_icons[MICON_CD]);
7339 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7341 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7344 // blit the red team indicator
7345 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
7346 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
7347 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT]);
7348 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7350 total_offset += Multi_common_icon_dims[MICON_TEAM1_SELECT][0] + 1;
7353 if(Multi_common_icons[MICON_TEAM1] != -1){
7354 gr_set_bitmap(Multi_common_icons[MICON_TEAM1]);
7355 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7357 total_offset += Multi_common_icon_dims[MICON_TEAM1][0] + 1;
7361 // make sure the string will fit
7362 strcpy(str,Net_players[idx].player->callsign);
7363 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
7366 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7368 // display him in the correct half of the list depending on his team
7369 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7375 void multi_jw_handle_join(net_player *pl)
7377 // for now just play a bloop sound
7378 gamesnd_play_iface(SND_ICON_DROP_ON_WING);
7381 short multi_jw_get_mouse_id()
7383 // determine where he clicked (y pixel value)
7385 Multi_jw_plist_select_button.get_mouse_pos(NULL,&y);
7387 // select things a little differently if we're in team vs. team or non-team vs. team mode
7389 if(Netgame.type_flags & NG_TYPE_TEAM){
7390 int player_index = -1;
7392 // look through all of team red first
7393 for(idx=0;idx<MAX_PLAYERS;idx++){
7394 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
7397 // if this is the _nth_ guy
7405 // if we still haven't found him yet, look through the green team
7406 if(player_index == -1){
7407 for(idx=0;idx<MAX_PLAYERS;idx++){
7408 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
7410 // if this is the _nth_ guy
7418 if(player_index != -1){
7419 return Net_players[idx].player_id;
7422 // select the nth active player if possible, disregarding the standalone server
7423 for(idx=0;idx<MAX_PLAYERS;idx++){
7424 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7427 // if this is the _nth_ guy
7429 return Net_players[idx].player_id;
7440 // -------------------------------------------------------------------------------------------------------------
7442 // MULTIPLAYER WAIT/SYNCH SCREEN
7445 #define MULTI_SYNC_HOST_COUNT 4 // host uses 4 buttons (and sometimes 5)
7446 #define MULTI_SYNC_CLIENT_COUNT 3 // client only uses 3 buttons
7448 char *Multi_sync_bitmap_fname[GR_NUM_RESOLUTIONS] = {
7449 "MultiSynch", // GR_640
7450 "2_MultiSynch" // GR_1024
7453 char *Multi_sync_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
7454 "MultiSynch-M", // GR_640
7455 "2_MultiSynch-M" // GR_1024
7460 // constants for coordinate lookup
7461 #define MS_X_COORD 0
7462 #define MS_Y_COORD 1
7463 #define MS_W_COORD 2
7464 #define MS_H_COORD 3
7466 UI_WINDOW Multi_sync_window; // the window object for the join screen
7467 int Multi_sync_button_count; // the # of buttons to use for this instance of mission sync
7468 int Multi_sync_bitmap; // the background bitmap
7471 #define MULTI_SYNC_NUM_BUTTONS 5
7472 #define MS_SCROLL_INFO_UP 0
7473 #define MS_SCROLL_INFO_DOWN 1
7477 ui_button_info Multi_sync_buttons[GR_NUM_RESOLUTIONS][MULTI_SYNC_NUM_BUTTONS] = {
7479 ui_button_info("MS_00", 1, 404, -1, -1, 0),
7480 ui_button_info("MS_01", 1, 446, -1, -1, 1),
7481 ui_button_info("MS_03", 518, 426, 519, 416, 3),
7482 ui_button_info("MS_02", 469, 426, 479, 416, 2),
7483 ui_button_info("MS_04", 571, 420, 577, 416, 4),
7486 ui_button_info("2_MS_00", 2, 647, -1, -1, 0),
7487 ui_button_info("2_MS_01", 2, 713, -1, -1, 1),
7488 ui_button_info("2_MS_03", 829, 682, 831, 667, 3),
7489 ui_button_info("2_MS_02", 751, 682, 766, 667, 2),
7490 ui_button_info("2_MS_04", 914, 672, 924, 667, 4),
7495 #define MULTI_SYNC_NUM_TEXT 5
7497 #define MST_LAUNCH 2
7498 UI_XSTR Multi_sync_text[GR_NUM_RESOLUTIONS][MULTI_SYNC_NUM_TEXT] = {
7500 { "Kick", 1266, 479, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_KICK].button },
7501 { "Cancel", 387, 519, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_CANCEL].button },
7502 { "Launch", 801, 577, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_LAUNCH].button },
7503 { "Players", 1269, 23, 133, UI_XSTR_COLOR_GREEN, -1, NULL },
7504 { "Status", 1304, 228, 133, UI_XSTR_COLOR_GREEN, -1, NULL }
7507 { "Kick", 1266, 766, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_KICK].button },
7508 { "Cancel", 387, 831, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_CANCEL].button },
7509 { "Launch", 801, 924, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_LAUNCH].button },
7510 { "Players", 1269, 38, 214, UI_XSTR_COLOR_GREEN, -1, NULL },
7511 { "Status", 1304, 366, 214, UI_XSTR_COLOR_GREEN, -1, NULL }
7516 int Ms_status_coords[GR_NUM_RESOLUTIONS][4] = {
7525 // player status coords
7526 int Ms_status2_coords[GR_NUM_RESOLUTIONS][4] = {
7535 int Ms_cd_icon_offset[GR_NUM_RESOLUTIONS] = {
7540 int Ms_team_icon_offset[GR_NUM_RESOLUTIONS] = {
7545 // player currently selected, index into Net_players[]
7546 int Multi_sync_player_select = -1;
7548 // player list control thingie defs
7549 #define MULTI_SYNC_PLIST_MAX_DISPLAY 15
7550 int Multi_sync_plist_start; // where to start displaying from
7551 int Multi_sync_plist_count; // how many we have
7553 // list select button
7554 UI_BUTTON Multi_sync_plist_button;
7556 int Multi_sync_mode = -1;
7558 #define MULTI_SYNC_COUNTDOWN_TIME 5 // in seconds
7559 float Multi_sync_countdown_timer;
7560 int Multi_sync_countdown = -1;
7562 int Multi_launch_button_created;
7565 // countdown animation timer
7566 char* Multi_sync_countdown_fname[GR_NUM_RESOLUTIONS] = {
7568 "2_Count" // GR_1024
7571 int Multi_sync_countdown_coords[GR_NUM_RESOLUTIONS][2] = {
7582 anim *Multi_sync_countdown_anim = NULL;
7583 anim_instance *Multi_sync_countdown_instance = NULL;
7586 // PREBRIEFING STUFF
7587 // syncing flags used by the server
7588 int Mission_sync_flags = 0;
7589 #define MS_FLAG_SENT_FILESIG (1<<0) // sent filesig requests
7590 #define MS_FLAG_SENT_LOAD (1<<1) // sent load packets
7591 #define MS_FLAG_PUSHED_BRIEFING (1<<2) // pushed everyone else into the briefing
7592 #define MS_FLAG_POST_DATA (1<<3) // sent the post data block
7593 #define MS_FLAG_WSS_SLOTS (1<<4) // all players have received wss slot data
7594 #define MS_FLAG_PSETTINGS (1<<5) // send the player settings packet
7595 #define MS_FLAG_MT_STATS_START (1<<6) // server has started getting player stats from the tracker
7596 #define MS_FLAG_MT_STATS_DONE (1<<7) // server has finished getting player stats from the tracker (success or fail)
7597 #define MS_FLAG_TS_SLOTS (1<<8) // team/ship slots have been sent
7598 #define MS_FLAG_DATA_DONE (1<<9) // done transferring all necessary data
7599 #define MS_FLAG_CAMP_DONE (1<<10) // send campaign pool/goal/event stuff
7601 // POSTBRIEFING STUFF
7602 int Multi_state_timestamp;
7603 int Multi_sync_launch_pressed;
7605 // LOCAL function definitions
7606 void multi_sync_check_buttons();
7607 void multi_sync_button_pressed(int n);
7608 void multi_sync_scroll_info_up();
7609 void multi_sync_scroll_info_down();
7610 void multi_sync_display_name(char *name,int index,int np_index); // display info on the left hand portion of the status window thingie
7611 void multi_sync_display_status(char *status,int index); // display info on the right hand portion of the status window thingie
7612 void multi_sync_force_start_pre();
7613 void multi_sync_force_start_post();
7614 void multi_sync_launch();
7615 void multi_sync_create_launch_button();
7616 void multi_sync_blit_screen_all();
7617 void multi_sync_handle_plist();
7619 void multi_sync_common_init();
7620 void multi_sync_common_do();
7621 void multi_sync_common_close();
7623 void multi_sync_pre_init();
7624 void multi_sync_pre_do();
7625 void multi_sync_pre_close();
7627 void multi_sync_post_init();
7628 void multi_sync_post_do();
7629 void multi_sync_post_close();
7634 // perform the correct init functions
7635 void multi_sync_init()
7637 Multi_sync_countdown = -1;
7641 // reset all timestamp
7642 multi_reset_timestamps();
7644 extern int Player_multi_died_check;
7645 Player_multi_died_check = -1;
7647 if(!(Game_mode & GM_STANDALONE_SERVER)){
7648 multi_sync_common_init();
7651 switch(Multi_sync_mode){
7652 case MULTI_SYNC_PRE_BRIEFING:
7653 multi_sync_pre_init();
7655 case MULTI_SYNC_POST_BRIEFING:
7656 multi_sync_post_init();
7658 case MULTI_SYNC_INGAME:
7659 multi_ingame_sync_init();
7664 // perform the correct do frame functions
7665 void multi_sync_do()
7667 if(!(Game_mode & GM_STANDALONE_SERVER)){
7668 multi_sync_common_do();
7671 // if the netgame is ending, don't do any sync processing
7672 if(multi_endgame_ending()){
7676 // process appropriateliy
7677 switch(Multi_sync_mode){
7678 case MULTI_SYNC_PRE_BRIEFING:
7679 multi_sync_pre_do();
7681 case MULTI_SYNC_POST_BRIEFING:
7682 multi_sync_post_do();
7684 case MULTI_SYNC_INGAME:
7685 multi_ingame_sync_do();
7688 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
7689 if(Multi_sync_bitmap != -1){
7690 gr_set_bitmap(Multi_sync_bitmap);
7693 Multi_sync_window.draw();
7695 multi_sync_blit_screen_all();
7702 // perform the correct close functions
7703 void multi_sync_close()
7705 switch(Multi_sync_mode){
7706 case MULTI_SYNC_PRE_BRIEFING:
7707 multi_sync_pre_close();
7709 case MULTI_SYNC_POST_BRIEFING:
7710 multi_sync_post_close();
7712 case MULTI_SYNC_INGAME:
7713 multi_ingame_sync_close();
7717 if(!(Game_mode & GM_STANDALONE_SERVER)){
7718 multi_sync_common_close();
7722 char *multi_sync_tooltip_handler(char *str)
7724 if (!stricmp(str, NOX("@launch"))) {
7725 if (Multi_launch_button_created){
7726 return XSTR("Launch",801);
7733 void multi_sync_common_init()
7737 // create the interface window
7738 Multi_sync_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
7739 Multi_sync_window.set_mask_bmap(Multi_sync_bitmap_mask_fname[gr_screen.res]);
7740 Multi_sync_window.tooltip_handler = multi_sync_tooltip_handler;
7742 // load the background bitmap
7743 Multi_sync_bitmap = bm_load(Multi_sync_bitmap_fname[gr_screen.res]);
7744 if (Multi_sync_bitmap < 0) {
7745 // we failed to load the bitmap - this is very bad
7749 // initialize the player list data
7750 Multi_sync_plist_start = 0;
7751 Multi_sync_plist_count = 1; // we can pretty safely assume that there's one player in the game - me.
7753 Multi_launch_button_created = 0;
7755 // create the chatbox thingie (shouldn't be necesary to do this, but we'll put it in for good measure)
7758 // force the chatbox to be small
7759 chatbox_force_small();
7761 // initialize the common notification messaging
7762 multi_common_notify_init();
7764 // initialize the common mission info display area.
7765 multi_common_set_text("");
7767 // use the common interface palette
7768 multi_common_set_palette();
7770 // don't select any player yet.
7771 Multi_sync_player_select = -1;
7773 // determine how many of the 5 buttons to create
7774 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
7775 Multi_sync_button_count = MULTI_SYNC_HOST_COUNT;
7777 Multi_sync_button_count = MULTI_SYNC_CLIENT_COUNT;
7779 // create the interface buttons
7780 for(idx=0; idx<Multi_sync_button_count; idx++){
7781 // create the object
7782 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);
7784 // set the sound to play when highlighted
7785 Multi_sync_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
7787 // set the ani for the button
7788 // this wierdness is necessary because cancel and kick buttons aren't drawn on the background bitmap,
7789 // so we have to load in frame 0, too (the file should exist)
7790 if ((idx == MS_CANCEL) || (idx == MS_KICK) || (idx == MS_LAUNCH)) {
7791 Multi_sync_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sync_buttons[gr_screen.res][idx].filename, 3, 0);
7793 Multi_sync_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sync_buttons[gr_screen.res][idx].filename);
7797 Multi_sync_buttons[gr_screen.res][idx].button.link_hotspot(Multi_sync_buttons[gr_screen.res][idx].hotspot);
7801 for(idx=0; idx<MULTI_SYNC_NUM_TEXT; idx++) {
7802 // don't create the "launch" button text just yet
7803 if(idx == MST_LAUNCH) {
7806 // multiplayer clients should ignore the kick button
7807 if(!MULTIPLAYER_MASTER && !MULTIPLAYER_HOST && (idx == MST_KICK)) {
7811 Multi_sync_window.add_XSTR(&Multi_sync_text[gr_screen.res][idx]);
7814 // create the player list select button and hide it
7815 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);
7816 Multi_sync_plist_button.hide();
7818 // set up hotkeys for certain common functions
7819 Multi_sync_buttons[gr_screen.res][MS_CANCEL].button.set_hotkey(KEY_ESC);
7822 void multi_sync_common_do()
7824 int k = chatbox_process();
7825 k = Multi_sync_window.process(k);
7827 // process the player list
7828 multi_sync_handle_plist();
7830 // process any button clicks
7831 multi_sync_check_buttons();
7833 // process any keypresses
7837 gamesnd_play_iface(SND_USER_SELECT);
7838 multi_quit_game(PROMPT_ALL);
7843 void multi_sync_common_close()
7845 // unload any bitmaps
7846 if(!bm_unload(Multi_sync_bitmap)){
7847 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_sync_bitmap_fname[gr_screen.res]));
7850 extern int Player_multi_died_check;
7851 Player_multi_died_check = -1;
7853 // destroy the UI_WINDOW
7854 Multi_sync_window.destroy();
7857 void multi_sync_blit_screen_all()
7864 // display any text in the info area
7865 multi_common_render_text();
7867 // display any pending notification messages
7868 multi_common_notify_do();
7870 // display any info about visible players
7872 for(idx=0;idx<MAX_PLAYERS;idx++){
7873 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7874 // display his name and status
7875 multi_sync_display_name(Net_players[idx].player->callsign,count,idx);
7877 // get the player state
7878 state = Net_players[idx].state;
7880 // if we're ingame joining, show all other players except myself as "playing"
7881 if((Net_player != NULL) && (&Net_players[idx] != Net_player) && ((Multi_sync_mode == MULTI_SYNC_INGAME) || (Net_player->flags & NETINFO_FLAG_INGAME_JOIN)) ){
7882 state = NETPLAYER_STATE_IN_MISSION;
7886 case NETPLAYER_STATE_MISSION_LOADING:
7887 multi_sync_display_status(XSTR("Mission Loading",802),count);
7889 case NETPLAYER_STATE_INGAME_SHIP_SELECT: // I don't think its possible to see this state, but...
7890 multi_sync_display_status(XSTR("Ingame Ship Select",803),count);
7892 case NETPLAYER_STATE_DEBRIEF:
7893 multi_sync_display_status(XSTR("Debriefing",804),count);
7895 case NETPLAYER_STATE_MISSION_SYNC:
7896 multi_sync_display_status(XSTR("Mission Sync",805),count);
7898 case NETPLAYER_STATE_JOINING:
7899 multi_sync_display_status(XSTR("Joining",806),count);
7901 case NETPLAYER_STATE_JOINED:
7902 multi_sync_display_status(XSTR("Joined",807),count);
7904 case NETPLAYER_STATE_SLOT_ACK :
7905 multi_sync_display_status(XSTR("Slot Ack",808),count);
7907 case NETPLAYER_STATE_BRIEFING:
7908 multi_sync_display_status(XSTR("Briefing",765),count);
7910 case NETPLAYER_STATE_SHIP_SELECT:
7911 multi_sync_display_status(XSTR("Ship Select",809),count);
7913 case NETPLAYER_STATE_WEAPON_SELECT:
7914 multi_sync_display_status(XSTR("Weapon Select",810),count);
7916 case NETPLAYER_STATE_WAITING:
7917 multi_sync_display_status(XSTR("Waiting",811),count);
7919 case NETPLAYER_STATE_IN_MISSION:
7920 multi_sync_display_status(XSTR("In Mission",812),count);
7922 case NETPLAYER_STATE_MISSION_LOADED:
7923 multi_sync_display_status(XSTR("Mission Loaded",813),count);
7925 case NETPLAYER_STATE_DATA_LOAD:
7926 multi_sync_display_status(XSTR("Data loading",814),count);
7928 case NETPLAYER_STATE_SETTINGS_ACK:
7929 multi_sync_display_status(XSTR("Ready To Enter Mission",815),count);
7931 case NETPLAYER_STATE_INGAME_SHIPS:
7932 multi_sync_display_status(XSTR("Ingame Ships Packet Ack",816),count);
7934 case NETPLAYER_STATE_INGAME_WINGS:
7935 multi_sync_display_status(XSTR("Ingame Wings Packet Ack",817),count);
7937 case NETPLAYER_STATE_INGAME_RPTS:
7938 multi_sync_display_status(XSTR("Ingame Respawn Points Ack",818),count);
7940 case NETPLAYER_STATE_SLOTS_ACK:
7941 multi_sync_display_status(XSTR("Ingame Weapon Slots Ack",819),count);
7943 case NETPLAYER_STATE_POST_DATA_ACK:
7944 multi_sync_display_status(XSTR("Post Briefing Data Block Ack",820),count);
7946 case NETPLAYER_STATE_FLAG_ACK :
7947 multi_sync_display_status(XSTR("Flags Ack",821),count);
7949 case NETPLAYER_STATE_MT_STATS :
7950 multi_sync_display_status(XSTR("Parallax Online Stats Updating",822),count);
7952 case NETPLAYER_STATE_WSS_ACK :
7953 multi_sync_display_status(XSTR("Weapon Slots Ack",823),count);
7955 case NETPLAYER_STATE_HOST_SETUP :
7956 multi_sync_display_status(XSTR("Host setup",824),count);
7958 case NETPLAYER_STATE_DEBRIEF_ACCEPT:
7959 multi_sync_display_status(XSTR("Debrief accept",825),count);
7961 case NETPLAYER_STATE_DEBRIEF_REPLAY:
7962 multi_sync_display_status(XSTR("Debrief replay",826),count);
7964 case NETPLAYER_STATE_CPOOL_ACK:
7965 multi_sync_display_status(XSTR("Campaign ship/weapon ack",827),count);
7967 case NETPLAYER_STATE_MISSION_XFER :
7969 // server should display the pct completion of all clients
7970 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
7971 if(Net_players[idx].s_info.xfer_handle != -1){
7972 pct_complete = multi_xfer_pct_complete(Net_players[idx].s_info.xfer_handle);
7974 // if we've got a valid xfer handle
7975 if((pct_complete >= 0.0) && (pct_complete <= 1.0)){
7976 sprintf(txt,XSTR("Mission file xfer %d%%",828),(int)(pct_complete * 100.0f));
7980 strcpy(txt,XSTR("Mission file xfer",829));
7983 strcpy(txt,XSTR("Mission file xfer",829));
7986 // clients should display only for themselves (which is the only thing they know)
7988 // if we've got a valid file xfer handle
7989 if((&Net_players[idx] == Net_player) && (Net_player->s_info.xfer_handle != -1)){
7990 pct_complete = multi_xfer_pct_complete(Net_player->s_info.xfer_handle);
7992 // if we've got a valid xfer handle
7993 if((pct_complete >= 0.0) && (pct_complete <= 1.0)){
7994 sprintf(txt,XSTR("Mission file xfer %d%%",828),(int)(pct_complete * 100.0f));
7998 strcpy(txt,XSTR("Mission file xfer",829));
8003 strcpy(txt,XSTR("Mission file xfer",829));
8008 multi_sync_display_status(txt,count);
8011 nprintf(("Network","Unhandled player state : %d !\n",Net_players[idx].state));
8018 // display the mission start countdown timer (if any)
8019 anim_render_all(GS_STATE_MULTI_MISSION_SYNC,flFrametime);
8021 // process and show the chatbox thingie
8025 Multi_sync_window.draw_tooltip();
8027 // display the voice status indicator
8028 multi_common_voice_display_status();
8031 void multi_sync_check_buttons()
8034 for(idx=0;idx<Multi_sync_button_count;idx++){
8035 // we only really need to check for one button pressed at a time, so we can break after
8037 if(Multi_sync_buttons[gr_screen.res][idx].button.pressed()){
8038 multi_sync_button_pressed(idx);
8044 void multi_sync_button_pressed(int n)
8049 gamesnd_play_iface(SND_USER_SELECT);
8050 multi_quit_game(PROMPT_ALL);
8053 // scroll the info box up
8054 case MS_SCROLL_INFO_UP:
8055 multi_common_scroll_text_up();
8058 // scroll the info box down
8059 case MS_SCROLL_INFO_DOWN:
8060 multi_common_scroll_text_down();
8065 // if we have a currently selected player, kick him
8066 if(Multi_sync_player_select >= 0){
8067 multi_kick_player(Multi_sync_player_select);
8071 // start the final launch countdown (post-sync only)
8073 multi_sync_start_countdown();
8076 // doesn't do anything
8082 void multi_sync_pre_init()
8086 Netgame.game_state = NETGAME_STATE_MISSION_SYNC;
8088 // if we're in teamplay mode, always force skill level to be medium
8089 if((Netgame.type_flags & NG_TYPE_TEAM) && (Net_player->flags & NETINFO_FLAG_GAME_HOST)){
8090 Netgame.options.skill_level = NUM_SKILL_LEVELS / 2;
8091 Game_skill_level = NUM_SKILL_LEVELS / 2;
8092 multi_options_update_netgame();
8095 // notify everyone of when we get here
8096 if(!(Game_mode & GM_STANDALONE_SERVER)){
8097 Net_player->state = NETPLAYER_STATE_MISSION_SYNC;
8098 send_netplayer_update_packet();
8101 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8103 ml_string(NOX("Server performing pre-briefing data sync"));
8105 if(!(Game_mode & GM_STANDALONE_SERVER)){
8106 multi_common_set_text(XSTR("Server performing sync\n",830),1);
8109 // maybe initialize tvt and squad war stuff
8110 if(Netgame.type_flags & NG_TYPE_TEAM){
8111 multi_team_level_init();
8114 // force everyone into this state
8115 send_netgame_update_packet();
8117 if(!(Game_mode & GM_STANDALONE_SERVER)){
8118 multi_common_add_text(XSTR("Send update packet\n",831),1);
8121 // setup some of my own data
8122 Net_player->flags |= NETINFO_FLAG_MISSION_OK;
8124 // do any output stuff
8125 if(Game_mode & GM_STANDALONE_SERVER){
8126 std_debug_set_standalone_state_string("Mission Sync");
8129 // do this here to insure we have the most up to date file checksum info
8130 multi_get_mission_checksum(Game_current_mission_filename);
8131 // parse_get_file_signature(Game_current_mission_filename);
8133 if(!(Game_mode & GM_STANDALONE_SERVER)){
8134 multi_common_add_text(XSTR("Got file signatures\n",832),1);
8137 if(!(Game_mode & GM_STANDALONE_SERVER)){
8138 multi_common_add_text(XSTR("Sending update state packet\n",833),1);
8142 // if we're not in team vs. team mode - set all player teams to be 0, and unset all captaincy bits
8143 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
8144 for(idx=0;idx<MAX_PLAYERS;idx++){
8145 Net_players[idx].p_info.team = 0;
8146 Net_players[idx].flags &= ~(NETINFO_FLAG_TEAM_CAPTAIN);
8150 // we aren't necessarily xferring the mission file yet
8151 Assert(Net_player->s_info.xfer_handle == -1);
8153 // always call this for good measure
8154 multi_campaign_flush_data();
8156 Mission_sync_flags = 0;
8157 Multi_mission_loaded = 0;
8160 void multi_sync_pre_do()
8164 // If I'm the server, wait for everyone to arrive in this state, then begin transferring data, etc.
8165 // all servers (standalone or no, go through this)
8166 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8167 // wait for everyone to arrive, then request filesig from all of them
8168 if(multi_netplayer_state_check(NETPLAYER_STATE_MISSION_SYNC) && !(Mission_sync_flags & MS_FLAG_SENT_FILESIG) && !multi_endgame_ending()){
8169 send_file_sig_request(Netgame.mission_name);
8170 Mission_sync_flags |= MS_FLAG_SENT_FILESIG;
8172 if(!(Game_mode & GM_STANDALONE_SERVER)){
8173 multi_common_add_text(XSTR("Sent filesig request\n",834),1);
8177 // if we're waiting for players to receive files, then check on their status
8178 if((Mission_sync_flags & MS_FLAG_SENT_FILESIG) && !multi_netplayer_flag_check(NETINFO_FLAG_MISSION_OK) && !multi_endgame_ending()){
8179 for(idx=0;idx<MAX_PLAYERS;idx++){
8180 // if this player is in the process of xferring a file
8181 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].s_info.xfer_handle != -1)){
8182 switch(multi_xfer_get_status(Net_players[idx].s_info.xfer_handle)){
8183 // if it has successfully completed, set his ok flag
8184 case MULTI_XFER_SUCCESS :
8186 Net_players[idx].flags |= NETINFO_FLAG_MISSION_OK;
8188 // release the xfer instance handle
8189 multi_xfer_release_handle(Net_players[idx].s_info.xfer_handle);
8190 Net_players[idx].s_info.xfer_handle = -1;
8192 // if it has failed or timed-out, kick the player
8193 case MULTI_XFER_TIMEDOUT:
8194 case MULTI_XFER_FAIL:
8195 // release the xfer handle
8196 multi_xfer_release_handle(Net_players[idx].s_info.xfer_handle);
8197 Net_players[idx].s_info.xfer_handle = -1;
8200 multi_kick_player(idx, 0, KICK_REASON_BAD_XFER);
8207 // NOTE : this is now obsolete
8208 // once everyone is verified, do any data transfer necessary
8209 if(multi_netplayer_flag_check(NETINFO_FLAG_MISSION_OK) && !(Mission_sync_flags & MS_FLAG_DATA_DONE) && !multi_endgame_ending()){
8210 // do nothing for now
8211 Mission_sync_flags |= MS_FLAG_DATA_DONE;
8213 // send campaign pool data
8214 multi_campaign_send_pool_status();
8217 // wait for everyone to ack on campaign pool data (even in non-campaign situations)
8218 if((Mission_sync_flags & MS_FLAG_DATA_DONE) && !(Mission_sync_flags & MS_FLAG_CAMP_DONE) && !multi_endgame_ending()){
8219 // check to see if everyone has acked the campaign pool data
8220 if(multi_netplayer_state_check(NETPLAYER_STATE_CPOOL_ACK)){
8221 Mission_sync_flags |= MS_FLAG_CAMP_DONE;
8225 // once everyone is verified, tell them to load the mission
8226 // also make sure to load the mission myself _AFTER_ telling everyone to do so. This makes the whole process
8227 // move along faster
8228 if((Mission_sync_flags & MS_FLAG_CAMP_DONE) && !(Mission_sync_flags & MS_FLAG_SENT_LOAD) && !multi_endgame_ending()){
8229 send_netplayer_load_packet(NULL);
8230 Mission_sync_flags |= MS_FLAG_SENT_LOAD;
8232 if(!(Game_mode & GM_STANDALONE_SERVER)){
8233 multi_common_add_text(XSTR("Sent load packet\n",835),1);
8236 // load the mission myself, as soon as possible
8237 if(!Multi_mission_loaded){
8238 nprintf(("Network","Server loading mission..."));
8240 // update everyone about my status
8241 Net_player->state = NETPLAYER_STATE_MISSION_LOADING;
8242 send_netplayer_update_packet();
8244 game_start_mission();
8246 nprintf(("Network","Done\n"));
8247 Multi_mission_loaded = 1;
8249 // update everyone about my status
8250 Net_player->state = NETPLAYER_STATE_MISSION_LOADED;
8251 send_netplayer_update_packet();
8253 if(!(Game_mode & GM_STANDALONE_SERVER)){
8254 multi_common_add_text(XSTR("Loaded mission locally\n",836),1);
8259 // if everyone has loaded the mission, randomly assign players to ships
8260 if(multi_netplayer_state_check(NETPLAYER_STATE_MISSION_LOADED) && !(Mission_sync_flags & MS_FLAG_TS_SLOTS) && !multi_endgame_ending()){
8261 // call the team select function to assign players to their ships, wings, etc
8262 multi_ts_assign_players_all();
8263 send_netplayer_slot_packet();
8266 Mission_sync_flags |= MS_FLAG_TS_SLOTS;
8269 // if everyone has loaded the mission, move to the team select stage
8270 if(Sync_test && multi_netplayer_state_check(NETPLAYER_STATE_SLOT_ACK) && !(Mission_sync_flags & MS_FLAG_PUSHED_BRIEFING) && !multi_endgame_ending()){
8271 Netgame.game_state = NETGAME_STATE_BRIEFING;
8272 send_netgame_update_packet(); // this will push everyone into the next state
8274 // the standalone moves to his own wait state, whereas in the normal game mode, the server/host moves in to the
8275 // team select state
8276 if(Game_mode & GM_STANDALONE_SERVER){
8277 gameseq_post_event(GS_EVENT_MULTI_STD_WAIT);
8279 gameseq_post_event(GS_EVENT_START_GAME);
8282 Mission_sync_flags |= MS_FLAG_PUSHED_BRIEFING;
8284 if(!(Game_mode & GM_STANDALONE_SERVER)){
8285 multi_common_add_text(XSTR("Moving to team select\n",837),1);
8289 // clients should detect here if they are doing a file xfer and do error processing
8290 if((Net_player->state == NETPLAYER_STATE_MISSION_XFER) && (Net_player->s_info.xfer_handle != -1) && !multi_endgame_ending()){
8291 switch(multi_xfer_get_status(Net_player->s_info.xfer_handle)){
8292 // if it has successfully completed, set his ok flag
8293 case MULTI_XFER_SUCCESS :
8294 // release my xfer handle
8295 multi_xfer_release_handle(Net_player->s_info.xfer_handle);
8296 Net_player->s_info.xfer_handle = -1;
8299 // if it has failed or timed-out, kick the player
8300 case MULTI_XFER_TIMEDOUT:
8301 case MULTI_XFER_FAIL:
8302 // release my xfer handle
8303 multi_xfer_release_handle(Net_player->s_info.xfer_handle);
8304 Net_player->s_info.xfer_handle = -1;
8306 // leave the game qith an error code
8307 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_XFER_FAIL);
8314 if(!(Game_mode & GM_STANDALONE_SERVER)){
8316 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8317 if(Multi_sync_bitmap != -1){
8318 gr_set_bitmap(Multi_sync_bitmap);
8321 Multi_sync_window.draw();
8323 multi_sync_blit_screen_all();
8329 void multi_sync_pre_close()
8331 // at this point, we should shut down any file xfers...
8332 if(Net_player->s_info.xfer_handle != -1){
8333 nprintf(("Network","WARNING - killing file xfer while leaving mission sync state!!!\n"));
8335 multi_xfer_abort(Net_player->s_info.xfer_handle);
8336 Net_player->s_info.xfer_handle;
8340 void multi_sync_post_init()
8342 multi_reset_timestamps();
8344 Multi_state_timestamp = timestamp(0);
8347 ml_string(NOX("Performing post-briefing data sync"));
8349 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8350 multi_common_add_text(XSTR("Server performing sync\n",830),1);
8352 multi_common_add_text(XSTR("Client performing sync\n",838),1);
8355 // everyone should re-initialize these
8356 init_multiplayer_stats();
8358 // reset all sequencing info
8359 multi_oo_reset_sequencing();
8361 // if I am not the master of the game, then send the firing information for my ship
8363 if ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) ){
8364 send_firing_info_packet();
8367 // if I'm not a standalone server, load up the countdown stuff
8368 if(!(Game_mode & GM_STANDALONE_SERVER)){
8369 Multi_sync_countdown_anim = NULL;
8370 Multi_sync_countdown_instance = NULL;
8371 Multi_sync_countdown_anim = anim_load(Multi_sync_countdown_fname[gr_screen.res]);
8372 if(Multi_sync_countdown_anim == NULL){
8373 nprintf(("General","WARNING!, Could not load countdown animation %s!\n",Multi_sync_countdown_fname[gr_screen.res]));
8377 // create objects for all permanent observers
8378 multi_obs_level_init();
8380 // clear the game start countdown timer
8381 Multi_sync_countdown_timer = -1.0f;
8382 Multi_sync_countdown = -1;
8384 // if this is a team vs. team mission, mark all ship teams appropriately
8385 if(Netgame.type_flags & NG_TYPE_TEAM){
8386 multi_team_mark_all_ships();
8389 Mission_sync_flags = 0;
8390 Multi_sync_launch_pressed = 0;
8393 #define MULTI_POST_TIMESTAMP 7000
8395 extern int create_wings();
8397 void multi_sync_post_do()
8401 // only if the host is also the master should he be doing this (non-standalone situation)
8402 if ( Net_player->flags & NETINFO_FLAG_AM_MASTER ) {
8404 // once everyone gets to this screen, send them the ship classes of all ships.
8405 if(multi_netplayer_state_check(NETPLAYER_STATE_WAITING) && !(Mission_sync_flags & MS_FLAG_POST_DATA)) {
8406 // only the host should ever do this
8407 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8408 // at this point we want to delete all necessary ships, change all necessary ship classes, and set all weapons up
8409 multi_ts_create_wings();
8411 // update player ets settings
8412 for(idx=0;idx<MAX_PLAYERS;idx++){
8413 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].player->objnum != -1)){
8414 multi_server_update_player_weapons(&Net_players[idx],&Ships[Objects[Net_players[idx].player->objnum].instance]);
8419 // note that this is done a little differently for standalones and nonstandalones
8420 send_post_sync_data_packet();
8422 multi_common_add_text(XSTR("Sending post briefing block information\n",839),1);
8424 Mission_sync_flags |= MS_FLAG_POST_DATA;
8427 // send weapon slots data
8428 if(multi_netplayer_state_check(NETPLAYER_STATE_POST_DATA_ACK) && !(Mission_sync_flags & MS_FLAG_WSS_SLOTS)) {
8429 // note that this is done a little differently for standalones and nonstandalones
8430 if(Netgame.type_flags & NG_TYPE_TEAM){
8431 send_wss_slots_data_packet(0,0);
8432 send_wss_slots_data_packet(1,1);
8434 send_wss_slots_data_packet(0,1);
8437 multi_common_add_text(XSTR("Sending weapon slots information\n",840),1);
8439 Mission_sync_flags |= MS_FLAG_WSS_SLOTS;
8442 // once weapon information is received, send player settings info
8443 if ( multi_netplayer_state_check(NETPLAYER_STATE_WSS_ACK) && !(Mission_sync_flags & MS_FLAG_PSETTINGS)) {
8444 send_player_settings_packet();
8446 // server (specifically, the standalone), should set this here
8447 Net_player->state = NETPLAYER_STATE_SETTINGS_ACK;
8448 send_netplayer_update_packet();
8450 multi_common_add_text(XSTR("Sending player settings packets\n",841),1);
8452 Mission_sync_flags |= MS_FLAG_PSETTINGS;
8455 // check to see if the countdown timer has started and act appropriately
8456 if( Multi_sync_countdown_timer > -1.0f ) {
8458 // increment by frametime.
8459 Multi_sync_countdown_timer += flFrametime;
8461 // if the animation is not playing, start it
8462 if(!(Game_mode & GM_STANDALONE_SERVER) && (Multi_sync_countdown_instance == NULL) && (Multi_sync_countdown_anim != NULL)){
8463 anim_play_struct aps;
8465 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]);
8466 aps.screen_id = GS_STATE_MULTI_MISSION_SYNC;
8467 aps.framerate_independent = 1;
8469 Multi_sync_countdown_instance = anim_play(&aps);
8472 // if the next second has expired
8473 if( Multi_sync_countdown_timer >= 1.0f ) {
8475 Multi_sync_countdown--;
8476 Multi_sync_countdown_timer = 0.0f;
8478 // if the countdown has reached 0, launch the mission
8479 if(Multi_sync_countdown == 0){
8480 Multi_sync_countdown_timer = -1.0f;
8482 Multi_sync_launch_pressed = 0;
8483 multi_sync_launch();
8485 // otherwise send a countdown packet
8487 send_countdown_packet(Multi_sync_countdown);
8492 // jump into the mission myself
8493 if((Multi_sync_countdown == 0) && multi_netplayer_state_check(NETPLAYER_STATE_IN_MISSION)){
8494 if(!((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !Multi_sync_launch_pressed)){
8495 Net_player->state = NETPLAYER_STATE_IN_MISSION;
8496 Netgame.game_state = NETGAME_STATE_IN_MISSION;
8497 gameseq_post_event(GS_EVENT_ENTER_GAME);
8499 multi_common_add_text(XSTR("Moving into game\n",842),1);
8503 // maybe start the animation countdown
8504 if((Multi_sync_countdown >= 0) && (Multi_sync_countdown_instance == NULL) && (Multi_sync_countdown_anim != NULL)){
8505 anim_play_struct aps;
8507 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]);
8508 aps.screen_id = GS_STATE_MULTI_MISSION_SYNC;
8509 aps.framerate_independent = 1;
8511 Multi_sync_countdown_instance = anim_play(&aps);
8515 // host - specific stuff
8516 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8517 // create the launch button so the host can click
8518 if( Sync_test && multi_netplayer_state_check(NETPLAYER_STATE_SETTINGS_ACK) ){
8519 multi_sync_create_launch_button();
8524 if(!(Game_mode & GM_STANDALONE_SERVER)){
8526 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8527 if(Multi_sync_bitmap != -1){
8528 gr_set_bitmap(Multi_sync_bitmap);
8531 Multi_sync_window.draw();
8533 multi_sync_blit_screen_all();
8539 void multi_sync_post_close()
8543 // if I'm not a standalone server, unload up the countdown stuff
8544 if(!(Game_mode & GM_STANDALONE_SERVER)){
8545 // release all rendering animation instances (should only be 1)
8546 anim_release_all_instances(GS_STATE_MULTI_MISSION_SYNC);
8547 Multi_sync_countdown_instance = NULL;
8549 // free up the countdown animation
8550 if(Multi_sync_countdown_anim != NULL){
8551 anim_free(Multi_sync_countdown_anim);
8552 Multi_sync_countdown_anim = NULL;
8556 // all players should reset sequencing
8557 for(idx=0;idx<MAX_PLAYERS;idx++){
8558 if(Net_player->flags & NETINFO_FLAG_CONNECTED){
8559 Net_players[idx].client_cinfo_seq = 0;
8560 Net_players[idx].client_server_seq = 0;
8564 // multiplayer dogfight
8565 multi_df_level_pre_enter();
8567 // clients should clear obj_pair array and add pair for themselves
8569 if ( MULTIPLAYER_CLIENT ) {
8571 obj_add_pairs( OBJ_INDEX(Player_obj) );
8576 void multi_sync_display_name(char *name,int index,int np_index)
8578 char fit[CALLSIGN_LEN];
8580 // make sure the string actually fits
8583 // if we're in team vs. team mode
8584 if(Netgame.type_flags & NG_TYPE_TEAM){
8585 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]);
8587 // if this is the currently selected player, draw him highlighted
8588 if(np_index == Multi_sync_player_select){
8589 gr_set_color_fast(&Color_text_selected);
8591 gr_set_color_fast(&Color_text_normal);
8595 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);
8597 // blit his team icon
8599 if(Net_players[np_index].p_info.team == 0){
8600 // blit the team captain icon
8601 if(Net_players[np_index].flags & NETINFO_FLAG_TEAM_CAPTAIN){
8602 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
8603 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT]);
8604 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);
8607 // normal team member icon
8609 if(Multi_common_icons[MICON_TEAM0] != -1){
8610 gr_set_bitmap(Multi_common_icons[MICON_TEAM0]);
8611 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);
8616 else if(Net_players[np_index].p_info.team == 1){
8617 // blit the team captain icon
8618 if(Net_players[np_index].flags & NETINFO_FLAG_TEAM_CAPTAIN){
8619 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
8620 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT]);
8621 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);
8624 // normal team member icon
8626 if(Multi_common_icons[MICON_TEAM1] != -1){
8627 gr_set_bitmap(Multi_common_icons[MICON_TEAM1]);
8628 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);
8633 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]);
8635 // if this is the currently selected player, draw him highlighted
8636 if(np_index == Multi_sync_player_select){
8637 gr_set_color_fast(&Color_text_selected);
8639 gr_set_color_fast(&Color_text_normal);
8643 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);
8646 // maybe blit his CD status icon
8647 if((Net_players[np_index].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
8648 gr_set_bitmap(Multi_common_icons[MICON_CD]);
8649 gr_bitmap(Ms_status_coords[gr_screen.res][MS_X_COORD], Ms_status_coords[gr_screen.res][MS_Y_COORD] + (index * 10));
8653 void multi_sync_display_status(char *status,int index)
8657 // make sure the string actually fits
8658 strcpy(fit, status);
8659 gr_force_fit_string(fit, 250, Ms_status2_coords[gr_screen.res][MS_W_COORD] - 20);
8660 gr_set_color_fast(&Color_bright);
8661 gr_string(Ms_status2_coords[gr_screen.res][MS_X_COORD], Ms_status2_coords[gr_screen.res][MS_Y_COORD] + (index * 10), fit);
8664 void multi_sync_force_start_pre()
8667 int want_state = NETPLAYER_STATE_SLOT_ACK; // kick any players who are still in this state
8669 // go through the player list and boot anyone who isn't in the right state
8670 for(idx=0;idx<MAX_PLAYERS;idx++){
8671 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx]) && (Net_players[idx].state == want_state)){
8672 multi_kick_player(idx,0);
8677 void multi_sync_force_start_post()
8681 int num_kill_states;
8683 // determine the state we want all players in so that we can find those who are not in the state
8684 kill_state[0] = NETPLAYER_STATE_BRIEFING;
8685 kill_state[1] = NETPLAYER_STATE_SHIP_SELECT;
8686 kill_state[2] = NETPLAYER_STATE_WEAPON_SELECT;
8687 num_kill_states = 3;
8689 // go through the player list and boot anyone who isn't in the right state
8690 for(idx=0;idx<MAX_PLAYERS;idx++){
8691 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx])){
8692 // check against all kill state
8693 for(idx2 = 0;idx2<num_kill_states;idx2++){
8694 if(Net_players[idx].state == kill_state[idx2]){
8695 multi_kick_player(idx,0);
8703 void multi_sync_start_countdown()
8705 // don't allow repeat button presses
8706 if(Multi_sync_launch_pressed){
8710 Multi_sync_launch_pressed = 1;
8712 // if I'm the server, begin the countdown
8713 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8714 gamesnd_play_iface(SND_COMMIT_PRESSED);
8715 Multi_sync_countdown_timer = 0.0f;
8716 Multi_sync_countdown = MULTI_SYNC_COUNTDOWN_TIME;
8718 // send an initial countdown value
8719 send_countdown_packet(Multi_sync_countdown);
8721 // otherwise send the "start countdown" packet to the standalone
8723 Assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
8724 send_countdown_packet(-1);
8728 void multi_sync_launch()
8730 // don't allow repeat button presses
8731 if(Multi_sync_launch_pressed){
8735 Multi_sync_launch_pressed = 1;
8738 ml_printf(NOX("Entering mission %s"), Game_current_mission_filename);
8740 // tell everyone to jump into the mission
8741 send_jump_into_mission_packet();
8742 Multi_state_timestamp = timestamp(MULTI_POST_TIMESTAMP);
8744 // set the # of players at the start of the mission
8745 Multi_num_players_at_start = multi_num_players();
8746 nprintf(("Network","# of players at start of mission : %d\n", Multi_num_players_at_start));
8748 // initialize datarate limiting for all clients
8749 multi_oo_rate_init_all();
8751 multi_common_add_text(XSTR("Sending mission start packet\n",843),1);
8754 void multi_sync_create_launch_button()
8756 if (!Multi_launch_button_created) {
8757 // create the object
8758 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);
8760 // set the sound to play when highlighted
8761 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_highlight_action(common_play_highlight_sound);
8763 // set the ani for the button
8764 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_bmaps(Multi_sync_buttons[gr_screen.res][MS_LAUNCH].filename, 3, 0);
8767 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.link_hotspot(Multi_sync_buttons[gr_screen.res][MS_LAUNCH].hotspot);
8770 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_hotkey(KEY_CTRLED+KEY_ENTER);
8772 // create the text for the button
8773 Multi_sync_window.add_XSTR(&Multi_sync_text[gr_screen.res][MST_LAUNCH]);
8775 // increment the button count so we start checking this one
8776 Multi_sync_button_count++;
8778 Multi_launch_button_created = 1;
8782 void multi_sync_handle_plist()
8788 // if we don't have a currently selected player, select one
8789 if((Multi_sync_player_select < 0) || !MULTI_CONNECTED(Net_players[Multi_sync_player_select])){
8790 for(idx=0;idx<MAX_PLAYERS;idx++){
8791 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
8792 Multi_sync_player_select = idx;
8798 // check for button list presses
8799 if(Multi_sync_plist_button.pressed()){
8800 // get the y mouse coords
8801 Multi_sync_plist_button.get_mouse_pos(NULL,&my);
8803 // get the index of the item selected
8804 select_index = my / 10;
8806 // if the index is greater than the current # connections, do nothing
8807 if(select_index > (multi_num_connections() - 1)){
8811 // translate into an absolute Net_players[] index (get the Nth net player)
8812 Multi_sync_player_select = -1;
8813 for(idx=0;idx<MAX_PLAYERS;idx++){
8814 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
8818 // if we've found the item we're looking for
8819 if(select_index < 0){
8820 Multi_sync_player_select = idx;
8825 // if for some bizarre reason, this is an invalid player, unselect him and wait for the next interation
8827 if((Multi_sync_player_select >= 0) && (!MULTI_CONNECTED(Net_players[Multi_sync_player_select]) || MULTI_STANDALONE(Net_players[Multi_sync_player_select])) ){
8828 Multi_sync_player_select = -1;
8834 // -------------------------------------------------------------------------------------------------------------
8836 // MULTIPLAYER DEBRIEF SCREEN
8839 // other relevant data
8840 int Multi_debrief_accept_hit;
8841 int Multi_debrief_replay_hit;
8843 // set if the server has left the game
8844 int Multi_debrief_server_left = 0;
8846 // if we've reported on TvT status all players are in the debrief
8847 int Multi_debrief_reported_tvt = 0;
8849 // whether stats are being accepted
8850 // -1 == no decision yet
8853 int Multi_debrief_stats_accept_code = -1;
8855 int Multi_debrief_server_framecount = 0;
8857 float Multi_debrief_time = 0.0f;
8858 float Multi_debrief_resend_time = 10.0f;
8860 void multi_debrief_init()
8864 Multi_debrief_time = 0.0f;
8865 Multi_debrief_resend_time = 10.0f;
8867 // do this to notify the standalone or the normal server that we're in the debrief state and ready to receive packets
8868 if (!(Net_player->flags & NETINFO_FLAG_AM_MASTER)) {
8869 Net_player->state = NETPLAYER_STATE_DEBRIEF;
8870 send_netplayer_update_packet();
8873 // unflag some stuff
8874 for(idx=0;idx<MAX_PLAYERS;idx++){
8875 if(MULTI_CONNECTED(Net_players[idx])){
8876 Net_players[idx].flags &= ~(NETINFO_FLAG_RESPAWNING | NETINFO_FLAG_LIMBO | NETINFO_FLAG_WARPING_OUT);
8880 // if text input mode is active, clear it
8881 multi_msg_text_flush();
8883 // the server has not left yet
8884 Multi_debrief_server_left = 0;
8886 // have not hit accept or replay yet
8887 Multi_debrief_accept_hit = 0;
8888 Multi_debrief_replay_hit = 0;
8890 // stats have not been accepted yet
8891 Multi_debrief_stats_accept_code = -1;
8893 // mark stats as not being store yet
8894 Netgame.flags &= ~(NG_FLAG_STORED_MT_STATS);
8896 // no report on TvT yet
8897 Multi_debrief_reported_tvt = 0;
8899 Multi_debrief_server_framecount = 0;
8902 void multi_debrief_do_frame()
8904 Multi_debrief_time += flFrametime;
8906 // set the netgame state to be debriefing when appropriate
8907 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)){
8908 Netgame.game_state = NETGAME_STATE_DEBRIEF;
8909 send_netgame_update_packet();
8912 // evaluate all server stuff
8913 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8914 multi_debrief_server_process();
8918 void multi_debrief_close()
8920 if ( MULTIPLAYER_CLIENT && (Netgame.game_state == NETGAME_STATE_MISSION_SYNC) ){
8921 gamesnd_play_iface( SND_COMMIT_PRESSED );
8925 // handle optional mission loop
8926 void multi_maybe_set_mission_loop()
8928 int cur = Campaign.current_mission;
8929 if (Campaign.missions[cur].has_mission_loop) {
8930 Assert(Campaign.loop_mission != CAMPAIGN_LOOP_MISSION_UNINITIALIZED);
8932 bool require_repeat_mission = (Campaign.current_mission == Campaign.next_mission);
8934 // check for (1) mission loop available, (2) dont have to repeat last mission
8935 if ( (Campaign.missions[cur].has_mission_loop && (Campaign.loop_mission != -1)) && !require_repeat_mission ) {
8938 debrief_assemble_optional_mission_popup_text(buffer, Campaign.missions[cur].mission_loop_desc);
8940 int choice = popup(0 , 2, POPUP_NO, POPUP_YES, buffer);
8942 Campaign.loop_enabled = 1;
8943 Campaign.next_mission = Campaign.loop_mission;
8948 // handle all cases for when the accept key is hit in a multiplayer debriefing
8949 void multi_debrief_accept_hit()
8951 // if we already accepted, do nothing
8952 // but he may need to hit accept again after the server has left the game, so allow this
8953 if(Multi_debrief_accept_hit){
8957 // mark this so that we don't hit it again
8958 Multi_debrief_accept_hit = 1;
8960 gamesnd_play_iface(SND_COMMIT_PRESSED);
8962 // if the server has left the game, always just end the game.
8963 if(Multi_debrief_server_left){
8964 if(!multi_quit_game(PROMPT_ALL)){
8965 Multi_debrief_server_left = 1;
8966 Multi_debrief_accept_hit = 0;
8968 Multi_debrief_server_left = 0;
8971 // query the host and see if he wants to accept stats
8972 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8973 // if we're on a tracker game, he gets no choice for storing stats
8974 if(MULTI_IS_TRACKER_GAME){
8975 multi_maybe_set_mission_loop();
8977 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));
8979 // evaluate the result
8984 Multi_debrief_accept_hit = 0;
8987 // set the accept code to be "not accepting"
8989 multi_debrief_stats_toss();
8990 multi_maybe_set_mission_loop();
8993 // accept the stats and continue
8995 multi_debrief_stats_accept();
8996 multi_maybe_set_mission_loop();
9002 // set my netplayer state to be "debrief_accept", and be done with it
9003 Net_player->state = NETPLAYER_STATE_DEBRIEF_ACCEPT;
9004 send_netplayer_update_packet();
9008 // handle all cases for when the escape key is hit in a multiplayer debriefing
9009 void multi_debrief_esc_hit()
9013 // if the server has left
9014 if(Multi_debrief_server_left){
9015 multi_quit_game(PROMPT_ALL);
9020 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
9021 // if the stats have already been accepted
9022 if((Multi_debrief_stats_accept_code != -1) || (MULTI_IS_TRACKER_GAME)){
9023 multi_quit_game(PROMPT_HOST);
9025 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));
9027 // evaluate the result
9034 // set the accept code to be "not accepting"
9036 multi_debrief_stats_toss();
9037 multi_quit_game(PROMPT_NONE);
9040 // accept the stats and continue
9042 multi_debrief_stats_accept();
9043 multi_quit_game(PROMPT_NONE);
9048 // if the stats haven't been accepted yet, or this is a tracker game
9049 if((Multi_debrief_stats_accept_code == -1) && !(MULTI_IS_TRACKER_GAME)){
9050 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));
9052 // evaluate the result
9054 multi_quit_game(PROMPT_NONE);
9057 // otherwise go through the normal endgame channels
9059 multi_quit_game(PROMPT_ALL);
9064 void multi_debrief_replay_hit()
9066 // only the host should ever get here
9067 Assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
9069 // if the button was already pressed, do nothing
9070 if(Multi_debrief_accept_hit){
9074 // same as hittin the except button except no stats are kept
9075 Multi_debrief_accept_hit = 1;
9077 // mark myself as being in the replay state so we know what to do next
9078 Net_player->state = NETPLAYER_STATE_DEBRIEF_REPLAY;
9079 send_netplayer_update_packet();
9082 // call this when the server has left and we would otherwise be saying "contact lost with server
9083 void multi_debrief_server_left()
9086 Multi_debrief_server_left = 1;
9088 // undo any "accept" hit so that clients can hit accept again to leave
9089 Multi_debrief_accept_hit = 0;
9092 void multi_debrief_stats_accept()
9094 // don't do anything if we've already accepted
9095 if(Multi_debrief_stats_accept_code != -1){
9099 Multi_debrief_stats_accept_code = 1;
9101 // if we're the host, and we're on a standalone, tell the standalone to begin the stats storing process
9102 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) || (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
9103 // send a packet to the players telling them to store their stats
9104 send_store_stats_packet(1);
9107 // add a chat line saying "stats have been accepted"
9108 multi_display_chat_msg(XSTR("<stats have been accepted>",850),0,0);
9111 ml_string(NOX("Stats stored"));
9114 void multi_debrief_stats_toss()
9116 // don't do anything if we've already accepted
9117 if(Multi_debrief_stats_accept_code != -1){
9121 Multi_debrief_stats_accept_code = 0;
9123 // if we're the host, and we're on a standalone, tell everyone to "toss" stats
9124 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) || (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
9125 // send a packet to the players telling them to store their stats
9126 send_store_stats_packet(0);
9129 // add a chat line saying "stats have been accepted"
9130 multi_display_chat_msg(XSTR("<stats have been tossed>",851),0,0);
9133 ml_string(NOX("Stats tossed"));
9136 int multi_debrief_stats_accept_code()
9138 return Multi_debrief_stats_accept_code;
9141 void multi_debrief_server_process()
9144 int player_status,other_status;
9146 Multi_debrief_server_framecount++;
9148 // if we're > 10 seconds into the debrief and not everyone is here, try warping everyone out again
9149 if((Multi_debrief_time >= Multi_debrief_resend_time) && !multi_netplayer_state_check3(NETPLAYER_STATE_DEBRIEF, NETPLAYER_STATE_DEBRIEF_ACCEPT, NETPLAYER_STATE_DEBRIEF_REPLAY, 1)){
9150 // find all players who are not in the debrief state and hit them with the endgame packet
9151 for(idx=0; idx<MAX_PLAYERS; idx++){
9152 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)) ){
9153 send_endgame_packet(&Net_players[idx]);
9158 Multi_debrief_resend_time += 7.0f;
9161 // evaluate the status of all players in the game (0 == not ready, 1 == ready to continue, 2 == ready to replay)
9164 // check all players
9165 for(idx=0;idx<MAX_PLAYERS;idx++){
9166 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_HOST(Net_players[idx])){
9167 if(Net_players[idx].state != NETPLAYER_STATE_DEBRIEF_ACCEPT){
9174 // if we haven't already reported TvT results
9175 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)){
9176 multi_team_report();
9177 Multi_debrief_reported_tvt = 1;
9180 // if all other players are good to go, check the host
9182 // if he is ready to continue
9183 if(Netgame.host->state == NETPLAYER_STATE_DEBRIEF_ACCEPT){
9186 // if he wants to replay the mission
9187 else if(Netgame.host->state == NETPLAYER_STATE_DEBRIEF_REPLAY){
9190 // if he is not ready
9195 // if all players are _not_ good to go
9200 // if we're in the debriefing state in a campaign mode, process accordingly
9201 if(Netgame.campaign_mode == MP_CAMPAIGN){
9202 multi_campaign_do_debrief(player_status);
9204 // otherwise process as normal (looking for all players to be ready to go to the next mission
9206 if(player_status == 1){
9207 multi_flush_mission_stuff();
9209 // set the netgame state to be forming and continue
9210 Netgame.game_state = NETGAME_STATE_FORMING;
9211 send_netgame_update_packet();
9213 // move to the proper state
9214 if(Game_mode & GM_STANDALONE_SERVER){
9215 gameseq_post_event(GS_EVENT_STANDALONE_MAIN);
9217 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
9220 multi_reset_timestamps();
9221 } else if(player_status == 2){
9222 multi_flush_mission_stuff();
9224 // tell everyone to move into the pre-briefing sync state
9225 Netgame.game_state = NETGAME_STATE_MISSION_SYNC;
9226 send_netgame_update_packet();
9228 // move back to the mission sync screen for the same mission again
9229 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
9230 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
9232 multi_reset_timestamps();
9238 // -------------------------------------------------------------------------------------------------------------
9240 // MULTIPLAYER PASSWORD POPUP
9245 static char *Multi_pwd_bitmap_fname[GR_NUM_RESOLUTIONS] = {
9246 "Password", // GR_640
9247 "2_Password" // GR_1024
9250 static char *Multi_pwd_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
9251 "Password-M", // GR_640
9252 "2_Password-M" // GR_1024
9257 // constants for coordinate lookup
9258 #define MPWD_X_COORD 0
9259 #define MPWD_Y_COORD 1
9260 #define MPWD_W_COORD 2
9261 #define MPWD_H_COORD 3
9264 #define MULTI_PWD_NUM_BUTTONS 2
9265 #define MPWD_CANCEL 0
9266 #define MPWD_COMMIT 1
9268 // password area defs
9269 int Mpwd_coords[GR_NUM_RESOLUTIONS][4] = {
9278 UI_WINDOW Multi_pwd_window; // the window object for the join screen
9279 UI_INPUTBOX Multi_pwd_passwd; // for Netgame.passwd
9280 int Multi_pwd_bitmap; // the background bitmap
9281 int Multi_passwd_background = -1;
9282 int Multi_passwd_done = -1;
9283 int Multi_passwd_running = 0;
9286 ui_button_info Multi_pwd_buttons[GR_NUM_RESOLUTIONS][MULTI_PWD_NUM_BUTTONS] = {
9288 ui_button_info("PWB_00", 411, 151, 405, 141, 0),
9289 ui_button_info("PWB_01", 460, 151, 465, 141, 1),
9292 ui_button_info("2_PWB_00", 659, 242, 649, 225, 0),
9293 ui_button_info("2_PWB_01", 737, 242, 736, 225, 1),
9298 #define MULTI_PWD_NUM_TEXT 3
9299 UI_XSTR Multi_pwd_text[GR_NUM_RESOLUTIONS][MULTI_PWD_NUM_TEXT] = {
9301 { "Cancel", 387, 400, 141, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[0][MPWD_CANCEL].button},
9302 { "Commit", 1062, 455, 141, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[0][MPWD_COMMIT].button},
9303 { "Enter Password", 1332, 149, 92, UI_XSTR_COLOR_GREEN, -1, NULL},
9306 { "Cancel", 387, 649, 225, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[1][MPWD_CANCEL].button},
9307 { "Commit", 1062, 736, 225, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[1][MPWD_COMMIT].button},
9308 { "Enter Password", 1332, 239, 148, UI_XSTR_COLOR_GREEN, -1, NULL},
9312 // initialize all graphics, etc
9313 void multi_passwd_init()
9317 // store the background as it currently is
9318 Multi_passwd_background = gr_save_screen();
9320 // create the interface window
9321 Multi_pwd_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
9322 Multi_pwd_window.set_mask_bmap(Multi_pwd_bitmap_mask_fname[gr_screen.res]);
9324 // load the background bitmap
9325 Multi_pwd_bitmap = bm_load(Multi_pwd_bitmap_fname[gr_screen.res]);
9326 if(Multi_pwd_bitmap < 0){
9327 // we failed to load the bitmap - this is very bad
9331 // create the interface buttons
9332 for(idx=0; idx<MULTI_PWD_NUM_BUTTONS; idx++){
9333 // create the object
9334 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);
9336 // set the sound to play when highlighted
9337 Multi_pwd_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
9339 // set the ani for the button
9340 Multi_pwd_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pwd_buttons[gr_screen.res][idx].filename);
9343 Multi_pwd_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pwd_buttons[gr_screen.res][idx].hotspot);
9347 for(idx=0; idx<MULTI_PWD_NUM_TEXT; idx++){
9348 Multi_pwd_window.add_XSTR(&Multi_pwd_text[gr_screen.res][idx]);
9351 // create the password input box
9352 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);
9353 Multi_pwd_passwd.set_focus();
9355 // link the enter key to ACCEPT
9356 Multi_pwd_buttons[gr_screen.res][MPWD_COMMIT].button.set_hotkey(KEY_ENTER);
9358 Multi_passwd_done = -1;
9359 Multi_passwd_running = 1;
9362 // close down all graphics, etc
9363 void multi_passwd_close()
9365 // unload any bitmaps
9366 bm_release(Multi_pwd_bitmap);
9368 // destroy the UI_WINDOW
9369 Multi_pwd_window.destroy();
9371 // free up the saved background screen
9372 if(Multi_passwd_background >= 0){
9373 gr_free_screen(Multi_passwd_background);
9374 Multi_passwd_background = -1;
9377 Multi_passwd_running = 0;
9380 // process any button pressed
9381 void multi_passwd_process_buttons()
9383 // if the accept button was pressed
9384 if(Multi_pwd_buttons[gr_screen.res][MPWD_COMMIT].button.pressed()){
9385 gamesnd_play_iface(SND_USER_SELECT);
9386 Multi_passwd_done = 1;
9389 // if the cancel button was pressed
9390 if(Multi_pwd_buttons[gr_screen.res][MPWD_CANCEL].button.pressed()){
9391 gamesnd_play_iface(SND_USER_SELECT);
9392 Multi_passwd_done = 0;
9396 // run the passwd popup
9397 void multi_passwd_do(char *passwd)
9401 while(Multi_passwd_done == -1){
9402 // set frametime and run background stuff
9403 game_set_frametime(-1);
9404 game_do_state_common(gameseq_get_state());
9406 k = Multi_pwd_window.process();
9408 // process any keypresses
9411 // set this to indicate the user has cancelled for one reason or another
9412 Multi_passwd_done = 0;
9416 // if the input box text has changed
9417 if(Multi_pwd_passwd.changed()){
9419 Multi_pwd_passwd.get_text(passwd);
9422 // process any button pressed
9423 multi_passwd_process_buttons();
9425 // draw the background, etc
9428 if(Multi_passwd_background >= 0){
9429 gr_restore_screen(Multi_passwd_background);
9431 gr_set_bitmap(Multi_pwd_bitmap);
9433 Multi_pwd_window.draw();
9440 // bring up the password string popup, fill in passwd (return 1 if accept was pressed, 0 if cancel was pressed)
9441 int multi_passwd_popup(char *passwd)
9443 // if the popup is already running for some reason, don't do anything
9444 if(Multi_passwd_running){
9448 // initialize all graphics
9449 multi_passwd_init();
9452 multi_passwd_do(passwd);
9454 // shut everything down
9455 multi_passwd_close();
9457 return Multi_passwd_done;