2 * $Logfile: /Freespace2/code/Network/MultiUI.cpp $
7 * C file for all the UI controls of the mulitiplayer screens
10 * Revision 1.3 2002/05/26 20:49:54 theoddone33
13 * Revision 1.2 2002/05/07 03:16:47 theoddone33
14 * The Great Newline Fix
16 * Revision 1.1.1.1 2002/05/03 03:28:10 root
20 * 94 6/16/00 3:16p Jefff
21 * sim of the year dvd version changes, a few german soty localization
24 * 93 10/14/99 2:51p Jefff
27 * 92 10/13/99 3:50p Jefff
28 * fixed unnumbered XSTRs
30 * 91 9/15/99 1:45a Dave
31 * Don't init joystick on standalone. Fixed campaign mode on standalone.
32 * Fixed no-score-report problem in TvT
34 * 90 9/14/99 12:51a Jefff
37 * 89 9/13/99 4:52p Dave
40 * 88 9/13/99 11:30a Dave
41 * Added checkboxes and functionality for disabling PXO banners as well as
42 * disabling d3d zbuffer biasing.
44 * 87 9/12/99 10:06p Jefff
45 * changed instances of "Squad War" to "SquadWar"
47 * 86 9/03/99 1:32a Dave
48 * CD checking by act. Added support to play 2 cutscenes in a row
49 * seamlessly. Fixed super low level cfile bug related to files in the
50 * root directory of a CD. Added cheat code to set campaign mission # in
53 * 85 9/01/99 10:49p Dave
54 * Added nice SquadWar checkbox to the client join wait screen.
56 * 84 8/30/99 2:49p Jefff
58 * 83 8/26/99 8:49p Jefff
59 * Updated medals screen and about everything that ever touches medals in
60 * one way or another. Sheesh.
62 * 82 8/25/99 4:38p Dave
63 * Updated PXO stuff. Make squad war report stuff much more nicely.
65 * 81 8/20/99 2:09p Dave
68 * 80 8/20/99 10:06a Jefff
69 * removed closed/rstricted buttons from multi start screen
71 * 79 8/18/99 11:30a Jefff
73 * 78 8/18/99 10:38a Jefff
75 * 77 8/16/99 4:06p Dave
76 * Big honking checkin.
78 * 76 8/16/99 1:08p Jefff
79 * added sounds to a few controls, made input boxes lose focus on ENTER
81 * 75 8/16/99 9:52a Jefff
82 * fixed bitmap loading on buttons in multi-sync screen
84 * 74 8/11/99 5:54p Dave
85 * Fixed collision problem. Fixed standalone ghost problem.
87 * 73 8/10/99 4:35p Jefff
90 * 72 8/06/99 12:29a Dave
93 * 71 8/05/99 3:13p Jasenw
94 * tweaked some text placement coords.
96 * 70 8/04/99 1:38p Jefff
97 * moved some text in multi join wait
99 * 69 8/03/99 12:45p Dave
102 * 68 7/25/99 5:17p Jefff
103 * campaign descriptions show up on multicreate screen
105 * 67 7/20/99 1:49p Dave
106 * Peter Drake build. Fixed some release build warnings.
108 * 66 7/19/99 2:13p Dave
109 * Added some new strings for Heiko.
111 * 65 7/15/99 9:20a Andsager
112 * FS2_DEMO initial checkin
114 * 64 7/08/99 10:53a Dave
115 * New multiplayer interpolation scheme. Not 100% done yet, but still
116 * better than the old way.
118 * 63 6/30/99 10:49a Jasenw
119 * Fixed coords for new launch countdown ani
121 * 62 6/29/99 7:39p Dave
122 * Lots of small bug fixes.
124 * 61 6/25/99 11:59a Dave
125 * Multi options screen.
127 * 60 6/09/99 2:17p Dave
128 * Fixed up pleasewait bitmap rendering.
130 * 59 6/05/99 3:42p Dave
131 * New multi sync screen.
133 * 58 6/01/99 6:07p Dave
134 * New loading/pause/please wait bar.
136 * 57 5/21/99 6:45p Dave
137 * Sped up ui loading a bit. Sped up localization disk access stuff. Multi
138 * start game screen, multi password, and multi pxo-help screen.
140 * 56 5/06/99 11:10a Dave
141 * Fixed coord on multi create screen.
143 * 55 5/04/99 6:38p Dave
144 * Finished multi join-wait screen.
146 * 54 5/04/99 5:20p Dave
147 * Fixed up multiplayer join screen and host options screen. Should both
150 * 53 5/03/99 11:04p Dave
151 * Most of the way done with the multi join screen.
153 * 52 5/03/99 8:32p Dave
154 * New version of multi host options screen.
156 * 51 4/29/99 2:15p Neilk
157 * slider2 code got modified; changed parameters for create
159 * 50 4/25/99 3:02p Dave
160 * Build defines for the E3 build.
162 * 49 4/21/99 6:15p Dave
163 * Did some serious housecleaning in the beam code. Made it ready to go
164 * for anti-fighter "pulse" weapons. Fixed collision pair creation. Added
165 * a handy macro for recalculating collision pairs for a given object.
167 * 48 4/16/99 5:27p Neilk
168 * added slider support and hir res for multi_create
170 * 47 4/14/99 6:37p Dave
171 * Fixed scroll button bug on host create screen.
173 * 46 4/14/99 5:28p Dave
176 * 45 4/12/99 10:07p Dave
177 * Made network startup more forgiving. Added checkmarks to dogfight
178 * screen for players who hit commit.
180 * 44 4/09/99 2:21p Dave
181 * Multiplayer beta stuff. CD checking.
183 * 43 4/08/99 1:28p Dave
184 * Small bug fixes for refresh button on the multi create screen.
186 * 42 4/08/99 11:55a Neilk
187 * Converted Multi_Create to new artwork (just lowres)
189 * 41 4/08/99 2:10a Dave
190 * Numerous bug fixes for the beta. Added builtin mission info for the
193 * 40 3/20/99 3:48p Andsager
194 * Do mission_loop stuff for PXO
196 * 39 3/10/99 6:50p Dave
197 * Changed the way we buffer packets for all clients. Optimized turret
198 * fired packets. Did some weapon firing optimizations.
200 * 38 3/09/99 6:24p Dave
201 * More work on object update revamping. Identified several sources of
202 * unnecessary bandwidth.
204 * 37 3/08/99 7:03p Dave
205 * First run of new object update system. Looks very promising.
207 * 36 2/25/99 4:19p Dave
208 * Added multiplayer_beta defines. Added cd_check define. Fixed a few
209 * release build warnings. Added more data to the squad war request and
212 * 35 2/24/99 3:26p Anoop
213 * Make sure the host is the only guy who bashes skill level for TvT.
215 * 34 2/24/99 2:25p Dave
216 * Fixed up chatbox bugs. Made squad war reporting better. Fixed a respawn
217 * bug for dogfight more.
219 * 33 2/23/99 2:29p Dave
220 * First run of oldschool dogfight mode.
222 * 32 2/17/99 2:11p Dave
223 * First full run of squad war. All freespace and tracker side stuff
226 * 31 2/12/99 6:16p Dave
227 * Pre-mission Squad War code is 95% done.
229 * 30 2/11/99 3:08p Dave
230 * PXO refresh button. Very preliminary squad war support.
232 * 29 2/08/99 5:07p Dave
233 * FS2 chat server support. FS2 specific validated missions.
235 * 28 2/04/99 6:29p Dave
236 * First full working rev of FS2 PXO support. Fixed Glide lighting
239 * 27 1/30/99 5:08p Dave
240 * More new hi-res stuff.Support for nice D3D textures.
242 * 26 1/29/99 2:08a Dave
243 * Fixed beam weapon collisions with players. Reduced size of scoring
244 * struct for multiplayer. Disabled PXO.
246 * 25 1/15/99 2:36p Neilk
247 * fixed multi_jw coordinates
249 * 24 1/13/99 7:19p Neilk
250 * Converted Mission Brief, Barracks, Synch to high res support
252 * 23 1/12/99 7:17p Neilk
254 * 22 1/12/99 5:45p Dave
255 * Moved weapon pipeline in multiplayer to almost exclusively client side.
256 * Very good results. Bandwidth goes down, playability goes up for crappy
257 * connections. Fixed object update problem for ship subsystems.
259 * 21 1/12/99 4:07a Dave
260 * Put in barracks code support for selecting squad logos. Properly
261 * distribute squad logos in a multiplayer game.
263 * 20 1/11/99 7:19p Neilk
264 * Converted multi_join interface to support multiple resolutions
266 * 19 12/18/98 1:13a Dave
267 * Rough 1024x768 support for Direct3D. Proper detection and usage through
270 * 18 12/17/98 4:50p Andsager
271 * Added debrief_assemble_optional_mission_popup_text() for single and
274 * 17 12/14/98 12:13p Dave
275 * Spiffed up xfer system a bit. Put in support for squad logo file xfer.
278 * 16 12/10/98 10:19a Andsager
279 * Fix mission loop assert
281 * 15 12/10/98 9:59a Andsager
282 * Fix some bugs with mission loops
284 * 14 12/09/98 1:56p Andsager
285 * Initial checkin of mission loop
287 * 13 12/03/98 5:22p Dave
288 * Ported over Freespace 1 multiplayer ships.tbl and weapons.tbl
291 * 12 11/30/98 1:07p Dave
292 * 16 bit conversion, first run.
294 * 11 11/20/98 11:16a Dave
295 * Fixed up IPX support a bit. Making sure that switching modes and
296 * loading/saving pilot files maintains proper state.
298 * 10 11/19/98 4:57p Dave
299 * Ignore PXO option if IPX is selected.
301 * 9 11/19/98 4:19p Dave
302 * Put IPX sockets back in psnet. Consolidated all multiplayer config
305 * 8 11/19/98 8:04a Dave
306 * Full support for D3-style reliable sockets. Revamped packet lag/loss
307 * system, made it receiver side and at the lowest possible level.
309 * 7 11/17/98 11:12a Dave
310 * Removed player identification by address. Now assign explicit id #'s.
312 * 6 10/19/98 11:15a Dave
313 * Changed requirements for stats storing in PXO mode.
315 * 5 10/16/98 9:40a Andsager
316 * Remove ".h" files from model.h
318 * 4 10/13/98 9:29a Dave
319 * Started neatening up freespace.h. Many variables renamed and
320 * reorganized. Added AlphaColors.[h,cpp]
322 * 3 10/07/98 6:27p Dave
323 * Globalized mission and campaign file extensions. Removed Silent Threat
324 * special code. Moved \cache \players and \multidata into the \data
327 * 2 10/07/98 10:53a Dave
330 * 1 10/07/98 10:50a Dave
332 * 333 10/02/98 3:22p Allender
333 * fix up the -connect option and fix the -port option
335 * 332 9/17/98 9:26p Dave
336 * Externalized new string.
338 * 331 9/17/98 3:08p Dave
339 * PXO to non-pxo game warning popup. Player icon stuff in create and join
340 * game screens. Upped server count refresh time in PXO to 35 secs (from
343 * 330 9/17/98 9:43a Allender
344 * removed an Assert that Dave called bogus.
346 * 329 9/16/98 6:54p Dave
347 * Upped max sexpression nodes to 1800 (from 1600). Changed FRED to sort
348 * the ship list box. Added code so that tracker stats are not stored with
351 * 328 9/15/98 7:24p Dave
352 * Minor UI changes. Localized bunch of new text.
354 * 327 9/15/98 4:03p Dave
355 * Changed readyroom and multi screens to display "st" icon for all
356 * missions with mission disk content (not necessarily just those that
357 * come with Silent Threat).
359 * 326 9/15/98 11:44a Dave
360 * Renamed builtin ships and wepaons appropriately in FRED. Put in scoring
361 * scale factors. Fixed standalone filtering of MD missions to non-MD
364 * 325 9/13/98 9:36p Dave
365 * Support for new info icons for multiplayer missions (from-volition,
366 * valid, mission disk, etc).
368 * 324 9/11/98 4:14p Dave
369 * Fixed file checksumming of < file_size. Put in more verbose kicking and
370 * PXO stats store reporting.
372 * 323 9/10/98 1:17p Dave
373 * Put in code to flag missions and campaigns as being MD or not in Fred
374 * and Freespace. Put in multiplayer support for filtering out MD
375 * missions. Put in multiplayer popups for warning of non-valid missions.
377 * 322 9/04/98 3:51p Dave
378 * Put in validated mission updating and application during stats
381 * 321 8/31/98 2:06p Dave
382 * Make cfile sort the ordering or vp files. Added support/checks for
383 * recognizing "mission disk" players.
385 * 320 8/21/98 1:15p Dave
386 * Put in log system hooks in useful places.
388 * 319 8/20/98 5:31p Dave
389 * Put in handy multiplayer logfile system. Now need to put in useful
390 * applications of it all over the code.
392 * 318 8/12/98 4:53p Dave
393 * Put in 32 bit checksumming for PXO missions. No validation on the
394 * actual tracker yet, though.
396 * 317 8/07/98 10:40a Allender
397 * new command line flags for starting netgames. Only starting currently
398 * works, and PXO isn't implemented yet
400 * 316 7/24/98 9:27a Dave
401 * Tidied up endgame sequencing by removing several old flags and
402 * standardizing _all_ endgame stuff with a single function call.
404 * 315 7/14/98 10:04a Allender
405 * fixed the countdown code to not be reliant on timer_get_fixed_seconds
407 * 314 7/10/98 5:04p Dave
408 * Fix connection speed bug on standalone server.
410 * 313 7/09/98 6:01p Dave
411 * Firsts full version of PXO updater. Put in stub for displaying
414 * 312 7/07/98 2:49p Dave
417 * 311 6/30/98 2:17p Dave
418 * Revised object update system. Removed updates for all weapons. Put
419 * button info back into control info packet.
421 * 310 6/13/98 9:32p Mike
422 * Kill last character in file which caused "Find in Files" to report the
423 * file as "not a text file."
430 #include <winsock.h> // for inet_addr()
432 #include <sys/types.h>
433 #include <sys/socket.h>
434 #include <netinet/in.h>
435 #include <arpa/inet.h>
440 #include "multiutil.h"
441 #include "multimsgs.h"
447 #include "gamesequence.h"
448 #include "freespace.h"
449 #include "contexthelp.h"
454 #include "missionshipchoice.h"
455 #include "multi_xfer.h"
457 #include "stand_gui.h"
458 #include "linklist.h"
459 #include "multiteamselect.h"
460 #include "missioncampaign.h"
467 #include "missiondebrief.h"
468 #include "multi_ingame.h"
469 #include "multi_kick.h"
470 #include "multi_data.h"
471 #include "multi_campaign.h"
472 #include "multi_team.h"
473 #include "multi_pinfo.h"
474 #include "multi_observer.h"
475 #include "multi_voice.h"
476 #include "multi_endgame.h"
477 #include "managepilot.h"
480 #include "objcollide.h"
482 #include "multi_pmsg.h"
483 #include "multi_obj.h"
484 #include "multi_log.h"
485 #include "alphacolors.h"
486 #include "animplay.h"
487 #include "multi_dogfight.h"
488 #include "missionpause.h"
490 // -------------------------------------------------------------------------------------------------------------
492 // MULTIPLAYER COMMON interface controls
495 // the common text info box stuff. This is lifted almost directly from Alans briefing code (minus the spiffy colored, scrolling
497 int Multi_common_text_coords[GR_NUM_RESOLUTIONS][4] = {
506 int Multi_common_text_max_display[GR_NUM_RESOLUTIONS] = {
511 #define MULTI_COMMON_TEXT_META_CHAR '$'
512 #define MULTI_COMMON_TEXT_MAX_LINE_LENGTH 100
513 #define MULTI_COMMON_TEXT_MAX_LINES 20
514 #define MULTI_COMMON_MAX_TEXT (MULTI_COMMON_TEXT_MAX_LINES * MULTI_COMMON_TEXT_MAX_LINE_LENGTH)
516 char Multi_common_all_text[MULTI_COMMON_MAX_TEXT];
517 char Multi_common_text[MULTI_COMMON_TEXT_MAX_LINES][MULTI_COMMON_TEXT_MAX_LINE_LENGTH];
519 int Multi_common_top_text_line = -1; // where to start displaying from
520 int Multi_common_num_text_lines = 0; // how many lines we have
522 void multi_common_scroll_text_up();
523 void multi_common_scroll_text_down();
524 void multi_common_move_to_bottom();
525 void multi_common_render_text();
526 void multi_common_split_text();
528 #define MAX_IP_STRING 255 // maximum length for ip string
530 void multi_common_scroll_text_up()
532 Multi_common_top_text_line--;
533 if ( Multi_common_top_text_line < 0 ) {
534 Multi_common_top_text_line = 0;
535 if ( !mouse_down(MOUSE_LEFT_BUTTON) )
536 gamesnd_play_iface(SND_GENERAL_FAIL);
539 gamesnd_play_iface(SND_SCROLL);
543 void multi_common_scroll_text_down()
545 Multi_common_top_text_line++;
546 if ( (Multi_common_num_text_lines - Multi_common_top_text_line) < Multi_common_text_max_display[gr_screen.res] ) {
547 Multi_common_top_text_line--;
548 if ( !mouse_down(MOUSE_LEFT_BUTTON) ){
549 gamesnd_play_iface(SND_GENERAL_FAIL);
552 gamesnd_play_iface(SND_SCROLL);
556 void multi_common_move_to_bottom()
558 // if there's nowhere to scroll down, do nothing
559 if(Multi_common_num_text_lines <= Multi_common_text_max_display[gr_screen.res]){
563 Multi_common_top_text_line = Multi_common_num_text_lines - Multi_common_text_max_display[gr_screen.res];
566 void multi_common_set_text(char *str,int auto_scroll)
569 // store the entire string as well
570 if(strlen(str) > MULTI_COMMON_MAX_TEXT){
573 strcpy(Multi_common_all_text,str);
576 // split the whole thing up
577 multi_common_split_text();
579 // scroll to the bottom if we're supposed to
581 multi_common_move_to_bottom();
585 void multi_common_add_text(char *str,int auto_scroll)
588 // store the entire string as well
589 if((strlen(str) + strlen(Multi_common_all_text)) > MULTI_COMMON_MAX_TEXT){
592 strcat(Multi_common_all_text,str);
595 // split the whole thing up
596 multi_common_split_text();
598 // scroll to the bottom if we're supposed to
600 multi_common_move_to_bottom();
604 void multi_common_split_text()
607 int n_chars[MAX_BRIEF_LINES];
608 char *p_str[MAX_BRIEF_LINES];
610 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);
611 Assert(n_lines != -1);
613 for ( i = 0; i < n_lines; i++ ) {
614 Assert(n_chars[i] < MULTI_COMMON_TEXT_MAX_LINE_LENGTH);
615 strncpy(Multi_common_text[i], p_str[i], n_chars[i]);
616 Multi_common_text[i][n_chars[i]] = 0;
617 drop_leading_white_space(Multi_common_text[i]);
620 Multi_common_top_text_line = 0;
621 Multi_common_num_text_lines = n_lines;
624 void multi_common_render_text()
626 int i, fh, line_count;
628 fh = gr_get_font_height();
631 gr_set_color_fast(&Color_text_normal);
632 for ( i = Multi_common_top_text_line; i < Multi_common_num_text_lines; i++ ) {
633 if ( line_count >= Multi_common_text_max_display[gr_screen.res] ){
636 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]);
640 if ( (Multi_common_num_text_lines - Multi_common_top_text_line) > Multi_common_text_max_display[gr_screen.res] ) {
641 gr_set_color_fast(&Color_bright_red);
642 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));
646 // common notification messaging stuff
647 #define MULTI_COMMON_NOTIFY_TIME 3500
648 int Multi_common_join_y[GR_NUM_RESOLUTIONS] = {
652 int Multi_common_create_y[GR_NUM_RESOLUTIONS] = {
657 int Multi_common_jw_y[GR_NUM_RESOLUTIONS] = {
662 int Multi_common_msg_y[GR_NUM_RESOLUTIONS] = {
667 char Multi_common_notify_text[200];
668 int Multi_common_notify_stamp;
670 void multi_common_notify_init()
672 strcpy(Multi_common_notify_text,"");
673 Multi_common_notify_stamp = -1;
676 // add a notification string, drawing appropriately depending on the state/screen we're in
677 void multi_common_add_notify(char *str)
680 strcpy(Multi_common_notify_text,str);
681 Multi_common_notify_stamp = timestamp(MULTI_COMMON_NOTIFY_TIME);
685 // process/display notification messages
686 void multi_common_notify_do()
688 if(Multi_common_notify_stamp != -1){
689 if(timestamp_elapsed(Multi_common_notify_stamp)){
690 Multi_common_notify_stamp = -1;
693 gr_get_string_size(&w,&h,Multi_common_notify_text);
694 gr_set_color_fast(&Color_white);
696 // determine where it should be placed based upon which screen we're on
698 switch(gameseq_get_state()){
699 case GS_STATE_MULTI_JOIN_GAME :
700 y = Multi_common_join_y[gr_screen.res];
702 case GS_STATE_MULTI_HOST_SETUP :
703 y = Multi_common_create_y[gr_screen.res];
705 case GS_STATE_MULTI_CLIENT_SETUP :
706 y = Multi_common_jw_y[gr_screen.res];
708 case GS_STATE_MULTI_START_GAME :
709 y = Multi_common_msg_y[gr_screen.res];
713 gr_string((gr_screen.max_w - w)/2, y, Multi_common_notify_text);
720 int Multi_common_icons[MULTI_NUM_COMMON_ICONS];
722 char *Multi_common_icon_names[MULTI_NUM_COMMON_ICONS] = {
723 "DotRed", // voice denied
724 "DotGreen", // voice recording
725 "OvalGreen", // team 0
726 "OvalGreen01", // team 0 select
728 "OvalRed01", // team 1 select
729 "mp_coop", // coop mission
730 "mp_teams", // TvT mission
731 "mp_furball", // furball mission
732 "icon-volition", // volition mission
733 "icon-valid", // mission is valid
737 // width and height of the icons
738 int Multi_common_icon_dims[MULTI_NUM_COMMON_ICONS][2] = {
739 {11, 11}, // voice denied
740 {11, 11}, // voice recording
742 {11, 11}, // team 0 select
744 {11, 11}, // team 1 select
747 {18, 11}, // mp furball
748 {9, 9}, // volition mission
749 {8, 8}, // mission is valid
753 void multi_load_common_icons()
758 for(idx=0; idx<MULTI_NUM_COMMON_ICONS; idx++){
759 Multi_common_icons[idx] = -1;
760 Multi_common_icons[idx] = bm_load(Multi_common_icon_names[idx]);
764 void multi_unload_common_icons()
769 for(idx=0; idx<MULTI_NUM_COMMON_ICONS; idx++){
770 if(Multi_common_icons[idx] != -1){
771 bm_unload(Multi_common_icons[idx]);
772 Multi_common_icons[idx] = -1;
777 // display any relevant voice status icons
778 void multi_common_voice_display_status()
780 switch(multi_voice_status()){
781 // i have been denied the voice token
782 case MULTI_VOICE_STATUS_DENIED:
783 if(Multi_common_icons[MICON_VOICE_DENIED] != -1){
784 gr_set_bitmap(Multi_common_icons[MICON_VOICE_DENIED]);
789 // i am currently recording
790 case MULTI_VOICE_STATUS_RECORDING:
791 if(Multi_common_icons[MICON_VOICE_RECORDING] != -1){
792 gr_set_bitmap(Multi_common_icons[MICON_VOICE_RECORDING]);
797 // i am currently playing back sound
798 case MULTI_VOICE_STATUS_PLAYING:
801 // the system is currently idle
802 case MULTI_VOICE_STATUS_IDLE:
808 // palette initialization stuff
809 #define MULTI_COMMON_PALETTE_FNAME "InterfacePalette"
812 int Multi_common_interface_palette = -1;
814 void multi_common_load_palette();
815 void multi_common_set_palette();
816 void multi_common_unload_palette();
818 // load in the palette if it doesn't already exist
819 void multi_common_load_palette()
821 if(Multi_common_interface_palette != -1){
825 Multi_common_interface_palette = bm_load(MULTI_COMMON_PALETTE_FNAME);
826 if(Multi_common_interface_palette == -1){
827 nprintf(("Network","Error loading multiplayer common palette!\n"));
831 // set the common palette to be the active one
832 void multi_common_set_palette()
834 // if the palette is not loaded yet, do so now
835 if(Multi_common_interface_palette == -1){
836 multi_common_load_palette();
839 if(Multi_common_interface_palette != -1){
840 #ifndef HARDWARE_ONLY
841 palette_use_bm_palette(Multi_common_interface_palette);
846 // unload the bitmap palette
847 void multi_common_unload_palette()
849 if(Multi_common_interface_palette != -1){
850 bm_unload(Multi_common_interface_palette);
851 Multi_common_interface_palette = -1;
855 void multi_common_verify_cd()
858 // otherwise, call the freespace function to determine if we have a cd
860 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) ){
869 // -------------------------------------------------------------------------------------------------------------
871 // MULTIPLAYER JOIN SCREEN
874 #define MULTI_JOIN_NUM_BUTTONS 11
878 #define MULTI_JOIN_PALETTE "InterfacePalette"
880 static char *Multi_join_bitmap_fname[GR_NUM_RESOLUTIONS] = {
881 "MultiJoin", // GR_640
882 "2_MultiJoin" // GR_1024
885 static char *Multi_join_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
886 "MultiJoin-M", // GR_640
887 "2_MultiJoin-M" // GR_1024
892 char *Mj_slider_name[GR_NUM_RESOLUTIONS] = {
896 int Mj_slider_coords[GR_NUM_RESOLUTIONS][4] = {
906 #define MJ_SCROLL_UP 0
907 #define MJ_SCROLL_DOWN 1
909 #define MJ_SCROLL_INFO_UP 3
910 #define MJ_SCROLL_INFO_DOWN 4
911 #define MJ_JOIN_OBSERVER 5
912 #define MJ_START_GAME 6
918 // uses MULTI_JOIN_REFRESH_TIME as its timestamp
919 int Multi_join_glr_stamp;
921 #define MULTI_JOIN_PING_TIME 15000 // how often we ping all the known servers
922 int Multi_join_ping_stamp;
923 UI_WINDOW Multi_join_window; // the window object for the join screen
924 UI_BUTTON Multi_join_select_button; // for selecting list items
925 UI_SLIDER2 Multi_join_slider; // handy dandy slider
926 int Multi_join_bitmap; // the background bitmap
928 ui_button_info Multi_join_buttons[GR_NUM_RESOLUTIONS][MULTI_JOIN_NUM_BUTTONS] = {
930 ui_button_info( "MJ_00", 1, 57, -1, -1, 0 ), // scroll up
931 ui_button_info( "MJ_02", 1, 297, -1, -1, 2 ), // scroll down
932 ui_button_info( "MJ_03", 10, 338, 65, 364, 3 ), // refresh
933 ui_button_info( "MJ_04", 1, 405, -1, -1, 4 ), // scroll info up
934 ui_button_info( "MJ_05", 1, 446, -1, -1, 5 ), // scroll info down
935 ui_button_info( "MJ_06", 489, 339, -1, -1, 6 ), // join as observer
936 ui_button_info( "MJ_07", 538, 339, -1, -1, 7 ), // create game
937 ui_button_info( "MJ_08", 583, 339, 588, 376, 8 ), // cancel
938 ui_button_info( "MJ_09", 534, 426, -1, -1, 9 ), // help
939 ui_button_info( "MJ_10", 534, 454, -1, -1, 10 ), // options
940 ui_button_info( "MJ_11", 571, 426, 589, 416, 11 ), // join
943 ui_button_info( "2_MJ_00", 2, 92, -1, -1, 0 ), // scroll up
944 ui_button_info( "2_MJ_02", 2, 475, -1, -1, 2 ), // scroll down
945 ui_button_info( "2_MJ_03", 16, 541, 104, 582, 3 ), // refresh
946 ui_button_info( "2_MJ_04", 2, 648, -1, -1, 4 ), // scroll info up
947 ui_button_info( "2_MJ_05", 2, 713, -1, -1, 5 ), // scroll info down
948 ui_button_info( "2_MJ_06", 783, 542, -1, -1, 6 ), // join as observer
949 ui_button_info( "2_MJ_07", 861, 542, -1, -1, 7 ), // create game
950 ui_button_info( "2_MJ_08", 933, 542, 588, 376, 8 ), // cancel
951 ui_button_info( "2_MJ_09", 854, 681, -1, -1, 9 ), // help
952 ui_button_info( "2_MJ_10", 854, 727, -1, -1, 10 ), // options
953 ui_button_info( "2_MJ_11", 914, 681, 937, 668, 11 ), // join
957 #define MULTI_JOIN_NUM_TEXT 13
959 UI_XSTR Multi_join_text[GR_NUM_RESOLUTIONS][MULTI_JOIN_NUM_TEXT] = {
961 {"Refresh", 1299, 65, 364, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_REFRESH].button},
962 {"Join as", 1300, 476, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_JOIN_OBSERVER].button},
963 {"Observer", 1301, 467, 385, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_JOIN_OBSERVER].button},
964 {"Create", 1408, 535, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_START_GAME].button},
965 {"Game", 1302, 541, 385, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_START_GAME].button},
966 {"Cancel", 387, 588, 376, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[0][MJ_CANCEL].button},
967 {"Help", 928, 479, 436, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_HELP].button},
968 {"Options", 1036, 479, 460, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_OPTIONS].button},
969 {"Join", 1303, 589, 416, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[0][MJ_ACCEPT].button},
970 {"Status", 1304, 37, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
971 {"Server", 1305, 116, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
972 {"Players", 1306, 471, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
973 {"Ping", 1307, 555, 37, UI_XSTR_COLOR_GREEN, -1, NULL}
976 {"Refresh", 1299, 104, 582, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_REFRESH].button},
977 {"Join as", 1300, 783, 602, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_JOIN_OBSERVER].button},
978 {"Observer", 1301, 774, 611, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_JOIN_OBSERVER].button},
979 {"Create", 1408, 868, 602, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_START_GAME].button},
980 {"Game", 1302, 872, 611, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_START_GAME].button},
981 {"Cancel", 387, 941, 602, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[1][MJ_CANCEL].button},
982 {"Help", 928, 782, 699, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_HELP].button},
983 {"Options", 1036, 782, 736, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_OPTIONS].button},
984 {"Join", 1303, 937, 668, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[1][MJ_ACCEPT].button},
985 {"Status", 1304, 60, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
986 {"Server", 1305, 186, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
987 {"Players", 1306, 753, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
988 {"Ping", 1307, 888, 60, UI_XSTR_COLOR_GREEN, -1, NULL}
992 // constants for coordinate look ups
998 #define MULTI_JOIN_SENT_WAIT 10000 // wait this long since a join was sent to allow another
999 int Multi_join_sent_stamp;
1001 // game information text areas
1002 int Mj_max_game_items[GR_NUM_RESOLUTIONS] = {
1007 int Mj_list_y[GR_NUM_RESOLUTIONS] = {
1012 int Mj_status_coords[GR_NUM_RESOLUTIONS][4] = {
1021 int Mj_game_icon_coords[GR_NUM_RESOLUTIONS][3] = {
1030 int Mj_speed_coords[GR_NUM_RESOLUTIONS][4] = {
1039 int Mj_game_name_coords[GR_NUM_RESOLUTIONS][4] = {
1048 int Mj_players_coords[GR_NUM_RESOLUTIONS][4] = {
1057 int Mj_ping_coords[GR_NUM_RESOLUTIONS][4] = {
1066 // game speed labels
1067 #define MJ_NUM_SPEED_LABELS 5
1068 char *Multi_join_speed_labels[MJ_NUM_SPEED_LABELS] = {
1075 color *Multi_join_speed_colors[MJ_NUM_SPEED_LABELS] = {
1078 &Color_bright_green,
1079 &Color_bright_green,
1083 int Mj_cd_coords[GR_NUM_RESOLUTIONS] = {
1088 #define IP_CONFIG_FNAME "tcp.cfg" // name of the file which contains known TCP addresses
1090 // extents of the entire boundable game info region
1091 // NOTE : these numbers are completely empirical
1092 #define MJ_PING_GREEN 160
1093 #define MJ_PING_YELLOW 300
1095 int Mj_list_area_coords[GR_NUM_RESOLUTIONS][4] = {
1104 // PXO channel filter
1105 #define MJ_PXO_FILTER_Y 0
1107 // special chars to indicate various status modes for servers
1108 #define MJ_CHAR_STANDALONE "*"
1109 #define MJ_CHAR_CAMPAIGN "c"
1112 // various interface indices
1113 int Multi_join_list_start; // where to start displaying from
1114 active_game *Multi_join_list_start_item; // a pointer to the corresponding active_game
1115 int Multi_join_list_selected; // which item we have selected
1116 active_game *Multi_join_selected_item; // a pointer to the corresponding active_game
1118 // use this macro to modify the list start
1119 #define MJ_LIST_START_INC() do { Multi_join_list_start++; } while(0);
1120 #define MJ_LIST_START_DEC() do { Multi_join_list_start--; } while(0);
1121 #define MJ_LIST_START_SET(vl) do { Multi_join_list_start = vl; } while(0);
1123 // if we should be sending a join request at the end of the frame
1124 int Multi_join_should_send = -1;
1126 // master tracker details
1127 int Multi_join_frame_count; // keep a count of frames displayed
1128 int Multi_join_mt_tried_verify; // already tried verifying the pilot with the tracker
1130 // data stuff for auto joining a game
1131 #define MULTI_AUTOJOIN_JOIN_STAMP 2000
1132 #define MULTI_AUTOJOIN_QUERY_STAMP 2000
1134 int Multi_did_autojoin;
1135 net_addr Multi_autojoin_addr;
1136 int Multi_autojoin_join_stamp;
1137 int Multi_autojoin_query_stamp;
1140 join_request Multi_join_request;
1142 // LOCAL function definitions
1143 void multi_join_check_buttons();
1144 void multi_join_button_pressed(int n);
1145 void multi_join_display_games();
1146 void multi_join_blit_game_status(active_game *game, int y);
1147 void multi_join_load_tcp_addrs();
1148 void multi_join_do_netstuff();
1149 void multi_join_ping_all();
1150 void multi_join_process_select();
1151 void multi_join_list_scroll_up();
1152 void multi_join_list_scroll_down();
1153 void multi_join_list_page_up();
1154 void multi_join_list_page_down();
1155 active_game *multi_join_get_game(int n);
1156 void multi_join_cull_timeouts();
1157 void multi_join_handle_item_cull(active_game *item, int item_index);
1158 void multi_join_send_join_request(int as_observer);
1159 void multi_join_create_game();
1160 void multi_join_blit_top_stuff();
1161 int multi_join_maybe_warn();
1162 int multi_join_warn_pxo();
1163 void multi_join_blit_protocol();
1167 active_game ag, *newitem;;
1170 dc_get_arg(ARG_INT);
1171 for(idx=0; idx<Dc_arg_int; idx++){
1172 // stuff some fake info
1173 memset(&ag, 0, sizeof(active_game));
1174 sprintf(ag.name, "Game %d", idx);
1175 ag.version = MULTI_FS_SERVER_VERSION;
1176 ag.comp_version = MULTI_FS_SERVER_VERSION;
1177 ag.server_addr.addr[0] = (char)idx;
1178 ag.flags = (AG_FLAG_COOP | AG_FLAG_FORMING | AG_FLAG_STANDALONE);
1181 newitem = multi_update_active_games(&ag);
1183 // timestamp it so we get random timeouts
1184 if(newitem != NULL){
1185 // newitem->heard_from_timer = timestamp((int)frand_range(500.0f, 10000.0f));
1190 void multi_join_notify_new_game()
1192 // reset the # of items
1193 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);
1194 Multi_join_slider.force_currentItem(Multi_join_list_start);
1197 int multi_join_autojoin_do()
1199 // if we have an active game on the list, then return a positive value so that we
1200 // can join the game
1201 if ( Active_game_head && (Active_game_count > 0) ) {
1202 Multi_join_selected_item = Active_game_head;
1206 // send out a server_query again
1207 if ( timestamp_elapsed(Multi_autojoin_query_stamp) ) {
1208 send_server_query(&Multi_autojoin_addr);
1209 Multi_autojoin_query_stamp = timestamp(MULTI_AUTOJOIN_QUERY_STAMP);
1215 void multi_join_game_init()
1219 // do the multiplayer init stuff - multi_level_init() now does all net_player zeroing.
1220 // setup various multiplayer things
1221 Assert( Game_mode & GM_MULTIPLAYER );
1222 Assert( Net_player != NULL );
1224 switch (Multi_options_g.protocol) {
1226 ADDRESS_LENGTH = IPX_ADDRESS_LENGTH;
1227 PORT_LENGTH = IPX_PORT_LENGTH;
1231 ADDRESS_LENGTH = IP_ADDRESS_LENGTH;
1232 PORT_LENGTH = IP_PORT_LENGTH;
1241 memset( &Netgame, 0, sizeof(Netgame) );
1244 Net_player->flags |= NETINFO_FLAG_DO_NETWORKING;
1245 Net_player->player = Player;
1246 memcpy(&Net_player->p_info.addr,&Psnet_my_addr,sizeof(net_addr));
1248 // check for the existence of a CD
1249 multi_common_verify_cd();
1251 // load my local netplayer options
1252 multi_options_local_load(&Net_player->p_info.options, Net_player);
1257 // common_set_interface_palette(MULTI_JOIN_PALETTE);
1259 // destroy any chatbox contents which previously existed (from another game)
1262 // create the interface window
1263 Multi_join_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
1264 Multi_join_window.set_mask_bmap(Multi_join_bitmap_mask_fname[gr_screen.res]);
1266 // load the background bitmap
1267 Multi_join_bitmap = bm_load(Multi_join_bitmap_fname[gr_screen.res]);
1268 if(Multi_join_bitmap < 0){
1269 // we failed to load the bitmap - this is very bad
1273 // intialize the endgame system
1274 multi_endgame_init();
1276 // initialize the common notification messaging
1277 multi_common_notify_init();
1279 // initialize the common text area
1280 multi_common_set_text("");
1282 // load and use the common interface palette
1283 multi_common_load_palette();
1284 multi_common_set_palette();
1286 // load the help overlay
1287 help_overlay_load(MULTI_JOIN_OVERLAY);
1288 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1290 // do TCP and VMT specific initialization
1291 if(Multi_options_g.protocol == NET_TCP){
1292 // if this is a TCP (non tracker) game, we'll load up our default address list right now
1293 multi_join_load_tcp_addrs();
1296 // initialize any and all timestamps
1297 Multi_join_glr_stamp = -1;
1298 Multi_join_ping_stamp = -1;
1299 Multi_join_sent_stamp = -1;
1301 // reset frame count
1302 Multi_join_frame_count = 0;
1304 // haven't tried to verify on the tracker yet.
1305 Multi_join_mt_tried_verify = 0;
1307 // clear our all game lists to save hassles
1308 multi_join_clear_game_list();
1310 // create the interface buttons
1311 for(idx=0; idx<MULTI_JOIN_NUM_BUTTONS; idx++){
1312 // create the object
1313 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);
1315 // set the sound to play when highlighted
1316 Multi_join_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
1318 // set the ani for the button
1319 Multi_join_buttons[gr_screen.res][idx].button.set_bmaps(Multi_join_buttons[gr_screen.res][idx].filename);
1322 Multi_join_buttons[gr_screen.res][idx].button.link_hotspot(Multi_join_buttons[gr_screen.res][idx].hotspot);
1326 for(idx=0; idx<MULTI_JOIN_NUM_TEXT; idx++){
1327 Multi_join_window.add_XSTR(&Multi_join_text[gr_screen.res][idx]);
1330 Multi_join_should_send = -1;
1332 // close any previously open chatbox
1335 // create the list item select button
1336 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);
1337 Multi_join_select_button.hide();
1340 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);
1342 // if starting a network game, then go to the create game screen
1343 if ( Cmdline_start_netgame ) {
1344 multi_join_create_game();
1345 } else if ( Cmdline_connect_addr != NULL ) {
1350 // joining a game. Send a join request to the given IP address, and wait for the return.
1351 memset( &Multi_autojoin_addr, 0, sizeof(net_addr) );
1352 Multi_autojoin_addr.type = NET_TCP;
1354 // create the address, looking out for port number at the end
1355 port_num = DEFAULT_GAME_PORT;
1356 p = strrchr(Cmdline_connect_addr, ':');
1360 port_num = (short)atoi(p);
1362 ip_addr = inet_addr(Cmdline_connect_addr);
1363 memcpy(Multi_autojoin_addr.addr, &ip_addr, 4);
1364 Multi_autojoin_addr.port = port_num;
1366 send_server_query(&Multi_autojoin_addr);
1367 Multi_autojoin_query_stamp = timestamp(MULTI_AUTOJOIN_QUERY_STAMP);
1368 Multi_did_autojoin = 0;
1372 void multi_join_clear_game_list()
1375 Multi_join_list_selected = -1;
1376 Multi_join_selected_item = NULL;
1377 MJ_LIST_START_SET(-1);
1378 Multi_join_list_start_item = NULL;
1380 // free up the active game list
1381 multi_free_active_games();
1383 // initialize the active game list
1384 Active_game_head = NULL;
1385 Active_game_count = 0;
1388 void multi_join_game_do_frame()
1390 // check the status of our reliable socket. If not valid, popup error and return to main menu
1391 // I put this code here to avoid nasty gameseq issues with states. Also, we will have nice
1392 // background for the popup
1393 if ( !psnet_rel_check() ) {
1394 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));
1395 gameseq_post_event(GS_EVENT_MAIN_MENU);
1399 // return here since we will be moving to the next stage anyway -- I don't want to see the backgrounds of
1400 // all the screens for < 1 second for every screen we automatically move to.
1401 if ( Cmdline_start_netgame ) {
1405 // when joining a network game, wait for the server query to come back, and then join the game
1406 if ( Cmdline_connect_addr != NULL ) {
1409 if ( !Multi_did_autojoin ) {
1410 rval = popup_till_condition(multi_join_autojoin_do, XSTR("&Cancel", 779), XSTR("Joining netgame", 1500) );
1412 // cancel was hit. Send the user back to the main hall
1413 gameseq_post_event(GS_EVENT_MAIN_MENU);
1414 Cmdline_connect_addr = NULL; // reset this value.
1417 // when we get here, we have the data -- join the game.
1418 multi_join_send_join_request(0);
1419 Multi_autojoin_join_stamp = timestamp(MULTI_AUTOJOIN_JOIN_STAMP);
1420 Multi_did_autojoin = 1;
1423 if ( timestamp_elapsed(Multi_autojoin_join_stamp) ) {
1424 multi_join_send_join_request(0);
1425 Multi_autojoin_join_stamp = timestamp(MULTI_AUTOJOIN_JOIN_STAMP);
1431 // reset the should send var
1432 Multi_join_should_send = -1;
1434 int k = Multi_join_window.process();
1436 // process any keypresses
1439 if(help_overlay_active(MULTI_JOIN_OVERLAY)){
1440 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1442 gameseq_post_event(GS_EVENT_MAIN_MENU);
1443 gamesnd_play_iface(SND_USER_SELECT);
1447 // page up the game list
1449 multi_join_list_page_up();
1450 Multi_join_slider.force_currentItem(Multi_join_list_start);
1454 multi_pinfo_popup(Net_player);
1457 // page down the game list
1459 multi_join_list_page_down();
1460 Multi_join_slider.force_currentItem(Multi_join_list_start);
1463 // send out a ping-all
1465 multi_join_ping_all();
1466 Multi_join_ping_stamp = timestamp(MULTI_JOIN_PING_TIME);
1469 // shortcut to start a game
1471 multi_join_create_game();
1474 // scroll the game list up
1476 multi_join_list_scroll_up();
1477 Multi_join_slider.force_currentItem(Multi_join_list_start);
1480 // scroll the game list down
1482 multi_join_list_scroll_down();
1483 Multi_join_slider.force_currentItem(Multi_join_list_start);
1487 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
1488 help_overlay_set_state(MULTI_JOIN_OVERLAY, 0);
1491 // do any network related stuff
1492 multi_join_do_netstuff();
1494 // process any button clicks
1495 multi_join_check_buttons();
1497 // process any list selection stuff
1498 multi_join_process_select();
1500 // draw the background, etc
1502 GR_MAYBE_CLEAR_RES(Multi_join_bitmap);
1503 if(Multi_join_bitmap != -1){
1504 gr_set_bitmap(Multi_join_bitmap);
1507 Multi_join_window.draw();
1509 // display the active games
1510 multi_join_display_games();
1512 // display any text in the info area
1513 multi_common_render_text();
1515 // display any pending notification messages
1516 multi_common_notify_do();
1518 // blit the CD icon and any PXO filter stuff
1519 multi_join_blit_top_stuff();
1521 // draw the help overlay
1522 help_overlay_maybe_blit(MULTI_JOIN_OVERLAY);
1527 // if we are supposed to be sending a join request
1528 if(Multi_join_should_send != -1){
1529 multi_join_send_join_request(Multi_join_should_send);
1531 Multi_join_should_send = -1;
1533 // increment the frame count
1534 Multi_join_frame_count++;
1537 void multi_join_game_close()
1539 // unload any bitmaps
1540 if(!bm_unload(Multi_join_bitmap)){
1541 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_join_bitmap_fname[gr_screen.res]));
1544 // unload the help overlay
1545 help_overlay_unload(MULTI_JOIN_OVERLAY);
1547 // free up the active game list
1548 multi_free_active_games();
1550 // destroy the UI_WINDOW
1551 Multi_join_window.destroy();
1554 void multi_join_check_buttons()
1557 for(idx=0;idx<MULTI_JOIN_NUM_BUTTONS;idx++){
1558 // we only really need to check for one button pressed at a time, so we can break after
1560 if(Multi_join_buttons[gr_screen.res][idx].button.pressed()){
1561 multi_join_button_pressed(idx);
1567 void multi_join_button_pressed(int n)
1571 // if we're player PXO, go back there
1572 gameseq_post_event(GS_EVENT_MAIN_MENU);
1573 gamesnd_play_iface(SND_USER_SELECT);
1576 if(Active_game_count <= 0){
1577 multi_common_add_notify(XSTR("No games found!",757));
1578 gamesnd_play_iface(SND_GENERAL_FAIL);
1579 } else if(Multi_join_list_selected == -1){
1580 multi_common_add_notify(XSTR("No game selected!",758));
1581 gamesnd_play_iface(SND_GENERAL_FAIL);
1582 } else if((Multi_join_sent_stamp != -1) && !timestamp_elapsed(Multi_join_sent_stamp)){
1583 multi_common_add_notify(XSTR("Still waiting on previous join request!",759));
1584 gamesnd_play_iface(SND_GENERAL_FAIL);
1586 // otherwise, if he's already played PXO games, warn him
1588 if(Player->flags & PLAYER_FLAGS_HAS_PLAYED_PXO){
1589 if(!multi_join_warn_pxo()){
1595 // send the join request here
1596 Assert(Multi_join_selected_item != NULL);
1598 // send a join request packet
1599 Multi_join_should_send = 0;
1601 gamesnd_play_iface(SND_COMMIT_PRESSED);
1607 if(!help_overlay_active(MULTI_JOIN_OVERLAY)){
1608 help_overlay_set_state(MULTI_JOIN_OVERLAY,1);
1610 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1614 // scroll the game list up
1616 multi_join_list_scroll_up();
1617 Multi_join_slider.force_currentItem(Multi_join_list_start);
1620 // scroll the game list down
1621 case MJ_SCROLL_DOWN:
1622 multi_join_list_scroll_down();
1623 Multi_join_slider.force_currentItem(Multi_join_list_start);
1626 // scroll the info text box up
1627 case MJ_SCROLL_INFO_UP:
1628 multi_common_scroll_text_up();
1631 // scroll the info text box down
1632 case MJ_SCROLL_INFO_DOWN:
1633 multi_common_scroll_text_down();
1636 // go to the options screen
1638 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
1641 // go to the start game screen
1643 multi_join_create_game();
1646 // refresh the game/server list
1648 gamesnd_play_iface(SND_USER_SELECT);
1649 broadcast_game_query();
1652 // join a game as an observer
1653 case MJ_JOIN_OBSERVER:
1654 if(Active_game_count <= 0){
1655 multi_common_add_notify(XSTR("No games found!",757));
1656 gamesnd_play_iface(SND_GENERAL_FAIL);
1657 } else if(Multi_join_list_selected == -1){
1658 multi_common_add_notify(XSTR("No game selected!",758));
1659 gamesnd_play_iface(SND_GENERAL_FAIL);
1660 } else if((Multi_join_sent_stamp != -1) && !timestamp_elapsed(Multi_join_sent_stamp)){
1661 multi_common_add_notify(XSTR("Still waiting on previous join request!",759));
1662 gamesnd_play_iface(SND_GENERAL_FAIL);
1664 // send the join request here
1665 Assert(Multi_join_selected_item != NULL);
1667 Multi_join_should_send = 1;
1669 gamesnd_play_iface(SND_COMMIT_PRESSED);
1674 multi_common_add_notify(XSTR("Not implemented yet!",760));
1675 gamesnd_play_iface(SND_GENERAL_FAIL);
1680 // display all relevant info for active games
1681 void multi_join_display_games()
1683 active_game *moveup = Multi_join_list_start_item;
1687 int y_start = Mj_list_y[gr_screen.res];
1692 // blit the game status (including text and type icon)
1693 multi_join_blit_game_status(moveup,y_start);
1695 // get the connection type
1696 con_type = (moveup->flags & AG_FLAG_CONNECTION_SPEED_MASK) >> AG_FLAG_CONNECTION_BIT;
1697 if((con_type > 4) || (con_type < 0)){
1701 // display the connection speed
1703 strcpy(str, Multi_join_speed_labels[con_type]);
1704 gr_set_color_fast(Multi_join_speed_colors[con_type]);
1705 gr_string(Mj_speed_coords[gr_screen.res][MJ_X_COORD], y_start, str);
1707 // we'll want to have different colors for highlighted items, etc.
1708 if(moveup == Multi_join_selected_item){
1709 gr_set_color_fast(&Color_text_selected);
1711 gr_set_color_fast(&Color_text_normal);
1714 // display the game name, adding appropriate status chars
1716 if(moveup->flags & AG_FLAG_STANDALONE){
1717 strcat(str,MJ_CHAR_STANDALONE);
1719 if(moveup->flags & AG_FLAG_CAMPAIGN){
1720 strcat(str,MJ_CHAR_CAMPAIGN);
1723 // tack on the actual server name
1725 strcat(str,moveup->name);
1726 if(strlen(moveup->mission_name) > 0){
1728 strcat(str,moveup->mission_name);
1731 // make sure the string fits in the display area and draw it
1732 gr_force_fit_string(str,200,Mj_game_name_coords[gr_screen.res][MJ_W_COORD]);
1733 gr_string(Mj_game_name_coords[gr_screen.res][MJ_X_COORD],y_start,str);
1735 // display the ping time
1736 if(moveup->ping.ping_avg > 0){
1737 if(moveup->ping.ping_avg > 1000){
1738 gr_set_color_fast(&Color_bright_red);
1739 strcpy(str,XSTR("> 1 sec",761));
1741 // set the appropriate ping time color indicator
1742 if(moveup->ping.ping_avg > MJ_PING_YELLOW){
1743 gr_set_color_fast(&Color_bright_red);
1744 } else if(moveup->ping.ping_avg > MJ_PING_YELLOW){
1745 gr_set_color_fast(&Color_bright_yellow);
1747 gr_set_color_fast(&Color_bright_green);
1750 sprintf(str,"%d",moveup->ping.ping_avg);
1751 strcat(str,XSTR(" ms",762)); // [[ Milliseconds ]]
1754 gr_string(Mj_ping_coords[gr_screen.res][MJ_X_COORD],y_start,str);
1757 // display the number of players (be sure to center it)
1758 if(moveup == Multi_join_selected_item){
1759 gr_set_color_fast(&Color_text_selected);
1761 gr_set_color_fast(&Color_text_normal);
1763 sprintf(str,"%d",moveup->num_players);
1764 gr_get_string_size(&w,&h,str);
1765 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);
1769 moveup = moveup->next;
1770 } while((moveup != Active_game_head) && (count < Mj_max_game_items[gr_screen.res]));
1772 // if there are no items on the list, display this info
1774 gr_set_color_fast(&Color_bright);
1775 gr_string(Mj_game_name_coords[gr_screen.res][MJ_X_COORD] - 30,y_start,XSTR("<No game servers found>",763));
1779 void multi_join_blit_game_status(active_game *game, int y)
1782 char status_text[25];
1784 // blit the proper icon
1786 switch( game->flags & AG_FLAG_TYPE_MASK ){
1789 if(Multi_common_icons[MICON_COOP] != -1){
1790 gr_set_bitmap(Multi_common_icons[MICON_COOP]);
1795 // team vs. team game
1797 if(Multi_common_icons[MICON_TVT] != -1){
1798 gr_set_bitmap(Multi_common_icons[MICON_TVT]);
1804 case AG_FLAG_DOGFIGHT:
1805 if(Multi_common_icons[MICON_DOGFIGHT] != -1){
1806 gr_set_bitmap(Multi_common_icons[MICON_DOGFIGHT]);
1811 // if we're supposed to draw a bitmap
1813 gr_bitmap(Mj_game_icon_coords[gr_screen.res][MJ_X_COORD],y-1);
1816 // blit the proper status text
1817 memset(status_text,0,25);
1819 switch( game->flags & AG_FLAG_STATE_MASK ){
1820 case AG_FLAG_FORMING:
1821 gr_set_color_fast(&Color_bright_green);
1822 strcpy(status_text,XSTR("Forming",764));
1824 case AG_FLAG_BRIEFING:
1825 gr_set_color_fast(&Color_bright_red);
1826 strcpy(status_text,XSTR("Briefing",765));
1828 case AG_FLAG_DEBRIEF:
1829 gr_set_color_fast(&Color_bright_red);
1830 strcpy(status_text,XSTR("Debrief",766));
1833 gr_set_color_fast(&Color_bright_red);
1834 strcpy(status_text,XSTR("Paused",767));
1836 case AG_FLAG_IN_MISSION:
1837 gr_set_color_fast(&Color_bright_red);
1838 strcpy(status_text,XSTR("Playing",768));
1841 gr_set_color_fast(&Color_bright);
1842 strcpy(status_text,XSTR("Unknown",769));
1845 gr_get_string_size(&str_w,NULL,status_text);
1846 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);
1849 // load in a list of active games from our tcp.cfg file
1850 void multi_join_load_tcp_addrs()
1852 char line[MAX_IP_STRING];
1857 // attempt to open the ip list file
1858 file = cfopen(IP_CONFIG_FNAME,"rt",CFILE_NORMAL,CF_TYPE_DATA);
1860 nprintf(("Network","Error loading tcp.cfg file!\n"));
1864 // free up any existing server list
1865 multi_free_server_list();
1867 // read in all the strings in the file
1868 while(!cfeof(file)){
1870 cfgets(line,MAX_IP_STRING,file);
1872 // strip off any newline character
1873 if(line[strlen(line) - 1] == '\n'){
1874 line[strlen(line) - 1] = '\0';
1877 // empty lines don't get processed
1878 if( (line[0] == '\0') || (line[0] == '\n') ){
1882 if ( !psnet_is_valid_ip_string(line) ) {
1883 nprintf(("Network","Invalid ip string (%s)\n",line));
1885 // copy the server ip address
1886 memset(&addr,0,sizeof(net_addr));
1887 addr.type = NET_TCP;
1888 psnet_string_to_addr(&addr,line);
1889 if ( addr.port == 0 ){
1890 addr.port = DEFAULT_GAME_PORT;
1893 // create a new server item on the list
1894 item = multi_new_server_item();
1896 memcpy(&item->server_addr,&addr,sizeof(net_addr));
1904 // do stuff like pinging servers, sending out requests, etc
1905 void multi_join_do_netstuff()
1907 // handle game query stuff
1908 if(Multi_join_glr_stamp == -1){
1909 broadcast_game_query();
1911 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
1912 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME_LOCAL);
1914 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME);
1917 // otherwise send out game query and restamp
1918 else if(timestamp_elapsed(Multi_join_glr_stamp)){
1919 broadcast_game_query();
1921 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
1922 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME_LOCAL);
1924 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME);
1928 // check to see if we've been accepted. If so, put up message saying so
1929 if ( Net_player->flags & (NETINFO_FLAG_ACCEPT_INGAME|NETINFO_FLAG_ACCEPT_CLIENT|NETINFO_FLAG_ACCEPT_HOST|NETINFO_FLAG_ACCEPT_OBSERVER) ) {
1930 multi_common_add_notify(XSTR("Accepted. Waiting for player data.",770));
1933 // check to see if any join packets we have sent have timed out
1934 if((Multi_join_sent_stamp != -1) && (timestamp_elapsed(Multi_join_sent_stamp))){
1935 Multi_join_sent_stamp = -1;
1936 multi_common_add_notify(XSTR("Join request timed out!",771));
1939 // check to see if we should be pinging everyone
1940 if((Multi_join_ping_stamp == -1) || (timestamp_elapsed(Multi_join_ping_stamp))){
1941 multi_join_ping_all();
1942 Multi_join_ping_stamp = timestamp(MULTI_JOIN_PING_TIME);
1946 multi_join_cull_timeouts();
1949 // evaluate a returned pong.
1950 void multi_join_eval_pong(net_addr *addr, fix pong_time)
1953 active_game *moveup = Active_game_head;
1958 if(psnet_same(&moveup->server_addr,addr)){
1960 multi_ping_eval_pong(&moveup->ping);
1964 moveup = moveup->next;
1966 } while(moveup != Active_game_head);
1969 // update the game's ping
1971 if(found && (moveup->ping_end > moveup->ping_start)){
1972 moveup->ping_time = f2fl(moveup->ping_end - moveup->ping_start);
1973 moveup->ping_start = -1;
1974 moveup->ping_end = -1;
1979 // ping all the server on the list
1980 void multi_join_ping_all()
1982 active_game *moveup = Active_game_head;
1987 moveup->ping_start = timer_get_fixed_seconds();
1988 moveup->ping_end = -1;
1989 send_ping(&moveup->server_addr);
1991 multi_ping_send(&moveup->server_addr,&moveup->ping);
1993 moveup = moveup->next;
1994 } while(moveup != Active_game_head);
1998 void multi_join_process_select()
2000 // if we don't have anything selected and there are items on the list - select the first one
2001 if((Multi_join_list_selected == -1) && (Active_game_count > 0)){
2002 Multi_join_list_selected = 0;
2003 Multi_join_selected_item = multi_join_get_game(0);
2004 MJ_LIST_START_SET(0);
2005 Multi_join_list_start_item = Multi_join_selected_item;
2007 // send a mission description request to this guy
2008 send_netgame_descript_packet(&Multi_join_selected_item->server_addr,0);
2009 multi_common_set_text("");
2011 // I sure hope this doesn't happen
2012 Assert(Multi_join_selected_item != NULL);
2015 // otherwise see if he's clicked on an item
2016 else if(Multi_join_select_button.pressed() && (Active_game_count > 0)){
2018 Multi_join_select_button.get_mouse_pos(NULL,&y);
2020 if(item + Multi_join_list_start < Active_game_count){
2021 gamesnd_play_iface(SND_IFACE_MOUSE_CLICK);
2023 Multi_join_list_selected = item + Multi_join_list_start;
2024 Multi_join_selected_item = multi_join_get_game(Multi_join_list_selected);
2026 // I sure hope this doesn't happen
2027 Assert(Multi_join_selected_item != NULL);
2029 // send a mission description request to this guy
2030 send_netgame_descript_packet(&Multi_join_selected_item->server_addr,0);
2031 multi_common_set_text("");
2035 // if he's double clicked, then select it and accept
2036 if(Multi_join_select_button.double_clicked()){
2038 Multi_join_select_button.get_mouse_pos(NULL,&y);
2040 if(item == Multi_join_list_selected){
2041 multi_join_button_pressed(MJ_ACCEPT);
2046 // return game n (0 based index)
2047 active_game *multi_join_get_game(int n)
2049 active_game *moveup = Active_game_head;
2056 moveup = moveup->next;
2057 while((moveup != Active_game_head) && (count != n)){
2058 moveup = moveup->next;
2061 if(moveup == Active_game_head){
2062 nprintf(("Network","Warning, couldn't find game item %d!\n",n));
2072 // scroll through the game list
2073 void multi_join_list_scroll_up()
2075 // if we're not at the beginning of the list, scroll up
2076 if(Multi_join_list_start_item != Active_game_head){
2077 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2079 MJ_LIST_START_DEC();
2081 gamesnd_play_iface(SND_SCROLL);
2083 gamesnd_play_iface(SND_GENERAL_FAIL);
2087 // scroll through the game list
2088 void multi_join_list_scroll_down()
2090 if((Active_game_count - Multi_join_list_start) > Mj_max_game_items[gr_screen.res]){
2091 Multi_join_list_start_item = Multi_join_list_start_item->next;
2093 MJ_LIST_START_INC();
2095 gamesnd_play_iface(SND_SCROLL);
2097 gamesnd_play_iface(SND_GENERAL_FAIL);
2101 void multi_join_list_page_up()
2103 // in this case, just set us to the beginning of the list
2104 if((Multi_join_list_start - Mj_max_game_items[gr_screen.res]) < 0){
2105 Multi_join_list_start_item = Active_game_head;
2107 MJ_LIST_START_SET(0);
2109 gamesnd_play_iface(SND_SCROLL);
2111 // otherwise page the whole thing up
2113 for(idx=0; idx<Mj_max_game_items[gr_screen.res]; idx++){
2114 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2116 MJ_LIST_START_DEC();
2118 gamesnd_play_iface(SND_SCROLL);
2122 void multi_join_list_page_down()
2126 // page the whole thing down
2127 while((count < Mj_max_game_items[gr_screen.res]) && ((Active_game_count - Multi_join_list_start) > Mj_max_game_items[gr_screen.res])){
2128 Multi_join_list_start_item = Multi_join_list_start_item->next;
2129 MJ_LIST_START_INC();
2134 gamesnd_play_iface(SND_SCROLL);
2137 void multi_join_cull_timeouts()
2139 active_game *backup;
2141 active_game *moveup = Active_game_head;
2143 // traverse through the entire list if any items exist
2147 if((moveup->heard_from_timer != -1) && (timestamp_elapsed(moveup->heard_from_timer))){
2148 Active_game_count--;
2150 // if this is the head of the list
2151 if(moveup == Active_game_head){
2152 // if this is the _only_ item on the list
2153 if(moveup->next == Active_game_head){
2154 // handle any gui details related to deleting this item
2155 multi_join_handle_item_cull(Active_game_head, count);
2157 free(Active_game_head);
2158 Active_game_head = NULL;
2161 // if there are other items on the list
2163 // handle any gui details related to deleting this item
2164 multi_join_handle_item_cull(moveup, count);
2166 Active_game_head = moveup->next;
2167 Active_game_head->prev = moveup->prev;
2168 Active_game_head->prev->next = Active_game_head;
2170 moveup = Active_game_head;
2173 // if its somewhere else on the list
2175 // handle any gui details related to deleting this item
2176 multi_join_handle_item_cull(moveup, count);
2178 // if its the last item on the list
2179 moveup->next->prev = moveup->prev;
2180 moveup->prev->next = moveup->next;
2182 // if it was the last element on the list, return
2183 if(moveup->next == Active_game_head){
2187 backup = moveup->next;
2193 moveup = moveup->next;
2196 } while(moveup != Active_game_head);
2200 // deep magic begins here.
2201 void multi_join_handle_item_cull(active_game *item, int item_index)
2203 // if this is the only item on the list, unset everything
2204 if(item->next == item){
2205 Multi_join_list_selected = -1;
2206 Multi_join_selected_item = NULL;
2208 Multi_join_slider.set_numberItems(0);
2209 MJ_LIST_START_SET(-1);
2210 Multi_join_list_start_item = NULL;
2216 // see if we should be adjusting our currently selected item
2217 if(item_index <= Multi_join_list_selected){
2218 // the selected item is the head of the list
2219 if(Multi_join_selected_item == Active_game_head){
2220 // move the pointer up since this item is about to be destroyed
2221 Multi_join_selected_item = Multi_join_selected_item->next;
2223 // if this is the item being deleted, select the previous one
2224 if(item == Multi_join_selected_item){
2226 Multi_join_selected_item = Multi_join_selected_item->prev;
2228 // decrement the selected index by 1
2229 Multi_join_list_selected--;
2231 // now we know its a previous item, so our pointer stays the same but our index goes down by one, since there will be
2232 // 1 less item on the list
2234 // decrement the selected index by 1
2235 Multi_join_list_selected--;
2240 // see if we should be adjusting out current start position
2241 if(item_index <= Multi_join_list_start){
2242 // the start position is the head of the list
2243 if(Multi_join_list_start_item == Active_game_head){
2244 // move the pointer up since this item is about to be destroyed
2245 Multi_join_list_start_item = Multi_join_list_start_item->next;
2247 // if this is the item being deleted, select the previous one
2248 if(item == Multi_join_list_start_item){
2249 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2251 // decrement the starting index by 1
2252 MJ_LIST_START_DEC();
2254 // but decrement the starting index by 1
2255 MJ_LIST_START_DEC();
2260 // maybe go back up a bit so that we always have a full page of items
2261 if(Active_game_count > Mj_max_game_items[gr_screen.res]){
2262 while((Active_game_count - Multi_join_list_start) < Mj_max_game_items[gr_screen.res]){
2263 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2264 MJ_LIST_START_DEC();
2268 // set slider location
2269 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);
2270 Multi_join_slider.force_currentItem(Multi_join_list_start);
2273 void multi_join_send_join_request(int as_observer)
2275 // don't do anything if we have no items selected
2276 if(Multi_join_selected_item == NULL){
2280 // 5/26/98 -- for team v team games, don't allow ingame joining :-(
2281 if ( (Multi_join_selected_item->flags & AG_FLAG_TEAMS) && (Multi_join_selected_item->flags & (AG_FLAG_PAUSE|AG_FLAG_IN_MISSION)) ) {
2282 popup(0, 1, POPUP_OK, XSTR("Joining ingame is currently not allowed for team vs. team games",772));
2286 memset(&Multi_join_request,0,sizeof(join_request));
2288 // if the netgame is in password mode, put up a request for the password
2289 if(Multi_join_selected_item->flags & AG_FLAG_PASSWD){
2290 if(!multi_passwd_popup(Multi_join_request.passwd)){
2294 nprintf(("Password : %s\n",Multi_join_request.passwd));
2297 // fill out the join request struct
2298 strcpy(Multi_join_request.callsign,Player->callsign);
2299 if(strlen(Player->image_filename) > 0){
2300 strcpy(Multi_join_request.image_filename, Player->image_filename);
2302 if(strlen(Player->squad_filename) > 0){
2303 strcpy(Multi_join_request.squad_filename, Player->squad_filename);
2306 // tracker id (if any)
2307 Multi_join_request.tracker_id = Multi_tracker_id;
2309 // player's rank (at least, what he wants you to _believe_)
2310 Multi_join_request.player_rank = (ubyte)Player->stats.rank;
2313 Multi_join_request.flags = 0;
2315 Multi_join_request.flags |= JOIN_FLAG_AS_OBSERVER;
2318 // if the player has hacked data
2319 if(game_hacked_data()){
2320 Multi_join_request.flags |= JOIN_FLAG_HAXOR;
2324 strncpy(Multi_join_request.pxo_squad_name, Multi_tracker_squad_name, LOGIN_LEN);
2326 // version of this server
2327 Multi_join_request.version = MULTI_FS_SERVER_VERSION;
2329 // server compatible version
2330 Multi_join_request.comp_version = MULTI_FS_SERVER_COMPATIBLE_VERSION;
2332 // his local player options
2333 memcpy(&Multi_join_request.player_options,&Player->m_local_options,sizeof(multi_local_options));
2335 // set the server address for the netgame
2336 memcpy(&Netgame.server_addr,&Multi_join_selected_item->server_addr,sizeof(net_addr));
2338 // send a join request to the guy
2339 send_join_packet(&Multi_join_selected_item->server_addr,&Multi_join_request);
2342 Multi_join_sent_stamp = timestamp(MULTI_JOIN_SENT_WAIT);
2345 multi_common_add_notify(XSTR("Sending join request...",773));
2348 void multi_join_create_game()
2350 // maybe warn the player about possible crappy server conditions
2351 if(!multi_join_maybe_warn()){
2355 // make sure to flag ourself as being the master
2356 Net_player->flags |= (NETINFO_FLAG_AM_MASTER | NETINFO_FLAG_GAME_HOST);
2357 Net_player->state = NETPLAYER_STATE_HOST_SETUP;
2359 // if we're in PXO mode, mark it down in our player struct
2360 if(MULTI_IS_TRACKER_GAME){
2361 Player->flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
2362 Player->save_flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
2364 // otherwise, if he's already played PXO games, warn him
2367 if(Player->flags & PLAYER_FLAGS_HAS_PLAYED_PXO){
2368 if(!multi_join_warn_pxo()){
2375 gameseq_post_event(GS_EVENT_MULTI_START_GAME);
2376 gamesnd_play_iface(SND_USER_SELECT);
2379 void multi_join_reset_join_stamp()
2381 // unset the timestamp here so the user can immediately send another join request
2382 Multi_join_sent_stamp = -1;
2383 multi_common_add_notify("");
2386 void multi_join_blit_top_stuff()
2388 // blit the cd icon if he has one
2389 if(Multi_has_cd && (Multi_common_icons[MICON_CD] != -1)){
2392 bm_get_info(Multi_common_icons[MICON_CD], &cd_w, NULL, NULL, NULL, NULL);
2394 gr_set_bitmap(Multi_common_icons[MICON_CD]);
2395 gr_bitmap((gr_screen.max_w / 2) - (cd_w / 2), Mj_cd_coords[gr_screen.res]);
2399 #define CW_CODE_CANCEL 0 // cancel the action
2400 #define CW_CODE_OK 1 // continue anyway
2401 #define CW_CODE_INFO 2 // gimme some more information
2403 #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)
2404 #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)
2406 #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)
2407 #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)
2409 int multi_join_warn_update_low(int code)
2413 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),LOW_WARN_TEXT);
2416 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),LOW_INFO_TEXT);
2419 return CW_CODE_CANCEL;
2422 int multi_join_warn_update_medium(int code)
2426 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),MED_WARN_TEXT);
2429 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),MED_INFO_TEXT);
2432 return CW_CODE_CANCEL;
2435 int multi_join_maybe_warn()
2439 // if the player is set for low updates
2440 if(Player->m_local_options.obj_update_level == OBJ_UPDATE_LOW){
2443 code = multi_join_warn_update_low(code);
2444 } while((code != CW_CODE_CANCEL) && (code != CW_CODE_OK));
2449 // if the player is set for medium updates
2450 else if(Player->m_local_options.obj_update_level == OBJ_UPDATE_MEDIUM){
2453 code = multi_join_warn_update_medium(code);
2454 } while((code != CW_CODE_CANCEL) && (code != CW_CODE_OK));
2462 int multi_join_warn_pxo()
2464 // 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;
2468 void multi_join_blit_protocol()
2470 gr_set_color_fast(&Color_bright);
2472 switch(Socket_type){
2475 gr_string(5, 2, "TCP");
2479 gr_string(5, 2, "IPX");
2485 // -------------------------------------------------------------------------------------------------
2487 // MULTIPLAYER START GAME screen
2492 #define MULTI_SG_PALETTE "InterfacePalette"
2494 static char *Multi_sg_bitmap_fname[GR_NUM_RESOLUTIONS] = {
2495 "MultiStartGame", // GR_640
2496 "2_MultiStartGame" // GR_1024
2499 static char *Multi_sg_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
2500 "MultiStartGame-M", // GR_640
2501 "2_MultiStartGame-M" // GR_1024
2506 int Multi_sg_rank_max_display[GR_NUM_RESOLUTIONS] = {
2511 // constants for coordinate look ups
2512 #define MSG_X_COORD 0
2513 #define MSG_Y_COORD 1
2514 #define MSG_W_COORD 2
2515 #define MSG_H_COORD 3
2519 // input password field
2520 int Msg_passwd_coords[GR_NUM_RESOLUTIONS][4] = {
2529 // input game title field
2530 int Msg_title_coords[GR_NUM_RESOLUTIONS][4] = {
2539 // rank selected field
2540 int Msg_rank_sel_coords[GR_NUM_RESOLUTIONS][4] = {
2550 int Msg_rank_list_coords[GR_NUM_RESOLUTIONS][4] = {
2561 #define MULTI_SG_NUM_BUTTONS 10
2562 #define MSG_OPEN_GAME 0
2563 //#define MSG_CLOSED_GAME 1
2564 //#define MSG_RESTRICTED_GAME 2
2565 #define MSG_PASSWD_GAME 1
2566 #define MSG_RANK_SET_GAME 2
2567 #define MSG_RANK_SCROLL_UP 3
2568 #define MSG_RANK_SCROLL_DOWN 4
2569 #define MSG_RANK_ABOVE 5
2570 #define MSG_RANK_BELOW 6
2572 #define MSG_OPTIONS 8
2573 #define MSG_ACCEPT 9
2575 UI_WINDOW Multi_sg_window; // the window object for the join screen
2576 UI_BUTTON Multi_sg_rank_button; // for selecting the rank marker
2577 UI_INPUTBOX Multi_sg_game_name; // for Netgame.name
2578 UI_INPUTBOX Multi_sg_game_passwd; // for Netgame.passwd
2579 int Multi_sg_bitmap; // the background bitmap
2581 ui_button_info Multi_sg_buttons[GR_NUM_RESOLUTIONS][MULTI_SG_NUM_BUTTONS] = {
2583 ui_button_info("MSG_00", 1, 184, 34, 191, 2), // open
2584 // ui_button_info("MSG_01", 1, 159, 34, 166, 1), // closed
2585 // ui_button_info("MSG_02", 1, 184, 34, 191, 2), // restricted
2586 ui_button_info("MSG_03", 1, 209, 34, 218, 3), // password
2587 ui_button_info("MSG_04", 1, 257, 34, 266, 4), // rank set
2588 ui_button_info("MSG_05", 1, 282, -1, -1, 5), // rank scroll up
2589 ui_button_info("MSG_06", 1, 307, -1, -1, 6), // rank scroll down
2590 ui_button_info("MSG_07", 177, 282, 210, 290, 7), // rank above
2591 ui_button_info("MSG_08", 177, 307, 210, 315, 8), // rank below
2592 ui_button_info("MSG_09", 536, 429, 500, 440, 9), // help
2593 ui_button_info("MSG_10", 536, 454, 479, 464, 10), // options
2594 ui_button_info("MSG_11", 576, 432, 571, 415, 11), // accept
2597 ui_button_info("2_MSG_00", 2, 295, 51, 307, 2), // open
2598 // ui_button_info("2_MSG_01", 2, 254, 51, 267, 1), // closed
2599 // ui_button_info("2_MSG_02", 2, 295, 51, 307, 2), // restricted
2600 ui_button_info("2_MSG_03", 2, 335, 51, 350, 3), // password
2601 ui_button_info("2_MSG_04", 2, 412, 51, 426, 4), // rank set
2602 ui_button_info("2_MSG_05", 2, 452, -1, -1, 5), // rank scroll up
2603 ui_button_info("2_MSG_06", 2, 492, -1, -1, 6), // rank scroll down
2604 ui_button_info("2_MSG_07", 284, 452, 335, 465, 7), // rank above
2605 ui_button_info("2_MSG_08", 284, 492, 335, 505, 8), // rank below
2606 ui_button_info("2_MSG_09", 858, 687, 817, 706, 9), // help
2607 ui_button_info("2_MSG_10", 858, 728, 797, 743, 10), // options
2608 ui_button_info("2_MSG_11", 921, 692, 921, 664, 11), // accept
2612 #define MULTI_SG_NUM_TEXT 11
2613 UI_XSTR Multi_sg_text[GR_NUM_RESOLUTIONS][MULTI_SG_NUM_TEXT] = {
2615 {"Open", 1322, 34, 191, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_OPEN_GAME].button},
2616 // {"Closed", 1323, 34, 166, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_CLOSED_GAME].button},
2617 // {"Restricted", 1324, 34, 191, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RESTRICTED_GAME].button},
2618 {"Password Protected", 1325, 34, 218, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_PASSWD_GAME].button},
2619 {"Allow Rank", 1326, 34, 266, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_SET_GAME].button},
2620 {"Above", 1327, 210, 290, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_ABOVE].button},
2621 {"Below", 1328, 210, 315, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_BELOW].button},
2622 {"Help", 928, 500, 440, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_HELP].button},
2623 {"Options", 1036, 479, 464, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_OPTIONS].button},
2624 {"Accept", 1035, 571, 415, UI_XSTR_COLOR_PINK, -1, &Multi_sg_buttons[0][MSG_ACCEPT].button},
2625 {"Start Game", 1329, 26, 10, UI_XSTR_COLOR_GREEN, -1, NULL},
2626 {"Title", 1330, 26, 31, UI_XSTR_COLOR_GREEN, -1, NULL},
2627 {"Game Type", 1331, 12, 165, UI_XSTR_COLOR_GREEN, -1, NULL},
2630 {"Open", 1322, 51, 307, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_OPEN_GAME].button},
2631 // {"Closed", 1323, 51, 267, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_CLOSED_GAME].button},
2632 // {"Restricted", 1324, 51, 307, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RESTRICTED_GAME].button},
2633 {"Password Protected", 1325, 51, 350, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_PASSWD_GAME].button},
2634 {"Allow Rank", 1326, 51, 426, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_SET_GAME].button},
2635 {"Above", 1327, 335, 465, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_ABOVE].button},
2636 {"Below", 1328, 335, 505, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_BELOW].button},
2637 {"Help", 928, 817, 706, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_HELP].button},
2638 {"Options", 1036, 797, 743, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_OPTIONS].button},
2639 {"Accept", 1035, 921, 664, UI_XSTR_COLOR_PINK, -1, &Multi_sg_buttons[1][MSG_ACCEPT].button},
2640 {"Start Game", 1329, 42, 22, UI_XSTR_COLOR_GREEN, -1, NULL},
2641 {"Title", 1330, 42, 50, UI_XSTR_COLOR_GREEN, -1, NULL},
2642 {"Game Type", 1331, 20, 264, UI_XSTR_COLOR_GREEN, -1, NULL},
2646 // starting index for displaying ranks
2647 int Multi_sg_rank_start;
2648 int Multi_sg_rank_select;
2650 // netgame pointer to indirect through
2651 netgame_info *Multi_sg_netgame;
2653 // hold temporary values in this structure when on a standalone server
2654 netgame_info Multi_sg_netgame_temp;
2656 // forward declarations
2657 void multi_sg_check_buttons();
2658 void multi_sg_button_pressed(int n);
2659 void multi_sg_init_gamenet();
2660 void multi_sg_draw_radio_buttons();
2661 void multi_sg_rank_scroll_up();
2662 void multi_sg_rank_scroll_down();
2663 void multi_sg_rank_display_stuff();
2664 void multi_sg_rank_process_select();
2665 void multi_sg_rank_build_name(char *in,char *out);
2666 void multi_sg_check_passwd();
2667 void multi_sg_check_name();
2668 void multi_sg_release_passwd();
2669 int multi_sg_rank_select_valid(int rank);
2670 void multi_sg_select_rank_default();
2672 // function which takes a rank name and returns the index. Useful for commandline options
2673 // for above and below rank. We return the index of the rank in the Ranks[] array. If
2674 // the rank isn't found, we return -1
2675 int multi_start_game_rank_from_name( char *rank ) {
2678 for ( i = 0; i <= MAX_FREESPACE2_RANK; i++ ) {
2679 if ( !stricmp(Ranks[i].name, rank) ) {
2687 void multi_start_game_init()
2691 // initialize the gamenet
2692 multi_sg_init_gamenet();
2694 // create the interface window
2695 Multi_sg_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
2696 Multi_sg_window.set_mask_bmap(Multi_sg_bitmap_mask_fname[gr_screen.res]);
2698 // load the background bitmap
2699 Multi_sg_bitmap = bm_load(Multi_sg_bitmap_fname[gr_screen.res]);
2700 if(Multi_sg_bitmap < 0){
2701 // we failed to load the bitmap - this is very bad
2705 // initialize the common notification messaging
2706 multi_common_notify_init();
2708 // initialize the common text area
2709 multi_common_set_text("");
2711 // use the common interface palette
2712 multi_common_set_palette();
2714 // create the interface buttons
2715 for(idx=0; idx<MULTI_SG_NUM_BUTTONS; idx++){
2716 // create the object
2717 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);
2719 // set the sound to play when highlighted
2720 Multi_sg_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
2722 // set the ani for the button
2723 Multi_sg_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sg_buttons[gr_screen.res][idx].filename);
2726 Multi_sg_buttons[gr_screen.res][idx].button.link_hotspot(Multi_sg_buttons[gr_screen.res][idx].hotspot);
2730 for(idx=0; idx<MULTI_SG_NUM_TEXT; idx++){
2731 Multi_sg_window.add_XSTR(&Multi_sg_text[gr_screen.res][idx]);
2734 // load the help overlay
2735 help_overlay_load(MULTI_START_OVERLAY);
2736 help_overlay_set_state(MULTI_START_OVERLAY,0);
2738 // intiialize the rank selection items
2739 multi_sg_select_rank_default();
2740 Multi_sg_rank_start = Multi_sg_rank_select;
2742 // create the rank select button
2743 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);
2744 Multi_sg_rank_button.hide();
2746 // create the netgame name input box
2747 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);
2749 // create the netgame password input box, and disable it by default
2750 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);
2751 Multi_sg_game_passwd.hide();
2752 Multi_sg_game_passwd.disable();
2754 // set the netgame text to this gadget and make it have focus
2755 Multi_sg_game_name.set_text(Multi_sg_netgame->name);
2756 Multi_sg_game_name.set_focus();
2758 // if starting a netgame, set the name of the game and any other options that are appropriate
2759 if ( Cmdline_start_netgame ) {
2760 if ( Cmdline_game_name != NULL ) {
2761 strcpy( Multi_sg_netgame->name, Cmdline_game_name );
2762 Multi_sg_game_name.set_text(Multi_sg_netgame->name);
2765 // deal with the different game types -- only one should even be active, so we will just go down
2766 // the line. Last one wins.
2767 if ( Cmdline_closed_game ) {
2768 Multi_sg_netgame->mode = NG_MODE_CLOSED;
2769 } else if ( Cmdline_restricted_game ) {
2770 Multi_sg_netgame->mode = NG_MODE_RESTRICTED;
2771 } else if ( Cmdline_game_password != NULL ) {
2772 Multi_sg_netgame->mode = NG_MODE_PASSWORD;
2773 strcpy(Multi_sg_netgame->passwd, Cmdline_game_password);
2774 Multi_sg_game_passwd.set_text(Multi_sg_netgame->passwd);
2777 // deal with rank above and rank below
2778 if ( (Cmdline_rank_above != NULL) || (Cmdline_rank_below != NULL) ) {
2782 if ( Cmdline_rank_above != NULL ) {
2783 rank_str = Cmdline_rank_above;
2785 rank_str = Cmdline_rank_below;
2788 // try and get the rank index from the name -- if found, then set the rank base
2789 // and the game type. apparently we only support either above or below, not both
2790 // together, so I make a random choice
2791 rank = multi_start_game_rank_from_name( rank_str );
2793 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
2795 // now an arbitrary decision
2796 if ( Cmdline_rank_above != NULL ) {
2797 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
2799 Multi_sg_netgame->mode = NG_MODE_RANK_BELOW;
2804 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
2808 void multi_start_game_do()
2810 // return here since we will be moving to the next stage anyway -- I don't want to see the backgrounds of
2811 // all the screens for < 1 second for every screen we automatically move to.
2812 if ( Cmdline_start_netgame ) {
2816 int k = Multi_sg_window.process();
2818 // process any keypresses
2821 if(help_overlay_active(MULTI_START_OVERLAY)){
2822 help_overlay_set_state(MULTI_START_OVERLAY,0);
2824 gamesnd_play_iface(SND_USER_SELECT);
2825 multi_quit_game(PROMPT_NONE);
2830 case KEY_LCTRL + KEY_ENTER :
2831 case KEY_RCTRL + KEY_ENTER :
2832 gamesnd_play_iface(SND_COMMIT_PRESSED);
2833 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
2837 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
2838 help_overlay_set_state(MULTI_START_OVERLAY, 0);
2841 // check to see if the user has selected a different rank
2842 multi_sg_rank_process_select();
2844 // check any button presses
2845 multi_sg_check_buttons();
2847 // check to see if any of the input boxes have changed, and update the appropriate Netgame fields if necessary
2848 multi_sg_check_passwd();
2849 multi_sg_check_name();
2851 // draw the background, etc
2853 GR_MAYBE_CLEAR_RES(Multi_sg_bitmap);
2854 if(Multi_sg_bitmap != -1){
2855 gr_set_bitmap(Multi_sg_bitmap);
2858 Multi_sg_window.draw();
2860 // display rank stuff
2861 multi_sg_rank_display_stuff();
2863 // display any pending notification messages
2864 multi_common_notify_do();
2866 // draw all radio button
2867 multi_sg_draw_radio_buttons();
2869 // draw the help overlay
2870 help_overlay_maybe_blit(MULTI_START_OVERLAY);
2876 void multi_start_game_close()
2878 // if i'm the host on a standalone server, send him my start game options (passwd, mode, etc)
2879 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
2880 multi_options_update_start_game(Multi_sg_netgame);
2883 // unload any bitmaps
2884 if(!bm_unload(Multi_sg_bitmap)){
2885 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_sg_bitmap_fname[gr_screen.res]));
2888 // unload the help overlay
2889 help_overlay_unload(MULTI_START_OVERLAY);
2891 // destroy the UI_WINDOW
2892 Multi_sg_window.destroy();
2895 void multi_sg_check_buttons()
2898 for(idx=0;idx<MULTI_SG_NUM_BUTTONS;idx++){
2899 // we only really need to check for one button pressed at a time, so we can break after
2901 if(Multi_sg_buttons[gr_screen.res][idx].button.pressed()){
2902 multi_sg_button_pressed(idx);
2908 void multi_sg_button_pressed(int n)
2911 // go to the options screen
2913 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
2918 if(!help_overlay_active(MULTI_START_OVERLAY)){
2919 help_overlay_set_state(MULTI_START_OVERLAY,1);
2921 help_overlay_set_state(MULTI_START_OVERLAY,0);
2925 // the open button was pressed
2927 // if the closed option is selected
2928 if(Multi_sg_netgame->mode != NG_MODE_OPEN){
2929 Multi_sg_netgame->mode = NG_MODE_OPEN;
2931 gamesnd_play_iface(SND_USER_SELECT);
2933 // release the password control if necessary
2934 multi_sg_release_passwd();
2936 // if its already selected
2938 gamesnd_play_iface(SND_GENERAL_FAIL);
2943 // the open button was pressed
2944 case MSG_CLOSED_GAME:
2945 // if the closed option is selected
2946 if(Multi_sg_netgame->mode != NG_MODE_CLOSED){
2947 Multi_sg_netgame->mode = NG_MODE_CLOSED;
2949 gamesnd_play_iface(SND_USER_SELECT);
2951 // release the password control if necessary
2952 multi_sg_release_passwd();
2954 // if its already selected
2956 gamesnd_play_iface(SND_GENERAL_FAIL);
2960 // toggle password protection
2961 case MSG_PASSWD_GAME:
2962 // if we selected it
2963 if(Multi_sg_netgame->mode != NG_MODE_PASSWORD){
2964 gamesnd_play_iface(SND_USER_SELECT);
2966 Multi_sg_game_passwd.enable();
2967 Multi_sg_game_passwd.unhide();
2968 Multi_sg_game_passwd.set_focus();
2970 Multi_sg_netgame->mode = NG_MODE_PASSWORD;
2972 // copy in the current network password
2973 Multi_sg_game_passwd.set_text(Multi_sg_netgame->passwd);
2975 gamesnd_play_iface(SND_GENERAL_FAIL);
2980 // toggle "restricted" on or off
2981 case MSG_RESTRICTED_GAME:
2982 if(Multi_sg_netgame->mode != NG_MODE_RESTRICTED){
2983 gamesnd_play_iface(SND_USER_SELECT);
2984 Multi_sg_netgame->mode = NG_MODE_RESTRICTED;
2986 // release the password control if necessary
2987 multi_sg_release_passwd();
2989 gamesnd_play_iface(SND_GENERAL_FAIL);
2993 // turn off all rank requirements
2994 case MSG_RANK_SET_GAME:
2995 // if either is set, then turn then both off
2996 if((Multi_sg_netgame->mode != NG_MODE_RANK_BELOW) && (Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE)){
2997 gamesnd_play_iface(SND_USER_SELECT);
2999 // set it to the default case if we're turning it off
3000 multi_sg_select_rank_default();
3001 Multi_sg_rank_start = Multi_sg_rank_select;
3003 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
3005 // release the password control if necessary
3006 multi_sg_release_passwd();
3008 gamesnd_play_iface(SND_GENERAL_FAIL);
3012 // rank above was pressed
3013 case MSG_RANK_ABOVE :
3014 if((Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE) || (Multi_sg_netgame->mode == NG_MODE_RANK_BELOW)){
3015 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
3017 // select the first item
3018 multi_sg_select_rank_default();
3019 Multi_sg_rank_start = Multi_sg_rank_select;
3022 gamesnd_play_iface(SND_USER_SELECT);
3024 gamesnd_play_iface(SND_GENERAL_FAIL);
3028 // rank below was pressed
3029 case MSG_RANK_BELOW :
3030 if((Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE) || (Multi_sg_netgame->mode == NG_MODE_RANK_BELOW)){
3031 Multi_sg_netgame->mode = NG_MODE_RANK_BELOW;
3033 // select the first item
3034 multi_sg_select_rank_default();
3035 Multi_sg_rank_start = Multi_sg_rank_select;
3038 gamesnd_play_iface(SND_USER_SELECT);
3040 gamesnd_play_iface(SND_GENERAL_FAIL);
3044 // scroll the rank list up
3045 case MSG_RANK_SCROLL_UP:
3046 multi_sg_rank_scroll_up();
3049 // scroll the rank list down
3050 case MSG_RANK_SCROLL_DOWN:
3051 multi_sg_rank_scroll_down();
3054 // move to the create game screen
3056 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
3057 gamesnd_play_iface(SND_COMMIT_PRESSED);
3061 gamesnd_play_iface(SND_GENERAL_FAIL);
3062 multi_common_add_notify(XSTR("Not implemented yet!",760));
3067 // NOTE : this is where all Netgame initialization should take place on the host
3068 void multi_sg_init_gamenet()
3070 char buf[128],out_name[128];
3072 net_player *server_save;
3074 // back this data up in case we are already connected to a standalone
3075 memcpy(&save,&Netgame.server_addr,sizeof(net_addr));
3076 server_save = Netgame.server;
3078 // remove campaign flags
3079 Game_mode &= ~(GM_CAMPAIGN_MODE);
3081 // clear out the Netgame structure and start filling in the values
3082 memset( &Netgame, 0, sizeof(Netgame) );
3083 memset( &Multi_sg_netgame_temp, 0, sizeof(netgame_info) );
3085 // if we're on the standalone, we're not the server, so we don't care about setting the netgame state
3086 if(Net_player->state != NETPLAYER_STATE_STD_HOST_SETUP){
3087 Netgame.game_state = NETGAME_STATE_HOST_SETUP;
3088 Multi_sg_netgame = &Netgame;
3091 ml_string(NOX("Starting netgame as Host/Server"));
3093 Multi_sg_netgame = &Multi_sg_netgame_temp;
3097 memcpy(&temp_addr.s_addr, &Netgame.server_addr, 4);
3098 char *server_addr = inet_ntoa(temp_addr);
3099 ml_printf(NOX("Starting netgame as Host on Standalone server : %s"), (server_addr == NULL) ? NOX("Unknown") : server_addr);
3102 Net_player->tracker_player_id = Multi_tracker_id;
3104 Multi_sg_netgame->security = (rand() % 32766) + 1; // get some random security number
3105 Multi_sg_netgame->mode = NG_MODE_OPEN;
3106 Multi_sg_netgame->rank_base = RANK_ENSIGN;
3107 if(Multi_sg_netgame->security < 16){
3108 Multi_sg_netgame->security += 16;
3111 // set the version_info field
3112 Multi_sg_netgame->version_info = NG_VERSION_ID;
3114 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
3115 Netgame.host = Net_player;
3118 // set the default netgame flags
3119 Multi_sg_netgame->flags = 0;
3121 // intialize endgame stuff
3122 multi_endgame_init();
3124 // load in my netgame options
3125 multi_options_netgame_load(&Netgame.options);
3127 // load my local netplayer options
3128 multi_options_local_load(&Net_player->p_info.options, Net_player);
3130 // setup the default game name, taking care of string length and player callsigns
3131 memset(out_name,0,128);
3133 pilot_format_callsign_personal(Player->callsign,out_name);
3134 sprintf(buf, XSTR("%s game",782), out_name); // [[ %s will be a pilot's name ]]
3135 if ( strlen(buf) > MAX_GAMENAME_LEN ){
3136 strcpy(buf, XSTR("Temporary name",783));
3138 strcpy(Multi_sg_netgame->name, buf);
3140 // set the default qos and duration
3141 multi_voice_maybe_update_vars(Netgame.options.voice_qos,Netgame.options.voice_record_time);
3143 // make sure to set the server correctly (me or the standalone)
3144 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3145 memcpy(&Netgame.server_addr, &Psnet_my_addr,sizeof(net_addr));
3146 Netgame.server = Net_player;
3147 Net_player->player_id = multi_get_new_id();
3149 // setup debug flags
3150 Netgame.debug_flags = 0;
3152 if(!Cmdline_server_firing){
3153 Netgame.debug_flags |= NETD_FLAG_CLIENT_FIRING;
3155 if(!Cmdline_client_dodamage){
3156 Netgame.debug_flags |= NETD_FLAG_CLIENT_NODAMAGE;
3160 memcpy(&Netgame.server_addr,&save,sizeof(net_addr));
3161 Netgame.server = server_save;
3164 // if I have a cd or not
3166 Net_player->flags |= NETINFO_FLAG_HAS_CD;
3169 // if I have hacked data
3170 if(game_hacked_data()){
3171 Net_player->flags |= NETINFO_FLAG_HAXOR;
3174 // assign my player struct and other data
3175 Net_player->flags |= (NETINFO_FLAG_CONNECTED | NETINFO_FLAG_DO_NETWORKING);
3176 Net_player->s_info.voice_token_timestamp = -1;
3178 // if we're supposed to flush our cache directory, do so now
3179 if(Net_player->p_info.options.flags & MLO_FLAG_FLUSH_CACHE){
3180 multi_flush_multidata_cache();
3183 ml_string(NOX("Flushing multi-data cache"));
3189 void multi_sg_draw_radio_buttons()
3191 // draw the appropriate radio button
3192 switch(Multi_sg_netgame->mode){
3194 Multi_sg_buttons[gr_screen.res][MSG_OPEN_GAME].button.draw_forced(2);
3197 case NG_MODE_CLOSED:
3198 Multi_sg_buttons[gr_screen.res][MSG_CLOSED_GAME].button.draw_forced(2);
3201 case NG_MODE_PASSWORD:
3202 Multi_sg_buttons[gr_screen.res][MSG_PASSWD_GAME].button.draw_forced(2);
3204 /* case NG_MODE_RESTRICTED:
3205 Multi_sg_buttons[gr_screen.res][MSG_RESTRICTED_GAME].button.draw_forced(2);
3208 case NG_MODE_RANK_ABOVE:
3209 Multi_sg_buttons[gr_screen.res][MSG_RANK_SET_GAME].button.draw_forced(2);
3210 Multi_sg_buttons[gr_screen.res][MSG_RANK_ABOVE].button.draw_forced(2);
3212 case NG_MODE_RANK_BELOW:
3213 Multi_sg_buttons[gr_screen.res][MSG_RANK_SET_GAME].button.draw_forced(2);
3214 Multi_sg_buttons[gr_screen.res][MSG_RANK_BELOW].button.draw_forced(2);
3219 void multi_sg_rank_scroll_up()
3221 // if he doesn't have either of the rank flags set, then ignore this
3222 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3226 if(Multi_sg_rank_start > 0){
3227 Multi_sg_rank_start--;
3228 gamesnd_play_iface(SND_SCROLL);
3230 gamesnd_play_iface(SND_GENERAL_FAIL);
3234 void multi_sg_rank_scroll_down()
3236 // if he doesn't have either of the rank flags set, then ignore this
3237 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3241 if((NUM_RANKS - Multi_sg_rank_start) > Multi_sg_rank_max_display[gr_screen.res]){
3242 Multi_sg_rank_start++;
3243 gamesnd_play_iface(SND_SCROLL);
3245 gamesnd_play_iface(SND_GENERAL_FAIL);
3249 void multi_sg_rank_display_stuff()
3254 // if he doesn't have either of the rank flags set, then ignore this
3255 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3259 // display the list of ranks
3260 y = Msg_rank_list_coords[gr_screen.res][MSG_Y_COORD];
3261 idx = Multi_sg_rank_start;
3263 while((count < NUM_RANKS) && (count < Multi_sg_rank_max_display[gr_screen.res]) && (idx < NUM_RANKS)){
3264 // if its the selected item, then color it differently
3265 if(idx == Multi_sg_rank_select){
3266 gr_set_color_fast(&Color_text_selected);
3268 gr_set_color_fast(&Color_text_normal);
3272 multi_sg_rank_build_name(Ranks[idx].name,rank_name);
3273 gr_string(Msg_rank_list_coords[gr_screen.res][MSG_X_COORD],y,rank_name);
3281 // display the selected rank
3283 gr_set_color_fast(&Color_bright);
3284 multi_sg_rank_build_name(Ranks[Multi_sg_netgame->rank_base].name,rank_name);
3285 gr_string(Msg_rank_sel_coords[gr_screen.res][MSG_X_COORD],Msg_rank_sel_coords[gr_screen.res][MSG_Y_COORD],rank_name);
3289 void multi_sg_rank_process_select()
3293 // if he doesn't have either of the rank flags set, then ignore this
3294 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3298 // see if he's clicked on an item on the rank list
3299 if(Multi_sg_rank_button.pressed()){
3301 Multi_sg_rank_button.get_mouse_pos(NULL,&y);
3304 if(item + Multi_sg_rank_start < NUM_RANKS){
3305 // evaluate whether this rank is valid for the guy to pick
3306 if(multi_sg_rank_select_valid(item + Multi_sg_rank_start)){
3307 gamesnd_play_iface(SND_USER_SELECT);
3309 Multi_sg_rank_select = item + Multi_sg_rank_start;
3311 // set the Netgame rank
3312 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
3314 gamesnd_play_iface(SND_GENERAL_FAIL);
3316 memset(string,0,255);
3317 sprintf(string,XSTR("Illegal value for a host of your rank (%s)\n",784),Ranks[Net_player->player->stats.rank].name);
3318 multi_common_add_notify(string);
3324 void multi_sg_rank_build_name(char *in,char *out)
3330 first = strtok(use," ");
3332 // just copy the string
3337 // if the first part of the string is lieutenant, then abbreivate it and tack on the rest of the string
3338 if (stricmp(first,XSTR("lieutenant",785)) == 0) {
3339 first = strtok(NULL, NOX("\n"));
3341 // if he's not just a plain lieutenant
3343 strcpy(out,XSTR("Lt. ",786)); // [[ lieutenant ]]
3346 // if he _is_ just a plain lieutenant
3355 void multi_sg_check_passwd()
3357 // check to see if the password input box has been pressed
3358 if(Multi_sg_game_passwd.changed()){
3359 Multi_sg_game_passwd.get_text(Multi_sg_netgame->passwd);
3363 void multi_sg_check_name()
3365 // check to see if the game name input box has been pressed
3366 if(Multi_sg_game_name.changed()){
3367 Multi_sg_game_name.get_text(Multi_sg_netgame->name);
3371 void multi_sg_release_passwd()
3373 // hide and disable the password input box
3374 Multi_sg_game_passwd.hide();
3375 Multi_sg_game_passwd.disable();
3377 // set the focus back to the name input box
3378 Multi_sg_game_name.set_focus();
3381 int multi_sg_rank_select_valid(int rank)
3384 if(Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE){
3385 if(Net_player->player->stats.rank >= rank){
3391 if(Net_player->player->stats.rank <= rank){
3399 void multi_sg_select_rank_default()
3401 // pick our rank for now
3402 Multi_sg_rank_select = Net_player->player->stats.rank;
3404 // set the Netgame rank
3405 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
3408 // -------------------------------------------------------------------------------------------------
3410 // MULTIPLAYER CREATE GAME screen
3415 char *Multi_create_bitmap_fname[GR_NUM_RESOLUTIONS] = {
3416 "MultiCreate", // GR_640
3417 "2_MultiCreate" // GR_1024
3420 char *Multi_create_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
3421 "MultiCreate-M", // GR_640
3422 "2_MultiCreate-M" // GR_1024
3425 char *Multi_create_loading_fname[GR_NUM_RESOLUTIONS] = {
3426 "PleaseWait", // GR_640
3427 "2_PleaseWait" // GR_1024
3431 #define MULTI_CREATE_NUM_BUTTONS 23
3434 #define MC_SHOW_ALL 0
3435 #define MC_SHOW_COOP 1
3436 #define MC_SHOW_TEAM 2
3437 #define MC_SHOW_DOGFIGHT 3
3438 #define MC_PXO_REFRESH 4
3439 #define MC_PILOT_INFO 5
3440 #define MC_SCROLL_LIST_UP 6
3441 #define MC_SCROLL_LIST_DOWN 7
3442 #define MC_SCROLL_PLAYERS_UP 8
3443 #define MC_SCROLL_PLAYERS_DOWN 9
3444 #define MC_MISSION_FILTER 10
3445 #define MC_CAMPAIGN_FILTER 11
3446 #define MC_CANCEL 12
3451 #define MC_SCROLL_INFO_UP 17
3452 #define MC_SCROLL_INFO_DOWN 18
3453 #define MC_HOST_OPTIONS 19
3455 #define MC_OPTIONS 21
3456 #define MC_ACCEPT 22
3459 UI_WINDOW Multi_create_window; // the window object for the create screen
3460 UI_BUTTON Multi_create_player_select_button; // for selecting players
3461 UI_BUTTON Multi_create_list_select_button; // for selecting missions/campaigns
3462 int Multi_create_bitmap; // the background bitmap
3463 UI_SLIDER2 Multi_create_slider; // for create list
3465 // constants for coordinate look ups
3466 #define MC_X_COORD 0
3467 #define MC_Y_COORD 1
3468 #define MC_W_COORD 2
3469 #define MC_H_COORD 3
3471 ui_button_info Multi_create_buttons[GR_NUM_RESOLUTIONS][MULTI_CREATE_NUM_BUTTONS] = {
3473 ui_button_info("MC_00", 32, 129, 36, 158, 0), // show all missions
3474 ui_button_info("MC_01", 76, 129, 71, 158, 1), // show coop missions
3475 ui_button_info("MC_02", 121, 129, 119, 158, 2), // show team missions
3476 ui_button_info("MC_03", 164, 129, 166, 158, 3), // show dogfight missions
3477 ui_button_info("MC_04", 399, 129, 229, 130, 4), // pxo mission refresh
3478 ui_button_info("MC_05", 567, 123, 467, 132, 5), // pilot info
3479 ui_button_info("MC_06", 1, 161, -1, -1, 6), // scroll mission info up
3480 ui_button_info("MC_08", 1, 304, -1, -1, 8), // scroll mission info down
3481 ui_button_info("MC_09", 613, 160, -1, -1, 9), // scroll players up
3482 ui_button_info("MC_10", 613, 202, -1, -1, 10), // scroll players down
3483 ui_button_info("MC_11", 22, 346, 27, 376, 11), // mission filter
3484 ui_button_info("MC_12", 104, 346, 110, 376, 12), // campaign filter
3485 ui_button_info("MC_13", 392, 341, 328, 364, 13), // cancel
3486 ui_button_info("MC_14", 472, 352, 482, 381, 14), // team 0
3487 ui_button_info("MC_15", 506, 352, 514, 381, 15), // team 1
3488 ui_button_info("MC_16", 539, 346, 539, 381, 16), // kick
3489 ui_button_info("MC_17", 589, 346, 582, 381, 17), // close
3490 ui_button_info("MC_18", 1, 406, -1, -1, 18), // scroll list up
3491 ui_button_info("MC_19", 1, 447, -1, -1, 19), // scroll list down
3492 ui_button_info("MC_20", 499, 434, 436, 423, 20), // host options
3493 ui_button_info("MC_21", 534, 426, -1, -1, 21), // help
3494 ui_button_info("MC_22", 534, 452, -1, -1, 22), // options
3495 ui_button_info("MC_23", 571, 426, 572, 413, 23), // commit
3498 ui_button_info("2_MC_00", 51, 207, 61, 253, 0), // show all missions
3499 ui_button_info("2_MC_01", 122, 207, 124, 253, 1), // show coop missions
3500 ui_button_info("2_MC_02", 193, 207, 194, 253, 2), // show team missions
3501 ui_button_info("2_MC_03", 263, 207, 261, 253, 3), // show dogfight missions
3502 ui_button_info("2_MC_04", 639, 207, 479, 218, 4), // pxo mission refresh
3503 ui_button_info("2_MC_05", 907, 197, 748, 216, 5), // pilot info
3504 ui_button_info("2_MC_06", 1, 258, -1, -1, 6), // scroll mission info up
3505 ui_button_info("2_MC_08", 1, 487, -1, -1, 8), // scroll mission info down
3506 ui_button_info("2_MC_09", 981, 256, -1, -1, 9), // scroll players up
3507 ui_button_info("2_MC_10", 981, 323, -1, -1, 10), // scroll players down
3508 ui_button_info("2_MC_11", 35, 554, 46, 601, 11), // mission filter
3509 ui_button_info("2_MC_12", 166, 554, 174, 601, 12), // campaign filter
3510 ui_button_info("2_MC_13", 628, 545, 559, 582, 13), // cancel
3511 ui_button_info("2_MC_14", 756, 564, 772, 610, 14), // team 0
3512 ui_button_info("2_MC_15", 810, 564, 826, 610, 15), // team 1
3513 ui_button_info("2_MC_16", 862, 554, 872, 610, 16), // kick
3514 ui_button_info("2_MC_17", 943, 554, 949, 610, 17), // close
3515 ui_button_info("2_MC_18", 1, 649, -1, -1, 18), // scroll list up
3516 ui_button_info("2_MC_19", 1, 716, -1, -1, 19), // scroll list down
3517 ui_button_info("2_MC_20", 798, 695, 726, 667, 20), // host options
3518 ui_button_info("2_MC_21", 854, 681, -1, -1, 21), // help
3519 ui_button_info("2_MC_22", 854, 724, -1, -1, 22), // options
3520 ui_button_info("2_MC_23", 914, 681, 932, 667, 23), // commit
3524 #define MULTI_CREATE_NUM_TEXT 15
3525 UI_XSTR Multi_create_text[GR_NUM_RESOLUTIONS][MULTI_CREATE_NUM_BUTTONS] = {
3527 {"All", 1256, 36, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_ALL].button},
3528 {"Coop", 1257, 71, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_COOP].button},
3529 {"Team", 1258, 119, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_TEAM].button},
3530 {"Dogfight", 1259, 166, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_DOGFIGHT].button},
3531 {"Refresh Missions", 1260, 229, 130, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_PXO_REFRESH].button},
3532 {"Pilot Info", 1261, 467, 132, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_PILOT_INFO].button},
3533 {"Missions", 1262, 27, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_MISSION_FILTER].button},
3534 {"Campaigns", 1263, 110, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_CAMPAIGN_FILTER].button},
3535 {"Cancel", 387, 328, 364, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_CANCEL].button},
3536 {"1", 1264, 482, 381, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_TEAM0].button},
3537 {"2", 1265, 514, 381, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_TEAM1].button},
3538 {"Kick", 1266, 539, 381, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_KICK].button},
3539 {"Close", 1508, 582, 381, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_CLOSE].button},
3540 {"Host Options", 1267, 436, 423, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_HOST_OPTIONS].button},
3541 {"Commit", 1062, 572, 413, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_ACCEPT].button}
3544 {"All", 1256, 61, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_ALL].button},
3545 {"Coop", 1257, 124, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_COOP].button},
3546 {"Team", 1258, 194, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_TEAM].button},
3547 {"Dogfight", 1259, 261, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_DOGFIGHT].button},
3548 {"Refresh Missions", 1260, 501, 218, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_PXO_REFRESH].button},
3549 {"Pilot Info", 1261, 814, 216, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_PILOT_INFO].button},
3550 {"Missions", 1262, 46, 601, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_MISSION_FILTER].button},
3551 {"Campaigns", 1263, 174, 601, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_CAMPAIGN_FILTER].button},
3552 {"Cancel", 387, 559, 582, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_CANCEL].button},
3553 {"1", 1264, 772, 610, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_TEAM0].button},
3554 {"2", 1265, 826, 610, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_TEAM1].button},
3555 {"Kick", 1266, 872, 610, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_KICK].button},
3556 {"Close", 1508, 949, 610, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_CLOSE].button},
3557 {"Host Options", 1267, 755, 683, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_HOST_OPTIONS].button},
3558 {"Commit", 1062, 932, 667, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_ACCEPT].button}
3562 // squad war checkbox
3563 UI_CHECKBOX Multi_create_sw_checkbox;
3564 char *Multi_create_sw_checkbox_fname[GR_NUM_RESOLUTIONS] = {
3568 int Multi_create_sw_checkbox_coords[GR_NUM_RESOLUTIONS][2] = {
3576 int Multi_create_sw_checkbox_text[GR_NUM_RESOLUTIONS][2] = {
3585 // game information text areas
3586 int Mc_list_coords[GR_NUM_RESOLUTIONS][4] = {
3595 int Mc_players_coords[GR_NUM_RESOLUTIONS][4] = {
3604 int Mc_info_coords[GR_NUM_RESOLUTIONS][4] = {
3613 // mission icon stuff
3614 int Mc_icon_type_coords[GR_NUM_RESOLUTIONS][2] = {
3616 38, -2 // y is an offset
3619 61, -2 // y is an offset
3623 int Mc_icon_volition_coords[GR_NUM_RESOLUTIONS][2] = {
3625 61, -1 // y is an offset
3628 98, 1 // y is an offset
3632 int Mc_icon_silent_coords[GR_NUM_RESOLUTIONS][2] = {
3634 72, 0 // y is an offset
3637 115, 0 // y is an offset
3641 int Mc_icon_valid_coords[GR_NUM_RESOLUTIONS][2] = {
3643 91, 0 // y is an offset
3646 146, 0 // y is an offset
3650 // mission/campaign list column areas
3651 int Mc_column1_w[GR_NUM_RESOLUTIONS] = {
3656 int Mc_column2_w[GR_NUM_RESOLUTIONS] = {
3661 int Mc_column3_w[GR_NUM_RESOLUTIONS] = {
3666 int Mc_mission_name_x[GR_NUM_RESOLUTIONS] = {
3671 int Mc_mission_count_x[GR_NUM_RESOLUTIONS] = {
3676 int Mc_mission_fname_x[GR_NUM_RESOLUTIONS] = {
3681 int Mc_create_game_text[GR_NUM_RESOLUTIONS][2] = {
3682 {13, 116}, // GR_640
3683 {21, 186} // GR_1024
3686 int Mc_players_text[GR_NUM_RESOLUTIONS][2] = {
3687 {467, 150}, // GR_640
3688 {747, 240} // GR_1024
3691 int Mc_team_text[GR_NUM_RESOLUTIONS][2] = {
3692 {484, 342}, // GR_640
3693 {774, 547} // GR_1024
3696 int Mc_slider_coords[GR_NUM_RESOLUTIONS][4] = {
3698 3, 197, 13, 105 // GR_640
3701 5, 316, 20, 168 // GR_1024
3705 char *Mc_slider_bitmap[GR_NUM_RESOLUTIONS] = {
3710 // player list control thingie defs
3711 #define MULTI_CREATE_PLIST_MAX_DISPLAY 20
3712 int Multi_create_plist_select_flag; // flag indicating if we have a play selected
3713 short Multi_create_plist_select_id; // the net address of the currently selected player (for lookup)
3715 // master tracker details
3716 int Multi_create_frame_count; // framecount
3717 int Multi_create_mt_tried_login; // attempted to login this server on the MT
3719 // mission filter settings
3720 int Multi_create_filter; // what mode we're in
3722 // game/campaign list control defs
3723 int Multi_create_list_max_display[GR_NUM_RESOLUTIONS] = {
3729 int Multi_create_list_count; // number of items in listbox
3730 int Multi_create_list_mode; // 0 == mission mode, 1 == campaign mode
3731 int Multi_create_list_start; // where to start displaying from
3732 int Multi_create_list_select; // which item is currently highlighted
3733 int Multi_create_files_loaded;
3735 char Multi_create_files_array[MULTI_CREATE_MAX_LIST_ITEMS][MAX_FILENAME_LEN];
3737 int Multi_create_mission_count; // how many we have
3738 int Multi_create_campaign_count;
3739 multi_create_info Multi_create_mission_list[MULTI_CREATE_MAX_LIST_ITEMS];
3740 multi_create_info Multi_create_campaign_list[MULTI_CREATE_MAX_LIST_ITEMS];
3742 // use a pointer for the file list. Will point to either the missions or the campaigns
3743 multi_create_info *Multi_create_file_list;
3745 // LOCAL function definitions
3746 void multi_create_check_buttons();
3747 void multi_create_button_pressed(int n);
3748 void multi_create_init_as_server();
3749 void multi_create_init_as_client();
3750 void multi_create_do_netstuff();
3751 void multi_create_plist_scroll_up();
3752 void multi_create_plist_scroll_down();
3753 void multi_create_plist_process();
3754 void multi_create_plist_blit_normal();
3755 void multi_create_plist_blit_team();
3756 void multi_create_list_scroll_up();
3757 void multi_create_list_scroll_down();
3758 void multi_create_list_do();
3759 void multi_create_list_select_item(int n);
3760 void multi_create_list_blit_icons(int list_index, int y_start);
3761 void multi_create_accept_hit();
3762 void multi_create_draw_filter_buttons();
3763 void multi_create_set_selected_team(int team);
3764 short multi_create_get_mouse_id();
3765 int multi_create_ok_to_commit();
3766 int multi_create_verify_cds();
3767 void multi_create_refresh_pxo();
3768 void multi_create_sw_clicked();
3770 // since we can selectively filter out mission/campaign types we always need to map a selected index (which is relative
3771 // to the displayed list), to an absolute index (which is relative to the total file list - some of which may filtered out)
3772 void multi_create_select_to_filename(int select_index,char *filename);
3773 int multi_create_select_to_index(int select_index);
3775 int Multi_create_should_show_popup = 0;
3778 // sorting function to sort mission lists.. Basic sorting on mission name
3779 int multi_create_sort_func(const void *a, const void *b)
3781 multi_create_info *m1, *m2;
3783 m1 = (multi_create_info *)a;
3784 m2 = (multi_create_info *)b;
3786 return ( strcmp(m1->name, m2->name) );
3789 void multi_create_setup_list_data(int mode)
3791 int idx,should_sort,switched_modes;
3793 // set the current mode
3796 if((Multi_create_list_mode != mode) && (mode != -1)){
3797 Multi_create_list_mode = mode;
3800 // set up the list pointers
3801 if ( Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ) {
3802 Multi_create_file_list = Multi_create_mission_list;
3803 } else if ( Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ) {
3804 Multi_create_file_list = Multi_create_campaign_list;
3810 // get the mission count based upon the filter selected
3811 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
3812 switch(Multi_create_filter){
3813 case MISSION_TYPE_MULTI:
3814 Multi_create_list_count = Multi_create_mission_count;
3817 Multi_create_list_count = 0;
3818 // find all missions which match
3819 for(idx=0;idx<Multi_create_mission_count;idx++){
3820 if(Multi_create_mission_list[idx].flags & Multi_create_filter){
3821 Multi_create_list_count++;
3825 // if we switched modes and we have more than 0 items, sort them
3826 if(switched_modes && (Multi_create_list_count > 0)){
3831 } else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
3832 switch(Multi_create_filter){
3833 case MISSION_TYPE_MULTI:
3834 Multi_create_list_count = Multi_create_campaign_count;
3837 Multi_create_list_count = 0;
3838 // find all missions which match
3839 for(idx=0;idx<Multi_create_campaign_count;idx++){
3840 if(Multi_create_campaign_list[idx].flags & Multi_create_filter){
3841 Multi_create_list_count++;
3845 // if we switched modes and we have more than 0 items, sort them
3846 if(switched_modes && (Multi_create_list_count > 0)){
3853 // reset the list start and selected indices
3854 Multi_create_list_start = 0;
3855 Multi_create_list_select = -1;
3856 multi_create_list_select_item(Multi_create_list_start);
3858 // sort the list of missions if necessary
3860 qsort(Multi_create_file_list, Multi_create_list_count, sizeof(multi_create_info), multi_create_sort_func);
3864 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);
3867 void multi_create_game_init()
3872 // now make sure to initialze various netgame stuff based upon whether we're on a standalone or not
3873 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3874 multi_create_init_as_server();
3876 multi_create_init_as_client();
3879 // initialize the player list data
3880 Multi_create_plist_select_flag = 0;
3881 Multi_create_plist_select_id = -1;
3883 // create the interface window
3884 Multi_create_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
3885 Multi_create_window.set_mask_bmap(Multi_create_bitmap_mask_fname[gr_screen.res]);
3887 // load the background bitmap
3888 Multi_create_bitmap = bm_load(Multi_create_bitmap_fname[gr_screen.res]);
3889 if(Multi_create_bitmap < 0){
3890 // we failed to load the bitmap - this is very bad
3894 // close any previous existing instances of the chatbox and create a new one
3898 // load the help overlay
3899 help_overlay_load(MULTI_CREATE_OVERLAY);
3900 help_overlay_set_state(MULTI_CREATE_OVERLAY, 0);
3902 // initialize the common notification messaging
3903 multi_common_notify_init();
3905 // use the common interface palette
3906 multi_common_set_palette();
3908 // create the interface buttons
3909 for(idx=0; idx<MULTI_CREATE_NUM_BUTTONS; idx++){
3910 b = &Multi_create_buttons[gr_screen.res][idx];
3912 // create the object
3913 b->button.create(&Multi_create_window, "", b->x, b->y, 1, 1, ((idx == MC_SCROLL_LIST_UP) || (idx == MC_SCROLL_LIST_DOWN)), 1);
3915 // set the sound to play when highlighted
3916 b->button.set_highlight_action(common_play_highlight_sound);
3918 // set the ani for the button
3919 b->button.set_bmaps(b->filename);
3922 b->button.link_hotspot(b->hotspot);
3924 // some special case stuff for the pxo refresh button
3925 if(idx == MC_PXO_REFRESH){
3926 // if not a PXO game, or if I'm not a server disable and hide the button
3927 if(!MULTI_IS_TRACKER_GAME || !MULTIPLAYER_MASTER){
3929 b->button.disable();
3935 for(idx=0; idx<MULTI_CREATE_NUM_TEXT; idx++){
3936 Multi_create_window.add_XSTR(&Multi_create_text[gr_screen.res][idx]);
3939 // if this is a PXO game, enable the squadwar checkbox
3940 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);
3941 Multi_create_sw_checkbox.set_bmaps(Multi_create_sw_checkbox_fname[gr_screen.res], 6, 0);
3942 if(!MULTI_IS_TRACKER_GAME){
3943 Multi_create_sw_checkbox.hide();
3944 Multi_create_sw_checkbox.disable();
3948 // disable squad war button in demo
3949 Multi_create_sw_checkbox.hide();
3950 Multi_create_sw_checkbox.disable();
3953 // initialize the mission type filtering mode
3954 Multi_create_filter = MISSION_TYPE_MULTI;
3956 // initialize the list mode, and load in a list
3957 memset(Multi_create_mission_list, 0, sizeof(multi_create_info) * MULTI_CREATE_MAX_LIST_ITEMS);
3958 memset(Multi_create_campaign_list, 0, sizeof(multi_create_info) * MULTI_CREATE_MAX_LIST_ITEMS);
3959 for(idx=0; idx<MULTI_CREATE_MAX_LIST_ITEMS; idx++){
3960 Multi_create_mission_list[idx].valid_status = MVALID_STATUS_UNKNOWN;
3961 Multi_create_campaign_list[idx].valid_status = MVALID_STATUS_UNKNOWN;
3963 Multi_create_list_mode = MULTI_CREATE_SHOW_MISSIONS;
3964 Multi_create_list_start = -1;
3965 Multi_create_list_select = -1;
3966 Multi_create_list_count = 0;
3968 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);
3970 // create the player list select button
3971 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);
3972 Multi_create_player_select_button.hide();
3974 // create the mission/campaign list select button
3975 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);
3976 Multi_create_list_select_button.hide();
3978 // set hotkeys for a couple of things.
3979 Multi_create_buttons[gr_screen.res][MC_ACCEPT].button.set_hotkey(KEY_CTRLED+KEY_ENTER);
3981 // init some master tracker stuff
3982 Multi_create_frame_count = 0;
3983 Multi_create_mt_tried_login = 0;
3985 // remove campaign flags
3986 Game_mode &= ~(GM_CAMPAIGN_MODE);
3988 // send any pilots as appropriate
3989 multi_data_send_my_junk();
3990 Multi_create_file_list = Multi_create_mission_list;
3992 Multi_create_campaign_count = 0;
3993 Multi_create_mission_count = 0;
3994 Multi_create_files_loaded = 0;
3997 void multi_create_game_do()
4000 char *loading_str = XSTR("Loading", 1336);
4003 // set this if we want to show the pilot info popup
4004 Multi_create_should_show_popup = 0;
4006 // first thing is to load the files
4007 if ( !Multi_create_files_loaded ) {
4008 // if I am a client, send a list request to the server for the missions
4009 if ( MULTIPLAYER_CLIENT ) {
4010 send_mission_list_request( MISSION_LIST_REQUEST );
4014 loading_bitmap = bm_load(Multi_create_loading_fname[gr_screen.res]);
4016 // draw the background, etc
4018 GR_MAYBE_CLEAR_RES(Multi_create_bitmap);
4019 if(Multi_create_bitmap != -1){
4020 gr_set_bitmap(Multi_create_bitmap);
4024 if ( loading_bitmap > -1 ){
4025 gr_set_bitmap(loading_bitmap);
4027 gr_bitmap( Please_wait_coords[gr_screen.res][MC_X_COORD], Please_wait_coords[gr_screen.res][MC_Y_COORD] );
4029 // draw "Loading" on it
4030 gr_set_color_fast(&Color_normal);
4032 gr_get_string_size(&str_w, &str_h, loading_str);
4033 gr_string((gr_screen.max_w - str_w) / 2, (gr_screen.max_h - str_h) / 2, loading_str);
4038 multi_create_list_load_missions();
4039 multi_create_list_load_campaigns();
4041 // if this is a tracker game, validate missions
4042 if(MULTI_IS_TRACKER_GAME){
4043 multi_update_valid_missions();
4046 // update the file list
4047 multi_create_setup_list_data(MULTI_CREATE_SHOW_MISSIONS);
4050 // don't bother setting netgame state if ont the server
4051 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4052 Netgame.game_state = NETGAME_STATE_FORMING;
4053 send_netgame_update_packet();
4056 // if we're on the standalone we have to tell him that we're now in the host setup screen
4057 Net_player->state = NETPLAYER_STATE_HOST_SETUP;
4058 send_netplayer_update_packet();
4060 Multi_create_files_loaded = 1;
4063 int k = chatbox_process();
4064 k = Multi_create_window.process(k,0);
4067 // same as the cancel button
4069 if(help_overlay_active(MULTI_CREATE_OVERLAY)){
4070 help_overlay_set_state(MULTI_CREATE_OVERLAY,0);
4072 gamesnd_play_iface(SND_USER_SELECT);
4073 multi_quit_game(PROMPT_HOST);
4078 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
4079 help_overlay_set_state(MULTI_CREATE_OVERLAY, 0);
4082 // process any button clicks
4083 multi_create_check_buttons();
4085 // do any network related stuff
4086 multi_create_do_netstuff();
4088 // draw the background, etc
4090 GR_MAYBE_CLEAR_RES(Multi_create_bitmap);
4091 if(Multi_create_bitmap != -1){
4092 gr_set_bitmap(Multi_create_bitmap);
4096 // if we're not in team vs. team mode, don't draw the team buttons
4097 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
4098 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.hide();
4099 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.hide();
4100 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.disable();
4101 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.disable();
4103 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.enable();
4104 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.enable();
4105 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.unhide();
4106 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.unhide();
4109 // draw the window itself
4110 Multi_create_window.draw();
4112 gr_set_color_fast(&Color_normal);
4114 // draw Create Game text
4115 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));
4117 // draw players text
4118 gr_string(Mc_players_text[gr_screen.res][MC_X_COORD], Mc_players_text[gr_screen.res][MC_Y_COORD], XSTR("Players", 1269));
4120 // draw players text
4121 gr_string(Mc_team_text[gr_screen.res][MC_X_COORD], Mc_team_text[gr_screen.res][MC_Y_COORD], XSTR("Team", 1258));
4123 // process and display the player list
4124 // NOTE : this must be done before the buttons are checked to insure that a player hasn't dropped
4125 multi_create_plist_process();
4126 if(Netgame.type_flags & NG_TYPE_TEAM){
4127 multi_create_plist_blit_team();
4129 multi_create_plist_blit_normal();
4132 // process and display the game/campaign list
4133 multi_create_list_do();
4135 // draw the correct mission filter button
4136 multi_create_draw_filter_buttons();
4138 // display any text in the info area
4139 multi_common_render_text();
4141 // display any pending notification messages
4142 multi_common_notify_do();
4144 // force the correct mission/campaign button to light up
4145 if( Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ){
4146 Multi_create_buttons[gr_screen.res][MC_MISSION_FILTER].button.draw_forced(2);
4148 Multi_create_buttons[gr_screen.res][MC_CAMPAIGN_FILTER].button.draw_forced(2);
4151 // force draw the closed button if it is toggled on
4152 if(Netgame.flags & NG_FLAG_TEMP_CLOSED){
4153 Multi_create_buttons[gr_screen.res][MC_CLOSE].button.draw_forced(2);
4156 // process and show the chatbox thingie
4160 Multi_create_window.draw_tooltip();
4162 // display the voice status indicator
4163 multi_common_voice_display_status();
4165 // blit the help overlay if necessary
4166 help_overlay_maybe_blit(MULTI_CREATE_OVERLAY);
4169 if(MULTI_IS_TRACKER_GAME){
4170 if(Netgame.type_flags & NG_TYPE_SW){
4171 gr_set_color_fast(&Color_bright);
4173 gr_set_color_fast(&Color_normal);
4175 gr_string(Multi_create_sw_checkbox_text[gr_screen.res][0], Multi_create_sw_checkbox_text[gr_screen.res][1], "SquadWar");
4181 // if we're supposed to show the pilot info popup, do it now
4182 if(Multi_create_should_show_popup){
4183 // get the player index and address of the player item the mouse is currently over
4184 if(Multi_create_plist_select_flag){
4185 player_index = find_player_id(Multi_create_plist_select_id);
4186 if(player_index != -1){
4187 multi_pinfo_popup(&Net_players[player_index]);
4192 // increment the frame count
4193 Multi_create_frame_count++;
4196 void multi_create_game_close()
4198 // unload any bitmaps
4199 if(!bm_unload(Multi_create_bitmap)){
4200 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_create_bitmap_fname[gr_screen.res]));
4203 // unload the help overlay
4204 help_overlay_unload(MULTI_CREATE_OVERLAY);
4206 // destroy the chatbox
4209 // destroy the UI_WINDOW
4210 Multi_create_window.destroy();
4213 void multi_create_check_buttons()
4216 for(idx=0;idx<MULTI_CREATE_NUM_BUTTONS;idx++){
4217 // we only really need to check for one button pressed at a time, so we can break after
4219 if(Multi_create_buttons[gr_screen.res][idx].button.pressed()){
4220 multi_create_button_pressed(idx);
4225 // if the squad war checkbox was clicked
4226 if(Multi_create_sw_checkbox.changed()){
4227 multi_create_sw_clicked();
4231 void multi_create_button_pressed(int n)
4237 gamesnd_play_iface(SND_USER_SELECT);
4238 multi_quit_game(PROMPT_HOST);
4241 // if valid commit conditions have not been met
4242 if(!multi_create_ok_to_commit()){
4247 multi_create_accept_hit();
4252 if(!help_overlay_active(MULTI_CREATE_OVERLAY)){
4253 help_overlay_set_state(MULTI_CREATE_OVERLAY,1);
4255 help_overlay_set_state(MULTI_CREATE_OVERLAY,0);
4259 // scroll the info text box up
4260 case MC_SCROLL_INFO_UP:
4261 multi_common_scroll_text_up();
4264 // scroll the info text box down
4265 case MC_SCROLL_INFO_DOWN:
4266 multi_common_scroll_text_down();
4269 // scroll the player list up
4270 case MC_SCROLL_PLAYERS_UP:
4271 multi_create_plist_scroll_up();
4274 // scroll the player list down
4275 case MC_SCROLL_PLAYERS_DOWN:
4276 multi_create_plist_scroll_down();
4279 // scroll the game/campaign list up
4280 case MC_SCROLL_LIST_UP:
4281 multi_create_list_scroll_up();
4282 Multi_create_slider.forceUp(); // move slider up
4285 // scroll the game/campaign list down
4286 case MC_SCROLL_LIST_DOWN:
4287 multi_create_list_scroll_down();
4288 Multi_create_slider.forceDown(); // move slider down
4291 // go to the options screen
4293 gamesnd_play_iface(SND_USER_SELECT);
4294 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
4297 // show all missions
4299 if(Multi_create_filter != MISSION_TYPE_MULTI){
4300 gamesnd_play_iface(SND_USER_SELECT);
4301 Multi_create_filter = MISSION_TYPE_MULTI;
4302 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4304 gamesnd_play_iface(SND_GENERAL_FAIL);
4308 // show cooperative missions
4310 if(Multi_create_filter != MISSION_TYPE_MULTI_COOP){
4311 gamesnd_play_iface(SND_USER_SELECT);
4312 Multi_create_filter = MISSION_TYPE_MULTI_COOP;
4313 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4315 gamesnd_play_iface(SND_GENERAL_FAIL);
4319 // show team vs. team missions
4321 if(Multi_create_filter != MISSION_TYPE_MULTI_TEAMS){
4322 gamesnd_play_iface(SND_USER_SELECT);
4323 Multi_create_filter = MISSION_TYPE_MULTI_TEAMS;
4324 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4326 gamesnd_play_iface(SND_GENERAL_FAIL);
4330 // show dogfight missions
4331 case MC_SHOW_DOGFIGHT:
4332 if (Multi_create_filter != MISSION_TYPE_MULTI_DOGFIGHT){
4333 gamesnd_play_iface(SND_USER_SELECT);
4334 Multi_create_filter = MISSION_TYPE_MULTI_DOGFIGHT;
4335 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4337 gamesnd_play_iface(SND_GENERAL_FAIL);
4341 // toggle temporary netgame closed on/off
4343 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4344 Netgame.flags ^= NG_FLAG_TEMP_CLOSED;
4346 Netgame.options.flags |= MLO_FLAG_TEMP_CLOSED;
4347 multi_options_update_netgame();
4349 gamesnd_play_iface(SND_USER_SELECT);
4352 // kick the currently selected player (if possible)
4354 // lookup the player at the specified index
4355 if(Multi_create_plist_select_flag){
4356 idx = find_player_id(Multi_create_plist_select_id);
4357 // kick him - but don't ban him
4359 multi_kick_player(idx,0);
4364 // switch to individual mission mode and load in a list
4365 case MC_MISSION_FILTER:
4366 if(Multi_create_list_mode != MULTI_CREATE_SHOW_MISSIONS){
4367 Netgame.campaign_mode = MP_SINGLE;
4369 gamesnd_play_iface(SND_USER_SELECT);
4371 // update the file list
4372 multi_create_setup_list_data(MULTI_CREATE_SHOW_MISSIONS);
4374 gamesnd_play_iface(SND_GENERAL_FAIL);
4378 // switch to campaign mode and load in a list
4379 case MC_CAMPAIGN_FILTER:
4380 // switch off squad war
4381 Multi_create_sw_checkbox.set_state(0);
4382 Netgame.type_flags = NG_TYPE_COOP;
4384 if(Multi_create_list_mode != MULTI_CREATE_SHOW_CAMPAIGNS){
4385 Netgame.campaign_mode = MP_CAMPAIGN;
4387 gamesnd_play_iface(SND_USER_SELECT);
4389 // update the file list
4390 multi_create_setup_list_data(MULTI_CREATE_SHOW_CAMPAIGNS);
4392 gamesnd_play_iface(SND_GENERAL_FAIL);
4396 // attempt to set the selected player's team
4398 multi_create_set_selected_team(0);
4399 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4400 multi_team_send_update();
4404 // attempt to set the selected player's team
4406 multi_create_set_selected_team(1);
4407 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4408 multi_team_send_update();
4412 // popup the pilot info dialog for the currently selected pilot (will occur at the end of the frame)
4414 Multi_create_should_show_popup = 1;
4417 // go to the host options screen
4418 case MC_HOST_OPTIONS:
4419 gamesnd_play_iface(SND_USER_SELECT);
4420 gameseq_post_event(GS_EVENT_MULTI_HOST_OPTIONS);
4423 // refresh PXO file list
4424 case MC_PXO_REFRESH:
4425 if(!MULTI_IS_TRACKER_GAME){
4428 multi_create_refresh_pxo();
4432 gamesnd_play_iface(SND_GENERAL_FAIL);
4433 multi_common_add_notify(XSTR("Not implemented yet!",760));
4438 // do stuff like pinging servers, sending out requests, etc
4439 void multi_create_do_netstuff()
4443 // if not on a standalone
4444 void multi_create_init_as_server()
4446 // set me up as the host and master
4447 Net_player->flags |= (NETINFO_FLAG_AM_MASTER | NETINFO_FLAG_GAME_HOST);
4450 // if on a standalone
4451 void multi_create_init_as_client()
4453 Net_player->flags |= NETINFO_FLAG_GAME_HOST;
4456 // scroll up through the player list
4457 void multi_create_plist_scroll_up()
4459 gamesnd_play_iface(SND_GENERAL_FAIL);
4462 // scroll down through the player list
4463 void multi_create_plist_scroll_down()
4465 gamesnd_play_iface(SND_GENERAL_FAIL);
4468 void multi_create_plist_process()
4470 int test_count,idx,player_index;
4472 // first determine if there are 0 players in the game. This should never happen since the host is _always_ in the game
4474 for(idx=0;idx<MAX_PLAYERS;idx++){
4475 // count anyone except the standalone server (if applicable)
4476 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
4480 if(test_count <= 0){
4484 // if we had a selected item but that player has left, select myself instead
4485 if(Multi_create_plist_select_flag){
4486 player_index = find_player_id(Multi_create_plist_select_id);
4487 if(player_index == -1){
4488 Multi_create_plist_select_id = Net_player->player_id;
4491 Multi_create_plist_select_flag = 1;
4492 Multi_create_plist_select_id = Net_player->player_id;
4495 // if the player has clicked somewhere in the player list area
4496 if(Multi_create_player_select_button.pressed()){
4499 // get the player index and address of the player item the mouse is currently over
4500 player_id = multi_create_get_mouse_id();
4501 player_index = find_player_id(player_id);
4502 if(player_index != -1){
4503 Multi_create_plist_select_flag = 1;
4504 Multi_create_plist_select_id = player_id;
4509 void multi_create_plist_blit_normal()
4512 char str[CALLSIGN_LEN+5];
4513 int y_start = Mc_players_coords[gr_screen.res][MC_Y_COORD];
4516 // display all the players
4517 for(idx=0;idx<MAX_PLAYERS;idx++){
4518 // count anyone except the standalone server (if applicable)
4519 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
4523 // highlight him if he's the host
4524 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4525 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4526 gr_set_color_fast(&Color_text_active_hi);
4528 gr_set_color_fast(&Color_bright);
4531 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4532 gr_set_color_fast(&Color_text_active);
4534 gr_set_color_fast(&Color_text_normal);
4538 // optionally draw his CD status
4539 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4540 gr_set_bitmap(Multi_common_icons[MICON_CD]);
4541 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4543 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4546 // make sure the string will fit, then display it
4547 strcpy(str,Net_players[idx].player->callsign);
4548 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4549 strcat(str,XSTR("(O)",787)); // [[ Observer ]]
4551 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4552 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4559 void multi_create_plist_blit_team()
4562 char str[CALLSIGN_LEN+1];
4563 int y_start = Mc_players_coords[gr_screen.res][MC_Y_COORD];
4566 // display all the red players first
4567 for(idx=0;idx<MAX_PLAYERS;idx++){
4568 // count anyone except the standalone server (if applicable)
4569 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
4570 // reset total offset
4573 // highlight him if he's the host
4574 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4575 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4576 gr_set_color_fast(&Color_text_active_hi);
4578 // be sure to blit the correct team button
4579 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.draw_forced(2);
4581 gr_set_color_fast(&Color_bright);
4584 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4585 gr_set_color_fast(&Color_text_active);
4587 // be sure to blit the correct team button
4588 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.draw_forced(2);
4590 gr_set_color_fast(&Color_text_normal);
4594 // optionally draw his CD status
4595 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4596 gr_set_bitmap(Multi_common_icons[MICON_CD]);
4597 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4599 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4602 // blit the red team indicator
4603 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
4604 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
4605 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT]);
4606 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4608 total_offset += Multi_common_icon_dims[MICON_TEAM0_SELECT][0] + 1;
4611 if(Multi_common_icons[MICON_TEAM0] != -1){
4612 gr_set_bitmap(Multi_common_icons[MICON_TEAM0]);
4613 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4615 total_offset += Multi_common_icon_dims[MICON_TEAM0][0] + 1;
4619 // make sure the string will fit
4620 strcpy(str,Net_players[idx].player->callsign);
4621 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4622 strcat(str,XSTR("(O)",787));
4624 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4626 // display him in the correct half of the list depending on his team
4627 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4632 // display all the green players next
4633 for(idx=0;idx<MAX_PLAYERS;idx++){
4634 // count anyone except the standalone server (if applicable)
4635 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
4636 // reset total offset
4639 // highlight him if he's the host
4640 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4641 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4642 gr_set_color_fast(&Color_text_active_hi);
4644 // be sure to blit the correct team button
4645 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.draw_forced(2);
4647 gr_set_color_fast(&Color_bright);
4650 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4651 gr_set_color_fast(&Color_text_active);
4653 // be sure to blit the correct team button
4654 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.draw_forced(2);
4656 gr_set_color_fast(&Color_text_normal);
4660 // optionally draw his CD status
4661 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4662 gr_set_bitmap(Multi_common_icons[MICON_CD]);
4663 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4665 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4668 // blit the red team indicator
4669 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
4670 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
4671 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT]);
4672 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4674 total_offset += Multi_common_icon_dims[MICON_TEAM1_SELECT][0] + 1;
4677 if(Multi_common_icons[MICON_TEAM1] != -1){
4678 gr_set_bitmap(Multi_common_icons[MICON_TEAM1]);
4679 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4681 total_offset += Multi_common_icon_dims[MICON_TEAM1][0] + 1;
4685 // make sure the string will fit
4686 strcpy(str,Net_players[idx].player->callsign);
4687 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4688 strcat(str,XSTR("(O)",787));
4690 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4692 // display him in the correct half of the list depending on his team
4693 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4699 void multi_create_list_scroll_up()
4701 if(Multi_create_list_start > 0){
4702 Multi_create_list_start--;
4704 gamesnd_play_iface(SND_SCROLL);
4706 gamesnd_play_iface(SND_GENERAL_FAIL);
4710 void multi_create_list_scroll_down()
4712 if((Multi_create_list_count - Multi_create_list_start) > Multi_create_list_max_display[gr_screen.res]){
4713 Multi_create_list_start++;
4715 gamesnd_play_iface(SND_SCROLL);
4717 gamesnd_play_iface(SND_GENERAL_FAIL);
4721 // gets a list of multiplayer misisons
4722 void multi_create_list_load_missions()
4724 char *fname, mission_name[NAME_LENGTH+1];
4725 char wild_card[256];
4728 memset(wild_card, 0, 256);
4729 strcpy(wild_card, NOX("*"));
4730 strcat(wild_card, FS_MISSION_FILE_EXT);
4731 file_count = cf_get_file_list_preallocated(MULTI_CREATE_MAX_LIST_ITEMS, Multi_create_files_array, NULL, CF_TYPE_MISSIONS, wild_card);
4732 Multi_create_mission_count = 0;
4734 // maybe create a standalone dialog
4735 if(Game_mode & GM_STANDALONE_SERVER){
4736 std_create_gen_dialog("Loading missions");
4737 std_gen_set_text("Mission:", 1);
4740 for(idx = 0; idx < file_count; idx++){
4741 int flags,max_players;
4745 fname = Multi_create_files_array[idx];
4747 // tack on any necessary file extension
4748 filename = cf_add_ext( fname, FS_MISSION_FILE_EXT );
4750 // for multiplayer beta builds, only accept builtin missions
4751 #if defined(MULTIPLAYER_BETA_BUILD) || defined(FS2_DEMO)
4752 if(game_find_builtin_mission(filename) == NULL){
4755 #elif defined(PD_BUILD)
4756 if((game_find_builtin_mission(filename) == NULL) && !strstr(filename, "peterdrake")){
4761 if(Game_mode & GM_STANDALONE_SERVER){
4762 std_gen_set_text(filename, 2);
4765 flags = mission_parse_is_multi(filename, mission_name);
4767 // if the mission is a multiplayer mission, then add it to the mission list
4769 max_players = mission_parse_get_multi_mission_info( filename );
4770 m_respawn = The_mission.num_respawns;
4772 if ( Multi_create_mission_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
4773 multi_create_info *mcip;
4775 mcip = &Multi_create_mission_list[Multi_create_mission_count];
4776 strcpy(mcip->filename, filename );
4777 strcpy(mcip->name, mission_name );
4778 mcip->flags = flags;
4779 mcip->respawn = m_respawn;
4780 mcip->max_players = (ubyte)max_players;
4782 // get any additional information for possibly builtin missions
4783 fs_builtin_mission *fb = game_find_builtin_mission(filename);
4787 Multi_create_mission_count++;
4792 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);
4794 // maybe create a standalone dialog
4795 if(Game_mode & GM_STANDALONE_SERVER){
4796 std_destroy_gen_dialog();
4800 void multi_create_list_load_campaigns()
4803 int idx, file_count;
4804 int campaign_type,max_players;
4806 char wild_card[256];
4808 // maybe create a standalone dialog
4809 if(Game_mode & GM_STANDALONE_SERVER){
4810 std_create_gen_dialog("Loading campaigns");
4811 std_gen_set_text("Campaign:", 1);
4814 Multi_create_campaign_count = 0;
4815 memset(wild_card, 0, 256);
4816 strcpy(wild_card, NOX("*"));
4817 strcat(wild_card, FS_CAMPAIGN_FILE_EXT);
4818 file_count = cf_get_file_list_preallocated(MULTI_CREATE_MAX_LIST_ITEMS, Multi_create_files_array, NULL, CF_TYPE_MISSIONS, wild_card);
4819 for(idx = 0; idx < file_count; idx++){
4821 char *filename, name[NAME_LENGTH];
4823 fname = Multi_create_files_array[idx];
4825 // tack on any necessary file extension
4826 filename = cf_add_ext( fname, FS_CAMPAIGN_FILE_EXT );
4828 // for multiplayer beta builds, only accept builtin missions
4829 #if defined(MULTIPLAYER_BETA_BUILD) || defined(FS2_DEMO)
4830 if(game_find_builtin_mission(filename) == NULL){
4833 #elif defined(PD_BUILD)
4834 if((game_find_builtin_mission(filename) == NULL) && !strstr(filename, "peterdrake")){
4839 if(Game_mode & GM_STANDALONE_SERVER){
4840 std_gen_set_text(filename, 2);
4843 // if the campaign is a multiplayer campaign, then add the data to the campaign list items
4844 flags = mission_campaign_parse_is_multi( filename, name );
4845 if( flags != CAMPAIGN_TYPE_SINGLE && mission_campaign_get_info(filename,title,&campaign_type,&max_players)) {
4846 if ( Multi_create_campaign_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
4847 multi_create_info *mcip;
4849 mcip = &Multi_create_campaign_list[Multi_create_campaign_count];
4850 strcpy(mcip->filename, filename );
4851 strcpy(mcip->name, name );
4853 // setup various flags
4854 if ( flags == CAMPAIGN_TYPE_MULTI_COOP ){
4855 mcip->flags = MISSION_TYPE_MULTI_COOP | MISSION_TYPE_MULTI;
4856 } else if ( flags == CAMPAIGN_TYPE_MULTI_TEAMS ) {
4857 mcip->flags = MISSION_TYPE_MULTI_TEAMS | MISSION_TYPE_MULTI;
4859 Int3(); // bogus campaign multi type -- find allender
4862 // 0 respawns for campaign files (should be contained within the mission files themselves)
4865 // 0 max players for campaign files
4866 mcip->max_players = (unsigned char)max_players;
4868 // get any additional information for possibly builtin missions
4869 fs_builtin_mission *fb = game_find_builtin_mission(filename);
4873 Multi_create_campaign_count++;
4878 // maybe create a standalone dialog
4879 if(Game_mode & GM_STANDALONE_SERVER){
4880 std_destroy_gen_dialog();
4884 void multi_create_list_do()
4887 int start_index,stop_index;
4888 char selected_name[255];
4890 // bail early if there aren't any selectable items
4891 if(Multi_create_list_count == 0){
4895 // first check to see if the user has clicked on an item
4896 if(Multi_create_list_select_button.pressed()){
4898 Multi_create_list_select_button.get_mouse_pos(NULL,&y);
4901 // make sure we are selectedin valid indices
4902 if((item < Multi_create_list_max_display[gr_screen.res]) && (item >= 0)){
4903 item += Multi_create_list_start;
4905 if(item < Multi_create_list_count){
4906 multi_create_list_select_item(item);
4907 gamesnd_play_iface(SND_IFACE_MOUSE_CLICK);
4912 // bail early if we don't have a start position
4913 if(Multi_create_list_start == -1){
4917 // display the list of individual campaigns/missions
4919 int y_start = Mc_list_coords[gr_screen.res][MC_Y_COORD];
4921 start_index = multi_create_select_to_index(Multi_create_list_start);
4922 stop_index = Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ? Multi_create_mission_count : Multi_create_campaign_count;
4923 for(idx=start_index; idx<stop_index; idx++){
4924 // see if we should drop out
4925 if(count == Multi_create_list_max_display[gr_screen.res]){
4929 // see if we should filter out this mission
4930 if ( !(Multi_create_file_list[idx].flags & Multi_create_filter) ){
4934 // highlight the selected item
4935 multi_create_select_to_filename(Multi_create_list_select,selected_name);
4936 if(!strcmp(selected_name,Multi_create_file_list[idx].filename)){
4937 gr_set_color_fast(&Color_text_selected);
4939 gr_set_color_fast(&Color_text_normal);
4942 // draw the type icon
4943 multi_create_list_blit_icons(idx, y_start);
4945 // force fit the mission name string
4946 strcpy(selected_name,Multi_create_file_list[idx].name);
4947 gr_force_fit_string(selected_name,255,Mc_column1_w[gr_screen.res]);
4948 gr_string(Mc_mission_name_x[gr_screen.res],y_start,selected_name);
4950 // draw the max players if in mission mode
4951 sprintf(selected_name,"%d",(int)Multi_create_file_list[idx].max_players);
4952 gr_string(Mc_mission_count_x[gr_screen.res],y_start,selected_name);
4954 // force fit the mission filename string
4955 strcpy(selected_name,Multi_create_file_list[idx].filename);
4956 gr_force_fit_string(selected_name,255,Mc_column3_w[gr_screen.res]);
4957 gr_string(Mc_mission_fname_x[gr_screen.res],y_start,selected_name);
4964 // takes care of stuff like changing indices around and setting up the netgame structure
4965 void multi_create_list_select_item(int n)
4967 int abs_index,campaign_type,max_players;
4968 char title[NAME_LENGTH+1];
4969 netgame_info ng_temp;
4972 char *campaign_desc;
4974 // if not on the standalone server
4975 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4978 // on the standalone
4980 memset(&ng_temp,0,sizeof(netgame_info));
4984 if ( n != Multi_create_list_select ) {
4985 // check to see if this is a valid index, and bail if it is not
4986 abs_index = multi_create_select_to_index(n);
4987 if(abs_index == -1){
4991 Multi_create_list_select = n;
4993 // set the mission name
4994 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
4995 multi_create_select_to_filename(n,ng->mission_name);
4997 multi_create_select_to_filename(n,ng->campaign_name);
5000 // make sure the netgame type is properly set
5001 int old_type = Netgame.type_flags;
5002 abs_index = multi_create_select_to_index(n);
5003 if(abs_index != -1){
5004 if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_TEAMS){
5005 // if we're in squad war mode, leave it as squad war
5006 if(old_type & NG_TYPE_SW){
5007 ng->type_flags = NG_TYPE_SW;
5009 ng->type_flags = NG_TYPE_TVT;
5011 } else if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_COOP){
5012 ng->type_flags = NG_TYPE_COOP;
5013 } else if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_DOGFIGHT){
5014 ng->type_flags = NG_TYPE_DOGFIGHT;
5018 // if we're no longer in a TvT game, just uncheck the squadwar checkbox
5019 if(!(ng->type_flags & NG_TYPE_TEAM)){
5020 Multi_create_sw_checkbox.set_state(0);
5023 // if we switched from something else to team vs. team mode, do some special processing
5024 if((ng->type_flags & NG_TYPE_TEAM) && (ng->type_flags != old_type) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
5028 switch(Multi_create_list_mode){
5029 case MULTI_CREATE_SHOW_MISSIONS:
5030 // don't forget to update the info box window thingie
5031 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5032 ship_init(); // mwa -- 10/15/97. Call this function to reset number of ships in mission
5033 ng->max_players = mission_parse_get_multi_mission_info( ng->mission_name );
5035 Assert(ng->max_players > 0);
5036 strcpy(ng->title,The_mission.name);
5038 // set the information area text
5039 multi_common_set_text(The_mission.mission_desc);
5041 // if we're on the standalone, send a request for the description
5043 send_netgame_descript_packet(&Netgame.server_addr,0);
5044 multi_common_set_text("");
5047 // set the respawns as appropriate
5048 if(Netgame.options.respawn <= Multi_create_file_list[abs_index].respawn){
5049 ng->respawn = Netgame.options.respawn;
5050 nprintf(("Network","Using netgame options for respawn count (%d %d)\n",Netgame.options.respawn,Multi_create_file_list[abs_index].respawn));
5052 ng->respawn = Multi_create_file_list[abs_index].respawn;
5053 nprintf(("Network","Using mission settings for respawn count (%d %d)\n",Netgame.options.respawn,Multi_create_file_list[abs_index].respawn));
5056 case MULTI_CREATE_SHOW_CAMPAIGNS:
5057 // if not on the standalone server
5058 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5059 // get the campaign info
5060 memset(title,0,NAME_LENGTH+1);
5061 if(!mission_campaign_get_info(ng->campaign_name,title,&campaign_type,&max_players, &campaign_desc)) {
5062 memset(ng->campaign_name,0,NAME_LENGTH+1);
5063 ng->max_players = 0;
5065 // if we successfully got the # of players
5067 memset(ng->title,0,NAME_LENGTH+1);
5068 strcpy(ng->title,title);
5069 ng->max_players = max_players;
5072 nprintf(("Network","MC MAX PLAYERS : %d\n",ng->max_players));
5074 // set the information area text
5075 // multi_common_set_text(ng->title);
5076 multi_common_set_text(campaign_desc);
5078 // if on the standalone server, send a request for the description
5080 // no descriptions currently kept for campaigns
5083 // netgame respawns are always 0 for campaigns (until the first mission is loaded)
5088 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5090 send_netgame_update_packet();
5092 // update all machines about stuff like respawns, etc.
5093 multi_options_update_netgame();
5095 multi_options_update_mission(ng, Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ? 1 : 0);
5100 void multi_create_list_blit_icons(int list_index, int y_start)
5102 multi_create_info *mcip;
5103 fs_builtin_mission *fb;
5106 // get a pointer to the list item
5107 max_index = Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ? Multi_create_mission_count - 1 : Multi_create_campaign_count - 1;
5108 if((list_index < 0) || (list_index > max_index)){
5111 mcip = &Multi_create_file_list[list_index];
5113 // blit the multiplayer type icons
5114 if(mcip->flags & MISSION_TYPE_MULTI_COOP){
5115 if(Multi_common_icons[MICON_COOP] >= 0){
5116 gr_set_bitmap(Multi_common_icons[MICON_COOP]);
5117 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5119 } else if(mcip->flags & MISSION_TYPE_MULTI_TEAMS){
5120 if(Multi_common_icons[MICON_TVT] >= 0){
5121 gr_set_bitmap(Multi_common_icons[MICON_TVT]);
5122 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5124 } else if(mcip->flags & MISSION_TYPE_MULTI_DOGFIGHT){
5125 if(Multi_common_icons[MICON_DOGFIGHT] >= 0){
5126 gr_set_bitmap(Multi_common_icons[MICON_DOGFIGHT]);
5127 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5131 // if its a valid mission, blit the valid mission icon
5132 if(MULTI_IS_TRACKER_GAME && (mcip->valid_status == MVALID_STATUS_VALID)){
5133 if(Multi_common_icons[MICON_VALID] >= 0){
5134 gr_set_bitmap(Multi_common_icons[MICON_VALID]);
5135 gr_bitmap(Mc_icon_valid_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_valid_coords[gr_screen.res][MC_Y_COORD]);
5139 // now see if its a builtin mission
5140 fb = game_find_builtin_mission(mcip->filename);
5141 // if the mission is from volition, blit the volition icon
5142 if((fb != NULL) && (fb->flags & FSB_FROM_VOLITION)){
5143 if(Multi_common_icons[MICON_VOLITION] >= 0){
5144 gr_set_bitmap(Multi_common_icons[MICON_VOLITION]);
5145 gr_bitmap(Mc_icon_volition_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_volition_coords[gr_screen.res][MC_Y_COORD]);
5150 void multi_create_accept_hit()
5152 char selected_name[255];
5153 int start_campaign = 0;
5155 // make sure all players have finished joining
5156 if(!multi_netplayer_state_check(NETPLAYER_STATE_JOINED,1)){
5157 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("Please wait until all clients have finished joining",788));
5158 gamesnd_play_iface(SND_GENERAL_FAIL);
5161 gamesnd_play_iface(SND_COMMIT_PRESSED);
5164 // do single mission stuff
5165 switch(Multi_create_list_mode){
5166 case MULTI_CREATE_SHOW_MISSIONS:
5167 if(Multi_create_list_select != -1){
5168 // set the netgame mode
5169 Netgame.campaign_mode = MP_SINGLE;
5171 // setup various filenames and mission names
5172 multi_create_select_to_filename(Multi_create_list_select,selected_name);
5173 strncpy( Game_current_mission_filename, selected_name, MAX_FILENAME_LEN );
5174 strncpy(Netgame.mission_name,selected_name,MAX_FILENAME_LEN);
5177 ml_printf(NOX("Starting single mission %s, with %d players"), Game_current_mission_filename, multi_num_players());
5179 multi_common_add_notify(XSTR("No mission selected!",789));
5184 case MULTI_CREATE_SHOW_CAMPAIGNS:
5185 // do campaign related stuff
5186 if(Multi_create_list_select != -1){
5187 // set the netgame mode
5188 Netgame.campaign_mode = MP_CAMPAIGN;
5190 // start a campaign instead of a single mission
5191 multi_create_select_to_filename(Multi_create_list_select,selected_name);
5192 multi_campaign_start(selected_name);
5196 ml_printf(NOX("Starting campaign %s, with %d players"), selected_name, multi_num_players());
5198 multi_common_add_notify(XSTR("No campaign selected!",790));
5204 // if this is a team vs team situation, lock the players send a final team update
5205 if((Netgame.type_flags & NG_TYPE_TEAM) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
5206 multi_team_host_lock_all();
5207 multi_team_send_update();
5210 // if not on the standalone, move to the mission sync state which will take care of everything
5211 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5212 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
5213 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
5215 // otherwise tell the standalone to do so
5217 // when the standalone receives this, he'll do the mission syncing himself
5218 send_mission_sync_packet(MULTI_SYNC_PRE_BRIEFING,start_campaign);
5222 void multi_create_draw_filter_buttons()
5224 // highlight the correct filter button
5225 if ( Multi_create_filter == MISSION_TYPE_MULTI ){
5226 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL].button.draw_forced(2);
5227 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_COOP ) {
5228 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 1].button.draw_forced(2);
5229 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_TEAMS ) {
5230 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 2].button.draw_forced(2);
5231 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_DOGFIGHT ){
5232 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 3].button.draw_forced(2);
5238 void multi_create_set_selected_team(int team)
5242 // if we don't currently have a player selected, don't do anything
5243 if(!Multi_create_plist_select_flag){
5244 gamesnd_play_iface(SND_GENERAL_FAIL);
5247 gamesnd_play_iface(SND_USER_SELECT);
5249 // otherwise attempt to set the team for this guy
5250 player_index = find_player_id(Multi_create_plist_select_id);
5251 if(player_index != -1){
5252 multi_team_set_team(&Net_players[player_index],team);
5256 void multi_create_handle_join(net_player *pl)
5258 // for now just play a bloop sound
5259 gamesnd_play_iface(SND_ICON_DROP_ON_WING);
5262 // fill in net address of player the mouse is over, return player index (or -1 if none)
5263 short multi_create_get_mouse_id()
5265 // determine where he clicked (y pixel value)
5267 Multi_create_player_select_button.get_mouse_pos(NULL,&y);
5269 // select things a little differently if we're in team vs. team or non-team vs. team mode
5271 if(Netgame.type_flags & NG_TYPE_TEAM){
5272 int player_index = -1;
5274 // look through all of team red first
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 == 0)){
5279 // if this is the _nth_ guy
5287 // if we still haven't found him yet, look through the green team
5288 if(player_index == -1){
5289 for(idx=0;idx<MAX_PLAYERS;idx++){
5290 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
5292 // if this is the _nth_ guy
5301 if(player_index != -1){
5302 return Net_players[player_index].player_id;
5305 // select the nth active player if possible, disregarding the standalone server
5306 for(idx=0;idx<MAX_PLAYERS;idx++){
5307 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
5310 // if this is the _nth_ guy
5312 return Net_players[idx].player_id;
5321 void multi_create_select_to_filename(int select_index,char *filename)
5325 // look through the mission list
5326 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5327 for(idx=0;idx<Multi_create_mission_count;idx++){
5328 if(Multi_create_file_list[idx].flags & Multi_create_filter){
5332 // if we found the item
5333 if(select_index < 0){
5334 strcpy(filename,Multi_create_file_list[idx].filename);
5339 // look through the campaign list
5340 else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
5341 for(idx=0;idx<Multi_create_campaign_count;idx++){
5344 // if we found the item
5345 if(select_index < 0){
5346 strcpy(filename,Multi_create_file_list[idx].filename);
5352 strcpy(filename,"");
5355 int multi_create_select_to_index(int select_index)
5358 int lookup_index = 0;
5360 // look through the mission list
5361 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5362 for(idx=0;idx<Multi_create_mission_count;idx++){
5363 if(Multi_create_file_list[idx].flags & Multi_create_filter){
5367 // if we found the item
5368 if(select_index < lookup_index){
5373 // look through the campaign list
5374 else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
5375 for(idx=0;idx<Multi_create_campaign_count;idx++){
5378 // if we found the item
5379 if(select_index < 0){
5388 int multi_create_ok_to_commit()
5390 int player_count, observer_count, idx;
5391 int notify_of_hacked_ships_tbl = 0;
5392 int notify_of_hacked_weapons_tbl = 0;
5393 char err_string[255];
5397 // make sure we have a valid mission selected
5398 if(Multi_create_list_select < 0){
5402 // if this is not a valid mission, let the player know
5403 abs_index = multi_create_select_to_index(Multi_create_list_select);
5408 // if we're playing with a hacked ships.tbl (on PXO)
5409 notify_of_hacked_ships_tbl = 0;
5410 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5411 if(!Game_ships_tbl_valid){
5412 notify_of_hacked_ships_tbl = 1;
5415 if(Netgame.flags & NG_FLAG_HACKED_SHIPS_TBL){
5416 notify_of_hacked_ships_tbl = 1;
5419 if(!MULTI_IS_TRACKER_GAME){
5420 notify_of_hacked_ships_tbl = 0;
5422 if(notify_of_hacked_ships_tbl){
5423 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){
5428 // if we're playing with a hacked weapons.tbl (on PXO)
5429 notify_of_hacked_weapons_tbl = 0;
5430 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5431 if(!Game_weapons_tbl_valid){
5432 notify_of_hacked_weapons_tbl = 1;
5435 if(Netgame.flags & NG_FLAG_HACKED_WEAPONS_TBL){
5436 notify_of_hacked_weapons_tbl = 1;
5439 if(!MULTI_IS_TRACKER_GAME){
5440 notify_of_hacked_weapons_tbl = 0;
5442 if(notify_of_hacked_weapons_tbl){
5443 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){
5448 // if any of the players have hacked data
5450 for(idx=0; idx<MAX_PLAYERS; idx++){
5451 // look for hacked players
5452 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAXOR)){
5456 // message everyone - haha
5457 if(Net_players[idx].player != NULL){
5458 sprintf(err_string, "%s %s", Net_players[idx].player->callsign, XSTR("has hacked tables/data", 1271));
5460 sprintf(err_string, "%s", Net_players[idx].player->callsign, XSTR("has hacked tables/data", 1271));
5462 send_game_chat_packet(Net_player, err_string, MULTI_MSG_ALL, NULL, NULL, 1);
5465 // if we found a hacked set of data
5468 if(MULTI_IS_TRACKER_GAME){
5469 // don't allow squad war matches to continue
5470 if(Netgame.type_flags & NG_TYPE_SW){
5472 // if this is squad war, don't allow it to continue
5473 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));
5478 // otherwise, warn the players that stats will not saved
5480 // if this is squad war, don't allow it to continue
5481 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){
5486 // non-pxo, just give a notice
5488 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){
5494 // check to see that we don't have too many observers
5495 observer_count = multi_num_observers();
5496 if(observer_count > Netgame.options.max_observers){
5497 // print up the error string
5498 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);
5500 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, err_string);
5504 // check to see that we have a valid # of players for the the # of ships in the game
5505 player_count = multi_num_players();
5506 if(player_count > Netgame.max_players){
5507 // print up the error string
5508 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);
5510 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, err_string);
5514 // check to see if teams are assigned properly in a team vs. team situation
5515 if(Netgame.type_flags & NG_TYPE_TEAM){
5516 if(!multi_team_ok_to_commit()){
5517 gamesnd_play_iface(SND_GENERAL_FAIL);
5518 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("Teams and/or team captains are not assigned properly", 793));
5524 if(!multi_create_verify_cds()){
5525 gamesnd_play_iface(SND_GENERAL_FAIL);
5527 #ifdef MULTIPLAYER_BETA_BUILD
5528 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, "You need 1 CD for every player!");
5530 #ifdef DVD_MESSAGE_HACK
5531 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You need 1 DVD for every 4 players!", 794));
5533 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You need 1 CD for every 4 players!", 794));
5539 // if we're playing on the tracker
5540 if(MULTI_IS_TRACKER_GAME){
5541 #ifdef PXO_CHECK_VALID_MISSIONS
5542 if((Multi_create_file_list == Multi_create_mission_list) && (Multi_create_file_list[abs_index].valid_status != MVALID_STATUS_VALID)){
5543 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){
5550 if(!(Netgame.type_flags & NG_TYPE_SW)){
5551 // if he is playing by himself, tell him stats will not be accepted
5552 if(multi_num_players() == 1){
5553 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){
5566 int multi_create_verify_cds()
5568 int player_count = multi_num_players();
5572 // count how many cds we have
5574 for(idx=0;idx<MAX_PLAYERS;idx++){
5575 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAS_CD)){
5580 // for the beta, everyone must have a CD
5581 #ifdef MULTIPLAYER_BETA_BUILD
5582 if(multi_cd_count < player_count){
5586 // determine if we have enough
5587 float ratio = (float)player_count / (float)multi_cd_count;
5588 // greater than a 4 to 1 ratio
5594 // we meet the conditions
5598 // returns an index into Multi_create_mission_list
5599 int multi_create_lookup_mission(char *fname)
5603 for(idx=0; idx<Multi_create_mission_count; idx++){
5604 if(!stricmp(fname, Multi_create_mission_list[idx].filename)){
5609 // couldn't find the mission
5613 // returns an index into Multi_create_campaign_list
5614 int multi_create_lookup_campaign(char *fname)
5618 for(idx=0; idx<Multi_create_campaign_count; idx++){
5619 if(!stricmp(fname, Multi_create_campaign_list[idx].filename)){
5624 // couldn't find the campaign
5628 void multi_create_refresh_pxo()
5630 // delete mvalid.cfg if it exists
5631 cf_delete(MULTI_VALID_MISSION_FILE, CF_TYPE_DATA);
5633 // refresh missions from the tracker
5634 multi_update_valid_missions();
5637 void multi_create_sw_clicked()
5639 netgame_info ng_temp;
5642 int file_index = multi_create_select_to_index(Multi_create_list_select);
5644 // either a temporary netgame or the real one
5645 if(MULTIPLAYER_MASTER){
5652 // maybe switch squad war off
5653 if(!Multi_create_sw_checkbox.checked()){
5654 // if the mission selected is a coop mission, go back to coop mode
5655 Assert(file_index != -1);
5656 if(file_index == -1){
5657 ng->type_flags = NG_TYPE_COOP;
5659 if(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_TEAMS){
5660 ng->type_flags = NG_TYPE_TVT;
5661 } else if(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_DOGFIGHT){
5662 ng->type_flags = NG_TYPE_DOGFIGHT;
5664 ng->type_flags = NG_TYPE_COOP;
5667 // switch squad war on
5669 Assert(file_index != -1);
5670 if((file_index == -1) || !(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_TEAMS)){
5671 Multi_create_sw_checkbox.set_state(0);
5673 // at this point we know its safe to switch squad war on
5674 ng->type_flags = NG_TYPE_SW;
5679 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5681 send_netgame_update_packet();
5683 // update all machines about stuff like respawns, etc.
5684 multi_options_update_netgame();
5686 // on the standalone
5688 // standalone will take care of polling the usertracker
5689 multi_options_update_mission(ng, Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ? 1 : 0);
5694 // -------------------------------------------------------------------------------------------------------------
5696 // MULTIPLAYER HOST OPTIONS SCREEN
5699 #define MULTI_HO_NUM_BUTTONS 12
5700 #define MULTI_HO_NUM_RADIO_BUTTONS 10
5704 #define MULTI_HO_PALETTE "InterfacePalette"
5706 static char *Multi_ho_bitmap_fname[GR_NUM_RESOLUTIONS] = {
5707 "MultiHost", // GR_640
5708 "2_MultiHost" // GR_1024
5711 static char *Multi_ho_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
5712 "MultiHost-M", // GR_640
5713 "2_MultiHost-M" // GR_1024
5717 UI_WINDOW Multi_ho_window; // the window object for the join screen
5718 UI_INPUTBOX Multi_ho_respawns; // the # of respawns allowed in the game
5719 UI_INPUTBOX Multi_ho_time_limit; // mission time limit
5720 UI_INPUTBOX Multi_ho_voice_wait; // wait time between tokens
5721 UI_INPUTBOX Multi_ho_kill_limit; // kill limit in a furball mission
5722 UI_INPUTBOX Multi_ho_obs; // # of observers we'll allow
5723 int Multi_ho_bitmap; // the background bitmap
5725 // constants for coordinate lookup
5726 #define MULTI_HO_X_COORD 0
5727 #define MULTI_HO_Y_COORD 1
5728 #define MULTI_HO_W_COORD 2
5729 #define MULTI_HO_H_COORD 3
5730 #define MULTI_HO_TEXT_X_COORD 4
5731 #define MULTI_HO_TEXT_Y_COORD 5
5734 #define MULTI_HO_MSG_RANK 0 // highest ranking players can do messaging
5735 #define MULTI_HO_MSG_LEADER 1 // wing/team leaders can do messaging
5736 #define MULTI_HO_MSG_ANY 2 // any player can do messaging
5737 #define MULTI_HO_MSG_HOST 3 // only the host can do messaging
5738 #define MULTI_HO_END_RANK 4 // highest rank can and host can end mission
5739 #define MULTI_HO_END_LEADER 5 // wing/team leaders and host can end the mission
5740 #define MULTI_HO_END_ANY 6 // any player can end the mission
5741 #define MULTI_HO_END_HOST 7 // only host can end the mission
5742 #define MULTI_HO_VOICE_ON 8 // voice toggled on
5743 #define MULTI_HO_VOICE_OFF 9 // voice toggled off
5744 #define MULTI_HO_HOST_MODIFIES 10 // only the host or team captains can modify ships/weapons in briefing
5745 #define MULTI_HO_ACCEPT 11 // accept button
5747 ui_button_info Multi_ho_buttons[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_BUTTONS] = {
5749 // who is allowed to message
5750 ui_button_info("MH_00", 3, 160, 46, 166, 0), // highest rank
5751 ui_button_info("MH_01", 3, 179, 46, 185, 1), // team/wing leader
5752 ui_button_info("MH_02", 3, 196, 46, 203, 2), // any
5753 ui_button_info("MH_03", 3, 214, 46, 220, 3), // host
5755 // who is allowed to end the mission
5756 ui_button_info("MH_04", 3, 257, 46, 265, 4), // highest rank
5757 ui_button_info("MH_05", 3, 276, 46, 283, 5), // team/wing leader
5758 ui_button_info("MH_06", 3, 294, 46, 300, 6), // any
5759 ui_button_info("MH_07", 3, 311, 46, 317, 7), // host
5761 // voice on/off button
5762 ui_button_info("MH_09", 542, 158, 545, 185, 9),
5763 ui_button_info("MH_10", 598, 158, 604, 185, 10),
5765 // host modifies ships
5766 ui_button_info("MH_13", 542, 377, 437, 363, 13),
5769 ui_button_info("MH_14", 572, 428, 580, 414, 14),
5772 // who is allowed to message
5773 ui_button_info("2_MH_00", 5, 256, 73, 269, 0), // highest rank
5774 ui_button_info("2_MH_01", 5, 286, 73, 297, 1), // team/wing leader
5775 ui_button_info("2_MH_02", 5, 314, 73, 325, 2), // any
5776 ui_button_info("2_MH_03", 5, 341, 73, 352, 3), // host
5778 // who is allowed to end the mission
5779 ui_button_info("2_MH_04", 5, 412, 73, 425, 4), // highest rank
5780 ui_button_info("2_MH_05", 5, 442, 73, 452, 5), // team/wing leader
5781 ui_button_info("2_MH_06", 5, 470, 73, 480, 6), // any
5782 ui_button_info("2_MH_07", 5, 497, 73, 508, 7), // host
5784 // voice on/off button
5785 ui_button_info("2_MH_09", 867, 253, 872, 296, 9),
5786 ui_button_info("2_MH_10", 957, 253, 966, 296, 10),
5788 // host modifies ships
5789 ui_button_info("2_MH_13", 867, 603, 784, 581, 13),
5792 ui_button_info("2_MH_14", 916, 685, 925, 665, 14),
5795 UI_XSTR Multi_ho_text[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_BUTTONS] = {
5797 {"Highest rank", 1280, 46, 166, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_RANK].button},
5798 {"Team / wing-leader", 1281, 46, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_LEADER].button},
5799 {"Any", 1282, 46, 203, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_ANY].button},
5800 {"Host", 1283, 46, 220, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_HOST].button},
5801 {"Highest rank", 1280, 46, 265, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_RANK].button},
5802 {"Team / wing-leader", 1281, 46, 283, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_LEADER].button},
5803 {"Any", 1282, 46, 300, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_ANY].button},
5804 {"Host", 1283, 46, 317, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_HOST].button},
5805 {"On", 1285, 545, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_VOICE_ON].button},
5806 {"Off", 1286, 604, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_VOICE_OFF].button},
5807 {"Host modifies ships", 1287, 437, 363, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_HOST_MODIFIES].button},
5808 {"Exit", 1417, 572, 418, UI_XSTR_COLOR_PINK, -1, &Multi_ho_buttons[0][MULTI_HO_ACCEPT].button},
5811 {"Highest rank", 1280, 62, 269, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_RANK].button},
5812 {"Team / wing-leader", 1281, 62, 297, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_LEADER].button},
5813 {"Any", 1282, 62, 325, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_ANY].button},
5814 {"Host", 1283, 62, 352, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_HOST].button},
5815 {"Highest rank", 1280, 62, 425, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_RANK].button},
5816 {"Team / wing-leader", 1281, 62, 452, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_LEADER].button},
5817 {"Any", 1282, 62, 480, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_ANY].button},
5818 {"Host", 1283, 62, 508, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_HOST].button},
5819 {"On", 1285, 877, 294, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_VOICE_ON].button},
5820 {"Off", 1286, 967, 293, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_VOICE_OFF].button},
5821 {"Host modifies ships", 1287, 869, 589, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_HOST_MODIFIES].button},
5822 {"Exit", 1417, 953, 672, UI_XSTR_COLOR_PINK, -1, &Multi_ho_buttons[1][MULTI_HO_ACCEPT].button},
5826 // radio button controls
5827 #define MULTI_HO_NUM_RADIO_GROUPS 3
5828 #define MULTI_HO_MSG_GROUP 0 // group dealing with squadmate messaging
5829 #define MULTI_HO_END_GROUP 1 // group dealing with ending the mission
5830 #define MULTI_HO_VOICE_GROUP 2 // group dealing with voice stuff
5831 int Multi_ho_radio_groups[MULTI_HO_NUM_RADIO_GROUPS] = { // currently selected button in the radio button group
5834 int Multi_ho_radio_info[MULTI_HO_NUM_RADIO_BUTTONS][3] = { // info related to each of the radio buttons themselves
5835 // { group #, value, button id# }
5836 {0, 0, 0}, // highest ranking players can do messaging
5837 {0, 1, 1}, // wing/team leaders can do messaging
5838 {0, 2, 2}, // any player can do messaging
5839 {0, 3, 3}, // only host can do messaging
5840 {1, 0, 4}, // highest rank and host can end the mission
5841 {1, 1, 5}, // team/wing leader can end the mission
5842 {1, 2, 6}, // any player can end the mission
5843 {1, 3, 7}, // only the host can end the mission
5844 {2, 0, 8}, // voice toggled on
5845 {2, 1, 9} // voice toggled off
5849 #define MULTI_HO_NUM_SLIDERS 3
5850 #define MULTI_HO_SLIDER_VOICE_QOS 0 // voice quality of sound
5851 #define MULTI_HO_SLIDER_VOICE_DUR 1 // max duration of voice recording
5852 #define MULTI_HO_SLIDER_SKILL 2 // skill level
5859 UI_DOT_SLIDER_NEW slider; // because we have a class inside this struct, we need the constructor below..
5861 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){}
5863 ho_sliders Multi_ho_sliders[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_SLIDERS] = {
5865 ho_sliders("MH_11", 428, 214, 437, 199, 11, 19, 10), // voice qos
5866 ho_sliders("MH_12", 428, 261, 437, 246, 12, 19, 10), // voice duration
5867 ho_sliders("MH_08", 237, 454, 230, 411, 8, 36, 5), // skill level
5870 ho_sliders("2_MH_11", 684, 343, 690, 323, 11, 32, 10), // voice qos
5871 ho_sliders("2_MH_12", 685, 418, 837, 468, 12, 32, 10), // voice duration
5872 ho_sliders("2_MH_08", 379, 727, 369, 663, 8, 60, 5), // skill level
5876 int Multi_ho_mission_respawn;
5878 int Multi_ho_host_modifies;
5880 // whether or not any of the inputboxes on this screen had focus last frame
5881 int Multi_ho_lastframe_input = 0;
5883 // game information text areas
5886 #define MULTI_HO_NUM_TITLES 14
5887 UI_XSTR Multi_ho_titles[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_TITLES] = {
5889 { "AI Orders", 1289, 32, 144, UI_XSTR_COLOR_GREEN, -1, NULL },
5890 { "End Mission", 1290, 32, 242, UI_XSTR_COLOR_GREEN, -1, NULL },
5891 { "Time Limit", 1291, 32, 347, UI_XSTR_COLOR_GREEN, -1, NULL },
5892 { "Min", 1292, 74, 362, UI_XSTR_COLOR_GREEN, -1, NULL },
5893 { "Respawn Limit", 1288, 32, 378, UI_XSTR_COLOR_GREEN, -1, NULL },
5894 { "Kill Limit", 1293, 32, 409, UI_XSTR_COLOR_GREEN, -1, NULL },
5895 { "Observers", 1294, 32, 441, UI_XSTR_COLOR_GREEN, -1, NULL },
5896 { "Skill Level", 1284, 230, 411, UI_XSTR_COLOR_GREEN, -1, NULL },
5897 { "Voice Transmission", 1295, 437, 144, UI_XSTR_COLOR_GREEN, -1, NULL },
5898 { "Voice Quality", 1296, 437, 199, UI_XSTR_COLOR_GREEN, -1, NULL },
5899 { "Message Duration", 1297, 437, 246, UI_XSTR_COLOR_GREEN, -1, NULL },
5900 { "sec", 1522, 523, 292, UI_XSTR_COLOR_GREEN, -1, NULL },
5901 { "sec", 1523, 523, 332, UI_XSTR_COLOR_GREEN, -1, NULL },
5902 { "Voice Wait", 1298, 437, 313, UI_XSTR_COLOR_GREEN, -1, NULL },
5905 { "AI Orders", 1289, 48, 238, UI_XSTR_COLOR_GREEN, -1, NULL },
5906 { "End Mission", 1290, 48, 394, UI_XSTR_COLOR_GREEN, -1, NULL },
5907 { "Time Limit", 1291, 50, 568, UI_XSTR_COLOR_GREEN, -1, NULL },
5908 { "Min", 1292, 119, 581, UI_XSTR_COLOR_GREEN, -1, NULL },
5909 { "Respawn Limit", 1288, 50, 618, UI_XSTR_COLOR_GREEN, -1, NULL },
5910 { "Kill Limit", 1293, 50, 668, UI_XSTR_COLOR_GREEN, -1, NULL },
5911 { "Observers", 1294, 50, 718, UI_XSTR_COLOR_GREEN, -1, NULL },
5912 { "Skill Level", 1284, 398, 670, UI_XSTR_COLOR_GREEN, -1, NULL },
5913 { "Voice Transmission", 1295, 869, 239, UI_XSTR_COLOR_GREEN, -1, NULL },
5914 { "Voice Quality", 1296, 690, 331, UI_XSTR_COLOR_GREEN, -1, NULL },
5915 { "Message Duration", 1297, 690, 405, UI_XSTR_COLOR_GREEN, -1, NULL },
5916 { "sec", 1522, 837, 467, UI_XSTR_COLOR_GREEN, -1, NULL },
5917 { "sec", 1523, 837, 534, UI_XSTR_COLOR_GREEN, -1, NULL },
5918 { "Voice Wait", 1298, 742, 510, UI_XSTR_COLOR_GREEN, -1, NULL },
5922 // mission time limit input box
5923 int Ho_time_coords[GR_NUM_RESOLUTIONS][4] = {
5932 // furball kill limit input box
5933 int Ho_kill_coords[GR_NUM_RESOLUTIONS][4] = {
5942 // voice recording duration text display area
5943 int Ho_vd_coords[GR_NUM_RESOLUTIONS][4] = {
5952 // voice token wait input box
5953 int Ho_vw_coords[GR_NUM_RESOLUTIONS][6] = {
5962 // observer count input box
5963 int Ho_obs_coords[GR_NUM_RESOLUTIONS][4] = {
5972 // skill text description area
5973 int Ho_st_coords[GR_NUM_RESOLUTIONS][4] = {
5982 // respawn input box
5983 int Ho_rsp_coords[GR_NUM_RESOLUTIONS][6] = {
5992 // respawn max text area
5993 int Ho_max_rsp_coords[GR_NUM_RESOLUTIONS][2] = {
6002 // maximum values for various input boxes (to notify user of overruns)
6003 #define MULTI_HO_MAX_TIME_LIMIT 500
6004 #define MULTI_HO_MAX_TOKEN_WAIT 5
6005 #define MULTI_HO_MAX_KILL_LIMIT 9999
6006 #define MULTI_HO_MAX_OBS 4
6008 // LOCAL function definitions
6009 void multi_ho_check_buttons();
6010 void multi_ho_button_pressed(int n);
6011 void multi_ho_draw_radio_groups();
6012 void multi_ho_accept_hit();
6013 void multi_ho_get_options();
6014 void multi_ho_apply_options();
6015 void multi_ho_display_record_time();
6016 int multi_ho_check_values();
6017 void multi_ho_check_focus();
6018 void multi_ho_blit_max_respawns();
6019 void multi_ho_display_skill_level();
6021 void multi_host_options_init()
6025 // create the interface window
6026 Multi_ho_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
6027 Multi_ho_window.set_mask_bmap(Multi_ho_bitmap_mask_fname[gr_screen.res]);
6029 // load the background bitmap
6030 Multi_ho_bitmap = bm_load(Multi_ho_bitmap_fname[gr_screen.res]);
6031 if(Multi_ho_bitmap < 0){
6032 // we failed to load the bitmap - this is very bad
6036 // initialize the common notification messaging
6037 multi_common_notify_init();
6039 // use the common interface palette
6040 multi_common_set_palette();
6042 // create the interface buttons
6043 for(idx=0;idx<MULTI_HO_NUM_BUTTONS;idx++){
6044 // create the object
6045 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);
6047 // set the sound to play when highlighted
6048 Multi_ho_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
6050 // set the ani for the button
6051 Multi_ho_buttons[gr_screen.res][idx].button.set_bmaps(Multi_ho_buttons[gr_screen.res][idx].filename);
6053 // set the hotspot, ignoring the skill level button
6054 Multi_ho_buttons[gr_screen.res][idx].button.link_hotspot(Multi_ho_buttons[gr_screen.res][idx].hotspot);
6057 Multi_ho_window.add_XSTR(&Multi_ho_text[gr_screen.res][idx]);
6061 for(idx=0; idx<MULTI_HO_NUM_TITLES; idx++){
6062 Multi_ho_window.add_XSTR(&Multi_ho_titles[gr_screen.res][idx]);
6065 // create the interface sliders
6066 for(idx=0; idx<MULTI_HO_NUM_SLIDERS; idx++){
6067 // create the object
6068 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);
6071 // create the respawn count input box
6072 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);
6073 // if we're in campaign mode, disable it
6074 if(Netgame.campaign_mode == MP_CAMPAIGN){
6075 Multi_ho_respawns.set_text(XSTR("NA",795)); // [[ Not applicable ]]
6076 Multi_ho_respawns.disable();
6078 Multi_ho_respawns.set_valid_chars("0123456789");
6081 // create the time limit input box
6082 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);
6083 Multi_ho_time_limit.set_valid_chars("-0123456789");
6085 // create the voice token wait input box
6086 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);
6087 Multi_ho_voice_wait.set_valid_chars("01243456789");
6089 // create the furball kill limit input box
6090 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);
6091 Multi_ho_kill_limit.set_valid_chars("0123456789");
6093 // create the observer limit input box
6094 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);
6095 Multi_ho_obs.set_valid_chars("01234");
6097 // load in the current netgame defaults
6098 multi_ho_get_options();
6100 // whether or not any of the inputboxes on this screen had focus last frame
6101 Multi_ho_lastframe_input = 0;
6103 // get the # of respawns for the currently selected mission (if any)
6104 if(Multi_create_list_select != -1){
6105 int abs_index = multi_create_select_to_index(Multi_create_list_select);
6107 // if he has a valid mission selected
6109 Multi_ho_mission_respawn = (int)Multi_create_file_list[abs_index].respawn;
6111 Multi_ho_mission_respawn = -1;
6114 Multi_ho_mission_respawn = -1;
6118 void multi_ho_update_sliders()
6120 // game skill slider
6121 if (Game_skill_level != Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos) {
6122 if ( !(Netgame.type_flags & NG_TYPE_TEAM) ){
6123 Game_skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6124 gamesnd_play_iface(SND_USER_SELECT);
6126 Game_skill_level = NUM_SKILL_LEVELS / 2;
6130 // get the voice qos options
6131 if (Netgame.options.voice_qos != (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1)) {
6132 Netgame.options.voice_qos = (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1);
6133 gamesnd_play_iface(SND_USER_SELECT);
6136 // get the voice duration options
6137 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)) {
6138 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);
6139 gamesnd_play_iface(SND_USER_SELECT);
6144 void multi_host_options_do()
6149 k = Multi_ho_window.process();
6152 // process any keypresses
6155 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
6158 case KEY_CTRLED + KEY_ENTER :
6159 gamesnd_play_iface(SND_COMMIT_PRESSED);
6160 multi_ho_accept_hit();
6164 // process any button clicks
6165 multi_ho_check_buttons();
6167 // update the sliders
6168 multi_ho_update_sliders();
6170 // make sure that the chatbox inputbox and any inputbox on this screen are mutually exclusive in terms of focus
6171 multi_ho_check_focus();
6173 // draw the background, etc
6175 GR_MAYBE_CLEAR_RES(Multi_ho_bitmap);
6176 if(Multi_ho_bitmap != -1){
6177 gr_set_bitmap(Multi_ho_bitmap);
6180 Multi_ho_window.draw();
6182 // draw all the radio buttons properly
6183 multi_ho_draw_radio_groups();
6185 // display any pending notification messages
6186 multi_common_notify_do();
6188 // display the voice record time settings
6189 multi_ho_display_record_time();
6191 // maybe display the max # of respawns next to the respawn input box
6192 multi_ho_blit_max_respawns();
6194 // blit the proper skill level
6195 multi_ho_display_skill_level();
6197 // blit the "host modifies button"
6198 if(Multi_ho_host_modifies){
6199 Multi_ho_buttons[gr_screen.res][MULTI_HO_HOST_MODIFIES].button.draw_forced(2);
6202 // process and show the chatbox thingie
6206 Multi_ho_window.draw_tooltip();
6208 // display the voice status indicator
6209 multi_common_voice_display_status();
6215 void multi_host_options_close()
6217 // unload any bitmaps
6218 if(!bm_unload(Multi_ho_bitmap)){
6219 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_ho_bitmap_fname[gr_screen.res]));
6222 // destroy the UI_WINDOW
6223 Multi_ho_window.destroy();
6226 void multi_ho_check_buttons()
6229 for(idx=0;idx<MULTI_HO_NUM_BUTTONS;idx++){
6230 // we only really need to check for one button pressed at a time, so we can break after
6232 if(Multi_ho_buttons[gr_screen.res][idx].button.pressed()){
6233 multi_ho_button_pressed(idx);
6239 void multi_ho_button_pressed(int n)
6241 int radio_index,idx;
6242 int x_pixel,y_pixel;
6244 // get the pixel position of the click
6245 Multi_ho_buttons[gr_screen.res][n].button.get_mouse_pos(&x_pixel,&y_pixel);
6248 // clicked on the accept button
6249 case MULTI_HO_ACCEPT:
6250 gamesnd_play_iface(SND_COMMIT_PRESSED);
6251 multi_ho_accept_hit();
6254 // clicked on the host/captains only modify button
6255 case MULTI_HO_HOST_MODIFIES:
6256 // toggle it on or off
6257 Multi_ho_host_modifies = !Multi_ho_host_modifies;
6258 gamesnd_play_iface(SND_USER_SELECT);
6262 // look through the radio buttons and see which one this corresponds to
6264 for(idx=0;idx<MULTI_HO_NUM_RADIO_BUTTONS;idx++){
6265 if(Multi_ho_radio_info[idx][2] == n){
6270 Assert(radio_index != -1);
6272 // check to see if a radio button was pressed
6273 if(radio_index < MULTI_HO_NUM_RADIO_BUTTONS){
6274 // see if this value is already picked for this radio group
6275 if(Multi_ho_radio_groups[Multi_ho_radio_info[radio_index][0]] != Multi_ho_radio_info[radio_index][1]){
6276 gamesnd_play_iface(SND_USER_SELECT);
6277 Multi_ho_radio_groups[Multi_ho_radio_info[radio_index][0]] = Multi_ho_radio_info[radio_index][1];
6279 gamesnd_play_iface(SND_GENERAL_FAIL);
6284 void multi_ho_draw_radio_groups()
6288 // go through each item and draw it if it is the selected button in its respective group
6289 for(idx=0;idx<MULTI_HO_NUM_RADIO_BUTTONS;idx++){
6290 /// if this button is the currently selected one in its group
6291 if(Multi_ho_radio_info[idx][1] == Multi_ho_radio_groups[Multi_ho_radio_info[idx][0]]){
6292 Multi_ho_buttons[gr_screen.res][Multi_ho_radio_info[idx][2]].button.draw_forced(2);
6297 void multi_ho_accept_hit()
6301 // check the values in the input boxes
6302 if(!multi_ho_check_values()){
6306 // zero out the netgame flags
6309 // set default options
6310 Netgame.options.flags = (MSO_FLAG_INGAME_XFER | MSO_FLAG_ACCEPT_PIX);
6312 // set the squadmate messaging flags
6313 switch(Multi_ho_radio_groups[MULTI_HO_MSG_GROUP]){
6315 Netgame.options.squad_set = MSO_SQUAD_RANK;
6318 Netgame.options.squad_set = MSO_SQUAD_LEADER;
6321 Netgame.options.squad_set = MSO_SQUAD_ANY;
6324 Netgame.options.squad_set = MSO_SQUAD_HOST;
6330 // set the end mission flags
6331 switch(Multi_ho_radio_groups[MULTI_HO_END_GROUP]){
6333 Netgame.options.endgame_set = MSO_END_RANK;
6336 Netgame.options.endgame_set = MSO_END_LEADER;
6339 Netgame.options.endgame_set = MSO_END_ANY;
6342 Netgame.options.endgame_set = MSO_END_HOST;
6348 // set the voice toggle
6349 switch(Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP]){
6351 Netgame.options.flags &= ~(MSO_FLAG_NO_VOICE);
6354 Netgame.options.flags |= MSO_FLAG_NO_VOICE;
6360 // get the voice qos options
6361 Netgame.options.voice_qos = (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1);
6363 // get the voice duration options
6364 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);
6366 // set the skill level. If in team vs. team mode, preserve the old setting before saving
6367 // the pilot file. I'll bet that this doesn't work though because the pilot file gets
6368 // written in a bunch of locations....sigh.
6369 if ( !(Netgame.type_flags & NG_TYPE_TEAM) ){
6370 Game_skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6372 Game_skill_level = NUM_SKILL_LEVELS / 2;
6375 // set the netgame respawn count
6376 // maybe warn the user that respawns will not be used for a campaign mission
6377 if(Netgame.campaign_mode == MP_SINGLE){
6378 Multi_ho_respawns.get_text(resp_str);
6379 uint temp_respawn = (uint)atoi(resp_str);
6380 // if he currently has no mission selected, let the user set any # of respawns
6381 if((int)temp_respawn > Multi_ho_mission_respawn){
6382 if(Multi_ho_mission_respawn == -1){
6383 Netgame.respawn = temp_respawn;
6384 Netgame.options.respawn = temp_respawn;
6386 // this should have been taken care of by the interface code
6391 Netgame.options.respawn = temp_respawn;
6392 Netgame.respawn = temp_respawn;
6396 // get the mission time limit
6397 Multi_ho_time_limit.get_text(resp_str);
6398 int temp_time = atoi(resp_str);
6400 Netgame.options.mission_time_limit = fl2f(-1.0f);
6401 } else if(temp_time > MULTI_HO_MAX_TIME_LIMIT){
6404 Netgame.options.mission_time_limit = fl2f(60.0f * (float)temp_time);
6407 // get observer count options
6408 Multi_ho_obs.get_text(resp_str);
6409 int temp_obs = atoi(resp_str);
6410 if(temp_obs > MULTI_HO_MAX_OBS){
6413 Netgame.options.max_observers = (ubyte)temp_obs;
6415 // get the furball kill limit
6416 Multi_ho_kill_limit.get_text(resp_str);
6417 int temp_kills = atoi(resp_str);
6418 if(temp_kills > MULTI_HO_MAX_KILL_LIMIT){
6421 Netgame.options.kill_limit = temp_kills;
6423 // get the token wait limit
6424 Multi_ho_voice_wait.get_text(resp_str);
6425 int temp_wait = atoi(resp_str);
6426 if(temp_wait > MULTI_HO_MAX_TOKEN_WAIT){
6429 Netgame.options.voice_token_wait = (temp_wait * 1000);
6431 // set the netgame option
6432 Netgame.options.skill_level = (ubyte)Game_skill_level;
6434 // get whether we're in host/captains only modify mode
6435 Netgame.options.flags &= ~(MSO_FLAG_SS_LEADERS);
6436 if(Multi_ho_host_modifies){
6437 Netgame.options.flags |= MSO_FLAG_SS_LEADERS;
6440 // store these values locally
6441 memcpy(&Player->m_local_options,&Net_player->p_info.options,sizeof(multi_local_options));
6442 memcpy(&Player->m_server_options,&Netgame.options,sizeof(multi_server_options));
6443 write_pilot_file(Player);
6445 // apply any changes in settings (notify everyone of voice qos changes, etc)
6446 multi_ho_apply_options();
6448 // move back to the create game screen
6449 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
6452 void multi_ho_get_options()
6456 // set the squadmate messaging buttons
6457 switch(Netgame.options.squad_set){
6458 case MSO_SQUAD_RANK :
6459 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 0;
6461 case MSO_SQUAD_LEADER:
6462 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 1;
6465 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 2;
6467 case MSO_SQUAD_HOST:
6468 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 3;
6474 // set the mission end buttons
6475 switch(Netgame.options.endgame_set){
6477 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 0;
6479 case MSO_END_LEADER:
6480 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 1;
6483 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 2;
6486 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 3;
6492 // set the voice toggle buttons
6493 if(Netgame.options.flags & MSO_FLAG_NO_VOICE){
6494 Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP] = 1;
6496 Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP] = 0;
6499 // get the voice qos options
6500 Assert((Netgame.options.voice_qos >= 1) && (Netgame.options.voice_qos <= 10));
6501 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos = (Netgame.options.voice_qos - 1);
6503 // get the voice duration options
6504 Assert((Netgame.options.voice_record_time > 0) && (Netgame.options.voice_record_time <= MULTI_VOICE_MAX_TIME));
6505 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos = ((int)((float)Netgame.options.voice_record_time / 500.0f)) - 1;
6507 // get the current skill level
6508 Assert((Game_skill_level >= 0) && (Game_skill_level < NUM_SKILL_LEVELS));
6509 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos = Game_skill_level;
6511 // get the # of observers
6512 memset(resp_str,0,10);
6513 sprintf(resp_str,"%d",Netgame.options.max_observers);
6514 Multi_ho_obs.set_text(resp_str);
6516 // set the respawn count
6517 if(Netgame.campaign_mode == MP_SINGLE){
6518 memset(resp_str,0,10);
6519 sprintf(resp_str,"%d",Netgame.respawn);
6520 Multi_ho_respawns.set_text(resp_str);
6523 // set the mission time limit
6524 memset(resp_str,0,10);
6525 float tl = f2fl(Netgame.options.mission_time_limit);
6526 sprintf(resp_str,"%d",(int)(tl / 60.0f));
6527 Multi_ho_time_limit.set_text(resp_str);
6529 // set the furball kill limit
6530 memset(resp_str,0,10);
6531 sprintf(resp_str,"%d",Netgame.options.kill_limit);
6532 Multi_ho_kill_limit.set_text(resp_str);
6534 // set the token wait time
6535 memset(resp_str,0,10);
6536 sprintf(resp_str,"%d",Netgame.options.voice_token_wait / 1000);
6537 Multi_ho_voice_wait.set_text(resp_str);
6539 // get whether we're in host/captains only modify mode
6540 if(Netgame.options.flags & MSO_FLAG_SS_LEADERS){
6541 Multi_ho_host_modifies = 1;
6543 Multi_ho_host_modifies = 0;
6547 void multi_ho_apply_options()
6549 // if the voice qos or duration has changed, apply the change
6550 multi_voice_maybe_update_vars(Netgame.options.voice_qos,Netgame.options.voice_record_time);
6552 // send an options update
6553 multi_options_update_netgame();
6556 // display the voice record time settings
6557 void multi_ho_display_record_time()
6560 int full_seconds, half_seconds;
6563 memset(time_str,0,30);
6566 full_seconds = (((Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 500) / 1000);
6568 // get the half-seconds
6569 half_seconds = ((((Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 500) % 1000) / 500) * 5;
6571 // format the string
6572 sprintf(time_str,"%d.%d",full_seconds,half_seconds);
6573 gr_set_color_fast(&Color_bright);
6574 gr_string(Ho_vd_coords[gr_screen.res][MULTI_HO_X_COORD],Ho_vd_coords[gr_screen.res][MULTI_HO_Y_COORD],time_str);
6577 int multi_ho_check_values()
6581 memset(val_txt,0,255);
6583 // check against respawn settings
6584 if(Multi_ho_mission_respawn != -1){
6585 Multi_ho_respawns.get_text(val_txt);
6586 // if the value is invalid, let the user know
6587 if(atoi(val_txt) > Multi_ho_mission_respawn){
6588 memset(val_txt,0,255);
6589 sprintf(val_txt,XSTR("Warning\nRespawn count in greater than mission specified max (%d)",796),Multi_ho_mission_respawn);
6590 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6595 // check against mission time limit max
6596 Multi_ho_time_limit.get_text(val_txt);
6597 // if the value is invalid, force it to be valid
6598 if(atoi(val_txt) > MULTI_HO_MAX_TIME_LIMIT){
6599 memset(val_txt,0,255);
6600 sprintf(val_txt,XSTR("Warning\nMission time limit is greater than max allowed (%d)",797),MULTI_HO_MAX_TIME_LIMIT);
6601 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6605 // check against max observer limit
6606 Multi_ho_obs.get_text(val_txt);
6607 // if the value is invalid, force it to be valid
6608 if(atoi(val_txt) > MULTI_HO_MAX_OBS){
6609 memset(val_txt,0,255);
6610 sprintf(val_txt,XSTR("Warning\nObserver count is greater than max allowed (%d)",798),MULTI_HO_MAX_OBS);
6611 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6615 // check against furball kill limit
6616 Multi_ho_kill_limit.get_text(val_txt);
6617 // if the value is invalid, force it to be valid
6618 if(atoi(val_txt) > MULTI_HO_MAX_KILL_LIMIT){
6619 memset(val_txt,0,255);
6620 sprintf(val_txt,XSTR("Warning\nMission kill limit is greater than max allowed (%d)",799),MULTI_HO_MAX_KILL_LIMIT);
6621 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6625 // check against the token wait limit
6626 Multi_ho_voice_wait.get_text(val_txt);
6627 if(atoi(val_txt) > MULTI_HO_MAX_TOKEN_WAIT){
6628 memset(val_txt,0,255);
6629 sprintf(val_txt,XSTR("Warning\nvoice wait time is greater than max allowed (%d)",800),MULTI_HO_MAX_TOKEN_WAIT);
6630 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6634 // all values are valid
6638 void multi_ho_check_focus()
6640 // if an inputbox has been pressed (hit enter), lose its focus
6641 if (Multi_ho_respawns.pressed() || Multi_ho_time_limit.pressed() || Multi_ho_voice_wait.pressed() || Multi_ho_kill_limit.pressed() || Multi_ho_obs.pressed()) {
6642 Multi_ho_respawns.clear_focus();
6643 Multi_ho_time_limit.clear_focus();
6644 Multi_ho_voice_wait.clear_focus();
6645 Multi_ho_kill_limit.clear_focus();
6646 Multi_ho_obs.clear_focus();
6647 gamesnd_play_iface(SND_COMMIT_PRESSED);
6648 chatbox_set_focus();
6649 Multi_ho_lastframe_input = 0;
6651 } else if(!Multi_ho_lastframe_input) {
6652 // if we didn't have focus last frame
6653 if(Multi_ho_respawns.has_focus() || Multi_ho_time_limit.has_focus() || Multi_ho_kill_limit.has_focus() || Multi_ho_voice_wait.has_focus() ){
6654 chatbox_lose_focus();
6656 Multi_ho_lastframe_input = 1;
6659 // if we _did_ have focus last frame
6661 // if we no longer have focus on any of the input boxes, set the focus on the chatbox
6662 if(!Multi_ho_respawns.has_focus() && !Multi_ho_time_limit.has_focus() && !Multi_ho_kill_limit.has_focus() && !Multi_ho_voice_wait.has_focus() && !chatbox_has_focus()){
6663 chatbox_set_focus();
6665 // if the chatbox now has focus, clear all focus from our inputboxes
6666 else if (chatbox_has_focus()) {
6667 Multi_ho_respawns.clear_focus();
6668 Multi_ho_time_limit.clear_focus();
6669 Multi_ho_kill_limit.clear_focus();
6670 Multi_ho_voice_wait.clear_focus();
6672 Multi_ho_lastframe_input = 0;
6677 void multi_ho_blit_max_respawns()
6681 // if we're in campaign mode, do nothing
6682 if(Netgame.campaign_mode == MP_CAMPAIGN){
6686 // otherwise blit the max as specified by the current mission file
6687 sprintf(string,"(%d)",Multi_ho_mission_respawn);
6688 gr_set_color_fast(&Color_normal);
6689 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);
6692 void multi_ho_display_skill_level()
6694 int skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6697 Assert((skill_level >= 0) && (skill_level < NUM_SKILL_LEVELS));
6698 if((skill_level < 0) || (skill_level >= NUM_SKILL_LEVELS)){
6702 gr_set_color_fast(&Color_bright);
6703 gr_string(Ho_st_coords[gr_screen.res][0], Ho_st_coords[gr_screen.res][1], Skill_level_names(skill_level, 1));
6706 // -------------------------------------------------------------------------------------------------------------
6708 // MULTIPLAYER JOIN SCREEN
6711 #define MULTI_JW_NUM_BUTTONS 8
6715 #define MULTI_JW_PALETTE "InterfacePalette"
6717 static char *Multi_jw_bitmap_fname[GR_NUM_RESOLUTIONS] = {
6718 "MultiJoinWait", // GR_640
6719 "2_MultiJoinWait" // GR_1024
6722 static char *Multi_jw_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
6723 "MultiJoinWait-M", // GR_640
6724 "2_MultiJoinWait-M" // GR_1024
6730 #define MJW_SCROLL_PLAYERS_UP 0
6731 #define MJW_SCROLL_PLAYERS_DOWN 1
6734 #define MJW_PILOT_INFO 4
6735 #define MJW_SCROLL_INFO_UP 5
6736 #define MJW_SCROLL_INFO_DOWN 6
6737 #define MJW_CANCEL 7
6739 UI_WINDOW Multi_jw_window; // the window object for the join screen
6740 int Multi_jw_bitmap; // the background bitmap
6742 // constants for coordinate lookup
6743 #define MJW_X_COORD 0
6744 #define MJW_Y_COORD 1
6745 #define MJW_W_COORD 2
6746 #define MJW_H_COORD 3
6748 ui_button_info Multi_jw_buttons[GR_NUM_RESOLUTIONS][MULTI_JW_NUM_BUTTONS] = {
6750 ui_button_info("MJW_00", 1, 24, -1, -1, 0),
6751 ui_button_info("MJW_01", 1, 66, -1, -1, 1),
6752 ui_button_info("MJW_02", 30, 244, 20, 272, 2),
6753 ui_button_info("MJW_03", 84, 244, 73, 272, 3),
6754 ui_button_info("MJW_04", 139, 242, 134, 272, 4),
6755 ui_button_info("MJW_05", 1, 406, -1, -1, 5),
6756 ui_button_info("MJW_06", 1, 447, -1, -1, 6),
6757 ui_button_info("MJW_07", 577, 428, 570, 414, 7),
6760 ui_button_info("2_MJW_00", 2, 38, -1, -1, 0),
6761 ui_button_info("2_MJW_01", 2, 106, -1, -1, 1),
6762 ui_button_info("2_MJW_02", 48, 390, 47, 435, 2),
6763 ui_button_info("2_MJW_03", 134, 390, 133, 435, 3),
6764 ui_button_info("2_MJW_04", 223, 388, 225, 435, 4),
6765 ui_button_info("2_MJW_05", 2, 649, -1, -1, 5),
6766 ui_button_info("2_MJW_06", 2, 715, -1, -1, 6),
6767 ui_button_info("2_MJW_07", 923, 685, 931, 667, 7),
6771 #define MULTI_JW_NUM_TEXT 7
6773 UI_XSTR Multi_jw_text[GR_NUM_RESOLUTIONS][MULTI_JW_NUM_TEXT] = {
6775 { "Team 1", 1308, 20, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_TEAM0].button },
6776 { "Team 2", 1309, 73, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_TEAM1].button },
6777 { "Pilot", 1310, 134, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_PILOT_INFO].button },
6778 { "Info", 1311, 134, 283, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_PILOT_INFO].button },
6779 { "Cancel", 387, 570, 414, UI_XSTR_COLOR_PINK, -1, &Multi_jw_buttons[0][MJW_CANCEL].button },
6780 { "Players", 1269, 38, 8, UI_XSTR_COLOR_GREEN, -1, NULL },
6781 { "Choose Team", 1312, 27, 231, UI_XSTR_COLOR_GREEN, -1, NULL },
6784 { "Team 1", 1308, 47, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_TEAM0].button },
6785 { "Team 2", 1309, 133, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_TEAM1].button },
6786 { "Pilot", 1310, 225, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_PILOT_INFO].button },
6787 { "Info", 1311, 225, 446, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_PILOT_INFO].button },
6788 { "Cancel", 387, 931, 667, UI_XSTR_COLOR_PINK, -1, &Multi_jw_buttons[1][MJW_CANCEL].button },
6789 { "Players", 1269, 165, 12, UI_XSTR_COLOR_GREEN, -1, NULL },
6790 { "Choose Team", 1312, 45, 373, UI_XSTR_COLOR_GREEN, -1, NULL },
6794 int Mjw_players_coords[GR_NUM_RESOLUTIONS][4] = {
6803 int Mjw_mission_name_coords[GR_NUM_RESOLUTIONS][2] = {
6812 // squad war checkbox
6813 UI_CHECKBOX Multi_jw_sw_checkbox;
6814 char *Multi_jw_sw_checkbox_fname[GR_NUM_RESOLUTIONS] = {
6818 int Multi_jw_sw_checkbox_coords[GR_NUM_RESOLUTIONS][2] = {
6826 int Multi_jw_sw_checkbox_text[GR_NUM_RESOLUTIONS][2] = {
6836 // player list control thingie defs
6837 #define MULTI_JW_PLIST_MAX_DISPLAY 19
6838 int Multi_jw_plist_select_flag; // indicates whether we currently have a selected player
6839 short Multi_jw_plist_select_id; // id of the current selected player
6840 UI_BUTTON Multi_jw_plist_select_button; // for selecting a player
6842 int Multi_jw_should_show_popup = 0;
6844 // LOCAL function definitions
6845 void multi_jw_check_buttons();
6846 void multi_jw_button_pressed(int n);
6847 void multi_jw_do_netstuff();
6848 void multi_jw_scroll_players_up();
6849 void multi_jw_scroll_players_down();
6850 void multi_jw_plist_process();
6851 void multi_jw_plist_blit_normal();
6852 void multi_jw_plist_blit_team();
6853 short multi_jw_get_mouse_id();
6855 void multi_game_client_setup_init()
6859 // create the interface window
6860 Multi_jw_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
6861 Multi_jw_window.set_mask_bmap(Multi_jw_bitmap_mask_fname[gr_screen.res]);
6863 // load the background bitmap
6864 Multi_jw_bitmap = bm_load(Multi_jw_bitmap_fname[gr_screen.res]);
6865 if(Multi_jw_bitmap < 0){
6866 // we failed to load the bitmap - this is very bad
6870 // initialize the player list data
6871 Multi_jw_plist_select_flag = 0;
6872 Multi_jw_plist_select_id = -1;
6874 // kill any old instances of the chatbox and create a new one
6876 chatbox_create(CHATBOX_FLAG_BIG | CHATBOX_FLAG_DRAW_BOX | CHATBOX_FLAG_BUTTONS);
6878 // initialize the common notification messaging
6879 multi_common_notify_init();
6881 // initialize the common mission info display area.
6882 multi_common_set_text("");
6884 // use the common interface palette
6885 multi_common_set_palette();
6887 // create the interface buttons
6888 for(idx=0; idx<MULTI_JW_NUM_BUTTONS; idx++){
6889 // create the object
6890 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);
6892 // set the sound to play when highlighted
6893 Multi_jw_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
6895 // set the ani for the button
6896 Multi_jw_buttons[gr_screen.res][idx].button.set_bmaps(Multi_jw_buttons[gr_screen.res][idx].filename);
6899 Multi_jw_buttons[gr_screen.res][idx].button.link_hotspot(Multi_jw_buttons[gr_screen.res][idx].hotspot);
6902 // if this is a PXO game, enable the squadwar checkbox
6903 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);
6904 Multi_jw_sw_checkbox.set_bmaps(Multi_jw_sw_checkbox_fname[gr_screen.res], 6, 0);
6905 Multi_jw_sw_checkbox.disable();
6906 if(!MULTI_IS_TRACKER_GAME){
6907 Multi_jw_sw_checkbox.hide();
6911 for(idx=0; idx<MULTI_JW_NUM_TEXT; idx++){
6912 Multi_jw_window.add_XSTR(&Multi_jw_text[gr_screen.res][idx]);
6915 // create the player select list button and hide it
6916 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);
6917 Multi_jw_plist_select_button.hide();
6920 Multi_jw_buttons[gr_screen.res][MJW_CANCEL].button.set_hotkey(KEY_ESC);
6922 // remove campaign flags
6923 Game_mode &= ~(GM_CAMPAIGN_MODE);
6925 // tell the server we have finished joining
6926 Net_player->state = NETPLAYER_STATE_JOINED;
6927 send_netplayer_update_packet();
6930 ml_printf(NOX("Joined netgame %s"), Netgame.name);
6932 // send any appropriate files
6933 multi_data_send_my_junk();
6936 void multi_game_client_setup_do_frame()
6939 int k = chatbox_process();
6940 char mission_text[255];
6941 k = Multi_jw_window.process(k,0);
6943 Multi_jw_should_show_popup = 0;
6945 // process any button clicks
6946 multi_jw_check_buttons();
6948 // do any network related stuff
6949 multi_jw_do_netstuff();
6951 // draw the background, etc
6953 GR_MAYBE_CLEAR_RES(Multi_jw_bitmap);
6954 if(Multi_jw_bitmap != -1){
6955 gr_set_bitmap(Multi_jw_bitmap);
6959 // if we're not in team vs. team mode, don't draw the team buttons
6960 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
6961 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.hide();
6962 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.hide();
6963 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.disable();
6964 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.disable();
6966 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.enable();
6967 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.enable();
6968 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.unhide();
6969 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.unhide();
6972 if(MULTI_IS_TRACKER_GAME){
6973 // maybe check the squadwar button
6974 if(Netgame.type_flags & NG_TYPE_SW){
6975 Multi_jw_sw_checkbox.set_state(1);
6976 gr_set_color_fast(&Color_bright);
6978 Multi_jw_sw_checkbox.set_state(0);
6979 gr_set_color_fast(&Color_normal);
6982 gr_string(Multi_jw_sw_checkbox_text[gr_screen.res][0], Multi_jw_sw_checkbox_text[gr_screen.res][1], "SquadWar");
6985 // draw the UI window
6986 Multi_jw_window.draw();
6988 // process and display the player list
6989 // NOTE : this must be done before the buttons are checked to insure that a player hasn't dropped
6990 multi_jw_plist_process();
6991 if(Netgame.type_flags & NG_TYPE_TEAM){
6992 multi_jw_plist_blit_team();
6994 multi_jw_plist_blit_normal();
6997 // display any text in the info area
6998 multi_common_render_text();
7000 // display any pending notification messages
7001 multi_common_notify_do();
7003 // blit the mission filename if possible
7004 if(Netgame.campaign_mode){
7005 if(strlen(Netgame.campaign_name) > 0){
7006 strcpy(mission_text,Netgame.campaign_name);
7008 if(strlen(Netgame.title) > 0){
7009 strcat(mission_text,", ");
7010 strcat(mission_text,Netgame.title);
7013 gr_set_color_fast(&Color_bright_white);
7014 gr_string(Mjw_mission_name_coords[gr_screen.res][MJW_X_COORD],Mjw_mission_name_coords[gr_screen.res][MJW_Y_COORD],mission_text);
7017 if(strlen(Netgame.mission_name) > 0){
7018 strcpy(mission_text,Netgame.mission_name);
7020 if(strlen(Netgame.title) > 0){
7021 strcat(mission_text,", ");
7022 strcat(mission_text,Netgame.title);
7025 gr_set_color_fast(&Color_bright_white);
7026 gr_string(Mjw_mission_name_coords[gr_screen.res][MJW_X_COORD],Mjw_mission_name_coords[gr_screen.res][MJW_Y_COORD],mission_text);
7030 // process and show the chatbox thingie
7034 Multi_jw_window.draw_tooltip();
7036 // display the voice status indicator
7037 multi_common_voice_display_status();
7042 // if we're supposed to be displaying a pilot info popup
7043 if(Multi_jw_should_show_popup){
7044 player_index = find_player_id(Multi_jw_plist_select_id);
7045 if(player_index != -1){
7046 multi_pinfo_popup(&Net_players[player_index]);
7051 void multi_game_client_setup_close()
7053 // unload any bitmaps
7054 if(!bm_unload(Multi_jw_bitmap)){
7055 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_jw_bitmap_fname[gr_screen.res]));
7058 // destroy the chatbox
7061 // destroy the UI_WINDOW
7062 Multi_jw_window.destroy();
7065 if(Netgame.game_state == NETGAME_STATE_MISSION_SYNC){
7066 gamesnd_play_iface(SND_COMMIT_PRESSED);
7071 void multi_jw_check_buttons()
7074 for(idx=0;idx<MULTI_JW_NUM_BUTTONS;idx++){
7075 // we only really need to check for one button pressed at a time, so we can break after
7077 if(Multi_jw_buttons[gr_screen.res][idx].button.pressed()){
7078 multi_jw_button_pressed(idx);
7084 void multi_jw_button_pressed(int n)
7088 gamesnd_play_iface(SND_USER_SELECT);
7089 multi_quit_game(PROMPT_CLIENT);
7091 case MJW_SCROLL_PLAYERS_UP:
7092 multi_jw_scroll_players_up();
7094 case MJW_SCROLL_PLAYERS_DOWN:
7095 multi_jw_scroll_players_down();
7097 case MJW_SCROLL_INFO_UP:
7098 multi_common_scroll_text_up();
7100 case MJW_SCROLL_INFO_DOWN:
7101 multi_common_scroll_text_down();
7104 // request to set myself to team 0
7106 gamesnd_play_iface(SND_USER_SELECT);
7107 multi_team_set_team(Net_player,0);
7110 // request to set myself to team 1
7112 gamesnd_play_iface(SND_USER_SELECT);
7113 multi_team_set_team(Net_player,1);
7117 case MJW_PILOT_INFO:
7118 Multi_jw_should_show_popup = 1;
7122 multi_common_add_notify(XSTR("Not implemented yet!",760));
7127 // do stuff like pinging servers, sending out requests, etc
7128 void multi_jw_do_netstuff()
7132 void multi_jw_scroll_players_up()
7134 gamesnd_play_iface(SND_GENERAL_FAIL);
7137 // scroll down through the player list
7138 void multi_jw_scroll_players_down()
7140 gamesnd_play_iface(SND_GENERAL_FAIL);
7143 void multi_jw_plist_process()
7145 int test_count,player_index,idx;
7147 // first determine if there are 0 players in the game. This should never happen since the host is _always_ in the game
7149 for(idx=0;idx<MAX_PLAYERS;idx++){
7150 // count anyone except the standalone server (if applicable)
7151 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7155 if(test_count <= 0){
7159 // if we had a selected item but that player has left, select myself instead
7160 if(Multi_jw_plist_select_flag){
7161 player_index = find_player_id(Multi_jw_plist_select_id);
7162 if(player_index == -1){
7163 Multi_jw_plist_select_id = Net_player->player_id;
7166 Multi_jw_plist_select_flag = 1;
7167 Multi_jw_plist_select_id = Net_player->player_id;
7170 // if the player has clicked somewhere in the player list area
7171 if(Multi_jw_plist_select_button.pressed()){
7175 player_id = multi_jw_get_mouse_id();
7176 player_index = find_player_id(player_id);
7177 if(player_index != -1){
7178 Multi_jw_plist_select_id = player_id;
7179 Multi_jw_plist_select_flag = 1;
7184 void multi_jw_plist_blit_normal()
7187 char str[CALLSIGN_LEN+1];
7188 int y_start = Mjw_players_coords[gr_screen.res][MJW_Y_COORD];
7191 // display all the players
7192 for(idx=0;idx<MAX_PLAYERS;idx++){
7193 // reset total offset
7196 // count anyone except the standalone server (if applicable)
7197 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7198 // highlight him if he's the host
7199 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7200 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7201 gr_set_color_fast(&Color_text_active_hi);
7203 gr_set_color_fast(&Color_bright);
7206 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7207 gr_set_color_fast(&Color_text_active);
7209 gr_set_color_fast(&Color_text_normal);
7213 // optionally draw his CD status
7214 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7215 gr_set_bitmap(Multi_common_icons[MICON_CD]);
7216 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7218 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7221 // make sure the string will fit, then display it
7222 strcpy(str,Net_players[idx].player->callsign);
7223 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
7226 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7227 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7234 void multi_jw_plist_blit_team()
7237 char str[CALLSIGN_LEN+1];
7238 int y_start = Mjw_players_coords[gr_screen.res][MJW_Y_COORD];
7241 // always blit the proper team button based on _my_ team status
7242 if(Net_player->p_info.team == 0){
7243 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.draw_forced(2);
7245 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.draw_forced(2);
7248 // display all the red players first
7249 for(idx=0;idx<MAX_PLAYERS;idx++){
7250 // reset total offset
7253 // count anyone except the standalone server (if applicable)
7254 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
7255 // highlight him if he's the host
7256 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7257 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7258 gr_set_color_fast(&Color_text_active_hi);
7260 gr_set_color_fast(&Color_bright);
7263 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7264 gr_set_color_fast(&Color_text_active);
7266 gr_set_color_fast(&Color_text_normal);
7270 // optionally draw his CD status
7271 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7272 gr_set_bitmap(Multi_common_icons[MICON_CD]);
7273 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7275 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7278 // blit the red team indicator
7279 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
7280 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
7281 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT]);
7282 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7284 total_offset += Multi_common_icon_dims[MICON_TEAM0_SELECT][0] + 1;
7287 if(Multi_common_icons[MICON_TEAM0] != -1){
7288 gr_set_bitmap(Multi_common_icons[MICON_TEAM0]);
7289 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7291 total_offset += Multi_common_icon_dims[MICON_TEAM0][0] + 1;
7295 // make sure the string will fit
7296 strcpy(str,Net_players[idx].player->callsign);
7297 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7299 // display him in the correct half of the list depending on his team
7300 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7305 // display all the green players next
7306 for(idx=0;idx<MAX_PLAYERS;idx++){
7307 // reset total offset
7310 // count anyone except the standalone server (if applicable)
7311 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
7312 // highlight him if he's the host
7313 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7314 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7315 gr_set_color_fast(&Color_text_active_hi);
7317 gr_set_color_fast(&Color_bright);
7320 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7321 gr_set_color_fast(&Color_text_active);
7323 gr_set_color_fast(&Color_text_normal);
7327 // optionally draw his CD status
7328 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7329 gr_set_bitmap(Multi_common_icons[MICON_CD]);
7330 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7332 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7335 // blit the red team indicator
7336 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
7337 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
7338 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT]);
7339 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7341 total_offset += Multi_common_icon_dims[MICON_TEAM1_SELECT][0] + 1;
7344 if(Multi_common_icons[MICON_TEAM1] != -1){
7345 gr_set_bitmap(Multi_common_icons[MICON_TEAM1]);
7346 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7348 total_offset += Multi_common_icon_dims[MICON_TEAM1][0] + 1;
7352 // make sure the string will fit
7353 strcpy(str,Net_players[idx].player->callsign);
7354 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
7357 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7359 // display him in the correct half of the list depending on his team
7360 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7366 void multi_jw_handle_join(net_player *pl)
7368 // for now just play a bloop sound
7369 gamesnd_play_iface(SND_ICON_DROP_ON_WING);
7372 short multi_jw_get_mouse_id()
7374 // determine where he clicked (y pixel value)
7376 Multi_jw_plist_select_button.get_mouse_pos(NULL,&y);
7378 // select things a little differently if we're in team vs. team or non-team vs. team mode
7380 if(Netgame.type_flags & NG_TYPE_TEAM){
7381 int player_index = -1;
7383 // look through all of team red first
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 == 0)){
7388 // if this is the _nth_ guy
7396 // if we still haven't found him yet, look through the green team
7397 if(player_index == -1){
7398 for(idx=0;idx<MAX_PLAYERS;idx++){
7399 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
7401 // if this is the _nth_ guy
7409 if(player_index != -1){
7410 return Net_players[idx].player_id;
7413 // select the nth active player if possible, disregarding the standalone server
7414 for(idx=0;idx<MAX_PLAYERS;idx++){
7415 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7418 // if this is the _nth_ guy
7420 return Net_players[idx].player_id;
7431 // -------------------------------------------------------------------------------------------------------------
7433 // MULTIPLAYER WAIT/SYNCH SCREEN
7436 #define MULTI_SYNC_HOST_COUNT 4 // host uses 4 buttons (and sometimes 5)
7437 #define MULTI_SYNC_CLIENT_COUNT 3 // client only uses 3 buttons
7439 char *Multi_sync_bitmap_fname[GR_NUM_RESOLUTIONS] = {
7440 "MultiSynch", // GR_640
7441 "2_MultiSynch" // GR_1024
7444 char *Multi_sync_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
7445 "MultiSynch-M", // GR_640
7446 "2_MultiSynch-M" // GR_1024
7451 // constants for coordinate lookup
7452 #define MS_X_COORD 0
7453 #define MS_Y_COORD 1
7454 #define MS_W_COORD 2
7455 #define MS_H_COORD 3
7457 UI_WINDOW Multi_sync_window; // the window object for the join screen
7458 int Multi_sync_button_count; // the # of buttons to use for this instance of mission sync
7459 int Multi_sync_bitmap; // the background bitmap
7462 #define MULTI_SYNC_NUM_BUTTONS 5
7463 #define MS_SCROLL_INFO_UP 0
7464 #define MS_SCROLL_INFO_DOWN 1
7468 ui_button_info Multi_sync_buttons[GR_NUM_RESOLUTIONS][MULTI_SYNC_NUM_BUTTONS] = {
7470 ui_button_info("MS_00", 1, 404, -1, -1, 0),
7471 ui_button_info("MS_01", 1, 446, -1, -1, 1),
7472 ui_button_info("MS_03", 518, 426, 519, 416, 3),
7473 ui_button_info("MS_02", 469, 426, 479, 416, 2),
7474 ui_button_info("MS_04", 571, 420, 577, 416, 4),
7477 ui_button_info("2_MS_00", 2, 647, -1, -1, 0),
7478 ui_button_info("2_MS_01", 2, 713, -1, -1, 1),
7479 ui_button_info("2_MS_03", 829, 682, 831, 667, 3),
7480 ui_button_info("2_MS_02", 751, 682, 766, 667, 2),
7481 ui_button_info("2_MS_04", 914, 672, 924, 667, 4),
7486 #define MULTI_SYNC_NUM_TEXT 5
7488 #define MST_LAUNCH 2
7489 UI_XSTR Multi_sync_text[GR_NUM_RESOLUTIONS][MULTI_SYNC_NUM_TEXT] = {
7491 { "Kick", 1266, 479, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_KICK].button },
7492 { "Cancel", 387, 519, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_CANCEL].button },
7493 { "Launch", 801, 577, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_LAUNCH].button },
7494 { "Players", 1269, 23, 133, UI_XSTR_COLOR_GREEN, -1, NULL },
7495 { "Status", 1304, 228, 133, UI_XSTR_COLOR_GREEN, -1, NULL }
7498 { "Kick", 1266, 766, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_KICK].button },
7499 { "Cancel", 387, 831, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_CANCEL].button },
7500 { "Launch", 801, 924, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_LAUNCH].button },
7501 { "Players", 1269, 38, 214, UI_XSTR_COLOR_GREEN, -1, NULL },
7502 { "Status", 1304, 366, 214, UI_XSTR_COLOR_GREEN, -1, NULL }
7507 int Ms_status_coords[GR_NUM_RESOLUTIONS][4] = {
7516 // player status coords
7517 int Ms_status2_coords[GR_NUM_RESOLUTIONS][4] = {
7526 int Ms_cd_icon_offset[GR_NUM_RESOLUTIONS] = {
7531 int Ms_team_icon_offset[GR_NUM_RESOLUTIONS] = {
7536 // player currently selected, index into Net_players[]
7537 int Multi_sync_player_select = -1;
7539 // player list control thingie defs
7540 #define MULTI_SYNC_PLIST_MAX_DISPLAY 15
7541 int Multi_sync_plist_start; // where to start displaying from
7542 int Multi_sync_plist_count; // how many we have
7544 // list select button
7545 UI_BUTTON Multi_sync_plist_button;
7547 int Multi_sync_mode = -1;
7549 #define MULTI_SYNC_COUNTDOWN_TIME 5 // in seconds
7550 float Multi_sync_countdown_timer;
7551 int Multi_sync_countdown = -1;
7553 int Multi_launch_button_created;
7556 // countdown animation timer
7557 char* Multi_sync_countdown_fname[GR_NUM_RESOLUTIONS] = {
7559 "2_Count" // GR_1024
7562 int Multi_sync_countdown_coords[GR_NUM_RESOLUTIONS][2] = {
7573 anim *Multi_sync_countdown_anim = NULL;
7574 anim_instance *Multi_sync_countdown_instance = NULL;
7577 // PREBRIEFING STUFF
7578 // syncing flags used by the server
7579 int Mission_sync_flags = 0;
7580 #define MS_FLAG_SENT_FILESIG (1<<0) // sent filesig requests
7581 #define MS_FLAG_SENT_LOAD (1<<1) // sent load packets
7582 #define MS_FLAG_PUSHED_BRIEFING (1<<2) // pushed everyone else into the briefing
7583 #define MS_FLAG_POST_DATA (1<<3) // sent the post data block
7584 #define MS_FLAG_WSS_SLOTS (1<<4) // all players have received wss slot data
7585 #define MS_FLAG_PSETTINGS (1<<5) // send the player settings packet
7586 #define MS_FLAG_MT_STATS_START (1<<6) // server has started getting player stats from the tracker
7587 #define MS_FLAG_MT_STATS_DONE (1<<7) // server has finished getting player stats from the tracker (success or fail)
7588 #define MS_FLAG_TS_SLOTS (1<<8) // team/ship slots have been sent
7589 #define MS_FLAG_DATA_DONE (1<<9) // done transferring all necessary data
7590 #define MS_FLAG_CAMP_DONE (1<<10) // send campaign pool/goal/event stuff
7592 // POSTBRIEFING STUFF
7593 int Multi_state_timestamp;
7594 int Multi_sync_launch_pressed;
7596 // LOCAL function definitions
7597 void multi_sync_check_buttons();
7598 void multi_sync_button_pressed(int n);
7599 void multi_sync_scroll_info_up();
7600 void multi_sync_scroll_info_down();
7601 void multi_sync_display_name(char *name,int index,int np_index); // display info on the left hand portion of the status window thingie
7602 void multi_sync_display_status(char *status,int index); // display info on the right hand portion of the status window thingie
7603 void multi_sync_force_start_pre();
7604 void multi_sync_force_start_post();
7605 void multi_sync_launch();
7606 void multi_sync_create_launch_button();
7607 void multi_sync_blit_screen_all();
7608 void multi_sync_handle_plist();
7610 void multi_sync_common_init();
7611 void multi_sync_common_do();
7612 void multi_sync_common_close();
7614 void multi_sync_pre_init();
7615 void multi_sync_pre_do();
7616 void multi_sync_pre_close();
7618 void multi_sync_post_init();
7619 void multi_sync_post_do();
7620 void multi_sync_post_close();
7625 // perform the correct init functions
7626 void multi_sync_init()
7628 Multi_sync_countdown = -1;
7632 // reset all timestamp
7633 multi_reset_timestamps();
7635 extern int Player_multi_died_check;
7636 Player_multi_died_check = -1;
7638 if(!(Game_mode & GM_STANDALONE_SERVER)){
7639 multi_sync_common_init();
7642 switch(Multi_sync_mode){
7643 case MULTI_SYNC_PRE_BRIEFING:
7644 multi_sync_pre_init();
7646 case MULTI_SYNC_POST_BRIEFING:
7647 multi_sync_post_init();
7649 case MULTI_SYNC_INGAME:
7650 multi_ingame_sync_init();
7655 // perform the correct do frame functions
7656 void multi_sync_do()
7658 if(!(Game_mode & GM_STANDALONE_SERVER)){
7659 multi_sync_common_do();
7662 // if the netgame is ending, don't do any sync processing
7663 if(multi_endgame_ending()){
7667 // process appropriateliy
7668 switch(Multi_sync_mode){
7669 case MULTI_SYNC_PRE_BRIEFING:
7670 multi_sync_pre_do();
7672 case MULTI_SYNC_POST_BRIEFING:
7673 multi_sync_post_do();
7675 case MULTI_SYNC_INGAME:
7676 multi_ingame_sync_do();
7679 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
7680 if(Multi_sync_bitmap != -1){
7681 gr_set_bitmap(Multi_sync_bitmap);
7684 Multi_sync_window.draw();
7686 multi_sync_blit_screen_all();
7693 // perform the correct close functions
7694 void multi_sync_close()
7696 switch(Multi_sync_mode){
7697 case MULTI_SYNC_PRE_BRIEFING:
7698 multi_sync_pre_close();
7700 case MULTI_SYNC_POST_BRIEFING:
7701 multi_sync_post_close();
7703 case MULTI_SYNC_INGAME:
7704 multi_ingame_sync_close();
7708 if(!(Game_mode & GM_STANDALONE_SERVER)){
7709 multi_sync_common_close();
7713 char *multi_sync_tooltip_handler(char *str)
7715 if (!stricmp(str, NOX("@launch"))) {
7716 if (Multi_launch_button_created){
7717 return XSTR("Launch",801);
7724 void multi_sync_common_init()
7728 // create the interface window
7729 Multi_sync_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
7730 Multi_sync_window.set_mask_bmap(Multi_sync_bitmap_mask_fname[gr_screen.res]);
7731 Multi_sync_window.tooltip_handler = multi_sync_tooltip_handler;
7733 // load the background bitmap
7734 Multi_sync_bitmap = bm_load(Multi_sync_bitmap_fname[gr_screen.res]);
7735 if (Multi_sync_bitmap < 0) {
7736 // we failed to load the bitmap - this is very bad
7740 // initialize the player list data
7741 Multi_sync_plist_start = 0;
7742 Multi_sync_plist_count = 1; // we can pretty safely assume that there's one player in the game - me.
7744 Multi_launch_button_created = 0;
7746 // create the chatbox thingie (shouldn't be necesary to do this, but we'll put it in for good measure)
7749 // force the chatbox to be small
7750 chatbox_force_small();
7752 // initialize the common notification messaging
7753 multi_common_notify_init();
7755 // initialize the common mission info display area.
7756 multi_common_set_text("");
7758 // use the common interface palette
7759 multi_common_set_palette();
7761 // don't select any player yet.
7762 Multi_sync_player_select = -1;
7764 // determine how many of the 5 buttons to create
7765 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
7766 Multi_sync_button_count = MULTI_SYNC_HOST_COUNT;
7768 Multi_sync_button_count = MULTI_SYNC_CLIENT_COUNT;
7770 // create the interface buttons
7771 for(idx=0; idx<Multi_sync_button_count; idx++){
7772 // create the object
7773 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);
7775 // set the sound to play when highlighted
7776 Multi_sync_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
7778 // set the ani for the button
7779 // this wierdness is necessary because cancel and kick buttons aren't drawn on the background bitmap,
7780 // so we have to load in frame 0, too (the file should exist)
7781 if ((idx == MS_CANCEL) || (idx == MS_KICK) || (idx == MS_LAUNCH)) {
7782 Multi_sync_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sync_buttons[gr_screen.res][idx].filename, 3, 0);
7784 Multi_sync_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sync_buttons[gr_screen.res][idx].filename);
7788 Multi_sync_buttons[gr_screen.res][idx].button.link_hotspot(Multi_sync_buttons[gr_screen.res][idx].hotspot);
7792 for(idx=0; idx<MULTI_SYNC_NUM_TEXT; idx++) {
7793 // don't create the "launch" button text just yet
7794 if(idx == MST_LAUNCH) {
7797 // multiplayer clients should ignore the kick button
7798 if(!MULTIPLAYER_MASTER && !MULTIPLAYER_HOST && (idx == MST_KICK)) {
7802 Multi_sync_window.add_XSTR(&Multi_sync_text[gr_screen.res][idx]);
7805 // create the player list select button and hide it
7806 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);
7807 Multi_sync_plist_button.hide();
7809 // set up hotkeys for certain common functions
7810 Multi_sync_buttons[gr_screen.res][MS_CANCEL].button.set_hotkey(KEY_ESC);
7813 void multi_sync_common_do()
7815 int k = chatbox_process();
7816 k = Multi_sync_window.process(k);
7818 // process the player list
7819 multi_sync_handle_plist();
7821 // process any button clicks
7822 multi_sync_check_buttons();
7824 // process any keypresses
7828 gamesnd_play_iface(SND_USER_SELECT);
7829 multi_quit_game(PROMPT_ALL);
7834 void multi_sync_common_close()
7836 // unload any bitmaps
7837 if(!bm_unload(Multi_sync_bitmap)){
7838 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_sync_bitmap_fname[gr_screen.res]));
7841 extern int Player_multi_died_check;
7842 Player_multi_died_check = -1;
7844 // destroy the UI_WINDOW
7845 Multi_sync_window.destroy();
7848 void multi_sync_blit_screen_all()
7855 // display any text in the info area
7856 multi_common_render_text();
7858 // display any pending notification messages
7859 multi_common_notify_do();
7861 // display any info about visible players
7863 for(idx=0;idx<MAX_PLAYERS;idx++){
7864 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7865 // display his name and status
7866 multi_sync_display_name(Net_players[idx].player->callsign,count,idx);
7868 // get the player state
7869 state = Net_players[idx].state;
7871 // if we're ingame joining, show all other players except myself as "playing"
7872 if((Net_player != NULL) && (&Net_players[idx] != Net_player) && ((Multi_sync_mode == MULTI_SYNC_INGAME) || (Net_player->flags & NETINFO_FLAG_INGAME_JOIN)) ){
7873 state = NETPLAYER_STATE_IN_MISSION;
7877 case NETPLAYER_STATE_MISSION_LOADING:
7878 multi_sync_display_status(XSTR("Mission Loading",802),count);
7880 case NETPLAYER_STATE_INGAME_SHIP_SELECT: // I don't think its possible to see this state, but...
7881 multi_sync_display_status(XSTR("Ingame Ship Select",803),count);
7883 case NETPLAYER_STATE_DEBRIEF:
7884 multi_sync_display_status(XSTR("Debriefing",804),count);
7886 case NETPLAYER_STATE_MISSION_SYNC:
7887 multi_sync_display_status(XSTR("Mission Sync",805),count);
7889 case NETPLAYER_STATE_JOINING:
7890 multi_sync_display_status(XSTR("Joining",806),count);
7892 case NETPLAYER_STATE_JOINED:
7893 multi_sync_display_status(XSTR("Joined",807),count);
7895 case NETPLAYER_STATE_SLOT_ACK :
7896 multi_sync_display_status(XSTR("Slot Ack",808),count);
7898 case NETPLAYER_STATE_BRIEFING:
7899 multi_sync_display_status(XSTR("Briefing",765),count);
7901 case NETPLAYER_STATE_SHIP_SELECT:
7902 multi_sync_display_status(XSTR("Ship Select",809),count);
7904 case NETPLAYER_STATE_WEAPON_SELECT:
7905 multi_sync_display_status(XSTR("Weapon Select",810),count);
7907 case NETPLAYER_STATE_WAITING:
7908 multi_sync_display_status(XSTR("Waiting",811),count);
7910 case NETPLAYER_STATE_IN_MISSION:
7911 multi_sync_display_status(XSTR("In Mission",812),count);
7913 case NETPLAYER_STATE_MISSION_LOADED:
7914 multi_sync_display_status(XSTR("Mission Loaded",813),count);
7916 case NETPLAYER_STATE_DATA_LOAD:
7917 multi_sync_display_status(XSTR("Data loading",814),count);
7919 case NETPLAYER_STATE_SETTINGS_ACK:
7920 multi_sync_display_status(XSTR("Ready To Enter Mission",815),count);
7922 case NETPLAYER_STATE_INGAME_SHIPS:
7923 multi_sync_display_status(XSTR("Ingame Ships Packet Ack",816),count);
7925 case NETPLAYER_STATE_INGAME_WINGS:
7926 multi_sync_display_status(XSTR("Ingame Wings Packet Ack",817),count);
7928 case NETPLAYER_STATE_INGAME_RPTS:
7929 multi_sync_display_status(XSTR("Ingame Respawn Points Ack",818),count);
7931 case NETPLAYER_STATE_SLOTS_ACK:
7932 multi_sync_display_status(XSTR("Ingame Weapon Slots Ack",819),count);
7934 case NETPLAYER_STATE_POST_DATA_ACK:
7935 multi_sync_display_status(XSTR("Post Briefing Data Block Ack",820),count);
7937 case NETPLAYER_STATE_FLAG_ACK :
7938 multi_sync_display_status(XSTR("Flags Ack",821),count);
7940 case NETPLAYER_STATE_MT_STATS :
7941 multi_sync_display_status(XSTR("Parallax Online Stats Updating",822),count);
7943 case NETPLAYER_STATE_WSS_ACK :
7944 multi_sync_display_status(XSTR("Weapon Slots Ack",823),count);
7946 case NETPLAYER_STATE_HOST_SETUP :
7947 multi_sync_display_status(XSTR("Host setup",824),count);
7949 case NETPLAYER_STATE_DEBRIEF_ACCEPT:
7950 multi_sync_display_status(XSTR("Debrief accept",825),count);
7952 case NETPLAYER_STATE_DEBRIEF_REPLAY:
7953 multi_sync_display_status(XSTR("Debrief replay",826),count);
7955 case NETPLAYER_STATE_CPOOL_ACK:
7956 multi_sync_display_status(XSTR("Campaign ship/weapon ack",827),count);
7958 case NETPLAYER_STATE_MISSION_XFER :
7960 // server should display the pct completion of all clients
7961 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
7962 if(Net_players[idx].s_info.xfer_handle != -1){
7963 pct_complete = multi_xfer_pct_complete(Net_players[idx].s_info.xfer_handle);
7965 // if we've got a valid xfer handle
7966 if((pct_complete >= 0.0) && (pct_complete <= 1.0)){
7967 sprintf(txt,XSTR("Mission file xfer %d%%",828),(int)(pct_complete * 100.0f));
7971 strcpy(txt,XSTR("Mission file xfer",829));
7974 strcpy(txt,XSTR("Mission file xfer",829));
7977 // clients should display only for themselves (which is the only thing they know)
7979 // if we've got a valid file xfer handle
7980 if((&Net_players[idx] == Net_player) && (Net_player->s_info.xfer_handle != -1)){
7981 pct_complete = multi_xfer_pct_complete(Net_player->s_info.xfer_handle);
7983 // if we've got a valid xfer handle
7984 if((pct_complete >= 0.0) && (pct_complete <= 1.0)){
7985 sprintf(txt,XSTR("Mission file xfer %d%%",828),(int)(pct_complete * 100.0f));
7989 strcpy(txt,XSTR("Mission file xfer",829));
7994 strcpy(txt,XSTR("Mission file xfer",829));
7999 multi_sync_display_status(txt,count);
8002 nprintf(("Network","Unhandled player state : %d !\n",Net_players[idx].state));
8009 // display the mission start countdown timer (if any)
8010 anim_render_all(GS_STATE_MULTI_MISSION_SYNC,flFrametime);
8012 // process and show the chatbox thingie
8016 Multi_sync_window.draw_tooltip();
8018 // display the voice status indicator
8019 multi_common_voice_display_status();
8022 void multi_sync_check_buttons()
8025 for(idx=0;idx<Multi_sync_button_count;idx++){
8026 // we only really need to check for one button pressed at a time, so we can break after
8028 if(Multi_sync_buttons[gr_screen.res][idx].button.pressed()){
8029 multi_sync_button_pressed(idx);
8035 void multi_sync_button_pressed(int n)
8040 gamesnd_play_iface(SND_USER_SELECT);
8041 multi_quit_game(PROMPT_ALL);
8044 // scroll the info box up
8045 case MS_SCROLL_INFO_UP:
8046 multi_common_scroll_text_up();
8049 // scroll the info box down
8050 case MS_SCROLL_INFO_DOWN:
8051 multi_common_scroll_text_down();
8056 // if we have a currently selected player, kick him
8057 if(Multi_sync_player_select >= 0){
8058 multi_kick_player(Multi_sync_player_select);
8062 // start the final launch countdown (post-sync only)
8064 multi_sync_start_countdown();
8067 // doesn't do anything
8073 void multi_sync_pre_init()
8077 Netgame.game_state = NETGAME_STATE_MISSION_SYNC;
8079 // if we're in teamplay mode, always force skill level to be medium
8080 if((Netgame.type_flags & NG_TYPE_TEAM) && (Net_player->flags & NETINFO_FLAG_GAME_HOST)){
8081 Netgame.options.skill_level = NUM_SKILL_LEVELS / 2;
8082 Game_skill_level = NUM_SKILL_LEVELS / 2;
8083 multi_options_update_netgame();
8086 // notify everyone of when we get here
8087 if(!(Game_mode & GM_STANDALONE_SERVER)){
8088 Net_player->state = NETPLAYER_STATE_MISSION_SYNC;
8089 send_netplayer_update_packet();
8092 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8094 ml_string(NOX("Server performing pre-briefing data sync"));
8096 if(!(Game_mode & GM_STANDALONE_SERVER)){
8097 multi_common_set_text(XSTR("Server performing sync\n",830),1);
8100 // maybe initialize tvt and squad war stuff
8101 if(Netgame.type_flags & NG_TYPE_TEAM){
8102 multi_team_level_init();
8105 // force everyone into this state
8106 send_netgame_update_packet();
8108 if(!(Game_mode & GM_STANDALONE_SERVER)){
8109 multi_common_add_text(XSTR("Send update packet\n",831),1);
8112 // setup some of my own data
8113 Net_player->flags |= NETINFO_FLAG_MISSION_OK;
8115 // do any output stuff
8116 if(Game_mode & GM_STANDALONE_SERVER){
8117 std_debug_set_standalone_state_string("Mission Sync");
8120 // do this here to insure we have the most up to date file checksum info
8121 multi_get_mission_checksum(Game_current_mission_filename);
8122 // parse_get_file_signature(Game_current_mission_filename);
8124 if(!(Game_mode & GM_STANDALONE_SERVER)){
8125 multi_common_add_text(XSTR("Got file signatures\n",832),1);
8128 if(!(Game_mode & GM_STANDALONE_SERVER)){
8129 multi_common_add_text(XSTR("Sending update state packet\n",833),1);
8133 // if we're not in team vs. team mode - set all player teams to be 0, and unset all captaincy bits
8134 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
8135 for(idx=0;idx<MAX_PLAYERS;idx++){
8136 Net_players[idx].p_info.team = 0;
8137 Net_players[idx].flags &= ~(NETINFO_FLAG_TEAM_CAPTAIN);
8141 // we aren't necessarily xferring the mission file yet
8142 Assert(Net_player->s_info.xfer_handle == -1);
8144 // always call this for good measure
8145 multi_campaign_flush_data();
8147 Mission_sync_flags = 0;
8148 Multi_mission_loaded = 0;
8151 void multi_sync_pre_do()
8155 // If I'm the server, wait for everyone to arrive in this state, then begin transferring data, etc.
8156 // all servers (standalone or no, go through this)
8157 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8158 // wait for everyone to arrive, then request filesig from all of them
8159 if(multi_netplayer_state_check(NETPLAYER_STATE_MISSION_SYNC) && !(Mission_sync_flags & MS_FLAG_SENT_FILESIG) && !multi_endgame_ending()){
8160 send_file_sig_request(Netgame.mission_name);
8161 Mission_sync_flags |= MS_FLAG_SENT_FILESIG;
8163 if(!(Game_mode & GM_STANDALONE_SERVER)){
8164 multi_common_add_text(XSTR("Sent filesig request\n",834),1);
8168 // if we're waiting for players to receive files, then check on their status
8169 if((Mission_sync_flags & MS_FLAG_SENT_FILESIG) && !multi_netplayer_flag_check(NETINFO_FLAG_MISSION_OK) && !multi_endgame_ending()){
8170 for(idx=0;idx<MAX_PLAYERS;idx++){
8171 // if this player is in the process of xferring a file
8172 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].s_info.xfer_handle != -1)){
8173 switch(multi_xfer_get_status(Net_players[idx].s_info.xfer_handle)){
8174 // if it has successfully completed, set his ok flag
8175 case MULTI_XFER_SUCCESS :
8177 Net_players[idx].flags |= NETINFO_FLAG_MISSION_OK;
8179 // release the xfer instance handle
8180 multi_xfer_release_handle(Net_players[idx].s_info.xfer_handle);
8181 Net_players[idx].s_info.xfer_handle = -1;
8183 // if it has failed or timed-out, kick the player
8184 case MULTI_XFER_TIMEDOUT:
8185 case MULTI_XFER_FAIL:
8186 // release the xfer handle
8187 multi_xfer_release_handle(Net_players[idx].s_info.xfer_handle);
8188 Net_players[idx].s_info.xfer_handle = -1;
8191 multi_kick_player(idx, 0, KICK_REASON_BAD_XFER);
8198 // NOTE : this is now obsolete
8199 // once everyone is verified, do any data transfer necessary
8200 if(multi_netplayer_flag_check(NETINFO_FLAG_MISSION_OK) && !(Mission_sync_flags & MS_FLAG_DATA_DONE) && !multi_endgame_ending()){
8201 // do nothing for now
8202 Mission_sync_flags |= MS_FLAG_DATA_DONE;
8204 // send campaign pool data
8205 multi_campaign_send_pool_status();
8208 // wait for everyone to ack on campaign pool data (even in non-campaign situations)
8209 if((Mission_sync_flags & MS_FLAG_DATA_DONE) && !(Mission_sync_flags & MS_FLAG_CAMP_DONE) && !multi_endgame_ending()){
8210 // check to see if everyone has acked the campaign pool data
8211 if(multi_netplayer_state_check(NETPLAYER_STATE_CPOOL_ACK)){
8212 Mission_sync_flags |= MS_FLAG_CAMP_DONE;
8216 // once everyone is verified, tell them to load the mission
8217 // also make sure to load the mission myself _AFTER_ telling everyone to do so. This makes the whole process
8218 // move along faster
8219 if((Mission_sync_flags & MS_FLAG_CAMP_DONE) && !(Mission_sync_flags & MS_FLAG_SENT_LOAD) && !multi_endgame_ending()){
8220 send_netplayer_load_packet(NULL);
8221 Mission_sync_flags |= MS_FLAG_SENT_LOAD;
8223 if(!(Game_mode & GM_STANDALONE_SERVER)){
8224 multi_common_add_text(XSTR("Sent load packet\n",835),1);
8227 // load the mission myself, as soon as possible
8228 if(!Multi_mission_loaded){
8229 nprintf(("Network","Server loading mission..."));
8231 // update everyone about my status
8232 Net_player->state = NETPLAYER_STATE_MISSION_LOADING;
8233 send_netplayer_update_packet();
8235 game_start_mission();
8237 nprintf(("Network","Done\n"));
8238 Multi_mission_loaded = 1;
8240 // update everyone about my status
8241 Net_player->state = NETPLAYER_STATE_MISSION_LOADED;
8242 send_netplayer_update_packet();
8244 if(!(Game_mode & GM_STANDALONE_SERVER)){
8245 multi_common_add_text(XSTR("Loaded mission locally\n",836),1);
8250 // if everyone has loaded the mission, randomly assign players to ships
8251 if(multi_netplayer_state_check(NETPLAYER_STATE_MISSION_LOADED) && !(Mission_sync_flags & MS_FLAG_TS_SLOTS) && !multi_endgame_ending()){
8252 // call the team select function to assign players to their ships, wings, etc
8253 multi_ts_assign_players_all();
8254 send_netplayer_slot_packet();
8257 Mission_sync_flags |= MS_FLAG_TS_SLOTS;
8260 // if everyone has loaded the mission, move to the team select stage
8261 if(Sync_test && multi_netplayer_state_check(NETPLAYER_STATE_SLOT_ACK) && !(Mission_sync_flags & MS_FLAG_PUSHED_BRIEFING) && !multi_endgame_ending()){
8262 Netgame.game_state = NETGAME_STATE_BRIEFING;
8263 send_netgame_update_packet(); // this will push everyone into the next state
8265 // the standalone moves to his own wait state, whereas in the normal game mode, the server/host moves in to the
8266 // team select state
8267 if(Game_mode & GM_STANDALONE_SERVER){
8268 gameseq_post_event(GS_EVENT_MULTI_STD_WAIT);
8270 gameseq_post_event(GS_EVENT_START_GAME);
8273 Mission_sync_flags |= MS_FLAG_PUSHED_BRIEFING;
8275 if(!(Game_mode & GM_STANDALONE_SERVER)){
8276 multi_common_add_text(XSTR("Moving to team select\n",837),1);
8280 // clients should detect here if they are doing a file xfer and do error processing
8281 if((Net_player->state == NETPLAYER_STATE_MISSION_XFER) && (Net_player->s_info.xfer_handle != -1) && !multi_endgame_ending()){
8282 switch(multi_xfer_get_status(Net_player->s_info.xfer_handle)){
8283 // if it has successfully completed, set his ok flag
8284 case MULTI_XFER_SUCCESS :
8285 // release my xfer handle
8286 multi_xfer_release_handle(Net_player->s_info.xfer_handle);
8287 Net_player->s_info.xfer_handle = -1;
8290 // if it has failed or timed-out, kick the player
8291 case MULTI_XFER_TIMEDOUT:
8292 case MULTI_XFER_FAIL:
8293 // release my xfer handle
8294 multi_xfer_release_handle(Net_player->s_info.xfer_handle);
8295 Net_player->s_info.xfer_handle = -1;
8297 // leave the game qith an error code
8298 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_XFER_FAIL);
8305 if(!(Game_mode & GM_STANDALONE_SERVER)){
8307 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8308 if(Multi_sync_bitmap != -1){
8309 gr_set_bitmap(Multi_sync_bitmap);
8312 Multi_sync_window.draw();
8314 multi_sync_blit_screen_all();
8320 void multi_sync_pre_close()
8322 // at this point, we should shut down any file xfers...
8323 if(Net_player->s_info.xfer_handle != -1){
8324 nprintf(("Network","WARNING - killing file xfer while leaving mission sync state!!!\n"));
8326 multi_xfer_abort(Net_player->s_info.xfer_handle);
8327 Net_player->s_info.xfer_handle;
8331 void multi_sync_post_init()
8333 multi_reset_timestamps();
8335 Multi_state_timestamp = timestamp(0);
8338 ml_string(NOX("Performing post-briefing data sync"));
8340 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8341 multi_common_add_text(XSTR("Server performing sync\n",830),1);
8343 multi_common_add_text(XSTR("Client performing sync\n",838),1);
8346 // everyone should re-initialize these
8347 init_multiplayer_stats();
8349 // reset all sequencing info
8350 multi_oo_reset_sequencing();
8352 // if I am not the master of the game, then send the firing information for my ship
8354 if ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) ){
8355 send_firing_info_packet();
8358 // if I'm not a standalone server, load up the countdown stuff
8359 if(!(Game_mode & GM_STANDALONE_SERVER)){
8360 Multi_sync_countdown_anim = NULL;
8361 Multi_sync_countdown_instance = NULL;
8362 Multi_sync_countdown_anim = anim_load(Multi_sync_countdown_fname[gr_screen.res]);
8363 if(Multi_sync_countdown_anim == NULL){
8364 nprintf(("General","WARNING!, Could not load countdown animation %s!\n",Multi_sync_countdown_fname[gr_screen.res]));
8368 // create objects for all permanent observers
8369 multi_obs_level_init();
8371 // clear the game start countdown timer
8372 Multi_sync_countdown_timer = -1.0f;
8373 Multi_sync_countdown = -1;
8375 // if this is a team vs. team mission, mark all ship teams appropriately
8376 if(Netgame.type_flags & NG_TYPE_TEAM){
8377 multi_team_mark_all_ships();
8380 Mission_sync_flags = 0;
8381 Multi_sync_launch_pressed = 0;
8384 #define MULTI_POST_TIMESTAMP 7000
8386 extern int create_wings();
8388 void multi_sync_post_do()
8392 // only if the host is also the master should he be doing this (non-standalone situation)
8393 if ( Net_player->flags & NETINFO_FLAG_AM_MASTER ) {
8395 // once everyone gets to this screen, send them the ship classes of all ships.
8396 if(multi_netplayer_state_check(NETPLAYER_STATE_WAITING) && !(Mission_sync_flags & MS_FLAG_POST_DATA)) {
8397 // only the host should ever do this
8398 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8399 // at this point we want to delete all necessary ships, change all necessary ship classes, and set all weapons up
8400 multi_ts_create_wings();
8402 // update player ets settings
8403 for(idx=0;idx<MAX_PLAYERS;idx++){
8404 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].player->objnum != -1)){
8405 multi_server_update_player_weapons(&Net_players[idx],&Ships[Objects[Net_players[idx].player->objnum].instance]);
8410 // note that this is done a little differently for standalones and nonstandalones
8411 send_post_sync_data_packet();
8413 multi_common_add_text(XSTR("Sending post briefing block information\n",839),1);
8415 Mission_sync_flags |= MS_FLAG_POST_DATA;
8418 // send weapon slots data
8419 if(multi_netplayer_state_check(NETPLAYER_STATE_POST_DATA_ACK) && !(Mission_sync_flags & MS_FLAG_WSS_SLOTS)) {
8420 // note that this is done a little differently for standalones and nonstandalones
8421 if(Netgame.type_flags & NG_TYPE_TEAM){
8422 send_wss_slots_data_packet(0,0);
8423 send_wss_slots_data_packet(1,1);
8425 send_wss_slots_data_packet(0,1);
8428 multi_common_add_text(XSTR("Sending weapon slots information\n",840),1);
8430 Mission_sync_flags |= MS_FLAG_WSS_SLOTS;
8433 // once weapon information is received, send player settings info
8434 if ( multi_netplayer_state_check(NETPLAYER_STATE_WSS_ACK) && !(Mission_sync_flags & MS_FLAG_PSETTINGS)) {
8435 send_player_settings_packet();
8437 // server (specifically, the standalone), should set this here
8438 Net_player->state = NETPLAYER_STATE_SETTINGS_ACK;
8439 send_netplayer_update_packet();
8441 multi_common_add_text(XSTR("Sending player settings packets\n",841),1);
8443 Mission_sync_flags |= MS_FLAG_PSETTINGS;
8446 // check to see if the countdown timer has started and act appropriately
8447 if( Multi_sync_countdown_timer > -1.0f ) {
8449 // increment by frametime.
8450 Multi_sync_countdown_timer += flFrametime;
8452 // if the animation is not playing, start it
8453 if(!(Game_mode & GM_STANDALONE_SERVER) && (Multi_sync_countdown_instance == NULL) && (Multi_sync_countdown_anim != NULL)){
8454 anim_play_struct aps;
8456 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]);
8457 aps.screen_id = GS_STATE_MULTI_MISSION_SYNC;
8458 aps.framerate_independent = 1;
8460 Multi_sync_countdown_instance = anim_play(&aps);
8463 // if the next second has expired
8464 if( Multi_sync_countdown_timer >= 1.0f ) {
8466 Multi_sync_countdown--;
8467 Multi_sync_countdown_timer = 0.0f;
8469 // if the countdown has reached 0, launch the mission
8470 if(Multi_sync_countdown == 0){
8471 Multi_sync_countdown_timer = -1.0f;
8473 Multi_sync_launch_pressed = 0;
8474 multi_sync_launch();
8476 // otherwise send a countdown packet
8478 send_countdown_packet(Multi_sync_countdown);
8483 // jump into the mission myself
8484 if((Multi_sync_countdown == 0) && multi_netplayer_state_check(NETPLAYER_STATE_IN_MISSION)){
8485 if(!((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !Multi_sync_launch_pressed)){
8486 Net_player->state = NETPLAYER_STATE_IN_MISSION;
8487 Netgame.game_state = NETGAME_STATE_IN_MISSION;
8488 gameseq_post_event(GS_EVENT_ENTER_GAME);
8490 multi_common_add_text(XSTR("Moving into game\n",842),1);
8494 // maybe start the animation countdown
8495 if((Multi_sync_countdown >= 0) && (Multi_sync_countdown_instance == NULL) && (Multi_sync_countdown_anim != NULL)){
8496 anim_play_struct aps;
8498 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]);
8499 aps.screen_id = GS_STATE_MULTI_MISSION_SYNC;
8500 aps.framerate_independent = 1;
8502 Multi_sync_countdown_instance = anim_play(&aps);
8506 // host - specific stuff
8507 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8508 // create the launch button so the host can click
8509 if( Sync_test && multi_netplayer_state_check(NETPLAYER_STATE_SETTINGS_ACK) ){
8510 multi_sync_create_launch_button();
8515 if(!(Game_mode & GM_STANDALONE_SERVER)){
8517 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8518 if(Multi_sync_bitmap != -1){
8519 gr_set_bitmap(Multi_sync_bitmap);
8522 Multi_sync_window.draw();
8524 multi_sync_blit_screen_all();
8530 void multi_sync_post_close()
8534 // if I'm not a standalone server, unload up the countdown stuff
8535 if(!(Game_mode & GM_STANDALONE_SERVER)){
8536 // release all rendering animation instances (should only be 1)
8537 anim_release_all_instances(GS_STATE_MULTI_MISSION_SYNC);
8538 Multi_sync_countdown_instance = NULL;
8540 // free up the countdown animation
8541 if(Multi_sync_countdown_anim != NULL){
8542 anim_free(Multi_sync_countdown_anim);
8543 Multi_sync_countdown_anim = NULL;
8547 // all players should reset sequencing
8548 for(idx=0;idx<MAX_PLAYERS;idx++){
8549 if(Net_player->flags & NETINFO_FLAG_CONNECTED){
8550 Net_players[idx].client_cinfo_seq = 0;
8551 Net_players[idx].client_server_seq = 0;
8555 // multiplayer dogfight
8556 multi_df_level_pre_enter();
8558 // clients should clear obj_pair array and add pair for themselves
8560 if ( MULTIPLAYER_CLIENT ) {
8562 obj_add_pairs( OBJ_INDEX(Player_obj) );
8567 void multi_sync_display_name(char *name,int index,int np_index)
8569 char fit[CALLSIGN_LEN];
8571 // make sure the string actually fits
8574 // if we're in team vs. team mode
8575 if(Netgame.type_flags & NG_TYPE_TEAM){
8576 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]);
8578 // if this is the currently selected player, draw him highlighted
8579 if(np_index == Multi_sync_player_select){
8580 gr_set_color_fast(&Color_text_selected);
8582 gr_set_color_fast(&Color_text_normal);
8586 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);
8588 // blit his team icon
8590 if(Net_players[np_index].p_info.team == 0){
8591 // blit the team captain icon
8592 if(Net_players[np_index].flags & NETINFO_FLAG_TEAM_CAPTAIN){
8593 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
8594 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT]);
8595 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);
8598 // normal team member icon
8600 if(Multi_common_icons[MICON_TEAM0] != -1){
8601 gr_set_bitmap(Multi_common_icons[MICON_TEAM0]);
8602 gr_bitmap(Ms_status_coords[gr_screen.res][MS_X_COORD] + Ms_cd_icon_offset[gr_screen.res], Ms_status_coords[gr_screen.res][MS_Y_COORD] + (index * 10) - 2);
8607 else if(Net_players[np_index].p_info.team == 1){
8608 // blit the team captain icon
8609 if(Net_players[np_index].flags & NETINFO_FLAG_TEAM_CAPTAIN){
8610 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
8611 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT]);
8612 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);
8615 // normal team member icon
8617 if(Multi_common_icons[MICON_TEAM1] != -1){
8618 gr_set_bitmap(Multi_common_icons[MICON_TEAM1]);
8619 gr_bitmap(Ms_status_coords[gr_screen.res][MS_X_COORD] + Ms_cd_icon_offset[gr_screen.res], Ms_status_coords[gr_screen.res][MS_Y_COORD] + (index * 10) - 2);
8624 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]);
8626 // if this is the currently selected player, draw him highlighted
8627 if(np_index == Multi_sync_player_select){
8628 gr_set_color_fast(&Color_text_selected);
8630 gr_set_color_fast(&Color_text_normal);
8634 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);
8637 // maybe blit his CD status icon
8638 if((Net_players[np_index].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
8639 gr_set_bitmap(Multi_common_icons[MICON_CD]);
8640 gr_bitmap(Ms_status_coords[gr_screen.res][MS_X_COORD], Ms_status_coords[gr_screen.res][MS_Y_COORD] + (index * 10));
8644 void multi_sync_display_status(char *status,int index)
8648 // make sure the string actually fits
8649 strcpy(fit, status);
8650 gr_force_fit_string(fit, 250, Ms_status2_coords[gr_screen.res][MS_W_COORD] - 20);
8651 gr_set_color_fast(&Color_bright);
8652 gr_string(Ms_status2_coords[gr_screen.res][MS_X_COORD], Ms_status2_coords[gr_screen.res][MS_Y_COORD] + (index * 10), fit);
8655 void multi_sync_force_start_pre()
8658 int want_state = NETPLAYER_STATE_SLOT_ACK; // kick any players who are still in this state
8660 // go through the player list and boot anyone who isn't in the right state
8661 for(idx=0;idx<MAX_PLAYERS;idx++){
8662 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx]) && (Net_players[idx].state == want_state)){
8663 multi_kick_player(idx,0);
8668 void multi_sync_force_start_post()
8672 int num_kill_states;
8674 // determine the state we want all players in so that we can find those who are not in the state
8675 kill_state[0] = NETPLAYER_STATE_BRIEFING;
8676 kill_state[1] = NETPLAYER_STATE_SHIP_SELECT;
8677 kill_state[2] = NETPLAYER_STATE_WEAPON_SELECT;
8678 num_kill_states = 3;
8680 // go through the player list and boot anyone who isn't in the right state
8681 for(idx=0;idx<MAX_PLAYERS;idx++){
8682 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx])){
8683 // check against all kill state
8684 for(idx2 = 0;idx2<num_kill_states;idx2++){
8685 if(Net_players[idx].state == kill_state[idx2]){
8686 multi_kick_player(idx,0);
8694 void multi_sync_start_countdown()
8696 // don't allow repeat button presses
8697 if(Multi_sync_launch_pressed){
8701 Multi_sync_launch_pressed = 1;
8703 // if I'm the server, begin the countdown
8704 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8705 gamesnd_play_iface(SND_COMMIT_PRESSED);
8706 Multi_sync_countdown_timer = 0.0f;
8707 Multi_sync_countdown = MULTI_SYNC_COUNTDOWN_TIME;
8709 // send an initial countdown value
8710 send_countdown_packet(Multi_sync_countdown);
8712 // otherwise send the "start countdown" packet to the standalone
8714 Assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
8715 send_countdown_packet(-1);
8719 void multi_sync_launch()
8721 // don't allow repeat button presses
8722 if(Multi_sync_launch_pressed){
8726 Multi_sync_launch_pressed = 1;
8729 ml_printf(NOX("Entering mission %s"), Game_current_mission_filename);
8731 // tell everyone to jump into the mission
8732 send_jump_into_mission_packet();
8733 Multi_state_timestamp = timestamp(MULTI_POST_TIMESTAMP);
8735 // set the # of players at the start of the mission
8736 Multi_num_players_at_start = multi_num_players();
8737 nprintf(("Network","# of players at start of mission : %d\n", Multi_num_players_at_start));
8739 // initialize datarate limiting for all clients
8740 multi_oo_rate_init_all();
8742 multi_common_add_text(XSTR("Sending mission start packet\n",843),1);
8745 void multi_sync_create_launch_button()
8747 if (!Multi_launch_button_created) {
8748 // create the object
8749 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);
8751 // set the sound to play when highlighted
8752 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_highlight_action(common_play_highlight_sound);
8754 // set the ani for the button
8755 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_bmaps(Multi_sync_buttons[gr_screen.res][MS_LAUNCH].filename, 3, 0);
8758 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.link_hotspot(Multi_sync_buttons[gr_screen.res][MS_LAUNCH].hotspot);
8761 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_hotkey(KEY_CTRLED+KEY_ENTER);
8763 // create the text for the button
8764 Multi_sync_window.add_XSTR(&Multi_sync_text[gr_screen.res][MST_LAUNCH]);
8766 // increment the button count so we start checking this one
8767 Multi_sync_button_count++;
8769 Multi_launch_button_created = 1;
8773 void multi_sync_handle_plist()
8779 // if we don't have a currently selected player, select one
8780 if((Multi_sync_player_select < 0) || !MULTI_CONNECTED(Net_players[Multi_sync_player_select])){
8781 for(idx=0;idx<MAX_PLAYERS;idx++){
8782 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
8783 Multi_sync_player_select = idx;
8789 // check for button list presses
8790 if(Multi_sync_plist_button.pressed()){
8791 // get the y mouse coords
8792 Multi_sync_plist_button.get_mouse_pos(NULL,&my);
8794 // get the index of the item selected
8795 select_index = my / 10;
8797 // if the index is greater than the current # connections, do nothing
8798 if(select_index > (multi_num_connections() - 1)){
8802 // translate into an absolute Net_players[] index (get the Nth net player)
8803 Multi_sync_player_select = -1;
8804 for(idx=0;idx<MAX_PLAYERS;idx++){
8805 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
8809 // if we've found the item we're looking for
8810 if(select_index < 0){
8811 Multi_sync_player_select = idx;
8816 // if for some bizarre reason, this is an invalid player, unselect him and wait for the next interation
8818 if((Multi_sync_player_select >= 0) && (!MULTI_CONNECTED(Net_players[Multi_sync_player_select]) || MULTI_STANDALONE(Net_players[Multi_sync_player_select])) ){
8819 Multi_sync_player_select = -1;
8825 // -------------------------------------------------------------------------------------------------------------
8827 // MULTIPLAYER DEBRIEF SCREEN
8830 // other relevant data
8831 int Multi_debrief_accept_hit;
8832 int Multi_debrief_replay_hit;
8834 // set if the server has left the game
8835 int Multi_debrief_server_left = 0;
8837 // if we've reported on TvT status all players are in the debrief
8838 int Multi_debrief_reported_tvt = 0;
8840 // whether stats are being accepted
8841 // -1 == no decision yet
8844 int Multi_debrief_stats_accept_code = -1;
8846 int Multi_debrief_server_framecount = 0;
8848 float Multi_debrief_time = 0.0f;
8849 float Multi_debrief_resend_time = 10.0f;
8851 void multi_debrief_init()
8855 Multi_debrief_time = 0.0f;
8856 Multi_debrief_resend_time = 10.0f;
8858 // do this to notify the standalone or the normal server that we're in the debrief state and ready to receive packets
8859 if (!(Net_player->flags & NETINFO_FLAG_AM_MASTER)) {
8860 Net_player->state = NETPLAYER_STATE_DEBRIEF;
8861 send_netplayer_update_packet();
8864 // unflag some stuff
8865 for(idx=0;idx<MAX_PLAYERS;idx++){
8866 if(MULTI_CONNECTED(Net_players[idx])){
8867 Net_players[idx].flags &= ~(NETINFO_FLAG_RESPAWNING | NETINFO_FLAG_LIMBO | NETINFO_FLAG_WARPING_OUT);
8871 // if text input mode is active, clear it
8872 multi_msg_text_flush();
8874 // the server has not left yet
8875 Multi_debrief_server_left = 0;
8877 // have not hit accept or replay yet
8878 Multi_debrief_accept_hit = 0;
8879 Multi_debrief_replay_hit = 0;
8881 // stats have not been accepted yet
8882 Multi_debrief_stats_accept_code = -1;
8884 // mark stats as not being store yet
8885 Netgame.flags &= ~(NG_FLAG_STORED_MT_STATS);
8887 // no report on TvT yet
8888 Multi_debrief_reported_tvt = 0;
8890 Multi_debrief_server_framecount = 0;
8893 void multi_debrief_do_frame()
8895 Multi_debrief_time += flFrametime;
8897 // set the netgame state to be debriefing when appropriate
8898 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)){
8899 Netgame.game_state = NETGAME_STATE_DEBRIEF;
8900 send_netgame_update_packet();
8903 // evaluate all server stuff
8904 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8905 multi_debrief_server_process();
8909 void multi_debrief_close()
8911 if ( MULTIPLAYER_CLIENT && (Netgame.game_state == NETGAME_STATE_MISSION_SYNC) ){
8912 gamesnd_play_iface( SND_COMMIT_PRESSED );
8916 // handle optional mission loop
8917 void multi_maybe_set_mission_loop()
8919 int cur = Campaign.current_mission;
8920 if (Campaign.missions[cur].has_mission_loop) {
8921 Assert(Campaign.loop_mission != CAMPAIGN_LOOP_MISSION_UNINITIALIZED);
8923 bool require_repeat_mission = (Campaign.current_mission == Campaign.next_mission);
8925 // check for (1) mission loop available, (2) dont have to repeat last mission
8926 if ( (Campaign.missions[cur].has_mission_loop && (Campaign.loop_mission != -1)) && !require_repeat_mission ) {
8929 debrief_assemble_optional_mission_popup_text(buffer, Campaign.missions[cur].mission_loop_desc);
8931 int choice = popup(0 , 2, POPUP_NO, POPUP_YES, buffer);
8933 Campaign.loop_enabled = 1;
8934 Campaign.next_mission = Campaign.loop_mission;
8939 // handle all cases for when the accept key is hit in a multiplayer debriefing
8940 void multi_debrief_accept_hit()
8942 // if we already accepted, do nothing
8943 // but he may need to hit accept again after the server has left the game, so allow this
8944 if(Multi_debrief_accept_hit){
8948 // mark this so that we don't hit it again
8949 Multi_debrief_accept_hit = 1;
8951 gamesnd_play_iface(SND_COMMIT_PRESSED);
8953 // if the server has left the game, always just end the game.
8954 if(Multi_debrief_server_left){
8955 if(!multi_quit_game(PROMPT_ALL)){
8956 Multi_debrief_server_left = 1;
8957 Multi_debrief_accept_hit = 0;
8959 Multi_debrief_server_left = 0;
8962 // query the host and see if he wants to accept stats
8963 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8964 // if we're on a tracker game, he gets no choice for storing stats
8965 if(MULTI_IS_TRACKER_GAME){
8966 multi_maybe_set_mission_loop();
8968 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));
8970 // evaluate the result
8975 Multi_debrief_accept_hit = 0;
8978 // set the accept code to be "not accepting"
8980 multi_debrief_stats_toss();
8981 multi_maybe_set_mission_loop();
8984 // accept the stats and continue
8986 multi_debrief_stats_accept();
8987 multi_maybe_set_mission_loop();
8993 // set my netplayer state to be "debrief_accept", and be done with it
8994 Net_player->state = NETPLAYER_STATE_DEBRIEF_ACCEPT;
8995 send_netplayer_update_packet();
8999 // handle all cases for when the escape key is hit in a multiplayer debriefing
9000 void multi_debrief_esc_hit()
9004 // if the server has left
9005 if(Multi_debrief_server_left){
9006 multi_quit_game(PROMPT_ALL);
9011 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
9012 // if the stats have already been accepted
9013 if((Multi_debrief_stats_accept_code != -1) || (MULTI_IS_TRACKER_GAME)){
9014 multi_quit_game(PROMPT_HOST);
9016 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));
9018 // evaluate the result
9025 // set the accept code to be "not accepting"
9027 multi_debrief_stats_toss();
9028 multi_quit_game(PROMPT_NONE);
9031 // accept the stats and continue
9033 multi_debrief_stats_accept();
9034 multi_quit_game(PROMPT_NONE);
9039 // if the stats haven't been accepted yet, or this is a tracker game
9040 if((Multi_debrief_stats_accept_code == -1) && !(MULTI_IS_TRACKER_GAME)){
9041 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));
9043 // evaluate the result
9045 multi_quit_game(PROMPT_NONE);
9048 // otherwise go through the normal endgame channels
9050 multi_quit_game(PROMPT_ALL);
9055 void multi_debrief_replay_hit()
9057 // only the host should ever get here
9058 Assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
9060 // if the button was already pressed, do nothing
9061 if(Multi_debrief_accept_hit){
9065 // same as hittin the except button except no stats are kept
9066 Multi_debrief_accept_hit = 1;
9068 // mark myself as being in the replay state so we know what to do next
9069 Net_player->state = NETPLAYER_STATE_DEBRIEF_REPLAY;
9070 send_netplayer_update_packet();
9073 // call this when the server has left and we would otherwise be saying "contact lost with server
9074 void multi_debrief_server_left()
9077 Multi_debrief_server_left = 1;
9079 // undo any "accept" hit so that clients can hit accept again to leave
9080 Multi_debrief_accept_hit = 0;
9083 void multi_debrief_stats_accept()
9085 // don't do anything if we've already accepted
9086 if(Multi_debrief_stats_accept_code != -1){
9090 Multi_debrief_stats_accept_code = 1;
9092 // if we're the host, and we're on a standalone, tell the standalone to begin the stats storing process
9093 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) || (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
9094 // send a packet to the players telling them to store their stats
9095 send_store_stats_packet(1);
9098 // add a chat line saying "stats have been accepted"
9099 multi_display_chat_msg(XSTR("<stats have been accepted>",850),0,0);
9102 ml_string(NOX("Stats stored"));
9105 void multi_debrief_stats_toss()
9107 // don't do anything if we've already accepted
9108 if(Multi_debrief_stats_accept_code != -1){
9112 Multi_debrief_stats_accept_code = 0;
9114 // if we're the host, and we're on a standalone, tell everyone to "toss" stats
9115 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) || (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
9116 // send a packet to the players telling them to store their stats
9117 send_store_stats_packet(0);
9120 // add a chat line saying "stats have been accepted"
9121 multi_display_chat_msg(XSTR("<stats have been tossed>",851),0,0);
9124 ml_string(NOX("Stats tossed"));
9127 int multi_debrief_stats_accept_code()
9129 return Multi_debrief_stats_accept_code;
9132 void multi_debrief_server_process()
9135 int player_status,other_status;
9137 Multi_debrief_server_framecount++;
9139 // if we're > 10 seconds into the debrief and not everyone is here, try warping everyone out again
9140 if((Multi_debrief_time >= Multi_debrief_resend_time) && !multi_netplayer_state_check3(NETPLAYER_STATE_DEBRIEF, NETPLAYER_STATE_DEBRIEF_ACCEPT, NETPLAYER_STATE_DEBRIEF_REPLAY, 1)){
9141 // find all players who are not in the debrief state and hit them with the endgame packet
9142 for(idx=0; idx<MAX_PLAYERS; idx++){
9143 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)) ){
9144 send_endgame_packet(&Net_players[idx]);
9149 Multi_debrief_resend_time += 7.0f;
9152 // evaluate the status of all players in the game (0 == not ready, 1 == ready to continue, 2 == ready to replay)
9155 // check all players
9156 for(idx=0;idx<MAX_PLAYERS;idx++){
9157 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_HOST(Net_players[idx])){
9158 if(Net_players[idx].state != NETPLAYER_STATE_DEBRIEF_ACCEPT){
9165 // if we haven't already reported TvT results
9166 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)){
9167 multi_team_report();
9168 Multi_debrief_reported_tvt = 1;
9171 // if all other players are good to go, check the host
9173 // if he is ready to continue
9174 if(Netgame.host->state == NETPLAYER_STATE_DEBRIEF_ACCEPT){
9177 // if he wants to replay the mission
9178 else if(Netgame.host->state == NETPLAYER_STATE_DEBRIEF_REPLAY){
9181 // if he is not ready
9186 // if all players are _not_ good to go
9191 // if we're in the debriefing state in a campaign mode, process accordingly
9192 if(Netgame.campaign_mode == MP_CAMPAIGN){
9193 multi_campaign_do_debrief(player_status);
9195 // otherwise process as normal (looking for all players to be ready to go to the next mission
9197 if(player_status == 1){
9198 multi_flush_mission_stuff();
9200 // set the netgame state to be forming and continue
9201 Netgame.game_state = NETGAME_STATE_FORMING;
9202 send_netgame_update_packet();
9204 // move to the proper state
9205 if(Game_mode & GM_STANDALONE_SERVER){
9206 gameseq_post_event(GS_EVENT_STANDALONE_MAIN);
9208 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
9211 multi_reset_timestamps();
9212 } else if(player_status == 2){
9213 multi_flush_mission_stuff();
9215 // tell everyone to move into the pre-briefing sync state
9216 Netgame.game_state = NETGAME_STATE_MISSION_SYNC;
9217 send_netgame_update_packet();
9219 // move back to the mission sync screen for the same mission again
9220 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
9221 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
9223 multi_reset_timestamps();
9229 // -------------------------------------------------------------------------------------------------------------
9231 // MULTIPLAYER PASSWORD POPUP
9236 static char *Multi_pwd_bitmap_fname[GR_NUM_RESOLUTIONS] = {
9237 "Password", // GR_640
9238 "2_Password" // GR_1024
9241 static char *Multi_pwd_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
9242 "Password-M", // GR_640
9243 "2_Password-M" // GR_1024
9248 // constants for coordinate lookup
9249 #define MPWD_X_COORD 0
9250 #define MPWD_Y_COORD 1
9251 #define MPWD_W_COORD 2
9252 #define MPWD_H_COORD 3
9255 #define MULTI_PWD_NUM_BUTTONS 2
9256 #define MPWD_CANCEL 0
9257 #define MPWD_COMMIT 1
9259 // password area defs
9260 int Mpwd_coords[GR_NUM_RESOLUTIONS][4] = {
9269 UI_WINDOW Multi_pwd_window; // the window object for the join screen
9270 UI_INPUTBOX Multi_pwd_passwd; // for Netgame.passwd
9271 int Multi_pwd_bitmap; // the background bitmap
9272 int Multi_passwd_background = -1;
9273 int Multi_passwd_done = -1;
9274 int Multi_passwd_running = 0;
9277 ui_button_info Multi_pwd_buttons[GR_NUM_RESOLUTIONS][MULTI_PWD_NUM_BUTTONS] = {
9279 ui_button_info("PWB_00", 411, 151, 405, 141, 0),
9280 ui_button_info("PWB_01", 460, 151, 465, 141, 1),
9283 ui_button_info("2_PWB_00", 659, 242, 649, 225, 0),
9284 ui_button_info("2_PWB_01", 737, 242, 736, 225, 1),
9289 #define MULTI_PWD_NUM_TEXT 3
9290 UI_XSTR Multi_pwd_text[GR_NUM_RESOLUTIONS][MULTI_PWD_NUM_TEXT] = {
9292 { "Cancel", 387, 400, 141, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[0][MPWD_CANCEL].button},
9293 { "Commit", 1062, 455, 141, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[0][MPWD_COMMIT].button},
9294 { "Enter Password", 1332, 149, 92, UI_XSTR_COLOR_GREEN, -1, NULL},
9297 { "Cancel", 387, 649, 225, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[1][MPWD_CANCEL].button},
9298 { "Commit", 1062, 736, 225, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[1][MPWD_COMMIT].button},
9299 { "Enter Password", 1332, 239, 148, UI_XSTR_COLOR_GREEN, -1, NULL},
9303 // initialize all graphics, etc
9304 void multi_passwd_init()
9308 // store the background as it currently is
9309 Multi_passwd_background = gr_save_screen();
9311 // create the interface window
9312 Multi_pwd_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
9313 Multi_pwd_window.set_mask_bmap(Multi_pwd_bitmap_mask_fname[gr_screen.res]);
9315 // load the background bitmap
9316 Multi_pwd_bitmap = bm_load(Multi_pwd_bitmap_fname[gr_screen.res]);
9317 if(Multi_pwd_bitmap < 0){
9318 // we failed to load the bitmap - this is very bad
9322 // create the interface buttons
9323 for(idx=0; idx<MULTI_PWD_NUM_BUTTONS; idx++){
9324 // create the object
9325 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);
9327 // set the sound to play when highlighted
9328 Multi_pwd_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
9330 // set the ani for the button
9331 Multi_pwd_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pwd_buttons[gr_screen.res][idx].filename);
9334 Multi_pwd_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pwd_buttons[gr_screen.res][idx].hotspot);
9338 for(idx=0; idx<MULTI_PWD_NUM_TEXT; idx++){
9339 Multi_pwd_window.add_XSTR(&Multi_pwd_text[gr_screen.res][idx]);
9342 // create the password input box
9343 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);
9344 Multi_pwd_passwd.set_focus();
9346 // link the enter key to ACCEPT
9347 Multi_pwd_buttons[gr_screen.res][MPWD_COMMIT].button.set_hotkey(KEY_ENTER);
9349 Multi_passwd_done = -1;
9350 Multi_passwd_running = 1;
9353 // close down all graphics, etc
9354 void multi_passwd_close()
9356 // unload any bitmaps
9357 bm_release(Multi_pwd_bitmap);
9359 // destroy the UI_WINDOW
9360 Multi_pwd_window.destroy();
9362 // free up the saved background screen
9363 if(Multi_passwd_background >= 0){
9364 gr_free_screen(Multi_passwd_background);
9365 Multi_passwd_background = -1;
9368 Multi_passwd_running = 0;
9371 // process any button pressed
9372 void multi_passwd_process_buttons()
9374 // if the accept button was pressed
9375 if(Multi_pwd_buttons[gr_screen.res][MPWD_COMMIT].button.pressed()){
9376 gamesnd_play_iface(SND_USER_SELECT);
9377 Multi_passwd_done = 1;
9380 // if the cancel button was pressed
9381 if(Multi_pwd_buttons[gr_screen.res][MPWD_CANCEL].button.pressed()){
9382 gamesnd_play_iface(SND_USER_SELECT);
9383 Multi_passwd_done = 0;
9387 // run the passwd popup
9388 void multi_passwd_do(char *passwd)
9392 while(Multi_passwd_done == -1){
9393 // set frametime and run background stuff
9394 game_set_frametime(-1);
9395 game_do_state_common(gameseq_get_state());
9397 k = Multi_pwd_window.process();
9399 // process any keypresses
9402 // set this to indicate the user has cancelled for one reason or another
9403 Multi_passwd_done = 0;
9407 // if the input box text has changed
9408 if(Multi_pwd_passwd.changed()){
9410 Multi_pwd_passwd.get_text(passwd);
9413 // process any button pressed
9414 multi_passwd_process_buttons();
9416 // draw the background, etc
9419 if(Multi_passwd_background >= 0){
9420 gr_restore_screen(Multi_passwd_background);
9422 gr_set_bitmap(Multi_pwd_bitmap);
9424 Multi_pwd_window.draw();
9431 // bring up the password string popup, fill in passwd (return 1 if accept was pressed, 0 if cancel was pressed)
9432 int multi_passwd_popup(char *passwd)
9434 // if the popup is already running for some reason, don't do anything
9435 if(Multi_passwd_running){
9439 // initialize all graphics
9440 multi_passwd_init();
9443 multi_passwd_do(passwd);
9445 // shut everything down
9446 multi_passwd_close();
9448 return Multi_passwd_done;