2 * $Logfile: /Freespace2/code/Network/MultiUI.cpp $
7 * C file for all the UI controls of the mulitiplayer screens
10 * Revision 1.5 2002/06/02 00:31:35 relnev
11 * implemented osregistry
13 * Revision 1.4 2002/06/01 07:12:33 relnev
14 * a few NDEBUG updates.
16 * removed a few warnings.
18 * Revision 1.3 2002/05/26 20:49:54 theoddone33
21 * Revision 1.2 2002/05/07 03:16:47 theoddone33
22 * The Great Newline Fix
24 * Revision 1.1.1.1 2002/05/03 03:28:10 root
28 * 94 6/16/00 3:16p Jefff
29 * sim of the year dvd version changes, a few german soty localization
32 * 93 10/14/99 2:51p Jefff
35 * 92 10/13/99 3:50p Jefff
36 * fixed unnumbered XSTRs
38 * 91 9/15/99 1:45a Dave
39 * Don't init joystick on standalone. Fixed campaign mode on standalone.
40 * Fixed no-score-report problem in TvT
42 * 90 9/14/99 12:51a Jefff
45 * 89 9/13/99 4:52p Dave
48 * 88 9/13/99 11:30a Dave
49 * Added checkboxes and functionality for disabling PXO banners as well as
50 * disabling d3d zbuffer biasing.
52 * 87 9/12/99 10:06p Jefff
53 * changed instances of "Squad War" to "SquadWar"
55 * 86 9/03/99 1:32a Dave
56 * CD checking by act. Added support to play 2 cutscenes in a row
57 * seamlessly. Fixed super low level cfile bug related to files in the
58 * root directory of a CD. Added cheat code to set campaign mission # in
61 * 85 9/01/99 10:49p Dave
62 * Added nice SquadWar checkbox to the client join wait screen.
64 * 84 8/30/99 2:49p Jefff
66 * 83 8/26/99 8:49p Jefff
67 * Updated medals screen and about everything that ever touches medals in
68 * one way or another. Sheesh.
70 * 82 8/25/99 4:38p Dave
71 * Updated PXO stuff. Make squad war report stuff much more nicely.
73 * 81 8/20/99 2:09p Dave
76 * 80 8/20/99 10:06a Jefff
77 * removed closed/rstricted buttons from multi start screen
79 * 79 8/18/99 11:30a Jefff
81 * 78 8/18/99 10:38a Jefff
83 * 77 8/16/99 4:06p Dave
84 * Big honking checkin.
86 * 76 8/16/99 1:08p Jefff
87 * added sounds to a few controls, made input boxes lose focus on ENTER
89 * 75 8/16/99 9:52a Jefff
90 * fixed bitmap loading on buttons in multi-sync screen
92 * 74 8/11/99 5:54p Dave
93 * Fixed collision problem. Fixed standalone ghost problem.
95 * 73 8/10/99 4:35p Jefff
98 * 72 8/06/99 12:29a Dave
101 * 71 8/05/99 3:13p Jasenw
102 * tweaked some text placement coords.
104 * 70 8/04/99 1:38p Jefff
105 * moved some text in multi join wait
107 * 69 8/03/99 12:45p Dave
110 * 68 7/25/99 5:17p Jefff
111 * campaign descriptions show up on multicreate screen
113 * 67 7/20/99 1:49p Dave
114 * Peter Drake build. Fixed some release build warnings.
116 * 66 7/19/99 2:13p Dave
117 * Added some new strings for Heiko.
119 * 65 7/15/99 9:20a Andsager
120 * FS2_DEMO initial checkin
122 * 64 7/08/99 10:53a Dave
123 * New multiplayer interpolation scheme. Not 100% done yet, but still
124 * better than the old way.
126 * 63 6/30/99 10:49a Jasenw
127 * Fixed coords for new launch countdown ani
129 * 62 6/29/99 7:39p Dave
130 * Lots of small bug fixes.
132 * 61 6/25/99 11:59a Dave
133 * Multi options screen.
135 * 60 6/09/99 2:17p Dave
136 * Fixed up pleasewait bitmap rendering.
138 * 59 6/05/99 3:42p Dave
139 * New multi sync screen.
141 * 58 6/01/99 6:07p Dave
142 * New loading/pause/please wait bar.
144 * 57 5/21/99 6:45p Dave
145 * Sped up ui loading a bit. Sped up localization disk access stuff. Multi
146 * start game screen, multi password, and multi pxo-help screen.
148 * 56 5/06/99 11:10a Dave
149 * Fixed coord on multi create screen.
151 * 55 5/04/99 6:38p Dave
152 * Finished multi join-wait screen.
154 * 54 5/04/99 5:20p Dave
155 * Fixed up multiplayer join screen and host options screen. Should both
158 * 53 5/03/99 11:04p Dave
159 * Most of the way done with the multi join screen.
161 * 52 5/03/99 8:32p Dave
162 * New version of multi host options screen.
164 * 51 4/29/99 2:15p Neilk
165 * slider2 code got modified; changed parameters for create
167 * 50 4/25/99 3:02p Dave
168 * Build defines for the E3 build.
170 * 49 4/21/99 6:15p Dave
171 * Did some serious housecleaning in the beam code. Made it ready to go
172 * for anti-fighter "pulse" weapons. Fixed collision pair creation. Added
173 * a handy macro for recalculating collision pairs for a given object.
175 * 48 4/16/99 5:27p Neilk
176 * added slider support and hir res for multi_create
178 * 47 4/14/99 6:37p Dave
179 * Fixed scroll button bug on host create screen.
181 * 46 4/14/99 5:28p Dave
184 * 45 4/12/99 10:07p Dave
185 * Made network startup more forgiving. Added checkmarks to dogfight
186 * screen for players who hit commit.
188 * 44 4/09/99 2:21p Dave
189 * Multiplayer beta stuff. CD checking.
191 * 43 4/08/99 1:28p Dave
192 * Small bug fixes for refresh button on the multi create screen.
194 * 42 4/08/99 11:55a Neilk
195 * Converted Multi_Create to new artwork (just lowres)
197 * 41 4/08/99 2:10a Dave
198 * Numerous bug fixes for the beta. Added builtin mission info for the
201 * 40 3/20/99 3:48p Andsager
202 * Do mission_loop stuff for PXO
204 * 39 3/10/99 6:50p Dave
205 * Changed the way we buffer packets for all clients. Optimized turret
206 * fired packets. Did some weapon firing optimizations.
208 * 38 3/09/99 6:24p Dave
209 * More work on object update revamping. Identified several sources of
210 * unnecessary bandwidth.
212 * 37 3/08/99 7:03p Dave
213 * First run of new object update system. Looks very promising.
215 * 36 2/25/99 4:19p Dave
216 * Added multiplayer_beta defines. Added cd_check define. Fixed a few
217 * release build warnings. Added more data to the squad war request and
220 * 35 2/24/99 3:26p Anoop
221 * Make sure the host is the only guy who bashes skill level for TvT.
223 * 34 2/24/99 2:25p Dave
224 * Fixed up chatbox bugs. Made squad war reporting better. Fixed a respawn
225 * bug for dogfight more.
227 * 33 2/23/99 2:29p Dave
228 * First run of oldschool dogfight mode.
230 * 32 2/17/99 2:11p Dave
231 * First full run of squad war. All freespace and tracker side stuff
234 * 31 2/12/99 6:16p Dave
235 * Pre-mission Squad War code is 95% done.
237 * 30 2/11/99 3:08p Dave
238 * PXO refresh button. Very preliminary squad war support.
240 * 29 2/08/99 5:07p Dave
241 * FS2 chat server support. FS2 specific validated missions.
243 * 28 2/04/99 6:29p Dave
244 * First full working rev of FS2 PXO support. Fixed Glide lighting
247 * 27 1/30/99 5:08p Dave
248 * More new hi-res stuff.Support for nice D3D textures.
250 * 26 1/29/99 2:08a Dave
251 * Fixed beam weapon collisions with players. Reduced size of scoring
252 * struct for multiplayer. Disabled PXO.
254 * 25 1/15/99 2:36p Neilk
255 * fixed multi_jw coordinates
257 * 24 1/13/99 7:19p Neilk
258 * Converted Mission Brief, Barracks, Synch to high res support
260 * 23 1/12/99 7:17p Neilk
262 * 22 1/12/99 5:45p Dave
263 * Moved weapon pipeline in multiplayer to almost exclusively client side.
264 * Very good results. Bandwidth goes down, playability goes up for crappy
265 * connections. Fixed object update problem for ship subsystems.
267 * 21 1/12/99 4:07a Dave
268 * Put in barracks code support for selecting squad logos. Properly
269 * distribute squad logos in a multiplayer game.
271 * 20 1/11/99 7:19p Neilk
272 * Converted multi_join interface to support multiple resolutions
274 * 19 12/18/98 1:13a Dave
275 * Rough 1024x768 support for Direct3D. Proper detection and usage through
278 * 18 12/17/98 4:50p Andsager
279 * Added debrief_assemble_optional_mission_popup_text() for single and
282 * 17 12/14/98 12:13p Dave
283 * Spiffed up xfer system a bit. Put in support for squad logo file xfer.
286 * 16 12/10/98 10:19a Andsager
287 * Fix mission loop assert
289 * 15 12/10/98 9:59a Andsager
290 * Fix some bugs with mission loops
292 * 14 12/09/98 1:56p Andsager
293 * Initial checkin of mission loop
295 * 13 12/03/98 5:22p Dave
296 * Ported over Freespace 1 multiplayer ships.tbl and weapons.tbl
299 * 12 11/30/98 1:07p Dave
300 * 16 bit conversion, first run.
302 * 11 11/20/98 11:16a Dave
303 * Fixed up IPX support a bit. Making sure that switching modes and
304 * loading/saving pilot files maintains proper state.
306 * 10 11/19/98 4:57p Dave
307 * Ignore PXO option if IPX is selected.
309 * 9 11/19/98 4:19p Dave
310 * Put IPX sockets back in psnet. Consolidated all multiplayer config
313 * 8 11/19/98 8:04a Dave
314 * Full support for D3-style reliable sockets. Revamped packet lag/loss
315 * system, made it receiver side and at the lowest possible level.
317 * 7 11/17/98 11:12a Dave
318 * Removed player identification by address. Now assign explicit id #'s.
320 * 6 10/19/98 11:15a Dave
321 * Changed requirements for stats storing in PXO mode.
323 * 5 10/16/98 9:40a Andsager
324 * Remove ".h" files from model.h
326 * 4 10/13/98 9:29a Dave
327 * Started neatening up freespace.h. Many variables renamed and
328 * reorganized. Added AlphaColors.[h,cpp]
330 * 3 10/07/98 6:27p Dave
331 * Globalized mission and campaign file extensions. Removed Silent Threat
332 * special code. Moved \cache \players and \multidata into the \data
335 * 2 10/07/98 10:53a Dave
338 * 1 10/07/98 10:50a Dave
340 * 333 10/02/98 3:22p Allender
341 * fix up the -connect option and fix the -port option
343 * 332 9/17/98 9:26p Dave
344 * Externalized new string.
346 * 331 9/17/98 3:08p Dave
347 * PXO to non-pxo game warning popup. Player icon stuff in create and join
348 * game screens. Upped server count refresh time in PXO to 35 secs (from
351 * 330 9/17/98 9:43a Allender
352 * removed an Assert that Dave called bogus.
354 * 329 9/16/98 6:54p Dave
355 * Upped max sexpression nodes to 1800 (from 1600). Changed FRED to sort
356 * the ship list box. Added code so that tracker stats are not stored with
359 * 328 9/15/98 7:24p Dave
360 * Minor UI changes. Localized bunch of new text.
362 * 327 9/15/98 4:03p Dave
363 * Changed readyroom and multi screens to display "st" icon for all
364 * missions with mission disk content (not necessarily just those that
365 * come with Silent Threat).
367 * 326 9/15/98 11:44a Dave
368 * Renamed builtin ships and wepaons appropriately in FRED. Put in scoring
369 * scale factors. Fixed standalone filtering of MD missions to non-MD
372 * 325 9/13/98 9:36p Dave
373 * Support for new info icons for multiplayer missions (from-volition,
374 * valid, mission disk, etc).
376 * 324 9/11/98 4:14p Dave
377 * Fixed file checksumming of < file_size. Put in more verbose kicking and
378 * PXO stats store reporting.
380 * 323 9/10/98 1:17p Dave
381 * Put in code to flag missions and campaigns as being MD or not in Fred
382 * and Freespace. Put in multiplayer support for filtering out MD
383 * missions. Put in multiplayer popups for warning of non-valid missions.
385 * 322 9/04/98 3:51p Dave
386 * Put in validated mission updating and application during stats
389 * 321 8/31/98 2:06p Dave
390 * Make cfile sort the ordering or vp files. Added support/checks for
391 * recognizing "mission disk" players.
393 * 320 8/21/98 1:15p Dave
394 * Put in log system hooks in useful places.
396 * 319 8/20/98 5:31p Dave
397 * Put in handy multiplayer logfile system. Now need to put in useful
398 * applications of it all over the code.
400 * 318 8/12/98 4:53p Dave
401 * Put in 32 bit checksumming for PXO missions. No validation on the
402 * actual tracker yet, though.
404 * 317 8/07/98 10:40a Allender
405 * new command line flags for starting netgames. Only starting currently
406 * works, and PXO isn't implemented yet
408 * 316 7/24/98 9:27a Dave
409 * Tidied up endgame sequencing by removing several old flags and
410 * standardizing _all_ endgame stuff with a single function call.
412 * 315 7/14/98 10:04a Allender
413 * fixed the countdown code to not be reliant on timer_get_fixed_seconds
415 * 314 7/10/98 5:04p Dave
416 * Fix connection speed bug on standalone server.
418 * 313 7/09/98 6:01p Dave
419 * Firsts full version of PXO updater. Put in stub for displaying
422 * 312 7/07/98 2:49p Dave
425 * 311 6/30/98 2:17p Dave
426 * Revised object update system. Removed updates for all weapons. Put
427 * button info back into control info packet.
429 * 310 6/13/98 9:32p Mike
430 * Kill last character in file which caused "Find in Files" to report the
431 * file as "not a text file."
438 #include <winsock.h> // for inet_addr()
440 #include <sys/types.h>
441 #include <sys/socket.h>
442 #include <netinet/in.h>
443 #include <arpa/inet.h>
448 #include "multiutil.h"
449 #include "multimsgs.h"
455 #include "gamesequence.h"
456 #include "freespace.h"
457 #include "contexthelp.h"
462 #include "missionshipchoice.h"
463 #include "multi_xfer.h"
465 #include "stand_gui.h"
466 #include "linklist.h"
467 #include "multiteamselect.h"
468 #include "missioncampaign.h"
475 #include "missiondebrief.h"
476 #include "multi_ingame.h"
477 #include "multi_kick.h"
478 #include "multi_data.h"
479 #include "multi_campaign.h"
480 #include "multi_team.h"
481 #include "multi_pinfo.h"
482 #include "multi_observer.h"
483 #include "multi_voice.h"
484 #include "multi_endgame.h"
485 #include "managepilot.h"
488 #include "objcollide.h"
490 #include "multi_pmsg.h"
491 #include "multi_obj.h"
492 #include "multi_log.h"
493 #include "alphacolors.h"
494 #include "animplay.h"
495 #include "multi_dogfight.h"
496 #include "missionpause.h"
498 // -------------------------------------------------------------------------------------------------------------
500 // MULTIPLAYER COMMON interface controls
503 // the common text info box stuff. This is lifted almost directly from Alans briefing code (minus the spiffy colored, scrolling
505 int Multi_common_text_coords[GR_NUM_RESOLUTIONS][4] = {
514 int Multi_common_text_max_display[GR_NUM_RESOLUTIONS] = {
519 #define MULTI_COMMON_TEXT_META_CHAR '$'
520 #define MULTI_COMMON_TEXT_MAX_LINE_LENGTH 100
521 #define MULTI_COMMON_TEXT_MAX_LINES 20
522 #define MULTI_COMMON_MAX_TEXT (MULTI_COMMON_TEXT_MAX_LINES * MULTI_COMMON_TEXT_MAX_LINE_LENGTH)
524 char Multi_common_all_text[MULTI_COMMON_MAX_TEXT];
525 char Multi_common_text[MULTI_COMMON_TEXT_MAX_LINES][MULTI_COMMON_TEXT_MAX_LINE_LENGTH];
527 int Multi_common_top_text_line = -1; // where to start displaying from
528 int Multi_common_num_text_lines = 0; // how many lines we have
530 void multi_common_scroll_text_up();
531 void multi_common_scroll_text_down();
532 void multi_common_move_to_bottom();
533 void multi_common_render_text();
534 void multi_common_split_text();
536 #define MAX_IP_STRING 255 // maximum length for ip string
538 void multi_common_scroll_text_up()
540 Multi_common_top_text_line--;
541 if ( Multi_common_top_text_line < 0 ) {
542 Multi_common_top_text_line = 0;
543 if ( !mouse_down(MOUSE_LEFT_BUTTON) )
544 gamesnd_play_iface(SND_GENERAL_FAIL);
547 gamesnd_play_iface(SND_SCROLL);
551 void multi_common_scroll_text_down()
553 Multi_common_top_text_line++;
554 if ( (Multi_common_num_text_lines - Multi_common_top_text_line) < Multi_common_text_max_display[gr_screen.res] ) {
555 Multi_common_top_text_line--;
556 if ( !mouse_down(MOUSE_LEFT_BUTTON) ){
557 gamesnd_play_iface(SND_GENERAL_FAIL);
560 gamesnd_play_iface(SND_SCROLL);
564 void multi_common_move_to_bottom()
566 // if there's nowhere to scroll down, do nothing
567 if(Multi_common_num_text_lines <= Multi_common_text_max_display[gr_screen.res]){
571 Multi_common_top_text_line = Multi_common_num_text_lines - Multi_common_text_max_display[gr_screen.res];
574 void multi_common_set_text(char *str,int auto_scroll)
577 // store the entire string as well
578 if(strlen(str) > MULTI_COMMON_MAX_TEXT){
581 strcpy(Multi_common_all_text,str);
584 // split the whole thing up
585 multi_common_split_text();
587 // scroll to the bottom if we're supposed to
589 multi_common_move_to_bottom();
593 void multi_common_add_text(char *str,int auto_scroll)
596 // store the entire string as well
597 if((strlen(str) + strlen(Multi_common_all_text)) > MULTI_COMMON_MAX_TEXT){
600 strcat(Multi_common_all_text,str);
603 // split the whole thing up
604 multi_common_split_text();
606 // scroll to the bottom if we're supposed to
608 multi_common_move_to_bottom();
612 void multi_common_split_text()
615 int n_chars[MAX_BRIEF_LINES];
616 char *p_str[MAX_BRIEF_LINES];
618 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);
619 Assert(n_lines != -1);
621 for ( i = 0; i < n_lines; i++ ) {
622 Assert(n_chars[i] < MULTI_COMMON_TEXT_MAX_LINE_LENGTH);
623 strncpy(Multi_common_text[i], p_str[i], n_chars[i]);
624 Multi_common_text[i][n_chars[i]] = 0;
625 drop_leading_white_space(Multi_common_text[i]);
628 Multi_common_top_text_line = 0;
629 Multi_common_num_text_lines = n_lines;
632 void multi_common_render_text()
634 int i, fh, line_count;
636 fh = gr_get_font_height();
639 gr_set_color_fast(&Color_text_normal);
640 for ( i = Multi_common_top_text_line; i < Multi_common_num_text_lines; i++ ) {
641 if ( line_count >= Multi_common_text_max_display[gr_screen.res] ){
644 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]);
648 if ( (Multi_common_num_text_lines - Multi_common_top_text_line) > Multi_common_text_max_display[gr_screen.res] ) {
649 gr_set_color_fast(&Color_bright_red);
650 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));
654 // common notification messaging stuff
655 #define MULTI_COMMON_NOTIFY_TIME 3500
656 int Multi_common_join_y[GR_NUM_RESOLUTIONS] = {
660 int Multi_common_create_y[GR_NUM_RESOLUTIONS] = {
665 int Multi_common_jw_y[GR_NUM_RESOLUTIONS] = {
670 int Multi_common_msg_y[GR_NUM_RESOLUTIONS] = {
675 char Multi_common_notify_text[200];
676 int Multi_common_notify_stamp;
678 void multi_common_notify_init()
680 strcpy(Multi_common_notify_text,"");
681 Multi_common_notify_stamp = -1;
684 // add a notification string, drawing appropriately depending on the state/screen we're in
685 void multi_common_add_notify(char *str)
688 strcpy(Multi_common_notify_text,str);
689 Multi_common_notify_stamp = timestamp(MULTI_COMMON_NOTIFY_TIME);
693 // process/display notification messages
694 void multi_common_notify_do()
696 if(Multi_common_notify_stamp != -1){
697 if(timestamp_elapsed(Multi_common_notify_stamp)){
698 Multi_common_notify_stamp = -1;
701 gr_get_string_size(&w,&h,Multi_common_notify_text);
702 gr_set_color_fast(&Color_white);
704 // determine where it should be placed based upon which screen we're on
706 switch(gameseq_get_state()){
707 case GS_STATE_MULTI_JOIN_GAME :
708 y = Multi_common_join_y[gr_screen.res];
710 case GS_STATE_MULTI_HOST_SETUP :
711 y = Multi_common_create_y[gr_screen.res];
713 case GS_STATE_MULTI_CLIENT_SETUP :
714 y = Multi_common_jw_y[gr_screen.res];
716 case GS_STATE_MULTI_START_GAME :
717 y = Multi_common_msg_y[gr_screen.res];
721 gr_string((gr_screen.max_w - w)/2, y, Multi_common_notify_text);
728 int Multi_common_icons[MULTI_NUM_COMMON_ICONS];
730 char *Multi_common_icon_names[MULTI_NUM_COMMON_ICONS] = {
731 "DotRed", // voice denied
732 "DotGreen", // voice recording
733 "OvalGreen", // team 0
734 "OvalGreen01", // team 0 select
736 "OvalRed01", // team 1 select
737 "mp_coop", // coop mission
738 "mp_teams", // TvT mission
739 "mp_furball", // furball mission
740 "icon-volition", // volition mission
741 "icon-valid", // mission is valid
745 // width and height of the icons
746 int Multi_common_icon_dims[MULTI_NUM_COMMON_ICONS][2] = {
747 {11, 11}, // voice denied
748 {11, 11}, // voice recording
750 {11, 11}, // team 0 select
752 {11, 11}, // team 1 select
755 {18, 11}, // mp furball
756 {9, 9}, // volition mission
757 {8, 8}, // mission is valid
761 void multi_load_common_icons()
766 for(idx=0; idx<MULTI_NUM_COMMON_ICONS; idx++){
767 Multi_common_icons[idx] = -1;
768 Multi_common_icons[idx] = bm_load(Multi_common_icon_names[idx]);
772 void multi_unload_common_icons()
777 for(idx=0; idx<MULTI_NUM_COMMON_ICONS; idx++){
778 if(Multi_common_icons[idx] != -1){
779 bm_unload(Multi_common_icons[idx]);
780 Multi_common_icons[idx] = -1;
785 // display any relevant voice status icons
786 void multi_common_voice_display_status()
788 switch(multi_voice_status()){
789 // i have been denied the voice token
790 case MULTI_VOICE_STATUS_DENIED:
791 if(Multi_common_icons[MICON_VOICE_DENIED] != -1){
792 gr_set_bitmap(Multi_common_icons[MICON_VOICE_DENIED]);
797 // i am currently recording
798 case MULTI_VOICE_STATUS_RECORDING:
799 if(Multi_common_icons[MICON_VOICE_RECORDING] != -1){
800 gr_set_bitmap(Multi_common_icons[MICON_VOICE_RECORDING]);
805 // i am currently playing back sound
806 case MULTI_VOICE_STATUS_PLAYING:
809 // the system is currently idle
810 case MULTI_VOICE_STATUS_IDLE:
816 // palette initialization stuff
817 #define MULTI_COMMON_PALETTE_FNAME "InterfacePalette"
820 int Multi_common_interface_palette = -1;
822 void multi_common_load_palette();
823 void multi_common_set_palette();
824 void multi_common_unload_palette();
826 // load in the palette if it doesn't already exist
827 void multi_common_load_palette()
829 if(Multi_common_interface_palette != -1){
833 Multi_common_interface_palette = bm_load(MULTI_COMMON_PALETTE_FNAME);
834 if(Multi_common_interface_palette == -1){
835 nprintf(("Network","Error loading multiplayer common palette!\n"));
839 // set the common palette to be the active one
840 void multi_common_set_palette()
842 // if the palette is not loaded yet, do so now
843 if(Multi_common_interface_palette == -1){
844 multi_common_load_palette();
847 if(Multi_common_interface_palette != -1){
848 #ifndef HARDWARE_ONLY
849 palette_use_bm_palette(Multi_common_interface_palette);
854 // unload the bitmap palette
855 void multi_common_unload_palette()
857 if(Multi_common_interface_palette != -1){
858 bm_unload(Multi_common_interface_palette);
859 Multi_common_interface_palette = -1;
863 void multi_common_verify_cd()
866 // otherwise, call the freespace function to determine if we have a cd
868 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) ){
877 // -------------------------------------------------------------------------------------------------------------
879 // MULTIPLAYER JOIN SCREEN
882 #define MULTI_JOIN_NUM_BUTTONS 11
886 #define MULTI_JOIN_PALETTE "InterfacePalette"
888 static char *Multi_join_bitmap_fname[GR_NUM_RESOLUTIONS] = {
889 "MultiJoin", // GR_640
890 "2_MultiJoin" // GR_1024
893 static char *Multi_join_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
894 "MultiJoin-M", // GR_640
895 "2_MultiJoin-M" // GR_1024
900 char *Mj_slider_name[GR_NUM_RESOLUTIONS] = {
904 int Mj_slider_coords[GR_NUM_RESOLUTIONS][4] = {
914 #define MJ_SCROLL_UP 0
915 #define MJ_SCROLL_DOWN 1
917 #define MJ_SCROLL_INFO_UP 3
918 #define MJ_SCROLL_INFO_DOWN 4
919 #define MJ_JOIN_OBSERVER 5
920 #define MJ_START_GAME 6
926 // uses MULTI_JOIN_REFRESH_TIME as its timestamp
927 int Multi_join_glr_stamp;
929 #define MULTI_JOIN_PING_TIME 15000 // how often we ping all the known servers
930 int Multi_join_ping_stamp;
931 UI_WINDOW Multi_join_window; // the window object for the join screen
932 UI_BUTTON Multi_join_select_button; // for selecting list items
933 UI_SLIDER2 Multi_join_slider; // handy dandy slider
934 int Multi_join_bitmap; // the background bitmap
936 ui_button_info Multi_join_buttons[GR_NUM_RESOLUTIONS][MULTI_JOIN_NUM_BUTTONS] = {
938 ui_button_info( "MJ_00", 1, 57, -1, -1, 0 ), // scroll up
939 ui_button_info( "MJ_02", 1, 297, -1, -1, 2 ), // scroll down
940 ui_button_info( "MJ_03", 10, 338, 65, 364, 3 ), // refresh
941 ui_button_info( "MJ_04", 1, 405, -1, -1, 4 ), // scroll info up
942 ui_button_info( "MJ_05", 1, 446, -1, -1, 5 ), // scroll info down
943 ui_button_info( "MJ_06", 489, 339, -1, -1, 6 ), // join as observer
944 ui_button_info( "MJ_07", 538, 339, -1, -1, 7 ), // create game
945 ui_button_info( "MJ_08", 583, 339, 588, 376, 8 ), // cancel
946 ui_button_info( "MJ_09", 534, 426, -1, -1, 9 ), // help
947 ui_button_info( "MJ_10", 534, 454, -1, -1, 10 ), // options
948 ui_button_info( "MJ_11", 571, 426, 589, 416, 11 ), // join
951 ui_button_info( "2_MJ_00", 2, 92, -1, -1, 0 ), // scroll up
952 ui_button_info( "2_MJ_02", 2, 475, -1, -1, 2 ), // scroll down
953 ui_button_info( "2_MJ_03", 16, 541, 104, 582, 3 ), // refresh
954 ui_button_info( "2_MJ_04", 2, 648, -1, -1, 4 ), // scroll info up
955 ui_button_info( "2_MJ_05", 2, 713, -1, -1, 5 ), // scroll info down
956 ui_button_info( "2_MJ_06", 783, 542, -1, -1, 6 ), // join as observer
957 ui_button_info( "2_MJ_07", 861, 542, -1, -1, 7 ), // create game
958 ui_button_info( "2_MJ_08", 933, 542, 588, 376, 8 ), // cancel
959 ui_button_info( "2_MJ_09", 854, 681, -1, -1, 9 ), // help
960 ui_button_info( "2_MJ_10", 854, 727, -1, -1, 10 ), // options
961 ui_button_info( "2_MJ_11", 914, 681, 937, 668, 11 ), // join
965 #define MULTI_JOIN_NUM_TEXT 13
967 UI_XSTR Multi_join_text[GR_NUM_RESOLUTIONS][MULTI_JOIN_NUM_TEXT] = {
969 {"Refresh", 1299, 65, 364, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_REFRESH].button},
970 {"Join as", 1300, 476, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_JOIN_OBSERVER].button},
971 {"Observer", 1301, 467, 385, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_JOIN_OBSERVER].button},
972 {"Create", 1408, 535, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_START_GAME].button},
973 {"Game", 1302, 541, 385, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_START_GAME].button},
974 {"Cancel", 387, 588, 376, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[0][MJ_CANCEL].button},
975 {"Help", 928, 479, 436, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_HELP].button},
976 {"Options", 1036, 479, 460, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_OPTIONS].button},
977 {"Join", 1303, 589, 416, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[0][MJ_ACCEPT].button},
978 {"Status", 1304, 37, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
979 {"Server", 1305, 116, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
980 {"Players", 1306, 471, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
981 {"Ping", 1307, 555, 37, UI_XSTR_COLOR_GREEN, -1, NULL}
984 {"Refresh", 1299, 104, 582, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_REFRESH].button},
985 {"Join as", 1300, 783, 602, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_JOIN_OBSERVER].button},
986 {"Observer", 1301, 774, 611, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_JOIN_OBSERVER].button},
987 {"Create", 1408, 868, 602, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_START_GAME].button},
988 {"Game", 1302, 872, 611, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_START_GAME].button},
989 {"Cancel", 387, 941, 602, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[1][MJ_CANCEL].button},
990 {"Help", 928, 782, 699, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_HELP].button},
991 {"Options", 1036, 782, 736, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_OPTIONS].button},
992 {"Join", 1303, 937, 668, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[1][MJ_ACCEPT].button},
993 {"Status", 1304, 60, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
994 {"Server", 1305, 186, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
995 {"Players", 1306, 753, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
996 {"Ping", 1307, 888, 60, UI_XSTR_COLOR_GREEN, -1, NULL}
1000 // constants for coordinate look ups
1001 #define MJ_X_COORD 0
1002 #define MJ_Y_COORD 1
1003 #define MJ_W_COORD 2
1004 #define MJ_H_COORD 3
1006 #define MULTI_JOIN_SENT_WAIT 10000 // wait this long since a join was sent to allow another
1007 int Multi_join_sent_stamp;
1009 // game information text areas
1010 int Mj_max_game_items[GR_NUM_RESOLUTIONS] = {
1015 int Mj_list_y[GR_NUM_RESOLUTIONS] = {
1020 int Mj_status_coords[GR_NUM_RESOLUTIONS][4] = {
1029 int Mj_game_icon_coords[GR_NUM_RESOLUTIONS][3] = {
1038 int Mj_speed_coords[GR_NUM_RESOLUTIONS][4] = {
1047 int Mj_game_name_coords[GR_NUM_RESOLUTIONS][4] = {
1056 int Mj_players_coords[GR_NUM_RESOLUTIONS][4] = {
1065 int Mj_ping_coords[GR_NUM_RESOLUTIONS][4] = {
1074 // game speed labels
1075 #define MJ_NUM_SPEED_LABELS 5
1076 char *Multi_join_speed_labels[MJ_NUM_SPEED_LABELS] = {
1083 color *Multi_join_speed_colors[MJ_NUM_SPEED_LABELS] = {
1086 &Color_bright_green,
1087 &Color_bright_green,
1091 int Mj_cd_coords[GR_NUM_RESOLUTIONS] = {
1096 #define IP_CONFIG_FNAME "tcp.cfg" // name of the file which contains known TCP addresses
1098 // extents of the entire boundable game info region
1099 // NOTE : these numbers are completely empirical
1100 #define MJ_PING_GREEN 160
1101 #define MJ_PING_YELLOW 300
1103 int Mj_list_area_coords[GR_NUM_RESOLUTIONS][4] = {
1112 // PXO channel filter
1113 #define MJ_PXO_FILTER_Y 0
1115 // special chars to indicate various status modes for servers
1116 #define MJ_CHAR_STANDALONE "*"
1117 #define MJ_CHAR_CAMPAIGN "c"
1120 // various interface indices
1121 int Multi_join_list_start; // where to start displaying from
1122 active_game *Multi_join_list_start_item; // a pointer to the corresponding active_game
1123 int Multi_join_list_selected; // which item we have selected
1124 active_game *Multi_join_selected_item; // a pointer to the corresponding active_game
1126 // use this macro to modify the list start
1127 #define MJ_LIST_START_INC() do { Multi_join_list_start++; } while(0);
1128 #define MJ_LIST_START_DEC() do { Multi_join_list_start--; } while(0);
1129 #define MJ_LIST_START_SET(vl) do { Multi_join_list_start = vl; } while(0);
1131 // if we should be sending a join request at the end of the frame
1132 int Multi_join_should_send = -1;
1134 // master tracker details
1135 int Multi_join_frame_count; // keep a count of frames displayed
1136 int Multi_join_mt_tried_verify; // already tried verifying the pilot with the tracker
1138 // data stuff for auto joining a game
1139 #define MULTI_AUTOJOIN_JOIN_STAMP 2000
1140 #define MULTI_AUTOJOIN_QUERY_STAMP 2000
1142 int Multi_did_autojoin;
1143 net_addr Multi_autojoin_addr;
1144 int Multi_autojoin_join_stamp;
1145 int Multi_autojoin_query_stamp;
1148 join_request Multi_join_request;
1150 // LOCAL function definitions
1151 void multi_join_check_buttons();
1152 void multi_join_button_pressed(int n);
1153 void multi_join_display_games();
1154 void multi_join_blit_game_status(active_game *game, int y);
1155 void multi_join_load_tcp_addrs();
1156 void multi_join_do_netstuff();
1157 void multi_join_ping_all();
1158 void multi_join_process_select();
1159 void multi_join_list_scroll_up();
1160 void multi_join_list_scroll_down();
1161 void multi_join_list_page_up();
1162 void multi_join_list_page_down();
1163 active_game *multi_join_get_game(int n);
1164 void multi_join_cull_timeouts();
1165 void multi_join_handle_item_cull(active_game *item, int item_index);
1166 void multi_join_send_join_request(int as_observer);
1167 void multi_join_create_game();
1168 void multi_join_blit_top_stuff();
1169 int multi_join_maybe_warn();
1170 int multi_join_warn_pxo();
1171 void multi_join_blit_protocol();
1175 active_game ag, *newitem;;
1178 dc_get_arg(ARG_INT);
1179 for(idx=0; idx<Dc_arg_int; idx++){
1180 // stuff some fake info
1181 memset(&ag, 0, sizeof(active_game));
1182 sprintf(ag.name, "Game %d", idx);
1183 ag.version = MULTI_FS_SERVER_VERSION;
1184 ag.comp_version = MULTI_FS_SERVER_VERSION;
1185 ag.server_addr.addr[0] = (char)idx;
1186 ag.flags = (AG_FLAG_COOP | AG_FLAG_FORMING | AG_FLAG_STANDALONE);
1189 newitem = multi_update_active_games(&ag);
1191 // timestamp it so we get random timeouts
1192 if(newitem != NULL){
1193 // newitem->heard_from_timer = timestamp((int)frand_range(500.0f, 10000.0f));
1198 void multi_join_notify_new_game()
1200 // reset the # of items
1201 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);
1202 Multi_join_slider.force_currentItem(Multi_join_list_start);
1205 int multi_join_autojoin_do()
1207 // if we have an active game on the list, then return a positive value so that we
1208 // can join the game
1209 if ( Active_game_head && (Active_game_count > 0) ) {
1210 Multi_join_selected_item = Active_game_head;
1214 // send out a server_query again
1215 if ( timestamp_elapsed(Multi_autojoin_query_stamp) ) {
1216 send_server_query(&Multi_autojoin_addr);
1217 Multi_autojoin_query_stamp = timestamp(MULTI_AUTOJOIN_QUERY_STAMP);
1223 void multi_join_game_init()
1227 // do the multiplayer init stuff - multi_level_init() now does all net_player zeroing.
1228 // setup various multiplayer things
1229 Assert( Game_mode & GM_MULTIPLAYER );
1230 Assert( Net_player != NULL );
1232 switch (Multi_options_g.protocol) {
1234 ADDRESS_LENGTH = IPX_ADDRESS_LENGTH;
1235 PORT_LENGTH = IPX_PORT_LENGTH;
1239 ADDRESS_LENGTH = IP_ADDRESS_LENGTH;
1240 PORT_LENGTH = IP_PORT_LENGTH;
1249 memset( &Netgame, 0, sizeof(Netgame) );
1252 Net_player->flags |= NETINFO_FLAG_DO_NETWORKING;
1253 Net_player->player = Player;
1254 memcpy(&Net_player->p_info.addr,&Psnet_my_addr,sizeof(net_addr));
1256 // check for the existence of a CD
1257 multi_common_verify_cd();
1259 // load my local netplayer options
1260 multi_options_local_load(&Net_player->p_info.options, Net_player);
1265 // common_set_interface_palette(MULTI_JOIN_PALETTE);
1267 // destroy any chatbox contents which previously existed (from another game)
1270 // create the interface window
1271 Multi_join_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
1272 Multi_join_window.set_mask_bmap(Multi_join_bitmap_mask_fname[gr_screen.res]);
1274 // load the background bitmap
1275 Multi_join_bitmap = bm_load(Multi_join_bitmap_fname[gr_screen.res]);
1276 if(Multi_join_bitmap < 0){
1277 // we failed to load the bitmap - this is very bad
1281 // intialize the endgame system
1282 multi_endgame_init();
1284 // initialize the common notification messaging
1285 multi_common_notify_init();
1287 // initialize the common text area
1288 multi_common_set_text("");
1290 // load and use the common interface palette
1291 multi_common_load_palette();
1292 multi_common_set_palette();
1294 // load the help overlay
1295 help_overlay_load(MULTI_JOIN_OVERLAY);
1296 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1298 // do TCP and VMT specific initialization
1299 if(Multi_options_g.protocol == NET_TCP){
1300 // if this is a TCP (non tracker) game, we'll load up our default address list right now
1301 multi_join_load_tcp_addrs();
1304 // initialize any and all timestamps
1305 Multi_join_glr_stamp = -1;
1306 Multi_join_ping_stamp = -1;
1307 Multi_join_sent_stamp = -1;
1309 // reset frame count
1310 Multi_join_frame_count = 0;
1312 // haven't tried to verify on the tracker yet.
1313 Multi_join_mt_tried_verify = 0;
1315 // clear our all game lists to save hassles
1316 multi_join_clear_game_list();
1318 // create the interface buttons
1319 for(idx=0; idx<MULTI_JOIN_NUM_BUTTONS; idx++){
1320 // create the object
1321 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);
1323 // set the sound to play when highlighted
1324 Multi_join_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
1326 // set the ani for the button
1327 Multi_join_buttons[gr_screen.res][idx].button.set_bmaps(Multi_join_buttons[gr_screen.res][idx].filename);
1330 Multi_join_buttons[gr_screen.res][idx].button.link_hotspot(Multi_join_buttons[gr_screen.res][idx].hotspot);
1334 for(idx=0; idx<MULTI_JOIN_NUM_TEXT; idx++){
1335 Multi_join_window.add_XSTR(&Multi_join_text[gr_screen.res][idx]);
1338 Multi_join_should_send = -1;
1340 // close any previously open chatbox
1343 // create the list item select button
1344 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);
1345 Multi_join_select_button.hide();
1348 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);
1350 // if starting a network game, then go to the create game screen
1351 if ( Cmdline_start_netgame ) {
1352 multi_join_create_game();
1353 } else if ( Cmdline_connect_addr != NULL ) {
1358 // joining a game. Send a join request to the given IP address, and wait for the return.
1359 memset( &Multi_autojoin_addr, 0, sizeof(net_addr) );
1360 Multi_autojoin_addr.type = NET_TCP;
1362 // create the address, looking out for port number at the end
1363 port_num = DEFAULT_GAME_PORT;
1364 p = strrchr(Cmdline_connect_addr, ':');
1368 port_num = (short)atoi(p);
1370 ip_addr = inet_addr(Cmdline_connect_addr);
1371 memcpy(Multi_autojoin_addr.addr, &ip_addr, 4);
1372 Multi_autojoin_addr.port = port_num;
1374 send_server_query(&Multi_autojoin_addr);
1375 Multi_autojoin_query_stamp = timestamp(MULTI_AUTOJOIN_QUERY_STAMP);
1376 Multi_did_autojoin = 0;
1380 void multi_join_clear_game_list()
1383 Multi_join_list_selected = -1;
1384 Multi_join_selected_item = NULL;
1385 MJ_LIST_START_SET(-1);
1386 Multi_join_list_start_item = NULL;
1388 // free up the active game list
1389 multi_free_active_games();
1391 // initialize the active game list
1392 Active_game_head = NULL;
1393 Active_game_count = 0;
1396 void multi_join_game_do_frame()
1398 // check the status of our reliable socket. If not valid, popup error and return to main menu
1399 // I put this code here to avoid nasty gameseq issues with states. Also, we will have nice
1400 // background for the popup
1401 if ( !psnet_rel_check() ) {
1402 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));
1403 gameseq_post_event(GS_EVENT_MAIN_MENU);
1407 // return here since we will be moving to the next stage anyway -- I don't want to see the backgrounds of
1408 // all the screens for < 1 second for every screen we automatically move to.
1409 if ( Cmdline_start_netgame ) {
1413 // when joining a network game, wait for the server query to come back, and then join the game
1414 if ( Cmdline_connect_addr != NULL ) {
1417 if ( !Multi_did_autojoin ) {
1418 rval = popup_till_condition(multi_join_autojoin_do, XSTR("&Cancel", 779), XSTR("Joining netgame", 1500) );
1420 // cancel was hit. Send the user back to the main hall
1421 gameseq_post_event(GS_EVENT_MAIN_MENU);
1422 Cmdline_connect_addr = NULL; // reset this value.
1425 // when we get here, we have the data -- join the game.
1426 multi_join_send_join_request(0);
1427 Multi_autojoin_join_stamp = timestamp(MULTI_AUTOJOIN_JOIN_STAMP);
1428 Multi_did_autojoin = 1;
1431 if ( timestamp_elapsed(Multi_autojoin_join_stamp) ) {
1432 multi_join_send_join_request(0);
1433 Multi_autojoin_join_stamp = timestamp(MULTI_AUTOJOIN_JOIN_STAMP);
1439 // reset the should send var
1440 Multi_join_should_send = -1;
1442 int k = Multi_join_window.process();
1444 // process any keypresses
1447 if(help_overlay_active(MULTI_JOIN_OVERLAY)){
1448 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1450 gameseq_post_event(GS_EVENT_MAIN_MENU);
1451 gamesnd_play_iface(SND_USER_SELECT);
1455 // page up the game list
1457 multi_join_list_page_up();
1458 Multi_join_slider.force_currentItem(Multi_join_list_start);
1462 multi_pinfo_popup(Net_player);
1465 // page down the game list
1467 multi_join_list_page_down();
1468 Multi_join_slider.force_currentItem(Multi_join_list_start);
1471 // send out a ping-all
1473 multi_join_ping_all();
1474 Multi_join_ping_stamp = timestamp(MULTI_JOIN_PING_TIME);
1477 // shortcut to start a game
1479 multi_join_create_game();
1482 // scroll the game list up
1484 multi_join_list_scroll_up();
1485 Multi_join_slider.force_currentItem(Multi_join_list_start);
1488 // scroll the game list down
1490 multi_join_list_scroll_down();
1491 Multi_join_slider.force_currentItem(Multi_join_list_start);
1495 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
1496 help_overlay_set_state(MULTI_JOIN_OVERLAY, 0);
1499 // do any network related stuff
1500 multi_join_do_netstuff();
1502 // process any button clicks
1503 multi_join_check_buttons();
1505 // process any list selection stuff
1506 multi_join_process_select();
1508 // draw the background, etc
1510 GR_MAYBE_CLEAR_RES(Multi_join_bitmap);
1511 if(Multi_join_bitmap != -1){
1512 gr_set_bitmap(Multi_join_bitmap);
1515 Multi_join_window.draw();
1517 // display the active games
1518 multi_join_display_games();
1520 // display any text in the info area
1521 multi_common_render_text();
1523 // display any pending notification messages
1524 multi_common_notify_do();
1526 // blit the CD icon and any PXO filter stuff
1527 multi_join_blit_top_stuff();
1529 // draw the help overlay
1530 help_overlay_maybe_blit(MULTI_JOIN_OVERLAY);
1535 // if we are supposed to be sending a join request
1536 if(Multi_join_should_send != -1){
1537 multi_join_send_join_request(Multi_join_should_send);
1539 Multi_join_should_send = -1;
1541 // increment the frame count
1542 Multi_join_frame_count++;
1545 void multi_join_game_close()
1547 // unload any bitmaps
1548 if(!bm_unload(Multi_join_bitmap)){
1549 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_join_bitmap_fname[gr_screen.res]));
1552 // unload the help overlay
1553 help_overlay_unload(MULTI_JOIN_OVERLAY);
1555 // free up the active game list
1556 multi_free_active_games();
1558 // destroy the UI_WINDOW
1559 Multi_join_window.destroy();
1562 void multi_join_check_buttons()
1565 for(idx=0;idx<MULTI_JOIN_NUM_BUTTONS;idx++){
1566 // we only really need to check for one button pressed at a time, so we can break after
1568 if(Multi_join_buttons[gr_screen.res][idx].button.pressed()){
1569 multi_join_button_pressed(idx);
1575 void multi_join_button_pressed(int n)
1579 // if we're player PXO, go back there
1580 gameseq_post_event(GS_EVENT_MAIN_MENU);
1581 gamesnd_play_iface(SND_USER_SELECT);
1584 if(Active_game_count <= 0){
1585 multi_common_add_notify(XSTR("No games found!",757));
1586 gamesnd_play_iface(SND_GENERAL_FAIL);
1587 } else if(Multi_join_list_selected == -1){
1588 multi_common_add_notify(XSTR("No game selected!",758));
1589 gamesnd_play_iface(SND_GENERAL_FAIL);
1590 } else if((Multi_join_sent_stamp != -1) && !timestamp_elapsed(Multi_join_sent_stamp)){
1591 multi_common_add_notify(XSTR("Still waiting on previous join request!",759));
1592 gamesnd_play_iface(SND_GENERAL_FAIL);
1594 // otherwise, if he's already played PXO games, warn him
1596 if(Player->flags & PLAYER_FLAGS_HAS_PLAYED_PXO){
1597 if(!multi_join_warn_pxo()){
1603 // send the join request here
1604 Assert(Multi_join_selected_item != NULL);
1606 // send a join request packet
1607 Multi_join_should_send = 0;
1609 gamesnd_play_iface(SND_COMMIT_PRESSED);
1615 if(!help_overlay_active(MULTI_JOIN_OVERLAY)){
1616 help_overlay_set_state(MULTI_JOIN_OVERLAY,1);
1618 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1622 // scroll the game list up
1624 multi_join_list_scroll_up();
1625 Multi_join_slider.force_currentItem(Multi_join_list_start);
1628 // scroll the game list down
1629 case MJ_SCROLL_DOWN:
1630 multi_join_list_scroll_down();
1631 Multi_join_slider.force_currentItem(Multi_join_list_start);
1634 // scroll the info text box up
1635 case MJ_SCROLL_INFO_UP:
1636 multi_common_scroll_text_up();
1639 // scroll the info text box down
1640 case MJ_SCROLL_INFO_DOWN:
1641 multi_common_scroll_text_down();
1644 // go to the options screen
1646 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
1649 // go to the start game screen
1651 multi_join_create_game();
1654 // refresh the game/server list
1656 gamesnd_play_iface(SND_USER_SELECT);
1657 broadcast_game_query();
1660 // join a game as an observer
1661 case MJ_JOIN_OBSERVER:
1662 if(Active_game_count <= 0){
1663 multi_common_add_notify(XSTR("No games found!",757));
1664 gamesnd_play_iface(SND_GENERAL_FAIL);
1665 } else if(Multi_join_list_selected == -1){
1666 multi_common_add_notify(XSTR("No game selected!",758));
1667 gamesnd_play_iface(SND_GENERAL_FAIL);
1668 } else if((Multi_join_sent_stamp != -1) && !timestamp_elapsed(Multi_join_sent_stamp)){
1669 multi_common_add_notify(XSTR("Still waiting on previous join request!",759));
1670 gamesnd_play_iface(SND_GENERAL_FAIL);
1672 // send the join request here
1673 Assert(Multi_join_selected_item != NULL);
1675 Multi_join_should_send = 1;
1677 gamesnd_play_iface(SND_COMMIT_PRESSED);
1682 multi_common_add_notify(XSTR("Not implemented yet!",760));
1683 gamesnd_play_iface(SND_GENERAL_FAIL);
1688 // display all relevant info for active games
1689 void multi_join_display_games()
1691 active_game *moveup = Multi_join_list_start_item;
1695 int y_start = Mj_list_y[gr_screen.res];
1700 // blit the game status (including text and type icon)
1701 multi_join_blit_game_status(moveup,y_start);
1703 // get the connection type
1704 con_type = (moveup->flags & AG_FLAG_CONNECTION_SPEED_MASK) >> AG_FLAG_CONNECTION_BIT;
1705 if((con_type > 4) || (con_type < 0)){
1709 // display the connection speed
1711 strcpy(str, Multi_join_speed_labels[con_type]);
1712 gr_set_color_fast(Multi_join_speed_colors[con_type]);
1713 gr_string(Mj_speed_coords[gr_screen.res][MJ_X_COORD], y_start, str);
1715 // we'll want to have different colors for highlighted items, etc.
1716 if(moveup == Multi_join_selected_item){
1717 gr_set_color_fast(&Color_text_selected);
1719 gr_set_color_fast(&Color_text_normal);
1722 // display the game name, adding appropriate status chars
1724 if(moveup->flags & AG_FLAG_STANDALONE){
1725 strcat(str,MJ_CHAR_STANDALONE);
1727 if(moveup->flags & AG_FLAG_CAMPAIGN){
1728 strcat(str,MJ_CHAR_CAMPAIGN);
1731 // tack on the actual server name
1733 strcat(str,moveup->name);
1734 if(strlen(moveup->mission_name) > 0){
1736 strcat(str,moveup->mission_name);
1739 // make sure the string fits in the display area and draw it
1740 gr_force_fit_string(str,200,Mj_game_name_coords[gr_screen.res][MJ_W_COORD]);
1741 gr_string(Mj_game_name_coords[gr_screen.res][MJ_X_COORD],y_start,str);
1743 // display the ping time
1744 if(moveup->ping.ping_avg > 0){
1745 if(moveup->ping.ping_avg > 1000){
1746 gr_set_color_fast(&Color_bright_red);
1747 strcpy(str,XSTR("> 1 sec",761));
1749 // set the appropriate ping time color indicator
1750 if(moveup->ping.ping_avg > MJ_PING_YELLOW){
1751 gr_set_color_fast(&Color_bright_red);
1752 } else if(moveup->ping.ping_avg > MJ_PING_YELLOW){
1753 gr_set_color_fast(&Color_bright_yellow);
1755 gr_set_color_fast(&Color_bright_green);
1758 sprintf(str,"%d",moveup->ping.ping_avg);
1759 strcat(str,XSTR(" ms",762)); // [[ Milliseconds ]]
1762 gr_string(Mj_ping_coords[gr_screen.res][MJ_X_COORD],y_start,str);
1765 // display the number of players (be sure to center it)
1766 if(moveup == Multi_join_selected_item){
1767 gr_set_color_fast(&Color_text_selected);
1769 gr_set_color_fast(&Color_text_normal);
1771 sprintf(str,"%d",moveup->num_players);
1772 gr_get_string_size(&w,&h,str);
1773 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);
1777 moveup = moveup->next;
1778 } while((moveup != Active_game_head) && (count < Mj_max_game_items[gr_screen.res]));
1780 // if there are no items on the list, display this info
1782 gr_set_color_fast(&Color_bright);
1783 gr_string(Mj_game_name_coords[gr_screen.res][MJ_X_COORD] - 30,y_start,XSTR("<No game servers found>",763));
1787 void multi_join_blit_game_status(active_game *game, int y)
1790 char status_text[25];
1792 // blit the proper icon
1794 switch( game->flags & AG_FLAG_TYPE_MASK ){
1797 if(Multi_common_icons[MICON_COOP] != -1){
1798 gr_set_bitmap(Multi_common_icons[MICON_COOP]);
1803 // team vs. team game
1805 if(Multi_common_icons[MICON_TVT] != -1){
1806 gr_set_bitmap(Multi_common_icons[MICON_TVT]);
1812 case AG_FLAG_DOGFIGHT:
1813 if(Multi_common_icons[MICON_DOGFIGHT] != -1){
1814 gr_set_bitmap(Multi_common_icons[MICON_DOGFIGHT]);
1819 // if we're supposed to draw a bitmap
1821 gr_bitmap(Mj_game_icon_coords[gr_screen.res][MJ_X_COORD],y-1);
1824 // blit the proper status text
1825 memset(status_text,0,25);
1827 switch( game->flags & AG_FLAG_STATE_MASK ){
1828 case AG_FLAG_FORMING:
1829 gr_set_color_fast(&Color_bright_green);
1830 strcpy(status_text,XSTR("Forming",764));
1832 case AG_FLAG_BRIEFING:
1833 gr_set_color_fast(&Color_bright_red);
1834 strcpy(status_text,XSTR("Briefing",765));
1836 case AG_FLAG_DEBRIEF:
1837 gr_set_color_fast(&Color_bright_red);
1838 strcpy(status_text,XSTR("Debrief",766));
1841 gr_set_color_fast(&Color_bright_red);
1842 strcpy(status_text,XSTR("Paused",767));
1844 case AG_FLAG_IN_MISSION:
1845 gr_set_color_fast(&Color_bright_red);
1846 strcpy(status_text,XSTR("Playing",768));
1849 gr_set_color_fast(&Color_bright);
1850 strcpy(status_text,XSTR("Unknown",769));
1853 gr_get_string_size(&str_w,NULL,status_text);
1854 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);
1857 // load in a list of active games from our tcp.cfg file
1858 void multi_join_load_tcp_addrs()
1860 char line[MAX_IP_STRING];
1865 // attempt to open the ip list file
1866 file = cfopen(IP_CONFIG_FNAME,"rt",CFILE_NORMAL,CF_TYPE_DATA);
1868 nprintf(("Network","Error loading tcp.cfg file!\n"));
1872 // free up any existing server list
1873 multi_free_server_list();
1875 // read in all the strings in the file
1876 while(!cfeof(file)){
1878 cfgets(line,MAX_IP_STRING,file);
1880 // strip off any newline character
1881 if(line[strlen(line) - 1] == '\n'){
1882 line[strlen(line) - 1] = '\0';
1885 // empty lines don't get processed
1886 if( (line[0] == '\0') || (line[0] == '\n') ){
1890 if ( !psnet_is_valid_ip_string(line) ) {
1891 nprintf(("Network","Invalid ip string (%s)\n",line));
1893 // copy the server ip address
1894 memset(&addr,0,sizeof(net_addr));
1895 addr.type = NET_TCP;
1896 psnet_string_to_addr(&addr,line);
1897 if ( addr.port == 0 ){
1898 addr.port = DEFAULT_GAME_PORT;
1901 // create a new server item on the list
1902 item = multi_new_server_item();
1904 memcpy(&item->server_addr,&addr,sizeof(net_addr));
1912 // do stuff like pinging servers, sending out requests, etc
1913 void multi_join_do_netstuff()
1915 // handle game query stuff
1916 if(Multi_join_glr_stamp == -1){
1917 broadcast_game_query();
1919 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
1920 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME_LOCAL);
1922 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME);
1925 // otherwise send out game query and restamp
1926 else if(timestamp_elapsed(Multi_join_glr_stamp)){
1927 broadcast_game_query();
1929 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
1930 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME_LOCAL);
1932 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME);
1936 // check to see if we've been accepted. If so, put up message saying so
1937 if ( Net_player->flags & (NETINFO_FLAG_ACCEPT_INGAME|NETINFO_FLAG_ACCEPT_CLIENT|NETINFO_FLAG_ACCEPT_HOST|NETINFO_FLAG_ACCEPT_OBSERVER) ) {
1938 multi_common_add_notify(XSTR("Accepted. Waiting for player data.",770));
1941 // check to see if any join packets we have sent have timed out
1942 if((Multi_join_sent_stamp != -1) && (timestamp_elapsed(Multi_join_sent_stamp))){
1943 Multi_join_sent_stamp = -1;
1944 multi_common_add_notify(XSTR("Join request timed out!",771));
1947 // check to see if we should be pinging everyone
1948 if((Multi_join_ping_stamp == -1) || (timestamp_elapsed(Multi_join_ping_stamp))){
1949 multi_join_ping_all();
1950 Multi_join_ping_stamp = timestamp(MULTI_JOIN_PING_TIME);
1954 multi_join_cull_timeouts();
1957 // evaluate a returned pong.
1958 void multi_join_eval_pong(net_addr *addr, fix pong_time)
1961 active_game *moveup = Active_game_head;
1966 if(psnet_same(&moveup->server_addr,addr)){
1968 multi_ping_eval_pong(&moveup->ping);
1972 moveup = moveup->next;
1974 } while(moveup != Active_game_head);
1977 // update the game's ping
1979 if(found && (moveup->ping_end > moveup->ping_start)){
1980 moveup->ping_time = f2fl(moveup->ping_end - moveup->ping_start);
1981 moveup->ping_start = -1;
1982 moveup->ping_end = -1;
1987 // ping all the server on the list
1988 void multi_join_ping_all()
1990 active_game *moveup = Active_game_head;
1995 moveup->ping_start = timer_get_fixed_seconds();
1996 moveup->ping_end = -1;
1997 send_ping(&moveup->server_addr);
1999 multi_ping_send(&moveup->server_addr,&moveup->ping);
2001 moveup = moveup->next;
2002 } while(moveup != Active_game_head);
2006 void multi_join_process_select()
2008 // if we don't have anything selected and there are items on the list - select the first one
2009 if((Multi_join_list_selected == -1) && (Active_game_count > 0)){
2010 Multi_join_list_selected = 0;
2011 Multi_join_selected_item = multi_join_get_game(0);
2012 MJ_LIST_START_SET(0);
2013 Multi_join_list_start_item = Multi_join_selected_item;
2015 // send a mission description request to this guy
2016 send_netgame_descript_packet(&Multi_join_selected_item->server_addr,0);
2017 multi_common_set_text("");
2019 // I sure hope this doesn't happen
2020 Assert(Multi_join_selected_item != NULL);
2023 // otherwise see if he's clicked on an item
2024 else if(Multi_join_select_button.pressed() && (Active_game_count > 0)){
2026 Multi_join_select_button.get_mouse_pos(NULL,&y);
2028 if(item + Multi_join_list_start < Active_game_count){
2029 gamesnd_play_iface(SND_IFACE_MOUSE_CLICK);
2031 Multi_join_list_selected = item + Multi_join_list_start;
2032 Multi_join_selected_item = multi_join_get_game(Multi_join_list_selected);
2034 // I sure hope this doesn't happen
2035 Assert(Multi_join_selected_item != NULL);
2037 // send a mission description request to this guy
2038 send_netgame_descript_packet(&Multi_join_selected_item->server_addr,0);
2039 multi_common_set_text("");
2043 // if he's double clicked, then select it and accept
2044 if(Multi_join_select_button.double_clicked()){
2046 Multi_join_select_button.get_mouse_pos(NULL,&y);
2048 if(item == Multi_join_list_selected){
2049 multi_join_button_pressed(MJ_ACCEPT);
2054 // return game n (0 based index)
2055 active_game *multi_join_get_game(int n)
2057 active_game *moveup = Active_game_head;
2064 moveup = moveup->next;
2065 while((moveup != Active_game_head) && (count != n)){
2066 moveup = moveup->next;
2069 if(moveup == Active_game_head){
2070 nprintf(("Network","Warning, couldn't find game item %d!\n",n));
2080 // scroll through the game list
2081 void multi_join_list_scroll_up()
2083 // if we're not at the beginning of the list, scroll up
2084 if(Multi_join_list_start_item != Active_game_head){
2085 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2087 MJ_LIST_START_DEC();
2089 gamesnd_play_iface(SND_SCROLL);
2091 gamesnd_play_iface(SND_GENERAL_FAIL);
2095 // scroll through the game list
2096 void multi_join_list_scroll_down()
2098 if((Active_game_count - Multi_join_list_start) > Mj_max_game_items[gr_screen.res]){
2099 Multi_join_list_start_item = Multi_join_list_start_item->next;
2101 MJ_LIST_START_INC();
2103 gamesnd_play_iface(SND_SCROLL);
2105 gamesnd_play_iface(SND_GENERAL_FAIL);
2109 void multi_join_list_page_up()
2111 // in this case, just set us to the beginning of the list
2112 if((Multi_join_list_start - Mj_max_game_items[gr_screen.res]) < 0){
2113 Multi_join_list_start_item = Active_game_head;
2115 MJ_LIST_START_SET(0);
2117 gamesnd_play_iface(SND_SCROLL);
2119 // otherwise page the whole thing up
2121 for(idx=0; idx<Mj_max_game_items[gr_screen.res]; idx++){
2122 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2124 MJ_LIST_START_DEC();
2126 gamesnd_play_iface(SND_SCROLL);
2130 void multi_join_list_page_down()
2134 // page the whole thing down
2135 while((count < Mj_max_game_items[gr_screen.res]) && ((Active_game_count - Multi_join_list_start) > Mj_max_game_items[gr_screen.res])){
2136 Multi_join_list_start_item = Multi_join_list_start_item->next;
2137 MJ_LIST_START_INC();
2142 gamesnd_play_iface(SND_SCROLL);
2145 void multi_join_cull_timeouts()
2147 active_game *backup;
2149 active_game *moveup = Active_game_head;
2151 // traverse through the entire list if any items exist
2155 if((moveup->heard_from_timer != -1) && (timestamp_elapsed(moveup->heard_from_timer))){
2156 Active_game_count--;
2158 // if this is the head of the list
2159 if(moveup == Active_game_head){
2160 // if this is the _only_ item on the list
2161 if(moveup->next == Active_game_head){
2162 // handle any gui details related to deleting this item
2163 multi_join_handle_item_cull(Active_game_head, count);
2165 free(Active_game_head);
2166 Active_game_head = NULL;
2169 // if there are other items on the list
2171 // handle any gui details related to deleting this item
2172 multi_join_handle_item_cull(moveup, count);
2174 Active_game_head = moveup->next;
2175 Active_game_head->prev = moveup->prev;
2176 Active_game_head->prev->next = Active_game_head;
2178 moveup = Active_game_head;
2181 // if its somewhere else on the list
2183 // handle any gui details related to deleting this item
2184 multi_join_handle_item_cull(moveup, count);
2186 // if its the last item on the list
2187 moveup->next->prev = moveup->prev;
2188 moveup->prev->next = moveup->next;
2190 // if it was the last element on the list, return
2191 if(moveup->next == Active_game_head){
2195 backup = moveup->next;
2201 moveup = moveup->next;
2204 } while(moveup != Active_game_head);
2208 // deep magic begins here.
2209 void multi_join_handle_item_cull(active_game *item, int item_index)
2211 // if this is the only item on the list, unset everything
2212 if(item->next == item){
2213 Multi_join_list_selected = -1;
2214 Multi_join_selected_item = NULL;
2216 Multi_join_slider.set_numberItems(0);
2217 MJ_LIST_START_SET(-1);
2218 Multi_join_list_start_item = NULL;
2224 // see if we should be adjusting our currently selected item
2225 if(item_index <= Multi_join_list_selected){
2226 // the selected item is the head of the list
2227 if(Multi_join_selected_item == Active_game_head){
2228 // move the pointer up since this item is about to be destroyed
2229 Multi_join_selected_item = Multi_join_selected_item->next;
2231 // if this is the item being deleted, select the previous one
2232 if(item == Multi_join_selected_item){
2234 Multi_join_selected_item = Multi_join_selected_item->prev;
2236 // decrement the selected index by 1
2237 Multi_join_list_selected--;
2239 // now we know its a previous item, so our pointer stays the same but our index goes down by one, since there will be
2240 // 1 less item on the list
2242 // decrement the selected index by 1
2243 Multi_join_list_selected--;
2248 // see if we should be adjusting out current start position
2249 if(item_index <= Multi_join_list_start){
2250 // the start position is the head of the list
2251 if(Multi_join_list_start_item == Active_game_head){
2252 // move the pointer up since this item is about to be destroyed
2253 Multi_join_list_start_item = Multi_join_list_start_item->next;
2255 // if this is the item being deleted, select the previous one
2256 if(item == Multi_join_list_start_item){
2257 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2259 // decrement the starting index by 1
2260 MJ_LIST_START_DEC();
2262 // but decrement the starting index by 1
2263 MJ_LIST_START_DEC();
2268 // maybe go back up a bit so that we always have a full page of items
2269 if(Active_game_count > Mj_max_game_items[gr_screen.res]){
2270 while((Active_game_count - Multi_join_list_start) < Mj_max_game_items[gr_screen.res]){
2271 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2272 MJ_LIST_START_DEC();
2276 // set slider location
2277 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);
2278 Multi_join_slider.force_currentItem(Multi_join_list_start);
2281 void multi_join_send_join_request(int as_observer)
2283 // don't do anything if we have no items selected
2284 if(Multi_join_selected_item == NULL){
2288 // 5/26/98 -- for team v team games, don't allow ingame joining :-(
2289 if ( (Multi_join_selected_item->flags & AG_FLAG_TEAMS) && (Multi_join_selected_item->flags & (AG_FLAG_PAUSE|AG_FLAG_IN_MISSION)) ) {
2290 popup(0, 1, POPUP_OK, XSTR("Joining ingame is currently not allowed for team vs. team games",772));
2294 memset(&Multi_join_request,0,sizeof(join_request));
2296 // if the netgame is in password mode, put up a request for the password
2297 if(Multi_join_selected_item->flags & AG_FLAG_PASSWD){
2298 if(!multi_passwd_popup(Multi_join_request.passwd)){
2302 nprintf(("Password : %s\n",Multi_join_request.passwd));
2305 // fill out the join request struct
2306 strcpy(Multi_join_request.callsign,Player->callsign);
2307 if(strlen(Player->image_filename) > 0){
2308 strcpy(Multi_join_request.image_filename, Player->image_filename);
2310 if(strlen(Player->squad_filename) > 0){
2311 strcpy(Multi_join_request.squad_filename, Player->squad_filename);
2314 // tracker id (if any)
2315 Multi_join_request.tracker_id = Multi_tracker_id;
2317 // player's rank (at least, what he wants you to _believe_)
2318 Multi_join_request.player_rank = (ubyte)Player->stats.rank;
2321 Multi_join_request.flags = 0;
2323 Multi_join_request.flags |= JOIN_FLAG_AS_OBSERVER;
2326 // if the player has hacked data
2327 if(game_hacked_data()){
2328 Multi_join_request.flags |= JOIN_FLAG_HAXOR;
2332 strncpy(Multi_join_request.pxo_squad_name, Multi_tracker_squad_name, LOGIN_LEN);
2334 // version of this server
2335 Multi_join_request.version = MULTI_FS_SERVER_VERSION;
2337 // server compatible version
2338 Multi_join_request.comp_version = MULTI_FS_SERVER_COMPATIBLE_VERSION;
2340 // his local player options
2341 memcpy(&Multi_join_request.player_options,&Player->m_local_options,sizeof(multi_local_options));
2343 // set the server address for the netgame
2344 memcpy(&Netgame.server_addr,&Multi_join_selected_item->server_addr,sizeof(net_addr));
2346 // send a join request to the guy
2347 send_join_packet(&Multi_join_selected_item->server_addr,&Multi_join_request);
2350 Multi_join_sent_stamp = timestamp(MULTI_JOIN_SENT_WAIT);
2353 multi_common_add_notify(XSTR("Sending join request...",773));
2356 void multi_join_create_game()
2358 // maybe warn the player about possible crappy server conditions
2359 if(!multi_join_maybe_warn()){
2363 // make sure to flag ourself as being the master
2364 Net_player->flags |= (NETINFO_FLAG_AM_MASTER | NETINFO_FLAG_GAME_HOST);
2365 Net_player->state = NETPLAYER_STATE_HOST_SETUP;
2367 // if we're in PXO mode, mark it down in our player struct
2368 if(MULTI_IS_TRACKER_GAME){
2369 Player->flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
2370 Player->save_flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
2372 // otherwise, if he's already played PXO games, warn him
2375 if(Player->flags & PLAYER_FLAGS_HAS_PLAYED_PXO){
2376 if(!multi_join_warn_pxo()){
2383 gameseq_post_event(GS_EVENT_MULTI_START_GAME);
2384 gamesnd_play_iface(SND_USER_SELECT);
2387 void multi_join_reset_join_stamp()
2389 // unset the timestamp here so the user can immediately send another join request
2390 Multi_join_sent_stamp = -1;
2391 multi_common_add_notify("");
2394 void multi_join_blit_top_stuff()
2396 // blit the cd icon if he has one
2397 if(Multi_has_cd && (Multi_common_icons[MICON_CD] != -1)){
2400 bm_get_info(Multi_common_icons[MICON_CD], &cd_w, NULL, NULL, NULL, NULL);
2402 gr_set_bitmap(Multi_common_icons[MICON_CD]);
2403 gr_bitmap((gr_screen.max_w / 2) - (cd_w / 2), Mj_cd_coords[gr_screen.res]);
2407 #define CW_CODE_CANCEL 0 // cancel the action
2408 #define CW_CODE_OK 1 // continue anyway
2409 #define CW_CODE_INFO 2 // gimme some more information
2411 #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)
2412 #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)
2414 #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)
2415 #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)
2417 int multi_join_warn_update_low(int code)
2421 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),LOW_WARN_TEXT);
2424 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),LOW_INFO_TEXT);
2427 return CW_CODE_CANCEL;
2430 int multi_join_warn_update_medium(int code)
2434 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),MED_WARN_TEXT);
2437 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),MED_INFO_TEXT);
2440 return CW_CODE_CANCEL;
2443 int multi_join_maybe_warn()
2447 // if the player is set for low updates
2448 if(Player->m_local_options.obj_update_level == OBJ_UPDATE_LOW){
2451 code = multi_join_warn_update_low(code);
2452 } while((code != CW_CODE_CANCEL) && (code != CW_CODE_OK));
2457 // if the player is set for medium updates
2458 else if(Player->m_local_options.obj_update_level == OBJ_UPDATE_MEDIUM){
2461 code = multi_join_warn_update_medium(code);
2462 } while((code != CW_CODE_CANCEL) && (code != CW_CODE_OK));
2470 int multi_join_warn_pxo()
2472 // 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;
2476 void multi_join_blit_protocol()
2478 gr_set_color_fast(&Color_bright);
2480 switch(Socket_type){
2483 gr_string(5, 2, "TCP");
2487 gr_string(5, 2, "IPX");
2493 // -------------------------------------------------------------------------------------------------
2495 // MULTIPLAYER START GAME screen
2500 #define MULTI_SG_PALETTE "InterfacePalette"
2502 static char *Multi_sg_bitmap_fname[GR_NUM_RESOLUTIONS] = {
2503 "MultiStartGame", // GR_640
2504 "2_MultiStartGame" // GR_1024
2507 static char *Multi_sg_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
2508 "MultiStartGame-M", // GR_640
2509 "2_MultiStartGame-M" // GR_1024
2514 int Multi_sg_rank_max_display[GR_NUM_RESOLUTIONS] = {
2519 // constants for coordinate look ups
2520 #define MSG_X_COORD 0
2521 #define MSG_Y_COORD 1
2522 #define MSG_W_COORD 2
2523 #define MSG_H_COORD 3
2527 // input password field
2528 int Msg_passwd_coords[GR_NUM_RESOLUTIONS][4] = {
2537 // input game title field
2538 int Msg_title_coords[GR_NUM_RESOLUTIONS][4] = {
2547 // rank selected field
2548 int Msg_rank_sel_coords[GR_NUM_RESOLUTIONS][4] = {
2558 int Msg_rank_list_coords[GR_NUM_RESOLUTIONS][4] = {
2569 #define MULTI_SG_NUM_BUTTONS 10
2570 #define MSG_OPEN_GAME 0
2571 //#define MSG_CLOSED_GAME 1
2572 //#define MSG_RESTRICTED_GAME 2
2573 #define MSG_PASSWD_GAME 1
2574 #define MSG_RANK_SET_GAME 2
2575 #define MSG_RANK_SCROLL_UP 3
2576 #define MSG_RANK_SCROLL_DOWN 4
2577 #define MSG_RANK_ABOVE 5
2578 #define MSG_RANK_BELOW 6
2580 #define MSG_OPTIONS 8
2581 #define MSG_ACCEPT 9
2583 UI_WINDOW Multi_sg_window; // the window object for the join screen
2584 UI_BUTTON Multi_sg_rank_button; // for selecting the rank marker
2585 UI_INPUTBOX Multi_sg_game_name; // for Netgame.name
2586 UI_INPUTBOX Multi_sg_game_passwd; // for Netgame.passwd
2587 int Multi_sg_bitmap; // the background bitmap
2589 ui_button_info Multi_sg_buttons[GR_NUM_RESOLUTIONS][MULTI_SG_NUM_BUTTONS] = {
2591 ui_button_info("MSG_00", 1, 184, 34, 191, 2), // open
2592 // ui_button_info("MSG_01", 1, 159, 34, 166, 1), // closed
2593 // ui_button_info("MSG_02", 1, 184, 34, 191, 2), // restricted
2594 ui_button_info("MSG_03", 1, 209, 34, 218, 3), // password
2595 ui_button_info("MSG_04", 1, 257, 34, 266, 4), // rank set
2596 ui_button_info("MSG_05", 1, 282, -1, -1, 5), // rank scroll up
2597 ui_button_info("MSG_06", 1, 307, -1, -1, 6), // rank scroll down
2598 ui_button_info("MSG_07", 177, 282, 210, 290, 7), // rank above
2599 ui_button_info("MSG_08", 177, 307, 210, 315, 8), // rank below
2600 ui_button_info("MSG_09", 536, 429, 500, 440, 9), // help
2601 ui_button_info("MSG_10", 536, 454, 479, 464, 10), // options
2602 ui_button_info("MSG_11", 576, 432, 571, 415, 11), // accept
2605 ui_button_info("2_MSG_00", 2, 295, 51, 307, 2), // open
2606 // ui_button_info("2_MSG_01", 2, 254, 51, 267, 1), // closed
2607 // ui_button_info("2_MSG_02", 2, 295, 51, 307, 2), // restricted
2608 ui_button_info("2_MSG_03", 2, 335, 51, 350, 3), // password
2609 ui_button_info("2_MSG_04", 2, 412, 51, 426, 4), // rank set
2610 ui_button_info("2_MSG_05", 2, 452, -1, -1, 5), // rank scroll up
2611 ui_button_info("2_MSG_06", 2, 492, -1, -1, 6), // rank scroll down
2612 ui_button_info("2_MSG_07", 284, 452, 335, 465, 7), // rank above
2613 ui_button_info("2_MSG_08", 284, 492, 335, 505, 8), // rank below
2614 ui_button_info("2_MSG_09", 858, 687, 817, 706, 9), // help
2615 ui_button_info("2_MSG_10", 858, 728, 797, 743, 10), // options
2616 ui_button_info("2_MSG_11", 921, 692, 921, 664, 11), // accept
2620 #define MULTI_SG_NUM_TEXT 11
2621 UI_XSTR Multi_sg_text[GR_NUM_RESOLUTIONS][MULTI_SG_NUM_TEXT] = {
2623 {"Open", 1322, 34, 191, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_OPEN_GAME].button},
2624 // {"Closed", 1323, 34, 166, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_CLOSED_GAME].button},
2625 // {"Restricted", 1324, 34, 191, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RESTRICTED_GAME].button},
2626 {"Password Protected", 1325, 34, 218, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_PASSWD_GAME].button},
2627 {"Allow Rank", 1326, 34, 266, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_SET_GAME].button},
2628 {"Above", 1327, 210, 290, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_ABOVE].button},
2629 {"Below", 1328, 210, 315, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_BELOW].button},
2630 {"Help", 928, 500, 440, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_HELP].button},
2631 {"Options", 1036, 479, 464, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_OPTIONS].button},
2632 {"Accept", 1035, 571, 415, UI_XSTR_COLOR_PINK, -1, &Multi_sg_buttons[0][MSG_ACCEPT].button},
2633 {"Start Game", 1329, 26, 10, UI_XSTR_COLOR_GREEN, -1, NULL},
2634 {"Title", 1330, 26, 31, UI_XSTR_COLOR_GREEN, -1, NULL},
2635 {"Game Type", 1331, 12, 165, UI_XSTR_COLOR_GREEN, -1, NULL},
2638 {"Open", 1322, 51, 307, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_OPEN_GAME].button},
2639 // {"Closed", 1323, 51, 267, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_CLOSED_GAME].button},
2640 // {"Restricted", 1324, 51, 307, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RESTRICTED_GAME].button},
2641 {"Password Protected", 1325, 51, 350, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_PASSWD_GAME].button},
2642 {"Allow Rank", 1326, 51, 426, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_SET_GAME].button},
2643 {"Above", 1327, 335, 465, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_ABOVE].button},
2644 {"Below", 1328, 335, 505, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_BELOW].button},
2645 {"Help", 928, 817, 706, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_HELP].button},
2646 {"Options", 1036, 797, 743, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_OPTIONS].button},
2647 {"Accept", 1035, 921, 664, UI_XSTR_COLOR_PINK, -1, &Multi_sg_buttons[1][MSG_ACCEPT].button},
2648 {"Start Game", 1329, 42, 22, UI_XSTR_COLOR_GREEN, -1, NULL},
2649 {"Title", 1330, 42, 50, UI_XSTR_COLOR_GREEN, -1, NULL},
2650 {"Game Type", 1331, 20, 264, UI_XSTR_COLOR_GREEN, -1, NULL},
2654 // starting index for displaying ranks
2655 int Multi_sg_rank_start;
2656 int Multi_sg_rank_select;
2658 // netgame pointer to indirect through
2659 netgame_info *Multi_sg_netgame;
2661 // hold temporary values in this structure when on a standalone server
2662 netgame_info Multi_sg_netgame_temp;
2664 // forward declarations
2665 void multi_sg_check_buttons();
2666 void multi_sg_button_pressed(int n);
2667 void multi_sg_init_gamenet();
2668 void multi_sg_draw_radio_buttons();
2669 void multi_sg_rank_scroll_up();
2670 void multi_sg_rank_scroll_down();
2671 void multi_sg_rank_display_stuff();
2672 void multi_sg_rank_process_select();
2673 void multi_sg_rank_build_name(char *in,char *out);
2674 void multi_sg_check_passwd();
2675 void multi_sg_check_name();
2676 void multi_sg_release_passwd();
2677 int multi_sg_rank_select_valid(int rank);
2678 void multi_sg_select_rank_default();
2680 // function which takes a rank name and returns the index. Useful for commandline options
2681 // for above and below rank. We return the index of the rank in the Ranks[] array. If
2682 // the rank isn't found, we return -1
2683 int multi_start_game_rank_from_name( char *rank ) {
2686 for ( i = 0; i <= MAX_FREESPACE2_RANK; i++ ) {
2687 if ( !stricmp(Ranks[i].name, rank) ) {
2695 void multi_start_game_init()
2699 // initialize the gamenet
2700 multi_sg_init_gamenet();
2702 // create the interface window
2703 Multi_sg_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
2704 Multi_sg_window.set_mask_bmap(Multi_sg_bitmap_mask_fname[gr_screen.res]);
2706 // load the background bitmap
2707 Multi_sg_bitmap = bm_load(Multi_sg_bitmap_fname[gr_screen.res]);
2708 if(Multi_sg_bitmap < 0){
2709 // we failed to load the bitmap - this is very bad
2713 // initialize the common notification messaging
2714 multi_common_notify_init();
2716 // initialize the common text area
2717 multi_common_set_text("");
2719 // use the common interface palette
2720 multi_common_set_palette();
2722 // create the interface buttons
2723 for(idx=0; idx<MULTI_SG_NUM_BUTTONS; idx++){
2724 // create the object
2725 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);
2727 // set the sound to play when highlighted
2728 Multi_sg_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
2730 // set the ani for the button
2731 Multi_sg_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sg_buttons[gr_screen.res][idx].filename);
2734 Multi_sg_buttons[gr_screen.res][idx].button.link_hotspot(Multi_sg_buttons[gr_screen.res][idx].hotspot);
2738 for(idx=0; idx<MULTI_SG_NUM_TEXT; idx++){
2739 Multi_sg_window.add_XSTR(&Multi_sg_text[gr_screen.res][idx]);
2742 // load the help overlay
2743 help_overlay_load(MULTI_START_OVERLAY);
2744 help_overlay_set_state(MULTI_START_OVERLAY,0);
2746 // intiialize the rank selection items
2747 multi_sg_select_rank_default();
2748 Multi_sg_rank_start = Multi_sg_rank_select;
2750 // create the rank select button
2751 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);
2752 Multi_sg_rank_button.hide();
2754 // create the netgame name input box
2755 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);
2757 // create the netgame password input box, and disable it by default
2758 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);
2759 Multi_sg_game_passwd.hide();
2760 Multi_sg_game_passwd.disable();
2762 // set the netgame text to this gadget and make it have focus
2763 Multi_sg_game_name.set_text(Multi_sg_netgame->name);
2764 Multi_sg_game_name.set_focus();
2766 // if starting a netgame, set the name of the game and any other options that are appropriate
2767 if ( Cmdline_start_netgame ) {
2768 if ( Cmdline_game_name != NULL ) {
2769 strcpy( Multi_sg_netgame->name, Cmdline_game_name );
2770 Multi_sg_game_name.set_text(Multi_sg_netgame->name);
2773 // deal with the different game types -- only one should even be active, so we will just go down
2774 // the line. Last one wins.
2775 if ( Cmdline_closed_game ) {
2776 Multi_sg_netgame->mode = NG_MODE_CLOSED;
2777 } else if ( Cmdline_restricted_game ) {
2778 Multi_sg_netgame->mode = NG_MODE_RESTRICTED;
2779 } else if ( Cmdline_game_password != NULL ) {
2780 Multi_sg_netgame->mode = NG_MODE_PASSWORD;
2781 strcpy(Multi_sg_netgame->passwd, Cmdline_game_password);
2782 Multi_sg_game_passwd.set_text(Multi_sg_netgame->passwd);
2785 // deal with rank above and rank below
2786 if ( (Cmdline_rank_above != NULL) || (Cmdline_rank_below != NULL) ) {
2790 if ( Cmdline_rank_above != NULL ) {
2791 rank_str = Cmdline_rank_above;
2793 rank_str = Cmdline_rank_below;
2796 // try and get the rank index from the name -- if found, then set the rank base
2797 // and the game type. apparently we only support either above or below, not both
2798 // together, so I make a random choice
2799 rank = multi_start_game_rank_from_name( rank_str );
2801 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
2803 // now an arbitrary decision
2804 if ( Cmdline_rank_above != NULL ) {
2805 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
2807 Multi_sg_netgame->mode = NG_MODE_RANK_BELOW;
2812 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
2816 void multi_start_game_do()
2818 // return here since we will be moving to the next stage anyway -- I don't want to see the backgrounds of
2819 // all the screens for < 1 second for every screen we automatically move to.
2820 if ( Cmdline_start_netgame ) {
2824 int k = Multi_sg_window.process();
2826 // process any keypresses
2829 if(help_overlay_active(MULTI_START_OVERLAY)){
2830 help_overlay_set_state(MULTI_START_OVERLAY,0);
2832 gamesnd_play_iface(SND_USER_SELECT);
2833 multi_quit_game(PROMPT_NONE);
2838 case KEY_LCTRL + KEY_ENTER :
2839 case KEY_RCTRL + KEY_ENTER :
2840 gamesnd_play_iface(SND_COMMIT_PRESSED);
2841 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
2845 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
2846 help_overlay_set_state(MULTI_START_OVERLAY, 0);
2849 // check to see if the user has selected a different rank
2850 multi_sg_rank_process_select();
2852 // check any button presses
2853 multi_sg_check_buttons();
2855 // check to see if any of the input boxes have changed, and update the appropriate Netgame fields if necessary
2856 multi_sg_check_passwd();
2857 multi_sg_check_name();
2859 // draw the background, etc
2861 GR_MAYBE_CLEAR_RES(Multi_sg_bitmap);
2862 if(Multi_sg_bitmap != -1){
2863 gr_set_bitmap(Multi_sg_bitmap);
2866 Multi_sg_window.draw();
2868 // display rank stuff
2869 multi_sg_rank_display_stuff();
2871 // display any pending notification messages
2872 multi_common_notify_do();
2874 // draw all radio button
2875 multi_sg_draw_radio_buttons();
2877 // draw the help overlay
2878 help_overlay_maybe_blit(MULTI_START_OVERLAY);
2884 void multi_start_game_close()
2886 // if i'm the host on a standalone server, send him my start game options (passwd, mode, etc)
2887 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
2888 multi_options_update_start_game(Multi_sg_netgame);
2891 // unload any bitmaps
2892 if(!bm_unload(Multi_sg_bitmap)){
2893 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_sg_bitmap_fname[gr_screen.res]));
2896 // unload the help overlay
2897 help_overlay_unload(MULTI_START_OVERLAY);
2899 // destroy the UI_WINDOW
2900 Multi_sg_window.destroy();
2903 void multi_sg_check_buttons()
2906 for(idx=0;idx<MULTI_SG_NUM_BUTTONS;idx++){
2907 // we only really need to check for one button pressed at a time, so we can break after
2909 if(Multi_sg_buttons[gr_screen.res][idx].button.pressed()){
2910 multi_sg_button_pressed(idx);
2916 void multi_sg_button_pressed(int n)
2919 // go to the options screen
2921 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
2926 if(!help_overlay_active(MULTI_START_OVERLAY)){
2927 help_overlay_set_state(MULTI_START_OVERLAY,1);
2929 help_overlay_set_state(MULTI_START_OVERLAY,0);
2933 // the open button was pressed
2935 // if the closed option is selected
2936 if(Multi_sg_netgame->mode != NG_MODE_OPEN){
2937 Multi_sg_netgame->mode = NG_MODE_OPEN;
2939 gamesnd_play_iface(SND_USER_SELECT);
2941 // release the password control if necessary
2942 multi_sg_release_passwd();
2944 // if its already selected
2946 gamesnd_play_iface(SND_GENERAL_FAIL);
2951 // the open button was pressed
2952 case MSG_CLOSED_GAME:
2953 // if the closed option is selected
2954 if(Multi_sg_netgame->mode != NG_MODE_CLOSED){
2955 Multi_sg_netgame->mode = NG_MODE_CLOSED;
2957 gamesnd_play_iface(SND_USER_SELECT);
2959 // release the password control if necessary
2960 multi_sg_release_passwd();
2962 // if its already selected
2964 gamesnd_play_iface(SND_GENERAL_FAIL);
2968 // toggle password protection
2969 case MSG_PASSWD_GAME:
2970 // if we selected it
2971 if(Multi_sg_netgame->mode != NG_MODE_PASSWORD){
2972 gamesnd_play_iface(SND_USER_SELECT);
2974 Multi_sg_game_passwd.enable();
2975 Multi_sg_game_passwd.unhide();
2976 Multi_sg_game_passwd.set_focus();
2978 Multi_sg_netgame->mode = NG_MODE_PASSWORD;
2980 // copy in the current network password
2981 Multi_sg_game_passwd.set_text(Multi_sg_netgame->passwd);
2983 gamesnd_play_iface(SND_GENERAL_FAIL);
2988 // toggle "restricted" on or off
2989 case MSG_RESTRICTED_GAME:
2990 if(Multi_sg_netgame->mode != NG_MODE_RESTRICTED){
2991 gamesnd_play_iface(SND_USER_SELECT);
2992 Multi_sg_netgame->mode = NG_MODE_RESTRICTED;
2994 // release the password control if necessary
2995 multi_sg_release_passwd();
2997 gamesnd_play_iface(SND_GENERAL_FAIL);
3001 // turn off all rank requirements
3002 case MSG_RANK_SET_GAME:
3003 // if either is set, then turn then both off
3004 if((Multi_sg_netgame->mode != NG_MODE_RANK_BELOW) && (Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE)){
3005 gamesnd_play_iface(SND_USER_SELECT);
3007 // set it to the default case if we're turning it off
3008 multi_sg_select_rank_default();
3009 Multi_sg_rank_start = Multi_sg_rank_select;
3011 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
3013 // release the password control if necessary
3014 multi_sg_release_passwd();
3016 gamesnd_play_iface(SND_GENERAL_FAIL);
3020 // rank above was pressed
3021 case MSG_RANK_ABOVE :
3022 if((Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE) || (Multi_sg_netgame->mode == NG_MODE_RANK_BELOW)){
3023 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
3025 // select the first item
3026 multi_sg_select_rank_default();
3027 Multi_sg_rank_start = Multi_sg_rank_select;
3030 gamesnd_play_iface(SND_USER_SELECT);
3032 gamesnd_play_iface(SND_GENERAL_FAIL);
3036 // rank below was pressed
3037 case MSG_RANK_BELOW :
3038 if((Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE) || (Multi_sg_netgame->mode == NG_MODE_RANK_BELOW)){
3039 Multi_sg_netgame->mode = NG_MODE_RANK_BELOW;
3041 // select the first item
3042 multi_sg_select_rank_default();
3043 Multi_sg_rank_start = Multi_sg_rank_select;
3046 gamesnd_play_iface(SND_USER_SELECT);
3048 gamesnd_play_iface(SND_GENERAL_FAIL);
3052 // scroll the rank list up
3053 case MSG_RANK_SCROLL_UP:
3054 multi_sg_rank_scroll_up();
3057 // scroll the rank list down
3058 case MSG_RANK_SCROLL_DOWN:
3059 multi_sg_rank_scroll_down();
3062 // move to the create game screen
3064 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
3065 gamesnd_play_iface(SND_COMMIT_PRESSED);
3069 gamesnd_play_iface(SND_GENERAL_FAIL);
3070 multi_common_add_notify(XSTR("Not implemented yet!",760));
3075 // NOTE : this is where all Netgame initialization should take place on the host
3076 void multi_sg_init_gamenet()
3078 char buf[128],out_name[128];
3080 net_player *server_save;
3082 // back this data up in case we are already connected to a standalone
3083 memcpy(&save,&Netgame.server_addr,sizeof(net_addr));
3084 server_save = Netgame.server;
3086 // remove campaign flags
3087 Game_mode &= ~(GM_CAMPAIGN_MODE);
3089 // clear out the Netgame structure and start filling in the values
3090 memset( &Netgame, 0, sizeof(Netgame) );
3091 memset( &Multi_sg_netgame_temp, 0, sizeof(netgame_info) );
3093 // if we're on the standalone, we're not the server, so we don't care about setting the netgame state
3094 if(Net_player->state != NETPLAYER_STATE_STD_HOST_SETUP){
3095 Netgame.game_state = NETGAME_STATE_HOST_SETUP;
3096 Multi_sg_netgame = &Netgame;
3099 ml_string(NOX("Starting netgame as Host/Server"));
3101 Multi_sg_netgame = &Multi_sg_netgame_temp;
3105 memcpy(&temp_addr.s_addr, &Netgame.server_addr, 4);
3106 char *server_addr = inet_ntoa(temp_addr);
3107 ml_printf(NOX("Starting netgame as Host on Standalone server : %s"), (server_addr == NULL) ? NOX("Unknown") : server_addr);
3110 Net_player->tracker_player_id = Multi_tracker_id;
3112 Multi_sg_netgame->security = (rand() % 32766) + 1; // get some random security number
3113 Multi_sg_netgame->mode = NG_MODE_OPEN;
3114 Multi_sg_netgame->rank_base = RANK_ENSIGN;
3115 if(Multi_sg_netgame->security < 16){
3116 Multi_sg_netgame->security += 16;
3119 // set the version_info field
3120 Multi_sg_netgame->version_info = NG_VERSION_ID;
3122 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
3123 Netgame.host = Net_player;
3126 // set the default netgame flags
3127 Multi_sg_netgame->flags = 0;
3129 // intialize endgame stuff
3130 multi_endgame_init();
3132 // load in my netgame options
3133 multi_options_netgame_load(&Netgame.options);
3135 // load my local netplayer options
3136 multi_options_local_load(&Net_player->p_info.options, Net_player);
3138 // setup the default game name, taking care of string length and player callsigns
3139 memset(out_name,0,128);
3141 pilot_format_callsign_personal(Player->callsign,out_name);
3142 sprintf(buf, XSTR("%s game",782), out_name); // [[ %s will be a pilot's name ]]
3143 if ( strlen(buf) > MAX_GAMENAME_LEN ){
3144 strcpy(buf, XSTR("Temporary name",783));
3146 strcpy(Multi_sg_netgame->name, buf);
3148 // set the default qos and duration
3149 multi_voice_maybe_update_vars(Netgame.options.voice_qos,Netgame.options.voice_record_time);
3151 // make sure to set the server correctly (me or the standalone)
3152 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3153 memcpy(&Netgame.server_addr, &Psnet_my_addr,sizeof(net_addr));
3154 Netgame.server = Net_player;
3155 Net_player->player_id = multi_get_new_id();
3157 // setup debug flags
3158 Netgame.debug_flags = 0;
3160 if(!Cmdline_server_firing){
3161 Netgame.debug_flags |= NETD_FLAG_CLIENT_FIRING;
3163 if(!Cmdline_client_dodamage){
3164 Netgame.debug_flags |= NETD_FLAG_CLIENT_NODAMAGE;
3168 memcpy(&Netgame.server_addr,&save,sizeof(net_addr));
3169 Netgame.server = server_save;
3172 // if I have a cd or not
3174 Net_player->flags |= NETINFO_FLAG_HAS_CD;
3177 // if I have hacked data
3178 if(game_hacked_data()){
3179 Net_player->flags |= NETINFO_FLAG_HAXOR;
3182 // assign my player struct and other data
3183 Net_player->flags |= (NETINFO_FLAG_CONNECTED | NETINFO_FLAG_DO_NETWORKING);
3184 Net_player->s_info.voice_token_timestamp = -1;
3186 // if we're supposed to flush our cache directory, do so now
3187 if(Net_player->p_info.options.flags & MLO_FLAG_FLUSH_CACHE){
3188 multi_flush_multidata_cache();
3191 ml_string(NOX("Flushing multi-data cache"));
3197 void multi_sg_draw_radio_buttons()
3199 // draw the appropriate radio button
3200 switch(Multi_sg_netgame->mode){
3202 Multi_sg_buttons[gr_screen.res][MSG_OPEN_GAME].button.draw_forced(2);
3205 case NG_MODE_CLOSED:
3206 Multi_sg_buttons[gr_screen.res][MSG_CLOSED_GAME].button.draw_forced(2);
3209 case NG_MODE_PASSWORD:
3210 Multi_sg_buttons[gr_screen.res][MSG_PASSWD_GAME].button.draw_forced(2);
3212 /* case NG_MODE_RESTRICTED:
3213 Multi_sg_buttons[gr_screen.res][MSG_RESTRICTED_GAME].button.draw_forced(2);
3216 case NG_MODE_RANK_ABOVE:
3217 Multi_sg_buttons[gr_screen.res][MSG_RANK_SET_GAME].button.draw_forced(2);
3218 Multi_sg_buttons[gr_screen.res][MSG_RANK_ABOVE].button.draw_forced(2);
3220 case NG_MODE_RANK_BELOW:
3221 Multi_sg_buttons[gr_screen.res][MSG_RANK_SET_GAME].button.draw_forced(2);
3222 Multi_sg_buttons[gr_screen.res][MSG_RANK_BELOW].button.draw_forced(2);
3227 void multi_sg_rank_scroll_up()
3229 // if he doesn't have either of the rank flags set, then ignore this
3230 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3234 if(Multi_sg_rank_start > 0){
3235 Multi_sg_rank_start--;
3236 gamesnd_play_iface(SND_SCROLL);
3238 gamesnd_play_iface(SND_GENERAL_FAIL);
3242 void multi_sg_rank_scroll_down()
3244 // if he doesn't have either of the rank flags set, then ignore this
3245 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3249 if((NUM_RANKS - Multi_sg_rank_start) > Multi_sg_rank_max_display[gr_screen.res]){
3250 Multi_sg_rank_start++;
3251 gamesnd_play_iface(SND_SCROLL);
3253 gamesnd_play_iface(SND_GENERAL_FAIL);
3257 void multi_sg_rank_display_stuff()
3262 // if he doesn't have either of the rank flags set, then ignore this
3263 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3267 // display the list of ranks
3268 y = Msg_rank_list_coords[gr_screen.res][MSG_Y_COORD];
3269 idx = Multi_sg_rank_start;
3271 while((count < NUM_RANKS) && (count < Multi_sg_rank_max_display[gr_screen.res]) && (idx < NUM_RANKS)){
3272 // if its the selected item, then color it differently
3273 if(idx == Multi_sg_rank_select){
3274 gr_set_color_fast(&Color_text_selected);
3276 gr_set_color_fast(&Color_text_normal);
3280 multi_sg_rank_build_name(Ranks[idx].name,rank_name);
3281 gr_string(Msg_rank_list_coords[gr_screen.res][MSG_X_COORD],y,rank_name);
3289 // display the selected rank
3291 gr_set_color_fast(&Color_bright);
3292 multi_sg_rank_build_name(Ranks[Multi_sg_netgame->rank_base].name,rank_name);
3293 gr_string(Msg_rank_sel_coords[gr_screen.res][MSG_X_COORD],Msg_rank_sel_coords[gr_screen.res][MSG_Y_COORD],rank_name);
3297 void multi_sg_rank_process_select()
3301 // if he doesn't have either of the rank flags set, then ignore this
3302 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3306 // see if he's clicked on an item on the rank list
3307 if(Multi_sg_rank_button.pressed()){
3309 Multi_sg_rank_button.get_mouse_pos(NULL,&y);
3312 if(item + Multi_sg_rank_start < NUM_RANKS){
3313 // evaluate whether this rank is valid for the guy to pick
3314 if(multi_sg_rank_select_valid(item + Multi_sg_rank_start)){
3315 gamesnd_play_iface(SND_USER_SELECT);
3317 Multi_sg_rank_select = item + Multi_sg_rank_start;
3319 // set the Netgame rank
3320 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
3322 gamesnd_play_iface(SND_GENERAL_FAIL);
3324 memset(string,0,255);
3325 sprintf(string,XSTR("Illegal value for a host of your rank (%s)\n",784),Ranks[Net_player->player->stats.rank].name);
3326 multi_common_add_notify(string);
3332 void multi_sg_rank_build_name(char *in,char *out)
3338 first = strtok(use," ");
3340 // just copy the string
3345 // if the first part of the string is lieutenant, then abbreivate it and tack on the rest of the string
3346 if (stricmp(first,XSTR("lieutenant",785)) == 0) {
3347 first = strtok(NULL, NOX("\n"));
3349 // if he's not just a plain lieutenant
3351 strcpy(out,XSTR("Lt. ",786)); // [[ lieutenant ]]
3354 // if he _is_ just a plain lieutenant
3363 void multi_sg_check_passwd()
3365 // check to see if the password input box has been pressed
3366 if(Multi_sg_game_passwd.changed()){
3367 Multi_sg_game_passwd.get_text(Multi_sg_netgame->passwd);
3371 void multi_sg_check_name()
3373 // check to see if the game name input box has been pressed
3374 if(Multi_sg_game_name.changed()){
3375 Multi_sg_game_name.get_text(Multi_sg_netgame->name);
3379 void multi_sg_release_passwd()
3381 // hide and disable the password input box
3382 Multi_sg_game_passwd.hide();
3383 Multi_sg_game_passwd.disable();
3385 // set the focus back to the name input box
3386 Multi_sg_game_name.set_focus();
3389 int multi_sg_rank_select_valid(int rank)
3392 if(Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE){
3393 if(Net_player->player->stats.rank >= rank){
3399 if(Net_player->player->stats.rank <= rank){
3407 void multi_sg_select_rank_default()
3409 // pick our rank for now
3410 Multi_sg_rank_select = Net_player->player->stats.rank;
3412 // set the Netgame rank
3413 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
3416 // -------------------------------------------------------------------------------------------------
3418 // MULTIPLAYER CREATE GAME screen
3423 char *Multi_create_bitmap_fname[GR_NUM_RESOLUTIONS] = {
3424 "MultiCreate", // GR_640
3425 "2_MultiCreate" // GR_1024
3428 char *Multi_create_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
3429 "MultiCreate-M", // GR_640
3430 "2_MultiCreate-M" // GR_1024
3433 char *Multi_create_loading_fname[GR_NUM_RESOLUTIONS] = {
3434 "PleaseWait", // GR_640
3435 "2_PleaseWait" // GR_1024
3439 #define MULTI_CREATE_NUM_BUTTONS 23
3442 #define MC_SHOW_ALL 0
3443 #define MC_SHOW_COOP 1
3444 #define MC_SHOW_TEAM 2
3445 #define MC_SHOW_DOGFIGHT 3
3446 #define MC_PXO_REFRESH 4
3447 #define MC_PILOT_INFO 5
3448 #define MC_SCROLL_LIST_UP 6
3449 #define MC_SCROLL_LIST_DOWN 7
3450 #define MC_SCROLL_PLAYERS_UP 8
3451 #define MC_SCROLL_PLAYERS_DOWN 9
3452 #define MC_MISSION_FILTER 10
3453 #define MC_CAMPAIGN_FILTER 11
3454 #define MC_CANCEL 12
3459 #define MC_SCROLL_INFO_UP 17
3460 #define MC_SCROLL_INFO_DOWN 18
3461 #define MC_HOST_OPTIONS 19
3463 #define MC_OPTIONS 21
3464 #define MC_ACCEPT 22
3467 UI_WINDOW Multi_create_window; // the window object for the create screen
3468 UI_BUTTON Multi_create_player_select_button; // for selecting players
3469 UI_BUTTON Multi_create_list_select_button; // for selecting missions/campaigns
3470 int Multi_create_bitmap; // the background bitmap
3471 UI_SLIDER2 Multi_create_slider; // for create list
3473 // constants for coordinate look ups
3474 #define MC_X_COORD 0
3475 #define MC_Y_COORD 1
3476 #define MC_W_COORD 2
3477 #define MC_H_COORD 3
3479 ui_button_info Multi_create_buttons[GR_NUM_RESOLUTIONS][MULTI_CREATE_NUM_BUTTONS] = {
3481 ui_button_info("MC_00", 32, 129, 36, 158, 0), // show all missions
3482 ui_button_info("MC_01", 76, 129, 71, 158, 1), // show coop missions
3483 ui_button_info("MC_02", 121, 129, 119, 158, 2), // show team missions
3484 ui_button_info("MC_03", 164, 129, 166, 158, 3), // show dogfight missions
3485 ui_button_info("MC_04", 399, 129, 229, 130, 4), // pxo mission refresh
3486 ui_button_info("MC_05", 567, 123, 467, 132, 5), // pilot info
3487 ui_button_info("MC_06", 1, 161, -1, -1, 6), // scroll mission info up
3488 ui_button_info("MC_08", 1, 304, -1, -1, 8), // scroll mission info down
3489 ui_button_info("MC_09", 613, 160, -1, -1, 9), // scroll players up
3490 ui_button_info("MC_10", 613, 202, -1, -1, 10), // scroll players down
3491 ui_button_info("MC_11", 22, 346, 27, 376, 11), // mission filter
3492 ui_button_info("MC_12", 104, 346, 110, 376, 12), // campaign filter
3493 ui_button_info("MC_13", 392, 341, 328, 364, 13), // cancel
3494 ui_button_info("MC_14", 472, 352, 482, 381, 14), // team 0
3495 ui_button_info("MC_15", 506, 352, 514, 381, 15), // team 1
3496 ui_button_info("MC_16", 539, 346, 539, 381, 16), // kick
3497 ui_button_info("MC_17", 589, 346, 582, 381, 17), // close
3498 ui_button_info("MC_18", 1, 406, -1, -1, 18), // scroll list up
3499 ui_button_info("MC_19", 1, 447, -1, -1, 19), // scroll list down
3500 ui_button_info("MC_20", 499, 434, 436, 423, 20), // host options
3501 ui_button_info("MC_21", 534, 426, -1, -1, 21), // help
3502 ui_button_info("MC_22", 534, 452, -1, -1, 22), // options
3503 ui_button_info("MC_23", 571, 426, 572, 413, 23), // commit
3506 ui_button_info("2_MC_00", 51, 207, 61, 253, 0), // show all missions
3507 ui_button_info("2_MC_01", 122, 207, 124, 253, 1), // show coop missions
3508 ui_button_info("2_MC_02", 193, 207, 194, 253, 2), // show team missions
3509 ui_button_info("2_MC_03", 263, 207, 261, 253, 3), // show dogfight missions
3510 ui_button_info("2_MC_04", 639, 207, 479, 218, 4), // pxo mission refresh
3511 ui_button_info("2_MC_05", 907, 197, 748, 216, 5), // pilot info
3512 ui_button_info("2_MC_06", 1, 258, -1, -1, 6), // scroll mission info up
3513 ui_button_info("2_MC_08", 1, 487, -1, -1, 8), // scroll mission info down
3514 ui_button_info("2_MC_09", 981, 256, -1, -1, 9), // scroll players up
3515 ui_button_info("2_MC_10", 981, 323, -1, -1, 10), // scroll players down
3516 ui_button_info("2_MC_11", 35, 554, 46, 601, 11), // mission filter
3517 ui_button_info("2_MC_12", 166, 554, 174, 601, 12), // campaign filter
3518 ui_button_info("2_MC_13", 628, 545, 559, 582, 13), // cancel
3519 ui_button_info("2_MC_14", 756, 564, 772, 610, 14), // team 0
3520 ui_button_info("2_MC_15", 810, 564, 826, 610, 15), // team 1
3521 ui_button_info("2_MC_16", 862, 554, 872, 610, 16), // kick
3522 ui_button_info("2_MC_17", 943, 554, 949, 610, 17), // close
3523 ui_button_info("2_MC_18", 1, 649, -1, -1, 18), // scroll list up
3524 ui_button_info("2_MC_19", 1, 716, -1, -1, 19), // scroll list down
3525 ui_button_info("2_MC_20", 798, 695, 726, 667, 20), // host options
3526 ui_button_info("2_MC_21", 854, 681, -1, -1, 21), // help
3527 ui_button_info("2_MC_22", 854, 724, -1, -1, 22), // options
3528 ui_button_info("2_MC_23", 914, 681, 932, 667, 23), // commit
3532 #define MULTI_CREATE_NUM_TEXT 15
3533 UI_XSTR Multi_create_text[GR_NUM_RESOLUTIONS][MULTI_CREATE_NUM_BUTTONS] = {
3535 {"All", 1256, 36, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_ALL].button},
3536 {"Coop", 1257, 71, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_COOP].button},
3537 {"Team", 1258, 119, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_TEAM].button},
3538 {"Dogfight", 1259, 166, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_DOGFIGHT].button},
3539 {"Refresh Missions", 1260, 229, 130, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_PXO_REFRESH].button},
3540 {"Pilot Info", 1261, 467, 132, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_PILOT_INFO].button},
3541 {"Missions", 1262, 27, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_MISSION_FILTER].button},
3542 {"Campaigns", 1263, 110, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_CAMPAIGN_FILTER].button},
3543 {"Cancel", 387, 328, 364, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_CANCEL].button},
3544 {"1", 1264, 482, 381, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_TEAM0].button},
3545 {"2", 1265, 514, 381, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_TEAM1].button},
3546 {"Kick", 1266, 539, 381, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_KICK].button},
3547 {"Close", 1508, 582, 381, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_CLOSE].button},
3548 {"Host Options", 1267, 436, 423, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_HOST_OPTIONS].button},
3549 {"Commit", 1062, 572, 413, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_ACCEPT].button}
3552 {"All", 1256, 61, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_ALL].button},
3553 {"Coop", 1257, 124, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_COOP].button},
3554 {"Team", 1258, 194, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_TEAM].button},
3555 {"Dogfight", 1259, 261, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_DOGFIGHT].button},
3556 {"Refresh Missions", 1260, 501, 218, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_PXO_REFRESH].button},
3557 {"Pilot Info", 1261, 814, 216, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_PILOT_INFO].button},
3558 {"Missions", 1262, 46, 601, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_MISSION_FILTER].button},
3559 {"Campaigns", 1263, 174, 601, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_CAMPAIGN_FILTER].button},
3560 {"Cancel", 387, 559, 582, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_CANCEL].button},
3561 {"1", 1264, 772, 610, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_TEAM0].button},
3562 {"2", 1265, 826, 610, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_TEAM1].button},
3563 {"Kick", 1266, 872, 610, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_KICK].button},
3564 {"Close", 1508, 949, 610, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_CLOSE].button},
3565 {"Host Options", 1267, 755, 683, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_HOST_OPTIONS].button},
3566 {"Commit", 1062, 932, 667, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_ACCEPT].button}
3570 // squad war checkbox
3571 UI_CHECKBOX Multi_create_sw_checkbox;
3572 char *Multi_create_sw_checkbox_fname[GR_NUM_RESOLUTIONS] = {
3576 int Multi_create_sw_checkbox_coords[GR_NUM_RESOLUTIONS][2] = {
3584 int Multi_create_sw_checkbox_text[GR_NUM_RESOLUTIONS][2] = {
3593 // game information text areas
3594 int Mc_list_coords[GR_NUM_RESOLUTIONS][4] = {
3603 int Mc_players_coords[GR_NUM_RESOLUTIONS][4] = {
3612 int Mc_info_coords[GR_NUM_RESOLUTIONS][4] = {
3621 // mission icon stuff
3622 int Mc_icon_type_coords[GR_NUM_RESOLUTIONS][2] = {
3624 38, -2 // y is an offset
3627 61, -2 // y is an offset
3631 int Mc_icon_volition_coords[GR_NUM_RESOLUTIONS][2] = {
3633 61, -1 // y is an offset
3636 98, 1 // y is an offset
3640 int Mc_icon_silent_coords[GR_NUM_RESOLUTIONS][2] = {
3642 72, 0 // y is an offset
3645 115, 0 // y is an offset
3649 int Mc_icon_valid_coords[GR_NUM_RESOLUTIONS][2] = {
3651 91, 0 // y is an offset
3654 146, 0 // y is an offset
3658 // mission/campaign list column areas
3659 int Mc_column1_w[GR_NUM_RESOLUTIONS] = {
3664 int Mc_column2_w[GR_NUM_RESOLUTIONS] = {
3669 int Mc_column3_w[GR_NUM_RESOLUTIONS] = {
3674 int Mc_mission_name_x[GR_NUM_RESOLUTIONS] = {
3679 int Mc_mission_count_x[GR_NUM_RESOLUTIONS] = {
3684 int Mc_mission_fname_x[GR_NUM_RESOLUTIONS] = {
3689 int Mc_create_game_text[GR_NUM_RESOLUTIONS][2] = {
3690 {13, 116}, // GR_640
3691 {21, 186} // GR_1024
3694 int Mc_players_text[GR_NUM_RESOLUTIONS][2] = {
3695 {467, 150}, // GR_640
3696 {747, 240} // GR_1024
3699 int Mc_team_text[GR_NUM_RESOLUTIONS][2] = {
3700 {484, 342}, // GR_640
3701 {774, 547} // GR_1024
3704 int Mc_slider_coords[GR_NUM_RESOLUTIONS][4] = {
3706 3, 197, 13, 105 // GR_640
3709 5, 316, 20, 168 // GR_1024
3713 char *Mc_slider_bitmap[GR_NUM_RESOLUTIONS] = {
3718 // player list control thingie defs
3719 #define MULTI_CREATE_PLIST_MAX_DISPLAY 20
3720 int Multi_create_plist_select_flag; // flag indicating if we have a play selected
3721 short Multi_create_plist_select_id; // the net address of the currently selected player (for lookup)
3723 // master tracker details
3724 int Multi_create_frame_count; // framecount
3725 int Multi_create_mt_tried_login; // attempted to login this server on the MT
3727 // mission filter settings
3728 int Multi_create_filter; // what mode we're in
3730 // game/campaign list control defs
3731 int Multi_create_list_max_display[GR_NUM_RESOLUTIONS] = {
3737 int Multi_create_list_count; // number of items in listbox
3738 int Multi_create_list_mode; // 0 == mission mode, 1 == campaign mode
3739 int Multi_create_list_start; // where to start displaying from
3740 int Multi_create_list_select; // which item is currently highlighted
3741 int Multi_create_files_loaded;
3743 char Multi_create_files_array[MULTI_CREATE_MAX_LIST_ITEMS][MAX_FILENAME_LEN];
3745 int Multi_create_mission_count; // how many we have
3746 int Multi_create_campaign_count;
3747 multi_create_info Multi_create_mission_list[MULTI_CREATE_MAX_LIST_ITEMS];
3748 multi_create_info Multi_create_campaign_list[MULTI_CREATE_MAX_LIST_ITEMS];
3750 // use a pointer for the file list. Will point to either the missions or the campaigns
3751 multi_create_info *Multi_create_file_list;
3753 // LOCAL function definitions
3754 void multi_create_check_buttons();
3755 void multi_create_button_pressed(int n);
3756 void multi_create_init_as_server();
3757 void multi_create_init_as_client();
3758 void multi_create_do_netstuff();
3759 void multi_create_plist_scroll_up();
3760 void multi_create_plist_scroll_down();
3761 void multi_create_plist_process();
3762 void multi_create_plist_blit_normal();
3763 void multi_create_plist_blit_team();
3764 void multi_create_list_scroll_up();
3765 void multi_create_list_scroll_down();
3766 void multi_create_list_do();
3767 void multi_create_list_select_item(int n);
3768 void multi_create_list_blit_icons(int list_index, int y_start);
3769 void multi_create_accept_hit();
3770 void multi_create_draw_filter_buttons();
3771 void multi_create_set_selected_team(int team);
3772 short multi_create_get_mouse_id();
3773 int multi_create_ok_to_commit();
3774 int multi_create_verify_cds();
3775 void multi_create_refresh_pxo();
3776 void multi_create_sw_clicked();
3778 // since we can selectively filter out mission/campaign types we always need to map a selected index (which is relative
3779 // to the displayed list), to an absolute index (which is relative to the total file list - some of which may filtered out)
3780 void multi_create_select_to_filename(int select_index,char *filename);
3781 int multi_create_select_to_index(int select_index);
3783 int Multi_create_should_show_popup = 0;
3786 // sorting function to sort mission lists.. Basic sorting on mission name
3787 int multi_create_sort_func(const void *a, const void *b)
3789 multi_create_info *m1, *m2;
3791 m1 = (multi_create_info *)a;
3792 m2 = (multi_create_info *)b;
3794 return ( strcmp(m1->name, m2->name) );
3797 void multi_create_setup_list_data(int mode)
3799 int idx,should_sort,switched_modes;
3801 // set the current mode
3804 if((Multi_create_list_mode != mode) && (mode != -1)){
3805 Multi_create_list_mode = mode;
3808 // set up the list pointers
3809 if ( Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ) {
3810 Multi_create_file_list = Multi_create_mission_list;
3811 } else if ( Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ) {
3812 Multi_create_file_list = Multi_create_campaign_list;
3818 // get the mission count based upon the filter selected
3819 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
3820 switch(Multi_create_filter){
3821 case MISSION_TYPE_MULTI:
3822 Multi_create_list_count = Multi_create_mission_count;
3825 Multi_create_list_count = 0;
3826 // find all missions which match
3827 for(idx=0;idx<Multi_create_mission_count;idx++){
3828 if(Multi_create_mission_list[idx].flags & Multi_create_filter){
3829 Multi_create_list_count++;
3833 // if we switched modes and we have more than 0 items, sort them
3834 if(switched_modes && (Multi_create_list_count > 0)){
3839 } else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
3840 switch(Multi_create_filter){
3841 case MISSION_TYPE_MULTI:
3842 Multi_create_list_count = Multi_create_campaign_count;
3845 Multi_create_list_count = 0;
3846 // find all missions which match
3847 for(idx=0;idx<Multi_create_campaign_count;idx++){
3848 if(Multi_create_campaign_list[idx].flags & Multi_create_filter){
3849 Multi_create_list_count++;
3853 // if we switched modes and we have more than 0 items, sort them
3854 if(switched_modes && (Multi_create_list_count > 0)){
3861 // reset the list start and selected indices
3862 Multi_create_list_start = 0;
3863 Multi_create_list_select = -1;
3864 multi_create_list_select_item(Multi_create_list_start);
3866 // sort the list of missions if necessary
3868 qsort(Multi_create_file_list, Multi_create_list_count, sizeof(multi_create_info), multi_create_sort_func);
3872 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);
3875 void multi_create_game_init()
3880 // now make sure to initialze various netgame stuff based upon whether we're on a standalone or not
3881 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3882 multi_create_init_as_server();
3884 multi_create_init_as_client();
3887 // initialize the player list data
3888 Multi_create_plist_select_flag = 0;
3889 Multi_create_plist_select_id = -1;
3891 // create the interface window
3892 Multi_create_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
3893 Multi_create_window.set_mask_bmap(Multi_create_bitmap_mask_fname[gr_screen.res]);
3895 // load the background bitmap
3896 Multi_create_bitmap = bm_load(Multi_create_bitmap_fname[gr_screen.res]);
3897 if(Multi_create_bitmap < 0){
3898 // we failed to load the bitmap - this is very bad
3902 // close any previous existing instances of the chatbox and create a new one
3906 // load the help overlay
3907 help_overlay_load(MULTI_CREATE_OVERLAY);
3908 help_overlay_set_state(MULTI_CREATE_OVERLAY, 0);
3910 // initialize the common notification messaging
3911 multi_common_notify_init();
3913 // use the common interface palette
3914 multi_common_set_palette();
3916 // create the interface buttons
3917 for(idx=0; idx<MULTI_CREATE_NUM_BUTTONS; idx++){
3918 b = &Multi_create_buttons[gr_screen.res][idx];
3920 // create the object
3921 b->button.create(&Multi_create_window, "", b->x, b->y, 1, 1, ((idx == MC_SCROLL_LIST_UP) || (idx == MC_SCROLL_LIST_DOWN)), 1);
3923 // set the sound to play when highlighted
3924 b->button.set_highlight_action(common_play_highlight_sound);
3926 // set the ani for the button
3927 b->button.set_bmaps(b->filename);
3930 b->button.link_hotspot(b->hotspot);
3932 // some special case stuff for the pxo refresh button
3933 if(idx == MC_PXO_REFRESH){
3934 // if not a PXO game, or if I'm not a server disable and hide the button
3935 if(!MULTI_IS_TRACKER_GAME || !MULTIPLAYER_MASTER){
3937 b->button.disable();
3943 for(idx=0; idx<MULTI_CREATE_NUM_TEXT; idx++){
3944 Multi_create_window.add_XSTR(&Multi_create_text[gr_screen.res][idx]);
3947 // if this is a PXO game, enable the squadwar checkbox
3948 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);
3949 Multi_create_sw_checkbox.set_bmaps(Multi_create_sw_checkbox_fname[gr_screen.res], 6, 0);
3950 if(!MULTI_IS_TRACKER_GAME){
3951 Multi_create_sw_checkbox.hide();
3952 Multi_create_sw_checkbox.disable();
3956 // disable squad war button in demo
3957 Multi_create_sw_checkbox.hide();
3958 Multi_create_sw_checkbox.disable();
3961 // initialize the mission type filtering mode
3962 Multi_create_filter = MISSION_TYPE_MULTI;
3964 // initialize the list mode, and load in a list
3965 memset(Multi_create_mission_list, 0, sizeof(multi_create_info) * MULTI_CREATE_MAX_LIST_ITEMS);
3966 memset(Multi_create_campaign_list, 0, sizeof(multi_create_info) * MULTI_CREATE_MAX_LIST_ITEMS);
3967 for(idx=0; idx<MULTI_CREATE_MAX_LIST_ITEMS; idx++){
3968 Multi_create_mission_list[idx].valid_status = MVALID_STATUS_UNKNOWN;
3969 Multi_create_campaign_list[idx].valid_status = MVALID_STATUS_UNKNOWN;
3971 Multi_create_list_mode = MULTI_CREATE_SHOW_MISSIONS;
3972 Multi_create_list_start = -1;
3973 Multi_create_list_select = -1;
3974 Multi_create_list_count = 0;
3976 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);
3978 // create the player list select button
3979 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);
3980 Multi_create_player_select_button.hide();
3982 // create the mission/campaign list select button
3983 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);
3984 Multi_create_list_select_button.hide();
3986 // set hotkeys for a couple of things.
3987 Multi_create_buttons[gr_screen.res][MC_ACCEPT].button.set_hotkey(KEY_CTRLED+KEY_ENTER);
3989 // init some master tracker stuff
3990 Multi_create_frame_count = 0;
3991 Multi_create_mt_tried_login = 0;
3993 // remove campaign flags
3994 Game_mode &= ~(GM_CAMPAIGN_MODE);
3996 // send any pilots as appropriate
3997 multi_data_send_my_junk();
3998 Multi_create_file_list = Multi_create_mission_list;
4000 Multi_create_campaign_count = 0;
4001 Multi_create_mission_count = 0;
4002 Multi_create_files_loaded = 0;
4005 void multi_create_game_do()
4008 char *loading_str = XSTR("Loading", 1336);
4011 // set this if we want to show the pilot info popup
4012 Multi_create_should_show_popup = 0;
4014 // first thing is to load the files
4015 if ( !Multi_create_files_loaded ) {
4016 // if I am a client, send a list request to the server for the missions
4017 if ( MULTIPLAYER_CLIENT ) {
4018 send_mission_list_request( MISSION_LIST_REQUEST );
4022 loading_bitmap = bm_load(Multi_create_loading_fname[gr_screen.res]);
4024 // draw the background, etc
4026 GR_MAYBE_CLEAR_RES(Multi_create_bitmap);
4027 if(Multi_create_bitmap != -1){
4028 gr_set_bitmap(Multi_create_bitmap);
4032 if ( loading_bitmap > -1 ){
4033 gr_set_bitmap(loading_bitmap);
4035 gr_bitmap( Please_wait_coords[gr_screen.res][MC_X_COORD], Please_wait_coords[gr_screen.res][MC_Y_COORD] );
4037 // draw "Loading" on it
4038 gr_set_color_fast(&Color_normal);
4040 gr_get_string_size(&str_w, &str_h, loading_str);
4041 gr_string((gr_screen.max_w - str_w) / 2, (gr_screen.max_h - str_h) / 2, loading_str);
4046 multi_create_list_load_missions();
4047 multi_create_list_load_campaigns();
4049 // if this is a tracker game, validate missions
4050 if(MULTI_IS_TRACKER_GAME){
4051 multi_update_valid_missions();
4054 // update the file list
4055 multi_create_setup_list_data(MULTI_CREATE_SHOW_MISSIONS);
4058 // don't bother setting netgame state if ont the server
4059 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4060 Netgame.game_state = NETGAME_STATE_FORMING;
4061 send_netgame_update_packet();
4064 // if we're on the standalone we have to tell him that we're now in the host setup screen
4065 Net_player->state = NETPLAYER_STATE_HOST_SETUP;
4066 send_netplayer_update_packet();
4068 Multi_create_files_loaded = 1;
4071 int k = chatbox_process();
4072 k = Multi_create_window.process(k,0);
4075 // same as the cancel button
4077 if(help_overlay_active(MULTI_CREATE_OVERLAY)){
4078 help_overlay_set_state(MULTI_CREATE_OVERLAY,0);
4080 gamesnd_play_iface(SND_USER_SELECT);
4081 multi_quit_game(PROMPT_HOST);
4086 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
4087 help_overlay_set_state(MULTI_CREATE_OVERLAY, 0);
4090 // process any button clicks
4091 multi_create_check_buttons();
4093 // do any network related stuff
4094 multi_create_do_netstuff();
4096 // draw the background, etc
4098 GR_MAYBE_CLEAR_RES(Multi_create_bitmap);
4099 if(Multi_create_bitmap != -1){
4100 gr_set_bitmap(Multi_create_bitmap);
4104 // if we're not in team vs. team mode, don't draw the team buttons
4105 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
4106 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.hide();
4107 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.hide();
4108 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.disable();
4109 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.disable();
4111 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.enable();
4112 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.enable();
4113 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.unhide();
4114 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.unhide();
4117 // draw the window itself
4118 Multi_create_window.draw();
4120 gr_set_color_fast(&Color_normal);
4122 // draw Create Game text
4123 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));
4125 // draw players text
4126 gr_string(Mc_players_text[gr_screen.res][MC_X_COORD], Mc_players_text[gr_screen.res][MC_Y_COORD], XSTR("Players", 1269));
4128 // draw players text
4129 gr_string(Mc_team_text[gr_screen.res][MC_X_COORD], Mc_team_text[gr_screen.res][MC_Y_COORD], XSTR("Team", 1258));
4131 // process and display the player list
4132 // NOTE : this must be done before the buttons are checked to insure that a player hasn't dropped
4133 multi_create_plist_process();
4134 if(Netgame.type_flags & NG_TYPE_TEAM){
4135 multi_create_plist_blit_team();
4137 multi_create_plist_blit_normal();
4140 // process and display the game/campaign list
4141 multi_create_list_do();
4143 // draw the correct mission filter button
4144 multi_create_draw_filter_buttons();
4146 // display any text in the info area
4147 multi_common_render_text();
4149 // display any pending notification messages
4150 multi_common_notify_do();
4152 // force the correct mission/campaign button to light up
4153 if( Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ){
4154 Multi_create_buttons[gr_screen.res][MC_MISSION_FILTER].button.draw_forced(2);
4156 Multi_create_buttons[gr_screen.res][MC_CAMPAIGN_FILTER].button.draw_forced(2);
4159 // force draw the closed button if it is toggled on
4160 if(Netgame.flags & NG_FLAG_TEMP_CLOSED){
4161 Multi_create_buttons[gr_screen.res][MC_CLOSE].button.draw_forced(2);
4164 // process and show the chatbox thingie
4168 Multi_create_window.draw_tooltip();
4170 // display the voice status indicator
4171 multi_common_voice_display_status();
4173 // blit the help overlay if necessary
4174 help_overlay_maybe_blit(MULTI_CREATE_OVERLAY);
4177 if(MULTI_IS_TRACKER_GAME){
4178 if(Netgame.type_flags & NG_TYPE_SW){
4179 gr_set_color_fast(&Color_bright);
4181 gr_set_color_fast(&Color_normal);
4183 gr_string(Multi_create_sw_checkbox_text[gr_screen.res][0], Multi_create_sw_checkbox_text[gr_screen.res][1], "SquadWar");
4189 // if we're supposed to show the pilot info popup, do it now
4190 if(Multi_create_should_show_popup){
4191 // get the player index and address of the player item the mouse is currently over
4192 if(Multi_create_plist_select_flag){
4193 player_index = find_player_id(Multi_create_plist_select_id);
4194 if(player_index != -1){
4195 multi_pinfo_popup(&Net_players[player_index]);
4200 // increment the frame count
4201 Multi_create_frame_count++;
4204 void multi_create_game_close()
4206 // unload any bitmaps
4207 if(!bm_unload(Multi_create_bitmap)){
4208 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_create_bitmap_fname[gr_screen.res]));
4211 // unload the help overlay
4212 help_overlay_unload(MULTI_CREATE_OVERLAY);
4214 // destroy the chatbox
4217 // destroy the UI_WINDOW
4218 Multi_create_window.destroy();
4221 void multi_create_check_buttons()
4224 for(idx=0;idx<MULTI_CREATE_NUM_BUTTONS;idx++){
4225 // we only really need to check for one button pressed at a time, so we can break after
4227 if(Multi_create_buttons[gr_screen.res][idx].button.pressed()){
4228 multi_create_button_pressed(idx);
4233 // if the squad war checkbox was clicked
4234 if(Multi_create_sw_checkbox.changed()){
4235 multi_create_sw_clicked();
4239 void multi_create_button_pressed(int n)
4245 gamesnd_play_iface(SND_USER_SELECT);
4246 multi_quit_game(PROMPT_HOST);
4249 // if valid commit conditions have not been met
4250 if(!multi_create_ok_to_commit()){
4255 multi_create_accept_hit();
4260 if(!help_overlay_active(MULTI_CREATE_OVERLAY)){
4261 help_overlay_set_state(MULTI_CREATE_OVERLAY,1);
4263 help_overlay_set_state(MULTI_CREATE_OVERLAY,0);
4267 // scroll the info text box up
4268 case MC_SCROLL_INFO_UP:
4269 multi_common_scroll_text_up();
4272 // scroll the info text box down
4273 case MC_SCROLL_INFO_DOWN:
4274 multi_common_scroll_text_down();
4277 // scroll the player list up
4278 case MC_SCROLL_PLAYERS_UP:
4279 multi_create_plist_scroll_up();
4282 // scroll the player list down
4283 case MC_SCROLL_PLAYERS_DOWN:
4284 multi_create_plist_scroll_down();
4287 // scroll the game/campaign list up
4288 case MC_SCROLL_LIST_UP:
4289 multi_create_list_scroll_up();
4290 Multi_create_slider.forceUp(); // move slider up
4293 // scroll the game/campaign list down
4294 case MC_SCROLL_LIST_DOWN:
4295 multi_create_list_scroll_down();
4296 Multi_create_slider.forceDown(); // move slider down
4299 // go to the options screen
4301 gamesnd_play_iface(SND_USER_SELECT);
4302 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
4305 // show all missions
4307 if(Multi_create_filter != MISSION_TYPE_MULTI){
4308 gamesnd_play_iface(SND_USER_SELECT);
4309 Multi_create_filter = MISSION_TYPE_MULTI;
4310 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4312 gamesnd_play_iface(SND_GENERAL_FAIL);
4316 // show cooperative missions
4318 if(Multi_create_filter != MISSION_TYPE_MULTI_COOP){
4319 gamesnd_play_iface(SND_USER_SELECT);
4320 Multi_create_filter = MISSION_TYPE_MULTI_COOP;
4321 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4323 gamesnd_play_iface(SND_GENERAL_FAIL);
4327 // show team vs. team missions
4329 if(Multi_create_filter != MISSION_TYPE_MULTI_TEAMS){
4330 gamesnd_play_iface(SND_USER_SELECT);
4331 Multi_create_filter = MISSION_TYPE_MULTI_TEAMS;
4332 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4334 gamesnd_play_iface(SND_GENERAL_FAIL);
4338 // show dogfight missions
4339 case MC_SHOW_DOGFIGHT:
4340 if (Multi_create_filter != MISSION_TYPE_MULTI_DOGFIGHT){
4341 gamesnd_play_iface(SND_USER_SELECT);
4342 Multi_create_filter = MISSION_TYPE_MULTI_DOGFIGHT;
4343 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4345 gamesnd_play_iface(SND_GENERAL_FAIL);
4349 // toggle temporary netgame closed on/off
4351 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4352 Netgame.flags ^= NG_FLAG_TEMP_CLOSED;
4354 Netgame.options.flags |= MLO_FLAG_TEMP_CLOSED;
4355 multi_options_update_netgame();
4357 gamesnd_play_iface(SND_USER_SELECT);
4360 // kick the currently selected player (if possible)
4362 // lookup the player at the specified index
4363 if(Multi_create_plist_select_flag){
4364 idx = find_player_id(Multi_create_plist_select_id);
4365 // kick him - but don't ban him
4367 multi_kick_player(idx,0);
4372 // switch to individual mission mode and load in a list
4373 case MC_MISSION_FILTER:
4374 if(Multi_create_list_mode != MULTI_CREATE_SHOW_MISSIONS){
4375 Netgame.campaign_mode = MP_SINGLE;
4377 gamesnd_play_iface(SND_USER_SELECT);
4379 // update the file list
4380 multi_create_setup_list_data(MULTI_CREATE_SHOW_MISSIONS);
4382 gamesnd_play_iface(SND_GENERAL_FAIL);
4386 // switch to campaign mode and load in a list
4387 case MC_CAMPAIGN_FILTER:
4388 // switch off squad war
4389 Multi_create_sw_checkbox.set_state(0);
4390 Netgame.type_flags = NG_TYPE_COOP;
4392 if(Multi_create_list_mode != MULTI_CREATE_SHOW_CAMPAIGNS){
4393 Netgame.campaign_mode = MP_CAMPAIGN;
4395 gamesnd_play_iface(SND_USER_SELECT);
4397 // update the file list
4398 multi_create_setup_list_data(MULTI_CREATE_SHOW_CAMPAIGNS);
4400 gamesnd_play_iface(SND_GENERAL_FAIL);
4404 // attempt to set the selected player's team
4406 multi_create_set_selected_team(0);
4407 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4408 multi_team_send_update();
4412 // attempt to set the selected player's team
4414 multi_create_set_selected_team(1);
4415 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4416 multi_team_send_update();
4420 // popup the pilot info dialog for the currently selected pilot (will occur at the end of the frame)
4422 Multi_create_should_show_popup = 1;
4425 // go to the host options screen
4426 case MC_HOST_OPTIONS:
4427 gamesnd_play_iface(SND_USER_SELECT);
4428 gameseq_post_event(GS_EVENT_MULTI_HOST_OPTIONS);
4431 // refresh PXO file list
4432 case MC_PXO_REFRESH:
4433 if(!MULTI_IS_TRACKER_GAME){
4436 multi_create_refresh_pxo();
4440 gamesnd_play_iface(SND_GENERAL_FAIL);
4441 multi_common_add_notify(XSTR("Not implemented yet!",760));
4446 // do stuff like pinging servers, sending out requests, etc
4447 void multi_create_do_netstuff()
4451 // if not on a standalone
4452 void multi_create_init_as_server()
4454 // set me up as the host and master
4455 Net_player->flags |= (NETINFO_FLAG_AM_MASTER | NETINFO_FLAG_GAME_HOST);
4458 // if on a standalone
4459 void multi_create_init_as_client()
4461 Net_player->flags |= NETINFO_FLAG_GAME_HOST;
4464 // scroll up through the player list
4465 void multi_create_plist_scroll_up()
4467 gamesnd_play_iface(SND_GENERAL_FAIL);
4470 // scroll down through the player list
4471 void multi_create_plist_scroll_down()
4473 gamesnd_play_iface(SND_GENERAL_FAIL);
4476 void multi_create_plist_process()
4478 int test_count,idx,player_index;
4480 // first determine if there are 0 players in the game. This should never happen since the host is _always_ in the game
4482 for(idx=0;idx<MAX_PLAYERS;idx++){
4483 // count anyone except the standalone server (if applicable)
4484 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
4488 if(test_count <= 0){
4492 // if we had a selected item but that player has left, select myself instead
4493 if(Multi_create_plist_select_flag){
4494 player_index = find_player_id(Multi_create_plist_select_id);
4495 if(player_index == -1){
4496 Multi_create_plist_select_id = Net_player->player_id;
4499 Multi_create_plist_select_flag = 1;
4500 Multi_create_plist_select_id = Net_player->player_id;
4503 // if the player has clicked somewhere in the player list area
4504 if(Multi_create_player_select_button.pressed()){
4507 // get the player index and address of the player item the mouse is currently over
4508 player_id = multi_create_get_mouse_id();
4509 player_index = find_player_id(player_id);
4510 if(player_index != -1){
4511 Multi_create_plist_select_flag = 1;
4512 Multi_create_plist_select_id = player_id;
4517 void multi_create_plist_blit_normal()
4520 char str[CALLSIGN_LEN+5];
4521 int y_start = Mc_players_coords[gr_screen.res][MC_Y_COORD];
4524 // display all the players
4525 for(idx=0;idx<MAX_PLAYERS;idx++){
4526 // count anyone except the standalone server (if applicable)
4527 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
4531 // highlight him if he's the host
4532 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4533 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4534 gr_set_color_fast(&Color_text_active_hi);
4536 gr_set_color_fast(&Color_bright);
4539 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4540 gr_set_color_fast(&Color_text_active);
4542 gr_set_color_fast(&Color_text_normal);
4546 // optionally draw his CD status
4547 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4548 gr_set_bitmap(Multi_common_icons[MICON_CD]);
4549 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4551 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4554 // make sure the string will fit, then display it
4555 strcpy(str,Net_players[idx].player->callsign);
4556 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4557 strcat(str,XSTR("(O)",787)); // [[ Observer ]]
4559 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4560 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4567 void multi_create_plist_blit_team()
4570 char str[CALLSIGN_LEN+1];
4571 int y_start = Mc_players_coords[gr_screen.res][MC_Y_COORD];
4574 // display all the red players first
4575 for(idx=0;idx<MAX_PLAYERS;idx++){
4576 // count anyone except the standalone server (if applicable)
4577 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
4578 // reset total offset
4581 // highlight him if he's the host
4582 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4583 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4584 gr_set_color_fast(&Color_text_active_hi);
4586 // be sure to blit the correct team button
4587 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.draw_forced(2);
4589 gr_set_color_fast(&Color_bright);
4592 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4593 gr_set_color_fast(&Color_text_active);
4595 // be sure to blit the correct team button
4596 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.draw_forced(2);
4598 gr_set_color_fast(&Color_text_normal);
4602 // optionally draw his CD status
4603 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4604 gr_set_bitmap(Multi_common_icons[MICON_CD]);
4605 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4607 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4610 // blit the red team indicator
4611 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
4612 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
4613 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT]);
4614 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4616 total_offset += Multi_common_icon_dims[MICON_TEAM0_SELECT][0] + 1;
4619 if(Multi_common_icons[MICON_TEAM0] != -1){
4620 gr_set_bitmap(Multi_common_icons[MICON_TEAM0]);
4621 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4623 total_offset += Multi_common_icon_dims[MICON_TEAM0][0] + 1;
4627 // make sure the string will fit
4628 strcpy(str,Net_players[idx].player->callsign);
4629 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4630 strcat(str,XSTR("(O)",787));
4632 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4634 // display him in the correct half of the list depending on his team
4635 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4640 // display all the green players next
4641 for(idx=0;idx<MAX_PLAYERS;idx++){
4642 // count anyone except the standalone server (if applicable)
4643 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
4644 // reset total offset
4647 // highlight him if he's the host
4648 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4649 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4650 gr_set_color_fast(&Color_text_active_hi);
4652 // be sure to blit the correct team button
4653 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.draw_forced(2);
4655 gr_set_color_fast(&Color_bright);
4658 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4659 gr_set_color_fast(&Color_text_active);
4661 // be sure to blit the correct team button
4662 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.draw_forced(2);
4664 gr_set_color_fast(&Color_text_normal);
4668 // optionally draw his CD status
4669 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4670 gr_set_bitmap(Multi_common_icons[MICON_CD]);
4671 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4673 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4676 // blit the red team indicator
4677 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
4678 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
4679 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT]);
4680 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4682 total_offset += Multi_common_icon_dims[MICON_TEAM1_SELECT][0] + 1;
4685 if(Multi_common_icons[MICON_TEAM1] != -1){
4686 gr_set_bitmap(Multi_common_icons[MICON_TEAM1]);
4687 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4689 total_offset += Multi_common_icon_dims[MICON_TEAM1][0] + 1;
4693 // make sure the string will fit
4694 strcpy(str,Net_players[idx].player->callsign);
4695 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4696 strcat(str,XSTR("(O)",787));
4698 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4700 // display him in the correct half of the list depending on his team
4701 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4707 void multi_create_list_scroll_up()
4709 if(Multi_create_list_start > 0){
4710 Multi_create_list_start--;
4712 gamesnd_play_iface(SND_SCROLL);
4714 gamesnd_play_iface(SND_GENERAL_FAIL);
4718 void multi_create_list_scroll_down()
4720 if((Multi_create_list_count - Multi_create_list_start) > Multi_create_list_max_display[gr_screen.res]){
4721 Multi_create_list_start++;
4723 gamesnd_play_iface(SND_SCROLL);
4725 gamesnd_play_iface(SND_GENERAL_FAIL);
4729 // gets a list of multiplayer misisons
4730 void multi_create_list_load_missions()
4732 char *fname, mission_name[NAME_LENGTH+1];
4733 char wild_card[256];
4736 memset(wild_card, 0, 256);
4737 strcpy(wild_card, NOX("*"));
4738 strcat(wild_card, FS_MISSION_FILE_EXT);
4739 file_count = cf_get_file_list_preallocated(MULTI_CREATE_MAX_LIST_ITEMS, Multi_create_files_array, NULL, CF_TYPE_MISSIONS, wild_card);
4740 Multi_create_mission_count = 0;
4742 // maybe create a standalone dialog
4743 if(Game_mode & GM_STANDALONE_SERVER){
4744 std_create_gen_dialog("Loading missions");
4745 std_gen_set_text("Mission:", 1);
4748 for(idx = 0; idx < file_count; idx++){
4749 int flags,max_players;
4753 fname = Multi_create_files_array[idx];
4755 // tack on any necessary file extension
4756 filename = cf_add_ext( fname, FS_MISSION_FILE_EXT );
4758 // for multiplayer beta builds, only accept builtin missions
4759 #if defined(MULTIPLAYER_BETA_BUILD) || defined(FS2_DEMO)
4760 if(game_find_builtin_mission(filename) == NULL){
4763 #elif defined(PD_BUILD)
4764 if((game_find_builtin_mission(filename) == NULL) && !strstr(filename, "peterdrake")){
4769 if(Game_mode & GM_STANDALONE_SERVER){
4770 std_gen_set_text(filename, 2);
4773 flags = mission_parse_is_multi(filename, mission_name);
4775 // if the mission is a multiplayer mission, then add it to the mission list
4777 max_players = mission_parse_get_multi_mission_info( filename );
4778 m_respawn = The_mission.num_respawns;
4780 if ( Multi_create_mission_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
4781 multi_create_info *mcip;
4783 mcip = &Multi_create_mission_list[Multi_create_mission_count];
4784 strcpy(mcip->filename, filename );
4785 strcpy(mcip->name, mission_name );
4786 mcip->flags = flags;
4787 mcip->respawn = m_respawn;
4788 mcip->max_players = (ubyte)max_players;
4790 // get any additional information for possibly builtin missions
4791 fs_builtin_mission *fb = game_find_builtin_mission(filename);
4795 Multi_create_mission_count++;
4800 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);
4802 // maybe create a standalone dialog
4803 if(Game_mode & GM_STANDALONE_SERVER){
4804 std_destroy_gen_dialog();
4808 void multi_create_list_load_campaigns()
4811 int idx, file_count;
4812 int campaign_type,max_players;
4814 char wild_card[256];
4816 // maybe create a standalone dialog
4817 if(Game_mode & GM_STANDALONE_SERVER){
4818 std_create_gen_dialog("Loading campaigns");
4819 std_gen_set_text("Campaign:", 1);
4822 Multi_create_campaign_count = 0;
4823 memset(wild_card, 0, 256);
4824 strcpy(wild_card, NOX("*"));
4825 strcat(wild_card, FS_CAMPAIGN_FILE_EXT);
4826 file_count = cf_get_file_list_preallocated(MULTI_CREATE_MAX_LIST_ITEMS, Multi_create_files_array, NULL, CF_TYPE_MISSIONS, wild_card);
4827 for(idx = 0; idx < file_count; idx++){
4829 char *filename, name[NAME_LENGTH];
4831 fname = Multi_create_files_array[idx];
4833 // tack on any necessary file extension
4834 filename = cf_add_ext( fname, FS_CAMPAIGN_FILE_EXT );
4836 // for multiplayer beta builds, only accept builtin missions
4837 #if defined(MULTIPLAYER_BETA_BUILD) || defined(FS2_DEMO)
4838 if(game_find_builtin_mission(filename) == NULL){
4841 #elif defined(PD_BUILD)
4842 if((game_find_builtin_mission(filename) == NULL) && !strstr(filename, "peterdrake")){
4847 if(Game_mode & GM_STANDALONE_SERVER){
4848 std_gen_set_text(filename, 2);
4851 // if the campaign is a multiplayer campaign, then add the data to the campaign list items
4852 flags = mission_campaign_parse_is_multi( filename, name );
4853 if( flags != CAMPAIGN_TYPE_SINGLE && mission_campaign_get_info(filename,title,&campaign_type,&max_players)) {
4854 if ( Multi_create_campaign_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
4855 multi_create_info *mcip;
4857 mcip = &Multi_create_campaign_list[Multi_create_campaign_count];
4858 strcpy(mcip->filename, filename );
4859 strcpy(mcip->name, name );
4861 // setup various flags
4862 if ( flags == CAMPAIGN_TYPE_MULTI_COOP ){
4863 mcip->flags = MISSION_TYPE_MULTI_COOP | MISSION_TYPE_MULTI;
4864 } else if ( flags == CAMPAIGN_TYPE_MULTI_TEAMS ) {
4865 mcip->flags = MISSION_TYPE_MULTI_TEAMS | MISSION_TYPE_MULTI;
4867 Int3(); // bogus campaign multi type -- find allender
4870 // 0 respawns for campaign files (should be contained within the mission files themselves)
4873 // 0 max players for campaign files
4874 mcip->max_players = (unsigned char)max_players;
4876 // get any additional information for possibly builtin missions
4877 fs_builtin_mission *fb = game_find_builtin_mission(filename);
4881 Multi_create_campaign_count++;
4886 // maybe create a standalone dialog
4887 if(Game_mode & GM_STANDALONE_SERVER){
4888 std_destroy_gen_dialog();
4892 void multi_create_list_do()
4895 int start_index,stop_index;
4896 char selected_name[255];
4898 // bail early if there aren't any selectable items
4899 if(Multi_create_list_count == 0){
4903 // first check to see if the user has clicked on an item
4904 if(Multi_create_list_select_button.pressed()){
4906 Multi_create_list_select_button.get_mouse_pos(NULL,&y);
4909 // make sure we are selectedin valid indices
4910 if((item < Multi_create_list_max_display[gr_screen.res]) && (item >= 0)){
4911 item += Multi_create_list_start;
4913 if(item < Multi_create_list_count){
4914 multi_create_list_select_item(item);
4915 gamesnd_play_iface(SND_IFACE_MOUSE_CLICK);
4920 // bail early if we don't have a start position
4921 if(Multi_create_list_start == -1){
4925 // display the list of individual campaigns/missions
4927 int y_start = Mc_list_coords[gr_screen.res][MC_Y_COORD];
4929 start_index = multi_create_select_to_index(Multi_create_list_start);
4930 stop_index = Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ? Multi_create_mission_count : Multi_create_campaign_count;
4931 for(idx=start_index; idx<stop_index; idx++){
4932 // see if we should drop out
4933 if(count == Multi_create_list_max_display[gr_screen.res]){
4937 // see if we should filter out this mission
4938 if ( !(Multi_create_file_list[idx].flags & Multi_create_filter) ){
4942 // highlight the selected item
4943 multi_create_select_to_filename(Multi_create_list_select,selected_name);
4944 if(!strcmp(selected_name,Multi_create_file_list[idx].filename)){
4945 gr_set_color_fast(&Color_text_selected);
4947 gr_set_color_fast(&Color_text_normal);
4950 // draw the type icon
4951 multi_create_list_blit_icons(idx, y_start);
4953 // force fit the mission name string
4954 strcpy(selected_name,Multi_create_file_list[idx].name);
4955 gr_force_fit_string(selected_name,255,Mc_column1_w[gr_screen.res]);
4956 gr_string(Mc_mission_name_x[gr_screen.res],y_start,selected_name);
4958 // draw the max players if in mission mode
4959 sprintf(selected_name,"%d",(int)Multi_create_file_list[idx].max_players);
4960 gr_string(Mc_mission_count_x[gr_screen.res],y_start,selected_name);
4962 // force fit the mission filename string
4963 strcpy(selected_name,Multi_create_file_list[idx].filename);
4964 gr_force_fit_string(selected_name,255,Mc_column3_w[gr_screen.res]);
4965 gr_string(Mc_mission_fname_x[gr_screen.res],y_start,selected_name);
4972 // takes care of stuff like changing indices around and setting up the netgame structure
4973 void multi_create_list_select_item(int n)
4975 int abs_index,campaign_type,max_players;
4976 char title[NAME_LENGTH+1];
4977 netgame_info ng_temp;
4980 char *campaign_desc;
4982 // if not on the standalone server
4983 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4986 // on the standalone
4988 memset(&ng_temp,0,sizeof(netgame_info));
4992 if ( n != Multi_create_list_select ) {
4993 // check to see if this is a valid index, and bail if it is not
4994 abs_index = multi_create_select_to_index(n);
4995 if(abs_index == -1){
4999 Multi_create_list_select = n;
5001 // set the mission name
5002 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5003 multi_create_select_to_filename(n,ng->mission_name);
5005 multi_create_select_to_filename(n,ng->campaign_name);
5008 // make sure the netgame type is properly set
5009 int old_type = Netgame.type_flags;
5010 abs_index = multi_create_select_to_index(n);
5011 if(abs_index != -1){
5012 if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_TEAMS){
5013 // if we're in squad war mode, leave it as squad war
5014 if(old_type & NG_TYPE_SW){
5015 ng->type_flags = NG_TYPE_SW;
5017 ng->type_flags = NG_TYPE_TVT;
5019 } else if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_COOP){
5020 ng->type_flags = NG_TYPE_COOP;
5021 } else if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_DOGFIGHT){
5022 ng->type_flags = NG_TYPE_DOGFIGHT;
5026 // if we're no longer in a TvT game, just uncheck the squadwar checkbox
5027 if(!(ng->type_flags & NG_TYPE_TEAM)){
5028 Multi_create_sw_checkbox.set_state(0);
5031 // if we switched from something else to team vs. team mode, do some special processing
5032 if((ng->type_flags & NG_TYPE_TEAM) && (ng->type_flags != old_type) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
5036 switch(Multi_create_list_mode){
5037 case MULTI_CREATE_SHOW_MISSIONS:
5038 // don't forget to update the info box window thingie
5039 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5040 ship_init(); // mwa -- 10/15/97. Call this function to reset number of ships in mission
5041 ng->max_players = mission_parse_get_multi_mission_info( ng->mission_name );
5043 Assert(ng->max_players > 0);
5044 strcpy(ng->title,The_mission.name);
5046 // set the information area text
5047 multi_common_set_text(The_mission.mission_desc);
5049 // if we're on the standalone, send a request for the description
5051 send_netgame_descript_packet(&Netgame.server_addr,0);
5052 multi_common_set_text("");
5055 // set the respawns as appropriate
5056 if(Netgame.options.respawn <= Multi_create_file_list[abs_index].respawn){
5057 ng->respawn = Netgame.options.respawn;
5058 nprintf(("Network","Using netgame options for respawn count (%d %d)\n",Netgame.options.respawn,Multi_create_file_list[abs_index].respawn));
5060 ng->respawn = Multi_create_file_list[abs_index].respawn;
5061 nprintf(("Network","Using mission settings for respawn count (%d %d)\n",Netgame.options.respawn,Multi_create_file_list[abs_index].respawn));
5064 case MULTI_CREATE_SHOW_CAMPAIGNS:
5065 // if not on the standalone server
5066 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5067 // get the campaign info
5068 memset(title,0,NAME_LENGTH+1);
5069 if(!mission_campaign_get_info(ng->campaign_name,title,&campaign_type,&max_players, &campaign_desc)) {
5070 memset(ng->campaign_name,0,NAME_LENGTH+1);
5071 ng->max_players = 0;
5073 // if we successfully got the # of players
5075 memset(ng->title,0,NAME_LENGTH+1);
5076 strcpy(ng->title,title);
5077 ng->max_players = max_players;
5080 nprintf(("Network","MC MAX PLAYERS : %d\n",ng->max_players));
5082 // set the information area text
5083 // multi_common_set_text(ng->title);
5084 multi_common_set_text(campaign_desc);
5086 // if on the standalone server, send a request for the description
5088 // no descriptions currently kept for campaigns
5091 // netgame respawns are always 0 for campaigns (until the first mission is loaded)
5096 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5098 send_netgame_update_packet();
5100 // update all machines about stuff like respawns, etc.
5101 multi_options_update_netgame();
5103 multi_options_update_mission(ng, Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ? 1 : 0);
5108 void multi_create_list_blit_icons(int list_index, int y_start)
5110 multi_create_info *mcip;
5111 fs_builtin_mission *fb;
5114 // get a pointer to the list item
5115 max_index = Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ? Multi_create_mission_count - 1 : Multi_create_campaign_count - 1;
5116 if((list_index < 0) || (list_index > max_index)){
5119 mcip = &Multi_create_file_list[list_index];
5121 // blit the multiplayer type icons
5122 if(mcip->flags & MISSION_TYPE_MULTI_COOP){
5123 if(Multi_common_icons[MICON_COOP] >= 0){
5124 gr_set_bitmap(Multi_common_icons[MICON_COOP]);
5125 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5127 } else if(mcip->flags & MISSION_TYPE_MULTI_TEAMS){
5128 if(Multi_common_icons[MICON_TVT] >= 0){
5129 gr_set_bitmap(Multi_common_icons[MICON_TVT]);
5130 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5132 } else if(mcip->flags & MISSION_TYPE_MULTI_DOGFIGHT){
5133 if(Multi_common_icons[MICON_DOGFIGHT] >= 0){
5134 gr_set_bitmap(Multi_common_icons[MICON_DOGFIGHT]);
5135 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5139 // if its a valid mission, blit the valid mission icon
5140 if(MULTI_IS_TRACKER_GAME && (mcip->valid_status == MVALID_STATUS_VALID)){
5141 if(Multi_common_icons[MICON_VALID] >= 0){
5142 gr_set_bitmap(Multi_common_icons[MICON_VALID]);
5143 gr_bitmap(Mc_icon_valid_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_valid_coords[gr_screen.res][MC_Y_COORD]);
5147 // now see if its a builtin mission
5148 fb = game_find_builtin_mission(mcip->filename);
5149 // if the mission is from volition, blit the volition icon
5150 if((fb != NULL) && (fb->flags & FSB_FROM_VOLITION)){
5151 if(Multi_common_icons[MICON_VOLITION] >= 0){
5152 gr_set_bitmap(Multi_common_icons[MICON_VOLITION]);
5153 gr_bitmap(Mc_icon_volition_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_volition_coords[gr_screen.res][MC_Y_COORD]);
5158 void multi_create_accept_hit()
5160 char selected_name[255];
5161 int start_campaign = 0;
5163 // make sure all players have finished joining
5164 if(!multi_netplayer_state_check(NETPLAYER_STATE_JOINED,1)){
5165 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("Please wait until all clients have finished joining",788));
5166 gamesnd_play_iface(SND_GENERAL_FAIL);
5169 gamesnd_play_iface(SND_COMMIT_PRESSED);
5172 // do single mission stuff
5173 switch(Multi_create_list_mode){
5174 case MULTI_CREATE_SHOW_MISSIONS:
5175 if(Multi_create_list_select != -1){
5176 // set the netgame mode
5177 Netgame.campaign_mode = MP_SINGLE;
5179 // setup various filenames and mission names
5180 multi_create_select_to_filename(Multi_create_list_select,selected_name);
5181 strncpy( Game_current_mission_filename, selected_name, MAX_FILENAME_LEN );
5182 strncpy(Netgame.mission_name,selected_name,MAX_FILENAME_LEN);
5185 ml_printf(NOX("Starting single mission %s, with %d players"), Game_current_mission_filename, multi_num_players());
5187 multi_common_add_notify(XSTR("No mission selected!",789));
5192 case MULTI_CREATE_SHOW_CAMPAIGNS:
5193 // do campaign related stuff
5194 if(Multi_create_list_select != -1){
5195 // set the netgame mode
5196 Netgame.campaign_mode = MP_CAMPAIGN;
5198 // start a campaign instead of a single mission
5199 multi_create_select_to_filename(Multi_create_list_select,selected_name);
5200 multi_campaign_start(selected_name);
5204 ml_printf(NOX("Starting campaign %s, with %d players"), selected_name, multi_num_players());
5206 multi_common_add_notify(XSTR("No campaign selected!",790));
5212 // if this is a team vs team situation, lock the players send a final team update
5213 if((Netgame.type_flags & NG_TYPE_TEAM) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
5214 multi_team_host_lock_all();
5215 multi_team_send_update();
5218 // if not on the standalone, move to the mission sync state which will take care of everything
5219 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5220 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
5221 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
5223 // otherwise tell the standalone to do so
5225 // when the standalone receives this, he'll do the mission syncing himself
5226 send_mission_sync_packet(MULTI_SYNC_PRE_BRIEFING,start_campaign);
5230 void multi_create_draw_filter_buttons()
5232 // highlight the correct filter button
5233 if ( Multi_create_filter == MISSION_TYPE_MULTI ){
5234 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL].button.draw_forced(2);
5235 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_COOP ) {
5236 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 1].button.draw_forced(2);
5237 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_TEAMS ) {
5238 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 2].button.draw_forced(2);
5239 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_DOGFIGHT ){
5240 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 3].button.draw_forced(2);
5246 void multi_create_set_selected_team(int team)
5250 // if we don't currently have a player selected, don't do anything
5251 if(!Multi_create_plist_select_flag){
5252 gamesnd_play_iface(SND_GENERAL_FAIL);
5255 gamesnd_play_iface(SND_USER_SELECT);
5257 // otherwise attempt to set the team for this guy
5258 player_index = find_player_id(Multi_create_plist_select_id);
5259 if(player_index != -1){
5260 multi_team_set_team(&Net_players[player_index],team);
5264 void multi_create_handle_join(net_player *pl)
5266 // for now just play a bloop sound
5267 gamesnd_play_iface(SND_ICON_DROP_ON_WING);
5270 // fill in net address of player the mouse is over, return player index (or -1 if none)
5271 short multi_create_get_mouse_id()
5273 // determine where he clicked (y pixel value)
5275 Multi_create_player_select_button.get_mouse_pos(NULL,&y);
5277 // select things a little differently if we're in team vs. team or non-team vs. team mode
5279 if(Netgame.type_flags & NG_TYPE_TEAM){
5280 int player_index = -1;
5282 // look through all of team red first
5283 for(idx=0;idx<MAX_PLAYERS;idx++){
5284 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
5287 // if this is the _nth_ guy
5295 // if we still haven't found him yet, look through the green team
5296 if(player_index == -1){
5297 for(idx=0;idx<MAX_PLAYERS;idx++){
5298 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
5300 // if this is the _nth_ guy
5309 if(player_index != -1){
5310 return Net_players[player_index].player_id;
5313 // select the nth active player if possible, disregarding the standalone server
5314 for(idx=0;idx<MAX_PLAYERS;idx++){
5315 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
5318 // if this is the _nth_ guy
5320 return Net_players[idx].player_id;
5329 void multi_create_select_to_filename(int select_index,char *filename)
5333 // look through the mission list
5334 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5335 for(idx=0;idx<Multi_create_mission_count;idx++){
5336 if(Multi_create_file_list[idx].flags & Multi_create_filter){
5340 // if we found the item
5341 if(select_index < 0){
5342 strcpy(filename,Multi_create_file_list[idx].filename);
5347 // look through the campaign list
5348 else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
5349 for(idx=0;idx<Multi_create_campaign_count;idx++){
5352 // if we found the item
5353 if(select_index < 0){
5354 strcpy(filename,Multi_create_file_list[idx].filename);
5360 strcpy(filename,"");
5363 int multi_create_select_to_index(int select_index)
5366 int lookup_index = 0;
5368 // look through the mission list
5369 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5370 for(idx=0;idx<Multi_create_mission_count;idx++){
5371 if(Multi_create_file_list[idx].flags & Multi_create_filter){
5375 // if we found the item
5376 if(select_index < lookup_index){
5381 // look through the campaign list
5382 else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
5383 for(idx=0;idx<Multi_create_campaign_count;idx++){
5386 // if we found the item
5387 if(select_index < 0){
5396 int multi_create_ok_to_commit()
5398 int player_count, observer_count, idx;
5399 int notify_of_hacked_ships_tbl = 0;
5400 int notify_of_hacked_weapons_tbl = 0;
5401 char err_string[255];
5405 // make sure we have a valid mission selected
5406 if(Multi_create_list_select < 0){
5410 // if this is not a valid mission, let the player know
5411 abs_index = multi_create_select_to_index(Multi_create_list_select);
5416 // if we're playing with a hacked ships.tbl (on PXO)
5417 notify_of_hacked_ships_tbl = 0;
5418 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5419 if(!Game_ships_tbl_valid){
5420 notify_of_hacked_ships_tbl = 1;
5423 if(Netgame.flags & NG_FLAG_HACKED_SHIPS_TBL){
5424 notify_of_hacked_ships_tbl = 1;
5427 if(!MULTI_IS_TRACKER_GAME){
5428 notify_of_hacked_ships_tbl = 0;
5430 if(notify_of_hacked_ships_tbl){
5431 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){
5436 // if we're playing with a hacked weapons.tbl (on PXO)
5437 notify_of_hacked_weapons_tbl = 0;
5438 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5439 if(!Game_weapons_tbl_valid){
5440 notify_of_hacked_weapons_tbl = 1;
5443 if(Netgame.flags & NG_FLAG_HACKED_WEAPONS_TBL){
5444 notify_of_hacked_weapons_tbl = 1;
5447 if(!MULTI_IS_TRACKER_GAME){
5448 notify_of_hacked_weapons_tbl = 0;
5450 if(notify_of_hacked_weapons_tbl){
5451 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){
5456 // if any of the players have hacked data
5458 for(idx=0; idx<MAX_PLAYERS; idx++){
5459 // look for hacked players
5460 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAXOR)){
5464 // message everyone - haha
5465 if(Net_players[idx].player != NULL){
5466 sprintf(err_string, "%s %s", Net_players[idx].player->callsign, XSTR("has hacked tables/data", 1271));
5468 sprintf(err_string, "somebody %s", XSTR("has hacked tables/data", 1271));
5470 send_game_chat_packet(Net_player, err_string, MULTI_MSG_ALL, NULL, NULL, 1);
5473 // if we found a hacked set of data
5476 if(MULTI_IS_TRACKER_GAME){
5477 // don't allow squad war matches to continue
5478 if(Netgame.type_flags & NG_TYPE_SW){
5480 // if this is squad war, don't allow it to continue
5481 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));
5486 // otherwise, warn the players that stats will not saved
5488 // if this is squad war, don't allow it to continue
5489 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){
5494 // non-pxo, just give a notice
5496 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){
5502 // check to see that we don't have too many observers
5503 observer_count = multi_num_observers();
5504 if(observer_count > Netgame.options.max_observers){
5505 // print up the error string
5506 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);
5508 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, err_string);
5512 // check to see that we have a valid # of players for the the # of ships in the game
5513 player_count = multi_num_players();
5514 if(player_count > Netgame.max_players){
5515 // print up the error string
5516 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);
5518 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, err_string);
5522 // check to see if teams are assigned properly in a team vs. team situation
5523 if(Netgame.type_flags & NG_TYPE_TEAM){
5524 if(!multi_team_ok_to_commit()){
5525 gamesnd_play_iface(SND_GENERAL_FAIL);
5526 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("Teams and/or team captains are not assigned properly", 793));
5532 if(!multi_create_verify_cds()){
5533 gamesnd_play_iface(SND_GENERAL_FAIL);
5535 #ifdef MULTIPLAYER_BETA_BUILD
5536 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, "You need 1 CD for every player!");
5538 #ifdef DVD_MESSAGE_HACK
5539 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You need 1 DVD for every 4 players!", 794));
5541 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You need 1 CD for every 4 players!", 794));
5547 // if we're playing on the tracker
5548 if(MULTI_IS_TRACKER_GAME){
5549 #ifdef PXO_CHECK_VALID_MISSIONS
5550 if((Multi_create_file_list == Multi_create_mission_list) && (Multi_create_file_list[abs_index].valid_status != MVALID_STATUS_VALID)){
5551 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){
5558 if(!(Netgame.type_flags & NG_TYPE_SW)){
5559 // if he is playing by himself, tell him stats will not be accepted
5560 if(multi_num_players() == 1){
5561 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){
5574 int multi_create_verify_cds()
5576 int player_count = multi_num_players();
5580 // count how many cds we have
5582 for(idx=0;idx<MAX_PLAYERS;idx++){
5583 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAS_CD)){
5588 // for the beta, everyone must have a CD
5589 #ifdef MULTIPLAYER_BETA_BUILD
5590 if(multi_cd_count < player_count){
5594 // determine if we have enough
5595 float ratio = (float)player_count / (float)multi_cd_count;
5596 // greater than a 4 to 1 ratio
5602 // we meet the conditions
5606 // returns an index into Multi_create_mission_list
5607 int multi_create_lookup_mission(char *fname)
5611 for(idx=0; idx<Multi_create_mission_count; idx++){
5612 if(!stricmp(fname, Multi_create_mission_list[idx].filename)){
5617 // couldn't find the mission
5621 // returns an index into Multi_create_campaign_list
5622 int multi_create_lookup_campaign(char *fname)
5626 for(idx=0; idx<Multi_create_campaign_count; idx++){
5627 if(!stricmp(fname, Multi_create_campaign_list[idx].filename)){
5632 // couldn't find the campaign
5636 void multi_create_refresh_pxo()
5638 // delete mvalid.cfg if it exists
5639 cf_delete(MULTI_VALID_MISSION_FILE, CF_TYPE_DATA);
5641 // refresh missions from the tracker
5642 multi_update_valid_missions();
5645 void multi_create_sw_clicked()
5647 netgame_info ng_temp;
5650 int file_index = multi_create_select_to_index(Multi_create_list_select);
5652 // either a temporary netgame or the real one
5653 if(MULTIPLAYER_MASTER){
5660 // maybe switch squad war off
5661 if(!Multi_create_sw_checkbox.checked()){
5662 // if the mission selected is a coop mission, go back to coop mode
5663 Assert(file_index != -1);
5664 if(file_index == -1){
5665 ng->type_flags = NG_TYPE_COOP;
5667 if(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_TEAMS){
5668 ng->type_flags = NG_TYPE_TVT;
5669 } else if(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_DOGFIGHT){
5670 ng->type_flags = NG_TYPE_DOGFIGHT;
5672 ng->type_flags = NG_TYPE_COOP;
5675 // switch squad war on
5677 Assert(file_index != -1);
5678 if((file_index == -1) || !(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_TEAMS)){
5679 Multi_create_sw_checkbox.set_state(0);
5681 // at this point we know its safe to switch squad war on
5682 ng->type_flags = NG_TYPE_SW;
5687 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5689 send_netgame_update_packet();
5691 // update all machines about stuff like respawns, etc.
5692 multi_options_update_netgame();
5694 // on the standalone
5696 // standalone will take care of polling the usertracker
5697 multi_options_update_mission(ng, Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ? 1 : 0);
5702 // -------------------------------------------------------------------------------------------------------------
5704 // MULTIPLAYER HOST OPTIONS SCREEN
5707 #define MULTI_HO_NUM_BUTTONS 12
5708 #define MULTI_HO_NUM_RADIO_BUTTONS 10
5712 #define MULTI_HO_PALETTE "InterfacePalette"
5714 static char *Multi_ho_bitmap_fname[GR_NUM_RESOLUTIONS] = {
5715 "MultiHost", // GR_640
5716 "2_MultiHost" // GR_1024
5719 static char *Multi_ho_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
5720 "MultiHost-M", // GR_640
5721 "2_MultiHost-M" // GR_1024
5725 UI_WINDOW Multi_ho_window; // the window object for the join screen
5726 UI_INPUTBOX Multi_ho_respawns; // the # of respawns allowed in the game
5727 UI_INPUTBOX Multi_ho_time_limit; // mission time limit
5728 UI_INPUTBOX Multi_ho_voice_wait; // wait time between tokens
5729 UI_INPUTBOX Multi_ho_kill_limit; // kill limit in a furball mission
5730 UI_INPUTBOX Multi_ho_obs; // # of observers we'll allow
5731 int Multi_ho_bitmap; // the background bitmap
5733 // constants for coordinate lookup
5734 #define MULTI_HO_X_COORD 0
5735 #define MULTI_HO_Y_COORD 1
5736 #define MULTI_HO_W_COORD 2
5737 #define MULTI_HO_H_COORD 3
5738 #define MULTI_HO_TEXT_X_COORD 4
5739 #define MULTI_HO_TEXT_Y_COORD 5
5742 #define MULTI_HO_MSG_RANK 0 // highest ranking players can do messaging
5743 #define MULTI_HO_MSG_LEADER 1 // wing/team leaders can do messaging
5744 #define MULTI_HO_MSG_ANY 2 // any player can do messaging
5745 #define MULTI_HO_MSG_HOST 3 // only the host can do messaging
5746 #define MULTI_HO_END_RANK 4 // highest rank can and host can end mission
5747 #define MULTI_HO_END_LEADER 5 // wing/team leaders and host can end the mission
5748 #define MULTI_HO_END_ANY 6 // any player can end the mission
5749 #define MULTI_HO_END_HOST 7 // only host can end the mission
5750 #define MULTI_HO_VOICE_ON 8 // voice toggled on
5751 #define MULTI_HO_VOICE_OFF 9 // voice toggled off
5752 #define MULTI_HO_HOST_MODIFIES 10 // only the host or team captains can modify ships/weapons in briefing
5753 #define MULTI_HO_ACCEPT 11 // accept button
5755 ui_button_info Multi_ho_buttons[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_BUTTONS] = {
5757 // who is allowed to message
5758 ui_button_info("MH_00", 3, 160, 46, 166, 0), // highest rank
5759 ui_button_info("MH_01", 3, 179, 46, 185, 1), // team/wing leader
5760 ui_button_info("MH_02", 3, 196, 46, 203, 2), // any
5761 ui_button_info("MH_03", 3, 214, 46, 220, 3), // host
5763 // who is allowed to end the mission
5764 ui_button_info("MH_04", 3, 257, 46, 265, 4), // highest rank
5765 ui_button_info("MH_05", 3, 276, 46, 283, 5), // team/wing leader
5766 ui_button_info("MH_06", 3, 294, 46, 300, 6), // any
5767 ui_button_info("MH_07", 3, 311, 46, 317, 7), // host
5769 // voice on/off button
5770 ui_button_info("MH_09", 542, 158, 545, 185, 9),
5771 ui_button_info("MH_10", 598, 158, 604, 185, 10),
5773 // host modifies ships
5774 ui_button_info("MH_13", 542, 377, 437, 363, 13),
5777 ui_button_info("MH_14", 572, 428, 580, 414, 14),
5780 // who is allowed to message
5781 ui_button_info("2_MH_00", 5, 256, 73, 269, 0), // highest rank
5782 ui_button_info("2_MH_01", 5, 286, 73, 297, 1), // team/wing leader
5783 ui_button_info("2_MH_02", 5, 314, 73, 325, 2), // any
5784 ui_button_info("2_MH_03", 5, 341, 73, 352, 3), // host
5786 // who is allowed to end the mission
5787 ui_button_info("2_MH_04", 5, 412, 73, 425, 4), // highest rank
5788 ui_button_info("2_MH_05", 5, 442, 73, 452, 5), // team/wing leader
5789 ui_button_info("2_MH_06", 5, 470, 73, 480, 6), // any
5790 ui_button_info("2_MH_07", 5, 497, 73, 508, 7), // host
5792 // voice on/off button
5793 ui_button_info("2_MH_09", 867, 253, 872, 296, 9),
5794 ui_button_info("2_MH_10", 957, 253, 966, 296, 10),
5796 // host modifies ships
5797 ui_button_info("2_MH_13", 867, 603, 784, 581, 13),
5800 ui_button_info("2_MH_14", 916, 685, 925, 665, 14),
5803 UI_XSTR Multi_ho_text[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_BUTTONS] = {
5805 {"Highest rank", 1280, 46, 166, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_RANK].button},
5806 {"Team / wing-leader", 1281, 46, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_LEADER].button},
5807 {"Any", 1282, 46, 203, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_ANY].button},
5808 {"Host", 1283, 46, 220, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_HOST].button},
5809 {"Highest rank", 1280, 46, 265, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_RANK].button},
5810 {"Team / wing-leader", 1281, 46, 283, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_LEADER].button},
5811 {"Any", 1282, 46, 300, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_ANY].button},
5812 {"Host", 1283, 46, 317, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_HOST].button},
5813 {"On", 1285, 545, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_VOICE_ON].button},
5814 {"Off", 1286, 604, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_VOICE_OFF].button},
5815 {"Host modifies ships", 1287, 437, 363, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_HOST_MODIFIES].button},
5816 {"Exit", 1417, 572, 418, UI_XSTR_COLOR_PINK, -1, &Multi_ho_buttons[0][MULTI_HO_ACCEPT].button},
5819 {"Highest rank", 1280, 62, 269, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_RANK].button},
5820 {"Team / wing-leader", 1281, 62, 297, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_LEADER].button},
5821 {"Any", 1282, 62, 325, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_ANY].button},
5822 {"Host", 1283, 62, 352, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_HOST].button},
5823 {"Highest rank", 1280, 62, 425, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_RANK].button},
5824 {"Team / wing-leader", 1281, 62, 452, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_LEADER].button},
5825 {"Any", 1282, 62, 480, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_ANY].button},
5826 {"Host", 1283, 62, 508, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_HOST].button},
5827 {"On", 1285, 877, 294, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_VOICE_ON].button},
5828 {"Off", 1286, 967, 293, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_VOICE_OFF].button},
5829 {"Host modifies ships", 1287, 869, 589, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_HOST_MODIFIES].button},
5830 {"Exit", 1417, 953, 672, UI_XSTR_COLOR_PINK, -1, &Multi_ho_buttons[1][MULTI_HO_ACCEPT].button},
5834 // radio button controls
5835 #define MULTI_HO_NUM_RADIO_GROUPS 3
5836 #define MULTI_HO_MSG_GROUP 0 // group dealing with squadmate messaging
5837 #define MULTI_HO_END_GROUP 1 // group dealing with ending the mission
5838 #define MULTI_HO_VOICE_GROUP 2 // group dealing with voice stuff
5839 int Multi_ho_radio_groups[MULTI_HO_NUM_RADIO_GROUPS] = { // currently selected button in the radio button group
5842 int Multi_ho_radio_info[MULTI_HO_NUM_RADIO_BUTTONS][3] = { // info related to each of the radio buttons themselves
5843 // { group #, value, button id# }
5844 {0, 0, 0}, // highest ranking players can do messaging
5845 {0, 1, 1}, // wing/team leaders can do messaging
5846 {0, 2, 2}, // any player can do messaging
5847 {0, 3, 3}, // only host can do messaging
5848 {1, 0, 4}, // highest rank and host can end the mission
5849 {1, 1, 5}, // team/wing leader can end the mission
5850 {1, 2, 6}, // any player can end the mission
5851 {1, 3, 7}, // only the host can end the mission
5852 {2, 0, 8}, // voice toggled on
5853 {2, 1, 9} // voice toggled off
5857 #define MULTI_HO_NUM_SLIDERS 3
5858 #define MULTI_HO_SLIDER_VOICE_QOS 0 // voice quality of sound
5859 #define MULTI_HO_SLIDER_VOICE_DUR 1 // max duration of voice recording
5860 #define MULTI_HO_SLIDER_SKILL 2 // skill level
5867 UI_DOT_SLIDER_NEW slider; // because we have a class inside this struct, we need the constructor below..
5869 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){}
5871 ho_sliders Multi_ho_sliders[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_SLIDERS] = {
5873 ho_sliders("MH_11", 428, 214, 437, 199, 11, 19, 10), // voice qos
5874 ho_sliders("MH_12", 428, 261, 437, 246, 12, 19, 10), // voice duration
5875 ho_sliders("MH_08", 237, 454, 230, 411, 8, 36, 5), // skill level
5878 ho_sliders("2_MH_11", 684, 343, 690, 323, 11, 32, 10), // voice qos
5879 ho_sliders("2_MH_12", 685, 418, 837, 468, 12, 32, 10), // voice duration
5880 ho_sliders("2_MH_08", 379, 727, 369, 663, 8, 60, 5), // skill level
5884 int Multi_ho_mission_respawn;
5886 int Multi_ho_host_modifies;
5888 // whether or not any of the inputboxes on this screen had focus last frame
5889 int Multi_ho_lastframe_input = 0;
5891 // game information text areas
5894 #define MULTI_HO_NUM_TITLES 14
5895 UI_XSTR Multi_ho_titles[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_TITLES] = {
5897 { "AI Orders", 1289, 32, 144, UI_XSTR_COLOR_GREEN, -1, NULL },
5898 { "End Mission", 1290, 32, 242, UI_XSTR_COLOR_GREEN, -1, NULL },
5899 { "Time Limit", 1291, 32, 347, UI_XSTR_COLOR_GREEN, -1, NULL },
5900 { "Min", 1292, 74, 362, UI_XSTR_COLOR_GREEN, -1, NULL },
5901 { "Respawn Limit", 1288, 32, 378, UI_XSTR_COLOR_GREEN, -1, NULL },
5902 { "Kill Limit", 1293, 32, 409, UI_XSTR_COLOR_GREEN, -1, NULL },
5903 { "Observers", 1294, 32, 441, UI_XSTR_COLOR_GREEN, -1, NULL },
5904 { "Skill Level", 1284, 230, 411, UI_XSTR_COLOR_GREEN, -1, NULL },
5905 { "Voice Transmission", 1295, 437, 144, UI_XSTR_COLOR_GREEN, -1, NULL },
5906 { "Voice Quality", 1296, 437, 199, UI_XSTR_COLOR_GREEN, -1, NULL },
5907 { "Message Duration", 1297, 437, 246, UI_XSTR_COLOR_GREEN, -1, NULL },
5908 { "sec", 1522, 523, 292, UI_XSTR_COLOR_GREEN, -1, NULL },
5909 { "sec", 1523, 523, 332, UI_XSTR_COLOR_GREEN, -1, NULL },
5910 { "Voice Wait", 1298, 437, 313, UI_XSTR_COLOR_GREEN, -1, NULL },
5913 { "AI Orders", 1289, 48, 238, UI_XSTR_COLOR_GREEN, -1, NULL },
5914 { "End Mission", 1290, 48, 394, UI_XSTR_COLOR_GREEN, -1, NULL },
5915 { "Time Limit", 1291, 50, 568, UI_XSTR_COLOR_GREEN, -1, NULL },
5916 { "Min", 1292, 119, 581, UI_XSTR_COLOR_GREEN, -1, NULL },
5917 { "Respawn Limit", 1288, 50, 618, UI_XSTR_COLOR_GREEN, -1, NULL },
5918 { "Kill Limit", 1293, 50, 668, UI_XSTR_COLOR_GREEN, -1, NULL },
5919 { "Observers", 1294, 50, 718, UI_XSTR_COLOR_GREEN, -1, NULL },
5920 { "Skill Level", 1284, 398, 670, UI_XSTR_COLOR_GREEN, -1, NULL },
5921 { "Voice Transmission", 1295, 869, 239, UI_XSTR_COLOR_GREEN, -1, NULL },
5922 { "Voice Quality", 1296, 690, 331, UI_XSTR_COLOR_GREEN, -1, NULL },
5923 { "Message Duration", 1297, 690, 405, UI_XSTR_COLOR_GREEN, -1, NULL },
5924 { "sec", 1522, 837, 467, UI_XSTR_COLOR_GREEN, -1, NULL },
5925 { "sec", 1523, 837, 534, UI_XSTR_COLOR_GREEN, -1, NULL },
5926 { "Voice Wait", 1298, 742, 510, UI_XSTR_COLOR_GREEN, -1, NULL },
5930 // mission time limit input box
5931 int Ho_time_coords[GR_NUM_RESOLUTIONS][4] = {
5940 // furball kill limit input box
5941 int Ho_kill_coords[GR_NUM_RESOLUTIONS][4] = {
5950 // voice recording duration text display area
5951 int Ho_vd_coords[GR_NUM_RESOLUTIONS][4] = {
5960 // voice token wait input box
5961 int Ho_vw_coords[GR_NUM_RESOLUTIONS][6] = {
5970 // observer count input box
5971 int Ho_obs_coords[GR_NUM_RESOLUTIONS][4] = {
5980 // skill text description area
5981 int Ho_st_coords[GR_NUM_RESOLUTIONS][4] = {
5990 // respawn input box
5991 int Ho_rsp_coords[GR_NUM_RESOLUTIONS][6] = {
6000 // respawn max text area
6001 int Ho_max_rsp_coords[GR_NUM_RESOLUTIONS][2] = {
6010 // maximum values for various input boxes (to notify user of overruns)
6011 #define MULTI_HO_MAX_TIME_LIMIT 500
6012 #define MULTI_HO_MAX_TOKEN_WAIT 5
6013 #define MULTI_HO_MAX_KILL_LIMIT 9999
6014 #define MULTI_HO_MAX_OBS 4
6016 // LOCAL function definitions
6017 void multi_ho_check_buttons();
6018 void multi_ho_button_pressed(int n);
6019 void multi_ho_draw_radio_groups();
6020 void multi_ho_accept_hit();
6021 void multi_ho_get_options();
6022 void multi_ho_apply_options();
6023 void multi_ho_display_record_time();
6024 int multi_ho_check_values();
6025 void multi_ho_check_focus();
6026 void multi_ho_blit_max_respawns();
6027 void multi_ho_display_skill_level();
6029 void multi_host_options_init()
6033 // create the interface window
6034 Multi_ho_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
6035 Multi_ho_window.set_mask_bmap(Multi_ho_bitmap_mask_fname[gr_screen.res]);
6037 // load the background bitmap
6038 Multi_ho_bitmap = bm_load(Multi_ho_bitmap_fname[gr_screen.res]);
6039 if(Multi_ho_bitmap < 0){
6040 // we failed to load the bitmap - this is very bad
6044 // initialize the common notification messaging
6045 multi_common_notify_init();
6047 // use the common interface palette
6048 multi_common_set_palette();
6050 // create the interface buttons
6051 for(idx=0;idx<MULTI_HO_NUM_BUTTONS;idx++){
6052 // create the object
6053 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);
6055 // set the sound to play when highlighted
6056 Multi_ho_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
6058 // set the ani for the button
6059 Multi_ho_buttons[gr_screen.res][idx].button.set_bmaps(Multi_ho_buttons[gr_screen.res][idx].filename);
6061 // set the hotspot, ignoring the skill level button
6062 Multi_ho_buttons[gr_screen.res][idx].button.link_hotspot(Multi_ho_buttons[gr_screen.res][idx].hotspot);
6065 Multi_ho_window.add_XSTR(&Multi_ho_text[gr_screen.res][idx]);
6069 for(idx=0; idx<MULTI_HO_NUM_TITLES; idx++){
6070 Multi_ho_window.add_XSTR(&Multi_ho_titles[gr_screen.res][idx]);
6073 // create the interface sliders
6074 for(idx=0; idx<MULTI_HO_NUM_SLIDERS; idx++){
6075 // create the object
6076 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);
6079 // create the respawn count input box
6080 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);
6081 // if we're in campaign mode, disable it
6082 if(Netgame.campaign_mode == MP_CAMPAIGN){
6083 Multi_ho_respawns.set_text(XSTR("NA",795)); // [[ Not applicable ]]
6084 Multi_ho_respawns.disable();
6086 Multi_ho_respawns.set_valid_chars("0123456789");
6089 // create the time limit input box
6090 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);
6091 Multi_ho_time_limit.set_valid_chars("-0123456789");
6093 // create the voice token wait input box
6094 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);
6095 Multi_ho_voice_wait.set_valid_chars("01243456789");
6097 // create the furball kill limit input box
6098 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);
6099 Multi_ho_kill_limit.set_valid_chars("0123456789");
6101 // create the observer limit input box
6102 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);
6103 Multi_ho_obs.set_valid_chars("01234");
6105 // load in the current netgame defaults
6106 multi_ho_get_options();
6108 // whether or not any of the inputboxes on this screen had focus last frame
6109 Multi_ho_lastframe_input = 0;
6111 // get the # of respawns for the currently selected mission (if any)
6112 if(Multi_create_list_select != -1){
6113 int abs_index = multi_create_select_to_index(Multi_create_list_select);
6115 // if he has a valid mission selected
6117 Multi_ho_mission_respawn = (int)Multi_create_file_list[abs_index].respawn;
6119 Multi_ho_mission_respawn = -1;
6122 Multi_ho_mission_respawn = -1;
6126 void multi_ho_update_sliders()
6128 // game skill slider
6129 if (Game_skill_level != Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos) {
6130 if ( !(Netgame.type_flags & NG_TYPE_TEAM) ){
6131 Game_skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6132 gamesnd_play_iface(SND_USER_SELECT);
6134 Game_skill_level = NUM_SKILL_LEVELS / 2;
6138 // get the voice qos options
6139 if (Netgame.options.voice_qos != (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1)) {
6140 Netgame.options.voice_qos = (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1);
6141 gamesnd_play_iface(SND_USER_SELECT);
6144 // get the voice duration options
6145 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)) {
6146 Netgame.options.voice_record_time = (int)(0.5f * (float)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 1000.0f);
6147 gamesnd_play_iface(SND_USER_SELECT);
6152 void multi_host_options_do()
6157 k = Multi_ho_window.process();
6160 // process any keypresses
6163 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
6166 case KEY_CTRLED + KEY_ENTER :
6167 gamesnd_play_iface(SND_COMMIT_PRESSED);
6168 multi_ho_accept_hit();
6172 // process any button clicks
6173 multi_ho_check_buttons();
6175 // update the sliders
6176 multi_ho_update_sliders();
6178 // make sure that the chatbox inputbox and any inputbox on this screen are mutually exclusive in terms of focus
6179 multi_ho_check_focus();
6181 // draw the background, etc
6183 GR_MAYBE_CLEAR_RES(Multi_ho_bitmap);
6184 if(Multi_ho_bitmap != -1){
6185 gr_set_bitmap(Multi_ho_bitmap);
6188 Multi_ho_window.draw();
6190 // draw all the radio buttons properly
6191 multi_ho_draw_radio_groups();
6193 // display any pending notification messages
6194 multi_common_notify_do();
6196 // display the voice record time settings
6197 multi_ho_display_record_time();
6199 // maybe display the max # of respawns next to the respawn input box
6200 multi_ho_blit_max_respawns();
6202 // blit the proper skill level
6203 multi_ho_display_skill_level();
6205 // blit the "host modifies button"
6206 if(Multi_ho_host_modifies){
6207 Multi_ho_buttons[gr_screen.res][MULTI_HO_HOST_MODIFIES].button.draw_forced(2);
6210 // process and show the chatbox thingie
6214 Multi_ho_window.draw_tooltip();
6216 // display the voice status indicator
6217 multi_common_voice_display_status();
6223 void multi_host_options_close()
6225 // unload any bitmaps
6226 if(!bm_unload(Multi_ho_bitmap)){
6227 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_ho_bitmap_fname[gr_screen.res]));
6230 // destroy the UI_WINDOW
6231 Multi_ho_window.destroy();
6234 void multi_ho_check_buttons()
6237 for(idx=0;idx<MULTI_HO_NUM_BUTTONS;idx++){
6238 // we only really need to check for one button pressed at a time, so we can break after
6240 if(Multi_ho_buttons[gr_screen.res][idx].button.pressed()){
6241 multi_ho_button_pressed(idx);
6247 void multi_ho_button_pressed(int n)
6249 int radio_index,idx;
6250 int x_pixel,y_pixel;
6252 // get the pixel position of the click
6253 Multi_ho_buttons[gr_screen.res][n].button.get_mouse_pos(&x_pixel,&y_pixel);
6256 // clicked on the accept button
6257 case MULTI_HO_ACCEPT:
6258 gamesnd_play_iface(SND_COMMIT_PRESSED);
6259 multi_ho_accept_hit();
6262 // clicked on the host/captains only modify button
6263 case MULTI_HO_HOST_MODIFIES:
6264 // toggle it on or off
6265 Multi_ho_host_modifies = !Multi_ho_host_modifies;
6266 gamesnd_play_iface(SND_USER_SELECT);
6270 // look through the radio buttons and see which one this corresponds to
6272 for(idx=0;idx<MULTI_HO_NUM_RADIO_BUTTONS;idx++){
6273 if(Multi_ho_radio_info[idx][2] == n){
6278 Assert(radio_index != -1);
6280 // check to see if a radio button was pressed
6281 if(radio_index < MULTI_HO_NUM_RADIO_BUTTONS){
6282 // see if this value is already picked for this radio group
6283 if(Multi_ho_radio_groups[Multi_ho_radio_info[radio_index][0]] != Multi_ho_radio_info[radio_index][1]){
6284 gamesnd_play_iface(SND_USER_SELECT);
6285 Multi_ho_radio_groups[Multi_ho_radio_info[radio_index][0]] = Multi_ho_radio_info[radio_index][1];
6287 gamesnd_play_iface(SND_GENERAL_FAIL);
6292 void multi_ho_draw_radio_groups()
6296 // go through each item and draw it if it is the selected button in its respective group
6297 for(idx=0;idx<MULTI_HO_NUM_RADIO_BUTTONS;idx++){
6298 /// if this button is the currently selected one in its group
6299 if(Multi_ho_radio_info[idx][1] == Multi_ho_radio_groups[Multi_ho_radio_info[idx][0]]){
6300 Multi_ho_buttons[gr_screen.res][Multi_ho_radio_info[idx][2]].button.draw_forced(2);
6305 void multi_ho_accept_hit()
6309 // check the values in the input boxes
6310 if(!multi_ho_check_values()){
6314 // zero out the netgame flags
6317 // set default options
6318 Netgame.options.flags = (MSO_FLAG_INGAME_XFER | MSO_FLAG_ACCEPT_PIX);
6320 // set the squadmate messaging flags
6321 switch(Multi_ho_radio_groups[MULTI_HO_MSG_GROUP]){
6323 Netgame.options.squad_set = MSO_SQUAD_RANK;
6326 Netgame.options.squad_set = MSO_SQUAD_LEADER;
6329 Netgame.options.squad_set = MSO_SQUAD_ANY;
6332 Netgame.options.squad_set = MSO_SQUAD_HOST;
6338 // set the end mission flags
6339 switch(Multi_ho_radio_groups[MULTI_HO_END_GROUP]){
6341 Netgame.options.endgame_set = MSO_END_RANK;
6344 Netgame.options.endgame_set = MSO_END_LEADER;
6347 Netgame.options.endgame_set = MSO_END_ANY;
6350 Netgame.options.endgame_set = MSO_END_HOST;
6356 // set the voice toggle
6357 switch(Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP]){
6359 Netgame.options.flags &= ~(MSO_FLAG_NO_VOICE);
6362 Netgame.options.flags |= MSO_FLAG_NO_VOICE;
6368 // get the voice qos options
6369 Netgame.options.voice_qos = (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1);
6371 // get the voice duration options
6372 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);
6374 // set the skill level. If in team vs. team mode, preserve the old setting before saving
6375 // the pilot file. I'll bet that this doesn't work though because the pilot file gets
6376 // written in a bunch of locations....sigh.
6377 if ( !(Netgame.type_flags & NG_TYPE_TEAM) ){
6378 Game_skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6380 Game_skill_level = NUM_SKILL_LEVELS / 2;
6383 // set the netgame respawn count
6384 // maybe warn the user that respawns will not be used for a campaign mission
6385 if(Netgame.campaign_mode == MP_SINGLE){
6386 Multi_ho_respawns.get_text(resp_str);
6387 uint temp_respawn = (uint)atoi(resp_str);
6388 // if he currently has no mission selected, let the user set any # of respawns
6389 if((int)temp_respawn > Multi_ho_mission_respawn){
6390 if(Multi_ho_mission_respawn == -1){
6391 Netgame.respawn = temp_respawn;
6392 Netgame.options.respawn = temp_respawn;
6394 // this should have been taken care of by the interface code
6399 Netgame.options.respawn = temp_respawn;
6400 Netgame.respawn = temp_respawn;
6404 // get the mission time limit
6405 Multi_ho_time_limit.get_text(resp_str);
6406 int temp_time = atoi(resp_str);
6408 Netgame.options.mission_time_limit = fl2f(-1.0f);
6409 } else if(temp_time > MULTI_HO_MAX_TIME_LIMIT){
6412 Netgame.options.mission_time_limit = fl2f(60.0f * (float)temp_time);
6415 // get observer count options
6416 Multi_ho_obs.get_text(resp_str);
6417 int temp_obs = atoi(resp_str);
6418 if(temp_obs > MULTI_HO_MAX_OBS){
6421 Netgame.options.max_observers = (ubyte)temp_obs;
6423 // get the furball kill limit
6424 Multi_ho_kill_limit.get_text(resp_str);
6425 int temp_kills = atoi(resp_str);
6426 if(temp_kills > MULTI_HO_MAX_KILL_LIMIT){
6429 Netgame.options.kill_limit = temp_kills;
6431 // get the token wait limit
6432 Multi_ho_voice_wait.get_text(resp_str);
6433 int temp_wait = atoi(resp_str);
6434 if(temp_wait > MULTI_HO_MAX_TOKEN_WAIT){
6437 Netgame.options.voice_token_wait = (temp_wait * 1000);
6439 // set the netgame option
6440 Netgame.options.skill_level = (ubyte)Game_skill_level;
6442 // get whether we're in host/captains only modify mode
6443 Netgame.options.flags &= ~(MSO_FLAG_SS_LEADERS);
6444 if(Multi_ho_host_modifies){
6445 Netgame.options.flags |= MSO_FLAG_SS_LEADERS;
6448 // store these values locally
6449 memcpy(&Player->m_local_options,&Net_player->p_info.options,sizeof(multi_local_options));
6450 memcpy(&Player->m_server_options,&Netgame.options,sizeof(multi_server_options));
6451 write_pilot_file(Player);
6453 // apply any changes in settings (notify everyone of voice qos changes, etc)
6454 multi_ho_apply_options();
6456 // move back to the create game screen
6457 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
6460 void multi_ho_get_options()
6464 // set the squadmate messaging buttons
6465 switch(Netgame.options.squad_set){
6466 case MSO_SQUAD_RANK :
6467 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 0;
6469 case MSO_SQUAD_LEADER:
6470 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 1;
6473 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 2;
6475 case MSO_SQUAD_HOST:
6476 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 3;
6482 // set the mission end buttons
6483 switch(Netgame.options.endgame_set){
6485 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 0;
6487 case MSO_END_LEADER:
6488 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 1;
6491 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 2;
6494 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 3;
6500 // set the voice toggle buttons
6501 if(Netgame.options.flags & MSO_FLAG_NO_VOICE){
6502 Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP] = 1;
6504 Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP] = 0;
6507 // get the voice qos options
6508 Assert((Netgame.options.voice_qos >= 1) && (Netgame.options.voice_qos <= 10));
6509 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos = (Netgame.options.voice_qos - 1);
6511 // get the voice duration options
6512 Assert((Netgame.options.voice_record_time > 0) && (Netgame.options.voice_record_time <= MULTI_VOICE_MAX_TIME));
6513 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos = ((int)((float)Netgame.options.voice_record_time / 500.0f)) - 1;
6515 // get the current skill level
6516 Assert((Game_skill_level >= 0) && (Game_skill_level < NUM_SKILL_LEVELS));
6517 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos = Game_skill_level;
6519 // get the # of observers
6520 memset(resp_str,0,10);
6521 sprintf(resp_str,"%d",Netgame.options.max_observers);
6522 Multi_ho_obs.set_text(resp_str);
6524 // set the respawn count
6525 if(Netgame.campaign_mode == MP_SINGLE){
6526 memset(resp_str,0,10);
6527 sprintf(resp_str,"%d",Netgame.respawn);
6528 Multi_ho_respawns.set_text(resp_str);
6531 // set the mission time limit
6532 memset(resp_str,0,10);
6533 float tl = f2fl(Netgame.options.mission_time_limit);
6534 sprintf(resp_str,"%d",(int)(tl / 60.0f));
6535 Multi_ho_time_limit.set_text(resp_str);
6537 // set the furball kill limit
6538 memset(resp_str,0,10);
6539 sprintf(resp_str,"%d",Netgame.options.kill_limit);
6540 Multi_ho_kill_limit.set_text(resp_str);
6542 // set the token wait time
6543 memset(resp_str,0,10);
6544 sprintf(resp_str,"%d",Netgame.options.voice_token_wait / 1000);
6545 Multi_ho_voice_wait.set_text(resp_str);
6547 // get whether we're in host/captains only modify mode
6548 if(Netgame.options.flags & MSO_FLAG_SS_LEADERS){
6549 Multi_ho_host_modifies = 1;
6551 Multi_ho_host_modifies = 0;
6555 void multi_ho_apply_options()
6557 // if the voice qos or duration has changed, apply the change
6558 multi_voice_maybe_update_vars(Netgame.options.voice_qos,Netgame.options.voice_record_time);
6560 // send an options update
6561 multi_options_update_netgame();
6564 // display the voice record time settings
6565 void multi_ho_display_record_time()
6568 int full_seconds, half_seconds;
6571 memset(time_str,0,30);
6574 full_seconds = (((Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 500) / 1000);
6576 // get the half-seconds
6577 half_seconds = ((((Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 500) % 1000) / 500) * 5;
6579 // format the string
6580 sprintf(time_str,"%d.%d",full_seconds,half_seconds);
6581 gr_set_color_fast(&Color_bright);
6582 gr_string(Ho_vd_coords[gr_screen.res][MULTI_HO_X_COORD],Ho_vd_coords[gr_screen.res][MULTI_HO_Y_COORD],time_str);
6585 int multi_ho_check_values()
6589 memset(val_txt,0,255);
6591 // check against respawn settings
6592 if(Multi_ho_mission_respawn != -1){
6593 Multi_ho_respawns.get_text(val_txt);
6594 // if the value is invalid, let the user know
6595 if(atoi(val_txt) > Multi_ho_mission_respawn){
6596 memset(val_txt,0,255);
6597 sprintf(val_txt,XSTR("Warning\nRespawn count in greater than mission specified max (%d)",796),Multi_ho_mission_respawn);
6598 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6603 // check against mission time limit max
6604 Multi_ho_time_limit.get_text(val_txt);
6605 // if the value is invalid, force it to be valid
6606 if(atoi(val_txt) > MULTI_HO_MAX_TIME_LIMIT){
6607 memset(val_txt,0,255);
6608 sprintf(val_txt,XSTR("Warning\nMission time limit is greater than max allowed (%d)",797),MULTI_HO_MAX_TIME_LIMIT);
6609 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6613 // check against max observer limit
6614 Multi_ho_obs.get_text(val_txt);
6615 // if the value is invalid, force it to be valid
6616 if(atoi(val_txt) > MULTI_HO_MAX_OBS){
6617 memset(val_txt,0,255);
6618 sprintf(val_txt,XSTR("Warning\nObserver count is greater than max allowed (%d)",798),MULTI_HO_MAX_OBS);
6619 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6623 // check against furball kill limit
6624 Multi_ho_kill_limit.get_text(val_txt);
6625 // if the value is invalid, force it to be valid
6626 if(atoi(val_txt) > MULTI_HO_MAX_KILL_LIMIT){
6627 memset(val_txt,0,255);
6628 sprintf(val_txt,XSTR("Warning\nMission kill limit is greater than max allowed (%d)",799),MULTI_HO_MAX_KILL_LIMIT);
6629 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6633 // check against the token wait limit
6634 Multi_ho_voice_wait.get_text(val_txt);
6635 if(atoi(val_txt) > MULTI_HO_MAX_TOKEN_WAIT){
6636 memset(val_txt,0,255);
6637 sprintf(val_txt,XSTR("Warning\nvoice wait time is greater than max allowed (%d)",800),MULTI_HO_MAX_TOKEN_WAIT);
6638 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6642 // all values are valid
6646 void multi_ho_check_focus()
6648 // if an inputbox has been pressed (hit enter), lose its focus
6649 if (Multi_ho_respawns.pressed() || Multi_ho_time_limit.pressed() || Multi_ho_voice_wait.pressed() || Multi_ho_kill_limit.pressed() || Multi_ho_obs.pressed()) {
6650 Multi_ho_respawns.clear_focus();
6651 Multi_ho_time_limit.clear_focus();
6652 Multi_ho_voice_wait.clear_focus();
6653 Multi_ho_kill_limit.clear_focus();
6654 Multi_ho_obs.clear_focus();
6655 gamesnd_play_iface(SND_COMMIT_PRESSED);
6656 chatbox_set_focus();
6657 Multi_ho_lastframe_input = 0;
6659 } else if(!Multi_ho_lastframe_input) {
6660 // if we didn't have focus last frame
6661 if(Multi_ho_respawns.has_focus() || Multi_ho_time_limit.has_focus() || Multi_ho_kill_limit.has_focus() || Multi_ho_voice_wait.has_focus() ){
6662 chatbox_lose_focus();
6664 Multi_ho_lastframe_input = 1;
6667 // if we _did_ have focus last frame
6669 // if we no longer have focus on any of the input boxes, set the focus on the chatbox
6670 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()){
6671 chatbox_set_focus();
6673 // if the chatbox now has focus, clear all focus from our inputboxes
6674 else if (chatbox_has_focus()) {
6675 Multi_ho_respawns.clear_focus();
6676 Multi_ho_time_limit.clear_focus();
6677 Multi_ho_kill_limit.clear_focus();
6678 Multi_ho_voice_wait.clear_focus();
6680 Multi_ho_lastframe_input = 0;
6685 void multi_ho_blit_max_respawns()
6689 // if we're in campaign mode, do nothing
6690 if(Netgame.campaign_mode == MP_CAMPAIGN){
6694 // otherwise blit the max as specified by the current mission file
6695 sprintf(string,"(%d)",Multi_ho_mission_respawn);
6696 gr_set_color_fast(&Color_normal);
6697 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);
6700 void multi_ho_display_skill_level()
6702 int skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6705 Assert((skill_level >= 0) && (skill_level < NUM_SKILL_LEVELS));
6706 if((skill_level < 0) || (skill_level >= NUM_SKILL_LEVELS)){
6710 gr_set_color_fast(&Color_bright);
6711 gr_string(Ho_st_coords[gr_screen.res][0], Ho_st_coords[gr_screen.res][1], Skill_level_names(skill_level, 1));
6714 // -------------------------------------------------------------------------------------------------------------
6716 // MULTIPLAYER JOIN SCREEN
6719 #define MULTI_JW_NUM_BUTTONS 8
6723 #define MULTI_JW_PALETTE "InterfacePalette"
6725 static char *Multi_jw_bitmap_fname[GR_NUM_RESOLUTIONS] = {
6726 "MultiJoinWait", // GR_640
6727 "2_MultiJoinWait" // GR_1024
6730 static char *Multi_jw_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
6731 "MultiJoinWait-M", // GR_640
6732 "2_MultiJoinWait-M" // GR_1024
6738 #define MJW_SCROLL_PLAYERS_UP 0
6739 #define MJW_SCROLL_PLAYERS_DOWN 1
6742 #define MJW_PILOT_INFO 4
6743 #define MJW_SCROLL_INFO_UP 5
6744 #define MJW_SCROLL_INFO_DOWN 6
6745 #define MJW_CANCEL 7
6747 UI_WINDOW Multi_jw_window; // the window object for the join screen
6748 int Multi_jw_bitmap; // the background bitmap
6750 // constants for coordinate lookup
6751 #define MJW_X_COORD 0
6752 #define MJW_Y_COORD 1
6753 #define MJW_W_COORD 2
6754 #define MJW_H_COORD 3
6756 ui_button_info Multi_jw_buttons[GR_NUM_RESOLUTIONS][MULTI_JW_NUM_BUTTONS] = {
6758 ui_button_info("MJW_00", 1, 24, -1, -1, 0),
6759 ui_button_info("MJW_01", 1, 66, -1, -1, 1),
6760 ui_button_info("MJW_02", 30, 244, 20, 272, 2),
6761 ui_button_info("MJW_03", 84, 244, 73, 272, 3),
6762 ui_button_info("MJW_04", 139, 242, 134, 272, 4),
6763 ui_button_info("MJW_05", 1, 406, -1, -1, 5),
6764 ui_button_info("MJW_06", 1, 447, -1, -1, 6),
6765 ui_button_info("MJW_07", 577, 428, 570, 414, 7),
6768 ui_button_info("2_MJW_00", 2, 38, -1, -1, 0),
6769 ui_button_info("2_MJW_01", 2, 106, -1, -1, 1),
6770 ui_button_info("2_MJW_02", 48, 390, 47, 435, 2),
6771 ui_button_info("2_MJW_03", 134, 390, 133, 435, 3),
6772 ui_button_info("2_MJW_04", 223, 388, 225, 435, 4),
6773 ui_button_info("2_MJW_05", 2, 649, -1, -1, 5),
6774 ui_button_info("2_MJW_06", 2, 715, -1, -1, 6),
6775 ui_button_info("2_MJW_07", 923, 685, 931, 667, 7),
6779 #define MULTI_JW_NUM_TEXT 7
6781 UI_XSTR Multi_jw_text[GR_NUM_RESOLUTIONS][MULTI_JW_NUM_TEXT] = {
6783 { "Team 1", 1308, 20, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_TEAM0].button },
6784 { "Team 2", 1309, 73, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_TEAM1].button },
6785 { "Pilot", 1310, 134, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_PILOT_INFO].button },
6786 { "Info", 1311, 134, 283, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_PILOT_INFO].button },
6787 { "Cancel", 387, 570, 414, UI_XSTR_COLOR_PINK, -1, &Multi_jw_buttons[0][MJW_CANCEL].button },
6788 { "Players", 1269, 38, 8, UI_XSTR_COLOR_GREEN, -1, NULL },
6789 { "Choose Team", 1312, 27, 231, UI_XSTR_COLOR_GREEN, -1, NULL },
6792 { "Team 1", 1308, 47, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_TEAM0].button },
6793 { "Team 2", 1309, 133, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_TEAM1].button },
6794 { "Pilot", 1310, 225, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_PILOT_INFO].button },
6795 { "Info", 1311, 225, 446, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_PILOT_INFO].button },
6796 { "Cancel", 387, 931, 667, UI_XSTR_COLOR_PINK, -1, &Multi_jw_buttons[1][MJW_CANCEL].button },
6797 { "Players", 1269, 165, 12, UI_XSTR_COLOR_GREEN, -1, NULL },
6798 { "Choose Team", 1312, 45, 373, UI_XSTR_COLOR_GREEN, -1, NULL },
6802 int Mjw_players_coords[GR_NUM_RESOLUTIONS][4] = {
6811 int Mjw_mission_name_coords[GR_NUM_RESOLUTIONS][2] = {
6820 // squad war checkbox
6821 UI_CHECKBOX Multi_jw_sw_checkbox;
6822 char *Multi_jw_sw_checkbox_fname[GR_NUM_RESOLUTIONS] = {
6826 int Multi_jw_sw_checkbox_coords[GR_NUM_RESOLUTIONS][2] = {
6834 int Multi_jw_sw_checkbox_text[GR_NUM_RESOLUTIONS][2] = {
6844 // player list control thingie defs
6845 #define MULTI_JW_PLIST_MAX_DISPLAY 19
6846 int Multi_jw_plist_select_flag; // indicates whether we currently have a selected player
6847 short Multi_jw_plist_select_id; // id of the current selected player
6848 UI_BUTTON Multi_jw_plist_select_button; // for selecting a player
6850 int Multi_jw_should_show_popup = 0;
6852 // LOCAL function definitions
6853 void multi_jw_check_buttons();
6854 void multi_jw_button_pressed(int n);
6855 void multi_jw_do_netstuff();
6856 void multi_jw_scroll_players_up();
6857 void multi_jw_scroll_players_down();
6858 void multi_jw_plist_process();
6859 void multi_jw_plist_blit_normal();
6860 void multi_jw_plist_blit_team();
6861 short multi_jw_get_mouse_id();
6863 void multi_game_client_setup_init()
6867 // create the interface window
6868 Multi_jw_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
6869 Multi_jw_window.set_mask_bmap(Multi_jw_bitmap_mask_fname[gr_screen.res]);
6871 // load the background bitmap
6872 Multi_jw_bitmap = bm_load(Multi_jw_bitmap_fname[gr_screen.res]);
6873 if(Multi_jw_bitmap < 0){
6874 // we failed to load the bitmap - this is very bad
6878 // initialize the player list data
6879 Multi_jw_plist_select_flag = 0;
6880 Multi_jw_plist_select_id = -1;
6882 // kill any old instances of the chatbox and create a new one
6884 chatbox_create(CHATBOX_FLAG_BIG | CHATBOX_FLAG_DRAW_BOX | CHATBOX_FLAG_BUTTONS);
6886 // initialize the common notification messaging
6887 multi_common_notify_init();
6889 // initialize the common mission info display area.
6890 multi_common_set_text("");
6892 // use the common interface palette
6893 multi_common_set_palette();
6895 // create the interface buttons
6896 for(idx=0; idx<MULTI_JW_NUM_BUTTONS; idx++){
6897 // create the object
6898 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);
6900 // set the sound to play when highlighted
6901 Multi_jw_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
6903 // set the ani for the button
6904 Multi_jw_buttons[gr_screen.res][idx].button.set_bmaps(Multi_jw_buttons[gr_screen.res][idx].filename);
6907 Multi_jw_buttons[gr_screen.res][idx].button.link_hotspot(Multi_jw_buttons[gr_screen.res][idx].hotspot);
6910 // if this is a PXO game, enable the squadwar checkbox
6911 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);
6912 Multi_jw_sw_checkbox.set_bmaps(Multi_jw_sw_checkbox_fname[gr_screen.res], 6, 0);
6913 Multi_jw_sw_checkbox.disable();
6914 if(!MULTI_IS_TRACKER_GAME){
6915 Multi_jw_sw_checkbox.hide();
6919 for(idx=0; idx<MULTI_JW_NUM_TEXT; idx++){
6920 Multi_jw_window.add_XSTR(&Multi_jw_text[gr_screen.res][idx]);
6923 // create the player select list button and hide it
6924 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);
6925 Multi_jw_plist_select_button.hide();
6928 Multi_jw_buttons[gr_screen.res][MJW_CANCEL].button.set_hotkey(KEY_ESC);
6930 // remove campaign flags
6931 Game_mode &= ~(GM_CAMPAIGN_MODE);
6933 // tell the server we have finished joining
6934 Net_player->state = NETPLAYER_STATE_JOINED;
6935 send_netplayer_update_packet();
6938 ml_printf(NOX("Joined netgame %s"), Netgame.name);
6940 // send any appropriate files
6941 multi_data_send_my_junk();
6944 void multi_game_client_setup_do_frame()
6947 int k = chatbox_process();
6948 char mission_text[255];
6949 k = Multi_jw_window.process(k,0);
6951 Multi_jw_should_show_popup = 0;
6953 // process any button clicks
6954 multi_jw_check_buttons();
6956 // do any network related stuff
6957 multi_jw_do_netstuff();
6959 // draw the background, etc
6961 GR_MAYBE_CLEAR_RES(Multi_jw_bitmap);
6962 if(Multi_jw_bitmap != -1){
6963 gr_set_bitmap(Multi_jw_bitmap);
6967 // if we're not in team vs. team mode, don't draw the team buttons
6968 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
6969 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.hide();
6970 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.hide();
6971 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.disable();
6972 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.disable();
6974 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.enable();
6975 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.enable();
6976 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.unhide();
6977 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.unhide();
6980 if(MULTI_IS_TRACKER_GAME){
6981 // maybe check the squadwar button
6982 if(Netgame.type_flags & NG_TYPE_SW){
6983 Multi_jw_sw_checkbox.set_state(1);
6984 gr_set_color_fast(&Color_bright);
6986 Multi_jw_sw_checkbox.set_state(0);
6987 gr_set_color_fast(&Color_normal);
6990 gr_string(Multi_jw_sw_checkbox_text[gr_screen.res][0], Multi_jw_sw_checkbox_text[gr_screen.res][1], "SquadWar");
6993 // draw the UI window
6994 Multi_jw_window.draw();
6996 // process and display the player list
6997 // NOTE : this must be done before the buttons are checked to insure that a player hasn't dropped
6998 multi_jw_plist_process();
6999 if(Netgame.type_flags & NG_TYPE_TEAM){
7000 multi_jw_plist_blit_team();
7002 multi_jw_plist_blit_normal();
7005 // display any text in the info area
7006 multi_common_render_text();
7008 // display any pending notification messages
7009 multi_common_notify_do();
7011 // blit the mission filename if possible
7012 if(Netgame.campaign_mode){
7013 if(strlen(Netgame.campaign_name) > 0){
7014 strcpy(mission_text,Netgame.campaign_name);
7016 if(strlen(Netgame.title) > 0){
7017 strcat(mission_text,", ");
7018 strcat(mission_text,Netgame.title);
7021 gr_set_color_fast(&Color_bright_white);
7022 gr_string(Mjw_mission_name_coords[gr_screen.res][MJW_X_COORD],Mjw_mission_name_coords[gr_screen.res][MJW_Y_COORD],mission_text);
7025 if(strlen(Netgame.mission_name) > 0){
7026 strcpy(mission_text,Netgame.mission_name);
7028 if(strlen(Netgame.title) > 0){
7029 strcat(mission_text,", ");
7030 strcat(mission_text,Netgame.title);
7033 gr_set_color_fast(&Color_bright_white);
7034 gr_string(Mjw_mission_name_coords[gr_screen.res][MJW_X_COORD],Mjw_mission_name_coords[gr_screen.res][MJW_Y_COORD],mission_text);
7038 // process and show the chatbox thingie
7042 Multi_jw_window.draw_tooltip();
7044 // display the voice status indicator
7045 multi_common_voice_display_status();
7050 // if we're supposed to be displaying a pilot info popup
7051 if(Multi_jw_should_show_popup){
7052 player_index = find_player_id(Multi_jw_plist_select_id);
7053 if(player_index != -1){
7054 multi_pinfo_popup(&Net_players[player_index]);
7059 void multi_game_client_setup_close()
7061 // unload any bitmaps
7062 if(!bm_unload(Multi_jw_bitmap)){
7063 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_jw_bitmap_fname[gr_screen.res]));
7066 // destroy the chatbox
7069 // destroy the UI_WINDOW
7070 Multi_jw_window.destroy();
7073 if(Netgame.game_state == NETGAME_STATE_MISSION_SYNC){
7074 gamesnd_play_iface(SND_COMMIT_PRESSED);
7079 void multi_jw_check_buttons()
7082 for(idx=0;idx<MULTI_JW_NUM_BUTTONS;idx++){
7083 // we only really need to check for one button pressed at a time, so we can break after
7085 if(Multi_jw_buttons[gr_screen.res][idx].button.pressed()){
7086 multi_jw_button_pressed(idx);
7092 void multi_jw_button_pressed(int n)
7096 gamesnd_play_iface(SND_USER_SELECT);
7097 multi_quit_game(PROMPT_CLIENT);
7099 case MJW_SCROLL_PLAYERS_UP:
7100 multi_jw_scroll_players_up();
7102 case MJW_SCROLL_PLAYERS_DOWN:
7103 multi_jw_scroll_players_down();
7105 case MJW_SCROLL_INFO_UP:
7106 multi_common_scroll_text_up();
7108 case MJW_SCROLL_INFO_DOWN:
7109 multi_common_scroll_text_down();
7112 // request to set myself to team 0
7114 gamesnd_play_iface(SND_USER_SELECT);
7115 multi_team_set_team(Net_player,0);
7118 // request to set myself to team 1
7120 gamesnd_play_iface(SND_USER_SELECT);
7121 multi_team_set_team(Net_player,1);
7125 case MJW_PILOT_INFO:
7126 Multi_jw_should_show_popup = 1;
7130 multi_common_add_notify(XSTR("Not implemented yet!",760));
7135 // do stuff like pinging servers, sending out requests, etc
7136 void multi_jw_do_netstuff()
7140 void multi_jw_scroll_players_up()
7142 gamesnd_play_iface(SND_GENERAL_FAIL);
7145 // scroll down through the player list
7146 void multi_jw_scroll_players_down()
7148 gamesnd_play_iface(SND_GENERAL_FAIL);
7151 void multi_jw_plist_process()
7153 int test_count,player_index,idx;
7155 // first determine if there are 0 players in the game. This should never happen since the host is _always_ in the game
7157 for(idx=0;idx<MAX_PLAYERS;idx++){
7158 // count anyone except the standalone server (if applicable)
7159 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7163 if(test_count <= 0){
7167 // if we had a selected item but that player has left, select myself instead
7168 if(Multi_jw_plist_select_flag){
7169 player_index = find_player_id(Multi_jw_plist_select_id);
7170 if(player_index == -1){
7171 Multi_jw_plist_select_id = Net_player->player_id;
7174 Multi_jw_plist_select_flag = 1;
7175 Multi_jw_plist_select_id = Net_player->player_id;
7178 // if the player has clicked somewhere in the player list area
7179 if(Multi_jw_plist_select_button.pressed()){
7183 player_id = multi_jw_get_mouse_id();
7184 player_index = find_player_id(player_id);
7185 if(player_index != -1){
7186 Multi_jw_plist_select_id = player_id;
7187 Multi_jw_plist_select_flag = 1;
7192 void multi_jw_plist_blit_normal()
7195 char str[CALLSIGN_LEN+1];
7196 int y_start = Mjw_players_coords[gr_screen.res][MJW_Y_COORD];
7199 // display all the players
7200 for(idx=0;idx<MAX_PLAYERS;idx++){
7201 // reset total offset
7204 // count anyone except the standalone server (if applicable)
7205 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7206 // highlight him if he's the host
7207 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7208 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7209 gr_set_color_fast(&Color_text_active_hi);
7211 gr_set_color_fast(&Color_bright);
7214 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7215 gr_set_color_fast(&Color_text_active);
7217 gr_set_color_fast(&Color_text_normal);
7221 // optionally draw his CD status
7222 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7223 gr_set_bitmap(Multi_common_icons[MICON_CD]);
7224 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7226 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7229 // make sure the string will fit, then display it
7230 strcpy(str,Net_players[idx].player->callsign);
7231 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
7234 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7235 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7242 void multi_jw_plist_blit_team()
7245 char str[CALLSIGN_LEN+1];
7246 int y_start = Mjw_players_coords[gr_screen.res][MJW_Y_COORD];
7249 // always blit the proper team button based on _my_ team status
7250 if(Net_player->p_info.team == 0){
7251 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.draw_forced(2);
7253 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.draw_forced(2);
7256 // display all the red players first
7257 for(idx=0;idx<MAX_PLAYERS;idx++){
7258 // reset total offset
7261 // count anyone except the standalone server (if applicable)
7262 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
7263 // highlight him if he's the host
7264 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7265 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7266 gr_set_color_fast(&Color_text_active_hi);
7268 gr_set_color_fast(&Color_bright);
7271 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7272 gr_set_color_fast(&Color_text_active);
7274 gr_set_color_fast(&Color_text_normal);
7278 // optionally draw his CD status
7279 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7280 gr_set_bitmap(Multi_common_icons[MICON_CD]);
7281 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7283 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7286 // blit the red team indicator
7287 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
7288 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
7289 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT]);
7290 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7292 total_offset += Multi_common_icon_dims[MICON_TEAM0_SELECT][0] + 1;
7295 if(Multi_common_icons[MICON_TEAM0] != -1){
7296 gr_set_bitmap(Multi_common_icons[MICON_TEAM0]);
7297 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7299 total_offset += Multi_common_icon_dims[MICON_TEAM0][0] + 1;
7303 // make sure the string will fit
7304 strcpy(str,Net_players[idx].player->callsign);
7305 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7307 // display him in the correct half of the list depending on his team
7308 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7313 // display all the green players next
7314 for(idx=0;idx<MAX_PLAYERS;idx++){
7315 // reset total offset
7318 // count anyone except the standalone server (if applicable)
7319 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
7320 // highlight him if he's the host
7321 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7322 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7323 gr_set_color_fast(&Color_text_active_hi);
7325 gr_set_color_fast(&Color_bright);
7328 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7329 gr_set_color_fast(&Color_text_active);
7331 gr_set_color_fast(&Color_text_normal);
7335 // optionally draw his CD status
7336 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7337 gr_set_bitmap(Multi_common_icons[MICON_CD]);
7338 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7340 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7343 // blit the red team indicator
7344 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
7345 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
7346 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT]);
7347 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7349 total_offset += Multi_common_icon_dims[MICON_TEAM1_SELECT][0] + 1;
7352 if(Multi_common_icons[MICON_TEAM1] != -1){
7353 gr_set_bitmap(Multi_common_icons[MICON_TEAM1]);
7354 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7356 total_offset += Multi_common_icon_dims[MICON_TEAM1][0] + 1;
7360 // make sure the string will fit
7361 strcpy(str,Net_players[idx].player->callsign);
7362 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
7365 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7367 // display him in the correct half of the list depending on his team
7368 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7374 void multi_jw_handle_join(net_player *pl)
7376 // for now just play a bloop sound
7377 gamesnd_play_iface(SND_ICON_DROP_ON_WING);
7380 short multi_jw_get_mouse_id()
7382 // determine where he clicked (y pixel value)
7384 Multi_jw_plist_select_button.get_mouse_pos(NULL,&y);
7386 // select things a little differently if we're in team vs. team or non-team vs. team mode
7388 if(Netgame.type_flags & NG_TYPE_TEAM){
7389 int player_index = -1;
7391 // look through all of team red first
7392 for(idx=0;idx<MAX_PLAYERS;idx++){
7393 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
7396 // if this is the _nth_ guy
7404 // if we still haven't found him yet, look through the green team
7405 if(player_index == -1){
7406 for(idx=0;idx<MAX_PLAYERS;idx++){
7407 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
7409 // if this is the _nth_ guy
7417 if(player_index != -1){
7418 return Net_players[idx].player_id;
7421 // select the nth active player if possible, disregarding the standalone server
7422 for(idx=0;idx<MAX_PLAYERS;idx++){
7423 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7426 // if this is the _nth_ guy
7428 return Net_players[idx].player_id;
7439 // -------------------------------------------------------------------------------------------------------------
7441 // MULTIPLAYER WAIT/SYNCH SCREEN
7444 #define MULTI_SYNC_HOST_COUNT 4 // host uses 4 buttons (and sometimes 5)
7445 #define MULTI_SYNC_CLIENT_COUNT 3 // client only uses 3 buttons
7447 char *Multi_sync_bitmap_fname[GR_NUM_RESOLUTIONS] = {
7448 "MultiSynch", // GR_640
7449 "2_MultiSynch" // GR_1024
7452 char *Multi_sync_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
7453 "MultiSynch-M", // GR_640
7454 "2_MultiSynch-M" // GR_1024
7459 // constants for coordinate lookup
7460 #define MS_X_COORD 0
7461 #define MS_Y_COORD 1
7462 #define MS_W_COORD 2
7463 #define MS_H_COORD 3
7465 UI_WINDOW Multi_sync_window; // the window object for the join screen
7466 int Multi_sync_button_count; // the # of buttons to use for this instance of mission sync
7467 int Multi_sync_bitmap; // the background bitmap
7470 #define MULTI_SYNC_NUM_BUTTONS 5
7471 #define MS_SCROLL_INFO_UP 0
7472 #define MS_SCROLL_INFO_DOWN 1
7476 ui_button_info Multi_sync_buttons[GR_NUM_RESOLUTIONS][MULTI_SYNC_NUM_BUTTONS] = {
7478 ui_button_info("MS_00", 1, 404, -1, -1, 0),
7479 ui_button_info("MS_01", 1, 446, -1, -1, 1),
7480 ui_button_info("MS_03", 518, 426, 519, 416, 3),
7481 ui_button_info("MS_02", 469, 426, 479, 416, 2),
7482 ui_button_info("MS_04", 571, 420, 577, 416, 4),
7485 ui_button_info("2_MS_00", 2, 647, -1, -1, 0),
7486 ui_button_info("2_MS_01", 2, 713, -1, -1, 1),
7487 ui_button_info("2_MS_03", 829, 682, 831, 667, 3),
7488 ui_button_info("2_MS_02", 751, 682, 766, 667, 2),
7489 ui_button_info("2_MS_04", 914, 672, 924, 667, 4),
7494 #define MULTI_SYNC_NUM_TEXT 5
7496 #define MST_LAUNCH 2
7497 UI_XSTR Multi_sync_text[GR_NUM_RESOLUTIONS][MULTI_SYNC_NUM_TEXT] = {
7499 { "Kick", 1266, 479, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_KICK].button },
7500 { "Cancel", 387, 519, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_CANCEL].button },
7501 { "Launch", 801, 577, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_LAUNCH].button },
7502 { "Players", 1269, 23, 133, UI_XSTR_COLOR_GREEN, -1, NULL },
7503 { "Status", 1304, 228, 133, UI_XSTR_COLOR_GREEN, -1, NULL }
7506 { "Kick", 1266, 766, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_KICK].button },
7507 { "Cancel", 387, 831, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_CANCEL].button },
7508 { "Launch", 801, 924, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_LAUNCH].button },
7509 { "Players", 1269, 38, 214, UI_XSTR_COLOR_GREEN, -1, NULL },
7510 { "Status", 1304, 366, 214, UI_XSTR_COLOR_GREEN, -1, NULL }
7515 int Ms_status_coords[GR_NUM_RESOLUTIONS][4] = {
7524 // player status coords
7525 int Ms_status2_coords[GR_NUM_RESOLUTIONS][4] = {
7534 int Ms_cd_icon_offset[GR_NUM_RESOLUTIONS] = {
7539 int Ms_team_icon_offset[GR_NUM_RESOLUTIONS] = {
7544 // player currently selected, index into Net_players[]
7545 int Multi_sync_player_select = -1;
7547 // player list control thingie defs
7548 #define MULTI_SYNC_PLIST_MAX_DISPLAY 15
7549 int Multi_sync_plist_start; // where to start displaying from
7550 int Multi_sync_plist_count; // how many we have
7552 // list select button
7553 UI_BUTTON Multi_sync_plist_button;
7555 int Multi_sync_mode = -1;
7557 #define MULTI_SYNC_COUNTDOWN_TIME 5 // in seconds
7558 float Multi_sync_countdown_timer;
7559 int Multi_sync_countdown = -1;
7561 int Multi_launch_button_created;
7564 // countdown animation timer
7565 char* Multi_sync_countdown_fname[GR_NUM_RESOLUTIONS] = {
7567 "2_Count" // GR_1024
7570 int Multi_sync_countdown_coords[GR_NUM_RESOLUTIONS][2] = {
7581 anim *Multi_sync_countdown_anim = NULL;
7582 anim_instance *Multi_sync_countdown_instance = NULL;
7585 // PREBRIEFING STUFF
7586 // syncing flags used by the server
7587 int Mission_sync_flags = 0;
7588 #define MS_FLAG_SENT_FILESIG (1<<0) // sent filesig requests
7589 #define MS_FLAG_SENT_LOAD (1<<1) // sent load packets
7590 #define MS_FLAG_PUSHED_BRIEFING (1<<2) // pushed everyone else into the briefing
7591 #define MS_FLAG_POST_DATA (1<<3) // sent the post data block
7592 #define MS_FLAG_WSS_SLOTS (1<<4) // all players have received wss slot data
7593 #define MS_FLAG_PSETTINGS (1<<5) // send the player settings packet
7594 #define MS_FLAG_MT_STATS_START (1<<6) // server has started getting player stats from the tracker
7595 #define MS_FLAG_MT_STATS_DONE (1<<7) // server has finished getting player stats from the tracker (success or fail)
7596 #define MS_FLAG_TS_SLOTS (1<<8) // team/ship slots have been sent
7597 #define MS_FLAG_DATA_DONE (1<<9) // done transferring all necessary data
7598 #define MS_FLAG_CAMP_DONE (1<<10) // send campaign pool/goal/event stuff
7600 // POSTBRIEFING STUFF
7601 int Multi_state_timestamp;
7602 int Multi_sync_launch_pressed;
7604 // LOCAL function definitions
7605 void multi_sync_check_buttons();
7606 void multi_sync_button_pressed(int n);
7607 void multi_sync_scroll_info_up();
7608 void multi_sync_scroll_info_down();
7609 void multi_sync_display_name(char *name,int index,int np_index); // display info on the left hand portion of the status window thingie
7610 void multi_sync_display_status(char *status,int index); // display info on the right hand portion of the status window thingie
7611 void multi_sync_force_start_pre();
7612 void multi_sync_force_start_post();
7613 void multi_sync_launch();
7614 void multi_sync_create_launch_button();
7615 void multi_sync_blit_screen_all();
7616 void multi_sync_handle_plist();
7618 void multi_sync_common_init();
7619 void multi_sync_common_do();
7620 void multi_sync_common_close();
7622 void multi_sync_pre_init();
7623 void multi_sync_pre_do();
7624 void multi_sync_pre_close();
7626 void multi_sync_post_init();
7627 void multi_sync_post_do();
7628 void multi_sync_post_close();
7633 // perform the correct init functions
7634 void multi_sync_init()
7636 Multi_sync_countdown = -1;
7640 // reset all timestamp
7641 multi_reset_timestamps();
7643 extern int Player_multi_died_check;
7644 Player_multi_died_check = -1;
7646 if(!(Game_mode & GM_STANDALONE_SERVER)){
7647 multi_sync_common_init();
7650 switch(Multi_sync_mode){
7651 case MULTI_SYNC_PRE_BRIEFING:
7652 multi_sync_pre_init();
7654 case MULTI_SYNC_POST_BRIEFING:
7655 multi_sync_post_init();
7657 case MULTI_SYNC_INGAME:
7658 multi_ingame_sync_init();
7663 // perform the correct do frame functions
7664 void multi_sync_do()
7666 if(!(Game_mode & GM_STANDALONE_SERVER)){
7667 multi_sync_common_do();
7670 // if the netgame is ending, don't do any sync processing
7671 if(multi_endgame_ending()){
7675 // process appropriateliy
7676 switch(Multi_sync_mode){
7677 case MULTI_SYNC_PRE_BRIEFING:
7678 multi_sync_pre_do();
7680 case MULTI_SYNC_POST_BRIEFING:
7681 multi_sync_post_do();
7683 case MULTI_SYNC_INGAME:
7684 multi_ingame_sync_do();
7687 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
7688 if(Multi_sync_bitmap != -1){
7689 gr_set_bitmap(Multi_sync_bitmap);
7692 Multi_sync_window.draw();
7694 multi_sync_blit_screen_all();
7701 // perform the correct close functions
7702 void multi_sync_close()
7704 switch(Multi_sync_mode){
7705 case MULTI_SYNC_PRE_BRIEFING:
7706 multi_sync_pre_close();
7708 case MULTI_SYNC_POST_BRIEFING:
7709 multi_sync_post_close();
7711 case MULTI_SYNC_INGAME:
7712 multi_ingame_sync_close();
7716 if(!(Game_mode & GM_STANDALONE_SERVER)){
7717 multi_sync_common_close();
7721 char *multi_sync_tooltip_handler(char *str)
7723 if (!stricmp(str, NOX("@launch"))) {
7724 if (Multi_launch_button_created){
7725 return XSTR("Launch",801);
7732 void multi_sync_common_init()
7736 // create the interface window
7737 Multi_sync_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
7738 Multi_sync_window.set_mask_bmap(Multi_sync_bitmap_mask_fname[gr_screen.res]);
7739 Multi_sync_window.tooltip_handler = multi_sync_tooltip_handler;
7741 // load the background bitmap
7742 Multi_sync_bitmap = bm_load(Multi_sync_bitmap_fname[gr_screen.res]);
7743 if (Multi_sync_bitmap < 0) {
7744 // we failed to load the bitmap - this is very bad
7748 // initialize the player list data
7749 Multi_sync_plist_start = 0;
7750 Multi_sync_plist_count = 1; // we can pretty safely assume that there's one player in the game - me.
7752 Multi_launch_button_created = 0;
7754 // create the chatbox thingie (shouldn't be necesary to do this, but we'll put it in for good measure)
7757 // force the chatbox to be small
7758 chatbox_force_small();
7760 // initialize the common notification messaging
7761 multi_common_notify_init();
7763 // initialize the common mission info display area.
7764 multi_common_set_text("");
7766 // use the common interface palette
7767 multi_common_set_palette();
7769 // don't select any player yet.
7770 Multi_sync_player_select = -1;
7772 // determine how many of the 5 buttons to create
7773 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
7774 Multi_sync_button_count = MULTI_SYNC_HOST_COUNT;
7776 Multi_sync_button_count = MULTI_SYNC_CLIENT_COUNT;
7778 // create the interface buttons
7779 for(idx=0; idx<Multi_sync_button_count; idx++){
7780 // create the object
7781 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);
7783 // set the sound to play when highlighted
7784 Multi_sync_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
7786 // set the ani for the button
7787 // this wierdness is necessary because cancel and kick buttons aren't drawn on the background bitmap,
7788 // so we have to load in frame 0, too (the file should exist)
7789 if ((idx == MS_CANCEL) || (idx == MS_KICK) || (idx == MS_LAUNCH)) {
7790 Multi_sync_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sync_buttons[gr_screen.res][idx].filename, 3, 0);
7792 Multi_sync_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sync_buttons[gr_screen.res][idx].filename);
7796 Multi_sync_buttons[gr_screen.res][idx].button.link_hotspot(Multi_sync_buttons[gr_screen.res][idx].hotspot);
7800 for(idx=0; idx<MULTI_SYNC_NUM_TEXT; idx++) {
7801 // don't create the "launch" button text just yet
7802 if(idx == MST_LAUNCH) {
7805 // multiplayer clients should ignore the kick button
7806 if(!MULTIPLAYER_MASTER && !MULTIPLAYER_HOST && (idx == MST_KICK)) {
7810 Multi_sync_window.add_XSTR(&Multi_sync_text[gr_screen.res][idx]);
7813 // create the player list select button and hide it
7814 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);
7815 Multi_sync_plist_button.hide();
7817 // set up hotkeys for certain common functions
7818 Multi_sync_buttons[gr_screen.res][MS_CANCEL].button.set_hotkey(KEY_ESC);
7821 void multi_sync_common_do()
7823 int k = chatbox_process();
7824 k = Multi_sync_window.process(k);
7826 // process the player list
7827 multi_sync_handle_plist();
7829 // process any button clicks
7830 multi_sync_check_buttons();
7832 // process any keypresses
7836 gamesnd_play_iface(SND_USER_SELECT);
7837 multi_quit_game(PROMPT_ALL);
7842 void multi_sync_common_close()
7844 // unload any bitmaps
7845 if(!bm_unload(Multi_sync_bitmap)){
7846 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_sync_bitmap_fname[gr_screen.res]));
7849 extern int Player_multi_died_check;
7850 Player_multi_died_check = -1;
7852 // destroy the UI_WINDOW
7853 Multi_sync_window.destroy();
7856 void multi_sync_blit_screen_all()
7863 // display any text in the info area
7864 multi_common_render_text();
7866 // display any pending notification messages
7867 multi_common_notify_do();
7869 // display any info about visible players
7871 for(idx=0;idx<MAX_PLAYERS;idx++){
7872 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7873 // display his name and status
7874 multi_sync_display_name(Net_players[idx].player->callsign,count,idx);
7876 // get the player state
7877 state = Net_players[idx].state;
7879 // if we're ingame joining, show all other players except myself as "playing"
7880 if((Net_player != NULL) && (&Net_players[idx] != Net_player) && ((Multi_sync_mode == MULTI_SYNC_INGAME) || (Net_player->flags & NETINFO_FLAG_INGAME_JOIN)) ){
7881 state = NETPLAYER_STATE_IN_MISSION;
7885 case NETPLAYER_STATE_MISSION_LOADING:
7886 multi_sync_display_status(XSTR("Mission Loading",802),count);
7888 case NETPLAYER_STATE_INGAME_SHIP_SELECT: // I don't think its possible to see this state, but...
7889 multi_sync_display_status(XSTR("Ingame Ship Select",803),count);
7891 case NETPLAYER_STATE_DEBRIEF:
7892 multi_sync_display_status(XSTR("Debriefing",804),count);
7894 case NETPLAYER_STATE_MISSION_SYNC:
7895 multi_sync_display_status(XSTR("Mission Sync",805),count);
7897 case NETPLAYER_STATE_JOINING:
7898 multi_sync_display_status(XSTR("Joining",806),count);
7900 case NETPLAYER_STATE_JOINED:
7901 multi_sync_display_status(XSTR("Joined",807),count);
7903 case NETPLAYER_STATE_SLOT_ACK :
7904 multi_sync_display_status(XSTR("Slot Ack",808),count);
7906 case NETPLAYER_STATE_BRIEFING:
7907 multi_sync_display_status(XSTR("Briefing",765),count);
7909 case NETPLAYER_STATE_SHIP_SELECT:
7910 multi_sync_display_status(XSTR("Ship Select",809),count);
7912 case NETPLAYER_STATE_WEAPON_SELECT:
7913 multi_sync_display_status(XSTR("Weapon Select",810),count);
7915 case NETPLAYER_STATE_WAITING:
7916 multi_sync_display_status(XSTR("Waiting",811),count);
7918 case NETPLAYER_STATE_IN_MISSION:
7919 multi_sync_display_status(XSTR("In Mission",812),count);
7921 case NETPLAYER_STATE_MISSION_LOADED:
7922 multi_sync_display_status(XSTR("Mission Loaded",813),count);
7924 case NETPLAYER_STATE_DATA_LOAD:
7925 multi_sync_display_status(XSTR("Data loading",814),count);
7927 case NETPLAYER_STATE_SETTINGS_ACK:
7928 multi_sync_display_status(XSTR("Ready To Enter Mission",815),count);
7930 case NETPLAYER_STATE_INGAME_SHIPS:
7931 multi_sync_display_status(XSTR("Ingame Ships Packet Ack",816),count);
7933 case NETPLAYER_STATE_INGAME_WINGS:
7934 multi_sync_display_status(XSTR("Ingame Wings Packet Ack",817),count);
7936 case NETPLAYER_STATE_INGAME_RPTS:
7937 multi_sync_display_status(XSTR("Ingame Respawn Points Ack",818),count);
7939 case NETPLAYER_STATE_SLOTS_ACK:
7940 multi_sync_display_status(XSTR("Ingame Weapon Slots Ack",819),count);
7942 case NETPLAYER_STATE_POST_DATA_ACK:
7943 multi_sync_display_status(XSTR("Post Briefing Data Block Ack",820),count);
7945 case NETPLAYER_STATE_FLAG_ACK :
7946 multi_sync_display_status(XSTR("Flags Ack",821),count);
7948 case NETPLAYER_STATE_MT_STATS :
7949 multi_sync_display_status(XSTR("Parallax Online Stats Updating",822),count);
7951 case NETPLAYER_STATE_WSS_ACK :
7952 multi_sync_display_status(XSTR("Weapon Slots Ack",823),count);
7954 case NETPLAYER_STATE_HOST_SETUP :
7955 multi_sync_display_status(XSTR("Host setup",824),count);
7957 case NETPLAYER_STATE_DEBRIEF_ACCEPT:
7958 multi_sync_display_status(XSTR("Debrief accept",825),count);
7960 case NETPLAYER_STATE_DEBRIEF_REPLAY:
7961 multi_sync_display_status(XSTR("Debrief replay",826),count);
7963 case NETPLAYER_STATE_CPOOL_ACK:
7964 multi_sync_display_status(XSTR("Campaign ship/weapon ack",827),count);
7966 case NETPLAYER_STATE_MISSION_XFER :
7968 // server should display the pct completion of all clients
7969 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
7970 if(Net_players[idx].s_info.xfer_handle != -1){
7971 pct_complete = multi_xfer_pct_complete(Net_players[idx].s_info.xfer_handle);
7973 // if we've got a valid xfer handle
7974 if((pct_complete >= 0.0) && (pct_complete <= 1.0)){
7975 sprintf(txt,XSTR("Mission file xfer %d%%",828),(int)(pct_complete * 100.0f));
7979 strcpy(txt,XSTR("Mission file xfer",829));
7982 strcpy(txt,XSTR("Mission file xfer",829));
7985 // clients should display only for themselves (which is the only thing they know)
7987 // if we've got a valid file xfer handle
7988 if((&Net_players[idx] == Net_player) && (Net_player->s_info.xfer_handle != -1)){
7989 pct_complete = multi_xfer_pct_complete(Net_player->s_info.xfer_handle);
7991 // if we've got a valid xfer handle
7992 if((pct_complete >= 0.0) && (pct_complete <= 1.0)){
7993 sprintf(txt,XSTR("Mission file xfer %d%%",828),(int)(pct_complete * 100.0f));
7997 strcpy(txt,XSTR("Mission file xfer",829));
8002 strcpy(txt,XSTR("Mission file xfer",829));
8007 multi_sync_display_status(txt,count);
8010 nprintf(("Network","Unhandled player state : %d !\n",Net_players[idx].state));
8017 // display the mission start countdown timer (if any)
8018 anim_render_all(GS_STATE_MULTI_MISSION_SYNC,flFrametime);
8020 // process and show the chatbox thingie
8024 Multi_sync_window.draw_tooltip();
8026 // display the voice status indicator
8027 multi_common_voice_display_status();
8030 void multi_sync_check_buttons()
8033 for(idx=0;idx<Multi_sync_button_count;idx++){
8034 // we only really need to check for one button pressed at a time, so we can break after
8036 if(Multi_sync_buttons[gr_screen.res][idx].button.pressed()){
8037 multi_sync_button_pressed(idx);
8043 void multi_sync_button_pressed(int n)
8048 gamesnd_play_iface(SND_USER_SELECT);
8049 multi_quit_game(PROMPT_ALL);
8052 // scroll the info box up
8053 case MS_SCROLL_INFO_UP:
8054 multi_common_scroll_text_up();
8057 // scroll the info box down
8058 case MS_SCROLL_INFO_DOWN:
8059 multi_common_scroll_text_down();
8064 // if we have a currently selected player, kick him
8065 if(Multi_sync_player_select >= 0){
8066 multi_kick_player(Multi_sync_player_select);
8070 // start the final launch countdown (post-sync only)
8072 multi_sync_start_countdown();
8075 // doesn't do anything
8081 void multi_sync_pre_init()
8085 Netgame.game_state = NETGAME_STATE_MISSION_SYNC;
8087 // if we're in teamplay mode, always force skill level to be medium
8088 if((Netgame.type_flags & NG_TYPE_TEAM) && (Net_player->flags & NETINFO_FLAG_GAME_HOST)){
8089 Netgame.options.skill_level = NUM_SKILL_LEVELS / 2;
8090 Game_skill_level = NUM_SKILL_LEVELS / 2;
8091 multi_options_update_netgame();
8094 // notify everyone of when we get here
8095 if(!(Game_mode & GM_STANDALONE_SERVER)){
8096 Net_player->state = NETPLAYER_STATE_MISSION_SYNC;
8097 send_netplayer_update_packet();
8100 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8102 ml_string(NOX("Server performing pre-briefing data sync"));
8104 if(!(Game_mode & GM_STANDALONE_SERVER)){
8105 multi_common_set_text(XSTR("Server performing sync\n",830),1);
8108 // maybe initialize tvt and squad war stuff
8109 if(Netgame.type_flags & NG_TYPE_TEAM){
8110 multi_team_level_init();
8113 // force everyone into this state
8114 send_netgame_update_packet();
8116 if(!(Game_mode & GM_STANDALONE_SERVER)){
8117 multi_common_add_text(XSTR("Send update packet\n",831),1);
8120 // setup some of my own data
8121 Net_player->flags |= NETINFO_FLAG_MISSION_OK;
8123 // do any output stuff
8124 if(Game_mode & GM_STANDALONE_SERVER){
8125 std_debug_set_standalone_state_string("Mission Sync");
8128 // do this here to insure we have the most up to date file checksum info
8129 multi_get_mission_checksum(Game_current_mission_filename);
8130 // parse_get_file_signature(Game_current_mission_filename);
8132 if(!(Game_mode & GM_STANDALONE_SERVER)){
8133 multi_common_add_text(XSTR("Got file signatures\n",832),1);
8136 if(!(Game_mode & GM_STANDALONE_SERVER)){
8137 multi_common_add_text(XSTR("Sending update state packet\n",833),1);
8141 // if we're not in team vs. team mode - set all player teams to be 0, and unset all captaincy bits
8142 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
8143 for(idx=0;idx<MAX_PLAYERS;idx++){
8144 Net_players[idx].p_info.team = 0;
8145 Net_players[idx].flags &= ~(NETINFO_FLAG_TEAM_CAPTAIN);
8149 // we aren't necessarily xferring the mission file yet
8150 Assert(Net_player->s_info.xfer_handle == -1);
8152 // always call this for good measure
8153 multi_campaign_flush_data();
8155 Mission_sync_flags = 0;
8156 Multi_mission_loaded = 0;
8159 void multi_sync_pre_do()
8163 // If I'm the server, wait for everyone to arrive in this state, then begin transferring data, etc.
8164 // all servers (standalone or no, go through this)
8165 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8166 // wait for everyone to arrive, then request filesig from all of them
8167 if(multi_netplayer_state_check(NETPLAYER_STATE_MISSION_SYNC) && !(Mission_sync_flags & MS_FLAG_SENT_FILESIG) && !multi_endgame_ending()){
8168 send_file_sig_request(Netgame.mission_name);
8169 Mission_sync_flags |= MS_FLAG_SENT_FILESIG;
8171 if(!(Game_mode & GM_STANDALONE_SERVER)){
8172 multi_common_add_text(XSTR("Sent filesig request\n",834),1);
8176 // if we're waiting for players to receive files, then check on their status
8177 if((Mission_sync_flags & MS_FLAG_SENT_FILESIG) && !multi_netplayer_flag_check(NETINFO_FLAG_MISSION_OK) && !multi_endgame_ending()){
8178 for(idx=0;idx<MAX_PLAYERS;idx++){
8179 // if this player is in the process of xferring a file
8180 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].s_info.xfer_handle != -1)){
8181 switch(multi_xfer_get_status(Net_players[idx].s_info.xfer_handle)){
8182 // if it has successfully completed, set his ok flag
8183 case MULTI_XFER_SUCCESS :
8185 Net_players[idx].flags |= NETINFO_FLAG_MISSION_OK;
8187 // release the xfer instance handle
8188 multi_xfer_release_handle(Net_players[idx].s_info.xfer_handle);
8189 Net_players[idx].s_info.xfer_handle = -1;
8191 // if it has failed or timed-out, kick the player
8192 case MULTI_XFER_TIMEDOUT:
8193 case MULTI_XFER_FAIL:
8194 // release the xfer handle
8195 multi_xfer_release_handle(Net_players[idx].s_info.xfer_handle);
8196 Net_players[idx].s_info.xfer_handle = -1;
8199 multi_kick_player(idx, 0, KICK_REASON_BAD_XFER);
8206 // NOTE : this is now obsolete
8207 // once everyone is verified, do any data transfer necessary
8208 if(multi_netplayer_flag_check(NETINFO_FLAG_MISSION_OK) && !(Mission_sync_flags & MS_FLAG_DATA_DONE) && !multi_endgame_ending()){
8209 // do nothing for now
8210 Mission_sync_flags |= MS_FLAG_DATA_DONE;
8212 // send campaign pool data
8213 multi_campaign_send_pool_status();
8216 // wait for everyone to ack on campaign pool data (even in non-campaign situations)
8217 if((Mission_sync_flags & MS_FLAG_DATA_DONE) && !(Mission_sync_flags & MS_FLAG_CAMP_DONE) && !multi_endgame_ending()){
8218 // check to see if everyone has acked the campaign pool data
8219 if(multi_netplayer_state_check(NETPLAYER_STATE_CPOOL_ACK)){
8220 Mission_sync_flags |= MS_FLAG_CAMP_DONE;
8224 // once everyone is verified, tell them to load the mission
8225 // also make sure to load the mission myself _AFTER_ telling everyone to do so. This makes the whole process
8226 // move along faster
8227 if((Mission_sync_flags & MS_FLAG_CAMP_DONE) && !(Mission_sync_flags & MS_FLAG_SENT_LOAD) && !multi_endgame_ending()){
8228 send_netplayer_load_packet(NULL);
8229 Mission_sync_flags |= MS_FLAG_SENT_LOAD;
8231 if(!(Game_mode & GM_STANDALONE_SERVER)){
8232 multi_common_add_text(XSTR("Sent load packet\n",835),1);
8235 // load the mission myself, as soon as possible
8236 if(!Multi_mission_loaded){
8237 nprintf(("Network","Server loading mission..."));
8239 // update everyone about my status
8240 Net_player->state = NETPLAYER_STATE_MISSION_LOADING;
8241 send_netplayer_update_packet();
8243 game_start_mission();
8245 nprintf(("Network","Done\n"));
8246 Multi_mission_loaded = 1;
8248 // update everyone about my status
8249 Net_player->state = NETPLAYER_STATE_MISSION_LOADED;
8250 send_netplayer_update_packet();
8252 if(!(Game_mode & GM_STANDALONE_SERVER)){
8253 multi_common_add_text(XSTR("Loaded mission locally\n",836),1);
8258 // if everyone has loaded the mission, randomly assign players to ships
8259 if(multi_netplayer_state_check(NETPLAYER_STATE_MISSION_LOADED) && !(Mission_sync_flags & MS_FLAG_TS_SLOTS) && !multi_endgame_ending()){
8260 // call the team select function to assign players to their ships, wings, etc
8261 multi_ts_assign_players_all();
8262 send_netplayer_slot_packet();
8265 Mission_sync_flags |= MS_FLAG_TS_SLOTS;
8268 // if everyone has loaded the mission, move to the team select stage
8269 if(Sync_test && multi_netplayer_state_check(NETPLAYER_STATE_SLOT_ACK) && !(Mission_sync_flags & MS_FLAG_PUSHED_BRIEFING) && !multi_endgame_ending()){
8270 Netgame.game_state = NETGAME_STATE_BRIEFING;
8271 send_netgame_update_packet(); // this will push everyone into the next state
8273 // the standalone moves to his own wait state, whereas in the normal game mode, the server/host moves in to the
8274 // team select state
8275 if(Game_mode & GM_STANDALONE_SERVER){
8276 gameseq_post_event(GS_EVENT_MULTI_STD_WAIT);
8278 gameseq_post_event(GS_EVENT_START_GAME);
8281 Mission_sync_flags |= MS_FLAG_PUSHED_BRIEFING;
8283 if(!(Game_mode & GM_STANDALONE_SERVER)){
8284 multi_common_add_text(XSTR("Moving to team select\n",837),1);
8288 // clients should detect here if they are doing a file xfer and do error processing
8289 if((Net_player->state == NETPLAYER_STATE_MISSION_XFER) && (Net_player->s_info.xfer_handle != -1) && !multi_endgame_ending()){
8290 switch(multi_xfer_get_status(Net_player->s_info.xfer_handle)){
8291 // if it has successfully completed, set his ok flag
8292 case MULTI_XFER_SUCCESS :
8293 // release my xfer handle
8294 multi_xfer_release_handle(Net_player->s_info.xfer_handle);
8295 Net_player->s_info.xfer_handle = -1;
8298 // if it has failed or timed-out, kick the player
8299 case MULTI_XFER_TIMEDOUT:
8300 case MULTI_XFER_FAIL:
8301 // release my xfer handle
8302 multi_xfer_release_handle(Net_player->s_info.xfer_handle);
8303 Net_player->s_info.xfer_handle = -1;
8305 // leave the game qith an error code
8306 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_XFER_FAIL);
8313 if(!(Game_mode & GM_STANDALONE_SERVER)){
8315 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8316 if(Multi_sync_bitmap != -1){
8317 gr_set_bitmap(Multi_sync_bitmap);
8320 Multi_sync_window.draw();
8322 multi_sync_blit_screen_all();
8328 void multi_sync_pre_close()
8330 // at this point, we should shut down any file xfers...
8331 if(Net_player->s_info.xfer_handle != -1){
8332 nprintf(("Network","WARNING - killing file xfer while leaving mission sync state!!!\n"));
8334 multi_xfer_abort(Net_player->s_info.xfer_handle);
8335 Net_player->s_info.xfer_handle;
8339 void multi_sync_post_init()
8341 multi_reset_timestamps();
8343 Multi_state_timestamp = timestamp(0);
8346 ml_string(NOX("Performing post-briefing data sync"));
8348 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8349 multi_common_add_text(XSTR("Server performing sync\n",830),1);
8351 multi_common_add_text(XSTR("Client performing sync\n",838),1);
8354 // everyone should re-initialize these
8355 init_multiplayer_stats();
8357 // reset all sequencing info
8358 multi_oo_reset_sequencing();
8360 // if I am not the master of the game, then send the firing information for my ship
8362 if ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) ){
8363 send_firing_info_packet();
8366 // if I'm not a standalone server, load up the countdown stuff
8367 if(!(Game_mode & GM_STANDALONE_SERVER)){
8368 Multi_sync_countdown_anim = NULL;
8369 Multi_sync_countdown_instance = NULL;
8370 Multi_sync_countdown_anim = anim_load(Multi_sync_countdown_fname[gr_screen.res]);
8371 if(Multi_sync_countdown_anim == NULL){
8372 nprintf(("General","WARNING!, Could not load countdown animation %s!\n",Multi_sync_countdown_fname[gr_screen.res]));
8376 // create objects for all permanent observers
8377 multi_obs_level_init();
8379 // clear the game start countdown timer
8380 Multi_sync_countdown_timer = -1.0f;
8381 Multi_sync_countdown = -1;
8383 // if this is a team vs. team mission, mark all ship teams appropriately
8384 if(Netgame.type_flags & NG_TYPE_TEAM){
8385 multi_team_mark_all_ships();
8388 Mission_sync_flags = 0;
8389 Multi_sync_launch_pressed = 0;
8392 #define MULTI_POST_TIMESTAMP 7000
8394 extern int create_wings();
8396 void multi_sync_post_do()
8400 // only if the host is also the master should he be doing this (non-standalone situation)
8401 if ( Net_player->flags & NETINFO_FLAG_AM_MASTER ) {
8403 // once everyone gets to this screen, send them the ship classes of all ships.
8404 if(multi_netplayer_state_check(NETPLAYER_STATE_WAITING) && !(Mission_sync_flags & MS_FLAG_POST_DATA)) {
8405 // only the host should ever do this
8406 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8407 // at this point we want to delete all necessary ships, change all necessary ship classes, and set all weapons up
8408 multi_ts_create_wings();
8410 // update player ets settings
8411 for(idx=0;idx<MAX_PLAYERS;idx++){
8412 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].player->objnum != -1)){
8413 multi_server_update_player_weapons(&Net_players[idx],&Ships[Objects[Net_players[idx].player->objnum].instance]);
8418 // note that this is done a little differently for standalones and nonstandalones
8419 send_post_sync_data_packet();
8421 multi_common_add_text(XSTR("Sending post briefing block information\n",839),1);
8423 Mission_sync_flags |= MS_FLAG_POST_DATA;
8426 // send weapon slots data
8427 if(multi_netplayer_state_check(NETPLAYER_STATE_POST_DATA_ACK) && !(Mission_sync_flags & MS_FLAG_WSS_SLOTS)) {
8428 // note that this is done a little differently for standalones and nonstandalones
8429 if(Netgame.type_flags & NG_TYPE_TEAM){
8430 send_wss_slots_data_packet(0,0);
8431 send_wss_slots_data_packet(1,1);
8433 send_wss_slots_data_packet(0,1);
8436 multi_common_add_text(XSTR("Sending weapon slots information\n",840),1);
8438 Mission_sync_flags |= MS_FLAG_WSS_SLOTS;
8441 // once weapon information is received, send player settings info
8442 if ( multi_netplayer_state_check(NETPLAYER_STATE_WSS_ACK) && !(Mission_sync_flags & MS_FLAG_PSETTINGS)) {
8443 send_player_settings_packet();
8445 // server (specifically, the standalone), should set this here
8446 Net_player->state = NETPLAYER_STATE_SETTINGS_ACK;
8447 send_netplayer_update_packet();
8449 multi_common_add_text(XSTR("Sending player settings packets\n",841),1);
8451 Mission_sync_flags |= MS_FLAG_PSETTINGS;
8454 // check to see if the countdown timer has started and act appropriately
8455 if( Multi_sync_countdown_timer > -1.0f ) {
8457 // increment by frametime.
8458 Multi_sync_countdown_timer += flFrametime;
8460 // if the animation is not playing, start it
8461 if(!(Game_mode & GM_STANDALONE_SERVER) && (Multi_sync_countdown_instance == NULL) && (Multi_sync_countdown_anim != NULL)){
8462 anim_play_struct aps;
8464 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]);
8465 aps.screen_id = GS_STATE_MULTI_MISSION_SYNC;
8466 aps.framerate_independent = 1;
8468 Multi_sync_countdown_instance = anim_play(&aps);
8471 // if the next second has expired
8472 if( Multi_sync_countdown_timer >= 1.0f ) {
8474 Multi_sync_countdown--;
8475 Multi_sync_countdown_timer = 0.0f;
8477 // if the countdown has reached 0, launch the mission
8478 if(Multi_sync_countdown == 0){
8479 Multi_sync_countdown_timer = -1.0f;
8481 Multi_sync_launch_pressed = 0;
8482 multi_sync_launch();
8484 // otherwise send a countdown packet
8486 send_countdown_packet(Multi_sync_countdown);
8491 // jump into the mission myself
8492 if((Multi_sync_countdown == 0) && multi_netplayer_state_check(NETPLAYER_STATE_IN_MISSION)){
8493 if(!((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !Multi_sync_launch_pressed)){
8494 Net_player->state = NETPLAYER_STATE_IN_MISSION;
8495 Netgame.game_state = NETGAME_STATE_IN_MISSION;
8496 gameseq_post_event(GS_EVENT_ENTER_GAME);
8498 multi_common_add_text(XSTR("Moving into game\n",842),1);
8502 // maybe start the animation countdown
8503 if((Multi_sync_countdown >= 0) && (Multi_sync_countdown_instance == NULL) && (Multi_sync_countdown_anim != NULL)){
8504 anim_play_struct aps;
8506 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]);
8507 aps.screen_id = GS_STATE_MULTI_MISSION_SYNC;
8508 aps.framerate_independent = 1;
8510 Multi_sync_countdown_instance = anim_play(&aps);
8514 // host - specific stuff
8515 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8516 // create the launch button so the host can click
8517 if( Sync_test && multi_netplayer_state_check(NETPLAYER_STATE_SETTINGS_ACK) ){
8518 multi_sync_create_launch_button();
8523 if(!(Game_mode & GM_STANDALONE_SERVER)){
8525 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8526 if(Multi_sync_bitmap != -1){
8527 gr_set_bitmap(Multi_sync_bitmap);
8530 Multi_sync_window.draw();
8532 multi_sync_blit_screen_all();
8538 void multi_sync_post_close()
8542 // if I'm not a standalone server, unload up the countdown stuff
8543 if(!(Game_mode & GM_STANDALONE_SERVER)){
8544 // release all rendering animation instances (should only be 1)
8545 anim_release_all_instances(GS_STATE_MULTI_MISSION_SYNC);
8546 Multi_sync_countdown_instance = NULL;
8548 // free up the countdown animation
8549 if(Multi_sync_countdown_anim != NULL){
8550 anim_free(Multi_sync_countdown_anim);
8551 Multi_sync_countdown_anim = NULL;
8555 // all players should reset sequencing
8556 for(idx=0;idx<MAX_PLAYERS;idx++){
8557 if(Net_player->flags & NETINFO_FLAG_CONNECTED){
8558 Net_players[idx].client_cinfo_seq = 0;
8559 Net_players[idx].client_server_seq = 0;
8563 // multiplayer dogfight
8564 multi_df_level_pre_enter();
8566 // clients should clear obj_pair array and add pair for themselves
8568 if ( MULTIPLAYER_CLIENT ) {
8570 obj_add_pairs( OBJ_INDEX(Player_obj) );
8575 void multi_sync_display_name(char *name,int index,int np_index)
8577 char fit[CALLSIGN_LEN];
8579 // make sure the string actually fits
8582 // if we're in team vs. team mode
8583 if(Netgame.type_flags & NG_TYPE_TEAM){
8584 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]);
8586 // if this is the currently selected player, draw him highlighted
8587 if(np_index == Multi_sync_player_select){
8588 gr_set_color_fast(&Color_text_selected);
8590 gr_set_color_fast(&Color_text_normal);
8594 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);
8596 // blit his team icon
8598 if(Net_players[np_index].p_info.team == 0){
8599 // blit the team captain icon
8600 if(Net_players[np_index].flags & NETINFO_FLAG_TEAM_CAPTAIN){
8601 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
8602 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT]);
8603 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);
8606 // normal team member icon
8608 if(Multi_common_icons[MICON_TEAM0] != -1){
8609 gr_set_bitmap(Multi_common_icons[MICON_TEAM0]);
8610 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 else if(Net_players[np_index].p_info.team == 1){
8616 // blit the team captain icon
8617 if(Net_players[np_index].flags & NETINFO_FLAG_TEAM_CAPTAIN){
8618 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
8619 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT]);
8620 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);
8623 // normal team member icon
8625 if(Multi_common_icons[MICON_TEAM1] != -1){
8626 gr_set_bitmap(Multi_common_icons[MICON_TEAM1]);
8627 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);
8632 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]);
8634 // if this is the currently selected player, draw him highlighted
8635 if(np_index == Multi_sync_player_select){
8636 gr_set_color_fast(&Color_text_selected);
8638 gr_set_color_fast(&Color_text_normal);
8642 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);
8645 // maybe blit his CD status icon
8646 if((Net_players[np_index].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
8647 gr_set_bitmap(Multi_common_icons[MICON_CD]);
8648 gr_bitmap(Ms_status_coords[gr_screen.res][MS_X_COORD], Ms_status_coords[gr_screen.res][MS_Y_COORD] + (index * 10));
8652 void multi_sync_display_status(char *status,int index)
8656 // make sure the string actually fits
8657 strcpy(fit, status);
8658 gr_force_fit_string(fit, 250, Ms_status2_coords[gr_screen.res][MS_W_COORD] - 20);
8659 gr_set_color_fast(&Color_bright);
8660 gr_string(Ms_status2_coords[gr_screen.res][MS_X_COORD], Ms_status2_coords[gr_screen.res][MS_Y_COORD] + (index * 10), fit);
8663 void multi_sync_force_start_pre()
8666 int want_state = NETPLAYER_STATE_SLOT_ACK; // kick any players who are still in this state
8668 // go through the player list and boot anyone who isn't in the right state
8669 for(idx=0;idx<MAX_PLAYERS;idx++){
8670 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx]) && (Net_players[idx].state == want_state)){
8671 multi_kick_player(idx,0);
8676 void multi_sync_force_start_post()
8680 int num_kill_states;
8682 // determine the state we want all players in so that we can find those who are not in the state
8683 kill_state[0] = NETPLAYER_STATE_BRIEFING;
8684 kill_state[1] = NETPLAYER_STATE_SHIP_SELECT;
8685 kill_state[2] = NETPLAYER_STATE_WEAPON_SELECT;
8686 num_kill_states = 3;
8688 // go through the player list and boot anyone who isn't in the right state
8689 for(idx=0;idx<MAX_PLAYERS;idx++){
8690 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx])){
8691 // check against all kill state
8692 for(idx2 = 0;idx2<num_kill_states;idx2++){
8693 if(Net_players[idx].state == kill_state[idx2]){
8694 multi_kick_player(idx,0);
8702 void multi_sync_start_countdown()
8704 // don't allow repeat button presses
8705 if(Multi_sync_launch_pressed){
8709 Multi_sync_launch_pressed = 1;
8711 // if I'm the server, begin the countdown
8712 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8713 gamesnd_play_iface(SND_COMMIT_PRESSED);
8714 Multi_sync_countdown_timer = 0.0f;
8715 Multi_sync_countdown = MULTI_SYNC_COUNTDOWN_TIME;
8717 // send an initial countdown value
8718 send_countdown_packet(Multi_sync_countdown);
8720 // otherwise send the "start countdown" packet to the standalone
8722 Assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
8723 send_countdown_packet(-1);
8727 void multi_sync_launch()
8729 // don't allow repeat button presses
8730 if(Multi_sync_launch_pressed){
8734 Multi_sync_launch_pressed = 1;
8737 ml_printf(NOX("Entering mission %s"), Game_current_mission_filename);
8739 // tell everyone to jump into the mission
8740 send_jump_into_mission_packet();
8741 Multi_state_timestamp = timestamp(MULTI_POST_TIMESTAMP);
8743 // set the # of players at the start of the mission
8744 Multi_num_players_at_start = multi_num_players();
8745 nprintf(("Network","# of players at start of mission : %d\n", Multi_num_players_at_start));
8747 // initialize datarate limiting for all clients
8748 multi_oo_rate_init_all();
8750 multi_common_add_text(XSTR("Sending mission start packet\n",843),1);
8753 void multi_sync_create_launch_button()
8755 if (!Multi_launch_button_created) {
8756 // create the object
8757 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);
8759 // set the sound to play when highlighted
8760 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_highlight_action(common_play_highlight_sound);
8762 // set the ani for the button
8763 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_bmaps(Multi_sync_buttons[gr_screen.res][MS_LAUNCH].filename, 3, 0);
8766 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.link_hotspot(Multi_sync_buttons[gr_screen.res][MS_LAUNCH].hotspot);
8769 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_hotkey(KEY_CTRLED+KEY_ENTER);
8771 // create the text for the button
8772 Multi_sync_window.add_XSTR(&Multi_sync_text[gr_screen.res][MST_LAUNCH]);
8774 // increment the button count so we start checking this one
8775 Multi_sync_button_count++;
8777 Multi_launch_button_created = 1;
8781 void multi_sync_handle_plist()
8787 // if we don't have a currently selected player, select one
8788 if((Multi_sync_player_select < 0) || !MULTI_CONNECTED(Net_players[Multi_sync_player_select])){
8789 for(idx=0;idx<MAX_PLAYERS;idx++){
8790 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
8791 Multi_sync_player_select = idx;
8797 // check for button list presses
8798 if(Multi_sync_plist_button.pressed()){
8799 // get the y mouse coords
8800 Multi_sync_plist_button.get_mouse_pos(NULL,&my);
8802 // get the index of the item selected
8803 select_index = my / 10;
8805 // if the index is greater than the current # connections, do nothing
8806 if(select_index > (multi_num_connections() - 1)){
8810 // translate into an absolute Net_players[] index (get the Nth net player)
8811 Multi_sync_player_select = -1;
8812 for(idx=0;idx<MAX_PLAYERS;idx++){
8813 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
8817 // if we've found the item we're looking for
8818 if(select_index < 0){
8819 Multi_sync_player_select = idx;
8824 // if for some bizarre reason, this is an invalid player, unselect him and wait for the next interation
8826 if((Multi_sync_player_select >= 0) && (!MULTI_CONNECTED(Net_players[Multi_sync_player_select]) || MULTI_STANDALONE(Net_players[Multi_sync_player_select])) ){
8827 Multi_sync_player_select = -1;
8833 // -------------------------------------------------------------------------------------------------------------
8835 // MULTIPLAYER DEBRIEF SCREEN
8838 // other relevant data
8839 int Multi_debrief_accept_hit;
8840 int Multi_debrief_replay_hit;
8842 // set if the server has left the game
8843 int Multi_debrief_server_left = 0;
8845 // if we've reported on TvT status all players are in the debrief
8846 int Multi_debrief_reported_tvt = 0;
8848 // whether stats are being accepted
8849 // -1 == no decision yet
8852 int Multi_debrief_stats_accept_code = -1;
8854 int Multi_debrief_server_framecount = 0;
8856 float Multi_debrief_time = 0.0f;
8857 float Multi_debrief_resend_time = 10.0f;
8859 void multi_debrief_init()
8863 Multi_debrief_time = 0.0f;
8864 Multi_debrief_resend_time = 10.0f;
8866 // do this to notify the standalone or the normal server that we're in the debrief state and ready to receive packets
8867 if (!(Net_player->flags & NETINFO_FLAG_AM_MASTER)) {
8868 Net_player->state = NETPLAYER_STATE_DEBRIEF;
8869 send_netplayer_update_packet();
8872 // unflag some stuff
8873 for(idx=0;idx<MAX_PLAYERS;idx++){
8874 if(MULTI_CONNECTED(Net_players[idx])){
8875 Net_players[idx].flags &= ~(NETINFO_FLAG_RESPAWNING | NETINFO_FLAG_LIMBO | NETINFO_FLAG_WARPING_OUT);
8879 // if text input mode is active, clear it
8880 multi_msg_text_flush();
8882 // the server has not left yet
8883 Multi_debrief_server_left = 0;
8885 // have not hit accept or replay yet
8886 Multi_debrief_accept_hit = 0;
8887 Multi_debrief_replay_hit = 0;
8889 // stats have not been accepted yet
8890 Multi_debrief_stats_accept_code = -1;
8892 // mark stats as not being store yet
8893 Netgame.flags &= ~(NG_FLAG_STORED_MT_STATS);
8895 // no report on TvT yet
8896 Multi_debrief_reported_tvt = 0;
8898 Multi_debrief_server_framecount = 0;
8901 void multi_debrief_do_frame()
8903 Multi_debrief_time += flFrametime;
8905 // set the netgame state to be debriefing when appropriate
8906 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)){
8907 Netgame.game_state = NETGAME_STATE_DEBRIEF;
8908 send_netgame_update_packet();
8911 // evaluate all server stuff
8912 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8913 multi_debrief_server_process();
8917 void multi_debrief_close()
8919 if ( MULTIPLAYER_CLIENT && (Netgame.game_state == NETGAME_STATE_MISSION_SYNC) ){
8920 gamesnd_play_iface( SND_COMMIT_PRESSED );
8924 // handle optional mission loop
8925 void multi_maybe_set_mission_loop()
8927 int cur = Campaign.current_mission;
8928 if (Campaign.missions[cur].has_mission_loop) {
8929 Assert(Campaign.loop_mission != CAMPAIGN_LOOP_MISSION_UNINITIALIZED);
8931 bool require_repeat_mission = (Campaign.current_mission == Campaign.next_mission);
8933 // check for (1) mission loop available, (2) dont have to repeat last mission
8934 if ( (Campaign.missions[cur].has_mission_loop && (Campaign.loop_mission != -1)) && !require_repeat_mission ) {
8937 debrief_assemble_optional_mission_popup_text(buffer, Campaign.missions[cur].mission_loop_desc);
8939 int choice = popup(0 , 2, POPUP_NO, POPUP_YES, buffer);
8941 Campaign.loop_enabled = 1;
8942 Campaign.next_mission = Campaign.loop_mission;
8947 // handle all cases for when the accept key is hit in a multiplayer debriefing
8948 void multi_debrief_accept_hit()
8950 // if we already accepted, do nothing
8951 // but he may need to hit accept again after the server has left the game, so allow this
8952 if(Multi_debrief_accept_hit){
8956 // mark this so that we don't hit it again
8957 Multi_debrief_accept_hit = 1;
8959 gamesnd_play_iface(SND_COMMIT_PRESSED);
8961 // if the server has left the game, always just end the game.
8962 if(Multi_debrief_server_left){
8963 if(!multi_quit_game(PROMPT_ALL)){
8964 Multi_debrief_server_left = 1;
8965 Multi_debrief_accept_hit = 0;
8967 Multi_debrief_server_left = 0;
8970 // query the host and see if he wants to accept stats
8971 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8972 // if we're on a tracker game, he gets no choice for storing stats
8973 if(MULTI_IS_TRACKER_GAME){
8974 multi_maybe_set_mission_loop();
8976 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));
8978 // evaluate the result
8983 Multi_debrief_accept_hit = 0;
8986 // set the accept code to be "not accepting"
8988 multi_debrief_stats_toss();
8989 multi_maybe_set_mission_loop();
8992 // accept the stats and continue
8994 multi_debrief_stats_accept();
8995 multi_maybe_set_mission_loop();
9001 // set my netplayer state to be "debrief_accept", and be done with it
9002 Net_player->state = NETPLAYER_STATE_DEBRIEF_ACCEPT;
9003 send_netplayer_update_packet();
9007 // handle all cases for when the escape key is hit in a multiplayer debriefing
9008 void multi_debrief_esc_hit()
9012 // if the server has left
9013 if(Multi_debrief_server_left){
9014 multi_quit_game(PROMPT_ALL);
9019 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
9020 // if the stats have already been accepted
9021 if((Multi_debrief_stats_accept_code != -1) || (MULTI_IS_TRACKER_GAME)){
9022 multi_quit_game(PROMPT_HOST);
9024 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));
9026 // evaluate the result
9033 // set the accept code to be "not accepting"
9035 multi_debrief_stats_toss();
9036 multi_quit_game(PROMPT_NONE);
9039 // accept the stats and continue
9041 multi_debrief_stats_accept();
9042 multi_quit_game(PROMPT_NONE);
9047 // if the stats haven't been accepted yet, or this is a tracker game
9048 if((Multi_debrief_stats_accept_code == -1) && !(MULTI_IS_TRACKER_GAME)){
9049 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));
9051 // evaluate the result
9053 multi_quit_game(PROMPT_NONE);
9056 // otherwise go through the normal endgame channels
9058 multi_quit_game(PROMPT_ALL);
9063 void multi_debrief_replay_hit()
9065 // only the host should ever get here
9066 Assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
9068 // if the button was already pressed, do nothing
9069 if(Multi_debrief_accept_hit){
9073 // same as hittin the except button except no stats are kept
9074 Multi_debrief_accept_hit = 1;
9076 // mark myself as being in the replay state so we know what to do next
9077 Net_player->state = NETPLAYER_STATE_DEBRIEF_REPLAY;
9078 send_netplayer_update_packet();
9081 // call this when the server has left and we would otherwise be saying "contact lost with server
9082 void multi_debrief_server_left()
9085 Multi_debrief_server_left = 1;
9087 // undo any "accept" hit so that clients can hit accept again to leave
9088 Multi_debrief_accept_hit = 0;
9091 void multi_debrief_stats_accept()
9093 // don't do anything if we've already accepted
9094 if(Multi_debrief_stats_accept_code != -1){
9098 Multi_debrief_stats_accept_code = 1;
9100 // if we're the host, and we're on a standalone, tell the standalone to begin the stats storing process
9101 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) || (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
9102 // send a packet to the players telling them to store their stats
9103 send_store_stats_packet(1);
9106 // add a chat line saying "stats have been accepted"
9107 multi_display_chat_msg(XSTR("<stats have been accepted>",850),0,0);
9110 ml_string(NOX("Stats stored"));
9113 void multi_debrief_stats_toss()
9115 // don't do anything if we've already accepted
9116 if(Multi_debrief_stats_accept_code != -1){
9120 Multi_debrief_stats_accept_code = 0;
9122 // if we're the host, and we're on a standalone, tell everyone to "toss" stats
9123 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) || (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
9124 // send a packet to the players telling them to store their stats
9125 send_store_stats_packet(0);
9128 // add a chat line saying "stats have been accepted"
9129 multi_display_chat_msg(XSTR("<stats have been tossed>",851),0,0);
9132 ml_string(NOX("Stats tossed"));
9135 int multi_debrief_stats_accept_code()
9137 return Multi_debrief_stats_accept_code;
9140 void multi_debrief_server_process()
9143 int player_status,other_status;
9145 Multi_debrief_server_framecount++;
9147 // if we're > 10 seconds into the debrief and not everyone is here, try warping everyone out again
9148 if((Multi_debrief_time >= Multi_debrief_resend_time) && !multi_netplayer_state_check3(NETPLAYER_STATE_DEBRIEF, NETPLAYER_STATE_DEBRIEF_ACCEPT, NETPLAYER_STATE_DEBRIEF_REPLAY, 1)){
9149 // find all players who are not in the debrief state and hit them with the endgame packet
9150 for(idx=0; idx<MAX_PLAYERS; idx++){
9151 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)) ){
9152 send_endgame_packet(&Net_players[idx]);
9157 Multi_debrief_resend_time += 7.0f;
9160 // evaluate the status of all players in the game (0 == not ready, 1 == ready to continue, 2 == ready to replay)
9163 // check all players
9164 for(idx=0;idx<MAX_PLAYERS;idx++){
9165 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_HOST(Net_players[idx])){
9166 if(Net_players[idx].state != NETPLAYER_STATE_DEBRIEF_ACCEPT){
9173 // if we haven't already reported TvT results
9174 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)){
9175 multi_team_report();
9176 Multi_debrief_reported_tvt = 1;
9179 // if all other players are good to go, check the host
9181 // if he is ready to continue
9182 if(Netgame.host->state == NETPLAYER_STATE_DEBRIEF_ACCEPT){
9185 // if he wants to replay the mission
9186 else if(Netgame.host->state == NETPLAYER_STATE_DEBRIEF_REPLAY){
9189 // if he is not ready
9194 // if all players are _not_ good to go
9199 // if we're in the debriefing state in a campaign mode, process accordingly
9200 if(Netgame.campaign_mode == MP_CAMPAIGN){
9201 multi_campaign_do_debrief(player_status);
9203 // otherwise process as normal (looking for all players to be ready to go to the next mission
9205 if(player_status == 1){
9206 multi_flush_mission_stuff();
9208 // set the netgame state to be forming and continue
9209 Netgame.game_state = NETGAME_STATE_FORMING;
9210 send_netgame_update_packet();
9212 // move to the proper state
9213 if(Game_mode & GM_STANDALONE_SERVER){
9214 gameseq_post_event(GS_EVENT_STANDALONE_MAIN);
9216 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
9219 multi_reset_timestamps();
9220 } else if(player_status == 2){
9221 multi_flush_mission_stuff();
9223 // tell everyone to move into the pre-briefing sync state
9224 Netgame.game_state = NETGAME_STATE_MISSION_SYNC;
9225 send_netgame_update_packet();
9227 // move back to the mission sync screen for the same mission again
9228 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
9229 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
9231 multi_reset_timestamps();
9237 // -------------------------------------------------------------------------------------------------------------
9239 // MULTIPLAYER PASSWORD POPUP
9244 static char *Multi_pwd_bitmap_fname[GR_NUM_RESOLUTIONS] = {
9245 "Password", // GR_640
9246 "2_Password" // GR_1024
9249 static char *Multi_pwd_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
9250 "Password-M", // GR_640
9251 "2_Password-M" // GR_1024
9256 // constants for coordinate lookup
9257 #define MPWD_X_COORD 0
9258 #define MPWD_Y_COORD 1
9259 #define MPWD_W_COORD 2
9260 #define MPWD_H_COORD 3
9263 #define MULTI_PWD_NUM_BUTTONS 2
9264 #define MPWD_CANCEL 0
9265 #define MPWD_COMMIT 1
9267 // password area defs
9268 int Mpwd_coords[GR_NUM_RESOLUTIONS][4] = {
9277 UI_WINDOW Multi_pwd_window; // the window object for the join screen
9278 UI_INPUTBOX Multi_pwd_passwd; // for Netgame.passwd
9279 int Multi_pwd_bitmap; // the background bitmap
9280 int Multi_passwd_background = -1;
9281 int Multi_passwd_done = -1;
9282 int Multi_passwd_running = 0;
9285 ui_button_info Multi_pwd_buttons[GR_NUM_RESOLUTIONS][MULTI_PWD_NUM_BUTTONS] = {
9287 ui_button_info("PWB_00", 411, 151, 405, 141, 0),
9288 ui_button_info("PWB_01", 460, 151, 465, 141, 1),
9291 ui_button_info("2_PWB_00", 659, 242, 649, 225, 0),
9292 ui_button_info("2_PWB_01", 737, 242, 736, 225, 1),
9297 #define MULTI_PWD_NUM_TEXT 3
9298 UI_XSTR Multi_pwd_text[GR_NUM_RESOLUTIONS][MULTI_PWD_NUM_TEXT] = {
9300 { "Cancel", 387, 400, 141, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[0][MPWD_CANCEL].button},
9301 { "Commit", 1062, 455, 141, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[0][MPWD_COMMIT].button},
9302 { "Enter Password", 1332, 149, 92, UI_XSTR_COLOR_GREEN, -1, NULL},
9305 { "Cancel", 387, 649, 225, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[1][MPWD_CANCEL].button},
9306 { "Commit", 1062, 736, 225, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[1][MPWD_COMMIT].button},
9307 { "Enter Password", 1332, 239, 148, UI_XSTR_COLOR_GREEN, -1, NULL},
9311 // initialize all graphics, etc
9312 void multi_passwd_init()
9316 // store the background as it currently is
9317 Multi_passwd_background = gr_save_screen();
9319 // create the interface window
9320 Multi_pwd_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
9321 Multi_pwd_window.set_mask_bmap(Multi_pwd_bitmap_mask_fname[gr_screen.res]);
9323 // load the background bitmap
9324 Multi_pwd_bitmap = bm_load(Multi_pwd_bitmap_fname[gr_screen.res]);
9325 if(Multi_pwd_bitmap < 0){
9326 // we failed to load the bitmap - this is very bad
9330 // create the interface buttons
9331 for(idx=0; idx<MULTI_PWD_NUM_BUTTONS; idx++){
9332 // create the object
9333 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);
9335 // set the sound to play when highlighted
9336 Multi_pwd_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
9338 // set the ani for the button
9339 Multi_pwd_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pwd_buttons[gr_screen.res][idx].filename);
9342 Multi_pwd_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pwd_buttons[gr_screen.res][idx].hotspot);
9346 for(idx=0; idx<MULTI_PWD_NUM_TEXT; idx++){
9347 Multi_pwd_window.add_XSTR(&Multi_pwd_text[gr_screen.res][idx]);
9350 // create the password input box
9351 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);
9352 Multi_pwd_passwd.set_focus();
9354 // link the enter key to ACCEPT
9355 Multi_pwd_buttons[gr_screen.res][MPWD_COMMIT].button.set_hotkey(KEY_ENTER);
9357 Multi_passwd_done = -1;
9358 Multi_passwd_running = 1;
9361 // close down all graphics, etc
9362 void multi_passwd_close()
9364 // unload any bitmaps
9365 bm_release(Multi_pwd_bitmap);
9367 // destroy the UI_WINDOW
9368 Multi_pwd_window.destroy();
9370 // free up the saved background screen
9371 if(Multi_passwd_background >= 0){
9372 gr_free_screen(Multi_passwd_background);
9373 Multi_passwd_background = -1;
9376 Multi_passwd_running = 0;
9379 // process any button pressed
9380 void multi_passwd_process_buttons()
9382 // if the accept button was pressed
9383 if(Multi_pwd_buttons[gr_screen.res][MPWD_COMMIT].button.pressed()){
9384 gamesnd_play_iface(SND_USER_SELECT);
9385 Multi_passwd_done = 1;
9388 // if the cancel button was pressed
9389 if(Multi_pwd_buttons[gr_screen.res][MPWD_CANCEL].button.pressed()){
9390 gamesnd_play_iface(SND_USER_SELECT);
9391 Multi_passwd_done = 0;
9395 // run the passwd popup
9396 void multi_passwd_do(char *passwd)
9400 while(Multi_passwd_done == -1){
9401 // set frametime and run background stuff
9402 game_set_frametime(-1);
9403 game_do_state_common(gameseq_get_state());
9405 k = Multi_pwd_window.process();
9407 // process any keypresses
9410 // set this to indicate the user has cancelled for one reason or another
9411 Multi_passwd_done = 0;
9415 // if the input box text has changed
9416 if(Multi_pwd_passwd.changed()){
9418 Multi_pwd_passwd.get_text(passwd);
9421 // process any button pressed
9422 multi_passwd_process_buttons();
9424 // draw the background, etc
9427 if(Multi_passwd_background >= 0){
9428 gr_restore_screen(Multi_passwd_background);
9430 gr_set_bitmap(Multi_pwd_bitmap);
9432 Multi_pwd_window.draw();
9439 // bring up the password string popup, fill in passwd (return 1 if accept was pressed, 0 if cancel was pressed)
9440 int multi_passwd_popup(char *passwd)
9442 // if the popup is already running for some reason, don't do anything
9443 if(Multi_passwd_running){
9447 // initialize all graphics
9448 multi_passwd_init();
9451 multi_passwd_do(passwd);
9453 // shut everything down
9454 multi_passwd_close();
9456 return Multi_passwd_done;