2 * $Logfile: /Freespace2/code/Network/MultiUI.cpp $
7 * C file for all the UI controls of the mulitiplayer screens
10 * Revision 1.1 2002/05/03 03:28:10 root
14 * 94 6/16/00 3:16p Jefff
15 * sim of the year dvd version changes, a few german soty localization
18 * 93 10/14/99 2:51p Jefff
21 * 92 10/13/99 3:50p Jefff
22 * fixed unnumbered XSTRs
24 * 91 9/15/99 1:45a Dave
25 * Don't init joystick on standalone. Fixed campaign mode on standalone.
26 * Fixed no-score-report problem in TvT
28 * 90 9/14/99 12:51a Jefff
31 * 89 9/13/99 4:52p Dave
34 * 88 9/13/99 11:30a Dave
35 * Added checkboxes and functionality for disabling PXO banners as well as
36 * disabling d3d zbuffer biasing.
38 * 87 9/12/99 10:06p Jefff
39 * changed instances of "Squad War" to "SquadWar"
41 * 86 9/03/99 1:32a Dave
42 * CD checking by act. Added support to play 2 cutscenes in a row
43 * seamlessly. Fixed super low level cfile bug related to files in the
44 * root directory of a CD. Added cheat code to set campaign mission # in
47 * 85 9/01/99 10:49p Dave
48 * Added nice SquadWar checkbox to the client join wait screen.
50 * 84 8/30/99 2:49p Jefff
52 * 83 8/26/99 8:49p Jefff
53 * Updated medals screen and about everything that ever touches medals in
54 * one way or another. Sheesh.
56 * 82 8/25/99 4:38p Dave
57 * Updated PXO stuff. Make squad war report stuff much more nicely.
59 * 81 8/20/99 2:09p Dave
62 * 80 8/20/99 10:06a Jefff
63 * removed closed/rstricted buttons from multi start screen
65 * 79 8/18/99 11:30a Jefff
67 * 78 8/18/99 10:38a Jefff
69 * 77 8/16/99 4:06p Dave
70 * Big honking checkin.
72 * 76 8/16/99 1:08p Jefff
73 * added sounds to a few controls, made input boxes lose focus on ENTER
75 * 75 8/16/99 9:52a Jefff
76 * fixed bitmap loading on buttons in multi-sync screen
78 * 74 8/11/99 5:54p Dave
79 * Fixed collision problem. Fixed standalone ghost problem.
81 * 73 8/10/99 4:35p Jefff
84 * 72 8/06/99 12:29a Dave
87 * 71 8/05/99 3:13p Jasenw
88 * tweaked some text placement coords.
90 * 70 8/04/99 1:38p Jefff
91 * moved some text in multi join wait
93 * 69 8/03/99 12:45p Dave
96 * 68 7/25/99 5:17p Jefff
97 * campaign descriptions show up on multicreate screen
99 * 67 7/20/99 1:49p Dave
100 * Peter Drake build. Fixed some release build warnings.
102 * 66 7/19/99 2:13p Dave
103 * Added some new strings for Heiko.
105 * 65 7/15/99 9:20a Andsager
106 * FS2_DEMO initial checkin
108 * 64 7/08/99 10:53a Dave
109 * New multiplayer interpolation scheme. Not 100% done yet, but still
110 * better than the old way.
112 * 63 6/30/99 10:49a Jasenw
113 * Fixed coords for new launch countdown ani
115 * 62 6/29/99 7:39p Dave
116 * Lots of small bug fixes.
118 * 61 6/25/99 11:59a Dave
119 * Multi options screen.
121 * 60 6/09/99 2:17p Dave
122 * Fixed up pleasewait bitmap rendering.
124 * 59 6/05/99 3:42p Dave
125 * New multi sync screen.
127 * 58 6/01/99 6:07p Dave
128 * New loading/pause/please wait bar.
130 * 57 5/21/99 6:45p Dave
131 * Sped up ui loading a bit. Sped up localization disk access stuff. Multi
132 * start game screen, multi password, and multi pxo-help screen.
134 * 56 5/06/99 11:10a Dave
135 * Fixed coord on multi create screen.
137 * 55 5/04/99 6:38p Dave
138 * Finished multi join-wait screen.
140 * 54 5/04/99 5:20p Dave
141 * Fixed up multiplayer join screen and host options screen. Should both
144 * 53 5/03/99 11:04p Dave
145 * Most of the way done with the multi join screen.
147 * 52 5/03/99 8:32p Dave
148 * New version of multi host options screen.
150 * 51 4/29/99 2:15p Neilk
151 * slider2 code got modified; changed parameters for create
153 * 50 4/25/99 3:02p Dave
154 * Build defines for the E3 build.
156 * 49 4/21/99 6:15p Dave
157 * Did some serious housecleaning in the beam code. Made it ready to go
158 * for anti-fighter "pulse" weapons. Fixed collision pair creation. Added
159 * a handy macro for recalculating collision pairs for a given object.
161 * 48 4/16/99 5:27p Neilk
162 * added slider support and hir res for multi_create
164 * 47 4/14/99 6:37p Dave
165 * Fixed scroll button bug on host create screen.
167 * 46 4/14/99 5:28p Dave
170 * 45 4/12/99 10:07p Dave
171 * Made network startup more forgiving. Added checkmarks to dogfight
172 * screen for players who hit commit.
174 * 44 4/09/99 2:21p Dave
175 * Multiplayer beta stuff. CD checking.
177 * 43 4/08/99 1:28p Dave
178 * Small bug fixes for refresh button on the multi create screen.
180 * 42 4/08/99 11:55a Neilk
181 * Converted Multi_Create to new artwork (just lowres)
183 * 41 4/08/99 2:10a Dave
184 * Numerous bug fixes for the beta. Added builtin mission info for the
187 * 40 3/20/99 3:48p Andsager
188 * Do mission_loop stuff for PXO
190 * 39 3/10/99 6:50p Dave
191 * Changed the way we buffer packets for all clients. Optimized turret
192 * fired packets. Did some weapon firing optimizations.
194 * 38 3/09/99 6:24p Dave
195 * More work on object update revamping. Identified several sources of
196 * unnecessary bandwidth.
198 * 37 3/08/99 7:03p Dave
199 * First run of new object update system. Looks very promising.
201 * 36 2/25/99 4:19p Dave
202 * Added multiplayer_beta defines. Added cd_check define. Fixed a few
203 * release build warnings. Added more data to the squad war request and
206 * 35 2/24/99 3:26p Anoop
207 * Make sure the host is the only guy who bashes skill level for TvT.
209 * 34 2/24/99 2:25p Dave
210 * Fixed up chatbox bugs. Made squad war reporting better. Fixed a respawn
211 * bug for dogfight more.
213 * 33 2/23/99 2:29p Dave
214 * First run of oldschool dogfight mode.
216 * 32 2/17/99 2:11p Dave
217 * First full run of squad war. All freespace and tracker side stuff
220 * 31 2/12/99 6:16p Dave
221 * Pre-mission Squad War code is 95% done.
223 * 30 2/11/99 3:08p Dave
224 * PXO refresh button. Very preliminary squad war support.
226 * 29 2/08/99 5:07p Dave
227 * FS2 chat server support. FS2 specific validated missions.
229 * 28 2/04/99 6:29p Dave
230 * First full working rev of FS2 PXO support. Fixed Glide lighting
233 * 27 1/30/99 5:08p Dave
234 * More new hi-res stuff.Support for nice D3D textures.
236 * 26 1/29/99 2:08a Dave
237 * Fixed beam weapon collisions with players. Reduced size of scoring
238 * struct for multiplayer. Disabled PXO.
240 * 25 1/15/99 2:36p Neilk
241 * fixed multi_jw coordinates
243 * 24 1/13/99 7:19p Neilk
244 * Converted Mission Brief, Barracks, Synch to high res support
246 * 23 1/12/99 7:17p Neilk
248 * 22 1/12/99 5:45p Dave
249 * Moved weapon pipeline in multiplayer to almost exclusively client side.
250 * Very good results. Bandwidth goes down, playability goes up for crappy
251 * connections. Fixed object update problem for ship subsystems.
253 * 21 1/12/99 4:07a Dave
254 * Put in barracks code support for selecting squad logos. Properly
255 * distribute squad logos in a multiplayer game.
257 * 20 1/11/99 7:19p Neilk
258 * Converted multi_join interface to support multiple resolutions
260 * 19 12/18/98 1:13a Dave
261 * Rough 1024x768 support for Direct3D. Proper detection and usage through
264 * 18 12/17/98 4:50p Andsager
265 * Added debrief_assemble_optional_mission_popup_text() for single and
268 * 17 12/14/98 12:13p Dave
269 * Spiffed up xfer system a bit. Put in support for squad logo file xfer.
272 * 16 12/10/98 10:19a Andsager
273 * Fix mission loop assert
275 * 15 12/10/98 9:59a Andsager
276 * Fix some bugs with mission loops
278 * 14 12/09/98 1:56p Andsager
279 * Initial checkin of mission loop
281 * 13 12/03/98 5:22p Dave
282 * Ported over Freespace 1 multiplayer ships.tbl and weapons.tbl
285 * 12 11/30/98 1:07p Dave
286 * 16 bit conversion, first run.
288 * 11 11/20/98 11:16a Dave
289 * Fixed up IPX support a bit. Making sure that switching modes and
290 * loading/saving pilot files maintains proper state.
292 * 10 11/19/98 4:57p Dave
293 * Ignore PXO option if IPX is selected.
295 * 9 11/19/98 4:19p Dave
296 * Put IPX sockets back in psnet. Consolidated all multiplayer config
299 * 8 11/19/98 8:04a Dave
300 * Full support for D3-style reliable sockets. Revamped packet lag/loss
301 * system, made it receiver side and at the lowest possible level.
303 * 7 11/17/98 11:12a Dave
304 * Removed player identification by address. Now assign explicit id #'s.
306 * 6 10/19/98 11:15a Dave
307 * Changed requirements for stats storing in PXO mode.
309 * 5 10/16/98 9:40a Andsager
310 * Remove ".h" files from model.h
312 * 4 10/13/98 9:29a Dave
313 * Started neatening up freespace.h. Many variables renamed and
314 * reorganized. Added AlphaColors.[h,cpp]
316 * 3 10/07/98 6:27p Dave
317 * Globalized mission and campaign file extensions. Removed Silent Threat
318 * special code. Moved \cache \players and \multidata into the \data
321 * 2 10/07/98 10:53a Dave
324 * 1 10/07/98 10:50a Dave
326 * 333 10/02/98 3:22p Allender
327 * fix up the -connect option and fix the -port option
329 * 332 9/17/98 9:26p Dave
330 * Externalized new string.
332 * 331 9/17/98 3:08p Dave
333 * PXO to non-pxo game warning popup. Player icon stuff in create and join
334 * game screens. Upped server count refresh time in PXO to 35 secs (from
337 * 330 9/17/98 9:43a Allender
338 * removed an Assert that Dave called bogus.
340 * 329 9/16/98 6:54p Dave
341 * Upped max sexpression nodes to 1800 (from 1600). Changed FRED to sort
342 * the ship list box. Added code so that tracker stats are not stored with
345 * 328 9/15/98 7:24p Dave
346 * Minor UI changes. Localized bunch of new text.
348 * 327 9/15/98 4:03p Dave
349 * Changed readyroom and multi screens to display "st" icon for all
350 * missions with mission disk content (not necessarily just those that
351 * come with Silent Threat).
353 * 326 9/15/98 11:44a Dave
354 * Renamed builtin ships and wepaons appropriately in FRED. Put in scoring
355 * scale factors. Fixed standalone filtering of MD missions to non-MD
358 * 325 9/13/98 9:36p Dave
359 * Support for new info icons for multiplayer missions (from-volition,
360 * valid, mission disk, etc).
362 * 324 9/11/98 4:14p Dave
363 * Fixed file checksumming of < file_size. Put in more verbose kicking and
364 * PXO stats store reporting.
366 * 323 9/10/98 1:17p Dave
367 * Put in code to flag missions and campaigns as being MD or not in Fred
368 * and Freespace. Put in multiplayer support for filtering out MD
369 * missions. Put in multiplayer popups for warning of non-valid missions.
371 * 322 9/04/98 3:51p Dave
372 * Put in validated mission updating and application during stats
375 * 321 8/31/98 2:06p Dave
376 * Make cfile sort the ordering or vp files. Added support/checks for
377 * recognizing "mission disk" players.
379 * 320 8/21/98 1:15p Dave
380 * Put in log system hooks in useful places.
382 * 319 8/20/98 5:31p Dave
383 * Put in handy multiplayer logfile system. Now need to put in useful
384 * applications of it all over the code.
386 * 318 8/12/98 4:53p Dave
387 * Put in 32 bit checksumming for PXO missions. No validation on the
388 * actual tracker yet, though.
390 * 317 8/07/98 10:40a Allender
391 * new command line flags for starting netgames. Only starting currently
392 * works, and PXO isn't implemented yet
394 * 316 7/24/98 9:27a Dave
395 * Tidied up endgame sequencing by removing several old flags and
396 * standardizing _all_ endgame stuff with a single function call.
398 * 315 7/14/98 10:04a Allender
399 * fixed the countdown code to not be reliant on timer_get_fixed_seconds
401 * 314 7/10/98 5:04p Dave
402 * Fix connection speed bug on standalone server.
404 * 313 7/09/98 6:01p Dave
405 * Firsts full version of PXO updater. Put in stub for displaying
408 * 312 7/07/98 2:49p Dave
411 * 311 6/30/98 2:17p Dave
412 * Revised object update system. Removed updates for all weapons. Put
413 * button info back into control info packet.
415 * 310 6/13/98 9:32p Mike
416 * Kill last character in file which caused "Find in Files" to report the
417 * file as "not a text file."
423 #include <winsock.h> // for inet_addr()
426 #include "multiutil.h"
427 #include "multimsgs.h"
433 #include "gamesequence.h"
434 #include "freespace.h"
435 #include "contexthelp.h"
440 #include "missionshipchoice.h"
441 #include "multi_xfer.h"
443 #include "stand_gui.h"
444 #include "linklist.h"
445 #include "multiteamselect.h"
446 #include "missioncampaign.h"
453 #include "missiondebrief.h"
454 #include "multi_ingame.h"
455 #include "multi_kick.h"
456 #include "multi_data.h"
457 #include "multi_campaign.h"
458 #include "multi_team.h"
459 #include "multi_pinfo.h"
460 #include "multi_observer.h"
461 #include "multi_voice.h"
462 #include "multi_endgame.h"
463 #include "managepilot.h"
466 #include "objcollide.h"
468 #include "multi_pmsg.h"
469 #include "multi_obj.h"
470 #include "multi_log.h"
471 #include "alphacolors.h"
472 #include "animplay.h"
473 #include "multi_dogfight.h"
474 #include "missionpause.h"
476 // -------------------------------------------------------------------------------------------------------------
478 // MULTIPLAYER COMMON interface controls
481 // the common text info box stuff. This is lifted almost directly from Alans briefing code (minus the spiffy colored, scrolling
483 int Multi_common_text_coords[GR_NUM_RESOLUTIONS][4] = {
492 int Multi_common_text_max_display[GR_NUM_RESOLUTIONS] = {
497 #define MULTI_COMMON_TEXT_META_CHAR '$'
498 #define MULTI_COMMON_TEXT_MAX_LINE_LENGTH 100
499 #define MULTI_COMMON_TEXT_MAX_LINES 20
500 #define MULTI_COMMON_MAX_TEXT (MULTI_COMMON_TEXT_MAX_LINES * MULTI_COMMON_TEXT_MAX_LINE_LENGTH)
502 char Multi_common_all_text[MULTI_COMMON_MAX_TEXT];
503 char Multi_common_text[MULTI_COMMON_TEXT_MAX_LINES][MULTI_COMMON_TEXT_MAX_LINE_LENGTH];
505 int Multi_common_top_text_line = -1; // where to start displaying from
506 int Multi_common_num_text_lines = 0; // how many lines we have
508 void multi_common_scroll_text_up();
509 void multi_common_scroll_text_down();
510 void multi_common_move_to_bottom();
511 void multi_common_render_text();
512 void multi_common_split_text();
514 #define MAX_IP_STRING 255 // maximum length for ip string
516 void multi_common_scroll_text_up()
518 Multi_common_top_text_line--;
519 if ( Multi_common_top_text_line < 0 ) {
520 Multi_common_top_text_line = 0;
521 if ( !mouse_down(MOUSE_LEFT_BUTTON) )
522 gamesnd_play_iface(SND_GENERAL_FAIL);
525 gamesnd_play_iface(SND_SCROLL);
529 void multi_common_scroll_text_down()
531 Multi_common_top_text_line++;
532 if ( (Multi_common_num_text_lines - Multi_common_top_text_line) < Multi_common_text_max_display[gr_screen.res] ) {
533 Multi_common_top_text_line--;
534 if ( !mouse_down(MOUSE_LEFT_BUTTON) ){
535 gamesnd_play_iface(SND_GENERAL_FAIL);
538 gamesnd_play_iface(SND_SCROLL);
542 void multi_common_move_to_bottom()
544 // if there's nowhere to scroll down, do nothing
545 if(Multi_common_num_text_lines <= Multi_common_text_max_display[gr_screen.res]){
549 Multi_common_top_text_line = Multi_common_num_text_lines - Multi_common_text_max_display[gr_screen.res];
552 void multi_common_set_text(char *str,int auto_scroll)
555 // store the entire string as well
556 if(strlen(str) > MULTI_COMMON_MAX_TEXT){
559 strcpy(Multi_common_all_text,str);
562 // split the whole thing up
563 multi_common_split_text();
565 // scroll to the bottom if we're supposed to
567 multi_common_move_to_bottom();
571 void multi_common_add_text(char *str,int auto_scroll)
574 // store the entire string as well
575 if((strlen(str) + strlen(Multi_common_all_text)) > MULTI_COMMON_MAX_TEXT){
578 strcat(Multi_common_all_text,str);
581 // split the whole thing up
582 multi_common_split_text();
584 // scroll to the bottom if we're supposed to
586 multi_common_move_to_bottom();
590 void multi_common_split_text()
593 int n_chars[MAX_BRIEF_LINES];
594 char *p_str[MAX_BRIEF_LINES];
596 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);
597 Assert(n_lines != -1);
599 for ( i = 0; i < n_lines; i++ ) {
600 Assert(n_chars[i] < MULTI_COMMON_TEXT_MAX_LINE_LENGTH);
601 strncpy(Multi_common_text[i], p_str[i], n_chars[i]);
602 Multi_common_text[i][n_chars[i]] = 0;
603 drop_leading_white_space(Multi_common_text[i]);
606 Multi_common_top_text_line = 0;
607 Multi_common_num_text_lines = n_lines;
610 void multi_common_render_text()
612 int i, fh, line_count;
614 fh = gr_get_font_height();
617 gr_set_color_fast(&Color_text_normal);
618 for ( i = Multi_common_top_text_line; i < Multi_common_num_text_lines; i++ ) {
619 if ( line_count >= Multi_common_text_max_display[gr_screen.res] ){
622 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]);
626 if ( (Multi_common_num_text_lines - Multi_common_top_text_line) > Multi_common_text_max_display[gr_screen.res] ) {
627 gr_set_color_fast(&Color_bright_red);
628 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));
632 // common notification messaging stuff
633 #define MULTI_COMMON_NOTIFY_TIME 3500
634 int Multi_common_join_y[GR_NUM_RESOLUTIONS] = {
638 int Multi_common_create_y[GR_NUM_RESOLUTIONS] = {
643 int Multi_common_jw_y[GR_NUM_RESOLUTIONS] = {
648 int Multi_common_msg_y[GR_NUM_RESOLUTIONS] = {
653 char Multi_common_notify_text[200];
654 int Multi_common_notify_stamp;
656 void multi_common_notify_init()
658 strcpy(Multi_common_notify_text,"");
659 Multi_common_notify_stamp = -1;
662 // add a notification string, drawing appropriately depending on the state/screen we're in
663 void multi_common_add_notify(char *str)
666 strcpy(Multi_common_notify_text,str);
667 Multi_common_notify_stamp = timestamp(MULTI_COMMON_NOTIFY_TIME);
671 // process/display notification messages
672 void multi_common_notify_do()
674 if(Multi_common_notify_stamp != -1){
675 if(timestamp_elapsed(Multi_common_notify_stamp)){
676 Multi_common_notify_stamp = -1;
679 gr_get_string_size(&w,&h,Multi_common_notify_text);
680 gr_set_color_fast(&Color_white);
682 // determine where it should be placed based upon which screen we're on
684 switch(gameseq_get_state()){
685 case GS_STATE_MULTI_JOIN_GAME :
686 y = Multi_common_join_y[gr_screen.res];
688 case GS_STATE_MULTI_HOST_SETUP :
689 y = Multi_common_create_y[gr_screen.res];
691 case GS_STATE_MULTI_CLIENT_SETUP :
692 y = Multi_common_jw_y[gr_screen.res];
694 case GS_STATE_MULTI_START_GAME :
695 y = Multi_common_msg_y[gr_screen.res];
699 gr_string((gr_screen.max_w - w)/2, y, Multi_common_notify_text);
706 int Multi_common_icons[MULTI_NUM_COMMON_ICONS];
708 char *Multi_common_icon_names[MULTI_NUM_COMMON_ICONS] = {
709 "DotRed", // voice denied
710 "DotGreen", // voice recording
711 "OvalGreen", // team 0
712 "OvalGreen01", // team 0 select
714 "OvalRed01", // team 1 select
715 "mp_coop", // coop mission
716 "mp_teams", // TvT mission
717 "mp_furball", // furball mission
718 "icon-volition", // volition mission
719 "icon-valid", // mission is valid
723 // width and height of the icons
724 int Multi_common_icon_dims[MULTI_NUM_COMMON_ICONS][2] = {
725 {11, 11}, // voice denied
726 {11, 11}, // voice recording
728 {11, 11}, // team 0 select
730 {11, 11}, // team 1 select
733 {18, 11}, // mp furball
734 {9, 9}, // volition mission
735 {8, 8}, // mission is valid
739 void multi_load_common_icons()
744 for(idx=0; idx<MULTI_NUM_COMMON_ICONS; idx++){
745 Multi_common_icons[idx] = -1;
746 Multi_common_icons[idx] = bm_load(Multi_common_icon_names[idx]);
750 void multi_unload_common_icons()
755 for(idx=0; idx<MULTI_NUM_COMMON_ICONS; idx++){
756 if(Multi_common_icons[idx] != -1){
757 bm_unload(Multi_common_icons[idx]);
758 Multi_common_icons[idx] = -1;
763 // display any relevant voice status icons
764 void multi_common_voice_display_status()
766 switch(multi_voice_status()){
767 // i have been denied the voice token
768 case MULTI_VOICE_STATUS_DENIED:
769 if(Multi_common_icons[MICON_VOICE_DENIED] != -1){
770 gr_set_bitmap(Multi_common_icons[MICON_VOICE_DENIED]);
775 // i am currently recording
776 case MULTI_VOICE_STATUS_RECORDING:
777 if(Multi_common_icons[MICON_VOICE_RECORDING] != -1){
778 gr_set_bitmap(Multi_common_icons[MICON_VOICE_RECORDING]);
783 // i am currently playing back sound
784 case MULTI_VOICE_STATUS_PLAYING:
787 // the system is currently idle
788 case MULTI_VOICE_STATUS_IDLE:
794 // palette initialization stuff
795 #define MULTI_COMMON_PALETTE_FNAME "InterfacePalette"
798 int Multi_common_interface_palette = -1;
800 void multi_common_load_palette();
801 void multi_common_set_palette();
802 void multi_common_unload_palette();
804 // load in the palette if it doesn't already exist
805 void multi_common_load_palette()
807 if(Multi_common_interface_palette != -1){
811 Multi_common_interface_palette = bm_load(MULTI_COMMON_PALETTE_FNAME);
812 if(Multi_common_interface_palette == -1){
813 nprintf(("Network","Error loading multiplayer common palette!\n"));
817 // set the common palette to be the active one
818 void multi_common_set_palette()
820 // if the palette is not loaded yet, do so now
821 if(Multi_common_interface_palette == -1){
822 multi_common_load_palette();
825 if(Multi_common_interface_palette != -1){
826 #ifndef HARDWARE_ONLY
827 palette_use_bm_palette(Multi_common_interface_palette);
832 // unload the bitmap palette
833 void multi_common_unload_palette()
835 if(Multi_common_interface_palette != -1){
836 bm_unload(Multi_common_interface_palette);
837 Multi_common_interface_palette = -1;
841 void multi_common_verify_cd()
844 // otherwise, call the freespace function to determine if we have a cd
846 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) ){
855 // -------------------------------------------------------------------------------------------------------------
857 // MULTIPLAYER JOIN SCREEN
860 #define MULTI_JOIN_NUM_BUTTONS 11
864 #define MULTI_JOIN_PALETTE "InterfacePalette"
866 static char *Multi_join_bitmap_fname[GR_NUM_RESOLUTIONS] = {
867 "MultiJoin", // GR_640
868 "2_MultiJoin" // GR_1024
871 static char *Multi_join_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
872 "MultiJoin-M", // GR_640
873 "2_MultiJoin-M" // GR_1024
878 char *Mj_slider_name[GR_NUM_RESOLUTIONS] = {
882 int Mj_slider_coords[GR_NUM_RESOLUTIONS][4] = {
892 #define MJ_SCROLL_UP 0
893 #define MJ_SCROLL_DOWN 1
895 #define MJ_SCROLL_INFO_UP 3
896 #define MJ_SCROLL_INFO_DOWN 4
897 #define MJ_JOIN_OBSERVER 5
898 #define MJ_START_GAME 6
904 // uses MULTI_JOIN_REFRESH_TIME as its timestamp
905 int Multi_join_glr_stamp;
907 #define MULTI_JOIN_PING_TIME 15000 // how often we ping all the known servers
908 int Multi_join_ping_stamp;
909 UI_WINDOW Multi_join_window; // the window object for the join screen
910 UI_BUTTON Multi_join_select_button; // for selecting list items
911 UI_SLIDER2 Multi_join_slider; // handy dandy slider
912 int Multi_join_bitmap; // the background bitmap
914 ui_button_info Multi_join_buttons[GR_NUM_RESOLUTIONS][MULTI_JOIN_NUM_BUTTONS] = {
916 ui_button_info( "MJ_00", 1, 57, -1, -1, 0 ), // scroll up
917 ui_button_info( "MJ_02", 1, 297, -1, -1, 2 ), // scroll down
918 ui_button_info( "MJ_03", 10, 338, 65, 364, 3 ), // refresh
919 ui_button_info( "MJ_04", 1, 405, -1, -1, 4 ), // scroll info up
920 ui_button_info( "MJ_05", 1, 446, -1, -1, 5 ), // scroll info down
921 ui_button_info( "MJ_06", 489, 339, -1, -1, 6 ), // join as observer
922 ui_button_info( "MJ_07", 538, 339, -1, -1, 7 ), // create game
923 ui_button_info( "MJ_08", 583, 339, 588, 376, 8 ), // cancel
924 ui_button_info( "MJ_09", 534, 426, -1, -1, 9 ), // help
925 ui_button_info( "MJ_10", 534, 454, -1, -1, 10 ), // options
926 ui_button_info( "MJ_11", 571, 426, 589, 416, 11 ), // join
929 ui_button_info( "2_MJ_00", 2, 92, -1, -1, 0 ), // scroll up
930 ui_button_info( "2_MJ_02", 2, 475, -1, -1, 2 ), // scroll down
931 ui_button_info( "2_MJ_03", 16, 541, 104, 582, 3 ), // refresh
932 ui_button_info( "2_MJ_04", 2, 648, -1, -1, 4 ), // scroll info up
933 ui_button_info( "2_MJ_05", 2, 713, -1, -1, 5 ), // scroll info down
934 ui_button_info( "2_MJ_06", 783, 542, -1, -1, 6 ), // join as observer
935 ui_button_info( "2_MJ_07", 861, 542, -1, -1, 7 ), // create game
936 ui_button_info( "2_MJ_08", 933, 542, 588, 376, 8 ), // cancel
937 ui_button_info( "2_MJ_09", 854, 681, -1, -1, 9 ), // help
938 ui_button_info( "2_MJ_10", 854, 727, -1, -1, 10 ), // options
939 ui_button_info( "2_MJ_11", 914, 681, 937, 668, 11 ), // join
943 #define MULTI_JOIN_NUM_TEXT 13
945 UI_XSTR Multi_join_text[GR_NUM_RESOLUTIONS][MULTI_JOIN_NUM_TEXT] = {
947 {"Refresh", 1299, 65, 364, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_REFRESH].button},
948 {"Join as", 1300, 476, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_JOIN_OBSERVER].button},
949 {"Observer", 1301, 467, 385, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_JOIN_OBSERVER].button},
950 {"Create", 1408, 535, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_START_GAME].button},
951 {"Game", 1302, 541, 385, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_START_GAME].button},
952 {"Cancel", 387, 588, 376, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[0][MJ_CANCEL].button},
953 {"Help", 928, 479, 436, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_HELP].button},
954 {"Options", 1036, 479, 460, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_OPTIONS].button},
955 {"Join", 1303, 589, 416, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[0][MJ_ACCEPT].button},
956 {"Status", 1304, 37, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
957 {"Server", 1305, 116, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
958 {"Players", 1306, 471, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
959 {"Ping", 1307, 555, 37, UI_XSTR_COLOR_GREEN, -1, NULL}
962 {"Refresh", 1299, 104, 582, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_REFRESH].button},
963 {"Join as", 1300, 783, 602, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_JOIN_OBSERVER].button},
964 {"Observer", 1301, 774, 611, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_JOIN_OBSERVER].button},
965 {"Create", 1408, 868, 602, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_START_GAME].button},
966 {"Game", 1302, 872, 611, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_START_GAME].button},
967 {"Cancel", 387, 941, 602, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[1][MJ_CANCEL].button},
968 {"Help", 928, 782, 699, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_HELP].button},
969 {"Options", 1036, 782, 736, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_OPTIONS].button},
970 {"Join", 1303, 937, 668, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[1][MJ_ACCEPT].button},
971 {"Status", 1304, 60, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
972 {"Server", 1305, 186, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
973 {"Players", 1306, 753, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
974 {"Ping", 1307, 888, 60, UI_XSTR_COLOR_GREEN, -1, NULL}
978 // constants for coordinate look ups
984 #define MULTI_JOIN_SENT_WAIT 10000 // wait this long since a join was sent to allow another
985 int Multi_join_sent_stamp;
987 // game information text areas
988 int Mj_max_game_items[GR_NUM_RESOLUTIONS] = {
993 int Mj_list_y[GR_NUM_RESOLUTIONS] = {
998 int Mj_status_coords[GR_NUM_RESOLUTIONS][4] = {
1007 int Mj_game_icon_coords[GR_NUM_RESOLUTIONS][3] = {
1016 int Mj_speed_coords[GR_NUM_RESOLUTIONS][4] = {
1025 int Mj_game_name_coords[GR_NUM_RESOLUTIONS][4] = {
1034 int Mj_players_coords[GR_NUM_RESOLUTIONS][4] = {
1043 int Mj_ping_coords[GR_NUM_RESOLUTIONS][4] = {
1052 // game speed labels
1053 #define MJ_NUM_SPEED_LABELS 5
1054 char *Multi_join_speed_labels[MJ_NUM_SPEED_LABELS] = {
1061 color *Multi_join_speed_colors[MJ_NUM_SPEED_LABELS] = {
1064 &Color_bright_green,
1065 &Color_bright_green,
1069 int Mj_cd_coords[GR_NUM_RESOLUTIONS] = {
1074 #define IP_CONFIG_FNAME "tcp.cfg" // name of the file which contains known TCP addresses
1076 // extents of the entire boundable game info region
1077 // NOTE : these numbers are completely empirical
1078 #define MJ_PING_GREEN 160
1079 #define MJ_PING_YELLOW 300
1081 int Mj_list_area_coords[GR_NUM_RESOLUTIONS][4] = {
1090 // PXO channel filter
1091 #define MJ_PXO_FILTER_Y 0
1093 // special chars to indicate various status modes for servers
1094 #define MJ_CHAR_STANDALONE "*"
1095 #define MJ_CHAR_CAMPAIGN "c"
1098 // various interface indices
1099 int Multi_join_list_start; // where to start displaying from
1100 active_game *Multi_join_list_start_item; // a pointer to the corresponding active_game
1101 int Multi_join_list_selected; // which item we have selected
1102 active_game *Multi_join_selected_item; // a pointer to the corresponding active_game
1104 // use this macro to modify the list start
1105 #define MJ_LIST_START_INC() do { Multi_join_list_start++; } while(0);
1106 #define MJ_LIST_START_DEC() do { Multi_join_list_start--; } while(0);
1107 #define MJ_LIST_START_SET(vl) do { Multi_join_list_start = vl; } while(0);
1109 // if we should be sending a join request at the end of the frame
1110 int Multi_join_should_send = -1;
1112 // master tracker details
1113 int Multi_join_frame_count; // keep a count of frames displayed
1114 int Multi_join_mt_tried_verify; // already tried verifying the pilot with the tracker
1116 // data stuff for auto joining a game
1117 #define MULTI_AUTOJOIN_JOIN_STAMP 2000
1118 #define MULTI_AUTOJOIN_QUERY_STAMP 2000
1120 int Multi_did_autojoin;
1121 net_addr Multi_autojoin_addr;
1122 int Multi_autojoin_join_stamp;
1123 int Multi_autojoin_query_stamp;
1126 join_request Multi_join_request;
1128 // LOCAL function definitions
1129 void multi_join_check_buttons();
1130 void multi_join_button_pressed(int n);
1131 void multi_join_display_games();
1132 void multi_join_blit_game_status(active_game *game, int y);
1133 void multi_join_load_tcp_addrs();
1134 void multi_join_do_netstuff();
1135 void multi_join_ping_all();
1136 void multi_join_process_select();
1137 void multi_join_list_scroll_up();
1138 void multi_join_list_scroll_down();
1139 void multi_join_list_page_up();
1140 void multi_join_list_page_down();
1141 active_game *multi_join_get_game(int n);
1142 void multi_join_cull_timeouts();
1143 void multi_join_handle_item_cull(active_game *item, int item_index);
1144 void multi_join_send_join_request(int as_observer);
1145 void multi_join_create_game();
1146 void multi_join_blit_top_stuff();
1147 int multi_join_maybe_warn();
1148 int multi_join_warn_pxo();
1149 void multi_join_blit_protocol();
1153 active_game ag, *newitem;;
1156 dc_get_arg(ARG_INT);
1157 for(idx=0; idx<Dc_arg_int; idx++){
1158 // stuff some fake info
1159 memset(&ag, 0, sizeof(active_game));
1160 sprintf(ag.name, "Game %d", idx);
1161 ag.version = MULTI_FS_SERVER_VERSION;
1162 ag.comp_version = MULTI_FS_SERVER_VERSION;
1163 ag.server_addr.addr[0] = (char)idx;
1164 ag.flags = (AG_FLAG_COOP | AG_FLAG_FORMING | AG_FLAG_STANDALONE);
1167 newitem = multi_update_active_games(&ag);
1169 // timestamp it so we get random timeouts
1170 if(newitem != NULL){
1171 // newitem->heard_from_timer = timestamp((int)frand_range(500.0f, 10000.0f));
1176 void multi_join_notify_new_game()
1178 // reset the # of items
1179 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);
1180 Multi_join_slider.force_currentItem(Multi_join_list_start);
1183 int multi_join_autojoin_do()
1185 // if we have an active game on the list, then return a positive value so that we
1186 // can join the game
1187 if ( Active_game_head && (Active_game_count > 0) ) {
1188 Multi_join_selected_item = Active_game_head;
1192 // send out a server_query again
1193 if ( timestamp_elapsed(Multi_autojoin_query_stamp) ) {
1194 send_server_query(&Multi_autojoin_addr);
1195 Multi_autojoin_query_stamp = timestamp(MULTI_AUTOJOIN_QUERY_STAMP);
1201 void multi_join_game_init()
1205 // do the multiplayer init stuff - multi_level_init() now does all net_player zeroing.
1206 // setup various multiplayer things
1207 Assert( Game_mode & GM_MULTIPLAYER );
1208 Assert( Net_player != NULL );
1210 switch (Multi_options_g.protocol) {
1212 ADDRESS_LENGTH = IPX_ADDRESS_LENGTH;
1213 PORT_LENGTH = IPX_PORT_LENGTH;
1217 ADDRESS_LENGTH = IP_ADDRESS_LENGTH;
1218 PORT_LENGTH = IP_PORT_LENGTH;
1227 memset( &Netgame, 0, sizeof(Netgame) );
1230 Net_player->flags |= NETINFO_FLAG_DO_NETWORKING;
1231 Net_player->player = Player;
1232 memcpy(&Net_player->p_info.addr,&Psnet_my_addr,sizeof(net_addr));
1234 // check for the existence of a CD
1235 multi_common_verify_cd();
1237 // load my local netplayer options
1238 multi_options_local_load(&Net_player->p_info.options, Net_player);
1243 // common_set_interface_palette(MULTI_JOIN_PALETTE);
1245 // destroy any chatbox contents which previously existed (from another game)
1248 // create the interface window
1249 Multi_join_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
1250 Multi_join_window.set_mask_bmap(Multi_join_bitmap_mask_fname[gr_screen.res]);
1252 // load the background bitmap
1253 Multi_join_bitmap = bm_load(Multi_join_bitmap_fname[gr_screen.res]);
1254 if(Multi_join_bitmap < 0){
1255 // we failed to load the bitmap - this is very bad
1259 // intialize the endgame system
1260 multi_endgame_init();
1262 // initialize the common notification messaging
1263 multi_common_notify_init();
1265 // initialize the common text area
1266 multi_common_set_text("");
1268 // load and use the common interface palette
1269 multi_common_load_palette();
1270 multi_common_set_palette();
1272 // load the help overlay
1273 help_overlay_load(MULTI_JOIN_OVERLAY);
1274 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1276 // do TCP and VMT specific initialization
1277 if(Multi_options_g.protocol == NET_TCP){
1278 // if this is a TCP (non tracker) game, we'll load up our default address list right now
1279 multi_join_load_tcp_addrs();
1282 // initialize any and all timestamps
1283 Multi_join_glr_stamp = -1;
1284 Multi_join_ping_stamp = -1;
1285 Multi_join_sent_stamp = -1;
1287 // reset frame count
1288 Multi_join_frame_count = 0;
1290 // haven't tried to verify on the tracker yet.
1291 Multi_join_mt_tried_verify = 0;
1293 // clear our all game lists to save hassles
1294 multi_join_clear_game_list();
1296 // create the interface buttons
1297 for(idx=0; idx<MULTI_JOIN_NUM_BUTTONS; idx++){
1298 // create the object
1299 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);
1301 // set the sound to play when highlighted
1302 Multi_join_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
1304 // set the ani for the button
1305 Multi_join_buttons[gr_screen.res][idx].button.set_bmaps(Multi_join_buttons[gr_screen.res][idx].filename);
1308 Multi_join_buttons[gr_screen.res][idx].button.link_hotspot(Multi_join_buttons[gr_screen.res][idx].hotspot);
1312 for(idx=0; idx<MULTI_JOIN_NUM_TEXT; idx++){
1313 Multi_join_window.add_XSTR(&Multi_join_text[gr_screen.res][idx]);
1316 Multi_join_should_send = -1;
1318 // close any previously open chatbox
1321 // create the list item select button
1322 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);
1323 Multi_join_select_button.hide();
1326 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);
1328 // if starting a network game, then go to the create game screen
1329 if ( Cmdline_start_netgame ) {
1330 multi_join_create_game();
1331 } else if ( Cmdline_connect_addr != NULL ) {
1336 // joining a game. Send a join request to the given IP address, and wait for the return.
1337 memset( &Multi_autojoin_addr, 0, sizeof(net_addr) );
1338 Multi_autojoin_addr.type = NET_TCP;
1340 // create the address, looking out for port number at the end
1341 port_num = DEFAULT_GAME_PORT;
1342 p = strrchr(Cmdline_connect_addr, ':');
1346 port_num = (short)atoi(p);
1348 ip_addr = inet_addr(Cmdline_connect_addr);
1349 memcpy(Multi_autojoin_addr.addr, &ip_addr, 4);
1350 Multi_autojoin_addr.port = port_num;
1352 send_server_query(&Multi_autojoin_addr);
1353 Multi_autojoin_query_stamp = timestamp(MULTI_AUTOJOIN_QUERY_STAMP);
1354 Multi_did_autojoin = 0;
1358 void multi_join_clear_game_list()
1361 Multi_join_list_selected = -1;
1362 Multi_join_selected_item = NULL;
1363 MJ_LIST_START_SET(-1);
1364 Multi_join_list_start_item = NULL;
1366 // free up the active game list
1367 multi_free_active_games();
1369 // initialize the active game list
1370 Active_game_head = NULL;
1371 Active_game_count = 0;
1374 void multi_join_game_do_frame()
1376 // check the status of our reliable socket. If not valid, popup error and return to main menu
1377 // I put this code here to avoid nasty gameseq issues with states. Also, we will have nice
1378 // background for the popup
1379 if ( !psnet_rel_check() ) {
1380 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));
1381 gameseq_post_event(GS_EVENT_MAIN_MENU);
1385 // return here since we will be moving to the next stage anyway -- I don't want to see the backgrounds of
1386 // all the screens for < 1 second for every screen we automatically move to.
1387 if ( Cmdline_start_netgame ) {
1391 // when joining a network game, wait for the server query to come back, and then join the game
1392 if ( Cmdline_connect_addr != NULL ) {
1395 if ( !Multi_did_autojoin ) {
1396 rval = popup_till_condition(multi_join_autojoin_do, XSTR("&Cancel", 779), XSTR("Joining netgame", 1500) );
1398 // cancel was hit. Send the user back to the main hall
1399 gameseq_post_event(GS_EVENT_MAIN_MENU);
1400 Cmdline_connect_addr = NULL; // reset this value.
1403 // when we get here, we have the data -- join the game.
1404 multi_join_send_join_request(0);
1405 Multi_autojoin_join_stamp = timestamp(MULTI_AUTOJOIN_JOIN_STAMP);
1406 Multi_did_autojoin = 1;
1409 if ( timestamp_elapsed(Multi_autojoin_join_stamp) ) {
1410 multi_join_send_join_request(0);
1411 Multi_autojoin_join_stamp = timestamp(MULTI_AUTOJOIN_JOIN_STAMP);
1417 // reset the should send var
1418 Multi_join_should_send = -1;
1420 int k = Multi_join_window.process();
1422 // process any keypresses
1425 if(help_overlay_active(MULTI_JOIN_OVERLAY)){
1426 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1428 gameseq_post_event(GS_EVENT_MAIN_MENU);
1429 gamesnd_play_iface(SND_USER_SELECT);
1433 // page up the game list
1435 multi_join_list_page_up();
1436 Multi_join_slider.force_currentItem(Multi_join_list_start);
1440 multi_pinfo_popup(Net_player);
1443 // page down the game list
1445 multi_join_list_page_down();
1446 Multi_join_slider.force_currentItem(Multi_join_list_start);
1449 // send out a ping-all
1451 multi_join_ping_all();
1452 Multi_join_ping_stamp = timestamp(MULTI_JOIN_PING_TIME);
1455 // shortcut to start a game
1457 multi_join_create_game();
1460 // scroll the game list up
1462 multi_join_list_scroll_up();
1463 Multi_join_slider.force_currentItem(Multi_join_list_start);
1466 // scroll the game list down
1468 multi_join_list_scroll_down();
1469 Multi_join_slider.force_currentItem(Multi_join_list_start);
1473 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
1474 help_overlay_set_state(MULTI_JOIN_OVERLAY, 0);
1477 // do any network related stuff
1478 multi_join_do_netstuff();
1480 // process any button clicks
1481 multi_join_check_buttons();
1483 // process any list selection stuff
1484 multi_join_process_select();
1486 // draw the background, etc
1488 GR_MAYBE_CLEAR_RES(Multi_join_bitmap);
1489 if(Multi_join_bitmap != -1){
1490 gr_set_bitmap(Multi_join_bitmap);
1493 Multi_join_window.draw();
1495 // display the active games
1496 multi_join_display_games();
1498 // display any text in the info area
1499 multi_common_render_text();
1501 // display any pending notification messages
1502 multi_common_notify_do();
1504 // blit the CD icon and any PXO filter stuff
1505 multi_join_blit_top_stuff();
1507 // draw the help overlay
1508 help_overlay_maybe_blit(MULTI_JOIN_OVERLAY);
1513 // if we are supposed to be sending a join request
1514 if(Multi_join_should_send != -1){
1515 multi_join_send_join_request(Multi_join_should_send);
1517 Multi_join_should_send = -1;
1519 // increment the frame count
1520 Multi_join_frame_count++;
1523 void multi_join_game_close()
1525 // unload any bitmaps
1526 if(!bm_unload(Multi_join_bitmap)){
1527 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_join_bitmap_fname[gr_screen.res]));
1530 // unload the help overlay
1531 help_overlay_unload(MULTI_JOIN_OVERLAY);
1533 // free up the active game list
1534 multi_free_active_games();
1536 // destroy the UI_WINDOW
1537 Multi_join_window.destroy();
1540 void multi_join_check_buttons()
1543 for(idx=0;idx<MULTI_JOIN_NUM_BUTTONS;idx++){
1544 // we only really need to check for one button pressed at a time, so we can break after
1546 if(Multi_join_buttons[gr_screen.res][idx].button.pressed()){
1547 multi_join_button_pressed(idx);
1553 void multi_join_button_pressed(int n)
1557 // if we're player PXO, go back there
1558 gameseq_post_event(GS_EVENT_MAIN_MENU);
1559 gamesnd_play_iface(SND_USER_SELECT);
1562 if(Active_game_count <= 0){
1563 multi_common_add_notify(XSTR("No games found!",757));
1564 gamesnd_play_iface(SND_GENERAL_FAIL);
1565 } else if(Multi_join_list_selected == -1){
1566 multi_common_add_notify(XSTR("No game selected!",758));
1567 gamesnd_play_iface(SND_GENERAL_FAIL);
1568 } else if((Multi_join_sent_stamp != -1) && !timestamp_elapsed(Multi_join_sent_stamp)){
1569 multi_common_add_notify(XSTR("Still waiting on previous join request!",759));
1570 gamesnd_play_iface(SND_GENERAL_FAIL);
1572 // otherwise, if he's already played PXO games, warn him
1574 if(Player->flags & PLAYER_FLAGS_HAS_PLAYED_PXO){
1575 if(!multi_join_warn_pxo()){
1581 // send the join request here
1582 Assert(Multi_join_selected_item != NULL);
1584 // send a join request packet
1585 Multi_join_should_send = 0;
1587 gamesnd_play_iface(SND_COMMIT_PRESSED);
1593 if(!help_overlay_active(MULTI_JOIN_OVERLAY)){
1594 help_overlay_set_state(MULTI_JOIN_OVERLAY,1);
1596 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1600 // scroll the game list up
1602 multi_join_list_scroll_up();
1603 Multi_join_slider.force_currentItem(Multi_join_list_start);
1606 // scroll the game list down
1607 case MJ_SCROLL_DOWN:
1608 multi_join_list_scroll_down();
1609 Multi_join_slider.force_currentItem(Multi_join_list_start);
1612 // scroll the info text box up
1613 case MJ_SCROLL_INFO_UP:
1614 multi_common_scroll_text_up();
1617 // scroll the info text box down
1618 case MJ_SCROLL_INFO_DOWN:
1619 multi_common_scroll_text_down();
1622 // go to the options screen
1624 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
1627 // go to the start game screen
1629 multi_join_create_game();
1632 // refresh the game/server list
1634 gamesnd_play_iface(SND_USER_SELECT);
1635 broadcast_game_query();
1638 // join a game as an observer
1639 case MJ_JOIN_OBSERVER:
1640 if(Active_game_count <= 0){
1641 multi_common_add_notify(XSTR("No games found!",757));
1642 gamesnd_play_iface(SND_GENERAL_FAIL);
1643 } else if(Multi_join_list_selected == -1){
1644 multi_common_add_notify(XSTR("No game selected!",758));
1645 gamesnd_play_iface(SND_GENERAL_FAIL);
1646 } else if((Multi_join_sent_stamp != -1) && !timestamp_elapsed(Multi_join_sent_stamp)){
1647 multi_common_add_notify(XSTR("Still waiting on previous join request!",759));
1648 gamesnd_play_iface(SND_GENERAL_FAIL);
1650 // send the join request here
1651 Assert(Multi_join_selected_item != NULL);
1653 Multi_join_should_send = 1;
1655 gamesnd_play_iface(SND_COMMIT_PRESSED);
1660 multi_common_add_notify(XSTR("Not implemented yet!",760));
1661 gamesnd_play_iface(SND_GENERAL_FAIL);
1666 // display all relevant info for active games
1667 void multi_join_display_games()
1669 active_game *moveup = Multi_join_list_start_item;
1673 int y_start = Mj_list_y[gr_screen.res];
1678 // blit the game status (including text and type icon)
1679 multi_join_blit_game_status(moveup,y_start);
1681 // get the connection type
1682 con_type = (moveup->flags & AG_FLAG_CONNECTION_SPEED_MASK) >> AG_FLAG_CONNECTION_BIT;
1683 if((con_type > 4) || (con_type < 0)){
1687 // display the connection speed
1689 strcpy(str, Multi_join_speed_labels[con_type]);
1690 gr_set_color_fast(Multi_join_speed_colors[con_type]);
1691 gr_string(Mj_speed_coords[gr_screen.res][MJ_X_COORD], y_start, str);
1693 // we'll want to have different colors for highlighted items, etc.
1694 if(moveup == Multi_join_selected_item){
1695 gr_set_color_fast(&Color_text_selected);
1697 gr_set_color_fast(&Color_text_normal);
1700 // display the game name, adding appropriate status chars
1702 if(moveup->flags & AG_FLAG_STANDALONE){
1703 strcat(str,MJ_CHAR_STANDALONE);
1705 if(moveup->flags & AG_FLAG_CAMPAIGN){
1706 strcat(str,MJ_CHAR_CAMPAIGN);
1709 // tack on the actual server name
1711 strcat(str,moveup->name);
1712 if(strlen(moveup->mission_name) > 0){
1714 strcat(str,moveup->mission_name);
1717 // make sure the string fits in the display area and draw it
1718 gr_force_fit_string(str,200,Mj_game_name_coords[gr_screen.res][MJ_W_COORD]);
1719 gr_string(Mj_game_name_coords[gr_screen.res][MJ_X_COORD],y_start,str);
1721 // display the ping time
1722 if(moveup->ping.ping_avg > 0){
1723 if(moveup->ping.ping_avg > 1000){
1724 gr_set_color_fast(&Color_bright_red);
1725 strcpy(str,XSTR("> 1 sec",761));
1727 // set the appropriate ping time color indicator
1728 if(moveup->ping.ping_avg > MJ_PING_YELLOW){
1729 gr_set_color_fast(&Color_bright_red);
1730 } else if(moveup->ping.ping_avg > MJ_PING_YELLOW){
1731 gr_set_color_fast(&Color_bright_yellow);
1733 gr_set_color_fast(&Color_bright_green);
1736 sprintf(str,"%d",moveup->ping.ping_avg);
1737 strcat(str,XSTR(" ms",762)); // [[ Milliseconds ]]
1740 gr_string(Mj_ping_coords[gr_screen.res][MJ_X_COORD],y_start,str);
1743 // display the number of players (be sure to center it)
1744 if(moveup == Multi_join_selected_item){
1745 gr_set_color_fast(&Color_text_selected);
1747 gr_set_color_fast(&Color_text_normal);
1749 sprintf(str,"%d",moveup->num_players);
1750 gr_get_string_size(&w,&h,str);
1751 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);
1755 moveup = moveup->next;
1756 } while((moveup != Active_game_head) && (count < Mj_max_game_items[gr_screen.res]));
1758 // if there are no items on the list, display this info
1760 gr_set_color_fast(&Color_bright);
1761 gr_string(Mj_game_name_coords[gr_screen.res][MJ_X_COORD] - 30,y_start,XSTR("<No game servers found>",763));
1765 void multi_join_blit_game_status(active_game *game, int y)
1768 char status_text[25];
1770 // blit the proper icon
1772 switch( game->flags & AG_FLAG_TYPE_MASK ){
1775 if(Multi_common_icons[MICON_COOP] != -1){
1776 gr_set_bitmap(Multi_common_icons[MICON_COOP]);
1781 // team vs. team game
1783 if(Multi_common_icons[MICON_TVT] != -1){
1784 gr_set_bitmap(Multi_common_icons[MICON_TVT]);
1790 case AG_FLAG_DOGFIGHT:
1791 if(Multi_common_icons[MICON_DOGFIGHT] != -1){
1792 gr_set_bitmap(Multi_common_icons[MICON_DOGFIGHT]);
1797 // if we're supposed to draw a bitmap
1799 gr_bitmap(Mj_game_icon_coords[gr_screen.res][MJ_X_COORD],y-1);
1802 // blit the proper status text
1803 memset(status_text,0,25);
1805 switch( game->flags & AG_FLAG_STATE_MASK ){
1806 case AG_FLAG_FORMING:
1807 gr_set_color_fast(&Color_bright_green);
1808 strcpy(status_text,XSTR("Forming",764));
1810 case AG_FLAG_BRIEFING:
1811 gr_set_color_fast(&Color_bright_red);
1812 strcpy(status_text,XSTR("Briefing",765));
1814 case AG_FLAG_DEBRIEF:
1815 gr_set_color_fast(&Color_bright_red);
1816 strcpy(status_text,XSTR("Debrief",766));
1819 gr_set_color_fast(&Color_bright_red);
1820 strcpy(status_text,XSTR("Paused",767));
1822 case AG_FLAG_IN_MISSION:
1823 gr_set_color_fast(&Color_bright_red);
1824 strcpy(status_text,XSTR("Playing",768));
1827 gr_set_color_fast(&Color_bright);
1828 strcpy(status_text,XSTR("Unknown",769));
1831 gr_get_string_size(&str_w,NULL,status_text);
1832 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);
1835 // load in a list of active games from our tcp.cfg file
1836 void multi_join_load_tcp_addrs()
1838 char line[MAX_IP_STRING];
1843 // attempt to open the ip list file
1844 file = cfopen(IP_CONFIG_FNAME,"rt",CFILE_NORMAL,CF_TYPE_DATA);
1846 nprintf(("Network","Error loading tcp.cfg file!\n"));
1850 // free up any existing server list
1851 multi_free_server_list();
1853 // read in all the strings in the file
1854 while(!cfeof(file)){
1856 cfgets(line,MAX_IP_STRING,file);
1858 // strip off any newline character
1859 if(line[strlen(line) - 1] == '\n'){
1860 line[strlen(line) - 1] = '\0';
1863 // empty lines don't get processed
1864 if( (line[0] == '\0') || (line[0] == '\n') ){
1868 if ( !psnet_is_valid_ip_string(line) ) {
1869 nprintf(("Network","Invalid ip string (%s)\n",line));
1871 // copy the server ip address
1872 memset(&addr,0,sizeof(net_addr));
1873 addr.type = NET_TCP;
1874 psnet_string_to_addr(&addr,line);
1875 if ( addr.port == 0 ){
1876 addr.port = DEFAULT_GAME_PORT;
1879 // create a new server item on the list
1880 item = multi_new_server_item();
1882 memcpy(&item->server_addr,&addr,sizeof(net_addr));
1890 // do stuff like pinging servers, sending out requests, etc
1891 void multi_join_do_netstuff()
1893 // handle game query stuff
1894 if(Multi_join_glr_stamp == -1){
1895 broadcast_game_query();
1897 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
1898 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME_LOCAL);
1900 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME);
1903 // otherwise send out game query and restamp
1904 else if(timestamp_elapsed(Multi_join_glr_stamp)){
1905 broadcast_game_query();
1907 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
1908 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME_LOCAL);
1910 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME);
1914 // check to see if we've been accepted. If so, put up message saying so
1915 if ( Net_player->flags & (NETINFO_FLAG_ACCEPT_INGAME|NETINFO_FLAG_ACCEPT_CLIENT|NETINFO_FLAG_ACCEPT_HOST|NETINFO_FLAG_ACCEPT_OBSERVER) ) {
1916 multi_common_add_notify(XSTR("Accepted. Waiting for player data.",770));
1919 // check to see if any join packets we have sent have timed out
1920 if((Multi_join_sent_stamp != -1) && (timestamp_elapsed(Multi_join_sent_stamp))){
1921 Multi_join_sent_stamp = -1;
1922 multi_common_add_notify(XSTR("Join request timed out!",771));
1925 // check to see if we should be pinging everyone
1926 if((Multi_join_ping_stamp == -1) || (timestamp_elapsed(Multi_join_ping_stamp))){
1927 multi_join_ping_all();
1928 Multi_join_ping_stamp = timestamp(MULTI_JOIN_PING_TIME);
1932 multi_join_cull_timeouts();
1935 // evaluate a returned pong.
1936 void multi_join_eval_pong(net_addr *addr, fix pong_time)
1939 active_game *moveup = Active_game_head;
1944 if(psnet_same(&moveup->server_addr,addr)){
1946 multi_ping_eval_pong(&moveup->ping);
1950 moveup = moveup->next;
1952 } while(moveup != Active_game_head);
1955 // update the game's ping
1957 if(found && (moveup->ping_end > moveup->ping_start)){
1958 moveup->ping_time = f2fl(moveup->ping_end - moveup->ping_start);
1959 moveup->ping_start = -1;
1960 moveup->ping_end = -1;
1965 // ping all the server on the list
1966 void multi_join_ping_all()
1968 active_game *moveup = Active_game_head;
1973 moveup->ping_start = timer_get_fixed_seconds();
1974 moveup->ping_end = -1;
1975 send_ping(&moveup->server_addr);
1977 multi_ping_send(&moveup->server_addr,&moveup->ping);
1979 moveup = moveup->next;
1980 } while(moveup != Active_game_head);
1984 void multi_join_process_select()
1986 // if we don't have anything selected and there are items on the list - select the first one
1987 if((Multi_join_list_selected == -1) && (Active_game_count > 0)){
1988 Multi_join_list_selected = 0;
1989 Multi_join_selected_item = multi_join_get_game(0);
1990 MJ_LIST_START_SET(0);
1991 Multi_join_list_start_item = Multi_join_selected_item;
1993 // send a mission description request to this guy
1994 send_netgame_descript_packet(&Multi_join_selected_item->server_addr,0);
1995 multi_common_set_text("");
1997 // I sure hope this doesn't happen
1998 Assert(Multi_join_selected_item != NULL);
2001 // otherwise see if he's clicked on an item
2002 else if(Multi_join_select_button.pressed() && (Active_game_count > 0)){
2004 Multi_join_select_button.get_mouse_pos(NULL,&y);
2006 if(item + Multi_join_list_start < Active_game_count){
2007 gamesnd_play_iface(SND_IFACE_MOUSE_CLICK);
2009 Multi_join_list_selected = item + Multi_join_list_start;
2010 Multi_join_selected_item = multi_join_get_game(Multi_join_list_selected);
2012 // I sure hope this doesn't happen
2013 Assert(Multi_join_selected_item != NULL);
2015 // send a mission description request to this guy
2016 send_netgame_descript_packet(&Multi_join_selected_item->server_addr,0);
2017 multi_common_set_text("");
2021 // if he's double clicked, then select it and accept
2022 if(Multi_join_select_button.double_clicked()){
2024 Multi_join_select_button.get_mouse_pos(NULL,&y);
2026 if(item == Multi_join_list_selected){
2027 multi_join_button_pressed(MJ_ACCEPT);
2032 // return game n (0 based index)
2033 active_game *multi_join_get_game(int n)
2035 active_game *moveup = Active_game_head;
2042 moveup = moveup->next;
2043 while((moveup != Active_game_head) && (count != n)){
2044 moveup = moveup->next;
2047 if(moveup == Active_game_head){
2048 nprintf(("Network","Warning, couldn't find game item %d!\n",n));
2058 // scroll through the game list
2059 void multi_join_list_scroll_up()
2061 // if we're not at the beginning of the list, scroll up
2062 if(Multi_join_list_start_item != Active_game_head){
2063 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2065 MJ_LIST_START_DEC();
2067 gamesnd_play_iface(SND_SCROLL);
2069 gamesnd_play_iface(SND_GENERAL_FAIL);
2073 // scroll through the game list
2074 void multi_join_list_scroll_down()
2076 if((Active_game_count - Multi_join_list_start) > Mj_max_game_items[gr_screen.res]){
2077 Multi_join_list_start_item = Multi_join_list_start_item->next;
2079 MJ_LIST_START_INC();
2081 gamesnd_play_iface(SND_SCROLL);
2083 gamesnd_play_iface(SND_GENERAL_FAIL);
2087 void multi_join_list_page_up()
2089 // in this case, just set us to the beginning of the list
2090 if((Multi_join_list_start - Mj_max_game_items[gr_screen.res]) < 0){
2091 Multi_join_list_start_item = Active_game_head;
2093 MJ_LIST_START_SET(0);
2095 gamesnd_play_iface(SND_SCROLL);
2097 // otherwise page the whole thing up
2099 for(idx=0; idx<Mj_max_game_items[gr_screen.res]; idx++){
2100 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2102 MJ_LIST_START_DEC();
2104 gamesnd_play_iface(SND_SCROLL);
2108 void multi_join_list_page_down()
2112 // page the whole thing down
2113 while((count < Mj_max_game_items[gr_screen.res]) && ((Active_game_count - Multi_join_list_start) > Mj_max_game_items[gr_screen.res])){
2114 Multi_join_list_start_item = Multi_join_list_start_item->next;
2115 MJ_LIST_START_INC();
2120 gamesnd_play_iface(SND_SCROLL);
2123 void multi_join_cull_timeouts()
2125 active_game *backup;
2127 active_game *moveup = Active_game_head;
2129 // traverse through the entire list if any items exist
2133 if((moveup->heard_from_timer != -1) && (timestamp_elapsed(moveup->heard_from_timer))){
2134 Active_game_count--;
2136 // if this is the head of the list
2137 if(moveup == Active_game_head){
2138 // if this is the _only_ item on the list
2139 if(moveup->next == Active_game_head){
2140 // handle any gui details related to deleting this item
2141 multi_join_handle_item_cull(Active_game_head, count);
2143 free(Active_game_head);
2144 Active_game_head = NULL;
2147 // if there are other items on the list
2149 // handle any gui details related to deleting this item
2150 multi_join_handle_item_cull(moveup, count);
2152 Active_game_head = moveup->next;
2153 Active_game_head->prev = moveup->prev;
2154 Active_game_head->prev->next = Active_game_head;
2156 moveup = Active_game_head;
2159 // if its somewhere else on the list
2161 // handle any gui details related to deleting this item
2162 multi_join_handle_item_cull(moveup, count);
2164 // if its the last item on the list
2165 moveup->next->prev = moveup->prev;
2166 moveup->prev->next = moveup->next;
2168 // if it was the last element on the list, return
2169 if(moveup->next == Active_game_head){
2173 backup = moveup->next;
2179 moveup = moveup->next;
2182 } while(moveup != Active_game_head);
2186 // deep magic begins here.
2187 void multi_join_handle_item_cull(active_game *item, int item_index)
2189 // if this is the only item on the list, unset everything
2190 if(item->next == item){
2191 Multi_join_list_selected = -1;
2192 Multi_join_selected_item = NULL;
2194 Multi_join_slider.set_numberItems(0);
2195 MJ_LIST_START_SET(-1);
2196 Multi_join_list_start_item = NULL;
2202 // see if we should be adjusting our currently selected item
2203 if(item_index <= Multi_join_list_selected){
2204 // the selected item is the head of the list
2205 if(Multi_join_selected_item == Active_game_head){
2206 // move the pointer up since this item is about to be destroyed
2207 Multi_join_selected_item = Multi_join_selected_item->next;
2209 // if this is the item being deleted, select the previous one
2210 if(item == Multi_join_selected_item){
2212 Multi_join_selected_item = Multi_join_selected_item->prev;
2214 // decrement the selected index by 1
2215 Multi_join_list_selected--;
2217 // now we know its a previous item, so our pointer stays the same but our index goes down by one, since there will be
2218 // 1 less item on the list
2220 // decrement the selected index by 1
2221 Multi_join_list_selected--;
2226 // see if we should be adjusting out current start position
2227 if(item_index <= Multi_join_list_start){
2228 // the start position is the head of the list
2229 if(Multi_join_list_start_item == Active_game_head){
2230 // move the pointer up since this item is about to be destroyed
2231 Multi_join_list_start_item = Multi_join_list_start_item->next;
2233 // if this is the item being deleted, select the previous one
2234 if(item == Multi_join_list_start_item){
2235 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2237 // decrement the starting index by 1
2238 MJ_LIST_START_DEC();
2240 // but decrement the starting index by 1
2241 MJ_LIST_START_DEC();
2246 // maybe go back up a bit so that we always have a full page of items
2247 if(Active_game_count > Mj_max_game_items[gr_screen.res]){
2248 while((Active_game_count - Multi_join_list_start) < Mj_max_game_items[gr_screen.res]){
2249 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2250 MJ_LIST_START_DEC();
2254 // set slider location
2255 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);
2256 Multi_join_slider.force_currentItem(Multi_join_list_start);
2259 void multi_join_send_join_request(int as_observer)
2261 // don't do anything if we have no items selected
2262 if(Multi_join_selected_item == NULL){
2266 // 5/26/98 -- for team v team games, don't allow ingame joining :-(
2267 if ( (Multi_join_selected_item->flags & AG_FLAG_TEAMS) && (Multi_join_selected_item->flags & (AG_FLAG_PAUSE|AG_FLAG_IN_MISSION)) ) {
2268 popup(0, 1, POPUP_OK, XSTR("Joining ingame is currently not allowed for team vs. team games",772));
2272 memset(&Multi_join_request,0,sizeof(join_request));
2274 // if the netgame is in password mode, put up a request for the password
2275 if(Multi_join_selected_item->flags & AG_FLAG_PASSWD){
2276 if(!multi_passwd_popup(Multi_join_request.passwd)){
2280 nprintf(("Password : %s\n",Multi_join_request.passwd));
2283 // fill out the join request struct
2284 strcpy(Multi_join_request.callsign,Player->callsign);
2285 if(strlen(Player->image_filename) > 0){
2286 strcpy(Multi_join_request.image_filename, Player->image_filename);
2288 if(strlen(Player->squad_filename) > 0){
2289 strcpy(Multi_join_request.squad_filename, Player->squad_filename);
2292 // tracker id (if any)
2293 Multi_join_request.tracker_id = Multi_tracker_id;
2295 // player's rank (at least, what he wants you to _believe_)
2296 Multi_join_request.player_rank = (ubyte)Player->stats.rank;
2299 Multi_join_request.flags = 0;
2301 Multi_join_request.flags |= JOIN_FLAG_AS_OBSERVER;
2304 // if the player has hacked data
2305 if(game_hacked_data()){
2306 Multi_join_request.flags |= JOIN_FLAG_HAXOR;
2310 strncpy(Multi_join_request.pxo_squad_name, Multi_tracker_squad_name, LOGIN_LEN);
2312 // version of this server
2313 Multi_join_request.version = MULTI_FS_SERVER_VERSION;
2315 // server compatible version
2316 Multi_join_request.comp_version = MULTI_FS_SERVER_COMPATIBLE_VERSION;
2318 // his local player options
2319 memcpy(&Multi_join_request.player_options,&Player->m_local_options,sizeof(multi_local_options));
2321 // set the server address for the netgame
2322 memcpy(&Netgame.server_addr,&Multi_join_selected_item->server_addr,sizeof(net_addr));
2324 // send a join request to the guy
2325 send_join_packet(&Multi_join_selected_item->server_addr,&Multi_join_request);
2328 Multi_join_sent_stamp = timestamp(MULTI_JOIN_SENT_WAIT);
2331 multi_common_add_notify(XSTR("Sending join request...",773));
2334 void multi_join_create_game()
2336 // maybe warn the player about possible crappy server conditions
2337 if(!multi_join_maybe_warn()){
2341 // make sure to flag ourself as being the master
2342 Net_player->flags |= (NETINFO_FLAG_AM_MASTER | NETINFO_FLAG_GAME_HOST);
2343 Net_player->state = NETPLAYER_STATE_HOST_SETUP;
2345 // if we're in PXO mode, mark it down in our player struct
2346 if(MULTI_IS_TRACKER_GAME){
2347 Player->flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
2348 Player->save_flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
2350 // otherwise, if he's already played PXO games, warn him
2353 if(Player->flags & PLAYER_FLAGS_HAS_PLAYED_PXO){
2354 if(!multi_join_warn_pxo()){
2361 gameseq_post_event(GS_EVENT_MULTI_START_GAME);
2362 gamesnd_play_iface(SND_USER_SELECT);
2365 void multi_join_reset_join_stamp()
2367 // unset the timestamp here so the user can immediately send another join request
2368 Multi_join_sent_stamp = -1;
2369 multi_common_add_notify("");
2372 void multi_join_blit_top_stuff()
2374 // blit the cd icon if he has one
2375 if(Multi_has_cd && (Multi_common_icons[MICON_CD] != -1)){
2378 bm_get_info(Multi_common_icons[MICON_CD], &cd_w, NULL, NULL, NULL, NULL);
2380 gr_set_bitmap(Multi_common_icons[MICON_CD]);
2381 gr_bitmap((gr_screen.max_w / 2) - (cd_w / 2), Mj_cd_coords[gr_screen.res]);
2385 #define CW_CODE_CANCEL 0 // cancel the action
2386 #define CW_CODE_OK 1 // continue anyway
2387 #define CW_CODE_INFO 2 // gimme some more information
2389 #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)
2390 #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)
2392 #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)
2393 #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)
2395 int multi_join_warn_update_low(int code)
2399 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),LOW_WARN_TEXT);
2402 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),LOW_INFO_TEXT);
2405 return CW_CODE_CANCEL;
2408 int multi_join_warn_update_medium(int code)
2412 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),MED_WARN_TEXT);
2415 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),MED_INFO_TEXT);
2418 return CW_CODE_CANCEL;
2421 int multi_join_maybe_warn()
2425 // if the player is set for low updates
2426 if(Player->m_local_options.obj_update_level == OBJ_UPDATE_LOW){
2429 code = multi_join_warn_update_low(code);
2430 } while((code != CW_CODE_CANCEL) && (code != CW_CODE_OK));
2435 // if the player is set for medium updates
2436 else if(Player->m_local_options.obj_update_level == OBJ_UPDATE_MEDIUM){
2439 code = multi_join_warn_update_medium(code);
2440 } while((code != CW_CODE_CANCEL) && (code != CW_CODE_OK));
2448 int multi_join_warn_pxo()
2450 // 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;
2454 void multi_join_blit_protocol()
2456 gr_set_color_fast(&Color_bright);
2458 switch(Socket_type){
2461 gr_string(5, 2, "TCP");
2465 gr_string(5, 2, "IPX");
2471 // -------------------------------------------------------------------------------------------------
2473 // MULTIPLAYER START GAME screen
2478 #define MULTI_SG_PALETTE "InterfacePalette"
2480 static char *Multi_sg_bitmap_fname[GR_NUM_RESOLUTIONS] = {
2481 "MultiStartGame", // GR_640
2482 "2_MultiStartGame" // GR_1024
2485 static char *Multi_sg_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
2486 "MultiStartGame-M", // GR_640
2487 "2_MultiStartGame-M" // GR_1024
2492 int Multi_sg_rank_max_display[GR_NUM_RESOLUTIONS] = {
2497 // constants for coordinate look ups
2498 #define MSG_X_COORD 0
2499 #define MSG_Y_COORD 1
2500 #define MSG_W_COORD 2
2501 #define MSG_H_COORD 3
2505 // input password field
2506 int Msg_passwd_coords[GR_NUM_RESOLUTIONS][4] = {
2515 // input game title field
2516 int Msg_title_coords[GR_NUM_RESOLUTIONS][4] = {
2525 // rank selected field
2526 int Msg_rank_sel_coords[GR_NUM_RESOLUTIONS][4] = {
2536 int Msg_rank_list_coords[GR_NUM_RESOLUTIONS][4] = {
2547 #define MULTI_SG_NUM_BUTTONS 10
2548 #define MSG_OPEN_GAME 0
2549 //#define MSG_CLOSED_GAME 1
2550 //#define MSG_RESTRICTED_GAME 2
2551 #define MSG_PASSWD_GAME 1
2552 #define MSG_RANK_SET_GAME 2
2553 #define MSG_RANK_SCROLL_UP 3
2554 #define MSG_RANK_SCROLL_DOWN 4
2555 #define MSG_RANK_ABOVE 5
2556 #define MSG_RANK_BELOW 6
2558 #define MSG_OPTIONS 8
2559 #define MSG_ACCEPT 9
2561 UI_WINDOW Multi_sg_window; // the window object for the join screen
2562 UI_BUTTON Multi_sg_rank_button; // for selecting the rank marker
2563 UI_INPUTBOX Multi_sg_game_name; // for Netgame.name
2564 UI_INPUTBOX Multi_sg_game_passwd; // for Netgame.passwd
2565 int Multi_sg_bitmap; // the background bitmap
2567 ui_button_info Multi_sg_buttons[GR_NUM_RESOLUTIONS][MULTI_SG_NUM_BUTTONS] = {
2569 ui_button_info("MSG_00", 1, 184, 34, 191, 2), // open
2570 // ui_button_info("MSG_01", 1, 159, 34, 166, 1), // closed
2571 // ui_button_info("MSG_02", 1, 184, 34, 191, 2), // restricted
2572 ui_button_info("MSG_03", 1, 209, 34, 218, 3), // password
2573 ui_button_info("MSG_04", 1, 257, 34, 266, 4), // rank set
2574 ui_button_info("MSG_05", 1, 282, -1, -1, 5), // rank scroll up
2575 ui_button_info("MSG_06", 1, 307, -1, -1, 6), // rank scroll down
2576 ui_button_info("MSG_07", 177, 282, 210, 290, 7), // rank above
2577 ui_button_info("MSG_08", 177, 307, 210, 315, 8), // rank below
2578 ui_button_info("MSG_09", 536, 429, 500, 440, 9), // help
2579 ui_button_info("MSG_10", 536, 454, 479, 464, 10), // options
2580 ui_button_info("MSG_11", 576, 432, 571, 415, 11), // accept
2583 ui_button_info("2_MSG_00", 2, 295, 51, 307, 2), // open
2584 // ui_button_info("2_MSG_01", 2, 254, 51, 267, 1), // closed
2585 // ui_button_info("2_MSG_02", 2, 295, 51, 307, 2), // restricted
2586 ui_button_info("2_MSG_03", 2, 335, 51, 350, 3), // password
2587 ui_button_info("2_MSG_04", 2, 412, 51, 426, 4), // rank set
2588 ui_button_info("2_MSG_05", 2, 452, -1, -1, 5), // rank scroll up
2589 ui_button_info("2_MSG_06", 2, 492, -1, -1, 6), // rank scroll down
2590 ui_button_info("2_MSG_07", 284, 452, 335, 465, 7), // rank above
2591 ui_button_info("2_MSG_08", 284, 492, 335, 505, 8), // rank below
2592 ui_button_info("2_MSG_09", 858, 687, 817, 706, 9), // help
2593 ui_button_info("2_MSG_10", 858, 728, 797, 743, 10), // options
2594 ui_button_info("2_MSG_11", 921, 692, 921, 664, 11), // accept
2598 #define MULTI_SG_NUM_TEXT 11
2599 UI_XSTR Multi_sg_text[GR_NUM_RESOLUTIONS][MULTI_SG_NUM_TEXT] = {
2601 {"Open", 1322, 34, 191, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_OPEN_GAME].button},
2602 // {"Closed", 1323, 34, 166, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_CLOSED_GAME].button},
2603 // {"Restricted", 1324, 34, 191, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RESTRICTED_GAME].button},
2604 {"Password Protected", 1325, 34, 218, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_PASSWD_GAME].button},
2605 {"Allow Rank", 1326, 34, 266, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_SET_GAME].button},
2606 {"Above", 1327, 210, 290, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_ABOVE].button},
2607 {"Below", 1328, 210, 315, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_BELOW].button},
2608 {"Help", 928, 500, 440, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_HELP].button},
2609 {"Options", 1036, 479, 464, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_OPTIONS].button},
2610 {"Accept", 1035, 571, 415, UI_XSTR_COLOR_PINK, -1, &Multi_sg_buttons[0][MSG_ACCEPT].button},
2611 {"Start Game", 1329, 26, 10, UI_XSTR_COLOR_GREEN, -1, NULL},
2612 {"Title", 1330, 26, 31, UI_XSTR_COLOR_GREEN, -1, NULL},
2613 {"Game Type", 1331, 12, 165, UI_XSTR_COLOR_GREEN, -1, NULL},
2616 {"Open", 1322, 51, 307, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_OPEN_GAME].button},
2617 // {"Closed", 1323, 51, 267, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_CLOSED_GAME].button},
2618 // {"Restricted", 1324, 51, 307, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RESTRICTED_GAME].button},
2619 {"Password Protected", 1325, 51, 350, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_PASSWD_GAME].button},
2620 {"Allow Rank", 1326, 51, 426, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_SET_GAME].button},
2621 {"Above", 1327, 335, 465, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_ABOVE].button},
2622 {"Below", 1328, 335, 505, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_BELOW].button},
2623 {"Help", 928, 817, 706, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_HELP].button},
2624 {"Options", 1036, 797, 743, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_OPTIONS].button},
2625 {"Accept", 1035, 921, 664, UI_XSTR_COLOR_PINK, -1, &Multi_sg_buttons[1][MSG_ACCEPT].button},
2626 {"Start Game", 1329, 42, 22, UI_XSTR_COLOR_GREEN, -1, NULL},
2627 {"Title", 1330, 42, 50, UI_XSTR_COLOR_GREEN, -1, NULL},
2628 {"Game Type", 1331, 20, 264, UI_XSTR_COLOR_GREEN, -1, NULL},
2632 // starting index for displaying ranks
2633 int Multi_sg_rank_start;
2634 int Multi_sg_rank_select;
2636 // netgame pointer to indirect through
2637 netgame_info *Multi_sg_netgame;
2639 // hold temporary values in this structure when on a standalone server
2640 netgame_info Multi_sg_netgame_temp;
2642 // forward declarations
2643 void multi_sg_check_buttons();
2644 void multi_sg_button_pressed(int n);
2645 void multi_sg_init_gamenet();
2646 void multi_sg_draw_radio_buttons();
2647 void multi_sg_rank_scroll_up();
2648 void multi_sg_rank_scroll_down();
2649 void multi_sg_rank_display_stuff();
2650 void multi_sg_rank_process_select();
2651 void multi_sg_rank_build_name(char *in,char *out);
2652 void multi_sg_check_passwd();
2653 void multi_sg_check_name();
2654 void multi_sg_release_passwd();
2655 int multi_sg_rank_select_valid(int rank);
2656 void multi_sg_select_rank_default();
2658 // function which takes a rank name and returns the index. Useful for commandline options
2659 // for above and below rank. We return the index of the rank in the Ranks[] array. If
2660 // the rank isn't found, we return -1
2661 int multi_start_game_rank_from_name( char *rank ) {
2664 for ( i = 0; i <= MAX_FREESPACE2_RANK; i++ ) {
2665 if ( !stricmp(Ranks[i].name, rank) ) {
2673 void multi_start_game_init()
2677 // initialize the gamenet
2678 multi_sg_init_gamenet();
2680 // create the interface window
2681 Multi_sg_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
2682 Multi_sg_window.set_mask_bmap(Multi_sg_bitmap_mask_fname[gr_screen.res]);
2684 // load the background bitmap
2685 Multi_sg_bitmap = bm_load(Multi_sg_bitmap_fname[gr_screen.res]);
2686 if(Multi_sg_bitmap < 0){
2687 // we failed to load the bitmap - this is very bad
2691 // initialize the common notification messaging
2692 multi_common_notify_init();
2694 // initialize the common text area
2695 multi_common_set_text("");
2697 // use the common interface palette
2698 multi_common_set_palette();
2700 // create the interface buttons
2701 for(idx=0; idx<MULTI_SG_NUM_BUTTONS; idx++){
2702 // create the object
2703 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);
2705 // set the sound to play when highlighted
2706 Multi_sg_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
2708 // set the ani for the button
2709 Multi_sg_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sg_buttons[gr_screen.res][idx].filename);
2712 Multi_sg_buttons[gr_screen.res][idx].button.link_hotspot(Multi_sg_buttons[gr_screen.res][idx].hotspot);
2716 for(idx=0; idx<MULTI_SG_NUM_TEXT; idx++){
2717 Multi_sg_window.add_XSTR(&Multi_sg_text[gr_screen.res][idx]);
2720 // load the help overlay
2721 help_overlay_load(MULTI_START_OVERLAY);
2722 help_overlay_set_state(MULTI_START_OVERLAY,0);
2724 // intiialize the rank selection items
2725 multi_sg_select_rank_default();
2726 Multi_sg_rank_start = Multi_sg_rank_select;
2728 // create the rank select button
2729 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);
2730 Multi_sg_rank_button.hide();
2732 // create the netgame name input box
2733 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);
2735 // create the netgame password input box, and disable it by default
2736 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);
2737 Multi_sg_game_passwd.hide();
2738 Multi_sg_game_passwd.disable();
2740 // set the netgame text to this gadget and make it have focus
2741 Multi_sg_game_name.set_text(Multi_sg_netgame->name);
2742 Multi_sg_game_name.set_focus();
2744 // if starting a netgame, set the name of the game and any other options that are appropriate
2745 if ( Cmdline_start_netgame ) {
2746 if ( Cmdline_game_name != NULL ) {
2747 strcpy( Multi_sg_netgame->name, Cmdline_game_name );
2748 Multi_sg_game_name.set_text(Multi_sg_netgame->name);
2751 // deal with the different game types -- only one should even be active, so we will just go down
2752 // the line. Last one wins.
2753 if ( Cmdline_closed_game ) {
2754 Multi_sg_netgame->mode = NG_MODE_CLOSED;
2755 } else if ( Cmdline_restricted_game ) {
2756 Multi_sg_netgame->mode = NG_MODE_RESTRICTED;
2757 } else if ( Cmdline_game_password != NULL ) {
2758 Multi_sg_netgame->mode = NG_MODE_PASSWORD;
2759 strcpy(Multi_sg_netgame->passwd, Cmdline_game_password);
2760 Multi_sg_game_passwd.set_text(Multi_sg_netgame->passwd);
2763 // deal with rank above and rank below
2764 if ( (Cmdline_rank_above != NULL) || (Cmdline_rank_below != NULL) ) {
2768 if ( Cmdline_rank_above != NULL ) {
2769 rank_str = Cmdline_rank_above;
2771 rank_str = Cmdline_rank_below;
2774 // try and get the rank index from the name -- if found, then set the rank base
2775 // and the game type. apparently we only support either above or below, not both
2776 // together, so I make a random choice
2777 rank = multi_start_game_rank_from_name( rank_str );
2779 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
2781 // now an arbitrary decision
2782 if ( Cmdline_rank_above != NULL ) {
2783 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
2785 Multi_sg_netgame->mode = NG_MODE_RANK_BELOW;
2790 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
2794 void multi_start_game_do()
2796 // return here since we will be moving to the next stage anyway -- I don't want to see the backgrounds of
2797 // all the screens for < 1 second for every screen we automatically move to.
2798 if ( Cmdline_start_netgame ) {
2802 int k = Multi_sg_window.process();
2804 // process any keypresses
2807 if(help_overlay_active(MULTI_START_OVERLAY)){
2808 help_overlay_set_state(MULTI_START_OVERLAY,0);
2810 gamesnd_play_iface(SND_USER_SELECT);
2811 multi_quit_game(PROMPT_NONE);
2816 case KEY_LCTRL + KEY_ENTER :
2817 case KEY_RCTRL + KEY_ENTER :
2818 gamesnd_play_iface(SND_COMMIT_PRESSED);
2819 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
2823 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
2824 help_overlay_set_state(MULTI_START_OVERLAY, 0);
2827 // check to see if the user has selected a different rank
2828 multi_sg_rank_process_select();
2830 // check any button presses
2831 multi_sg_check_buttons();
2833 // check to see if any of the input boxes have changed, and update the appropriate Netgame fields if necessary
2834 multi_sg_check_passwd();
2835 multi_sg_check_name();
2837 // draw the background, etc
2839 GR_MAYBE_CLEAR_RES(Multi_sg_bitmap);
2840 if(Multi_sg_bitmap != -1){
2841 gr_set_bitmap(Multi_sg_bitmap);
2844 Multi_sg_window.draw();
2846 // display rank stuff
2847 multi_sg_rank_display_stuff();
2849 // display any pending notification messages
2850 multi_common_notify_do();
2852 // draw all radio button
2853 multi_sg_draw_radio_buttons();
2855 // draw the help overlay
2856 help_overlay_maybe_blit(MULTI_START_OVERLAY);
2862 void multi_start_game_close()
2864 // if i'm the host on a standalone server, send him my start game options (passwd, mode, etc)
2865 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
2866 multi_options_update_start_game(Multi_sg_netgame);
2869 // unload any bitmaps
2870 if(!bm_unload(Multi_sg_bitmap)){
2871 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_sg_bitmap_fname[gr_screen.res]));
2874 // unload the help overlay
2875 help_overlay_unload(MULTI_START_OVERLAY);
2877 // destroy the UI_WINDOW
2878 Multi_sg_window.destroy();
2881 void multi_sg_check_buttons()
2884 for(idx=0;idx<MULTI_SG_NUM_BUTTONS;idx++){
2885 // we only really need to check for one button pressed at a time, so we can break after
2887 if(Multi_sg_buttons[gr_screen.res][idx].button.pressed()){
2888 multi_sg_button_pressed(idx);
2894 void multi_sg_button_pressed(int n)
2897 // go to the options screen
2899 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
2904 if(!help_overlay_active(MULTI_START_OVERLAY)){
2905 help_overlay_set_state(MULTI_START_OVERLAY,1);
2907 help_overlay_set_state(MULTI_START_OVERLAY,0);
2911 // the open button was pressed
2913 // if the closed option is selected
2914 if(Multi_sg_netgame->mode != NG_MODE_OPEN){
2915 Multi_sg_netgame->mode = NG_MODE_OPEN;
2917 gamesnd_play_iface(SND_USER_SELECT);
2919 // release the password control if necessary
2920 multi_sg_release_passwd();
2922 // if its already selected
2924 gamesnd_play_iface(SND_GENERAL_FAIL);
2929 // the open button was pressed
2930 case MSG_CLOSED_GAME:
2931 // if the closed option is selected
2932 if(Multi_sg_netgame->mode != NG_MODE_CLOSED){
2933 Multi_sg_netgame->mode = NG_MODE_CLOSED;
2935 gamesnd_play_iface(SND_USER_SELECT);
2937 // release the password control if necessary
2938 multi_sg_release_passwd();
2940 // if its already selected
2942 gamesnd_play_iface(SND_GENERAL_FAIL);
2946 // toggle password protection
2947 case MSG_PASSWD_GAME:
2948 // if we selected it
2949 if(Multi_sg_netgame->mode != NG_MODE_PASSWORD){
2950 gamesnd_play_iface(SND_USER_SELECT);
2952 Multi_sg_game_passwd.enable();
2953 Multi_sg_game_passwd.unhide();
2954 Multi_sg_game_passwd.set_focus();
2956 Multi_sg_netgame->mode = NG_MODE_PASSWORD;
2958 // copy in the current network password
2959 Multi_sg_game_passwd.set_text(Multi_sg_netgame->passwd);
2961 gamesnd_play_iface(SND_GENERAL_FAIL);
2966 // toggle "restricted" on or off
2967 case MSG_RESTRICTED_GAME:
2968 if(Multi_sg_netgame->mode != NG_MODE_RESTRICTED){
2969 gamesnd_play_iface(SND_USER_SELECT);
2970 Multi_sg_netgame->mode = NG_MODE_RESTRICTED;
2972 // release the password control if necessary
2973 multi_sg_release_passwd();
2975 gamesnd_play_iface(SND_GENERAL_FAIL);
2979 // turn off all rank requirements
2980 case MSG_RANK_SET_GAME:
2981 // if either is set, then turn then both off
2982 if((Multi_sg_netgame->mode != NG_MODE_RANK_BELOW) && (Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE)){
2983 gamesnd_play_iface(SND_USER_SELECT);
2985 // set it to the default case if we're turning it off
2986 multi_sg_select_rank_default();
2987 Multi_sg_rank_start = Multi_sg_rank_select;
2989 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
2991 // release the password control if necessary
2992 multi_sg_release_passwd();
2994 gamesnd_play_iface(SND_GENERAL_FAIL);
2998 // rank above was pressed
2999 case MSG_RANK_ABOVE :
3000 if((Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE) || (Multi_sg_netgame->mode == NG_MODE_RANK_BELOW)){
3001 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
3003 // select the first item
3004 multi_sg_select_rank_default();
3005 Multi_sg_rank_start = Multi_sg_rank_select;
3008 gamesnd_play_iface(SND_USER_SELECT);
3010 gamesnd_play_iface(SND_GENERAL_FAIL);
3014 // rank below was pressed
3015 case MSG_RANK_BELOW :
3016 if((Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE) || (Multi_sg_netgame->mode == NG_MODE_RANK_BELOW)){
3017 Multi_sg_netgame->mode = NG_MODE_RANK_BELOW;
3019 // select the first item
3020 multi_sg_select_rank_default();
3021 Multi_sg_rank_start = Multi_sg_rank_select;
3024 gamesnd_play_iface(SND_USER_SELECT);
3026 gamesnd_play_iface(SND_GENERAL_FAIL);
3030 // scroll the rank list up
3031 case MSG_RANK_SCROLL_UP:
3032 multi_sg_rank_scroll_up();
3035 // scroll the rank list down
3036 case MSG_RANK_SCROLL_DOWN:
3037 multi_sg_rank_scroll_down();
3040 // move to the create game screen
3042 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
3043 gamesnd_play_iface(SND_COMMIT_PRESSED);
3047 gamesnd_play_iface(SND_GENERAL_FAIL);
3048 multi_common_add_notify(XSTR("Not implemented yet!",760));
3053 // NOTE : this is where all Netgame initialization should take place on the host
3054 void multi_sg_init_gamenet()
3056 char buf[128],out_name[128];
3058 net_player *server_save;
3060 // back this data up in case we are already connected to a standalone
3061 memcpy(&save,&Netgame.server_addr,sizeof(net_addr));
3062 server_save = Netgame.server;
3064 // remove campaign flags
3065 Game_mode &= ~(GM_CAMPAIGN_MODE);
3067 // clear out the Netgame structure and start filling in the values
3068 memset( &Netgame, 0, sizeof(Netgame) );
3069 memset( &Multi_sg_netgame_temp, 0, sizeof(netgame_info) );
3071 // if we're on the standalone, we're not the server, so we don't care about setting the netgame state
3072 if(Net_player->state != NETPLAYER_STATE_STD_HOST_SETUP){
3073 Netgame.game_state = NETGAME_STATE_HOST_SETUP;
3074 Multi_sg_netgame = &Netgame;
3077 ml_string(NOX("Starting netgame as Host/Server"));
3079 Multi_sg_netgame = &Multi_sg_netgame_temp;
3083 memcpy(&temp_addr.s_addr, &Netgame.server_addr, 4);
3084 char *server_addr = inet_ntoa(temp_addr);
3085 ml_printf(NOX("Starting netgame as Host on Standalone server : %s"), (server_addr == NULL) ? NOX("Unknown") : server_addr);
3088 Net_player->tracker_player_id = Multi_tracker_id;
3090 Multi_sg_netgame->security = (rand() % 32766) + 1; // get some random security number
3091 Multi_sg_netgame->mode = NG_MODE_OPEN;
3092 Multi_sg_netgame->rank_base = RANK_ENSIGN;
3093 if(Multi_sg_netgame->security < 16){
3094 Multi_sg_netgame->security += 16;
3097 // set the version_info field
3098 Multi_sg_netgame->version_info = NG_VERSION_ID;
3100 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
3101 Netgame.host = Net_player;
3104 // set the default netgame flags
3105 Multi_sg_netgame->flags = 0;
3107 // intialize endgame stuff
3108 multi_endgame_init();
3110 // load in my netgame options
3111 multi_options_netgame_load(&Netgame.options);
3113 // load my local netplayer options
3114 multi_options_local_load(&Net_player->p_info.options, Net_player);
3116 // setup the default game name, taking care of string length and player callsigns
3117 memset(out_name,0,128);
3119 pilot_format_callsign_personal(Player->callsign,out_name);
3120 sprintf(buf, XSTR("%s game",782), out_name); // [[ %s will be a pilot's name ]]
3121 if ( strlen(buf) > MAX_GAMENAME_LEN ){
3122 strcpy(buf, XSTR("Temporary name",783));
3124 strcpy(Multi_sg_netgame->name, buf);
3126 // set the default qos and duration
3127 multi_voice_maybe_update_vars(Netgame.options.voice_qos,Netgame.options.voice_record_time);
3129 // make sure to set the server correctly (me or the standalone)
3130 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3131 memcpy(&Netgame.server_addr, &Psnet_my_addr,sizeof(net_addr));
3132 Netgame.server = Net_player;
3133 Net_player->player_id = multi_get_new_id();
3135 // setup debug flags
3136 Netgame.debug_flags = 0;
3138 if(!Cmdline_server_firing){
3139 Netgame.debug_flags |= NETD_FLAG_CLIENT_FIRING;
3141 if(!Cmdline_client_dodamage){
3142 Netgame.debug_flags |= NETD_FLAG_CLIENT_NODAMAGE;
3146 memcpy(&Netgame.server_addr,&save,sizeof(net_addr));
3147 Netgame.server = server_save;
3150 // if I have a cd or not
3152 Net_player->flags |= NETINFO_FLAG_HAS_CD;
3155 // if I have hacked data
3156 if(game_hacked_data()){
3157 Net_player->flags |= NETINFO_FLAG_HAXOR;
3160 // assign my player struct and other data
3161 Net_player->flags |= (NETINFO_FLAG_CONNECTED | NETINFO_FLAG_DO_NETWORKING);
3162 Net_player->s_info.voice_token_timestamp = -1;
3164 // if we're supposed to flush our cache directory, do so now
3165 if(Net_player->p_info.options.flags & MLO_FLAG_FLUSH_CACHE){
3166 multi_flush_multidata_cache();
3169 ml_string(NOX("Flushing multi-data cache"));
3175 void multi_sg_draw_radio_buttons()
3177 // draw the appropriate radio button
3178 switch(Multi_sg_netgame->mode){
3180 Multi_sg_buttons[gr_screen.res][MSG_OPEN_GAME].button.draw_forced(2);
3183 case NG_MODE_CLOSED:
3184 Multi_sg_buttons[gr_screen.res][MSG_CLOSED_GAME].button.draw_forced(2);
3187 case NG_MODE_PASSWORD:
3188 Multi_sg_buttons[gr_screen.res][MSG_PASSWD_GAME].button.draw_forced(2);
3190 /* case NG_MODE_RESTRICTED:
3191 Multi_sg_buttons[gr_screen.res][MSG_RESTRICTED_GAME].button.draw_forced(2);
3194 case NG_MODE_RANK_ABOVE:
3195 Multi_sg_buttons[gr_screen.res][MSG_RANK_SET_GAME].button.draw_forced(2);
3196 Multi_sg_buttons[gr_screen.res][MSG_RANK_ABOVE].button.draw_forced(2);
3198 case NG_MODE_RANK_BELOW:
3199 Multi_sg_buttons[gr_screen.res][MSG_RANK_SET_GAME].button.draw_forced(2);
3200 Multi_sg_buttons[gr_screen.res][MSG_RANK_BELOW].button.draw_forced(2);
3205 void multi_sg_rank_scroll_up()
3207 // if he doesn't have either of the rank flags set, then ignore this
3208 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3212 if(Multi_sg_rank_start > 0){
3213 Multi_sg_rank_start--;
3214 gamesnd_play_iface(SND_SCROLL);
3216 gamesnd_play_iface(SND_GENERAL_FAIL);
3220 void multi_sg_rank_scroll_down()
3222 // if he doesn't have either of the rank flags set, then ignore this
3223 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3227 if((NUM_RANKS - Multi_sg_rank_start) > Multi_sg_rank_max_display[gr_screen.res]){
3228 Multi_sg_rank_start++;
3229 gamesnd_play_iface(SND_SCROLL);
3231 gamesnd_play_iface(SND_GENERAL_FAIL);
3235 void multi_sg_rank_display_stuff()
3240 // if he doesn't have either of the rank flags set, then ignore this
3241 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3245 // display the list of ranks
3246 y = Msg_rank_list_coords[gr_screen.res][MSG_Y_COORD];
3247 idx = Multi_sg_rank_start;
3249 while((count < NUM_RANKS) && (count < Multi_sg_rank_max_display[gr_screen.res]) && (idx < NUM_RANKS)){
3250 // if its the selected item, then color it differently
3251 if(idx == Multi_sg_rank_select){
3252 gr_set_color_fast(&Color_text_selected);
3254 gr_set_color_fast(&Color_text_normal);
3258 multi_sg_rank_build_name(Ranks[idx].name,rank_name);
3259 gr_string(Msg_rank_list_coords[gr_screen.res][MSG_X_COORD],y,rank_name);
3267 // display the selected rank
3269 gr_set_color_fast(&Color_bright);
3270 multi_sg_rank_build_name(Ranks[Multi_sg_netgame->rank_base].name,rank_name);
3271 gr_string(Msg_rank_sel_coords[gr_screen.res][MSG_X_COORD],Msg_rank_sel_coords[gr_screen.res][MSG_Y_COORD],rank_name);
3275 void multi_sg_rank_process_select()
3279 // if he doesn't have either of the rank flags set, then ignore this
3280 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3284 // see if he's clicked on an item on the rank list
3285 if(Multi_sg_rank_button.pressed()){
3287 Multi_sg_rank_button.get_mouse_pos(NULL,&y);
3290 if(item + Multi_sg_rank_start < NUM_RANKS){
3291 // evaluate whether this rank is valid for the guy to pick
3292 if(multi_sg_rank_select_valid(item + Multi_sg_rank_start)){
3293 gamesnd_play_iface(SND_USER_SELECT);
3295 Multi_sg_rank_select = item + Multi_sg_rank_start;
3297 // set the Netgame rank
3298 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
3300 gamesnd_play_iface(SND_GENERAL_FAIL);
3302 memset(string,0,255);
3303 sprintf(string,XSTR("Illegal value for a host of your rank (%s)\n",784),Ranks[Net_player->player->stats.rank].name);
3304 multi_common_add_notify(string);
3310 void multi_sg_rank_build_name(char *in,char *out)
3316 first = strtok(use," ");
3318 // just copy the string
3323 // if the first part of the string is lieutenant, then abbreivate it and tack on the rest of the string
3324 if (stricmp(first,XSTR("lieutenant",785)) == 0) {
3325 first = strtok(NULL, NOX("\n"));
3327 // if he's not just a plain lieutenant
3329 strcpy(out,XSTR("Lt. ",786)); // [[ lieutenant ]]
3332 // if he _is_ just a plain lieutenant
3341 void multi_sg_check_passwd()
3343 // check to see if the password input box has been pressed
3344 if(Multi_sg_game_passwd.changed()){
3345 Multi_sg_game_passwd.get_text(Multi_sg_netgame->passwd);
3349 void multi_sg_check_name()
3351 // check to see if the game name input box has been pressed
3352 if(Multi_sg_game_name.changed()){
3353 Multi_sg_game_name.get_text(Multi_sg_netgame->name);
3357 void multi_sg_release_passwd()
3359 // hide and disable the password input box
3360 Multi_sg_game_passwd.hide();
3361 Multi_sg_game_passwd.disable();
3363 // set the focus back to the name input box
3364 Multi_sg_game_name.set_focus();
3367 int multi_sg_rank_select_valid(int rank)
3370 if(Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE){
3371 if(Net_player->player->stats.rank >= rank){
3377 if(Net_player->player->stats.rank <= rank){
3385 void multi_sg_select_rank_default()
3387 // pick our rank for now
3388 Multi_sg_rank_select = Net_player->player->stats.rank;
3390 // set the Netgame rank
3391 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
3394 // -------------------------------------------------------------------------------------------------
3396 // MULTIPLAYER CREATE GAME screen
3401 char *Multi_create_bitmap_fname[GR_NUM_RESOLUTIONS] = {
3402 "MultiCreate", // GR_640
3403 "2_MultiCreate" // GR_1024
3406 char *Multi_create_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
3407 "MultiCreate-M", // GR_640
3408 "2_MultiCreate-M" // GR_1024
3411 char *Multi_create_loading_fname[GR_NUM_RESOLUTIONS] = {
3412 "PleaseWait", // GR_640
3413 "2_PleaseWait" // GR_1024
3417 #define MULTI_CREATE_NUM_BUTTONS 23
3420 #define MC_SHOW_ALL 0
3421 #define MC_SHOW_COOP 1
3422 #define MC_SHOW_TEAM 2
3423 #define MC_SHOW_DOGFIGHT 3
3424 #define MC_PXO_REFRESH 4
3425 #define MC_PILOT_INFO 5
3426 #define MC_SCROLL_LIST_UP 6
3427 #define MC_SCROLL_LIST_DOWN 7
3428 #define MC_SCROLL_PLAYERS_UP 8
3429 #define MC_SCROLL_PLAYERS_DOWN 9
3430 #define MC_MISSION_FILTER 10
3431 #define MC_CAMPAIGN_FILTER 11
3432 #define MC_CANCEL 12
3437 #define MC_SCROLL_INFO_UP 17
3438 #define MC_SCROLL_INFO_DOWN 18
3439 #define MC_HOST_OPTIONS 19
3441 #define MC_OPTIONS 21
3442 #define MC_ACCEPT 22
3445 UI_WINDOW Multi_create_window; // the window object for the create screen
3446 UI_BUTTON Multi_create_player_select_button; // for selecting players
3447 UI_BUTTON Multi_create_list_select_button; // for selecting missions/campaigns
3448 int Multi_create_bitmap; // the background bitmap
3449 UI_SLIDER2 Multi_create_slider; // for create list
3451 // constants for coordinate look ups
3452 #define MC_X_COORD 0
3453 #define MC_Y_COORD 1
3454 #define MC_W_COORD 2
3455 #define MC_H_COORD 3
3457 ui_button_info Multi_create_buttons[GR_NUM_RESOLUTIONS][MULTI_CREATE_NUM_BUTTONS] = {
3459 ui_button_info("MC_00", 32, 129, 36, 158, 0), // show all missions
3460 ui_button_info("MC_01", 76, 129, 71, 158, 1), // show coop missions
3461 ui_button_info("MC_02", 121, 129, 119, 158, 2), // show team missions
3462 ui_button_info("MC_03", 164, 129, 166, 158, 3), // show dogfight missions
3463 ui_button_info("MC_04", 399, 129, 229, 130, 4), // pxo mission refresh
3464 ui_button_info("MC_05", 567, 123, 467, 132, 5), // pilot info
3465 ui_button_info("MC_06", 1, 161, -1, -1, 6), // scroll mission info up
3466 ui_button_info("MC_08", 1, 304, -1, -1, 8), // scroll mission info down
3467 ui_button_info("MC_09", 613, 160, -1, -1, 9), // scroll players up
3468 ui_button_info("MC_10", 613, 202, -1, -1, 10), // scroll players down
3469 ui_button_info("MC_11", 22, 346, 27, 376, 11), // mission filter
3470 ui_button_info("MC_12", 104, 346, 110, 376, 12), // campaign filter
3471 ui_button_info("MC_13", 392, 341, 328, 364, 13), // cancel
3472 ui_button_info("MC_14", 472, 352, 482, 381, 14), // team 0
3473 ui_button_info("MC_15", 506, 352, 514, 381, 15), // team 1
3474 ui_button_info("MC_16", 539, 346, 539, 381, 16), // kick
3475 ui_button_info("MC_17", 589, 346, 582, 381, 17), // close
3476 ui_button_info("MC_18", 1, 406, -1, -1, 18), // scroll list up
3477 ui_button_info("MC_19", 1, 447, -1, -1, 19), // scroll list down
3478 ui_button_info("MC_20", 499, 434, 436, 423, 20), // host options
3479 ui_button_info("MC_21", 534, 426, -1, -1, 21), // help
3480 ui_button_info("MC_22", 534, 452, -1, -1, 22), // options
3481 ui_button_info("MC_23", 571, 426, 572, 413, 23), // commit
3484 ui_button_info("2_MC_00", 51, 207, 61, 253, 0), // show all missions
3485 ui_button_info("2_MC_01", 122, 207, 124, 253, 1), // show coop missions
3486 ui_button_info("2_MC_02", 193, 207, 194, 253, 2), // show team missions
3487 ui_button_info("2_MC_03", 263, 207, 261, 253, 3), // show dogfight missions
3488 ui_button_info("2_MC_04", 639, 207, 479, 218, 4), // pxo mission refresh
3489 ui_button_info("2_MC_05", 907, 197, 748, 216, 5), // pilot info
3490 ui_button_info("2_MC_06", 1, 258, -1, -1, 6), // scroll mission info up
3491 ui_button_info("2_MC_08", 1, 487, -1, -1, 8), // scroll mission info down
3492 ui_button_info("2_MC_09", 981, 256, -1, -1, 9), // scroll players up
3493 ui_button_info("2_MC_10", 981, 323, -1, -1, 10), // scroll players down
3494 ui_button_info("2_MC_11", 35, 554, 46, 601, 11), // mission filter
3495 ui_button_info("2_MC_12", 166, 554, 174, 601, 12), // campaign filter
3496 ui_button_info("2_MC_13", 628, 545, 559, 582, 13), // cancel
3497 ui_button_info("2_MC_14", 756, 564, 772, 610, 14), // team 0
3498 ui_button_info("2_MC_15", 810, 564, 826, 610, 15), // team 1
3499 ui_button_info("2_MC_16", 862, 554, 872, 610, 16), // kick
3500 ui_button_info("2_MC_17", 943, 554, 949, 610, 17), // close
3501 ui_button_info("2_MC_18", 1, 649, -1, -1, 18), // scroll list up
3502 ui_button_info("2_MC_19", 1, 716, -1, -1, 19), // scroll list down
3503 ui_button_info("2_MC_20", 798, 695, 726, 667, 20), // host options
3504 ui_button_info("2_MC_21", 854, 681, -1, -1, 21), // help
3505 ui_button_info("2_MC_22", 854, 724, -1, -1, 22), // options
3506 ui_button_info("2_MC_23", 914, 681, 932, 667, 23), // commit
3510 #define MULTI_CREATE_NUM_TEXT 15
3511 UI_XSTR Multi_create_text[GR_NUM_RESOLUTIONS][MULTI_CREATE_NUM_BUTTONS] = {
3513 {"All", 1256, 36, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_ALL].button},
3514 {"Coop", 1257, 71, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_COOP].button},
3515 {"Team", 1258, 119, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_TEAM].button},
3516 {"Dogfight", 1259, 166, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_DOGFIGHT].button},
3517 {"Refresh Missions", 1260, 229, 130, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_PXO_REFRESH].button},
3518 {"Pilot Info", 1261, 467, 132, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_PILOT_INFO].button},
3519 {"Missions", 1262, 27, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_MISSION_FILTER].button},
3520 {"Campaigns", 1263, 110, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_CAMPAIGN_FILTER].button},
3521 {"Cancel", 387, 328, 364, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_CANCEL].button},
3522 {"1", 1264, 482, 381, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_TEAM0].button},
3523 {"2", 1265, 514, 381, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_TEAM1].button},
3524 {"Kick", 1266, 539, 381, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_KICK].button},
3525 {"Close", 1508, 582, 381, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_CLOSE].button},
3526 {"Host Options", 1267, 436, 423, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_HOST_OPTIONS].button},
3527 {"Commit", 1062, 572, 413, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_ACCEPT].button}
3530 {"All", 1256, 61, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_ALL].button},
3531 {"Coop", 1257, 124, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_COOP].button},
3532 {"Team", 1258, 194, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_TEAM].button},
3533 {"Dogfight", 1259, 261, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_DOGFIGHT].button},
3534 {"Refresh Missions", 1260, 501, 218, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_PXO_REFRESH].button},
3535 {"Pilot Info", 1261, 814, 216, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_PILOT_INFO].button},
3536 {"Missions", 1262, 46, 601, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_MISSION_FILTER].button},
3537 {"Campaigns", 1263, 174, 601, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_CAMPAIGN_FILTER].button},
3538 {"Cancel", 387, 559, 582, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_CANCEL].button},
3539 {"1", 1264, 772, 610, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_TEAM0].button},
3540 {"2", 1265, 826, 610, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_TEAM1].button},
3541 {"Kick", 1266, 872, 610, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_KICK].button},
3542 {"Close", 1508, 949, 610, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_CLOSE].button},
3543 {"Host Options", 1267, 755, 683, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_HOST_OPTIONS].button},
3544 {"Commit", 1062, 932, 667, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_ACCEPT].button}
3548 // squad war checkbox
3549 UI_CHECKBOX Multi_create_sw_checkbox;
3550 char *Multi_create_sw_checkbox_fname[GR_NUM_RESOLUTIONS] = {
3554 int Multi_create_sw_checkbox_coords[GR_NUM_RESOLUTIONS][2] = {
3562 int Multi_create_sw_checkbox_text[GR_NUM_RESOLUTIONS][2] = {
3571 // game information text areas
3572 int Mc_list_coords[GR_NUM_RESOLUTIONS][4] = {
3581 int Mc_players_coords[GR_NUM_RESOLUTIONS][4] = {
3590 int Mc_info_coords[GR_NUM_RESOLUTIONS][4] = {
3599 // mission icon stuff
3600 int Mc_icon_type_coords[GR_NUM_RESOLUTIONS][2] = {
3602 38, -2 // y is an offset
3605 61, -2 // y is an offset
3609 int Mc_icon_volition_coords[GR_NUM_RESOLUTIONS][2] = {
3611 61, -1 // y is an offset
3614 98, 1 // y is an offset
3618 int Mc_icon_silent_coords[GR_NUM_RESOLUTIONS][2] = {
3620 72, 0 // y is an offset
3623 115, 0 // y is an offset
3627 int Mc_icon_valid_coords[GR_NUM_RESOLUTIONS][2] = {
3629 91, 0 // y is an offset
3632 146, 0 // y is an offset
3636 // mission/campaign list column areas
3637 int Mc_column1_w[GR_NUM_RESOLUTIONS] = {
3642 int Mc_column2_w[GR_NUM_RESOLUTIONS] = {
3647 int Mc_column3_w[GR_NUM_RESOLUTIONS] = {
3652 int Mc_mission_name_x[GR_NUM_RESOLUTIONS] = {
3657 int Mc_mission_count_x[GR_NUM_RESOLUTIONS] = {
3662 int Mc_mission_fname_x[GR_NUM_RESOLUTIONS] = {
3667 int Mc_create_game_text[GR_NUM_RESOLUTIONS][2] = {
3668 {13, 116}, // GR_640
3669 {21, 186} // GR_1024
3672 int Mc_players_text[GR_NUM_RESOLUTIONS][2] = {
3673 {467, 150}, // GR_640
3674 {747, 240} // GR_1024
3677 int Mc_team_text[GR_NUM_RESOLUTIONS][2] = {
3678 {484, 342}, // GR_640
3679 {774, 547} // GR_1024
3682 int Mc_slider_coords[GR_NUM_RESOLUTIONS][4] = {
3684 3, 197, 13, 105 // GR_640
3687 5, 316, 20, 168 // GR_1024
3691 char *Mc_slider_bitmap[GR_NUM_RESOLUTIONS] = {
3696 // player list control thingie defs
3697 #define MULTI_CREATE_PLIST_MAX_DISPLAY 20
3698 int Multi_create_plist_select_flag; // flag indicating if we have a play selected
3699 short Multi_create_plist_select_id; // the net address of the currently selected player (for lookup)
3701 // master tracker details
3702 int Multi_create_frame_count; // framecount
3703 int Multi_create_mt_tried_login; // attempted to login this server on the MT
3705 // mission filter settings
3706 int Multi_create_filter; // what mode we're in
3708 // game/campaign list control defs
3709 int Multi_create_list_max_display[GR_NUM_RESOLUTIONS] = {
3715 int Multi_create_list_count; // number of items in listbox
3716 int Multi_create_list_mode; // 0 == mission mode, 1 == campaign mode
3717 int Multi_create_list_start; // where to start displaying from
3718 int Multi_create_list_select; // which item is currently highlighted
3719 int Multi_create_files_loaded;
3721 char Multi_create_files_array[MULTI_CREATE_MAX_LIST_ITEMS][MAX_FILENAME_LEN];
3723 int Multi_create_mission_count; // how many we have
3724 int Multi_create_campaign_count;
3725 multi_create_info Multi_create_mission_list[MULTI_CREATE_MAX_LIST_ITEMS];
3726 multi_create_info Multi_create_campaign_list[MULTI_CREATE_MAX_LIST_ITEMS];
3728 // use a pointer for the file list. Will point to either the missions or the campaigns
3729 multi_create_info *Multi_create_file_list;
3731 // LOCAL function definitions
3732 void multi_create_check_buttons();
3733 void multi_create_button_pressed(int n);
3734 void multi_create_init_as_server();
3735 void multi_create_init_as_client();
3736 void multi_create_do_netstuff();
3737 void multi_create_plist_scroll_up();
3738 void multi_create_plist_scroll_down();
3739 void multi_create_plist_process();
3740 void multi_create_plist_blit_normal();
3741 void multi_create_plist_blit_team();
3742 void multi_create_list_scroll_up();
3743 void multi_create_list_scroll_down();
3744 void multi_create_list_do();
3745 void multi_create_list_select_item(int n);
3746 void multi_create_list_blit_icons(int list_index, int y_start);
3747 void multi_create_accept_hit();
3748 void multi_create_draw_filter_buttons();
3749 void multi_create_set_selected_team(int team);
3750 short multi_create_get_mouse_id();
3751 int multi_create_ok_to_commit();
3752 int multi_create_verify_cds();
3753 void multi_create_refresh_pxo();
3754 void multi_create_sw_clicked();
3756 // since we can selectively filter out mission/campaign types we always need to map a selected index (which is relative
3757 // to the displayed list), to an absolute index (which is relative to the total file list - some of which may filtered out)
3758 void multi_create_select_to_filename(int select_index,char *filename);
3759 int multi_create_select_to_index(int select_index);
3761 int Multi_create_should_show_popup = 0;
3764 // sorting function to sort mission lists.. Basic sorting on mission name
3765 int multi_create_sort_func(const void *a, const void *b)
3767 multi_create_info *m1, *m2;
3769 m1 = (multi_create_info *)a;
3770 m2 = (multi_create_info *)b;
3772 return ( strcmp(m1->name, m2->name) );
3775 void multi_create_setup_list_data(int mode)
3777 int idx,should_sort,switched_modes;
3779 // set the current mode
3782 if((Multi_create_list_mode != mode) && (mode != -1)){
3783 Multi_create_list_mode = mode;
3786 // set up the list pointers
3787 if ( Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ) {
3788 Multi_create_file_list = Multi_create_mission_list;
3789 } else if ( Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ) {
3790 Multi_create_file_list = Multi_create_campaign_list;
3796 // get the mission count based upon the filter selected
3797 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
3798 switch(Multi_create_filter){
3799 case MISSION_TYPE_MULTI:
3800 Multi_create_list_count = Multi_create_mission_count;
3803 Multi_create_list_count = 0;
3804 // find all missions which match
3805 for(idx=0;idx<Multi_create_mission_count;idx++){
3806 if(Multi_create_mission_list[idx].flags & Multi_create_filter){
3807 Multi_create_list_count++;
3811 // if we switched modes and we have more than 0 items, sort them
3812 if(switched_modes && (Multi_create_list_count > 0)){
3817 } else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
3818 switch(Multi_create_filter){
3819 case MISSION_TYPE_MULTI:
3820 Multi_create_list_count = Multi_create_campaign_count;
3823 Multi_create_list_count = 0;
3824 // find all missions which match
3825 for(idx=0;idx<Multi_create_campaign_count;idx++){
3826 if(Multi_create_campaign_list[idx].flags & Multi_create_filter){
3827 Multi_create_list_count++;
3831 // if we switched modes and we have more than 0 items, sort them
3832 if(switched_modes && (Multi_create_list_count > 0)){
3839 // reset the list start and selected indices
3840 Multi_create_list_start = 0;
3841 Multi_create_list_select = -1;
3842 multi_create_list_select_item(Multi_create_list_start);
3844 // sort the list of missions if necessary
3846 qsort(Multi_create_file_list, Multi_create_list_count, sizeof(multi_create_info), multi_create_sort_func);
3850 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);
3853 void multi_create_game_init()
3858 // now make sure to initialze various netgame stuff based upon whether we're on a standalone or not
3859 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3860 multi_create_init_as_server();
3862 multi_create_init_as_client();
3865 // initialize the player list data
3866 Multi_create_plist_select_flag = 0;
3867 Multi_create_plist_select_id = -1;
3869 // create the interface window
3870 Multi_create_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
3871 Multi_create_window.set_mask_bmap(Multi_create_bitmap_mask_fname[gr_screen.res]);
3873 // load the background bitmap
3874 Multi_create_bitmap = bm_load(Multi_create_bitmap_fname[gr_screen.res]);
3875 if(Multi_create_bitmap < 0){
3876 // we failed to load the bitmap - this is very bad
3880 // close any previous existing instances of the chatbox and create a new one
3884 // load the help overlay
3885 help_overlay_load(MULTI_CREATE_OVERLAY);
3886 help_overlay_set_state(MULTI_CREATE_OVERLAY, 0);
3888 // initialize the common notification messaging
3889 multi_common_notify_init();
3891 // use the common interface palette
3892 multi_common_set_palette();
3894 // create the interface buttons
3895 for(idx=0; idx<MULTI_CREATE_NUM_BUTTONS; idx++){
3896 b = &Multi_create_buttons[gr_screen.res][idx];
3898 // create the object
3899 b->button.create(&Multi_create_window, "", b->x, b->y, 1, 1, ((idx == MC_SCROLL_LIST_UP) || (idx == MC_SCROLL_LIST_DOWN)), 1);
3901 // set the sound to play when highlighted
3902 b->button.set_highlight_action(common_play_highlight_sound);
3904 // set the ani for the button
3905 b->button.set_bmaps(b->filename);
3908 b->button.link_hotspot(b->hotspot);
3910 // some special case stuff for the pxo refresh button
3911 if(idx == MC_PXO_REFRESH){
3912 // if not a PXO game, or if I'm not a server disable and hide the button
3913 if(!MULTI_IS_TRACKER_GAME || !MULTIPLAYER_MASTER){
3915 b->button.disable();
3921 for(idx=0; idx<MULTI_CREATE_NUM_TEXT; idx++){
3922 Multi_create_window.add_XSTR(&Multi_create_text[gr_screen.res][idx]);
3925 // if this is a PXO game, enable the squadwar checkbox
3926 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);
3927 Multi_create_sw_checkbox.set_bmaps(Multi_create_sw_checkbox_fname[gr_screen.res], 6, 0);
3928 if(!MULTI_IS_TRACKER_GAME){
3929 Multi_create_sw_checkbox.hide();
3930 Multi_create_sw_checkbox.disable();
3934 // disable squad war button in demo
3935 Multi_create_sw_checkbox.hide();
3936 Multi_create_sw_checkbox.disable();
3939 // initialize the mission type filtering mode
3940 Multi_create_filter = MISSION_TYPE_MULTI;
3942 // initialize the list mode, and load in a list
3943 memset(Multi_create_mission_list, 0, sizeof(multi_create_info) * MULTI_CREATE_MAX_LIST_ITEMS);
3944 memset(Multi_create_campaign_list, 0, sizeof(multi_create_info) * MULTI_CREATE_MAX_LIST_ITEMS);
3945 for(idx=0; idx<MULTI_CREATE_MAX_LIST_ITEMS; idx++){
3946 Multi_create_mission_list[idx].valid_status = MVALID_STATUS_UNKNOWN;
3947 Multi_create_campaign_list[idx].valid_status = MVALID_STATUS_UNKNOWN;
3949 Multi_create_list_mode = MULTI_CREATE_SHOW_MISSIONS;
3950 Multi_create_list_start = -1;
3951 Multi_create_list_select = -1;
3952 Multi_create_list_count = 0;
3954 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);
3956 // create the player list select button
3957 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);
3958 Multi_create_player_select_button.hide();
3960 // create the mission/campaign list select button
3961 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);
3962 Multi_create_list_select_button.hide();
3964 // set hotkeys for a couple of things.
3965 Multi_create_buttons[gr_screen.res][MC_ACCEPT].button.set_hotkey(KEY_CTRLED+KEY_ENTER);
3967 // init some master tracker stuff
3968 Multi_create_frame_count = 0;
3969 Multi_create_mt_tried_login = 0;
3971 // remove campaign flags
3972 Game_mode &= ~(GM_CAMPAIGN_MODE);
3974 // send any pilots as appropriate
3975 multi_data_send_my_junk();
3976 Multi_create_file_list = Multi_create_mission_list;
3978 Multi_create_campaign_count = 0;
3979 Multi_create_mission_count = 0;
3980 Multi_create_files_loaded = 0;
3983 void multi_create_game_do()
3986 char *loading_str = XSTR("Loading", 1336);
3989 // set this if we want to show the pilot info popup
3990 Multi_create_should_show_popup = 0;
3992 // first thing is to load the files
3993 if ( !Multi_create_files_loaded ) {
3994 // if I am a client, send a list request to the server for the missions
3995 if ( MULTIPLAYER_CLIENT ) {
3996 send_mission_list_request( MISSION_LIST_REQUEST );
4000 loading_bitmap = bm_load(Multi_create_loading_fname[gr_screen.res]);
4002 // draw the background, etc
4004 GR_MAYBE_CLEAR_RES(Multi_create_bitmap);
4005 if(Multi_create_bitmap != -1){
4006 gr_set_bitmap(Multi_create_bitmap);
4010 if ( loading_bitmap > -1 ){
4011 gr_set_bitmap(loading_bitmap);
4013 gr_bitmap( Please_wait_coords[gr_screen.res][MC_X_COORD], Please_wait_coords[gr_screen.res][MC_Y_COORD] );
4015 // draw "Loading" on it
4016 gr_set_color_fast(&Color_normal);
4018 gr_get_string_size(&str_w, &str_h, loading_str);
4019 gr_string((gr_screen.max_w - str_w) / 2, (gr_screen.max_h - str_h) / 2, loading_str);
4024 multi_create_list_load_missions();
4025 multi_create_list_load_campaigns();
4027 // if this is a tracker game, validate missions
4028 if(MULTI_IS_TRACKER_GAME){
4029 multi_update_valid_missions();
4032 // update the file list
4033 multi_create_setup_list_data(MULTI_CREATE_SHOW_MISSIONS);
4036 // don't bother setting netgame state if ont the server
4037 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4038 Netgame.game_state = NETGAME_STATE_FORMING;
4039 send_netgame_update_packet();
4042 // if we're on the standalone we have to tell him that we're now in the host setup screen
4043 Net_player->state = NETPLAYER_STATE_HOST_SETUP;
4044 send_netplayer_update_packet();
4046 Multi_create_files_loaded = 1;
4049 int k = chatbox_process();
4050 k = Multi_create_window.process(k,0);
4053 // same as the cancel button
4055 if(help_overlay_active(MULTI_CREATE_OVERLAY)){
4056 help_overlay_set_state(MULTI_CREATE_OVERLAY,0);
4058 gamesnd_play_iface(SND_USER_SELECT);
4059 multi_quit_game(PROMPT_HOST);
4064 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
4065 help_overlay_set_state(MULTI_CREATE_OVERLAY, 0);
4068 // process any button clicks
4069 multi_create_check_buttons();
4071 // do any network related stuff
4072 multi_create_do_netstuff();
4074 // draw the background, etc
4076 GR_MAYBE_CLEAR_RES(Multi_create_bitmap);
4077 if(Multi_create_bitmap != -1){
4078 gr_set_bitmap(Multi_create_bitmap);
4082 // if we're not in team vs. team mode, don't draw the team buttons
4083 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
4084 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.hide();
4085 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.hide();
4086 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.disable();
4087 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.disable();
4089 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.enable();
4090 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.enable();
4091 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.unhide();
4092 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.unhide();
4095 // draw the window itself
4096 Multi_create_window.draw();
4098 gr_set_color_fast(&Color_normal);
4100 // draw Create Game text
4101 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));
4103 // draw players text
4104 gr_string(Mc_players_text[gr_screen.res][MC_X_COORD], Mc_players_text[gr_screen.res][MC_Y_COORD], XSTR("Players", 1269));
4106 // draw players text
4107 gr_string(Mc_team_text[gr_screen.res][MC_X_COORD], Mc_team_text[gr_screen.res][MC_Y_COORD], XSTR("Team", 1258));
4109 // process and display the player list
4110 // NOTE : this must be done before the buttons are checked to insure that a player hasn't dropped
4111 multi_create_plist_process();
4112 if(Netgame.type_flags & NG_TYPE_TEAM){
4113 multi_create_plist_blit_team();
4115 multi_create_plist_blit_normal();
4118 // process and display the game/campaign list
4119 multi_create_list_do();
4121 // draw the correct mission filter button
4122 multi_create_draw_filter_buttons();
4124 // display any text in the info area
4125 multi_common_render_text();
4127 // display any pending notification messages
4128 multi_common_notify_do();
4130 // force the correct mission/campaign button to light up
4131 if( Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ){
4132 Multi_create_buttons[gr_screen.res][MC_MISSION_FILTER].button.draw_forced(2);
4134 Multi_create_buttons[gr_screen.res][MC_CAMPAIGN_FILTER].button.draw_forced(2);
4137 // force draw the closed button if it is toggled on
4138 if(Netgame.flags & NG_FLAG_TEMP_CLOSED){
4139 Multi_create_buttons[gr_screen.res][MC_CLOSE].button.draw_forced(2);
4142 // process and show the chatbox thingie
4146 Multi_create_window.draw_tooltip();
4148 // display the voice status indicator
4149 multi_common_voice_display_status();
4151 // blit the help overlay if necessary
4152 help_overlay_maybe_blit(MULTI_CREATE_OVERLAY);
4155 if(MULTI_IS_TRACKER_GAME){
4156 if(Netgame.type_flags & NG_TYPE_SW){
4157 gr_set_color_fast(&Color_bright);
4159 gr_set_color_fast(&Color_normal);
4161 gr_string(Multi_create_sw_checkbox_text[gr_screen.res][0], Multi_create_sw_checkbox_text[gr_screen.res][1], "SquadWar");
4167 // if we're supposed to show the pilot info popup, do it now
4168 if(Multi_create_should_show_popup){
4169 // get the player index and address of the player item the mouse is currently over
4170 if(Multi_create_plist_select_flag){
4171 player_index = find_player_id(Multi_create_plist_select_id);
4172 if(player_index != -1){
4173 multi_pinfo_popup(&Net_players[player_index]);
4178 // increment the frame count
4179 Multi_create_frame_count++;
4182 void multi_create_game_close()
4184 // unload any bitmaps
4185 if(!bm_unload(Multi_create_bitmap)){
4186 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_create_bitmap_fname[gr_screen.res]));
4189 // unload the help overlay
4190 help_overlay_unload(MULTI_CREATE_OVERLAY);
4192 // destroy the chatbox
4195 // destroy the UI_WINDOW
4196 Multi_create_window.destroy();
4199 void multi_create_check_buttons()
4202 for(idx=0;idx<MULTI_CREATE_NUM_BUTTONS;idx++){
4203 // we only really need to check for one button pressed at a time, so we can break after
4205 if(Multi_create_buttons[gr_screen.res][idx].button.pressed()){
4206 multi_create_button_pressed(idx);
4211 // if the squad war checkbox was clicked
4212 if(Multi_create_sw_checkbox.changed()){
4213 multi_create_sw_clicked();
4217 void multi_create_button_pressed(int n)
4223 gamesnd_play_iface(SND_USER_SELECT);
4224 multi_quit_game(PROMPT_HOST);
4227 // if valid commit conditions have not been met
4228 if(!multi_create_ok_to_commit()){
4233 multi_create_accept_hit();
4238 if(!help_overlay_active(MULTI_CREATE_OVERLAY)){
4239 help_overlay_set_state(MULTI_CREATE_OVERLAY,1);
4241 help_overlay_set_state(MULTI_CREATE_OVERLAY,0);
4245 // scroll the info text box up
4246 case MC_SCROLL_INFO_UP:
4247 multi_common_scroll_text_up();
4250 // scroll the info text box down
4251 case MC_SCROLL_INFO_DOWN:
4252 multi_common_scroll_text_down();
4255 // scroll the player list up
4256 case MC_SCROLL_PLAYERS_UP:
4257 multi_create_plist_scroll_up();
4260 // scroll the player list down
4261 case MC_SCROLL_PLAYERS_DOWN:
4262 multi_create_plist_scroll_down();
4265 // scroll the game/campaign list up
4266 case MC_SCROLL_LIST_UP:
4267 multi_create_list_scroll_up();
4268 Multi_create_slider.forceUp(); // move slider up
4271 // scroll the game/campaign list down
4272 case MC_SCROLL_LIST_DOWN:
4273 multi_create_list_scroll_down();
4274 Multi_create_slider.forceDown(); // move slider down
4277 // go to the options screen
4279 gamesnd_play_iface(SND_USER_SELECT);
4280 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
4283 // show all missions
4285 if(Multi_create_filter != MISSION_TYPE_MULTI){
4286 gamesnd_play_iface(SND_USER_SELECT);
4287 Multi_create_filter = MISSION_TYPE_MULTI;
4288 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4290 gamesnd_play_iface(SND_GENERAL_FAIL);
4294 // show cooperative missions
4296 if(Multi_create_filter != MISSION_TYPE_MULTI_COOP){
4297 gamesnd_play_iface(SND_USER_SELECT);
4298 Multi_create_filter = MISSION_TYPE_MULTI_COOP;
4299 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4301 gamesnd_play_iface(SND_GENERAL_FAIL);
4305 // show team vs. team missions
4307 if(Multi_create_filter != MISSION_TYPE_MULTI_TEAMS){
4308 gamesnd_play_iface(SND_USER_SELECT);
4309 Multi_create_filter = MISSION_TYPE_MULTI_TEAMS;
4310 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4312 gamesnd_play_iface(SND_GENERAL_FAIL);
4316 // show dogfight missions
4317 case MC_SHOW_DOGFIGHT:
4318 if (Multi_create_filter != MISSION_TYPE_MULTI_DOGFIGHT){
4319 gamesnd_play_iface(SND_USER_SELECT);
4320 Multi_create_filter = MISSION_TYPE_MULTI_DOGFIGHT;
4321 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4323 gamesnd_play_iface(SND_GENERAL_FAIL);
4327 // toggle temporary netgame closed on/off
4329 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4330 Netgame.flags ^= NG_FLAG_TEMP_CLOSED;
4332 Netgame.options.flags |= MLO_FLAG_TEMP_CLOSED;
4333 multi_options_update_netgame();
4335 gamesnd_play_iface(SND_USER_SELECT);
4338 // kick the currently selected player (if possible)
4340 // lookup the player at the specified index
4341 if(Multi_create_plist_select_flag){
4342 idx = find_player_id(Multi_create_plist_select_id);
4343 // kick him - but don't ban him
4345 multi_kick_player(idx,0);
4350 // switch to individual mission mode and load in a list
4351 case MC_MISSION_FILTER:
4352 if(Multi_create_list_mode != MULTI_CREATE_SHOW_MISSIONS){
4353 Netgame.campaign_mode = MP_SINGLE;
4355 gamesnd_play_iface(SND_USER_SELECT);
4357 // update the file list
4358 multi_create_setup_list_data(MULTI_CREATE_SHOW_MISSIONS);
4360 gamesnd_play_iface(SND_GENERAL_FAIL);
4364 // switch to campaign mode and load in a list
4365 case MC_CAMPAIGN_FILTER:
4366 // switch off squad war
4367 Multi_create_sw_checkbox.set_state(0);
4368 Netgame.type_flags = NG_TYPE_COOP;
4370 if(Multi_create_list_mode != MULTI_CREATE_SHOW_CAMPAIGNS){
4371 Netgame.campaign_mode = MP_CAMPAIGN;
4373 gamesnd_play_iface(SND_USER_SELECT);
4375 // update the file list
4376 multi_create_setup_list_data(MULTI_CREATE_SHOW_CAMPAIGNS);
4378 gamesnd_play_iface(SND_GENERAL_FAIL);
4382 // attempt to set the selected player's team
4384 multi_create_set_selected_team(0);
4385 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4386 multi_team_send_update();
4390 // attempt to set the selected player's team
4392 multi_create_set_selected_team(1);
4393 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4394 multi_team_send_update();
4398 // popup the pilot info dialog for the currently selected pilot (will occur at the end of the frame)
4400 Multi_create_should_show_popup = 1;
4403 // go to the host options screen
4404 case MC_HOST_OPTIONS:
4405 gamesnd_play_iface(SND_USER_SELECT);
4406 gameseq_post_event(GS_EVENT_MULTI_HOST_OPTIONS);
4409 // refresh PXO file list
4410 case MC_PXO_REFRESH:
4411 if(!MULTI_IS_TRACKER_GAME){
4414 multi_create_refresh_pxo();
4418 gamesnd_play_iface(SND_GENERAL_FAIL);
4419 multi_common_add_notify(XSTR("Not implemented yet!",760));
4424 // do stuff like pinging servers, sending out requests, etc
4425 void multi_create_do_netstuff()
4429 // if not on a standalone
4430 void multi_create_init_as_server()
4432 // set me up as the host and master
4433 Net_player->flags |= (NETINFO_FLAG_AM_MASTER | NETINFO_FLAG_GAME_HOST);
4436 // if on a standalone
4437 void multi_create_init_as_client()
4439 Net_player->flags |= NETINFO_FLAG_GAME_HOST;
4442 // scroll up through the player list
4443 void multi_create_plist_scroll_up()
4445 gamesnd_play_iface(SND_GENERAL_FAIL);
4448 // scroll down through the player list
4449 void multi_create_plist_scroll_down()
4451 gamesnd_play_iface(SND_GENERAL_FAIL);
4454 void multi_create_plist_process()
4456 int test_count,idx,player_index;
4458 // first determine if there are 0 players in the game. This should never happen since the host is _always_ in the game
4460 for(idx=0;idx<MAX_PLAYERS;idx++){
4461 // count anyone except the standalone server (if applicable)
4462 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
4466 if(test_count <= 0){
4470 // if we had a selected item but that player has left, select myself instead
4471 if(Multi_create_plist_select_flag){
4472 player_index = find_player_id(Multi_create_plist_select_id);
4473 if(player_index == -1){
4474 Multi_create_plist_select_id = Net_player->player_id;
4477 Multi_create_plist_select_flag = 1;
4478 Multi_create_plist_select_id = Net_player->player_id;
4481 // if the player has clicked somewhere in the player list area
4482 if(Multi_create_player_select_button.pressed()){
4485 // get the player index and address of the player item the mouse is currently over
4486 player_id = multi_create_get_mouse_id();
4487 player_index = find_player_id(player_id);
4488 if(player_index != -1){
4489 Multi_create_plist_select_flag = 1;
4490 Multi_create_plist_select_id = player_id;
4495 void multi_create_plist_blit_normal()
4498 char str[CALLSIGN_LEN+5];
4499 int y_start = Mc_players_coords[gr_screen.res][MC_Y_COORD];
4502 // display all the players
4503 for(idx=0;idx<MAX_PLAYERS;idx++){
4504 // count anyone except the standalone server (if applicable)
4505 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
4509 // highlight him if he's the host
4510 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4511 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4512 gr_set_color_fast(&Color_text_active_hi);
4514 gr_set_color_fast(&Color_bright);
4517 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4518 gr_set_color_fast(&Color_text_active);
4520 gr_set_color_fast(&Color_text_normal);
4524 // optionally draw his CD status
4525 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4526 gr_set_bitmap(Multi_common_icons[MICON_CD]);
4527 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4529 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4532 // make sure the string will fit, then display it
4533 strcpy(str,Net_players[idx].player->callsign);
4534 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4535 strcat(str,XSTR("(O)",787)); // [[ Observer ]]
4537 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4538 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4545 void multi_create_plist_blit_team()
4548 char str[CALLSIGN_LEN+1];
4549 int y_start = Mc_players_coords[gr_screen.res][MC_Y_COORD];
4552 // display all the red players first
4553 for(idx=0;idx<MAX_PLAYERS;idx++){
4554 // count anyone except the standalone server (if applicable)
4555 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
4556 // reset total offset
4559 // highlight him if he's the host
4560 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4561 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4562 gr_set_color_fast(&Color_text_active_hi);
4564 // be sure to blit the correct team button
4565 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.draw_forced(2);
4567 gr_set_color_fast(&Color_bright);
4570 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4571 gr_set_color_fast(&Color_text_active);
4573 // be sure to blit the correct team button
4574 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.draw_forced(2);
4576 gr_set_color_fast(&Color_text_normal);
4580 // optionally draw his CD status
4581 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4582 gr_set_bitmap(Multi_common_icons[MICON_CD]);
4583 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4585 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4588 // blit the red team indicator
4589 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
4590 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
4591 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT]);
4592 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4594 total_offset += Multi_common_icon_dims[MICON_TEAM0_SELECT][0] + 1;
4597 if(Multi_common_icons[MICON_TEAM0] != -1){
4598 gr_set_bitmap(Multi_common_icons[MICON_TEAM0]);
4599 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4601 total_offset += Multi_common_icon_dims[MICON_TEAM0][0] + 1;
4605 // make sure the string will fit
4606 strcpy(str,Net_players[idx].player->callsign);
4607 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4608 strcat(str,XSTR("(O)",787));
4610 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4612 // display him in the correct half of the list depending on his team
4613 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4618 // display all the green players next
4619 for(idx=0;idx<MAX_PLAYERS;idx++){
4620 // count anyone except the standalone server (if applicable)
4621 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
4622 // reset total offset
4625 // highlight him if he's the host
4626 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4627 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4628 gr_set_color_fast(&Color_text_active_hi);
4630 // be sure to blit the correct team button
4631 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.draw_forced(2);
4633 gr_set_color_fast(&Color_bright);
4636 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4637 gr_set_color_fast(&Color_text_active);
4639 // be sure to blit the correct team button
4640 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.draw_forced(2);
4642 gr_set_color_fast(&Color_text_normal);
4646 // optionally draw his CD status
4647 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4648 gr_set_bitmap(Multi_common_icons[MICON_CD]);
4649 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4651 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4654 // blit the red team indicator
4655 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
4656 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
4657 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT]);
4658 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4660 total_offset += Multi_common_icon_dims[MICON_TEAM1_SELECT][0] + 1;
4663 if(Multi_common_icons[MICON_TEAM1] != -1){
4664 gr_set_bitmap(Multi_common_icons[MICON_TEAM1]);
4665 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4667 total_offset += Multi_common_icon_dims[MICON_TEAM1][0] + 1;
4671 // make sure the string will fit
4672 strcpy(str,Net_players[idx].player->callsign);
4673 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4674 strcat(str,XSTR("(O)",787));
4676 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4678 // display him in the correct half of the list depending on his team
4679 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4685 void multi_create_list_scroll_up()
4687 if(Multi_create_list_start > 0){
4688 Multi_create_list_start--;
4690 gamesnd_play_iface(SND_SCROLL);
4692 gamesnd_play_iface(SND_GENERAL_FAIL);
4696 void multi_create_list_scroll_down()
4698 if((Multi_create_list_count - Multi_create_list_start) > Multi_create_list_max_display[gr_screen.res]){
4699 Multi_create_list_start++;
4701 gamesnd_play_iface(SND_SCROLL);
4703 gamesnd_play_iface(SND_GENERAL_FAIL);
4707 // gets a list of multiplayer misisons
4708 void multi_create_list_load_missions()
4710 char *fname, mission_name[NAME_LENGTH+1];
4711 char wild_card[256];
4714 memset(wild_card, 0, 256);
4715 strcpy(wild_card, NOX("*"));
4716 strcat(wild_card, FS_MISSION_FILE_EXT);
4717 file_count = cf_get_file_list_preallocated(MULTI_CREATE_MAX_LIST_ITEMS, Multi_create_files_array, NULL, CF_TYPE_MISSIONS, wild_card);
4718 Multi_create_mission_count = 0;
4720 // maybe create a standalone dialog
4721 if(Game_mode & GM_STANDALONE_SERVER){
4722 std_create_gen_dialog("Loading missions");
4723 std_gen_set_text("Mission:", 1);
4726 for(idx = 0; idx < file_count; idx++){
4727 int flags,max_players;
4731 fname = Multi_create_files_array[idx];
4733 // tack on any necessary file extension
4734 filename = cf_add_ext( fname, FS_MISSION_FILE_EXT );
4736 // for multiplayer beta builds, only accept builtin missions
4737 #if defined(MULTIPLAYER_BETA_BUILD) || defined(FS2_DEMO)
4738 if(game_find_builtin_mission(filename) == NULL){
4741 #elif defined(PD_BUILD)
4742 if((game_find_builtin_mission(filename) == NULL) && !strstr(filename, "peterdrake")){
4747 if(Game_mode & GM_STANDALONE_SERVER){
4748 std_gen_set_text(filename, 2);
4751 flags = mission_parse_is_multi(filename, mission_name);
4753 // if the mission is a multiplayer mission, then add it to the mission list
4755 max_players = mission_parse_get_multi_mission_info( filename );
4756 m_respawn = The_mission.num_respawns;
4758 if ( Multi_create_mission_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
4759 multi_create_info *mcip;
4761 mcip = &Multi_create_mission_list[Multi_create_mission_count];
4762 strcpy(mcip->filename, filename );
4763 strcpy(mcip->name, mission_name );
4764 mcip->flags = flags;
4765 mcip->respawn = m_respawn;
4766 mcip->max_players = (ubyte)max_players;
4768 // get any additional information for possibly builtin missions
4769 fs_builtin_mission *fb = game_find_builtin_mission(filename);
4773 Multi_create_mission_count++;
4778 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);
4780 // maybe create a standalone dialog
4781 if(Game_mode & GM_STANDALONE_SERVER){
4782 std_destroy_gen_dialog();
4786 void multi_create_list_load_campaigns()
4789 int idx, file_count;
4790 int campaign_type,max_players;
4792 char wild_card[256];
4794 // maybe create a standalone dialog
4795 if(Game_mode & GM_STANDALONE_SERVER){
4796 std_create_gen_dialog("Loading campaigns");
4797 std_gen_set_text("Campaign:", 1);
4800 Multi_create_campaign_count = 0;
4801 memset(wild_card, 0, 256);
4802 strcpy(wild_card, NOX("*"));
4803 strcat(wild_card, FS_CAMPAIGN_FILE_EXT);
4804 file_count = cf_get_file_list_preallocated(MULTI_CREATE_MAX_LIST_ITEMS, Multi_create_files_array, NULL, CF_TYPE_MISSIONS, wild_card);
4805 for(idx = 0; idx < file_count; idx++){
4807 char *filename, name[NAME_LENGTH];
4809 fname = Multi_create_files_array[idx];
4811 // tack on any necessary file extension
4812 filename = cf_add_ext( fname, FS_CAMPAIGN_FILE_EXT );
4814 // for multiplayer beta builds, only accept builtin missions
4815 #if defined(MULTIPLAYER_BETA_BUILD) || defined(FS2_DEMO)
4816 if(game_find_builtin_mission(filename) == NULL){
4819 #elif defined(PD_BUILD)
4820 if((game_find_builtin_mission(filename) == NULL) && !strstr(filename, "peterdrake")){
4825 if(Game_mode & GM_STANDALONE_SERVER){
4826 std_gen_set_text(filename, 2);
4829 // if the campaign is a multiplayer campaign, then add the data to the campaign list items
4830 flags = mission_campaign_parse_is_multi( filename, name );
4831 if( flags != CAMPAIGN_TYPE_SINGLE && mission_campaign_get_info(filename,title,&campaign_type,&max_players)) {
4832 if ( Multi_create_campaign_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
4833 multi_create_info *mcip;
4835 mcip = &Multi_create_campaign_list[Multi_create_campaign_count];
4836 strcpy(mcip->filename, filename );
4837 strcpy(mcip->name, name );
4839 // setup various flags
4840 if ( flags == CAMPAIGN_TYPE_MULTI_COOP ){
4841 mcip->flags = MISSION_TYPE_MULTI_COOP | MISSION_TYPE_MULTI;
4842 } else if ( flags == CAMPAIGN_TYPE_MULTI_TEAMS ) {
4843 mcip->flags = MISSION_TYPE_MULTI_TEAMS | MISSION_TYPE_MULTI;
4845 Int3(); // bogus campaign multi type -- find allender
4848 // 0 respawns for campaign files (should be contained within the mission files themselves)
4851 // 0 max players for campaign files
4852 mcip->max_players = (unsigned char)max_players;
4854 // get any additional information for possibly builtin missions
4855 fs_builtin_mission *fb = game_find_builtin_mission(filename);
4859 Multi_create_campaign_count++;
4864 // maybe create a standalone dialog
4865 if(Game_mode & GM_STANDALONE_SERVER){
4866 std_destroy_gen_dialog();
4870 void multi_create_list_do()
4873 int start_index,stop_index;
4874 char selected_name[255];
4876 // bail early if there aren't any selectable items
4877 if(Multi_create_list_count == 0){
4881 // first check to see if the user has clicked on an item
4882 if(Multi_create_list_select_button.pressed()){
4884 Multi_create_list_select_button.get_mouse_pos(NULL,&y);
4887 // make sure we are selectedin valid indices
4888 if((item < Multi_create_list_max_display[gr_screen.res]) && (item >= 0)){
4889 item += Multi_create_list_start;
4891 if(item < Multi_create_list_count){
4892 multi_create_list_select_item(item);
4893 gamesnd_play_iface(SND_IFACE_MOUSE_CLICK);
4898 // bail early if we don't have a start position
4899 if(Multi_create_list_start == -1){
4903 // display the list of individual campaigns/missions
4905 int y_start = Mc_list_coords[gr_screen.res][MC_Y_COORD];
4907 start_index = multi_create_select_to_index(Multi_create_list_start);
4908 stop_index = Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ? Multi_create_mission_count : Multi_create_campaign_count;
4909 for(idx=start_index; idx<stop_index; idx++){
4910 // see if we should drop out
4911 if(count == Multi_create_list_max_display[gr_screen.res]){
4915 // see if we should filter out this mission
4916 if ( !(Multi_create_file_list[idx].flags & Multi_create_filter) ){
4920 // highlight the selected item
4921 multi_create_select_to_filename(Multi_create_list_select,selected_name);
4922 if(!strcmp(selected_name,Multi_create_file_list[idx].filename)){
4923 gr_set_color_fast(&Color_text_selected);
4925 gr_set_color_fast(&Color_text_normal);
4928 // draw the type icon
4929 multi_create_list_blit_icons(idx, y_start);
4931 // force fit the mission name string
4932 strcpy(selected_name,Multi_create_file_list[idx].name);
4933 gr_force_fit_string(selected_name,255,Mc_column1_w[gr_screen.res]);
4934 gr_string(Mc_mission_name_x[gr_screen.res],y_start,selected_name);
4936 // draw the max players if in mission mode
4937 sprintf(selected_name,"%d",(int)Multi_create_file_list[idx].max_players);
4938 gr_string(Mc_mission_count_x[gr_screen.res],y_start,selected_name);
4940 // force fit the mission filename string
4941 strcpy(selected_name,Multi_create_file_list[idx].filename);
4942 gr_force_fit_string(selected_name,255,Mc_column3_w[gr_screen.res]);
4943 gr_string(Mc_mission_fname_x[gr_screen.res],y_start,selected_name);
4950 // takes care of stuff like changing indices around and setting up the netgame structure
4951 void multi_create_list_select_item(int n)
4953 int abs_index,campaign_type,max_players;
4954 char title[NAME_LENGTH+1];
4955 netgame_info ng_temp;
4958 char *campaign_desc;
4960 // if not on the standalone server
4961 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4964 // on the standalone
4966 memset(&ng_temp,0,sizeof(netgame_info));
4970 if ( n != Multi_create_list_select ) {
4971 // check to see if this is a valid index, and bail if it is not
4972 abs_index = multi_create_select_to_index(n);
4973 if(abs_index == -1){
4977 Multi_create_list_select = n;
4979 // set the mission name
4980 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
4981 multi_create_select_to_filename(n,ng->mission_name);
4983 multi_create_select_to_filename(n,ng->campaign_name);
4986 // make sure the netgame type is properly set
4987 int old_type = Netgame.type_flags;
4988 abs_index = multi_create_select_to_index(n);
4989 if(abs_index != -1){
4990 if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_TEAMS){
4991 // if we're in squad war mode, leave it as squad war
4992 if(old_type & NG_TYPE_SW){
4993 ng->type_flags = NG_TYPE_SW;
4995 ng->type_flags = NG_TYPE_TVT;
4997 } else if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_COOP){
4998 ng->type_flags = NG_TYPE_COOP;
4999 } else if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_DOGFIGHT){
5000 ng->type_flags = NG_TYPE_DOGFIGHT;
5004 // if we're no longer in a TvT game, just uncheck the squadwar checkbox
5005 if(!(ng->type_flags & NG_TYPE_TEAM)){
5006 Multi_create_sw_checkbox.set_state(0);
5009 // if we switched from something else to team vs. team mode, do some special processing
5010 if((ng->type_flags & NG_TYPE_TEAM) && (ng->type_flags != old_type) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
5014 switch(Multi_create_list_mode){
5015 case MULTI_CREATE_SHOW_MISSIONS:
5016 // don't forget to update the info box window thingie
5017 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5018 ship_init(); // mwa -- 10/15/97. Call this function to reset number of ships in mission
5019 ng->max_players = mission_parse_get_multi_mission_info( ng->mission_name );
5021 Assert(ng->max_players > 0);
5022 strcpy(ng->title,The_mission.name);
5024 // set the information area text
5025 multi_common_set_text(The_mission.mission_desc);
5027 // if we're on the standalone, send a request for the description
5029 send_netgame_descript_packet(&Netgame.server_addr,0);
5030 multi_common_set_text("");
5033 // set the respawns as appropriate
5034 if(Netgame.options.respawn <= Multi_create_file_list[abs_index].respawn){
5035 ng->respawn = Netgame.options.respawn;
5036 nprintf(("Network","Using netgame options for respawn count (%d %d)\n",Netgame.options.respawn,Multi_create_file_list[abs_index].respawn));
5038 ng->respawn = Multi_create_file_list[abs_index].respawn;
5039 nprintf(("Network","Using mission settings for respawn count (%d %d)\n",Netgame.options.respawn,Multi_create_file_list[abs_index].respawn));
5042 case MULTI_CREATE_SHOW_CAMPAIGNS:
5043 // if not on the standalone server
5044 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5045 // get the campaign info
5046 memset(title,0,NAME_LENGTH+1);
5047 if(!mission_campaign_get_info(ng->campaign_name,title,&campaign_type,&max_players, &campaign_desc)) {
5048 memset(ng->campaign_name,0,NAME_LENGTH+1);
5049 ng->max_players = 0;
5051 // if we successfully got the # of players
5053 memset(ng->title,0,NAME_LENGTH+1);
5054 strcpy(ng->title,title);
5055 ng->max_players = max_players;
5058 nprintf(("Network","MC MAX PLAYERS : %d\n",ng->max_players));
5060 // set the information area text
5061 // multi_common_set_text(ng->title);
5062 multi_common_set_text(campaign_desc);
5064 // if on the standalone server, send a request for the description
5066 // no descriptions currently kept for campaigns
5069 // netgame respawns are always 0 for campaigns (until the first mission is loaded)
5074 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5076 send_netgame_update_packet();
5078 // update all machines about stuff like respawns, etc.
5079 multi_options_update_netgame();
5081 multi_options_update_mission(ng, Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ? 1 : 0);
5086 void multi_create_list_blit_icons(int list_index, int y_start)
5088 multi_create_info *mcip;
5089 fs_builtin_mission *fb;
5092 // get a pointer to the list item
5093 max_index = Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ? Multi_create_mission_count - 1 : Multi_create_campaign_count - 1;
5094 if((list_index < 0) || (list_index > max_index)){
5097 mcip = &Multi_create_file_list[list_index];
5099 // blit the multiplayer type icons
5100 if(mcip->flags & MISSION_TYPE_MULTI_COOP){
5101 if(Multi_common_icons[MICON_COOP] >= 0){
5102 gr_set_bitmap(Multi_common_icons[MICON_COOP]);
5103 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5105 } else if(mcip->flags & MISSION_TYPE_MULTI_TEAMS){
5106 if(Multi_common_icons[MICON_TVT] >= 0){
5107 gr_set_bitmap(Multi_common_icons[MICON_TVT]);
5108 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5110 } else if(mcip->flags & MISSION_TYPE_MULTI_DOGFIGHT){
5111 if(Multi_common_icons[MICON_DOGFIGHT] >= 0){
5112 gr_set_bitmap(Multi_common_icons[MICON_DOGFIGHT]);
5113 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5117 // if its a valid mission, blit the valid mission icon
5118 if(MULTI_IS_TRACKER_GAME && (mcip->valid_status == MVALID_STATUS_VALID)){
5119 if(Multi_common_icons[MICON_VALID] >= 0){
5120 gr_set_bitmap(Multi_common_icons[MICON_VALID]);
5121 gr_bitmap(Mc_icon_valid_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_valid_coords[gr_screen.res][MC_Y_COORD]);
5125 // now see if its a builtin mission
5126 fb = game_find_builtin_mission(mcip->filename);
5127 // if the mission is from volition, blit the volition icon
5128 if((fb != NULL) && (fb->flags & FSB_FROM_VOLITION)){
5129 if(Multi_common_icons[MICON_VOLITION] >= 0){
5130 gr_set_bitmap(Multi_common_icons[MICON_VOLITION]);
5131 gr_bitmap(Mc_icon_volition_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_volition_coords[gr_screen.res][MC_Y_COORD]);
5136 void multi_create_accept_hit()
5138 char selected_name[255];
5139 int start_campaign = 0;
5141 // make sure all players have finished joining
5142 if(!multi_netplayer_state_check(NETPLAYER_STATE_JOINED,1)){
5143 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("Please wait until all clients have finished joining",788));
5144 gamesnd_play_iface(SND_GENERAL_FAIL);
5147 gamesnd_play_iface(SND_COMMIT_PRESSED);
5150 // do single mission stuff
5151 switch(Multi_create_list_mode){
5152 case MULTI_CREATE_SHOW_MISSIONS:
5153 if(Multi_create_list_select != -1){
5154 // set the netgame mode
5155 Netgame.campaign_mode = MP_SINGLE;
5157 // setup various filenames and mission names
5158 multi_create_select_to_filename(Multi_create_list_select,selected_name);
5159 strncpy( Game_current_mission_filename, selected_name, MAX_FILENAME_LEN );
5160 strncpy(Netgame.mission_name,selected_name,MAX_FILENAME_LEN);
5163 ml_printf(NOX("Starting single mission %s, with %d players"), Game_current_mission_filename, multi_num_players());
5165 multi_common_add_notify(XSTR("No mission selected!",789));
5170 case MULTI_CREATE_SHOW_CAMPAIGNS:
5171 // do campaign related stuff
5172 if(Multi_create_list_select != -1){
5173 // set the netgame mode
5174 Netgame.campaign_mode = MP_CAMPAIGN;
5176 // start a campaign instead of a single mission
5177 multi_create_select_to_filename(Multi_create_list_select,selected_name);
5178 multi_campaign_start(selected_name);
5182 ml_printf(NOX("Starting campaign %s, with %d players"), selected_name, multi_num_players());
5184 multi_common_add_notify(XSTR("No campaign selected!",790));
5190 // if this is a team vs team situation, lock the players send a final team update
5191 if((Netgame.type_flags & NG_TYPE_TEAM) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
5192 multi_team_host_lock_all();
5193 multi_team_send_update();
5196 // if not on the standalone, move to the mission sync state which will take care of everything
5197 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5198 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
5199 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
5201 // otherwise tell the standalone to do so
5203 // when the standalone receives this, he'll do the mission syncing himself
5204 send_mission_sync_packet(MULTI_SYNC_PRE_BRIEFING,start_campaign);
5208 void multi_create_draw_filter_buttons()
5210 // highlight the correct filter button
5211 if ( Multi_create_filter == MISSION_TYPE_MULTI ){
5212 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL].button.draw_forced(2);
5213 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_COOP ) {
5214 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 1].button.draw_forced(2);
5215 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_TEAMS ) {
5216 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 2].button.draw_forced(2);
5217 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_DOGFIGHT ){
5218 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 3].button.draw_forced(2);
5224 void multi_create_set_selected_team(int team)
5228 // if we don't currently have a player selected, don't do anything
5229 if(!Multi_create_plist_select_flag){
5230 gamesnd_play_iface(SND_GENERAL_FAIL);
5233 gamesnd_play_iface(SND_USER_SELECT);
5235 // otherwise attempt to set the team for this guy
5236 player_index = find_player_id(Multi_create_plist_select_id);
5237 if(player_index != -1){
5238 multi_team_set_team(&Net_players[player_index],team);
5242 void multi_create_handle_join(net_player *pl)
5244 // for now just play a bloop sound
5245 gamesnd_play_iface(SND_ICON_DROP_ON_WING);
5248 // fill in net address of player the mouse is over, return player index (or -1 if none)
5249 short multi_create_get_mouse_id()
5251 // determine where he clicked (y pixel value)
5253 Multi_create_player_select_button.get_mouse_pos(NULL,&y);
5255 // select things a little differently if we're in team vs. team or non-team vs. team mode
5257 if(Netgame.type_flags & NG_TYPE_TEAM){
5258 int player_index = -1;
5260 // look through all of team red first
5261 for(idx=0;idx<MAX_PLAYERS;idx++){
5262 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
5265 // if this is the _nth_ guy
5273 // if we still haven't found him yet, look through the green team
5274 if(player_index == -1){
5275 for(idx=0;idx<MAX_PLAYERS;idx++){
5276 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
5278 // if this is the _nth_ guy
5287 if(player_index != -1){
5288 return Net_players[player_index].player_id;
5291 // select the nth active player if possible, disregarding the standalone server
5292 for(idx=0;idx<MAX_PLAYERS;idx++){
5293 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
5296 // if this is the _nth_ guy
5298 return Net_players[idx].player_id;
5307 void multi_create_select_to_filename(int select_index,char *filename)
5311 // look through the mission list
5312 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5313 for(idx=0;idx<Multi_create_mission_count;idx++){
5314 if(Multi_create_file_list[idx].flags & Multi_create_filter){
5318 // if we found the item
5319 if(select_index < 0){
5320 strcpy(filename,Multi_create_file_list[idx].filename);
5325 // look through the campaign list
5326 else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
5327 for(idx=0;idx<Multi_create_campaign_count;idx++){
5330 // if we found the item
5331 if(select_index < 0){
5332 strcpy(filename,Multi_create_file_list[idx].filename);
5338 strcpy(filename,"");
5341 int multi_create_select_to_index(int select_index)
5344 int lookup_index = 0;
5346 // look through the mission list
5347 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5348 for(idx=0;idx<Multi_create_mission_count;idx++){
5349 if(Multi_create_file_list[idx].flags & Multi_create_filter){
5353 // if we found the item
5354 if(select_index < lookup_index){
5359 // look through the campaign list
5360 else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
5361 for(idx=0;idx<Multi_create_campaign_count;idx++){
5364 // if we found the item
5365 if(select_index < 0){
5374 int multi_create_ok_to_commit()
5376 int player_count, observer_count, idx;
5377 int notify_of_hacked_ships_tbl = 0;
5378 int notify_of_hacked_weapons_tbl = 0;
5379 char err_string[255];
5383 // make sure we have a valid mission selected
5384 if(Multi_create_list_select < 0){
5388 // if this is not a valid mission, let the player know
5389 abs_index = multi_create_select_to_index(Multi_create_list_select);
5394 // if we're playing with a hacked ships.tbl (on PXO)
5395 notify_of_hacked_ships_tbl = 0;
5396 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5397 if(!Game_ships_tbl_valid){
5398 notify_of_hacked_ships_tbl = 1;
5401 if(Netgame.flags & NG_FLAG_HACKED_SHIPS_TBL){
5402 notify_of_hacked_ships_tbl = 1;
5405 if(!MULTI_IS_TRACKER_GAME){
5406 notify_of_hacked_ships_tbl = 0;
5408 if(notify_of_hacked_ships_tbl){
5409 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){
5414 // if we're playing with a hacked weapons.tbl (on PXO)
5415 notify_of_hacked_weapons_tbl = 0;
5416 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5417 if(!Game_weapons_tbl_valid){
5418 notify_of_hacked_weapons_tbl = 1;
5421 if(Netgame.flags & NG_FLAG_HACKED_WEAPONS_TBL){
5422 notify_of_hacked_weapons_tbl = 1;
5425 if(!MULTI_IS_TRACKER_GAME){
5426 notify_of_hacked_weapons_tbl = 0;
5428 if(notify_of_hacked_weapons_tbl){
5429 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){
5434 // if any of the players have hacked data
5436 for(idx=0; idx<MAX_PLAYERS; idx++){
5437 // look for hacked players
5438 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAXOR)){
5442 // message everyone - haha
5443 if(Net_players[idx].player != NULL){
5444 sprintf(err_string, "%s %s", Net_players[idx].player->callsign, XSTR("has hacked tables/data", 1271));
5446 sprintf(err_string, "%s", Net_players[idx].player->callsign, XSTR("has hacked tables/data", 1271));
5448 send_game_chat_packet(Net_player, err_string, MULTI_MSG_ALL, NULL, NULL, 1);
5451 // if we found a hacked set of data
5454 if(MULTI_IS_TRACKER_GAME){
5455 // don't allow squad war matches to continue
5456 if(Netgame.type_flags & NG_TYPE_SW){
5458 // if this is squad war, don't allow it to continue
5459 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));
5464 // otherwise, warn the players that stats will not saved
5466 // if this is squad war, don't allow it to continue
5467 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){
5472 // non-pxo, just give a notice
5474 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){
5480 // check to see that we don't have too many observers
5481 observer_count = multi_num_observers();
5482 if(observer_count > Netgame.options.max_observers){
5483 // print up the error string
5484 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);
5486 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, err_string);
5490 // check to see that we have a valid # of players for the the # of ships in the game
5491 player_count = multi_num_players();
5492 if(player_count > Netgame.max_players){
5493 // print up the error string
5494 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);
5496 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, err_string);
5500 // check to see if teams are assigned properly in a team vs. team situation
5501 if(Netgame.type_flags & NG_TYPE_TEAM){
5502 if(!multi_team_ok_to_commit()){
5503 gamesnd_play_iface(SND_GENERAL_FAIL);
5504 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("Teams and/or team captains are not assigned properly", 793));
5510 if(!multi_create_verify_cds()){
5511 gamesnd_play_iface(SND_GENERAL_FAIL);
5513 #ifdef MULTIPLAYER_BETA_BUILD
5514 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, "You need 1 CD for every player!");
5516 #ifdef DVD_MESSAGE_HACK
5517 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You need 1 DVD for every 4 players!", 794));
5519 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You need 1 CD for every 4 players!", 794));
5525 // if we're playing on the tracker
5526 if(MULTI_IS_TRACKER_GAME){
5527 #ifdef PXO_CHECK_VALID_MISSIONS
5528 if((Multi_create_file_list == Multi_create_mission_list) && (Multi_create_file_list[abs_index].valid_status != MVALID_STATUS_VALID)){
5529 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){
5536 if(!(Netgame.type_flags & NG_TYPE_SW)){
5537 // if he is playing by himself, tell him stats will not be accepted
5538 if(multi_num_players() == 1){
5539 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){
5552 int multi_create_verify_cds()
5554 int player_count = multi_num_players();
5558 // count how many cds we have
5560 for(idx=0;idx<MAX_PLAYERS;idx++){
5561 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAS_CD)){
5566 // for the beta, everyone must have a CD
5567 #ifdef MULTIPLAYER_BETA_BUILD
5568 if(multi_cd_count < player_count){
5572 // determine if we have enough
5573 float ratio = (float)player_count / (float)multi_cd_count;
5574 // greater than a 4 to 1 ratio
5580 // we meet the conditions
5584 // returns an index into Multi_create_mission_list
5585 int multi_create_lookup_mission(char *fname)
5589 for(idx=0; idx<Multi_create_mission_count; idx++){
5590 if(!stricmp(fname, Multi_create_mission_list[idx].filename)){
5595 // couldn't find the mission
5599 // returns an index into Multi_create_campaign_list
5600 int multi_create_lookup_campaign(char *fname)
5604 for(idx=0; idx<Multi_create_campaign_count; idx++){
5605 if(!stricmp(fname, Multi_create_campaign_list[idx].filename)){
5610 // couldn't find the campaign
5614 void multi_create_refresh_pxo()
5616 // delete mvalid.cfg if it exists
5617 cf_delete(MULTI_VALID_MISSION_FILE, CF_TYPE_DATA);
5619 // refresh missions from the tracker
5620 multi_update_valid_missions();
5623 void multi_create_sw_clicked()
5625 netgame_info ng_temp;
5628 int file_index = multi_create_select_to_index(Multi_create_list_select);
5630 // either a temporary netgame or the real one
5631 if(MULTIPLAYER_MASTER){
5638 // maybe switch squad war off
5639 if(!Multi_create_sw_checkbox.checked()){
5640 // if the mission selected is a coop mission, go back to coop mode
5641 Assert(file_index != -1);
5642 if(file_index == -1){
5643 ng->type_flags = NG_TYPE_COOP;
5645 if(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_TEAMS){
5646 ng->type_flags = NG_TYPE_TVT;
5647 } else if(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_DOGFIGHT){
5648 ng->type_flags = NG_TYPE_DOGFIGHT;
5650 ng->type_flags = NG_TYPE_COOP;
5653 // switch squad war on
5655 Assert(file_index != -1);
5656 if((file_index == -1) || !(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_TEAMS)){
5657 Multi_create_sw_checkbox.set_state(0);
5659 // at this point we know its safe to switch squad war on
5660 ng->type_flags = NG_TYPE_SW;
5665 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5667 send_netgame_update_packet();
5669 // update all machines about stuff like respawns, etc.
5670 multi_options_update_netgame();
5672 // on the standalone
5674 // standalone will take care of polling the usertracker
5675 multi_options_update_mission(ng, Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ? 1 : 0);
5680 // -------------------------------------------------------------------------------------------------------------
5682 // MULTIPLAYER HOST OPTIONS SCREEN
5685 #define MULTI_HO_NUM_BUTTONS 12
5686 #define MULTI_HO_NUM_RADIO_BUTTONS 10
5690 #define MULTI_HO_PALETTE "InterfacePalette"
5692 static char *Multi_ho_bitmap_fname[GR_NUM_RESOLUTIONS] = {
5693 "MultiHost", // GR_640
5694 "2_MultiHost" // GR_1024
5697 static char *Multi_ho_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
5698 "MultiHost-M", // GR_640
5699 "2_MultiHost-M" // GR_1024
5703 UI_WINDOW Multi_ho_window; // the window object for the join screen
5704 UI_INPUTBOX Multi_ho_respawns; // the # of respawns allowed in the game
5705 UI_INPUTBOX Multi_ho_time_limit; // mission time limit
5706 UI_INPUTBOX Multi_ho_voice_wait; // wait time between tokens
5707 UI_INPUTBOX Multi_ho_kill_limit; // kill limit in a furball mission
5708 UI_INPUTBOX Multi_ho_obs; // # of observers we'll allow
5709 int Multi_ho_bitmap; // the background bitmap
5711 // constants for coordinate lookup
5712 #define MULTI_HO_X_COORD 0
5713 #define MULTI_HO_Y_COORD 1
5714 #define MULTI_HO_W_COORD 2
5715 #define MULTI_HO_H_COORD 3
5716 #define MULTI_HO_TEXT_X_COORD 4
5717 #define MULTI_HO_TEXT_Y_COORD 5
5720 #define MULTI_HO_MSG_RANK 0 // highest ranking players can do messaging
5721 #define MULTI_HO_MSG_LEADER 1 // wing/team leaders can do messaging
5722 #define MULTI_HO_MSG_ANY 2 // any player can do messaging
5723 #define MULTI_HO_MSG_HOST 3 // only the host can do messaging
5724 #define MULTI_HO_END_RANK 4 // highest rank can and host can end mission
5725 #define MULTI_HO_END_LEADER 5 // wing/team leaders and host can end the mission
5726 #define MULTI_HO_END_ANY 6 // any player can end the mission
5727 #define MULTI_HO_END_HOST 7 // only host can end the mission
5728 #define MULTI_HO_VOICE_ON 8 // voice toggled on
5729 #define MULTI_HO_VOICE_OFF 9 // voice toggled off
5730 #define MULTI_HO_HOST_MODIFIES 10 // only the host or team captains can modify ships/weapons in briefing
5731 #define MULTI_HO_ACCEPT 11 // accept button
5733 ui_button_info Multi_ho_buttons[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_BUTTONS] = {
5735 // who is allowed to message
5736 ui_button_info("MH_00", 3, 160, 46, 166, 0), // highest rank
5737 ui_button_info("MH_01", 3, 179, 46, 185, 1), // team/wing leader
5738 ui_button_info("MH_02", 3, 196, 46, 203, 2), // any
5739 ui_button_info("MH_03", 3, 214, 46, 220, 3), // host
5741 // who is allowed to end the mission
5742 ui_button_info("MH_04", 3, 257, 46, 265, 4), // highest rank
5743 ui_button_info("MH_05", 3, 276, 46, 283, 5), // team/wing leader
5744 ui_button_info("MH_06", 3, 294, 46, 300, 6), // any
5745 ui_button_info("MH_07", 3, 311, 46, 317, 7), // host
5747 // voice on/off button
5748 ui_button_info("MH_09", 542, 158, 545, 185, 9),
5749 ui_button_info("MH_10", 598, 158, 604, 185, 10),
5751 // host modifies ships
5752 ui_button_info("MH_13", 542, 377, 437, 363, 13),
5755 ui_button_info("MH_14", 572, 428, 580, 414, 14),
5758 // who is allowed to message
5759 ui_button_info("2_MH_00", 5, 256, 73, 269, 0), // highest rank
5760 ui_button_info("2_MH_01", 5, 286, 73, 297, 1), // team/wing leader
5761 ui_button_info("2_MH_02", 5, 314, 73, 325, 2), // any
5762 ui_button_info("2_MH_03", 5, 341, 73, 352, 3), // host
5764 // who is allowed to end the mission
5765 ui_button_info("2_MH_04", 5, 412, 73, 425, 4), // highest rank
5766 ui_button_info("2_MH_05", 5, 442, 73, 452, 5), // team/wing leader
5767 ui_button_info("2_MH_06", 5, 470, 73, 480, 6), // any
5768 ui_button_info("2_MH_07", 5, 497, 73, 508, 7), // host
5770 // voice on/off button
5771 ui_button_info("2_MH_09", 867, 253, 872, 296, 9),
5772 ui_button_info("2_MH_10", 957, 253, 966, 296, 10),
5774 // host modifies ships
5775 ui_button_info("2_MH_13", 867, 603, 784, 581, 13),
5778 ui_button_info("2_MH_14", 916, 685, 925, 665, 14),
5781 UI_XSTR Multi_ho_text[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_BUTTONS] = {
5783 {"Highest rank", 1280, 46, 166, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_RANK].button},
5784 {"Team / wing-leader", 1281, 46, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_LEADER].button},
5785 {"Any", 1282, 46, 203, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_ANY].button},
5786 {"Host", 1283, 46, 220, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_HOST].button},
5787 {"Highest rank", 1280, 46, 265, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_RANK].button},
5788 {"Team / wing-leader", 1281, 46, 283, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_LEADER].button},
5789 {"Any", 1282, 46, 300, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_ANY].button},
5790 {"Host", 1283, 46, 317, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_HOST].button},
5791 {"On", 1285, 545, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_VOICE_ON].button},
5792 {"Off", 1286, 604, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_VOICE_OFF].button},
5793 {"Host modifies ships", 1287, 437, 363, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_HOST_MODIFIES].button},
5794 {"Exit", 1417, 572, 418, UI_XSTR_COLOR_PINK, -1, &Multi_ho_buttons[0][MULTI_HO_ACCEPT].button},
5797 {"Highest rank", 1280, 62, 269, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_RANK].button},
5798 {"Team / wing-leader", 1281, 62, 297, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_LEADER].button},
5799 {"Any", 1282, 62, 325, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_ANY].button},
5800 {"Host", 1283, 62, 352, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_HOST].button},
5801 {"Highest rank", 1280, 62, 425, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_RANK].button},
5802 {"Team / wing-leader", 1281, 62, 452, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_LEADER].button},
5803 {"Any", 1282, 62, 480, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_ANY].button},
5804 {"Host", 1283, 62, 508, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_HOST].button},
5805 {"On", 1285, 877, 294, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_VOICE_ON].button},
5806 {"Off", 1286, 967, 293, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_VOICE_OFF].button},
5807 {"Host modifies ships", 1287, 869, 589, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_HOST_MODIFIES].button},
5808 {"Exit", 1417, 953, 672, UI_XSTR_COLOR_PINK, -1, &Multi_ho_buttons[1][MULTI_HO_ACCEPT].button},
5812 // radio button controls
5813 #define MULTI_HO_NUM_RADIO_GROUPS 3
5814 #define MULTI_HO_MSG_GROUP 0 // group dealing with squadmate messaging
5815 #define MULTI_HO_END_GROUP 1 // group dealing with ending the mission
5816 #define MULTI_HO_VOICE_GROUP 2 // group dealing with voice stuff
5817 int Multi_ho_radio_groups[MULTI_HO_NUM_RADIO_GROUPS] = { // currently selected button in the radio button group
5820 int Multi_ho_radio_info[MULTI_HO_NUM_RADIO_BUTTONS][3] = { // info related to each of the radio buttons themselves
5821 // { group #, value, button id# }
5822 {0, 0, 0}, // highest ranking players can do messaging
5823 {0, 1, 1}, // wing/team leaders can do messaging
5824 {0, 2, 2}, // any player can do messaging
5825 {0, 3, 3}, // only host can do messaging
5826 {1, 0, 4}, // highest rank and host can end the mission
5827 {1, 1, 5}, // team/wing leader can end the mission
5828 {1, 2, 6}, // any player can end the mission
5829 {1, 3, 7}, // only the host can end the mission
5830 {2, 0, 8}, // voice toggled on
5831 {2, 1, 9} // voice toggled off
5835 #define MULTI_HO_NUM_SLIDERS 3
5836 #define MULTI_HO_SLIDER_VOICE_QOS 0 // voice quality of sound
5837 #define MULTI_HO_SLIDER_VOICE_DUR 1 // max duration of voice recording
5838 #define MULTI_HO_SLIDER_SKILL 2 // skill level
5845 UI_DOT_SLIDER_NEW slider; // because we have a class inside this struct, we need the constructor below..
5847 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){}
5849 ho_sliders Multi_ho_sliders[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_SLIDERS] = {
5851 ho_sliders("MH_11", 428, 214, 437, 199, 11, 19, 10), // voice qos
5852 ho_sliders("MH_12", 428, 261, 437, 246, 12, 19, 10), // voice duration
5853 ho_sliders("MH_08", 237, 454, 230, 411, 8, 36, 5), // skill level
5856 ho_sliders("2_MH_11", 684, 343, 690, 323, 11, 32, 10), // voice qos
5857 ho_sliders("2_MH_12", 685, 418, 837, 468, 12, 32, 10), // voice duration
5858 ho_sliders("2_MH_08", 379, 727, 369, 663, 8, 60, 5), // skill level
5862 int Multi_ho_mission_respawn;
5864 int Multi_ho_host_modifies;
5866 // whether or not any of the inputboxes on this screen had focus last frame
5867 int Multi_ho_lastframe_input = 0;
5869 // game information text areas
5872 #define MULTI_HO_NUM_TITLES 14
5873 UI_XSTR Multi_ho_titles[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_TITLES] = {
5875 { "AI Orders", 1289, 32, 144, UI_XSTR_COLOR_GREEN, -1, NULL },
5876 { "End Mission", 1290, 32, 242, UI_XSTR_COLOR_GREEN, -1, NULL },
5877 { "Time Limit", 1291, 32, 347, UI_XSTR_COLOR_GREEN, -1, NULL },
5878 { "Min", 1292, 74, 362, UI_XSTR_COLOR_GREEN, -1, NULL },
5879 { "Respawn Limit", 1288, 32, 378, UI_XSTR_COLOR_GREEN, -1, NULL },
5880 { "Kill Limit", 1293, 32, 409, UI_XSTR_COLOR_GREEN, -1, NULL },
5881 { "Observers", 1294, 32, 441, UI_XSTR_COLOR_GREEN, -1, NULL },
5882 { "Skill Level", 1284, 230, 411, UI_XSTR_COLOR_GREEN, -1, NULL },
5883 { "Voice Transmission", 1295, 437, 144, UI_XSTR_COLOR_GREEN, -1, NULL },
5884 { "Voice Quality", 1296, 437, 199, UI_XSTR_COLOR_GREEN, -1, NULL },
5885 { "Message Duration", 1297, 437, 246, UI_XSTR_COLOR_GREEN, -1, NULL },
5886 { "sec", 1522, 523, 292, UI_XSTR_COLOR_GREEN, -1, NULL },
5887 { "sec", 1523, 523, 332, UI_XSTR_COLOR_GREEN, -1, NULL },
5888 { "Voice Wait", 1298, 437, 313, UI_XSTR_COLOR_GREEN, -1, NULL },
5891 { "AI Orders", 1289, 48, 238, UI_XSTR_COLOR_GREEN, -1, NULL },
5892 { "End Mission", 1290, 48, 394, UI_XSTR_COLOR_GREEN, -1, NULL },
5893 { "Time Limit", 1291, 50, 568, UI_XSTR_COLOR_GREEN, -1, NULL },
5894 { "Min", 1292, 119, 581, UI_XSTR_COLOR_GREEN, -1, NULL },
5895 { "Respawn Limit", 1288, 50, 618, UI_XSTR_COLOR_GREEN, -1, NULL },
5896 { "Kill Limit", 1293, 50, 668, UI_XSTR_COLOR_GREEN, -1, NULL },
5897 { "Observers", 1294, 50, 718, UI_XSTR_COLOR_GREEN, -1, NULL },
5898 { "Skill Level", 1284, 398, 670, UI_XSTR_COLOR_GREEN, -1, NULL },
5899 { "Voice Transmission", 1295, 869, 239, UI_XSTR_COLOR_GREEN, -1, NULL },
5900 { "Voice Quality", 1296, 690, 331, UI_XSTR_COLOR_GREEN, -1, NULL },
5901 { "Message Duration", 1297, 690, 405, UI_XSTR_COLOR_GREEN, -1, NULL },
5902 { "sec", 1522, 837, 467, UI_XSTR_COLOR_GREEN, -1, NULL },
5903 { "sec", 1523, 837, 534, UI_XSTR_COLOR_GREEN, -1, NULL },
5904 { "Voice Wait", 1298, 742, 510, UI_XSTR_COLOR_GREEN, -1, NULL },
5908 // mission time limit input box
5909 int Ho_time_coords[GR_NUM_RESOLUTIONS][4] = {
5918 // furball kill limit input box
5919 int Ho_kill_coords[GR_NUM_RESOLUTIONS][4] = {
5928 // voice recording duration text display area
5929 int Ho_vd_coords[GR_NUM_RESOLUTIONS][4] = {
5938 // voice token wait input box
5939 int Ho_vw_coords[GR_NUM_RESOLUTIONS][6] = {
5948 // observer count input box
5949 int Ho_obs_coords[GR_NUM_RESOLUTIONS][4] = {
5958 // skill text description area
5959 int Ho_st_coords[GR_NUM_RESOLUTIONS][4] = {
5968 // respawn input box
5969 int Ho_rsp_coords[GR_NUM_RESOLUTIONS][6] = {
5978 // respawn max text area
5979 int Ho_max_rsp_coords[GR_NUM_RESOLUTIONS][2] = {
5988 // maximum values for various input boxes (to notify user of overruns)
5989 #define MULTI_HO_MAX_TIME_LIMIT 500
5990 #define MULTI_HO_MAX_TOKEN_WAIT 5
5991 #define MULTI_HO_MAX_KILL_LIMIT 9999
5992 #define MULTI_HO_MAX_OBS 4
5994 // LOCAL function definitions
5995 void multi_ho_check_buttons();
5996 void multi_ho_button_pressed(int n);
5997 void multi_ho_draw_radio_groups();
5998 void multi_ho_accept_hit();
5999 void multi_ho_get_options();
6000 void multi_ho_apply_options();
6001 void multi_ho_display_record_time();
6002 int multi_ho_check_values();
6003 void multi_ho_check_focus();
6004 void multi_ho_blit_max_respawns();
6005 void multi_ho_display_skill_level();
6007 void multi_host_options_init()
6011 // create the interface window
6012 Multi_ho_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
6013 Multi_ho_window.set_mask_bmap(Multi_ho_bitmap_mask_fname[gr_screen.res]);
6015 // load the background bitmap
6016 Multi_ho_bitmap = bm_load(Multi_ho_bitmap_fname[gr_screen.res]);
6017 if(Multi_ho_bitmap < 0){
6018 // we failed to load the bitmap - this is very bad
6022 // initialize the common notification messaging
6023 multi_common_notify_init();
6025 // use the common interface palette
6026 multi_common_set_palette();
6028 // create the interface buttons
6029 for(idx=0;idx<MULTI_HO_NUM_BUTTONS;idx++){
6030 // create the object
6031 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);
6033 // set the sound to play when highlighted
6034 Multi_ho_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
6036 // set the ani for the button
6037 Multi_ho_buttons[gr_screen.res][idx].button.set_bmaps(Multi_ho_buttons[gr_screen.res][idx].filename);
6039 // set the hotspot, ignoring the skill level button
6040 Multi_ho_buttons[gr_screen.res][idx].button.link_hotspot(Multi_ho_buttons[gr_screen.res][idx].hotspot);
6043 Multi_ho_window.add_XSTR(&Multi_ho_text[gr_screen.res][idx]);
6047 for(idx=0; idx<MULTI_HO_NUM_TITLES; idx++){
6048 Multi_ho_window.add_XSTR(&Multi_ho_titles[gr_screen.res][idx]);
6051 // create the interface sliders
6052 for(idx=0; idx<MULTI_HO_NUM_SLIDERS; idx++){
6053 // create the object
6054 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);
6057 // create the respawn count input box
6058 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);
6059 // if we're in campaign mode, disable it
6060 if(Netgame.campaign_mode == MP_CAMPAIGN){
6061 Multi_ho_respawns.set_text(XSTR("NA",795)); // [[ Not applicable ]]
6062 Multi_ho_respawns.disable();
6064 Multi_ho_respawns.set_valid_chars("0123456789");
6067 // create the time limit input box
6068 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);
6069 Multi_ho_time_limit.set_valid_chars("-0123456789");
6071 // create the voice token wait input box
6072 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);
6073 Multi_ho_voice_wait.set_valid_chars("01243456789");
6075 // create the furball kill limit input box
6076 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);
6077 Multi_ho_kill_limit.set_valid_chars("0123456789");
6079 // create the observer limit input box
6080 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);
6081 Multi_ho_obs.set_valid_chars("01234");
6083 // load in the current netgame defaults
6084 multi_ho_get_options();
6086 // whether or not any of the inputboxes on this screen had focus last frame
6087 Multi_ho_lastframe_input = 0;
6089 // get the # of respawns for the currently selected mission (if any)
6090 if(Multi_create_list_select != -1){
6091 int abs_index = multi_create_select_to_index(Multi_create_list_select);
6093 // if he has a valid mission selected
6095 Multi_ho_mission_respawn = (int)Multi_create_file_list[abs_index].respawn;
6097 Multi_ho_mission_respawn = -1;
6100 Multi_ho_mission_respawn = -1;
6104 void multi_ho_update_sliders()
6106 // game skill slider
6107 if (Game_skill_level != Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos) {
6108 if ( !(Netgame.type_flags & NG_TYPE_TEAM) ){
6109 Game_skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6110 gamesnd_play_iface(SND_USER_SELECT);
6112 Game_skill_level = NUM_SKILL_LEVELS / 2;
6116 // get the voice qos options
6117 if (Netgame.options.voice_qos != (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1)) {
6118 Netgame.options.voice_qos = (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1);
6119 gamesnd_play_iface(SND_USER_SELECT);
6122 // get the voice duration options
6123 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)) {
6124 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);
6125 gamesnd_play_iface(SND_USER_SELECT);
6130 void multi_host_options_do()
6135 k = Multi_ho_window.process();
6138 // process any keypresses
6141 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
6144 case KEY_CTRLED + KEY_ENTER :
6145 gamesnd_play_iface(SND_COMMIT_PRESSED);
6146 multi_ho_accept_hit();
6150 // process any button clicks
6151 multi_ho_check_buttons();
6153 // update the sliders
6154 multi_ho_update_sliders();
6156 // make sure that the chatbox inputbox and any inputbox on this screen are mutually exclusive in terms of focus
6157 multi_ho_check_focus();
6159 // draw the background, etc
6161 GR_MAYBE_CLEAR_RES(Multi_ho_bitmap);
6162 if(Multi_ho_bitmap != -1){
6163 gr_set_bitmap(Multi_ho_bitmap);
6166 Multi_ho_window.draw();
6168 // draw all the radio buttons properly
6169 multi_ho_draw_radio_groups();
6171 // display any pending notification messages
6172 multi_common_notify_do();
6174 // display the voice record time settings
6175 multi_ho_display_record_time();
6177 // maybe display the max # of respawns next to the respawn input box
6178 multi_ho_blit_max_respawns();
6180 // blit the proper skill level
6181 multi_ho_display_skill_level();
6183 // blit the "host modifies button"
6184 if(Multi_ho_host_modifies){
6185 Multi_ho_buttons[gr_screen.res][MULTI_HO_HOST_MODIFIES].button.draw_forced(2);
6188 // process and show the chatbox thingie
6192 Multi_ho_window.draw_tooltip();
6194 // display the voice status indicator
6195 multi_common_voice_display_status();
6201 void multi_host_options_close()
6203 // unload any bitmaps
6204 if(!bm_unload(Multi_ho_bitmap)){
6205 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_ho_bitmap_fname[gr_screen.res]));
6208 // destroy the UI_WINDOW
6209 Multi_ho_window.destroy();
6212 void multi_ho_check_buttons()
6215 for(idx=0;idx<MULTI_HO_NUM_BUTTONS;idx++){
6216 // we only really need to check for one button pressed at a time, so we can break after
6218 if(Multi_ho_buttons[gr_screen.res][idx].button.pressed()){
6219 multi_ho_button_pressed(idx);
6225 void multi_ho_button_pressed(int n)
6227 int radio_index,idx;
6228 int x_pixel,y_pixel;
6230 // get the pixel position of the click
6231 Multi_ho_buttons[gr_screen.res][n].button.get_mouse_pos(&x_pixel,&y_pixel);
6234 // clicked on the accept button
6235 case MULTI_HO_ACCEPT:
6236 gamesnd_play_iface(SND_COMMIT_PRESSED);
6237 multi_ho_accept_hit();
6240 // clicked on the host/captains only modify button
6241 case MULTI_HO_HOST_MODIFIES:
6242 // toggle it on or off
6243 Multi_ho_host_modifies = !Multi_ho_host_modifies;
6244 gamesnd_play_iface(SND_USER_SELECT);
6248 // look through the radio buttons and see which one this corresponds to
6250 for(idx=0;idx<MULTI_HO_NUM_RADIO_BUTTONS;idx++){
6251 if(Multi_ho_radio_info[idx][2] == n){
6256 Assert(radio_index != -1);
6258 // check to see if a radio button was pressed
6259 if(radio_index < MULTI_HO_NUM_RADIO_BUTTONS){
6260 // see if this value is already picked for this radio group
6261 if(Multi_ho_radio_groups[Multi_ho_radio_info[radio_index][0]] != Multi_ho_radio_info[radio_index][1]){
6262 gamesnd_play_iface(SND_USER_SELECT);
6263 Multi_ho_radio_groups[Multi_ho_radio_info[radio_index][0]] = Multi_ho_radio_info[radio_index][1];
6265 gamesnd_play_iface(SND_GENERAL_FAIL);
6270 void multi_ho_draw_radio_groups()
6274 // go through each item and draw it if it is the selected button in its respective group
6275 for(idx=0;idx<MULTI_HO_NUM_RADIO_BUTTONS;idx++){
6276 /// if this button is the currently selected one in its group
6277 if(Multi_ho_radio_info[idx][1] == Multi_ho_radio_groups[Multi_ho_radio_info[idx][0]]){
6278 Multi_ho_buttons[gr_screen.res][Multi_ho_radio_info[idx][2]].button.draw_forced(2);
6283 void multi_ho_accept_hit()
6287 // check the values in the input boxes
6288 if(!multi_ho_check_values()){
6292 // zero out the netgame flags
6295 // set default options
6296 Netgame.options.flags = (MSO_FLAG_INGAME_XFER | MSO_FLAG_ACCEPT_PIX);
6298 // set the squadmate messaging flags
6299 switch(Multi_ho_radio_groups[MULTI_HO_MSG_GROUP]){
6301 Netgame.options.squad_set = MSO_SQUAD_RANK;
6304 Netgame.options.squad_set = MSO_SQUAD_LEADER;
6307 Netgame.options.squad_set = MSO_SQUAD_ANY;
6310 Netgame.options.squad_set = MSO_SQUAD_HOST;
6316 // set the end mission flags
6317 switch(Multi_ho_radio_groups[MULTI_HO_END_GROUP]){
6319 Netgame.options.endgame_set = MSO_END_RANK;
6322 Netgame.options.endgame_set = MSO_END_LEADER;
6325 Netgame.options.endgame_set = MSO_END_ANY;
6328 Netgame.options.endgame_set = MSO_END_HOST;
6334 // set the voice toggle
6335 switch(Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP]){
6337 Netgame.options.flags &= ~(MSO_FLAG_NO_VOICE);
6340 Netgame.options.flags |= MSO_FLAG_NO_VOICE;
6346 // get the voice qos options
6347 Netgame.options.voice_qos = (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1);
6349 // get the voice duration options
6350 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);
6352 // set the skill level. If in team vs. team mode, preserve the old setting before saving
6353 // the pilot file. I'll bet that this doesn't work though because the pilot file gets
6354 // written in a bunch of locations....sigh.
6355 if ( !(Netgame.type_flags & NG_TYPE_TEAM) ){
6356 Game_skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6358 Game_skill_level = NUM_SKILL_LEVELS / 2;
6361 // set the netgame respawn count
6362 // maybe warn the user that respawns will not be used for a campaign mission
6363 if(Netgame.campaign_mode == MP_SINGLE){
6364 Multi_ho_respawns.get_text(resp_str);
6365 uint temp_respawn = (uint)atoi(resp_str);
6366 // if he currently has no mission selected, let the user set any # of respawns
6367 if((int)temp_respawn > Multi_ho_mission_respawn){
6368 if(Multi_ho_mission_respawn == -1){
6369 Netgame.respawn = temp_respawn;
6370 Netgame.options.respawn = temp_respawn;
6372 // this should have been taken care of by the interface code
6377 Netgame.options.respawn = temp_respawn;
6378 Netgame.respawn = temp_respawn;
6382 // get the mission time limit
6383 Multi_ho_time_limit.get_text(resp_str);
6384 int temp_time = atoi(resp_str);
6386 Netgame.options.mission_time_limit = fl2f(-1.0f);
6387 } else if(temp_time > MULTI_HO_MAX_TIME_LIMIT){
6390 Netgame.options.mission_time_limit = fl2f(60.0f * (float)temp_time);
6393 // get observer count options
6394 Multi_ho_obs.get_text(resp_str);
6395 int temp_obs = atoi(resp_str);
6396 if(temp_obs > MULTI_HO_MAX_OBS){
6399 Netgame.options.max_observers = (ubyte)temp_obs;
6401 // get the furball kill limit
6402 Multi_ho_kill_limit.get_text(resp_str);
6403 int temp_kills = atoi(resp_str);
6404 if(temp_kills > MULTI_HO_MAX_KILL_LIMIT){
6407 Netgame.options.kill_limit = temp_kills;
6409 // get the token wait limit
6410 Multi_ho_voice_wait.get_text(resp_str);
6411 int temp_wait = atoi(resp_str);
6412 if(temp_wait > MULTI_HO_MAX_TOKEN_WAIT){
6415 Netgame.options.voice_token_wait = (temp_wait * 1000);
6417 // set the netgame option
6418 Netgame.options.skill_level = (ubyte)Game_skill_level;
6420 // get whether we're in host/captains only modify mode
6421 Netgame.options.flags &= ~(MSO_FLAG_SS_LEADERS);
6422 if(Multi_ho_host_modifies){
6423 Netgame.options.flags |= MSO_FLAG_SS_LEADERS;
6426 // store these values locally
6427 memcpy(&Player->m_local_options,&Net_player->p_info.options,sizeof(multi_local_options));
6428 memcpy(&Player->m_server_options,&Netgame.options,sizeof(multi_server_options));
6429 write_pilot_file(Player);
6431 // apply any changes in settings (notify everyone of voice qos changes, etc)
6432 multi_ho_apply_options();
6434 // move back to the create game screen
6435 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
6438 void multi_ho_get_options()
6442 // set the squadmate messaging buttons
6443 switch(Netgame.options.squad_set){
6444 case MSO_SQUAD_RANK :
6445 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 0;
6447 case MSO_SQUAD_LEADER:
6448 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 1;
6451 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 2;
6453 case MSO_SQUAD_HOST:
6454 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 3;
6460 // set the mission end buttons
6461 switch(Netgame.options.endgame_set){
6463 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 0;
6465 case MSO_END_LEADER:
6466 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 1;
6469 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 2;
6472 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 3;
6478 // set the voice toggle buttons
6479 if(Netgame.options.flags & MSO_FLAG_NO_VOICE){
6480 Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP] = 1;
6482 Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP] = 0;
6485 // get the voice qos options
6486 Assert((Netgame.options.voice_qos >= 1) && (Netgame.options.voice_qos <= 10));
6487 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos = (Netgame.options.voice_qos - 1);
6489 // get the voice duration options
6490 Assert((Netgame.options.voice_record_time > 0) && (Netgame.options.voice_record_time <= MULTI_VOICE_MAX_TIME));
6491 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos = ((int)((float)Netgame.options.voice_record_time / 500.0f)) - 1;
6493 // get the current skill level
6494 Assert((Game_skill_level >= 0) && (Game_skill_level < NUM_SKILL_LEVELS));
6495 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos = Game_skill_level;
6497 // get the # of observers
6498 memset(resp_str,0,10);
6499 sprintf(resp_str,"%d",Netgame.options.max_observers);
6500 Multi_ho_obs.set_text(resp_str);
6502 // set the respawn count
6503 if(Netgame.campaign_mode == MP_SINGLE){
6504 memset(resp_str,0,10);
6505 sprintf(resp_str,"%d",Netgame.respawn);
6506 Multi_ho_respawns.set_text(resp_str);
6509 // set the mission time limit
6510 memset(resp_str,0,10);
6511 float tl = f2fl(Netgame.options.mission_time_limit);
6512 sprintf(resp_str,"%d",(int)(tl / 60.0f));
6513 Multi_ho_time_limit.set_text(resp_str);
6515 // set the furball kill limit
6516 memset(resp_str,0,10);
6517 sprintf(resp_str,"%d",Netgame.options.kill_limit);
6518 Multi_ho_kill_limit.set_text(resp_str);
6520 // set the token wait time
6521 memset(resp_str,0,10);
6522 sprintf(resp_str,"%d",Netgame.options.voice_token_wait / 1000);
6523 Multi_ho_voice_wait.set_text(resp_str);
6525 // get whether we're in host/captains only modify mode
6526 if(Netgame.options.flags & MSO_FLAG_SS_LEADERS){
6527 Multi_ho_host_modifies = 1;
6529 Multi_ho_host_modifies = 0;
6533 void multi_ho_apply_options()
6535 // if the voice qos or duration has changed, apply the change
6536 multi_voice_maybe_update_vars(Netgame.options.voice_qos,Netgame.options.voice_record_time);
6538 // send an options update
6539 multi_options_update_netgame();
6542 // display the voice record time settings
6543 void multi_ho_display_record_time()
6546 int full_seconds, half_seconds;
6549 memset(time_str,0,30);
6552 full_seconds = (((Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 500) / 1000);
6554 // get the half-seconds
6555 half_seconds = ((((Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 500) % 1000) / 500) * 5;
6557 // format the string
6558 sprintf(time_str,"%d.%d",full_seconds,half_seconds);
6559 gr_set_color_fast(&Color_bright);
6560 gr_string(Ho_vd_coords[gr_screen.res][MULTI_HO_X_COORD],Ho_vd_coords[gr_screen.res][MULTI_HO_Y_COORD],time_str);
6563 int multi_ho_check_values()
6567 memset(val_txt,0,255);
6569 // check against respawn settings
6570 if(Multi_ho_mission_respawn != -1){
6571 Multi_ho_respawns.get_text(val_txt);
6572 // if the value is invalid, let the user know
6573 if(atoi(val_txt) > Multi_ho_mission_respawn){
6574 memset(val_txt,0,255);
6575 sprintf(val_txt,XSTR("Warning\nRespawn count in greater than mission specified max (%d)",796),Multi_ho_mission_respawn);
6576 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6581 // check against mission time limit max
6582 Multi_ho_time_limit.get_text(val_txt);
6583 // if the value is invalid, force it to be valid
6584 if(atoi(val_txt) > MULTI_HO_MAX_TIME_LIMIT){
6585 memset(val_txt,0,255);
6586 sprintf(val_txt,XSTR("Warning\nMission time limit is greater than max allowed (%d)",797),MULTI_HO_MAX_TIME_LIMIT);
6587 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6591 // check against max observer limit
6592 Multi_ho_obs.get_text(val_txt);
6593 // if the value is invalid, force it to be valid
6594 if(atoi(val_txt) > MULTI_HO_MAX_OBS){
6595 memset(val_txt,0,255);
6596 sprintf(val_txt,XSTR("Warning\nObserver count is greater than max allowed (%d)",798),MULTI_HO_MAX_OBS);
6597 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6601 // check against furball kill limit
6602 Multi_ho_kill_limit.get_text(val_txt);
6603 // if the value is invalid, force it to be valid
6604 if(atoi(val_txt) > MULTI_HO_MAX_KILL_LIMIT){
6605 memset(val_txt,0,255);
6606 sprintf(val_txt,XSTR("Warning\nMission kill limit is greater than max allowed (%d)",799),MULTI_HO_MAX_KILL_LIMIT);
6607 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6611 // check against the token wait limit
6612 Multi_ho_voice_wait.get_text(val_txt);
6613 if(atoi(val_txt) > MULTI_HO_MAX_TOKEN_WAIT){
6614 memset(val_txt,0,255);
6615 sprintf(val_txt,XSTR("Warning\nvoice wait time is greater than max allowed (%d)",800),MULTI_HO_MAX_TOKEN_WAIT);
6616 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6620 // all values are valid
6624 void multi_ho_check_focus()
6626 // if an inputbox has been pressed (hit enter), lose its focus
6627 if (Multi_ho_respawns.pressed() || Multi_ho_time_limit.pressed() || Multi_ho_voice_wait.pressed() || Multi_ho_kill_limit.pressed() || Multi_ho_obs.pressed()) {
6628 Multi_ho_respawns.clear_focus();
6629 Multi_ho_time_limit.clear_focus();
6630 Multi_ho_voice_wait.clear_focus();
6631 Multi_ho_kill_limit.clear_focus();
6632 Multi_ho_obs.clear_focus();
6633 gamesnd_play_iface(SND_COMMIT_PRESSED);
6634 chatbox_set_focus();
6635 Multi_ho_lastframe_input = 0;
6637 } else if(!Multi_ho_lastframe_input) {
6638 // if we didn't have focus last frame
6639 if(Multi_ho_respawns.has_focus() || Multi_ho_time_limit.has_focus() || Multi_ho_kill_limit.has_focus() || Multi_ho_voice_wait.has_focus() ){
6640 chatbox_lose_focus();
6642 Multi_ho_lastframe_input = 1;
6645 // if we _did_ have focus last frame
6647 // if we no longer have focus on any of the input boxes, set the focus on the chatbox
6648 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()){
6649 chatbox_set_focus();
6651 // if the chatbox now has focus, clear all focus from our inputboxes
6652 else if (chatbox_has_focus()) {
6653 Multi_ho_respawns.clear_focus();
6654 Multi_ho_time_limit.clear_focus();
6655 Multi_ho_kill_limit.clear_focus();
6656 Multi_ho_voice_wait.clear_focus();
6658 Multi_ho_lastframe_input = 0;
6663 void multi_ho_blit_max_respawns()
6667 // if we're in campaign mode, do nothing
6668 if(Netgame.campaign_mode == MP_CAMPAIGN){
6672 // otherwise blit the max as specified by the current mission file
6673 sprintf(string,"(%d)",Multi_ho_mission_respawn);
6674 gr_set_color_fast(&Color_normal);
6675 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);
6678 void multi_ho_display_skill_level()
6680 int skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6683 Assert((skill_level >= 0) && (skill_level < NUM_SKILL_LEVELS));
6684 if((skill_level < 0) || (skill_level >= NUM_SKILL_LEVELS)){
6688 gr_set_color_fast(&Color_bright);
6689 gr_string(Ho_st_coords[gr_screen.res][0], Ho_st_coords[gr_screen.res][1], Skill_level_names(skill_level, 1));
6692 // -------------------------------------------------------------------------------------------------------------
6694 // MULTIPLAYER JOIN SCREEN
6697 #define MULTI_JW_NUM_BUTTONS 8
6701 #define MULTI_JW_PALETTE "InterfacePalette"
6703 static char *Multi_jw_bitmap_fname[GR_NUM_RESOLUTIONS] = {
6704 "MultiJoinWait", // GR_640
6705 "2_MultiJoinWait" // GR_1024
6708 static char *Multi_jw_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
6709 "MultiJoinWait-M", // GR_640
6710 "2_MultiJoinWait-M" // GR_1024
6716 #define MJW_SCROLL_PLAYERS_UP 0
6717 #define MJW_SCROLL_PLAYERS_DOWN 1
6720 #define MJW_PILOT_INFO 4
6721 #define MJW_SCROLL_INFO_UP 5
6722 #define MJW_SCROLL_INFO_DOWN 6
6723 #define MJW_CANCEL 7
6725 UI_WINDOW Multi_jw_window; // the window object for the join screen
6726 int Multi_jw_bitmap; // the background bitmap
6728 // constants for coordinate lookup
6729 #define MJW_X_COORD 0
6730 #define MJW_Y_COORD 1
6731 #define MJW_W_COORD 2
6732 #define MJW_H_COORD 3
6734 ui_button_info Multi_jw_buttons[GR_NUM_RESOLUTIONS][MULTI_JW_NUM_BUTTONS] = {
6736 ui_button_info("MJW_00", 1, 24, -1, -1, 0),
6737 ui_button_info("MJW_01", 1, 66, -1, -1, 1),
6738 ui_button_info("MJW_02", 30, 244, 20, 272, 2),
6739 ui_button_info("MJW_03", 84, 244, 73, 272, 3),
6740 ui_button_info("MJW_04", 139, 242, 134, 272, 4),
6741 ui_button_info("MJW_05", 1, 406, -1, -1, 5),
6742 ui_button_info("MJW_06", 1, 447, -1, -1, 6),
6743 ui_button_info("MJW_07", 577, 428, 570, 414, 7),
6746 ui_button_info("2_MJW_00", 2, 38, -1, -1, 0),
6747 ui_button_info("2_MJW_01", 2, 106, -1, -1, 1),
6748 ui_button_info("2_MJW_02", 48, 390, 47, 435, 2),
6749 ui_button_info("2_MJW_03", 134, 390, 133, 435, 3),
6750 ui_button_info("2_MJW_04", 223, 388, 225, 435, 4),
6751 ui_button_info("2_MJW_05", 2, 649, -1, -1, 5),
6752 ui_button_info("2_MJW_06", 2, 715, -1, -1, 6),
6753 ui_button_info("2_MJW_07", 923, 685, 931, 667, 7),
6757 #define MULTI_JW_NUM_TEXT 7
6759 UI_XSTR Multi_jw_text[GR_NUM_RESOLUTIONS][MULTI_JW_NUM_TEXT] = {
6761 { "Team 1", 1308, 20, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_TEAM0].button },
6762 { "Team 2", 1309, 73, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_TEAM1].button },
6763 { "Pilot", 1310, 134, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_PILOT_INFO].button },
6764 { "Info", 1311, 134, 283, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_PILOT_INFO].button },
6765 { "Cancel", 387, 570, 414, UI_XSTR_COLOR_PINK, -1, &Multi_jw_buttons[0][MJW_CANCEL].button },
6766 { "Players", 1269, 38, 8, UI_XSTR_COLOR_GREEN, -1, NULL },
6767 { "Choose Team", 1312, 27, 231, UI_XSTR_COLOR_GREEN, -1, NULL },
6770 { "Team 1", 1308, 47, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_TEAM0].button },
6771 { "Team 2", 1309, 133, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_TEAM1].button },
6772 { "Pilot", 1310, 225, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_PILOT_INFO].button },
6773 { "Info", 1311, 225, 446, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_PILOT_INFO].button },
6774 { "Cancel", 387, 931, 667, UI_XSTR_COLOR_PINK, -1, &Multi_jw_buttons[1][MJW_CANCEL].button },
6775 { "Players", 1269, 165, 12, UI_XSTR_COLOR_GREEN, -1, NULL },
6776 { "Choose Team", 1312, 45, 373, UI_XSTR_COLOR_GREEN, -1, NULL },
6780 int Mjw_players_coords[GR_NUM_RESOLUTIONS][4] = {
6789 int Mjw_mission_name_coords[GR_NUM_RESOLUTIONS][2] = {
6798 // squad war checkbox
6799 UI_CHECKBOX Multi_jw_sw_checkbox;
6800 char *Multi_jw_sw_checkbox_fname[GR_NUM_RESOLUTIONS] = {
6804 int Multi_jw_sw_checkbox_coords[GR_NUM_RESOLUTIONS][2] = {
6812 int Multi_jw_sw_checkbox_text[GR_NUM_RESOLUTIONS][2] = {
6822 // player list control thingie defs
6823 #define MULTI_JW_PLIST_MAX_DISPLAY 19
6824 int Multi_jw_plist_select_flag; // indicates whether we currently have a selected player
6825 short Multi_jw_plist_select_id; // id of the current selected player
6826 UI_BUTTON Multi_jw_plist_select_button; // for selecting a player
6828 int Multi_jw_should_show_popup = 0;
6830 // LOCAL function definitions
6831 void multi_jw_check_buttons();
6832 void multi_jw_button_pressed(int n);
6833 void multi_jw_do_netstuff();
6834 void multi_jw_scroll_players_up();
6835 void multi_jw_scroll_players_down();
6836 void multi_jw_plist_process();
6837 void multi_jw_plist_blit_normal();
6838 void multi_jw_plist_blit_team();
6839 short multi_jw_get_mouse_id();
6841 void multi_game_client_setup_init()
6845 // create the interface window
6846 Multi_jw_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
6847 Multi_jw_window.set_mask_bmap(Multi_jw_bitmap_mask_fname[gr_screen.res]);
6849 // load the background bitmap
6850 Multi_jw_bitmap = bm_load(Multi_jw_bitmap_fname[gr_screen.res]);
6851 if(Multi_jw_bitmap < 0){
6852 // we failed to load the bitmap - this is very bad
6856 // initialize the player list data
6857 Multi_jw_plist_select_flag = 0;
6858 Multi_jw_plist_select_id = -1;
6860 // kill any old instances of the chatbox and create a new one
6862 chatbox_create(CHATBOX_FLAG_BIG | CHATBOX_FLAG_DRAW_BOX | CHATBOX_FLAG_BUTTONS);
6864 // initialize the common notification messaging
6865 multi_common_notify_init();
6867 // initialize the common mission info display area.
6868 multi_common_set_text("");
6870 // use the common interface palette
6871 multi_common_set_palette();
6873 // create the interface buttons
6874 for(idx=0; idx<MULTI_JW_NUM_BUTTONS; idx++){
6875 // create the object
6876 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);
6878 // set the sound to play when highlighted
6879 Multi_jw_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
6881 // set the ani for the button
6882 Multi_jw_buttons[gr_screen.res][idx].button.set_bmaps(Multi_jw_buttons[gr_screen.res][idx].filename);
6885 Multi_jw_buttons[gr_screen.res][idx].button.link_hotspot(Multi_jw_buttons[gr_screen.res][idx].hotspot);
6888 // if this is a PXO game, enable the squadwar checkbox
6889 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);
6890 Multi_jw_sw_checkbox.set_bmaps(Multi_jw_sw_checkbox_fname[gr_screen.res], 6, 0);
6891 Multi_jw_sw_checkbox.disable();
6892 if(!MULTI_IS_TRACKER_GAME){
6893 Multi_jw_sw_checkbox.hide();
6897 for(idx=0; idx<MULTI_JW_NUM_TEXT; idx++){
6898 Multi_jw_window.add_XSTR(&Multi_jw_text[gr_screen.res][idx]);
6901 // create the player select list button and hide it
6902 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);
6903 Multi_jw_plist_select_button.hide();
6906 Multi_jw_buttons[gr_screen.res][MJW_CANCEL].button.set_hotkey(KEY_ESC);
6908 // remove campaign flags
6909 Game_mode &= ~(GM_CAMPAIGN_MODE);
6911 // tell the server we have finished joining
6912 Net_player->state = NETPLAYER_STATE_JOINED;
6913 send_netplayer_update_packet();
6916 ml_printf(NOX("Joined netgame %s"), Netgame.name);
6918 // send any appropriate files
6919 multi_data_send_my_junk();
6922 void multi_game_client_setup_do_frame()
6925 int k = chatbox_process();
6926 char mission_text[255];
6927 k = Multi_jw_window.process(k,0);
6929 Multi_jw_should_show_popup = 0;
6931 // process any button clicks
6932 multi_jw_check_buttons();
6934 // do any network related stuff
6935 multi_jw_do_netstuff();
6937 // draw the background, etc
6939 GR_MAYBE_CLEAR_RES(Multi_jw_bitmap);
6940 if(Multi_jw_bitmap != -1){
6941 gr_set_bitmap(Multi_jw_bitmap);
6945 // if we're not in team vs. team mode, don't draw the team buttons
6946 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
6947 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.hide();
6948 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.hide();
6949 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.disable();
6950 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.disable();
6952 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.enable();
6953 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.enable();
6954 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.unhide();
6955 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.unhide();
6958 if(MULTI_IS_TRACKER_GAME){
6959 // maybe check the squadwar button
6960 if(Netgame.type_flags & NG_TYPE_SW){
6961 Multi_jw_sw_checkbox.set_state(1);
6962 gr_set_color_fast(&Color_bright);
6964 Multi_jw_sw_checkbox.set_state(0);
6965 gr_set_color_fast(&Color_normal);
6968 gr_string(Multi_jw_sw_checkbox_text[gr_screen.res][0], Multi_jw_sw_checkbox_text[gr_screen.res][1], "SquadWar");
6971 // draw the UI window
6972 Multi_jw_window.draw();
6974 // process and display the player list
6975 // NOTE : this must be done before the buttons are checked to insure that a player hasn't dropped
6976 multi_jw_plist_process();
6977 if(Netgame.type_flags & NG_TYPE_TEAM){
6978 multi_jw_plist_blit_team();
6980 multi_jw_plist_blit_normal();
6983 // display any text in the info area
6984 multi_common_render_text();
6986 // display any pending notification messages
6987 multi_common_notify_do();
6989 // blit the mission filename if possible
6990 if(Netgame.campaign_mode){
6991 if(strlen(Netgame.campaign_name) > 0){
6992 strcpy(mission_text,Netgame.campaign_name);
6994 if(strlen(Netgame.title) > 0){
6995 strcat(mission_text,", ");
6996 strcat(mission_text,Netgame.title);
6999 gr_set_color_fast(&Color_bright_white);
7000 gr_string(Mjw_mission_name_coords[gr_screen.res][MJW_X_COORD],Mjw_mission_name_coords[gr_screen.res][MJW_Y_COORD],mission_text);
7003 if(strlen(Netgame.mission_name) > 0){
7004 strcpy(mission_text,Netgame.mission_name);
7006 if(strlen(Netgame.title) > 0){
7007 strcat(mission_text,", ");
7008 strcat(mission_text,Netgame.title);
7011 gr_set_color_fast(&Color_bright_white);
7012 gr_string(Mjw_mission_name_coords[gr_screen.res][MJW_X_COORD],Mjw_mission_name_coords[gr_screen.res][MJW_Y_COORD],mission_text);
7016 // process and show the chatbox thingie
7020 Multi_jw_window.draw_tooltip();
7022 // display the voice status indicator
7023 multi_common_voice_display_status();
7028 // if we're supposed to be displaying a pilot info popup
7029 if(Multi_jw_should_show_popup){
7030 player_index = find_player_id(Multi_jw_plist_select_id);
7031 if(player_index != -1){
7032 multi_pinfo_popup(&Net_players[player_index]);
7037 void multi_game_client_setup_close()
7039 // unload any bitmaps
7040 if(!bm_unload(Multi_jw_bitmap)){
7041 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_jw_bitmap_fname[gr_screen.res]));
7044 // destroy the chatbox
7047 // destroy the UI_WINDOW
7048 Multi_jw_window.destroy();
7051 if(Netgame.game_state == NETGAME_STATE_MISSION_SYNC){
7052 gamesnd_play_iface(SND_COMMIT_PRESSED);
7057 void multi_jw_check_buttons()
7060 for(idx=0;idx<MULTI_JW_NUM_BUTTONS;idx++){
7061 // we only really need to check for one button pressed at a time, so we can break after
7063 if(Multi_jw_buttons[gr_screen.res][idx].button.pressed()){
7064 multi_jw_button_pressed(idx);
7070 void multi_jw_button_pressed(int n)
7074 gamesnd_play_iface(SND_USER_SELECT);
7075 multi_quit_game(PROMPT_CLIENT);
7077 case MJW_SCROLL_PLAYERS_UP:
7078 multi_jw_scroll_players_up();
7080 case MJW_SCROLL_PLAYERS_DOWN:
7081 multi_jw_scroll_players_down();
7083 case MJW_SCROLL_INFO_UP:
7084 multi_common_scroll_text_up();
7086 case MJW_SCROLL_INFO_DOWN:
7087 multi_common_scroll_text_down();
7090 // request to set myself to team 0
7092 gamesnd_play_iface(SND_USER_SELECT);
7093 multi_team_set_team(Net_player,0);
7096 // request to set myself to team 1
7098 gamesnd_play_iface(SND_USER_SELECT);
7099 multi_team_set_team(Net_player,1);
7103 case MJW_PILOT_INFO:
7104 Multi_jw_should_show_popup = 1;
7108 multi_common_add_notify(XSTR("Not implemented yet!",760));
7113 // do stuff like pinging servers, sending out requests, etc
7114 void multi_jw_do_netstuff()
7118 void multi_jw_scroll_players_up()
7120 gamesnd_play_iface(SND_GENERAL_FAIL);
7123 // scroll down through the player list
7124 void multi_jw_scroll_players_down()
7126 gamesnd_play_iface(SND_GENERAL_FAIL);
7129 void multi_jw_plist_process()
7131 int test_count,player_index,idx;
7133 // first determine if there are 0 players in the game. This should never happen since the host is _always_ in the game
7135 for(idx=0;idx<MAX_PLAYERS;idx++){
7136 // count anyone except the standalone server (if applicable)
7137 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7141 if(test_count <= 0){
7145 // if we had a selected item but that player has left, select myself instead
7146 if(Multi_jw_plist_select_flag){
7147 player_index = find_player_id(Multi_jw_plist_select_id);
7148 if(player_index == -1){
7149 Multi_jw_plist_select_id = Net_player->player_id;
7152 Multi_jw_plist_select_flag = 1;
7153 Multi_jw_plist_select_id = Net_player->player_id;
7156 // if the player has clicked somewhere in the player list area
7157 if(Multi_jw_plist_select_button.pressed()){
7161 player_id = multi_jw_get_mouse_id();
7162 player_index = find_player_id(player_id);
7163 if(player_index != -1){
7164 Multi_jw_plist_select_id = player_id;
7165 Multi_jw_plist_select_flag = 1;
7170 void multi_jw_plist_blit_normal()
7173 char str[CALLSIGN_LEN+1];
7174 int y_start = Mjw_players_coords[gr_screen.res][MJW_Y_COORD];
7177 // display all the players
7178 for(idx=0;idx<MAX_PLAYERS;idx++){
7179 // reset total offset
7182 // count anyone except the standalone server (if applicable)
7183 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7184 // highlight him if he's the host
7185 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7186 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7187 gr_set_color_fast(&Color_text_active_hi);
7189 gr_set_color_fast(&Color_bright);
7192 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7193 gr_set_color_fast(&Color_text_active);
7195 gr_set_color_fast(&Color_text_normal);
7199 // optionally draw his CD status
7200 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7201 gr_set_bitmap(Multi_common_icons[MICON_CD]);
7202 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7204 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7207 // make sure the string will fit, then display it
7208 strcpy(str,Net_players[idx].player->callsign);
7209 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
7212 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7213 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7220 void multi_jw_plist_blit_team()
7223 char str[CALLSIGN_LEN+1];
7224 int y_start = Mjw_players_coords[gr_screen.res][MJW_Y_COORD];
7227 // always blit the proper team button based on _my_ team status
7228 if(Net_player->p_info.team == 0){
7229 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.draw_forced(2);
7231 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.draw_forced(2);
7234 // display all the red players first
7235 for(idx=0;idx<MAX_PLAYERS;idx++){
7236 // reset total offset
7239 // count anyone except the standalone server (if applicable)
7240 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
7241 // highlight him if he's the host
7242 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7243 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7244 gr_set_color_fast(&Color_text_active_hi);
7246 gr_set_color_fast(&Color_bright);
7249 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7250 gr_set_color_fast(&Color_text_active);
7252 gr_set_color_fast(&Color_text_normal);
7256 // optionally draw his CD status
7257 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7258 gr_set_bitmap(Multi_common_icons[MICON_CD]);
7259 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7261 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7264 // blit the red team indicator
7265 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
7266 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
7267 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT]);
7268 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7270 total_offset += Multi_common_icon_dims[MICON_TEAM0_SELECT][0] + 1;
7273 if(Multi_common_icons[MICON_TEAM0] != -1){
7274 gr_set_bitmap(Multi_common_icons[MICON_TEAM0]);
7275 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7277 total_offset += Multi_common_icon_dims[MICON_TEAM0][0] + 1;
7281 // make sure the string will fit
7282 strcpy(str,Net_players[idx].player->callsign);
7283 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7285 // display him in the correct half of the list depending on his team
7286 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7291 // display all the green players next
7292 for(idx=0;idx<MAX_PLAYERS;idx++){
7293 // reset total offset
7296 // count anyone except the standalone server (if applicable)
7297 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
7298 // highlight him if he's the host
7299 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7300 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7301 gr_set_color_fast(&Color_text_active_hi);
7303 gr_set_color_fast(&Color_bright);
7306 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7307 gr_set_color_fast(&Color_text_active);
7309 gr_set_color_fast(&Color_text_normal);
7313 // optionally draw his CD status
7314 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7315 gr_set_bitmap(Multi_common_icons[MICON_CD]);
7316 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7318 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7321 // blit the red team indicator
7322 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
7323 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
7324 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT]);
7325 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7327 total_offset += Multi_common_icon_dims[MICON_TEAM1_SELECT][0] + 1;
7330 if(Multi_common_icons[MICON_TEAM1] != -1){
7331 gr_set_bitmap(Multi_common_icons[MICON_TEAM1]);
7332 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7334 total_offset += Multi_common_icon_dims[MICON_TEAM1][0] + 1;
7338 // make sure the string will fit
7339 strcpy(str,Net_players[idx].player->callsign);
7340 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
7343 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7345 // display him in the correct half of the list depending on his team
7346 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7352 void multi_jw_handle_join(net_player *pl)
7354 // for now just play a bloop sound
7355 gamesnd_play_iface(SND_ICON_DROP_ON_WING);
7358 short multi_jw_get_mouse_id()
7360 // determine where he clicked (y pixel value)
7362 Multi_jw_plist_select_button.get_mouse_pos(NULL,&y);
7364 // select things a little differently if we're in team vs. team or non-team vs. team mode
7366 if(Netgame.type_flags & NG_TYPE_TEAM){
7367 int player_index = -1;
7369 // look through all of team red first
7370 for(idx=0;idx<MAX_PLAYERS;idx++){
7371 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
7374 // if this is the _nth_ guy
7382 // if we still haven't found him yet, look through the green team
7383 if(player_index == -1){
7384 for(idx=0;idx<MAX_PLAYERS;idx++){
7385 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
7387 // if this is the _nth_ guy
7395 if(player_index != -1){
7396 return Net_players[idx].player_id;
7399 // select the nth active player if possible, disregarding the standalone server
7400 for(idx=0;idx<MAX_PLAYERS;idx++){
7401 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7404 // if this is the _nth_ guy
7406 return Net_players[idx].player_id;
7417 // -------------------------------------------------------------------------------------------------------------
7419 // MULTIPLAYER WAIT/SYNCH SCREEN
7422 #define MULTI_SYNC_HOST_COUNT 4 // host uses 4 buttons (and sometimes 5)
7423 #define MULTI_SYNC_CLIENT_COUNT 3 // client only uses 3 buttons
7425 char *Multi_sync_bitmap_fname[GR_NUM_RESOLUTIONS] = {
7426 "MultiSynch", // GR_640
7427 "2_MultiSynch" // GR_1024
7430 char *Multi_sync_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
7431 "MultiSynch-M", // GR_640
7432 "2_MultiSynch-M" // GR_1024
7437 // constants for coordinate lookup
7438 #define MS_X_COORD 0
7439 #define MS_Y_COORD 1
7440 #define MS_W_COORD 2
7441 #define MS_H_COORD 3
7443 UI_WINDOW Multi_sync_window; // the window object for the join screen
7444 int Multi_sync_button_count; // the # of buttons to use for this instance of mission sync
7445 int Multi_sync_bitmap; // the background bitmap
7448 #define MULTI_SYNC_NUM_BUTTONS 5
7449 #define MS_SCROLL_INFO_UP 0
7450 #define MS_SCROLL_INFO_DOWN 1
7454 ui_button_info Multi_sync_buttons[GR_NUM_RESOLUTIONS][MULTI_SYNC_NUM_BUTTONS] = {
7456 ui_button_info("MS_00", 1, 404, -1, -1, 0),
7457 ui_button_info("MS_01", 1, 446, -1, -1, 1),
7458 ui_button_info("MS_03", 518, 426, 519, 416, 3),
7459 ui_button_info("MS_02", 469, 426, 479, 416, 2),
7460 ui_button_info("MS_04", 571, 420, 577, 416, 4),
7463 ui_button_info("2_MS_00", 2, 647, -1, -1, 0),
7464 ui_button_info("2_MS_01", 2, 713, -1, -1, 1),
7465 ui_button_info("2_MS_03", 829, 682, 831, 667, 3),
7466 ui_button_info("2_MS_02", 751, 682, 766, 667, 2),
7467 ui_button_info("2_MS_04", 914, 672, 924, 667, 4),
7472 #define MULTI_SYNC_NUM_TEXT 5
7474 #define MST_LAUNCH 2
7475 UI_XSTR Multi_sync_text[GR_NUM_RESOLUTIONS][MULTI_SYNC_NUM_TEXT] = {
7477 { "Kick", 1266, 479, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_KICK].button },
7478 { "Cancel", 387, 519, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_CANCEL].button },
7479 { "Launch", 801, 577, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_LAUNCH].button },
7480 { "Players", 1269, 23, 133, UI_XSTR_COLOR_GREEN, -1, NULL },
7481 { "Status", 1304, 228, 133, UI_XSTR_COLOR_GREEN, -1, NULL }
7484 { "Kick", 1266, 766, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_KICK].button },
7485 { "Cancel", 387, 831, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_CANCEL].button },
7486 { "Launch", 801, 924, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_LAUNCH].button },
7487 { "Players", 1269, 38, 214, UI_XSTR_COLOR_GREEN, -1, NULL },
7488 { "Status", 1304, 366, 214, UI_XSTR_COLOR_GREEN, -1, NULL }
7493 int Ms_status_coords[GR_NUM_RESOLUTIONS][4] = {
7502 // player status coords
7503 int Ms_status2_coords[GR_NUM_RESOLUTIONS][4] = {
7512 int Ms_cd_icon_offset[GR_NUM_RESOLUTIONS] = {
7517 int Ms_team_icon_offset[GR_NUM_RESOLUTIONS] = {
7522 // player currently selected, index into Net_players[]
7523 int Multi_sync_player_select = -1;
7525 // player list control thingie defs
7526 #define MULTI_SYNC_PLIST_MAX_DISPLAY 15
7527 int Multi_sync_plist_start; // where to start displaying from
7528 int Multi_sync_plist_count; // how many we have
7530 // list select button
7531 UI_BUTTON Multi_sync_plist_button;
7533 int Multi_sync_mode = -1;
7535 #define MULTI_SYNC_COUNTDOWN_TIME 5 // in seconds
7536 float Multi_sync_countdown_timer;
7537 int Multi_sync_countdown = -1;
7539 int Multi_launch_button_created;
7542 // countdown animation timer
7543 char* Multi_sync_countdown_fname[GR_NUM_RESOLUTIONS] = {
7545 "2_Count" // GR_1024
7548 int Multi_sync_countdown_coords[GR_NUM_RESOLUTIONS][2] = {
7559 anim *Multi_sync_countdown_anim = NULL;
7560 anim_instance *Multi_sync_countdown_instance = NULL;
7563 // PREBRIEFING STUFF
7564 // syncing flags used by the server
7565 int Mission_sync_flags = 0;
7566 #define MS_FLAG_SENT_FILESIG (1<<0) // sent filesig requests
7567 #define MS_FLAG_SENT_LOAD (1<<1) // sent load packets
7568 #define MS_FLAG_PUSHED_BRIEFING (1<<2) // pushed everyone else into the briefing
7569 #define MS_FLAG_POST_DATA (1<<3) // sent the post data block
7570 #define MS_FLAG_WSS_SLOTS (1<<4) // all players have received wss slot data
7571 #define MS_FLAG_PSETTINGS (1<<5) // send the player settings packet
7572 #define MS_FLAG_MT_STATS_START (1<<6) // server has started getting player stats from the tracker
7573 #define MS_FLAG_MT_STATS_DONE (1<<7) // server has finished getting player stats from the tracker (success or fail)
7574 #define MS_FLAG_TS_SLOTS (1<<8) // team/ship slots have been sent
7575 #define MS_FLAG_DATA_DONE (1<<9) // done transferring all necessary data
7576 #define MS_FLAG_CAMP_DONE (1<<10) // send campaign pool/goal/event stuff
7578 // POSTBRIEFING STUFF
7579 int Multi_state_timestamp;
7580 int Multi_sync_launch_pressed;
7582 // LOCAL function definitions
7583 void multi_sync_check_buttons();
7584 void multi_sync_button_pressed(int n);
7585 void multi_sync_scroll_info_up();
7586 void multi_sync_scroll_info_down();
7587 void multi_sync_display_name(char *name,int index,int np_index); // display info on the left hand portion of the status window thingie
7588 void multi_sync_display_status(char *status,int index); // display info on the right hand portion of the status window thingie
7589 void multi_sync_force_start_pre();
7590 void multi_sync_force_start_post();
7591 void multi_sync_launch();
7592 void multi_sync_create_launch_button();
7593 void multi_sync_blit_screen_all();
7594 void multi_sync_handle_plist();
7596 void multi_sync_common_init();
7597 void multi_sync_common_do();
7598 void multi_sync_common_close();
7600 void multi_sync_pre_init();
7601 void multi_sync_pre_do();
7602 void multi_sync_pre_close();
7604 void multi_sync_post_init();
7605 void multi_sync_post_do();
7606 void multi_sync_post_close();
7611 // perform the correct init functions
7612 void multi_sync_init()
7614 Multi_sync_countdown = -1;
7618 // reset all timestamp
7619 multi_reset_timestamps();
7621 extern int Player_multi_died_check;
7622 Player_multi_died_check = -1;
7624 if(!(Game_mode & GM_STANDALONE_SERVER)){
7625 multi_sync_common_init();
7628 switch(Multi_sync_mode){
7629 case MULTI_SYNC_PRE_BRIEFING:
7630 multi_sync_pre_init();
7632 case MULTI_SYNC_POST_BRIEFING:
7633 multi_sync_post_init();
7635 case MULTI_SYNC_INGAME:
7636 multi_ingame_sync_init();
7641 // perform the correct do frame functions
7642 void multi_sync_do()
7644 if(!(Game_mode & GM_STANDALONE_SERVER)){
7645 multi_sync_common_do();
7648 // if the netgame is ending, don't do any sync processing
7649 if(multi_endgame_ending()){
7653 // process appropriateliy
7654 switch(Multi_sync_mode){
7655 case MULTI_SYNC_PRE_BRIEFING:
7656 multi_sync_pre_do();
7658 case MULTI_SYNC_POST_BRIEFING:
7659 multi_sync_post_do();
7661 case MULTI_SYNC_INGAME:
7662 multi_ingame_sync_do();
7665 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
7666 if(Multi_sync_bitmap != -1){
7667 gr_set_bitmap(Multi_sync_bitmap);
7670 Multi_sync_window.draw();
7672 multi_sync_blit_screen_all();
7679 // perform the correct close functions
7680 void multi_sync_close()
7682 switch(Multi_sync_mode){
7683 case MULTI_SYNC_PRE_BRIEFING:
7684 multi_sync_pre_close();
7686 case MULTI_SYNC_POST_BRIEFING:
7687 multi_sync_post_close();
7689 case MULTI_SYNC_INGAME:
7690 multi_ingame_sync_close();
7694 if(!(Game_mode & GM_STANDALONE_SERVER)){
7695 multi_sync_common_close();
7699 char *multi_sync_tooltip_handler(char *str)
7701 if (!stricmp(str, NOX("@launch"))) {
7702 if (Multi_launch_button_created){
7703 return XSTR("Launch",801);
7710 void multi_sync_common_init()
7714 // create the interface window
7715 Multi_sync_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
7716 Multi_sync_window.set_mask_bmap(Multi_sync_bitmap_mask_fname[gr_screen.res]);
7717 Multi_sync_window.tooltip_handler = multi_sync_tooltip_handler;
7719 // load the background bitmap
7720 Multi_sync_bitmap = bm_load(Multi_sync_bitmap_fname[gr_screen.res]);
7721 if (Multi_sync_bitmap < 0) {
7722 // we failed to load the bitmap - this is very bad
7726 // initialize the player list data
7727 Multi_sync_plist_start = 0;
7728 Multi_sync_plist_count = 1; // we can pretty safely assume that there's one player in the game - me.
7730 Multi_launch_button_created = 0;
7732 // create the chatbox thingie (shouldn't be necesary to do this, but we'll put it in for good measure)
7735 // force the chatbox to be small
7736 chatbox_force_small();
7738 // initialize the common notification messaging
7739 multi_common_notify_init();
7741 // initialize the common mission info display area.
7742 multi_common_set_text("");
7744 // use the common interface palette
7745 multi_common_set_palette();
7747 // don't select any player yet.
7748 Multi_sync_player_select = -1;
7750 // determine how many of the 5 buttons to create
7751 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
7752 Multi_sync_button_count = MULTI_SYNC_HOST_COUNT;
7754 Multi_sync_button_count = MULTI_SYNC_CLIENT_COUNT;
7756 // create the interface buttons
7757 for(idx=0; idx<Multi_sync_button_count; idx++){
7758 // create the object
7759 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);
7761 // set the sound to play when highlighted
7762 Multi_sync_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
7764 // set the ani for the button
7765 // this wierdness is necessary because cancel and kick buttons aren't drawn on the background bitmap,
7766 // so we have to load in frame 0, too (the file should exist)
7767 if ((idx == MS_CANCEL) || (idx == MS_KICK) || (idx == MS_LAUNCH)) {
7768 Multi_sync_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sync_buttons[gr_screen.res][idx].filename, 3, 0);
7770 Multi_sync_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sync_buttons[gr_screen.res][idx].filename);
7774 Multi_sync_buttons[gr_screen.res][idx].button.link_hotspot(Multi_sync_buttons[gr_screen.res][idx].hotspot);
7778 for(idx=0; idx<MULTI_SYNC_NUM_TEXT; idx++) {
7779 // don't create the "launch" button text just yet
7780 if(idx == MST_LAUNCH) {
7783 // multiplayer clients should ignore the kick button
7784 if(!MULTIPLAYER_MASTER && !MULTIPLAYER_HOST && (idx == MST_KICK)) {
7788 Multi_sync_window.add_XSTR(&Multi_sync_text[gr_screen.res][idx]);
7791 // create the player list select button and hide it
7792 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);
7793 Multi_sync_plist_button.hide();
7795 // set up hotkeys for certain common functions
7796 Multi_sync_buttons[gr_screen.res][MS_CANCEL].button.set_hotkey(KEY_ESC);
7799 void multi_sync_common_do()
7801 int k = chatbox_process();
7802 k = Multi_sync_window.process(k);
7804 // process the player list
7805 multi_sync_handle_plist();
7807 // process any button clicks
7808 multi_sync_check_buttons();
7810 // process any keypresses
7814 gamesnd_play_iface(SND_USER_SELECT);
7815 multi_quit_game(PROMPT_ALL);
7820 void multi_sync_common_close()
7822 // unload any bitmaps
7823 if(!bm_unload(Multi_sync_bitmap)){
7824 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_sync_bitmap_fname[gr_screen.res]));
7827 extern int Player_multi_died_check;
7828 Player_multi_died_check = -1;
7830 // destroy the UI_WINDOW
7831 Multi_sync_window.destroy();
7834 void multi_sync_blit_screen_all()
7841 // display any text in the info area
7842 multi_common_render_text();
7844 // display any pending notification messages
7845 multi_common_notify_do();
7847 // display any info about visible players
7849 for(idx=0;idx<MAX_PLAYERS;idx++){
7850 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7851 // display his name and status
7852 multi_sync_display_name(Net_players[idx].player->callsign,count,idx);
7854 // get the player state
7855 state = Net_players[idx].state;
7857 // if we're ingame joining, show all other players except myself as "playing"
7858 if((Net_player != NULL) && (&Net_players[idx] != Net_player) && ((Multi_sync_mode == MULTI_SYNC_INGAME) || (Net_player->flags & NETINFO_FLAG_INGAME_JOIN)) ){
7859 state = NETPLAYER_STATE_IN_MISSION;
7863 case NETPLAYER_STATE_MISSION_LOADING:
7864 multi_sync_display_status(XSTR("Mission Loading",802),count);
7866 case NETPLAYER_STATE_INGAME_SHIP_SELECT: // I don't think its possible to see this state, but...
7867 multi_sync_display_status(XSTR("Ingame Ship Select",803),count);
7869 case NETPLAYER_STATE_DEBRIEF:
7870 multi_sync_display_status(XSTR("Debriefing",804),count);
7872 case NETPLAYER_STATE_MISSION_SYNC:
7873 multi_sync_display_status(XSTR("Mission Sync",805),count);
7875 case NETPLAYER_STATE_JOINING:
7876 multi_sync_display_status(XSTR("Joining",806),count);
7878 case NETPLAYER_STATE_JOINED:
7879 multi_sync_display_status(XSTR("Joined",807),count);
7881 case NETPLAYER_STATE_SLOT_ACK :
7882 multi_sync_display_status(XSTR("Slot Ack",808),count);
7884 case NETPLAYER_STATE_BRIEFING:
7885 multi_sync_display_status(XSTR("Briefing",765),count);
7887 case NETPLAYER_STATE_SHIP_SELECT:
7888 multi_sync_display_status(XSTR("Ship Select",809),count);
7890 case NETPLAYER_STATE_WEAPON_SELECT:
7891 multi_sync_display_status(XSTR("Weapon Select",810),count);
7893 case NETPLAYER_STATE_WAITING:
7894 multi_sync_display_status(XSTR("Waiting",811),count);
7896 case NETPLAYER_STATE_IN_MISSION:
7897 multi_sync_display_status(XSTR("In Mission",812),count);
7899 case NETPLAYER_STATE_MISSION_LOADED:
7900 multi_sync_display_status(XSTR("Mission Loaded",813),count);
7902 case NETPLAYER_STATE_DATA_LOAD:
7903 multi_sync_display_status(XSTR("Data loading",814),count);
7905 case NETPLAYER_STATE_SETTINGS_ACK:
7906 multi_sync_display_status(XSTR("Ready To Enter Mission",815),count);
7908 case NETPLAYER_STATE_INGAME_SHIPS:
7909 multi_sync_display_status(XSTR("Ingame Ships Packet Ack",816),count);
7911 case NETPLAYER_STATE_INGAME_WINGS:
7912 multi_sync_display_status(XSTR("Ingame Wings Packet Ack",817),count);
7914 case NETPLAYER_STATE_INGAME_RPTS:
7915 multi_sync_display_status(XSTR("Ingame Respawn Points Ack",818),count);
7917 case NETPLAYER_STATE_SLOTS_ACK:
7918 multi_sync_display_status(XSTR("Ingame Weapon Slots Ack",819),count);
7920 case NETPLAYER_STATE_POST_DATA_ACK:
7921 multi_sync_display_status(XSTR("Post Briefing Data Block Ack",820),count);
7923 case NETPLAYER_STATE_FLAG_ACK :
7924 multi_sync_display_status(XSTR("Flags Ack",821),count);
7926 case NETPLAYER_STATE_MT_STATS :
7927 multi_sync_display_status(XSTR("Parallax Online Stats Updating",822),count);
7929 case NETPLAYER_STATE_WSS_ACK :
7930 multi_sync_display_status(XSTR("Weapon Slots Ack",823),count);
7932 case NETPLAYER_STATE_HOST_SETUP :
7933 multi_sync_display_status(XSTR("Host setup",824),count);
7935 case NETPLAYER_STATE_DEBRIEF_ACCEPT:
7936 multi_sync_display_status(XSTR("Debrief accept",825),count);
7938 case NETPLAYER_STATE_DEBRIEF_REPLAY:
7939 multi_sync_display_status(XSTR("Debrief replay",826),count);
7941 case NETPLAYER_STATE_CPOOL_ACK:
7942 multi_sync_display_status(XSTR("Campaign ship/weapon ack",827),count);
7944 case NETPLAYER_STATE_MISSION_XFER :
7946 // server should display the pct completion of all clients
7947 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
7948 if(Net_players[idx].s_info.xfer_handle != -1){
7949 pct_complete = multi_xfer_pct_complete(Net_players[idx].s_info.xfer_handle);
7951 // if we've got a valid xfer handle
7952 if((pct_complete >= 0.0) && (pct_complete <= 1.0)){
7953 sprintf(txt,XSTR("Mission file xfer %d%%",828),(int)(pct_complete * 100.0f));
7957 strcpy(txt,XSTR("Mission file xfer",829));
7960 strcpy(txt,XSTR("Mission file xfer",829));
7963 // clients should display only for themselves (which is the only thing they know)
7965 // if we've got a valid file xfer handle
7966 if((&Net_players[idx] == Net_player) && (Net_player->s_info.xfer_handle != -1)){
7967 pct_complete = multi_xfer_pct_complete(Net_player->s_info.xfer_handle);
7969 // if we've got a valid xfer handle
7970 if((pct_complete >= 0.0) && (pct_complete <= 1.0)){
7971 sprintf(txt,XSTR("Mission file xfer %d%%",828),(int)(pct_complete * 100.0f));
7975 strcpy(txt,XSTR("Mission file xfer",829));
7980 strcpy(txt,XSTR("Mission file xfer",829));
7985 multi_sync_display_status(txt,count);
7988 nprintf(("Network","Unhandled player state : %d !\n",Net_players[idx].state));
7995 // display the mission start countdown timer (if any)
7996 anim_render_all(GS_STATE_MULTI_MISSION_SYNC,flFrametime);
7998 // process and show the chatbox thingie
8002 Multi_sync_window.draw_tooltip();
8004 // display the voice status indicator
8005 multi_common_voice_display_status();
8008 void multi_sync_check_buttons()
8011 for(idx=0;idx<Multi_sync_button_count;idx++){
8012 // we only really need to check for one button pressed at a time, so we can break after
8014 if(Multi_sync_buttons[gr_screen.res][idx].button.pressed()){
8015 multi_sync_button_pressed(idx);
8021 void multi_sync_button_pressed(int n)
8026 gamesnd_play_iface(SND_USER_SELECT);
8027 multi_quit_game(PROMPT_ALL);
8030 // scroll the info box up
8031 case MS_SCROLL_INFO_UP:
8032 multi_common_scroll_text_up();
8035 // scroll the info box down
8036 case MS_SCROLL_INFO_DOWN:
8037 multi_common_scroll_text_down();
8042 // if we have a currently selected player, kick him
8043 if(Multi_sync_player_select >= 0){
8044 multi_kick_player(Multi_sync_player_select);
8048 // start the final launch countdown (post-sync only)
8050 multi_sync_start_countdown();
8053 // doesn't do anything
8059 void multi_sync_pre_init()
8063 Netgame.game_state = NETGAME_STATE_MISSION_SYNC;
8065 // if we're in teamplay mode, always force skill level to be medium
8066 if((Netgame.type_flags & NG_TYPE_TEAM) && (Net_player->flags & NETINFO_FLAG_GAME_HOST)){
8067 Netgame.options.skill_level = NUM_SKILL_LEVELS / 2;
8068 Game_skill_level = NUM_SKILL_LEVELS / 2;
8069 multi_options_update_netgame();
8072 // notify everyone of when we get here
8073 if(!(Game_mode & GM_STANDALONE_SERVER)){
8074 Net_player->state = NETPLAYER_STATE_MISSION_SYNC;
8075 send_netplayer_update_packet();
8078 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8080 ml_string(NOX("Server performing pre-briefing data sync"));
8082 if(!(Game_mode & GM_STANDALONE_SERVER)){
8083 multi_common_set_text(XSTR("Server performing sync\n",830),1);
8086 // maybe initialize tvt and squad war stuff
8087 if(Netgame.type_flags & NG_TYPE_TEAM){
8088 multi_team_level_init();
8091 // force everyone into this state
8092 send_netgame_update_packet();
8094 if(!(Game_mode & GM_STANDALONE_SERVER)){
8095 multi_common_add_text(XSTR("Send update packet\n",831),1);
8098 // setup some of my own data
8099 Net_player->flags |= NETINFO_FLAG_MISSION_OK;
8101 // do any output stuff
8102 if(Game_mode & GM_STANDALONE_SERVER){
8103 std_debug_set_standalone_state_string("Mission Sync");
8106 // do this here to insure we have the most up to date file checksum info
8107 multi_get_mission_checksum(Game_current_mission_filename);
8108 // parse_get_file_signature(Game_current_mission_filename);
8110 if(!(Game_mode & GM_STANDALONE_SERVER)){
8111 multi_common_add_text(XSTR("Got file signatures\n",832),1);
8114 if(!(Game_mode & GM_STANDALONE_SERVER)){
8115 multi_common_add_text(XSTR("Sending update state packet\n",833),1);
8119 // if we're not in team vs. team mode - set all player teams to be 0, and unset all captaincy bits
8120 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
8121 for(idx=0;idx<MAX_PLAYERS;idx++){
8122 Net_players[idx].p_info.team = 0;
8123 Net_players[idx].flags &= ~(NETINFO_FLAG_TEAM_CAPTAIN);
8127 // we aren't necessarily xferring the mission file yet
8128 Assert(Net_player->s_info.xfer_handle == -1);
8130 // always call this for good measure
8131 multi_campaign_flush_data();
8133 Mission_sync_flags = 0;
8134 Multi_mission_loaded = 0;
8137 void multi_sync_pre_do()
8141 // If I'm the server, wait for everyone to arrive in this state, then begin transferring data, etc.
8142 // all servers (standalone or no, go through this)
8143 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8144 // wait for everyone to arrive, then request filesig from all of them
8145 if(multi_netplayer_state_check(NETPLAYER_STATE_MISSION_SYNC) && !(Mission_sync_flags & MS_FLAG_SENT_FILESIG) && !multi_endgame_ending()){
8146 send_file_sig_request(Netgame.mission_name);
8147 Mission_sync_flags |= MS_FLAG_SENT_FILESIG;
8149 if(!(Game_mode & GM_STANDALONE_SERVER)){
8150 multi_common_add_text(XSTR("Sent filesig request\n",834),1);
8154 // if we're waiting for players to receive files, then check on their status
8155 if((Mission_sync_flags & MS_FLAG_SENT_FILESIG) && !multi_netplayer_flag_check(NETINFO_FLAG_MISSION_OK) && !multi_endgame_ending()){
8156 for(idx=0;idx<MAX_PLAYERS;idx++){
8157 // if this player is in the process of xferring a file
8158 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].s_info.xfer_handle != -1)){
8159 switch(multi_xfer_get_status(Net_players[idx].s_info.xfer_handle)){
8160 // if it has successfully completed, set his ok flag
8161 case MULTI_XFER_SUCCESS :
8163 Net_players[idx].flags |= NETINFO_FLAG_MISSION_OK;
8165 // release the xfer instance handle
8166 multi_xfer_release_handle(Net_players[idx].s_info.xfer_handle);
8167 Net_players[idx].s_info.xfer_handle = -1;
8169 // if it has failed or timed-out, kick the player
8170 case MULTI_XFER_TIMEDOUT:
8171 case MULTI_XFER_FAIL:
8172 // release the xfer handle
8173 multi_xfer_release_handle(Net_players[idx].s_info.xfer_handle);
8174 Net_players[idx].s_info.xfer_handle = -1;
8177 multi_kick_player(idx, 0, KICK_REASON_BAD_XFER);
8184 // NOTE : this is now obsolete
8185 // once everyone is verified, do any data transfer necessary
8186 if(multi_netplayer_flag_check(NETINFO_FLAG_MISSION_OK) && !(Mission_sync_flags & MS_FLAG_DATA_DONE) && !multi_endgame_ending()){
8187 // do nothing for now
8188 Mission_sync_flags |= MS_FLAG_DATA_DONE;
8190 // send campaign pool data
8191 multi_campaign_send_pool_status();
8194 // wait for everyone to ack on campaign pool data (even in non-campaign situations)
8195 if((Mission_sync_flags & MS_FLAG_DATA_DONE) && !(Mission_sync_flags & MS_FLAG_CAMP_DONE) && !multi_endgame_ending()){
8196 // check to see if everyone has acked the campaign pool data
8197 if(multi_netplayer_state_check(NETPLAYER_STATE_CPOOL_ACK)){
8198 Mission_sync_flags |= MS_FLAG_CAMP_DONE;
8202 // once everyone is verified, tell them to load the mission
8203 // also make sure to load the mission myself _AFTER_ telling everyone to do so. This makes the whole process
8204 // move along faster
8205 if((Mission_sync_flags & MS_FLAG_CAMP_DONE) && !(Mission_sync_flags & MS_FLAG_SENT_LOAD) && !multi_endgame_ending()){
8206 send_netplayer_load_packet(NULL);
8207 Mission_sync_flags |= MS_FLAG_SENT_LOAD;
8209 if(!(Game_mode & GM_STANDALONE_SERVER)){
8210 multi_common_add_text(XSTR("Sent load packet\n",835),1);
8213 // load the mission myself, as soon as possible
8214 if(!Multi_mission_loaded){
8215 nprintf(("Network","Server loading mission..."));
8217 // update everyone about my status
8218 Net_player->state = NETPLAYER_STATE_MISSION_LOADING;
8219 send_netplayer_update_packet();
8221 game_start_mission();
8223 nprintf(("Network","Done\n"));
8224 Multi_mission_loaded = 1;
8226 // update everyone about my status
8227 Net_player->state = NETPLAYER_STATE_MISSION_LOADED;
8228 send_netplayer_update_packet();
8230 if(!(Game_mode & GM_STANDALONE_SERVER)){
8231 multi_common_add_text(XSTR("Loaded mission locally\n",836),1);
8236 // if everyone has loaded the mission, randomly assign players to ships
8237 if(multi_netplayer_state_check(NETPLAYER_STATE_MISSION_LOADED) && !(Mission_sync_flags & MS_FLAG_TS_SLOTS) && !multi_endgame_ending()){
8238 // call the team select function to assign players to their ships, wings, etc
8239 multi_ts_assign_players_all();
8240 send_netplayer_slot_packet();
8243 Mission_sync_flags |= MS_FLAG_TS_SLOTS;
8246 // if everyone has loaded the mission, move to the team select stage
8247 if(Sync_test && multi_netplayer_state_check(NETPLAYER_STATE_SLOT_ACK) && !(Mission_sync_flags & MS_FLAG_PUSHED_BRIEFING) && !multi_endgame_ending()){
8248 Netgame.game_state = NETGAME_STATE_BRIEFING;
8249 send_netgame_update_packet(); // this will push everyone into the next state
8251 // the standalone moves to his own wait state, whereas in the normal game mode, the server/host moves in to the
8252 // team select state
8253 if(Game_mode & GM_STANDALONE_SERVER){
8254 gameseq_post_event(GS_EVENT_MULTI_STD_WAIT);
8256 gameseq_post_event(GS_EVENT_START_GAME);
8259 Mission_sync_flags |= MS_FLAG_PUSHED_BRIEFING;
8261 if(!(Game_mode & GM_STANDALONE_SERVER)){
8262 multi_common_add_text(XSTR("Moving to team select\n",837),1);
8266 // clients should detect here if they are doing a file xfer and do error processing
8267 if((Net_player->state == NETPLAYER_STATE_MISSION_XFER) && (Net_player->s_info.xfer_handle != -1) && !multi_endgame_ending()){
8268 switch(multi_xfer_get_status(Net_player->s_info.xfer_handle)){
8269 // if it has successfully completed, set his ok flag
8270 case MULTI_XFER_SUCCESS :
8271 // release my xfer handle
8272 multi_xfer_release_handle(Net_player->s_info.xfer_handle);
8273 Net_player->s_info.xfer_handle = -1;
8276 // if it has failed or timed-out, kick the player
8277 case MULTI_XFER_TIMEDOUT:
8278 case MULTI_XFER_FAIL:
8279 // release my xfer handle
8280 multi_xfer_release_handle(Net_player->s_info.xfer_handle);
8281 Net_player->s_info.xfer_handle = -1;
8283 // leave the game qith an error code
8284 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_XFER_FAIL);
8291 if(!(Game_mode & GM_STANDALONE_SERVER)){
8293 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8294 if(Multi_sync_bitmap != -1){
8295 gr_set_bitmap(Multi_sync_bitmap);
8298 Multi_sync_window.draw();
8300 multi_sync_blit_screen_all();
8306 void multi_sync_pre_close()
8308 // at this point, we should shut down any file xfers...
8309 if(Net_player->s_info.xfer_handle != -1){
8310 nprintf(("Network","WARNING - killing file xfer while leaving mission sync state!!!\n"));
8312 multi_xfer_abort(Net_player->s_info.xfer_handle);
8313 Net_player->s_info.xfer_handle;
8317 void multi_sync_post_init()
8319 multi_reset_timestamps();
8321 Multi_state_timestamp = timestamp(0);
8324 ml_string(NOX("Performing post-briefing data sync"));
8326 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8327 multi_common_add_text(XSTR("Server performing sync\n",830),1);
8329 multi_common_add_text(XSTR("Client performing sync\n",838),1);
8332 // everyone should re-initialize these
8333 init_multiplayer_stats();
8335 // reset all sequencing info
8336 multi_oo_reset_sequencing();
8338 // if I am not the master of the game, then send the firing information for my ship
8340 if ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) ){
8341 send_firing_info_packet();
8344 // if I'm not a standalone server, load up the countdown stuff
8345 if(!(Game_mode & GM_STANDALONE_SERVER)){
8346 Multi_sync_countdown_anim = NULL;
8347 Multi_sync_countdown_instance = NULL;
8348 Multi_sync_countdown_anim = anim_load(Multi_sync_countdown_fname[gr_screen.res]);
8349 if(Multi_sync_countdown_anim == NULL){
8350 nprintf(("General","WARNING!, Could not load countdown animation %s!\n",Multi_sync_countdown_fname[gr_screen.res]));
8354 // create objects for all permanent observers
8355 multi_obs_level_init();
8357 // clear the game start countdown timer
8358 Multi_sync_countdown_timer = -1.0f;
8359 Multi_sync_countdown = -1;
8361 // if this is a team vs. team mission, mark all ship teams appropriately
8362 if(Netgame.type_flags & NG_TYPE_TEAM){
8363 multi_team_mark_all_ships();
8366 Mission_sync_flags = 0;
8367 Multi_sync_launch_pressed = 0;
8370 #define MULTI_POST_TIMESTAMP 7000
8372 extern int create_wings();
8374 void multi_sync_post_do()
8378 // only if the host is also the master should he be doing this (non-standalone situation)
8379 if ( Net_player->flags & NETINFO_FLAG_AM_MASTER ) {
8381 // once everyone gets to this screen, send them the ship classes of all ships.
8382 if(multi_netplayer_state_check(NETPLAYER_STATE_WAITING) && !(Mission_sync_flags & MS_FLAG_POST_DATA)) {
8383 // only the host should ever do this
8384 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8385 // at this point we want to delete all necessary ships, change all necessary ship classes, and set all weapons up
8386 multi_ts_create_wings();
8388 // update player ets settings
8389 for(idx=0;idx<MAX_PLAYERS;idx++){
8390 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].player->objnum != -1)){
8391 multi_server_update_player_weapons(&Net_players[idx],&Ships[Objects[Net_players[idx].player->objnum].instance]);
8396 // note that this is done a little differently for standalones and nonstandalones
8397 send_post_sync_data_packet();
8399 multi_common_add_text(XSTR("Sending post briefing block information\n",839),1);
8401 Mission_sync_flags |= MS_FLAG_POST_DATA;
8404 // send weapon slots data
8405 if(multi_netplayer_state_check(NETPLAYER_STATE_POST_DATA_ACK) && !(Mission_sync_flags & MS_FLAG_WSS_SLOTS)) {
8406 // note that this is done a little differently for standalones and nonstandalones
8407 if(Netgame.type_flags & NG_TYPE_TEAM){
8408 send_wss_slots_data_packet(0,0);
8409 send_wss_slots_data_packet(1,1);
8411 send_wss_slots_data_packet(0,1);
8414 multi_common_add_text(XSTR("Sending weapon slots information\n",840),1);
8416 Mission_sync_flags |= MS_FLAG_WSS_SLOTS;
8419 // once weapon information is received, send player settings info
8420 if ( multi_netplayer_state_check(NETPLAYER_STATE_WSS_ACK) && !(Mission_sync_flags & MS_FLAG_PSETTINGS)) {
8421 send_player_settings_packet();
8423 // server (specifically, the standalone), should set this here
8424 Net_player->state = NETPLAYER_STATE_SETTINGS_ACK;
8425 send_netplayer_update_packet();
8427 multi_common_add_text(XSTR("Sending player settings packets\n",841),1);
8429 Mission_sync_flags |= MS_FLAG_PSETTINGS;
8432 // check to see if the countdown timer has started and act appropriately
8433 if( Multi_sync_countdown_timer > -1.0f ) {
8435 // increment by frametime.
8436 Multi_sync_countdown_timer += flFrametime;
8438 // if the animation is not playing, start it
8439 if(!(Game_mode & GM_STANDALONE_SERVER) && (Multi_sync_countdown_instance == NULL) && (Multi_sync_countdown_anim != NULL)){
8440 anim_play_struct aps;
8442 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]);
8443 aps.screen_id = GS_STATE_MULTI_MISSION_SYNC;
8444 aps.framerate_independent = 1;
8446 Multi_sync_countdown_instance = anim_play(&aps);
8449 // if the next second has expired
8450 if( Multi_sync_countdown_timer >= 1.0f ) {
8452 Multi_sync_countdown--;
8453 Multi_sync_countdown_timer = 0.0f;
8455 // if the countdown has reached 0, launch the mission
8456 if(Multi_sync_countdown == 0){
8457 Multi_sync_countdown_timer = -1.0f;
8459 Multi_sync_launch_pressed = 0;
8460 multi_sync_launch();
8462 // otherwise send a countdown packet
8464 send_countdown_packet(Multi_sync_countdown);
8469 // jump into the mission myself
8470 if((Multi_sync_countdown == 0) && multi_netplayer_state_check(NETPLAYER_STATE_IN_MISSION)){
8471 if(!((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !Multi_sync_launch_pressed)){
8472 Net_player->state = NETPLAYER_STATE_IN_MISSION;
8473 Netgame.game_state = NETGAME_STATE_IN_MISSION;
8474 gameseq_post_event(GS_EVENT_ENTER_GAME);
8476 multi_common_add_text(XSTR("Moving into game\n",842),1);
8480 // maybe start the animation countdown
8481 if((Multi_sync_countdown >= 0) && (Multi_sync_countdown_instance == NULL) && (Multi_sync_countdown_anim != NULL)){
8482 anim_play_struct aps;
8484 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]);
8485 aps.screen_id = GS_STATE_MULTI_MISSION_SYNC;
8486 aps.framerate_independent = 1;
8488 Multi_sync_countdown_instance = anim_play(&aps);
8492 // host - specific stuff
8493 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8494 // create the launch button so the host can click
8495 if( Sync_test && multi_netplayer_state_check(NETPLAYER_STATE_SETTINGS_ACK) ){
8496 multi_sync_create_launch_button();
8501 if(!(Game_mode & GM_STANDALONE_SERVER)){
8503 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8504 if(Multi_sync_bitmap != -1){
8505 gr_set_bitmap(Multi_sync_bitmap);
8508 Multi_sync_window.draw();
8510 multi_sync_blit_screen_all();
8516 void multi_sync_post_close()
8520 // if I'm not a standalone server, unload up the countdown stuff
8521 if(!(Game_mode & GM_STANDALONE_SERVER)){
8522 // release all rendering animation instances (should only be 1)
8523 anim_release_all_instances(GS_STATE_MULTI_MISSION_SYNC);
8524 Multi_sync_countdown_instance = NULL;
8526 // free up the countdown animation
8527 if(Multi_sync_countdown_anim != NULL){
8528 anim_free(Multi_sync_countdown_anim);
8529 Multi_sync_countdown_anim = NULL;
8533 // all players should reset sequencing
8534 for(idx=0;idx<MAX_PLAYERS;idx++){
8535 if(Net_player->flags & NETINFO_FLAG_CONNECTED){
8536 Net_players[idx].client_cinfo_seq = 0;
8537 Net_players[idx].client_server_seq = 0;
8541 // multiplayer dogfight
8542 multi_df_level_pre_enter();
8544 // clients should clear obj_pair array and add pair for themselves
8546 if ( MULTIPLAYER_CLIENT ) {
8548 obj_add_pairs( OBJ_INDEX(Player_obj) );
8553 void multi_sync_display_name(char *name,int index,int np_index)
8555 char fit[CALLSIGN_LEN];
8557 // make sure the string actually fits
8560 // if we're in team vs. team mode
8561 if(Netgame.type_flags & NG_TYPE_TEAM){
8562 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]);
8564 // if this is the currently selected player, draw him highlighted
8565 if(np_index == Multi_sync_player_select){
8566 gr_set_color_fast(&Color_text_selected);
8568 gr_set_color_fast(&Color_text_normal);
8572 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);
8574 // blit his team icon
8576 if(Net_players[np_index].p_info.team == 0){
8577 // blit the team captain icon
8578 if(Net_players[np_index].flags & NETINFO_FLAG_TEAM_CAPTAIN){
8579 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
8580 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT]);
8581 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);
8584 // normal team member icon
8586 if(Multi_common_icons[MICON_TEAM0] != -1){
8587 gr_set_bitmap(Multi_common_icons[MICON_TEAM0]);
8588 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);
8593 else if(Net_players[np_index].p_info.team == 1){
8594 // blit the team captain icon
8595 if(Net_players[np_index].flags & NETINFO_FLAG_TEAM_CAPTAIN){
8596 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
8597 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT]);
8598 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);
8601 // normal team member icon
8603 if(Multi_common_icons[MICON_TEAM1] != -1){
8604 gr_set_bitmap(Multi_common_icons[MICON_TEAM1]);
8605 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);
8610 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]);
8612 // if this is the currently selected player, draw him highlighted
8613 if(np_index == Multi_sync_player_select){
8614 gr_set_color_fast(&Color_text_selected);
8616 gr_set_color_fast(&Color_text_normal);
8620 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);
8623 // maybe blit his CD status icon
8624 if((Net_players[np_index].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
8625 gr_set_bitmap(Multi_common_icons[MICON_CD]);
8626 gr_bitmap(Ms_status_coords[gr_screen.res][MS_X_COORD], Ms_status_coords[gr_screen.res][MS_Y_COORD] + (index * 10));
8630 void multi_sync_display_status(char *status,int index)
8634 // make sure the string actually fits
8635 strcpy(fit, status);
8636 gr_force_fit_string(fit, 250, Ms_status2_coords[gr_screen.res][MS_W_COORD] - 20);
8637 gr_set_color_fast(&Color_bright);
8638 gr_string(Ms_status2_coords[gr_screen.res][MS_X_COORD], Ms_status2_coords[gr_screen.res][MS_Y_COORD] + (index * 10), fit);
8641 void multi_sync_force_start_pre()
8644 int want_state = NETPLAYER_STATE_SLOT_ACK; // kick any players who are still in this state
8646 // go through the player list and boot anyone who isn't in the right state
8647 for(idx=0;idx<MAX_PLAYERS;idx++){
8648 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx]) && (Net_players[idx].state == want_state)){
8649 multi_kick_player(idx,0);
8654 void multi_sync_force_start_post()
8658 int num_kill_states;
8660 // determine the state we want all players in so that we can find those who are not in the state
8661 kill_state[0] = NETPLAYER_STATE_BRIEFING;
8662 kill_state[1] = NETPLAYER_STATE_SHIP_SELECT;
8663 kill_state[2] = NETPLAYER_STATE_WEAPON_SELECT;
8664 num_kill_states = 3;
8666 // go through the player list and boot anyone who isn't in the right state
8667 for(idx=0;idx<MAX_PLAYERS;idx++){
8668 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx])){
8669 // check against all kill state
8670 for(idx2 = 0;idx2<num_kill_states;idx2++){
8671 if(Net_players[idx].state == kill_state[idx2]){
8672 multi_kick_player(idx,0);
8680 void multi_sync_start_countdown()
8682 // don't allow repeat button presses
8683 if(Multi_sync_launch_pressed){
8687 Multi_sync_launch_pressed = 1;
8689 // if I'm the server, begin the countdown
8690 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8691 gamesnd_play_iface(SND_COMMIT_PRESSED);
8692 Multi_sync_countdown_timer = 0.0f;
8693 Multi_sync_countdown = MULTI_SYNC_COUNTDOWN_TIME;
8695 // send an initial countdown value
8696 send_countdown_packet(Multi_sync_countdown);
8698 // otherwise send the "start countdown" packet to the standalone
8700 Assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
8701 send_countdown_packet(-1);
8705 void multi_sync_launch()
8707 // don't allow repeat button presses
8708 if(Multi_sync_launch_pressed){
8712 Multi_sync_launch_pressed = 1;
8715 ml_printf(NOX("Entering mission %s"), Game_current_mission_filename);
8717 // tell everyone to jump into the mission
8718 send_jump_into_mission_packet();
8719 Multi_state_timestamp = timestamp(MULTI_POST_TIMESTAMP);
8721 // set the # of players at the start of the mission
8722 Multi_num_players_at_start = multi_num_players();
8723 nprintf(("Network","# of players at start of mission : %d\n", Multi_num_players_at_start));
8725 // initialize datarate limiting for all clients
8726 multi_oo_rate_init_all();
8728 multi_common_add_text(XSTR("Sending mission start packet\n",843),1);
8731 void multi_sync_create_launch_button()
8733 if (!Multi_launch_button_created) {
8734 // create the object
8735 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);
8737 // set the sound to play when highlighted
8738 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_highlight_action(common_play_highlight_sound);
8740 // set the ani for the button
8741 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_bmaps(Multi_sync_buttons[gr_screen.res][MS_LAUNCH].filename, 3, 0);
8744 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.link_hotspot(Multi_sync_buttons[gr_screen.res][MS_LAUNCH].hotspot);
8747 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_hotkey(KEY_CTRLED+KEY_ENTER);
8749 // create the text for the button
8750 Multi_sync_window.add_XSTR(&Multi_sync_text[gr_screen.res][MST_LAUNCH]);
8752 // increment the button count so we start checking this one
8753 Multi_sync_button_count++;
8755 Multi_launch_button_created = 1;
8759 void multi_sync_handle_plist()
8765 // if we don't have a currently selected player, select one
8766 if((Multi_sync_player_select < 0) || !MULTI_CONNECTED(Net_players[Multi_sync_player_select])){
8767 for(idx=0;idx<MAX_PLAYERS;idx++){
8768 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
8769 Multi_sync_player_select = idx;
8775 // check for button list presses
8776 if(Multi_sync_plist_button.pressed()){
8777 // get the y mouse coords
8778 Multi_sync_plist_button.get_mouse_pos(NULL,&my);
8780 // get the index of the item selected
8781 select_index = my / 10;
8783 // if the index is greater than the current # connections, do nothing
8784 if(select_index > (multi_num_connections() - 1)){
8788 // translate into an absolute Net_players[] index (get the Nth net player)
8789 Multi_sync_player_select = -1;
8790 for(idx=0;idx<MAX_PLAYERS;idx++){
8791 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
8795 // if we've found the item we're looking for
8796 if(select_index < 0){
8797 Multi_sync_player_select = idx;
8802 // if for some bizarre reason, this is an invalid player, unselect him and wait for the next interation
8804 if((Multi_sync_player_select >= 0) && (!MULTI_CONNECTED(Net_players[Multi_sync_player_select]) || MULTI_STANDALONE(Net_players[Multi_sync_player_select])) ){
8805 Multi_sync_player_select = -1;
8811 // -------------------------------------------------------------------------------------------------------------
8813 // MULTIPLAYER DEBRIEF SCREEN
8816 // other relevant data
8817 int Multi_debrief_accept_hit;
8818 int Multi_debrief_replay_hit;
8820 // set if the server has left the game
8821 int Multi_debrief_server_left = 0;
8823 // if we've reported on TvT status all players are in the debrief
8824 int Multi_debrief_reported_tvt = 0;
8826 // whether stats are being accepted
8827 // -1 == no decision yet
8830 int Multi_debrief_stats_accept_code = -1;
8832 int Multi_debrief_server_framecount = 0;
8834 float Multi_debrief_time = 0.0f;
8835 float Multi_debrief_resend_time = 10.0f;
8837 void multi_debrief_init()
8841 Multi_debrief_time = 0.0f;
8842 Multi_debrief_resend_time = 10.0f;
8844 // do this to notify the standalone or the normal server that we're in the debrief state and ready to receive packets
8845 if (!(Net_player->flags & NETINFO_FLAG_AM_MASTER)) {
8846 Net_player->state = NETPLAYER_STATE_DEBRIEF;
8847 send_netplayer_update_packet();
8850 // unflag some stuff
8851 for(idx=0;idx<MAX_PLAYERS;idx++){
8852 if(MULTI_CONNECTED(Net_players[idx])){
8853 Net_players[idx].flags &= ~(NETINFO_FLAG_RESPAWNING | NETINFO_FLAG_LIMBO | NETINFO_FLAG_WARPING_OUT);
8857 // if text input mode is active, clear it
8858 multi_msg_text_flush();
8860 // the server has not left yet
8861 Multi_debrief_server_left = 0;
8863 // have not hit accept or replay yet
8864 Multi_debrief_accept_hit = 0;
8865 Multi_debrief_replay_hit = 0;
8867 // stats have not been accepted yet
8868 Multi_debrief_stats_accept_code = -1;
8870 // mark stats as not being store yet
8871 Netgame.flags &= ~(NG_FLAG_STORED_MT_STATS);
8873 // no report on TvT yet
8874 Multi_debrief_reported_tvt = 0;
8876 Multi_debrief_server_framecount = 0;
8879 void multi_debrief_do_frame()
8881 Multi_debrief_time += flFrametime;
8883 // set the netgame state to be debriefing when appropriate
8884 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)){
8885 Netgame.game_state = NETGAME_STATE_DEBRIEF;
8886 send_netgame_update_packet();
8889 // evaluate all server stuff
8890 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8891 multi_debrief_server_process();
8895 void multi_debrief_close()
8897 if ( MULTIPLAYER_CLIENT && (Netgame.game_state == NETGAME_STATE_MISSION_SYNC) ){
8898 gamesnd_play_iface( SND_COMMIT_PRESSED );
8902 // handle optional mission loop
8903 void multi_maybe_set_mission_loop()
8905 int cur = Campaign.current_mission;
8906 if (Campaign.missions[cur].has_mission_loop) {
8907 Assert(Campaign.loop_mission != CAMPAIGN_LOOP_MISSION_UNINITIALIZED);
8909 bool require_repeat_mission = (Campaign.current_mission == Campaign.next_mission);
8911 // check for (1) mission loop available, (2) dont have to repeat last mission
8912 if ( (Campaign.missions[cur].has_mission_loop && (Campaign.loop_mission != -1)) && !require_repeat_mission ) {
8915 debrief_assemble_optional_mission_popup_text(buffer, Campaign.missions[cur].mission_loop_desc);
8917 int choice = popup(0 , 2, POPUP_NO, POPUP_YES, buffer);
8919 Campaign.loop_enabled = 1;
8920 Campaign.next_mission = Campaign.loop_mission;
8925 // handle all cases for when the accept key is hit in a multiplayer debriefing
8926 void multi_debrief_accept_hit()
8928 // if we already accepted, do nothing
8929 // but he may need to hit accept again after the server has left the game, so allow this
8930 if(Multi_debrief_accept_hit){
8934 // mark this so that we don't hit it again
8935 Multi_debrief_accept_hit = 1;
8937 gamesnd_play_iface(SND_COMMIT_PRESSED);
8939 // if the server has left the game, always just end the game.
8940 if(Multi_debrief_server_left){
8941 if(!multi_quit_game(PROMPT_ALL)){
8942 Multi_debrief_server_left = 1;
8943 Multi_debrief_accept_hit = 0;
8945 Multi_debrief_server_left = 0;
8948 // query the host and see if he wants to accept stats
8949 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8950 // if we're on a tracker game, he gets no choice for storing stats
8951 if(MULTI_IS_TRACKER_GAME){
8952 multi_maybe_set_mission_loop();
8954 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));
8956 // evaluate the result
8961 Multi_debrief_accept_hit = 0;
8964 // set the accept code to be "not accepting"
8966 multi_debrief_stats_toss();
8967 multi_maybe_set_mission_loop();
8970 // accept the stats and continue
8972 multi_debrief_stats_accept();
8973 multi_maybe_set_mission_loop();
8979 // set my netplayer state to be "debrief_accept", and be done with it
8980 Net_player->state = NETPLAYER_STATE_DEBRIEF_ACCEPT;
8981 send_netplayer_update_packet();
8985 // handle all cases for when the escape key is hit in a multiplayer debriefing
8986 void multi_debrief_esc_hit()
8990 // if the server has left
8991 if(Multi_debrief_server_left){
8992 multi_quit_game(PROMPT_ALL);
8997 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8998 // if the stats have already been accepted
8999 if((Multi_debrief_stats_accept_code != -1) || (MULTI_IS_TRACKER_GAME)){
9000 multi_quit_game(PROMPT_HOST);
9002 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));
9004 // evaluate the result
9011 // set the accept code to be "not accepting"
9013 multi_debrief_stats_toss();
9014 multi_quit_game(PROMPT_NONE);
9017 // accept the stats and continue
9019 multi_debrief_stats_accept();
9020 multi_quit_game(PROMPT_NONE);
9025 // if the stats haven't been accepted yet, or this is a tracker game
9026 if((Multi_debrief_stats_accept_code == -1) && !(MULTI_IS_TRACKER_GAME)){
9027 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));
9029 // evaluate the result
9031 multi_quit_game(PROMPT_NONE);
9034 // otherwise go through the normal endgame channels
9036 multi_quit_game(PROMPT_ALL);
9041 void multi_debrief_replay_hit()
9043 // only the host should ever get here
9044 Assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
9046 // if the button was already pressed, do nothing
9047 if(Multi_debrief_accept_hit){
9051 // same as hittin the except button except no stats are kept
9052 Multi_debrief_accept_hit = 1;
9054 // mark myself as being in the replay state so we know what to do next
9055 Net_player->state = NETPLAYER_STATE_DEBRIEF_REPLAY;
9056 send_netplayer_update_packet();
9059 // call this when the server has left and we would otherwise be saying "contact lost with server
9060 void multi_debrief_server_left()
9063 Multi_debrief_server_left = 1;
9065 // undo any "accept" hit so that clients can hit accept again to leave
9066 Multi_debrief_accept_hit = 0;
9069 void multi_debrief_stats_accept()
9071 // don't do anything if we've already accepted
9072 if(Multi_debrief_stats_accept_code != -1){
9076 Multi_debrief_stats_accept_code = 1;
9078 // if we're the host, and we're on a standalone, tell the standalone to begin the stats storing process
9079 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) || (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
9080 // send a packet to the players telling them to store their stats
9081 send_store_stats_packet(1);
9084 // add a chat line saying "stats have been accepted"
9085 multi_display_chat_msg(XSTR("<stats have been accepted>",850),0,0);
9088 ml_string(NOX("Stats stored"));
9091 void multi_debrief_stats_toss()
9093 // don't do anything if we've already accepted
9094 if(Multi_debrief_stats_accept_code != -1){
9098 Multi_debrief_stats_accept_code = 0;
9100 // if we're the host, and we're on a standalone, tell everyone to "toss" stats
9101 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) || (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
9102 // send a packet to the players telling them to store their stats
9103 send_store_stats_packet(0);
9106 // add a chat line saying "stats have been accepted"
9107 multi_display_chat_msg(XSTR("<stats have been tossed>",851),0,0);
9110 ml_string(NOX("Stats tossed"));
9113 int multi_debrief_stats_accept_code()
9115 return Multi_debrief_stats_accept_code;
9118 void multi_debrief_server_process()
9121 int player_status,other_status;
9123 Multi_debrief_server_framecount++;
9125 // if we're > 10 seconds into the debrief and not everyone is here, try warping everyone out again
9126 if((Multi_debrief_time >= Multi_debrief_resend_time) && !multi_netplayer_state_check3(NETPLAYER_STATE_DEBRIEF, NETPLAYER_STATE_DEBRIEF_ACCEPT, NETPLAYER_STATE_DEBRIEF_REPLAY, 1)){
9127 // find all players who are not in the debrief state and hit them with the endgame packet
9128 for(idx=0; idx<MAX_PLAYERS; idx++){
9129 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)) ){
9130 send_endgame_packet(&Net_players[idx]);
9135 Multi_debrief_resend_time += 7.0f;
9138 // evaluate the status of all players in the game (0 == not ready, 1 == ready to continue, 2 == ready to replay)
9141 // check all players
9142 for(idx=0;idx<MAX_PLAYERS;idx++){
9143 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_HOST(Net_players[idx])){
9144 if(Net_players[idx].state != NETPLAYER_STATE_DEBRIEF_ACCEPT){
9151 // if we haven't already reported TvT results
9152 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)){
9153 multi_team_report();
9154 Multi_debrief_reported_tvt = 1;
9157 // if all other players are good to go, check the host
9159 // if he is ready to continue
9160 if(Netgame.host->state == NETPLAYER_STATE_DEBRIEF_ACCEPT){
9163 // if he wants to replay the mission
9164 else if(Netgame.host->state == NETPLAYER_STATE_DEBRIEF_REPLAY){
9167 // if he is not ready
9172 // if all players are _not_ good to go
9177 // if we're in the debriefing state in a campaign mode, process accordingly
9178 if(Netgame.campaign_mode == MP_CAMPAIGN){
9179 multi_campaign_do_debrief(player_status);
9181 // otherwise process as normal (looking for all players to be ready to go to the next mission
9183 if(player_status == 1){
9184 multi_flush_mission_stuff();
9186 // set the netgame state to be forming and continue
9187 Netgame.game_state = NETGAME_STATE_FORMING;
9188 send_netgame_update_packet();
9190 // move to the proper state
9191 if(Game_mode & GM_STANDALONE_SERVER){
9192 gameseq_post_event(GS_EVENT_STANDALONE_MAIN);
9194 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
9197 multi_reset_timestamps();
9198 } else if(player_status == 2){
9199 multi_flush_mission_stuff();
9201 // tell everyone to move into the pre-briefing sync state
9202 Netgame.game_state = NETGAME_STATE_MISSION_SYNC;
9203 send_netgame_update_packet();
9205 // move back to the mission sync screen for the same mission again
9206 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
9207 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
9209 multi_reset_timestamps();
9215 // -------------------------------------------------------------------------------------------------------------
9217 // MULTIPLAYER PASSWORD POPUP
9222 static char *Multi_pwd_bitmap_fname[GR_NUM_RESOLUTIONS] = {
9223 "Password", // GR_640
9224 "2_Password" // GR_1024
9227 static char *Multi_pwd_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
9228 "Password-M", // GR_640
9229 "2_Password-M" // GR_1024
9234 // constants for coordinate lookup
9235 #define MPWD_X_COORD 0
9236 #define MPWD_Y_COORD 1
9237 #define MPWD_W_COORD 2
9238 #define MPWD_H_COORD 3
9241 #define MULTI_PWD_NUM_BUTTONS 2
9242 #define MPWD_CANCEL 0
9243 #define MPWD_COMMIT 1
9245 // password area defs
9246 int Mpwd_coords[GR_NUM_RESOLUTIONS][4] = {
9255 UI_WINDOW Multi_pwd_window; // the window object for the join screen
9256 UI_INPUTBOX Multi_pwd_passwd; // for Netgame.passwd
9257 int Multi_pwd_bitmap; // the background bitmap
9258 int Multi_passwd_background = -1;
9259 int Multi_passwd_done = -1;
9260 int Multi_passwd_running = 0;
9263 ui_button_info Multi_pwd_buttons[GR_NUM_RESOLUTIONS][MULTI_PWD_NUM_BUTTONS] = {
9265 ui_button_info("PWB_00", 411, 151, 405, 141, 0),
9266 ui_button_info("PWB_01", 460, 151, 465, 141, 1),
9269 ui_button_info("2_PWB_00", 659, 242, 649, 225, 0),
9270 ui_button_info("2_PWB_01", 737, 242, 736, 225, 1),
9275 #define MULTI_PWD_NUM_TEXT 3
9276 UI_XSTR Multi_pwd_text[GR_NUM_RESOLUTIONS][MULTI_PWD_NUM_TEXT] = {
9278 { "Cancel", 387, 400, 141, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[0][MPWD_CANCEL].button},
9279 { "Commit", 1062, 455, 141, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[0][MPWD_COMMIT].button},
9280 { "Enter Password", 1332, 149, 92, UI_XSTR_COLOR_GREEN, -1, NULL},
9283 { "Cancel", 387, 649, 225, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[1][MPWD_CANCEL].button},
9284 { "Commit", 1062, 736, 225, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[1][MPWD_COMMIT].button},
9285 { "Enter Password", 1332, 239, 148, UI_XSTR_COLOR_GREEN, -1, NULL},
9289 // initialize all graphics, etc
9290 void multi_passwd_init()
9294 // store the background as it currently is
9295 Multi_passwd_background = gr_save_screen();
9297 // create the interface window
9298 Multi_pwd_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
9299 Multi_pwd_window.set_mask_bmap(Multi_pwd_bitmap_mask_fname[gr_screen.res]);
9301 // load the background bitmap
9302 Multi_pwd_bitmap = bm_load(Multi_pwd_bitmap_fname[gr_screen.res]);
9303 if(Multi_pwd_bitmap < 0){
9304 // we failed to load the bitmap - this is very bad
9308 // create the interface buttons
9309 for(idx=0; idx<MULTI_PWD_NUM_BUTTONS; idx++){
9310 // create the object
9311 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);
9313 // set the sound to play when highlighted
9314 Multi_pwd_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
9316 // set the ani for the button
9317 Multi_pwd_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pwd_buttons[gr_screen.res][idx].filename);
9320 Multi_pwd_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pwd_buttons[gr_screen.res][idx].hotspot);
9324 for(idx=0; idx<MULTI_PWD_NUM_TEXT; idx++){
9325 Multi_pwd_window.add_XSTR(&Multi_pwd_text[gr_screen.res][idx]);
9328 // create the password input box
9329 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);
9330 Multi_pwd_passwd.set_focus();
9332 // link the enter key to ACCEPT
9333 Multi_pwd_buttons[gr_screen.res][MPWD_COMMIT].button.set_hotkey(KEY_ENTER);
9335 Multi_passwd_done = -1;
9336 Multi_passwd_running = 1;
9339 // close down all graphics, etc
9340 void multi_passwd_close()
9342 // unload any bitmaps
9343 bm_release(Multi_pwd_bitmap);
9345 // destroy the UI_WINDOW
9346 Multi_pwd_window.destroy();
9348 // free up the saved background screen
9349 if(Multi_passwd_background >= 0){
9350 gr_free_screen(Multi_passwd_background);
9351 Multi_passwd_background = -1;
9354 Multi_passwd_running = 0;
9357 // process any button pressed
9358 void multi_passwd_process_buttons()
9360 // if the accept button was pressed
9361 if(Multi_pwd_buttons[gr_screen.res][MPWD_COMMIT].button.pressed()){
9362 gamesnd_play_iface(SND_USER_SELECT);
9363 Multi_passwd_done = 1;
9366 // if the cancel button was pressed
9367 if(Multi_pwd_buttons[gr_screen.res][MPWD_CANCEL].button.pressed()){
9368 gamesnd_play_iface(SND_USER_SELECT);
9369 Multi_passwd_done = 0;
9373 // run the passwd popup
9374 void multi_passwd_do(char *passwd)
9378 while(Multi_passwd_done == -1){
9379 // set frametime and run background stuff
9380 game_set_frametime(-1);
9381 game_do_state_common(gameseq_get_state());
9383 k = Multi_pwd_window.process();
9385 // process any keypresses
9388 // set this to indicate the user has cancelled for one reason or another
9389 Multi_passwd_done = 0;
9393 // if the input box text has changed
9394 if(Multi_pwd_passwd.changed()){
9396 Multi_pwd_passwd.get_text(passwd);
9399 // process any button pressed
9400 multi_passwd_process_buttons();
9402 // draw the background, etc
9405 if(Multi_passwd_background >= 0){
9406 gr_restore_screen(Multi_passwd_background);
9408 gr_set_bitmap(Multi_pwd_bitmap);
9410 Multi_pwd_window.draw();
9417 // bring up the password string popup, fill in passwd (return 1 if accept was pressed, 0 if cancel was pressed)
9418 int multi_passwd_popup(char *passwd)
9420 // if the popup is already running for some reason, don't do anything
9421 if(Multi_passwd_running){
9425 // initialize all graphics
9426 multi_passwd_init();
9429 multi_passwd_do(passwd);
9431 // shut everything down
9432 multi_passwd_close();
9434 return Multi_passwd_done;