2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on
10 * $Logfile: /Freespace2/code/Network/MultiUI.cpp $
15 * C file for all the UI controls of the mulitiplayer screens
18 * Revision 1.9 2004/07/04 11:39:06 taylor
19 * fix missing debrief text, crash on exit, path separator's, warning fixes, no GR_SOFT
21 * Revision 1.8 2003/05/25 02:30:43 taylor
24 * Revision 1.7 2002/06/09 04:41:24 relnev
25 * added copyright header
27 * Revision 1.6 2002/06/02 06:02:59 relnev
30 * Revision 1.5 2002/06/02 00:31:35 relnev
31 * implemented osregistry
33 * Revision 1.4 2002/06/01 07:12:33 relnev
34 * a few NDEBUG updates.
36 * removed a few warnings.
38 * Revision 1.3 2002/05/26 20:49:54 theoddone33
41 * Revision 1.2 2002/05/07 03:16:47 theoddone33
42 * The Great Newline Fix
44 * Revision 1.1.1.1 2002/05/03 03:28:10 root
48 * 94 6/16/00 3:16p Jefff
49 * sim of the year dvd version changes, a few german soty localization
52 * 93 10/14/99 2:51p Jefff
55 * 92 10/13/99 3:50p Jefff
56 * fixed unnumbered XSTRs
58 * 91 9/15/99 1:45a Dave
59 * Don't init joystick on standalone. Fixed campaign mode on standalone.
60 * Fixed no-score-report problem in TvT
62 * 90 9/14/99 12:51a Jefff
65 * 89 9/13/99 4:52p Dave
68 * 88 9/13/99 11:30a Dave
69 * Added checkboxes and functionality for disabling PXO banners as well as
70 * disabling d3d zbuffer biasing.
72 * 87 9/12/99 10:06p Jefff
73 * changed instances of "Squad War" to "SquadWar"
75 * 86 9/03/99 1:32a Dave
76 * CD checking by act. Added support to play 2 cutscenes in a row
77 * seamlessly. Fixed super low level cfile bug related to files in the
78 * root directory of a CD. Added cheat code to set campaign mission # in
81 * 85 9/01/99 10:49p Dave
82 * Added nice SquadWar checkbox to the client join wait screen.
84 * 84 8/30/99 2:49p Jefff
86 * 83 8/26/99 8:49p Jefff
87 * Updated medals screen and about everything that ever touches medals in
88 * one way or another. Sheesh.
90 * 82 8/25/99 4:38p Dave
91 * Updated PXO stuff. Make squad war report stuff much more nicely.
93 * 81 8/20/99 2:09p Dave
96 * 80 8/20/99 10:06a Jefff
97 * removed closed/rstricted buttons from multi start screen
99 * 79 8/18/99 11:30a Jefff
101 * 78 8/18/99 10:38a Jefff
103 * 77 8/16/99 4:06p Dave
104 * Big honking checkin.
106 * 76 8/16/99 1:08p Jefff
107 * added sounds to a few controls, made input boxes lose focus on ENTER
109 * 75 8/16/99 9:52a Jefff
110 * fixed bitmap loading on buttons in multi-sync screen
112 * 74 8/11/99 5:54p Dave
113 * Fixed collision problem. Fixed standalone ghost problem.
115 * 73 8/10/99 4:35p Jefff
116 * fixed hi-res coords
118 * 72 8/06/99 12:29a Dave
119 * Multiple bug fixes.
121 * 71 8/05/99 3:13p Jasenw
122 * tweaked some text placement coords.
124 * 70 8/04/99 1:38p Jefff
125 * moved some text in multi join wait
127 * 69 8/03/99 12:45p Dave
130 * 68 7/25/99 5:17p Jefff
131 * campaign descriptions show up on multicreate screen
133 * 67 7/20/99 1:49p Dave
134 * Peter Drake build. Fixed some release build warnings.
136 * 66 7/19/99 2:13p Dave
137 * Added some new strings for Heiko.
139 * 65 7/15/99 9:20a Andsager
140 * FS2_DEMO initial checkin
142 * 64 7/08/99 10:53a Dave
143 * New multiplayer interpolation scheme. Not 100% done yet, but still
144 * better than the old way.
146 * 63 6/30/99 10:49a Jasenw
147 * Fixed coords for new launch countdown ani
149 * 62 6/29/99 7:39p Dave
150 * Lots of small bug fixes.
152 * 61 6/25/99 11:59a Dave
153 * Multi options screen.
155 * 60 6/09/99 2:17p Dave
156 * Fixed up pleasewait bitmap rendering.
158 * 59 6/05/99 3:42p Dave
159 * New multi sync screen.
161 * 58 6/01/99 6:07p Dave
162 * New loading/pause/please wait bar.
164 * 57 5/21/99 6:45p Dave
165 * Sped up ui loading a bit. Sped up localization disk access stuff. Multi
166 * start game screen, multi password, and multi pxo-help screen.
168 * 56 5/06/99 11:10a Dave
169 * Fixed coord on multi create screen.
171 * 55 5/04/99 6:38p Dave
172 * Finished multi join-wait screen.
174 * 54 5/04/99 5:20p Dave
175 * Fixed up multiplayer join screen and host options screen. Should both
178 * 53 5/03/99 11:04p Dave
179 * Most of the way done with the multi join screen.
181 * 52 5/03/99 8:32p Dave
182 * New version of multi host options screen.
184 * 51 4/29/99 2:15p Neilk
185 * slider2 code got modified; changed parameters for create
187 * 50 4/25/99 3:02p Dave
188 * Build defines for the E3 build.
190 * 49 4/21/99 6:15p Dave
191 * Did some serious housecleaning in the beam code. Made it ready to go
192 * for anti-fighter "pulse" weapons. Fixed collision pair creation. Added
193 * a handy macro for recalculating collision pairs for a given object.
195 * 48 4/16/99 5:27p Neilk
196 * added slider support and hir res for multi_create
198 * 47 4/14/99 6:37p Dave
199 * Fixed scroll button bug on host create screen.
201 * 46 4/14/99 5:28p Dave
204 * 45 4/12/99 10:07p Dave
205 * Made network startup more forgiving. Added checkmarks to dogfight
206 * screen for players who hit commit.
208 * 44 4/09/99 2:21p Dave
209 * Multiplayer beta stuff. CD checking.
211 * 43 4/08/99 1:28p Dave
212 * Small bug fixes for refresh button on the multi create screen.
214 * 42 4/08/99 11:55a Neilk
215 * Converted Multi_Create to new artwork (just lowres)
217 * 41 4/08/99 2:10a Dave
218 * Numerous bug fixes for the beta. Added builtin mission info for the
221 * 40 3/20/99 3:48p Andsager
222 * Do mission_loop stuff for PXO
224 * 39 3/10/99 6:50p Dave
225 * Changed the way we buffer packets for all clients. Optimized turret
226 * fired packets. Did some weapon firing optimizations.
228 * 38 3/09/99 6:24p Dave
229 * More work on object update revamping. Identified several sources of
230 * unnecessary bandwidth.
232 * 37 3/08/99 7:03p Dave
233 * First run of new object update system. Looks very promising.
235 * 36 2/25/99 4:19p Dave
236 * Added multiplayer_beta defines. Added cd_check define. Fixed a few
237 * release build warnings. Added more data to the squad war request and
240 * 35 2/24/99 3:26p Anoop
241 * Make sure the host is the only guy who bashes skill level for TvT.
243 * 34 2/24/99 2:25p Dave
244 * Fixed up chatbox bugs. Made squad war reporting better. Fixed a respawn
245 * bug for dogfight more.
247 * 33 2/23/99 2:29p Dave
248 * First run of oldschool dogfight mode.
250 * 32 2/17/99 2:11p Dave
251 * First full run of squad war. All freespace and tracker side stuff
254 * 31 2/12/99 6:16p Dave
255 * Pre-mission Squad War code is 95% done.
257 * 30 2/11/99 3:08p Dave
258 * PXO refresh button. Very preliminary squad war support.
260 * 29 2/08/99 5:07p Dave
261 * FS2 chat server support. FS2 specific validated missions.
263 * 28 2/04/99 6:29p Dave
264 * First full working rev of FS2 PXO support. Fixed Glide lighting
267 * 27 1/30/99 5:08p Dave
268 * More new hi-res stuff.Support for nice D3D textures.
270 * 26 1/29/99 2:08a Dave
271 * Fixed beam weapon collisions with players. Reduced size of scoring
272 * struct for multiplayer. Disabled PXO.
274 * 25 1/15/99 2:36p Neilk
275 * fixed multi_jw coordinates
277 * 24 1/13/99 7:19p Neilk
278 * Converted Mission Brief, Barracks, Synch to high res support
280 * 23 1/12/99 7:17p Neilk
282 * 22 1/12/99 5:45p Dave
283 * Moved weapon pipeline in multiplayer to almost exclusively client side.
284 * Very good results. Bandwidth goes down, playability goes up for crappy
285 * connections. Fixed object update problem for ship subsystems.
287 * 21 1/12/99 4:07a Dave
288 * Put in barracks code support for selecting squad logos. Properly
289 * distribute squad logos in a multiplayer game.
291 * 20 1/11/99 7:19p Neilk
292 * Converted multi_join interface to support multiple resolutions
294 * 19 12/18/98 1:13a Dave
295 * Rough 1024x768 support for Direct3D. Proper detection and usage through
298 * 18 12/17/98 4:50p Andsager
299 * Added debrief_assemble_optional_mission_popup_text() for single and
302 * 17 12/14/98 12:13p Dave
303 * Spiffed up xfer system a bit. Put in support for squad logo file xfer.
306 * 16 12/10/98 10:19a Andsager
307 * Fix mission loop assert
309 * 15 12/10/98 9:59a Andsager
310 * Fix some bugs with mission loops
312 * 14 12/09/98 1:56p Andsager
313 * Initial checkin of mission loop
315 * 13 12/03/98 5:22p Dave
316 * Ported over Freespace 1 multiplayer ships.tbl and weapons.tbl
319 * 12 11/30/98 1:07p Dave
320 * 16 bit conversion, first run.
322 * 11 11/20/98 11:16a Dave
323 * Fixed up IPX support a bit. Making sure that switching modes and
324 * loading/saving pilot files maintains proper state.
326 * 10 11/19/98 4:57p Dave
327 * Ignore PXO option if IPX is selected.
329 * 9 11/19/98 4:19p Dave
330 * Put IPX sockets back in psnet. Consolidated all multiplayer config
333 * 8 11/19/98 8:04a Dave
334 * Full support for D3-style reliable sockets. Revamped packet lag/loss
335 * system, made it receiver side and at the lowest possible level.
337 * 7 11/17/98 11:12a Dave
338 * Removed player identification by address. Now assign explicit id #'s.
340 * 6 10/19/98 11:15a Dave
341 * Changed requirements for stats storing in PXO mode.
343 * 5 10/16/98 9:40a Andsager
344 * Remove ".h" files from model.h
346 * 4 10/13/98 9:29a Dave
347 * Started neatening up freespace.h. Many variables renamed and
348 * reorganized. Added AlphaColors.[h,cpp]
350 * 3 10/07/98 6:27p Dave
351 * Globalized mission and campaign file extensions. Removed Silent Threat
352 * special code. Moved \cache \players and \multidata into the \data
355 * 2 10/07/98 10:53a Dave
358 * 1 10/07/98 10:50a Dave
360 * 333 10/02/98 3:22p Allender
361 * fix up the -connect option and fix the -port option
363 * 332 9/17/98 9:26p Dave
364 * Externalized new string.
366 * 331 9/17/98 3:08p Dave
367 * PXO to non-pxo game warning popup. Player icon stuff in create and join
368 * game screens. Upped server count refresh time in PXO to 35 secs (from
371 * 330 9/17/98 9:43a Allender
372 * removed an Assert that Dave called bogus.
374 * 329 9/16/98 6:54p Dave
375 * Upped max sexpression nodes to 1800 (from 1600). Changed FRED to sort
376 * the ship list box. Added code so that tracker stats are not stored with
379 * 328 9/15/98 7:24p Dave
380 * Minor UI changes. Localized bunch of new text.
382 * 327 9/15/98 4:03p Dave
383 * Changed readyroom and multi screens to display "st" icon for all
384 * missions with mission disk content (not necessarily just those that
385 * come with Silent Threat).
387 * 326 9/15/98 11:44a Dave
388 * Renamed builtin ships and wepaons appropriately in FRED. Put in scoring
389 * scale factors. Fixed standalone filtering of MD missions to non-MD
392 * 325 9/13/98 9:36p Dave
393 * Support for new info icons for multiplayer missions (from-volition,
394 * valid, mission disk, etc).
396 * 324 9/11/98 4:14p Dave
397 * Fixed file checksumming of < file_size. Put in more verbose kicking and
398 * PXO stats store reporting.
400 * 323 9/10/98 1:17p Dave
401 * Put in code to flag missions and campaigns as being MD or not in Fred
402 * and Freespace. Put in multiplayer support for filtering out MD
403 * missions. Put in multiplayer popups for warning of non-valid missions.
405 * 322 9/04/98 3:51p Dave
406 * Put in validated mission updating and application during stats
409 * 321 8/31/98 2:06p Dave
410 * Make cfile sort the ordering or vp files. Added support/checks for
411 * recognizing "mission disk" players.
413 * 320 8/21/98 1:15p Dave
414 * Put in log system hooks in useful places.
416 * 319 8/20/98 5:31p Dave
417 * Put in handy multiplayer logfile system. Now need to put in useful
418 * applications of it all over the code.
420 * 318 8/12/98 4:53p Dave
421 * Put in 32 bit checksumming for PXO missions. No validation on the
422 * actual tracker yet, though.
424 * 317 8/07/98 10:40a Allender
425 * new command line flags for starting netgames. Only starting currently
426 * works, and PXO isn't implemented yet
428 * 316 7/24/98 9:27a Dave
429 * Tidied up endgame sequencing by removing several old flags and
430 * standardizing _all_ endgame stuff with a single function call.
432 * 315 7/14/98 10:04a Allender
433 * fixed the countdown code to not be reliant on timer_get_fixed_seconds
435 * 314 7/10/98 5:04p Dave
436 * Fix connection speed bug on standalone server.
438 * 313 7/09/98 6:01p Dave
439 * Firsts full version of PXO updater. Put in stub for displaying
442 * 312 7/07/98 2:49p Dave
445 * 311 6/30/98 2:17p Dave
446 * Revised object update system. Removed updates for all weapons. Put
447 * button info back into control info packet.
449 * 310 6/13/98 9:32p Mike
450 * Kill last character in file which caused "Find in Files" to report the
451 * file as "not a text file."
458 #include <winsock.h> // for inet_addr()
460 #include <sys/types.h>
461 #include <sys/socket.h>
462 #include <netinet/in.h>
463 #include <arpa/inet.h>
468 #include "multiutil.h"
469 #include "multimsgs.h"
475 #include "gamesequence.h"
476 #include "freespace.h"
477 #include "contexthelp.h"
482 #include "missionshipchoice.h"
483 #include "multi_xfer.h"
485 #include "stand_gui.h"
486 #include "linklist.h"
487 #include "multiteamselect.h"
488 #include "missioncampaign.h"
495 #include "missiondebrief.h"
496 #include "multi_ingame.h"
497 #include "multi_kick.h"
498 #include "multi_data.h"
499 #include "multi_campaign.h"
500 #include "multi_team.h"
501 #include "multi_pinfo.h"
502 #include "multi_observer.h"
503 #include "multi_voice.h"
504 #include "multi_endgame.h"
505 #include "managepilot.h"
508 #include "objcollide.h"
510 #include "multi_pmsg.h"
511 #include "multi_obj.h"
512 #include "multi_log.h"
513 #include "alphacolors.h"
514 #include "animplay.h"
515 #include "multi_dogfight.h"
516 #include "missionpause.h"
518 // -------------------------------------------------------------------------------------------------------------
520 // MULTIPLAYER COMMON interface controls
523 // the common text info box stuff. This is lifted almost directly from Alans briefing code (minus the spiffy colored, scrolling
525 int Multi_common_text_coords[GR_NUM_RESOLUTIONS][4] = {
538 int Multi_common_text_max_display[GR_NUM_RESOLUTIONS] = {
547 #define MULTI_COMMON_TEXT_META_CHAR '$'
548 #define MULTI_COMMON_TEXT_MAX_LINE_LENGTH 100
549 #define MULTI_COMMON_TEXT_MAX_LINES 20
550 #define MULTI_COMMON_MAX_TEXT (MULTI_COMMON_TEXT_MAX_LINES * MULTI_COMMON_TEXT_MAX_LINE_LENGTH)
552 char Multi_common_all_text[MULTI_COMMON_MAX_TEXT];
553 char Multi_common_text[MULTI_COMMON_TEXT_MAX_LINES][MULTI_COMMON_TEXT_MAX_LINE_LENGTH];
555 int Multi_common_top_text_line = -1; // where to start displaying from
556 int Multi_common_num_text_lines = 0; // how many lines we have
558 void multi_common_scroll_text_up();
559 void multi_common_scroll_text_down();
560 void multi_common_move_to_bottom();
561 void multi_common_render_text();
562 void multi_common_split_text();
564 #define MAX_IP_STRING 255 // maximum length for ip string
566 void multi_common_scroll_text_up()
568 Multi_common_top_text_line--;
569 if ( Multi_common_top_text_line < 0 ) {
570 Multi_common_top_text_line = 0;
571 if ( !mouse_down(MOUSE_LEFT_BUTTON) )
572 gamesnd_play_iface(SND_GENERAL_FAIL);
575 gamesnd_play_iface(SND_SCROLL);
579 void multi_common_scroll_text_down()
581 Multi_common_top_text_line++;
582 if ( (Multi_common_num_text_lines - Multi_common_top_text_line) < Multi_common_text_max_display[gr_screen.res] ) {
583 Multi_common_top_text_line--;
584 if ( !mouse_down(MOUSE_LEFT_BUTTON) ){
585 gamesnd_play_iface(SND_GENERAL_FAIL);
588 gamesnd_play_iface(SND_SCROLL);
592 void multi_common_move_to_bottom()
594 // if there's nowhere to scroll down, do nothing
595 if(Multi_common_num_text_lines <= Multi_common_text_max_display[gr_screen.res]){
599 Multi_common_top_text_line = Multi_common_num_text_lines - Multi_common_text_max_display[gr_screen.res];
602 void multi_common_set_text(char *str,int auto_scroll)
605 // store the entire string as well
606 if(strlen(str) > MULTI_COMMON_MAX_TEXT){
609 strcpy(Multi_common_all_text,str);
612 // split the whole thing up
613 multi_common_split_text();
615 // scroll to the bottom if we're supposed to
617 multi_common_move_to_bottom();
621 void multi_common_add_text(char *str,int auto_scroll)
624 // store the entire string as well
625 if((strlen(str) + strlen(Multi_common_all_text)) > MULTI_COMMON_MAX_TEXT){
628 strcat(Multi_common_all_text,str);
631 // split the whole thing up
632 multi_common_split_text();
634 // scroll to the bottom if we're supposed to
636 multi_common_move_to_bottom();
640 void multi_common_split_text()
643 int n_chars[MAX_BRIEF_LINES];
644 char *p_str[MAX_BRIEF_LINES];
646 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);
647 Assert(n_lines != -1);
649 for ( i = 0; i < n_lines; i++ ) {
650 Assert(n_chars[i] < MULTI_COMMON_TEXT_MAX_LINE_LENGTH);
651 strncpy(Multi_common_text[i], p_str[i], n_chars[i]);
652 Multi_common_text[i][n_chars[i]] = 0;
653 drop_leading_white_space(Multi_common_text[i]);
656 Multi_common_top_text_line = 0;
657 Multi_common_num_text_lines = n_lines;
660 void multi_common_render_text()
662 int i, fh, line_count;
664 fh = gr_get_font_height();
667 gr_set_color_fast(&Color_text_normal);
668 for ( i = Multi_common_top_text_line; i < Multi_common_num_text_lines; i++ ) {
669 if ( line_count >= Multi_common_text_max_display[gr_screen.res] ){
672 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]);
676 if ( (Multi_common_num_text_lines - Multi_common_top_text_line) > Multi_common_text_max_display[gr_screen.res] ) {
677 gr_set_color_fast(&Color_bright_red);
678 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));
682 // common notification messaging stuff
683 #define MULTI_COMMON_NOTIFY_TIME 3500
684 int Multi_common_join_y[GR_NUM_RESOLUTIONS] = {
688 int Multi_common_create_y[GR_NUM_RESOLUTIONS] = {
693 int Multi_common_jw_y[GR_NUM_RESOLUTIONS] = {
698 int Multi_common_msg_y[GR_NUM_RESOLUTIONS] = {
703 char Multi_common_notify_text[200];
704 int Multi_common_notify_stamp;
706 void multi_common_notify_init()
708 strcpy(Multi_common_notify_text,"");
709 Multi_common_notify_stamp = -1;
712 // add a notification string, drawing appropriately depending on the state/screen we're in
713 void multi_common_add_notify(char *str)
716 strcpy(Multi_common_notify_text,str);
717 Multi_common_notify_stamp = timestamp(MULTI_COMMON_NOTIFY_TIME);
721 // process/display notification messages
722 void multi_common_notify_do()
724 if(Multi_common_notify_stamp != -1){
725 if(timestamp_elapsed(Multi_common_notify_stamp)){
726 Multi_common_notify_stamp = -1;
729 gr_get_string_size(&w,&h,Multi_common_notify_text);
730 gr_set_color_fast(&Color_white);
732 // determine where it should be placed based upon which screen we're on
734 switch(gameseq_get_state()){
735 case GS_STATE_MULTI_JOIN_GAME :
736 y = Multi_common_join_y[gr_screen.res];
738 case GS_STATE_MULTI_HOST_SETUP :
739 y = Multi_common_create_y[gr_screen.res];
741 case GS_STATE_MULTI_CLIENT_SETUP :
742 y = Multi_common_jw_y[gr_screen.res];
744 case GS_STATE_MULTI_START_GAME :
745 y = Multi_common_msg_y[gr_screen.res];
749 gr_string((gr_screen.max_w - w)/2, y, Multi_common_notify_text);
756 int Multi_common_icons[MULTI_NUM_COMMON_ICONS];
758 char *Multi_common_icon_names[MULTI_NUM_COMMON_ICONS] = {
759 "DotRed", // voice denied
760 "DotGreen", // voice recording
761 "OvalGreen", // team 0
762 "OvalGreen01", // team 0 select
764 "OvalRed01", // team 1 select
765 "mp_coop", // coop mission
766 "mp_teams", // TvT mission
767 "mp_furball", // furball mission
768 "icon-volition", // volition mission
769 "icon-valid", // mission is valid
774 "icon-silent" // SilentThreat
778 // width and height of the icons
779 int Multi_common_icon_dims[MULTI_NUM_COMMON_ICONS][2] = {
780 {11, 11}, // voice denied
781 {11, 11}, // voice recording
783 {11, 11}, // team 0 select
785 {11, 11}, // team 1 select
788 {18, 11}, // mp furball
789 {9, 9}, // volition mission
790 {8, 8}, // mission is valid
795 {16, 7} // silent threat
799 void multi_load_common_icons()
804 for(idx=0; idx<MULTI_NUM_COMMON_ICONS; idx++){
805 Multi_common_icons[idx] = -1;
806 Multi_common_icons[idx] = bm_load(Multi_common_icon_names[idx]);
810 void multi_unload_common_icons()
815 for(idx=0; idx<MULTI_NUM_COMMON_ICONS; idx++){
816 if(Multi_common_icons[idx] != -1){
817 bm_unload(Multi_common_icons[idx]);
818 Multi_common_icons[idx] = -1;
823 // display any relevant voice status icons
824 void multi_common_voice_display_status()
826 switch(multi_voice_status()){
827 // i have been denied the voice token
828 case MULTI_VOICE_STATUS_DENIED:
829 if(Multi_common_icons[MICON_VOICE_DENIED] != -1){
830 gr_set_bitmap(Multi_common_icons[MICON_VOICE_DENIED]);
835 // i am currently recording
836 case MULTI_VOICE_STATUS_RECORDING:
837 if(Multi_common_icons[MICON_VOICE_RECORDING] != -1){
838 gr_set_bitmap(Multi_common_icons[MICON_VOICE_RECORDING]);
843 // i am currently playing back sound
844 case MULTI_VOICE_STATUS_PLAYING:
847 // the system is currently idle
848 case MULTI_VOICE_STATUS_IDLE:
854 // palette initialization stuff
855 #define MULTI_COMMON_PALETTE_FNAME "InterfacePalette"
858 int Multi_common_interface_palette = -1;
860 void multi_common_load_palette();
861 void multi_common_set_palette();
862 void multi_common_unload_palette();
864 // load in the palette if it doesn't already exist
865 void multi_common_load_palette()
867 if(Multi_common_interface_palette != -1){
871 Multi_common_interface_palette = bm_load(MULTI_COMMON_PALETTE_FNAME);
872 if(Multi_common_interface_palette == -1){
873 nprintf(("Network","Error loading multiplayer common palette!\n"));
877 // set the common palette to be the active one
878 void multi_common_set_palette()
880 // if the palette is not loaded yet, do so now
881 if(Multi_common_interface_palette == -1){
882 multi_common_load_palette();
885 if(Multi_common_interface_palette != -1){
886 #ifndef HARDWARE_ONLY
887 palette_use_bm_palette(Multi_common_interface_palette);
892 // unload the bitmap palette
893 void multi_common_unload_palette()
895 if(Multi_common_interface_palette != -1){
896 bm_unload(Multi_common_interface_palette);
897 Multi_common_interface_palette = -1;
901 void multi_common_verify_cd()
904 // otherwise, call the freespace function to determine if we have a cd
907 if((find_freespace_cd(FS_CDROM_VOLUME_1) >= 0) || (find_freespace_cd(FS_CDROM_VOLUME_2) >= 0) ){
909 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) ){
919 // -------------------------------------------------------------------------------------------------------------
921 // MULTIPLAYER JOIN SCREEN
924 #define MULTI_JOIN_NUM_BUTTONS 11
928 #define MULTI_JOIN_PALETTE "InterfacePalette"
930 static char *Multi_join_bitmap_fname[GR_NUM_RESOLUTIONS] = {
931 "MultiJoin", // GR_640
932 "2_MultiJoin" // GR_1024
935 static char *Multi_join_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
936 "MultiJoin-M", // GR_640
937 "2_MultiJoin-M" // GR_1024
943 char *Mj_slider_name[GR_NUM_RESOLUTIONS] = {
947 int Mj_slider_coords[GR_NUM_RESOLUTIONS][4] = {
958 #define MJ_SCROLL_UP 0
959 #define MJ_SCROLL_DOWN 1
961 #define MJ_SCROLL_INFO_UP 3
962 #define MJ_SCROLL_INFO_DOWN 4
963 #define MJ_JOIN_OBSERVER 5
964 #define MJ_START_GAME 6
970 // uses MULTI_JOIN_REFRESH_TIME as its timestamp
971 int Multi_join_glr_stamp;
973 #define MULTI_JOIN_PING_TIME 15000 // how often we ping all the known servers
974 int Multi_join_ping_stamp;
975 UI_WINDOW Multi_join_window; // the window object for the join screen
976 UI_BUTTON Multi_join_select_button; // for selecting list items
978 UI_SLIDER2 Multi_join_slider; // handy dandy slider
980 int Multi_join_bitmap; // the background bitmap
982 ui_button_info Multi_join_buttons[GR_NUM_RESOLUTIONS][MULTI_JOIN_NUM_BUTTONS] = {
985 ui_button_info("MJ_00", 0, 85, -1, -1, 0),
986 ui_button_info("MJ_01", 0, 125, -1, -1, 1),
987 ui_button_info("MJ_03", 20, 324, -1, -1, 3),
988 ui_button_info("MJ_04", 0, 399, -1, -1, 4),
989 ui_button_info("MJ_05", 0, 436, -1, -1, 5),
990 ui_button_info("MJ_15", 450, 323, -1, -1, 15),
991 ui_button_info("MJ_10", 519, 323, -1, -1, 10),
992 ui_button_info("MJ_06", 574, 323, -1, -1, 6),
993 ui_button_info("MJ_08", 470, 427, -1, -1, 8),
994 ui_button_info("MJ_09", 448, 454, -1, -1, 9),
995 ui_button_info("MJ_07", 563, 411, -1, -1, 7),
997 ui_button_info( "MJ_00", 1, 57, -1, -1, 0 ), // scroll up
998 ui_button_info( "MJ_02", 1, 297, -1, -1, 2 ), // scroll down
999 ui_button_info( "MJ_03", 10, 338, 65, 364, 3 ), // refresh
1000 ui_button_info( "MJ_04", 1, 405, -1, -1, 4 ), // scroll info up
1001 ui_button_info( "MJ_05", 1, 446, -1, -1, 5 ), // scroll info down
1002 ui_button_info( "MJ_06", 489, 339, -1, -1, 6 ), // join as observer
1003 ui_button_info( "MJ_07", 538, 339, -1, -1, 7 ), // create game
1004 ui_button_info( "MJ_08", 583, 339, 588, 376, 8 ), // cancel
1005 ui_button_info( "MJ_09", 534, 426, -1, -1, 9 ), // help
1006 ui_button_info( "MJ_10", 534, 454, -1, -1, 10 ), // options
1007 ui_button_info( "MJ_11", 571, 426, 589, 416, 11 ), // join
1011 ui_button_info( "2_MJ_00", 2, 92, -1, -1, 0 ), // scroll up
1012 ui_button_info( "2_MJ_02", 2, 475, -1, -1, 2 ), // scroll down
1013 ui_button_info( "2_MJ_03", 16, 541, 104, 582, 3 ), // refresh
1014 ui_button_info( "2_MJ_04", 2, 648, -1, -1, 4 ), // scroll info up
1015 ui_button_info( "2_MJ_05", 2, 713, -1, -1, 5 ), // scroll info down
1016 ui_button_info( "2_MJ_06", 783, 542, -1, -1, 6 ), // join as observer
1017 ui_button_info( "2_MJ_07", 861, 542, -1, -1, 7 ), // create game
1018 ui_button_info( "2_MJ_08", 933, 542, 588, 376, 8 ), // cancel
1019 ui_button_info( "2_MJ_09", 854, 681, -1, -1, 9 ), // help
1020 ui_button_info( "2_MJ_10", 854, 727, -1, -1, 10 ), // options
1021 ui_button_info( "2_MJ_11", 914, 681, 937, 668, 11 ), // join
1026 #define MULTI_JOIN_NUM_TEXT 0
1028 #define MULTI_JOIN_NUM_TEXT 13
1031 UI_XSTR Multi_join_text[GR_NUM_RESOLUTIONS][MULTI_JOIN_NUM_TEXT] = {
1033 // not needed for FS1
1035 {"Refresh", 1299, 65, 364, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_REFRESH].button},
1036 {"Join as", 1300, 476, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_JOIN_OBSERVER].button},
1037 {"Observer", 1301, 467, 385, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_JOIN_OBSERVER].button},
1038 {"Create", 1408, 535, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_START_GAME].button},
1039 {"Game", 1302, 541, 385, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_START_GAME].button},
1040 {"Cancel", 387, 588, 376, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[0][MJ_CANCEL].button},
1041 {"Help", 928, 479, 436, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_HELP].button},
1042 {"Options", 1036, 479, 460, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_OPTIONS].button},
1043 {"Join", 1303, 589, 416, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[0][MJ_ACCEPT].button},
1044 {"Status", 1304, 37, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
1045 {"Server", 1305, 116, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
1046 {"Players", 1306, 471, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
1047 {"Ping", 1307, 555, 37, UI_XSTR_COLOR_GREEN, -1, NULL}
1051 // not needed for FS1
1053 {"Refresh", 1299, 104, 582, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_REFRESH].button},
1054 {"Join as", 1300, 783, 602, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_JOIN_OBSERVER].button},
1055 {"Observer", 1301, 774, 611, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_JOIN_OBSERVER].button},
1056 {"Create", 1408, 868, 602, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_START_GAME].button},
1057 {"Game", 1302, 872, 611, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_START_GAME].button},
1058 {"Cancel", 387, 941, 602, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[1][MJ_CANCEL].button},
1059 {"Help", 928, 782, 699, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_HELP].button},
1060 {"Options", 1036, 782, 736, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_OPTIONS].button},
1061 {"Join", 1303, 937, 668, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[1][MJ_ACCEPT].button},
1062 {"Status", 1304, 60, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
1063 {"Server", 1305, 186, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
1064 {"Players", 1306, 753, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
1065 {"Ping", 1307, 888, 60, UI_XSTR_COLOR_GREEN, -1, NULL}
1070 // constants for coordinate look ups
1071 #define MJ_X_COORD 0
1072 #define MJ_Y_COORD 1
1073 #define MJ_W_COORD 2
1074 #define MJ_H_COORD 3
1076 #define MULTI_JOIN_SENT_WAIT 10000 // wait this long since a join was sent to allow another
1077 int Multi_join_sent_stamp;
1079 // game information text areas
1080 int Mj_max_game_items[GR_NUM_RESOLUTIONS] = {
1085 int Mj_list_y[GR_NUM_RESOLUTIONS] = {
1090 int Mj_status_coords[GR_NUM_RESOLUTIONS][4] = {
1099 int Mj_game_icon_coords[GR_NUM_RESOLUTIONS][3] = {
1108 int Mj_speed_coords[GR_NUM_RESOLUTIONS][4] = {
1117 int Mj_game_name_coords[GR_NUM_RESOLUTIONS][4] = {
1126 int Mj_players_coords[GR_NUM_RESOLUTIONS][4] = {
1135 int Mj_ping_coords[GR_NUM_RESOLUTIONS][4] = {
1144 // game speed labels
1145 #define MJ_NUM_SPEED_LABELS 5
1146 char *Multi_join_speed_labels[MJ_NUM_SPEED_LABELS] = {
1153 color *Multi_join_speed_colors[MJ_NUM_SPEED_LABELS] = {
1156 &Color_bright_green,
1157 &Color_bright_green,
1161 int Mj_cd_coords[GR_NUM_RESOLUTIONS] = {
1166 // extents of the entire boundable game info region
1167 // NOTE : these numbers are completely empirical
1168 #define MJ_PING_GREEN 160
1169 #define MJ_PING_YELLOW 300
1171 int Mj_list_area_coords[GR_NUM_RESOLUTIONS][4] = {
1180 // PXO channel filter
1181 #define MJ_PXO_FILTER_Y 0
1183 // special chars to indicate various status modes for servers
1184 #define MJ_CHAR_STANDALONE "*"
1185 #define MJ_CHAR_CAMPAIGN "c"
1188 // various interface indices
1189 int Multi_join_list_start; // where to start displaying from
1190 active_game *Multi_join_list_start_item; // a pointer to the corresponding active_game
1191 int Multi_join_list_selected; // which item we have selected
1192 active_game *Multi_join_selected_item; // a pointer to the corresponding active_game
1194 // use this macro to modify the list start
1195 #define MJ_LIST_START_INC() do { Multi_join_list_start++; } while(0);
1196 #define MJ_LIST_START_DEC() do { Multi_join_list_start--; } while(0);
1197 #define MJ_LIST_START_SET(vl) do { Multi_join_list_start = vl; } while(0);
1199 // if we should be sending a join request at the end of the frame
1200 int Multi_join_should_send = -1;
1202 // master tracker details
1203 int Multi_join_frame_count; // keep a count of frames displayed
1204 int Multi_join_mt_tried_verify; // already tried verifying the pilot with the tracker
1206 // data stuff for auto joining a game
1207 #define MULTI_AUTOJOIN_JOIN_STAMP 2000
1208 #define MULTI_AUTOJOIN_QUERY_STAMP 2000
1210 int Multi_did_autojoin;
1211 net_addr Multi_autojoin_addr;
1212 int Multi_autojoin_join_stamp;
1213 int Multi_autojoin_query_stamp;
1216 join_request Multi_join_request;
1218 // LOCAL function definitions
1219 void multi_join_check_buttons();
1220 void multi_join_button_pressed(int n);
1221 void multi_join_display_games();
1222 void multi_join_blit_game_status(active_game *game, int y);
1223 void multi_join_load_tcp_addrs();
1224 void multi_join_do_netstuff();
1225 void multi_join_ping_all();
1226 void multi_join_process_select();
1227 void multi_join_list_scroll_up();
1228 void multi_join_list_scroll_down();
1229 void multi_join_list_page_up();
1230 void multi_join_list_page_down();
1231 active_game *multi_join_get_game(int n);
1232 void multi_join_cull_timeouts();
1233 void multi_join_handle_item_cull(active_game *item, int item_index);
1234 void multi_join_send_join_request(int as_observer);
1235 void multi_join_create_game();
1236 void multi_join_blit_top_stuff();
1237 int multi_join_maybe_warn();
1238 int multi_join_warn_pxo();
1239 void multi_join_blit_protocol();
1243 active_game ag, *newitem;;
1246 dc_get_arg(ARG_INT);
1247 for(idx=0; idx<Dc_arg_int; idx++){
1248 // stuff some fake info
1249 memset(&ag, 0, sizeof(active_game));
1250 sprintf(ag.name, "Game %d", idx);
1251 ag.version = MULTI_FS_SERVER_VERSION;
1252 ag.comp_version = MULTI_FS_SERVER_VERSION;
1253 ag.server_addr.addr[0] = (char)idx;
1254 ag.flags = (AG_FLAG_COOP | AG_FLAG_FORMING | AG_FLAG_STANDALONE);
1257 newitem = multi_update_active_games(&ag);
1259 // timestamp it so we get random timeouts
1260 if(newitem != NULL){
1261 // newitem->heard_from_timer = timestamp((int)frand_range(500.0f, 10000.0f));
1266 void multi_join_notify_new_game()
1269 // reset the # of items
1270 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);
1271 Multi_join_slider.force_currentItem(Multi_join_list_start);
1275 int multi_join_autojoin_do()
1277 // if we have an active game on the list, then return a positive value so that we
1278 // can join the game
1279 if ( Active_game_head && (Active_game_count > 0) ) {
1280 Multi_join_selected_item = Active_game_head;
1284 // send out a server_query again
1285 if ( timestamp_elapsed(Multi_autojoin_query_stamp) ) {
1286 send_server_query(&Multi_autojoin_addr);
1287 Multi_autojoin_query_stamp = timestamp(MULTI_AUTOJOIN_QUERY_STAMP);
1293 void multi_join_game_init()
1297 // do the multiplayer init stuff - multi_level_init() now does all net_player zeroing.
1298 // setup various multiplayer things
1299 Assert( Game_mode & GM_MULTIPLAYER );
1300 Assert( Net_player != NULL );
1302 switch (Multi_options_g.protocol) {
1304 ADDRESS_LENGTH = IPX_ADDRESS_LENGTH;
1305 PORT_LENGTH = IPX_PORT_LENGTH;
1309 ADDRESS_LENGTH = IP_ADDRESS_LENGTH;
1310 PORT_LENGTH = IP_PORT_LENGTH;
1319 memset( &Netgame, 0, sizeof(Netgame) );
1322 Net_player->flags |= NETINFO_FLAG_DO_NETWORKING;
1323 Net_player->player = Player;
1324 memcpy(&Net_player->p_info.addr,&Psnet_my_addr,sizeof(net_addr));
1326 // check for the existence of a CD
1327 multi_common_verify_cd();
1329 // load my local netplayer options
1330 multi_options_local_load(&Net_player->p_info.options, Net_player);
1336 common_set_interface_palette(MULTI_JOIN_PALETTE);
1339 // destroy any chatbox contents which previously existed (from another game)
1342 // create the interface window
1343 Multi_join_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
1344 Multi_join_window.set_mask_bmap(Multi_join_bitmap_mask_fname[gr_screen.res]);
1346 // load the background bitmap
1347 Multi_join_bitmap = bm_load(Multi_join_bitmap_fname[gr_screen.res]);
1348 if(Multi_join_bitmap < 0){
1349 // we failed to load the bitmap - this is very bad
1353 // intialize the endgame system
1354 multi_endgame_init();
1356 // initialize the common notification messaging
1357 multi_common_notify_init();
1359 // initialize the common text area
1360 multi_common_set_text("");
1362 // load and use the common interface palette
1363 multi_common_load_palette();
1364 multi_common_set_palette();
1366 // load the help overlay
1367 help_overlay_load(MULTI_JOIN_OVERLAY);
1368 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1370 // do TCP and VMT specific initialization
1371 if(Multi_options_g.protocol == NET_TCP){
1372 // if this is a TCP (non tracker) game, we'll load up our default address list right now
1373 multi_join_load_tcp_addrs();
1376 // initialize any and all timestamps
1377 Multi_join_glr_stamp = -1;
1378 Multi_join_ping_stamp = -1;
1379 Multi_join_sent_stamp = -1;
1381 // reset frame count
1382 Multi_join_frame_count = 0;
1384 // haven't tried to verify on the tracker yet.
1385 Multi_join_mt_tried_verify = 0;
1387 // clear our all game lists to save hassles
1388 multi_join_clear_game_list();
1390 // create the interface buttons
1391 for(idx=0; idx<MULTI_JOIN_NUM_BUTTONS; idx++){
1392 // create the object
1393 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);
1395 // set the sound to play when highlighted
1396 Multi_join_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
1398 // set the ani for the button
1399 Multi_join_buttons[gr_screen.res][idx].button.set_bmaps(Multi_join_buttons[gr_screen.res][idx].filename);
1402 Multi_join_buttons[gr_screen.res][idx].button.link_hotspot(Multi_join_buttons[gr_screen.res][idx].hotspot);
1406 for(idx=0; idx<MULTI_JOIN_NUM_TEXT; idx++){
1407 Multi_join_window.add_XSTR(&Multi_join_text[gr_screen.res][idx]);
1410 Multi_join_should_send = -1;
1412 // close any previously open chatbox
1415 // create the list item select button
1416 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);
1417 Multi_join_select_button.hide();
1421 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);
1424 // if starting a network game, then go to the create game screen
1425 if ( Cmdline_start_netgame ) {
1426 multi_join_create_game();
1427 } else if ( Cmdline_connect_addr != NULL ) {
1432 // joining a game. Send a join request to the given IP address, and wait for the return.
1433 memset( &Multi_autojoin_addr, 0, sizeof(net_addr) );
1434 Multi_autojoin_addr.type = NET_TCP;
1436 // create the address, looking out for port number at the end
1437 port_num = DEFAULT_GAME_PORT;
1438 p = strrchr(Cmdline_connect_addr, ':');
1442 port_num = (short)atoi(p);
1444 ip_addr = inet_addr(Cmdline_connect_addr);
1445 memcpy(Multi_autojoin_addr.addr, &ip_addr, 4);
1446 Multi_autojoin_addr.port = port_num;
1448 send_server_query(&Multi_autojoin_addr);
1449 Multi_autojoin_query_stamp = timestamp(MULTI_AUTOJOIN_QUERY_STAMP);
1450 Multi_did_autojoin = 0;
1454 void multi_join_clear_game_list()
1457 Multi_join_list_selected = -1;
1458 Multi_join_selected_item = NULL;
1459 MJ_LIST_START_SET(-1);
1460 Multi_join_list_start_item = NULL;
1462 // free up the active game list
1463 multi_free_active_games();
1465 // initialize the active game list
1466 Active_game_head = NULL;
1467 Active_game_count = 0;
1470 void multi_join_game_do_frame()
1472 // check the status of our reliable socket. If not valid, popup error and return to main menu
1473 // I put this code here to avoid nasty gameseq issues with states. Also, we will have nice
1474 // background for the popup
1475 if ( !psnet_rel_check() ) {
1476 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));
1477 gameseq_post_event(GS_EVENT_MAIN_MENU);
1481 // return here since we will be moving to the next stage anyway -- I don't want to see the backgrounds of
1482 // all the screens for < 1 second for every screen we automatically move to.
1483 if ( Cmdline_start_netgame ) {
1487 // when joining a network game, wait for the server query to come back, and then join the game
1488 if ( Cmdline_connect_addr != NULL ) {
1491 if ( !Multi_did_autojoin ) {
1492 rval = popup_till_condition(multi_join_autojoin_do, XSTR("&Cancel", 779), XSTR("Joining netgame", 1500) );
1494 // cancel was hit. Send the user back to the main hall
1495 gameseq_post_event(GS_EVENT_MAIN_MENU);
1496 Cmdline_connect_addr = NULL; // reset this value.
1499 // when we get here, we have the data -- join the game.
1500 multi_join_send_join_request(0);
1501 Multi_autojoin_join_stamp = timestamp(MULTI_AUTOJOIN_JOIN_STAMP);
1502 Multi_did_autojoin = 1;
1505 if ( timestamp_elapsed(Multi_autojoin_join_stamp) ) {
1506 multi_join_send_join_request(0);
1507 Multi_autojoin_join_stamp = timestamp(MULTI_AUTOJOIN_JOIN_STAMP);
1513 // reset the should send var
1514 Multi_join_should_send = -1;
1516 int k = Multi_join_window.process();
1518 // process any keypresses
1521 if(help_overlay_active(MULTI_JOIN_OVERLAY)){
1522 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1524 gameseq_post_event(GS_EVENT_MAIN_MENU);
1525 gamesnd_play_iface(SND_USER_SELECT);
1529 // page up the game list
1531 multi_join_list_page_up();
1533 Multi_join_slider.force_currentItem(Multi_join_list_start);
1538 multi_pinfo_popup(Net_player);
1541 // page down the game list
1543 multi_join_list_page_down();
1545 Multi_join_slider.force_currentItem(Multi_join_list_start);
1549 // send out a ping-all
1551 multi_join_ping_all();
1552 Multi_join_ping_stamp = timestamp(MULTI_JOIN_PING_TIME);
1555 // shortcut to start a game
1557 multi_join_create_game();
1560 // scroll the game list up
1562 multi_join_list_scroll_up();
1564 Multi_join_slider.force_currentItem(Multi_join_list_start);
1568 // scroll the game list down
1570 multi_join_list_scroll_down();
1572 Multi_join_slider.force_currentItem(Multi_join_list_start);
1577 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
1578 help_overlay_set_state(MULTI_JOIN_OVERLAY, 0);
1581 // do any network related stuff
1582 multi_join_do_netstuff();
1584 // process any button clicks
1585 multi_join_check_buttons();
1587 // process any list selection stuff
1588 multi_join_process_select();
1590 // draw the background, etc
1592 GR_MAYBE_CLEAR_RES(Multi_join_bitmap);
1593 if(Multi_join_bitmap != -1){
1594 gr_set_bitmap(Multi_join_bitmap);
1597 Multi_join_window.draw();
1599 // display the active games
1600 multi_join_display_games();
1602 // display any text in the info area
1603 multi_common_render_text();
1605 // display any pending notification messages
1606 multi_common_notify_do();
1608 // blit the CD icon and any PXO filter stuff
1609 multi_join_blit_top_stuff();
1611 // draw the help overlay
1612 help_overlay_maybe_blit(MULTI_JOIN_OVERLAY);
1617 // if we are supposed to be sending a join request
1618 if(Multi_join_should_send != -1){
1619 multi_join_send_join_request(Multi_join_should_send);
1621 Multi_join_should_send = -1;
1623 // increment the frame count
1624 Multi_join_frame_count++;
1627 void multi_join_game_close()
1629 // unload any bitmaps
1630 if(!bm_unload(Multi_join_bitmap)){
1631 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_join_bitmap_fname[gr_screen.res]));
1634 // unload the help overlay
1635 help_overlay_unload(MULTI_JOIN_OVERLAY);
1637 // free up the active game list
1638 multi_free_active_games();
1640 // destroy the UI_WINDOW
1641 Multi_join_window.destroy();
1644 common_free_interface_palette();
1648 void multi_join_check_buttons()
1651 for(idx=0;idx<MULTI_JOIN_NUM_BUTTONS;idx++){
1652 // we only really need to check for one button pressed at a time, so we can break after
1654 if(Multi_join_buttons[gr_screen.res][idx].button.pressed()){
1655 multi_join_button_pressed(idx);
1661 void multi_join_button_pressed(int n)
1665 // if we're player PXO, go back there
1666 gameseq_post_event(GS_EVENT_MAIN_MENU);
1667 gamesnd_play_iface(SND_USER_SELECT);
1670 if(Active_game_count <= 0){
1671 multi_common_add_notify(XSTR("No games found!",757));
1672 gamesnd_play_iface(SND_GENERAL_FAIL);
1673 } else if(Multi_join_list_selected == -1){
1674 multi_common_add_notify(XSTR("No game selected!",758));
1675 gamesnd_play_iface(SND_GENERAL_FAIL);
1676 } else if((Multi_join_sent_stamp != -1) && !timestamp_elapsed(Multi_join_sent_stamp)){
1677 multi_common_add_notify(XSTR("Still waiting on previous join request!",759));
1678 gamesnd_play_iface(SND_GENERAL_FAIL);
1680 // otherwise, if he's already played PXO games, warn him
1682 if(Player->flags & PLAYER_FLAGS_HAS_PLAYED_PXO){
1683 if(!multi_join_warn_pxo()){
1689 // send the join request here
1690 Assert(Multi_join_selected_item != NULL);
1692 // send a join request packet
1693 Multi_join_should_send = 0;
1695 gamesnd_play_iface(SND_COMMIT_PRESSED);
1701 if(!help_overlay_active(MULTI_JOIN_OVERLAY)){
1702 help_overlay_set_state(MULTI_JOIN_OVERLAY,1);
1704 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1708 // scroll the game list up
1710 multi_join_list_scroll_up();
1712 Multi_join_slider.force_currentItem(Multi_join_list_start);
1716 // scroll the game list down
1717 case MJ_SCROLL_DOWN:
1718 multi_join_list_scroll_down();
1720 Multi_join_slider.force_currentItem(Multi_join_list_start);
1724 // scroll the info text box up
1725 case MJ_SCROLL_INFO_UP:
1726 multi_common_scroll_text_up();
1729 // scroll the info text box down
1730 case MJ_SCROLL_INFO_DOWN:
1731 multi_common_scroll_text_down();
1734 // go to the options screen
1736 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
1739 // go to the start game screen
1741 multi_join_create_game();
1744 // refresh the game/server list
1746 gamesnd_play_iface(SND_USER_SELECT);
1747 broadcast_game_query();
1750 // join a game as an observer
1751 case MJ_JOIN_OBSERVER:
1752 if(Active_game_count <= 0){
1753 multi_common_add_notify(XSTR("No games found!",757));
1754 gamesnd_play_iface(SND_GENERAL_FAIL);
1755 } else if(Multi_join_list_selected == -1){
1756 multi_common_add_notify(XSTR("No game selected!",758));
1757 gamesnd_play_iface(SND_GENERAL_FAIL);
1758 } else if((Multi_join_sent_stamp != -1) && !timestamp_elapsed(Multi_join_sent_stamp)){
1759 multi_common_add_notify(XSTR("Still waiting on previous join request!",759));
1760 gamesnd_play_iface(SND_GENERAL_FAIL);
1762 // send the join request here
1763 Assert(Multi_join_selected_item != NULL);
1765 Multi_join_should_send = 1;
1767 gamesnd_play_iface(SND_COMMIT_PRESSED);
1772 multi_common_add_notify(XSTR("Not implemented yet!",760));
1773 gamesnd_play_iface(SND_GENERAL_FAIL);
1778 // display all relevant info for active games
1779 void multi_join_display_games()
1781 active_game *moveup = Multi_join_list_start_item;
1785 int y_start = Mj_list_y[gr_screen.res];
1790 // blit the game status (including text and type icon)
1791 multi_join_blit_game_status(moveup,y_start);
1793 // get the connection type
1794 con_type = (moveup->flags & AG_FLAG_CONNECTION_SPEED_MASK) >> AG_FLAG_CONNECTION_BIT;
1795 if((con_type > 4) || (con_type < 0)){
1799 // display the connection speed
1801 strcpy(str, Multi_join_speed_labels[con_type]);
1802 gr_set_color_fast(Multi_join_speed_colors[con_type]);
1803 gr_string(Mj_speed_coords[gr_screen.res][MJ_X_COORD], y_start, str);
1805 // we'll want to have different colors for highlighted items, etc.
1806 if(moveup == Multi_join_selected_item){
1807 gr_set_color_fast(&Color_text_selected);
1809 gr_set_color_fast(&Color_text_normal);
1812 // display the game name, adding appropriate status chars
1814 if(moveup->flags & AG_FLAG_STANDALONE){
1815 strcat(str,MJ_CHAR_STANDALONE);
1817 if(moveup->flags & AG_FLAG_CAMPAIGN){
1818 strcat(str,MJ_CHAR_CAMPAIGN);
1821 // tack on the actual server name
1823 strcat(str,moveup->name);
1824 if(strlen(moveup->mission_name) > 0){
1826 strcat(str,moveup->mission_name);
1829 // make sure the string fits in the display area and draw it
1830 gr_force_fit_string(str,200,Mj_game_name_coords[gr_screen.res][MJ_W_COORD]);
1831 gr_string(Mj_game_name_coords[gr_screen.res][MJ_X_COORD],y_start,str);
1833 // display the ping time
1834 if(moveup->ping.ping_avg > 0){
1835 if(moveup->ping.ping_avg > 1000){
1836 gr_set_color_fast(&Color_bright_red);
1837 strcpy(str,XSTR("> 1 sec",761));
1839 // set the appropriate ping time color indicator
1840 if(moveup->ping.ping_avg > MJ_PING_YELLOW){
1841 gr_set_color_fast(&Color_bright_red);
1842 } else if(moveup->ping.ping_avg > MJ_PING_YELLOW){
1843 gr_set_color_fast(&Color_bright_yellow);
1845 gr_set_color_fast(&Color_bright_green);
1848 sprintf(str,"%d",moveup->ping.ping_avg);
1849 strcat(str,XSTR(" ms",762)); // [[ Milliseconds ]]
1852 gr_string(Mj_ping_coords[gr_screen.res][MJ_X_COORD],y_start,str);
1855 // display the number of players (be sure to center it)
1856 if(moveup == Multi_join_selected_item){
1857 gr_set_color_fast(&Color_text_selected);
1859 gr_set_color_fast(&Color_text_normal);
1861 sprintf(str,"%d",moveup->num_players);
1862 gr_get_string_size(&w,&h,str);
1863 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);
1867 moveup = moveup->next;
1868 } while((moveup != Active_game_head) && (count < Mj_max_game_items[gr_screen.res]));
1870 // if there are no items on the list, display this info
1872 gr_set_color_fast(&Color_bright);
1873 gr_string(Mj_game_name_coords[gr_screen.res][MJ_X_COORD] - 30,y_start,XSTR("<No game servers found>",763));
1877 void multi_join_blit_game_status(active_game *game, int y)
1880 char status_text[25];
1882 // blit the proper icon
1884 switch( game->flags & AG_FLAG_TYPE_MASK ){
1887 if(Multi_common_icons[MICON_COOP] != -1){
1888 gr_set_bitmap(Multi_common_icons[MICON_COOP]);
1893 // team vs. team game
1895 if(Multi_common_icons[MICON_TVT] != -1){
1896 gr_set_bitmap(Multi_common_icons[MICON_TVT]);
1903 case AG_FLAG_DOGFIGHT:
1904 if(Multi_common_icons[MICON_DOGFIGHT] != -1){
1905 gr_set_bitmap(Multi_common_icons[MICON_DOGFIGHT]);
1911 // if we're supposed to draw a bitmap
1913 gr_bitmap(Mj_game_icon_coords[gr_screen.res][MJ_X_COORD],y-1);
1916 // blit the proper status text
1917 memset(status_text,0,25);
1919 switch( game->flags & AG_FLAG_STATE_MASK ){
1920 case AG_FLAG_FORMING:
1921 gr_set_color_fast(&Color_bright_green);
1922 strcpy(status_text,XSTR("Forming",764));
1924 case AG_FLAG_BRIEFING:
1925 gr_set_color_fast(&Color_bright_red);
1926 strcpy(status_text,XSTR("Briefing",765));
1928 case AG_FLAG_DEBRIEF:
1929 gr_set_color_fast(&Color_bright_red);
1930 strcpy(status_text,XSTR("Debrief",766));
1933 gr_set_color_fast(&Color_bright_red);
1934 strcpy(status_text,XSTR("Paused",767));
1936 case AG_FLAG_IN_MISSION:
1937 gr_set_color_fast(&Color_bright_red);
1938 strcpy(status_text,XSTR("Playing",768));
1941 gr_set_color_fast(&Color_bright);
1942 strcpy(status_text,XSTR("Unknown",769));
1945 gr_get_string_size(&str_w,NULL,status_text);
1946 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);
1949 // load in a list of active games from our tcp.cfg file
1950 void multi_join_load_tcp_addrs()
1952 char line[MAX_IP_STRING];
1957 // attempt to open the ip list file
1958 file = cfopen(IP_CONFIG_FNAME,"rt",CFILE_NORMAL,CF_TYPE_DATA);
1960 nprintf(("Network","Error loading tcp.cfg file!\n"));
1964 // free up any existing server list
1965 multi_free_server_list();
1967 // read in all the strings in the file
1968 while(!cfeof(file)){
1970 cfgets(line,MAX_IP_STRING,file);
1972 // strip off any newline character
1973 if(line[strlen(line) - 1] == '\n'){
1974 line[strlen(line) - 1] = '\0';
1977 // empty lines don't get processed
1978 if( (line[0] == '\0') || (line[0] == '\n') ){
1982 if ( !psnet_is_valid_ip_string(line) ) {
1983 nprintf(("Network","Invalid ip string (%s)\n",line));
1985 // copy the server ip address
1986 memset(&addr,0,sizeof(net_addr));
1987 addr.type = NET_TCP;
1988 psnet_string_to_addr(&addr,line);
1989 if ( addr.port == 0 ){
1990 addr.port = DEFAULT_GAME_PORT;
1993 // create a new server item on the list
1994 item = multi_new_server_item();
1996 memcpy(&item->server_addr,&addr,sizeof(net_addr));
2004 // do stuff like pinging servers, sending out requests, etc
2005 void multi_join_do_netstuff()
2007 // handle game query stuff
2008 if(Multi_join_glr_stamp == -1){
2009 broadcast_game_query();
2011 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
2012 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME_LOCAL);
2014 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME);
2017 // otherwise send out game query and restamp
2018 else if(timestamp_elapsed(Multi_join_glr_stamp)){
2019 broadcast_game_query();
2021 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
2022 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME_LOCAL);
2024 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME);
2028 // check to see if we've been accepted. If so, put up message saying so
2029 if ( Net_player->flags & (NETINFO_FLAG_ACCEPT_INGAME|NETINFO_FLAG_ACCEPT_CLIENT|NETINFO_FLAG_ACCEPT_HOST|NETINFO_FLAG_ACCEPT_OBSERVER) ) {
2030 multi_common_add_notify(XSTR("Accepted. Waiting for player data.",770));
2033 // check to see if any join packets we have sent have timed out
2034 if((Multi_join_sent_stamp != -1) && (timestamp_elapsed(Multi_join_sent_stamp))){
2035 Multi_join_sent_stamp = -1;
2036 multi_common_add_notify(XSTR("Join request timed out!",771));
2039 // check to see if we should be pinging everyone
2040 if((Multi_join_ping_stamp == -1) || (timestamp_elapsed(Multi_join_ping_stamp))){
2041 multi_join_ping_all();
2042 Multi_join_ping_stamp = timestamp(MULTI_JOIN_PING_TIME);
2046 multi_join_cull_timeouts();
2049 // evaluate a returned pong.
2050 void multi_join_eval_pong(net_addr *addr, fix pong_time)
2053 active_game *moveup = Active_game_head;
2058 if(psnet_same(&moveup->server_addr,addr)){
2060 multi_ping_eval_pong(&moveup->ping);
2064 moveup = moveup->next;
2066 } while(moveup != Active_game_head);
2069 // update the game's ping
2071 if(found && (moveup->ping_end > moveup->ping_start)){
2072 moveup->ping_time = f2fl(moveup->ping_end - moveup->ping_start);
2073 moveup->ping_start = -1;
2074 moveup->ping_end = -1;
2079 // ping all the server on the list
2080 void multi_join_ping_all()
2082 active_game *moveup = Active_game_head;
2087 moveup->ping_start = timer_get_fixed_seconds();
2088 moveup->ping_end = -1;
2089 send_ping(&moveup->server_addr);
2091 multi_ping_send(&moveup->server_addr,&moveup->ping);
2093 moveup = moveup->next;
2094 } while(moveup != Active_game_head);
2098 void multi_join_process_select()
2100 // if we don't have anything selected and there are items on the list - select the first one
2101 if((Multi_join_list_selected == -1) && (Active_game_count > 0)){
2102 Multi_join_list_selected = 0;
2103 Multi_join_selected_item = multi_join_get_game(0);
2104 MJ_LIST_START_SET(0);
2105 Multi_join_list_start_item = Multi_join_selected_item;
2107 // send a mission description request to this guy
2108 send_netgame_descript_packet(&Multi_join_selected_item->server_addr,0);
2109 multi_common_set_text("");
2111 // I sure hope this doesn't happen
2112 Assert(Multi_join_selected_item != NULL);
2115 // otherwise see if he's clicked on an item
2116 else if(Multi_join_select_button.pressed() && (Active_game_count > 0)){
2118 Multi_join_select_button.get_mouse_pos(NULL,&y);
2120 if(item + Multi_join_list_start < Active_game_count){
2121 gamesnd_play_iface(SND_IFACE_MOUSE_CLICK);
2123 Multi_join_list_selected = item + Multi_join_list_start;
2124 Multi_join_selected_item = multi_join_get_game(Multi_join_list_selected);
2126 // I sure hope this doesn't happen
2127 Assert(Multi_join_selected_item != NULL);
2129 // send a mission description request to this guy
2130 send_netgame_descript_packet(&Multi_join_selected_item->server_addr,0);
2131 multi_common_set_text("");
2135 // if he's double clicked, then select it and accept
2136 if(Multi_join_select_button.double_clicked()){
2138 Multi_join_select_button.get_mouse_pos(NULL,&y);
2140 if(item == Multi_join_list_selected){
2141 multi_join_button_pressed(MJ_ACCEPT);
2146 // return game n (0 based index)
2147 active_game *multi_join_get_game(int n)
2149 active_game *moveup = Active_game_head;
2156 moveup = moveup->next;
2157 while((moveup != Active_game_head) && (count != n)){
2158 moveup = moveup->next;
2161 if(moveup == Active_game_head){
2162 nprintf(("Network","Warning, couldn't find game item %d!\n",n));
2172 // scroll through the game list
2173 void multi_join_list_scroll_up()
2175 // if we're not at the beginning of the list, scroll up
2176 if(Multi_join_list_start_item != Active_game_head){
2177 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2179 MJ_LIST_START_DEC();
2181 gamesnd_play_iface(SND_SCROLL);
2183 gamesnd_play_iface(SND_GENERAL_FAIL);
2187 // scroll through the game list
2188 void multi_join_list_scroll_down()
2190 if((Active_game_count - Multi_join_list_start) > Mj_max_game_items[gr_screen.res]){
2191 Multi_join_list_start_item = Multi_join_list_start_item->next;
2193 MJ_LIST_START_INC();
2195 gamesnd_play_iface(SND_SCROLL);
2197 gamesnd_play_iface(SND_GENERAL_FAIL);
2201 void multi_join_list_page_up()
2203 // in this case, just set us to the beginning of the list
2204 if((Multi_join_list_start - Mj_max_game_items[gr_screen.res]) < 0){
2205 Multi_join_list_start_item = Active_game_head;
2207 MJ_LIST_START_SET(0);
2209 gamesnd_play_iface(SND_SCROLL);
2211 // otherwise page the whole thing up
2213 for(idx=0; idx<Mj_max_game_items[gr_screen.res]; idx++){
2214 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2216 MJ_LIST_START_DEC();
2218 gamesnd_play_iface(SND_SCROLL);
2222 void multi_join_list_page_down()
2226 // page the whole thing down
2227 while((count < Mj_max_game_items[gr_screen.res]) && ((Active_game_count - Multi_join_list_start) > Mj_max_game_items[gr_screen.res])){
2228 Multi_join_list_start_item = Multi_join_list_start_item->next;
2229 MJ_LIST_START_INC();
2234 gamesnd_play_iface(SND_SCROLL);
2237 void multi_join_cull_timeouts()
2239 active_game *backup;
2241 active_game *moveup = Active_game_head;
2243 // traverse through the entire list if any items exist
2247 if((moveup->heard_from_timer != -1) && (timestamp_elapsed(moveup->heard_from_timer))){
2248 Active_game_count--;
2250 // if this is the head of the list
2251 if(moveup == Active_game_head){
2252 // if this is the _only_ item on the list
2253 if(moveup->next == Active_game_head){
2254 // handle any gui details related to deleting this item
2255 multi_join_handle_item_cull(Active_game_head, count);
2257 free(Active_game_head);
2258 Active_game_head = NULL;
2261 // if there are other items on the list
2263 // handle any gui details related to deleting this item
2264 multi_join_handle_item_cull(moveup, count);
2266 Active_game_head = moveup->next;
2267 Active_game_head->prev = moveup->prev;
2268 Active_game_head->prev->next = Active_game_head;
2270 moveup = Active_game_head;
2273 // if its somewhere else on the list
2275 // handle any gui details related to deleting this item
2276 multi_join_handle_item_cull(moveup, count);
2278 // if its the last item on the list
2279 moveup->next->prev = moveup->prev;
2280 moveup->prev->next = moveup->next;
2282 // if it was the last element on the list, return
2283 if(moveup->next == Active_game_head){
2287 backup = moveup->next;
2293 moveup = moveup->next;
2296 } while(moveup != Active_game_head);
2300 // deep magic begins here.
2301 void multi_join_handle_item_cull(active_game *item, int item_index)
2303 // if this is the only item on the list, unset everything
2304 if(item->next == item){
2305 Multi_join_list_selected = -1;
2306 Multi_join_selected_item = NULL;
2309 Multi_join_slider.set_numberItems(0);
2311 MJ_LIST_START_SET(-1);
2312 Multi_join_list_start_item = NULL;
2318 // see if we should be adjusting our currently selected item
2319 if(item_index <= Multi_join_list_selected){
2320 // the selected item is the head of the list
2321 if(Multi_join_selected_item == Active_game_head){
2322 // move the pointer up since this item is about to be destroyed
2323 Multi_join_selected_item = Multi_join_selected_item->next;
2325 // if this is the item being deleted, select the previous one
2326 if(item == Multi_join_selected_item){
2328 Multi_join_selected_item = Multi_join_selected_item->prev;
2330 // decrement the selected index by 1
2331 Multi_join_list_selected--;
2333 // now we know its a previous item, so our pointer stays the same but our index goes down by one, since there will be
2334 // 1 less item on the list
2336 // decrement the selected index by 1
2337 Multi_join_list_selected--;
2342 // see if we should be adjusting out current start position
2343 if(item_index <= Multi_join_list_start){
2344 // the start position is the head of the list
2345 if(Multi_join_list_start_item == Active_game_head){
2346 // move the pointer up since this item is about to be destroyed
2347 Multi_join_list_start_item = Multi_join_list_start_item->next;
2349 // if this is the item being deleted, select the previous one
2350 if(item == Multi_join_list_start_item){
2351 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2353 // decrement the starting index by 1
2354 MJ_LIST_START_DEC();
2356 // but decrement the starting index by 1
2357 MJ_LIST_START_DEC();
2362 // maybe go back up a bit so that we always have a full page of items
2363 if(Active_game_count > Mj_max_game_items[gr_screen.res]){
2364 while((Active_game_count - Multi_join_list_start) < Mj_max_game_items[gr_screen.res]){
2365 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2366 MJ_LIST_START_DEC();
2370 // set slider location
2372 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);
2373 Multi_join_slider.force_currentItem(Multi_join_list_start);
2377 void multi_join_send_join_request(int as_observer)
2379 // don't do anything if we have no items selected
2380 if(Multi_join_selected_item == NULL){
2384 // 5/26/98 -- for team v team games, don't allow ingame joining :-(
2385 if ( (Multi_join_selected_item->flags & AG_FLAG_TEAMS) && (Multi_join_selected_item->flags & (AG_FLAG_PAUSE|AG_FLAG_IN_MISSION)) ) {
2386 popup(0, 1, POPUP_OK, XSTR("Joining ingame is currently not allowed for team vs. team games",772));
2390 memset(&Multi_join_request,0,sizeof(join_request));
2392 // if the netgame is in password mode, put up a request for the password
2393 if(Multi_join_selected_item->flags & AG_FLAG_PASSWD){
2394 if(!multi_passwd_popup(Multi_join_request.passwd)){
2398 nprintf(("Password : %s\n",Multi_join_request.passwd));
2401 // fill out the join request struct
2402 strcpy(Multi_join_request.callsign,Player->callsign);
2403 if(strlen(Player->image_filename) > 0){
2404 strcpy(Multi_join_request.image_filename, Player->image_filename);
2407 if(strlen(Player->squad_filename) > 0){
2408 strcpy(Multi_join_request.squad_filename, Player->squad_filename);
2412 // tracker id (if any)
2413 Multi_join_request.tracker_id = Multi_tracker_id;
2415 // player's rank (at least, what he wants you to _believe_)
2416 Multi_join_request.player_rank = (ubyte)Player->stats.rank;
2419 Multi_join_request.flags = 0;
2421 Multi_join_request.flags |= JOIN_FLAG_AS_OBSERVER;
2424 // if the player has hacked data
2425 if(game_hacked_data()){
2426 Multi_join_request.flags |= JOIN_FLAG_HAXOR;
2431 strncpy(Multi_join_request.pxo_squad_name, Multi_tracker_squad_name, LOGIN_LEN);
2434 // version of this server
2435 Multi_join_request.version = MULTI_FS_SERVER_VERSION;
2437 // server compatible version
2438 Multi_join_request.comp_version = MULTI_FS_SERVER_COMPATIBLE_VERSION;
2440 // his local player options
2441 memcpy(&Multi_join_request.player_options,&Player->m_local_options,sizeof(multi_local_options));
2443 // set the server address for the netgame
2444 memcpy(&Netgame.server_addr,&Multi_join_selected_item->server_addr,sizeof(net_addr));
2446 // send a join request to the guy
2447 send_join_packet(&Multi_join_selected_item->server_addr,&Multi_join_request);
2450 Multi_join_sent_stamp = timestamp(MULTI_JOIN_SENT_WAIT);
2453 multi_common_add_notify(XSTR("Sending join request...",773));
2456 void multi_join_create_game()
2458 // maybe warn the player about possible crappy server conditions
2459 if(!multi_join_maybe_warn()){
2463 // make sure to flag ourself as being the master
2464 Net_player->flags |= (NETINFO_FLAG_AM_MASTER | NETINFO_FLAG_GAME_HOST);
2465 Net_player->state = NETPLAYER_STATE_HOST_SETUP;
2467 // if we're in PXO mode, mark it down in our player struct
2468 if(MULTI_IS_TRACKER_GAME){
2469 Player->flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
2470 Player->save_flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
2472 // otherwise, if he's already played PXO games, warn him
2475 if(Player->flags & PLAYER_FLAGS_HAS_PLAYED_PXO){
2476 if(!multi_join_warn_pxo()){
2483 gameseq_post_event(GS_EVENT_MULTI_START_GAME);
2484 gamesnd_play_iface(SND_USER_SELECT);
2487 void multi_join_reset_join_stamp()
2489 // unset the timestamp here so the user can immediately send another join request
2490 Multi_join_sent_stamp = -1;
2491 multi_common_add_notify("");
2494 void multi_join_blit_top_stuff()
2496 // blit the cd icon if he has one
2497 if(Multi_has_cd && (Multi_common_icons[MICON_CD] != -1)){
2500 bm_get_info(Multi_common_icons[MICON_CD], &cd_w, NULL, NULL, NULL, NULL);
2502 gr_set_bitmap(Multi_common_icons[MICON_CD]);
2503 gr_bitmap((gr_screen.max_w / 2) - (cd_w / 2), Mj_cd_coords[gr_screen.res]);
2507 #define CW_CODE_CANCEL 0 // cancel the action
2508 #define CW_CODE_OK 1 // continue anyway
2509 #define CW_CODE_INFO 2 // gimme some more information
2511 #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)
2512 #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)
2514 #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)
2515 #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)
2517 int multi_join_warn_update_low(int code)
2521 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),LOW_WARN_TEXT);
2524 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),LOW_INFO_TEXT);
2527 return CW_CODE_CANCEL;
2530 int multi_join_warn_update_medium(int code)
2534 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),MED_WARN_TEXT);
2537 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),MED_INFO_TEXT);
2540 return CW_CODE_CANCEL;
2543 int multi_join_maybe_warn()
2547 // if the player is set for low updates
2548 if(Player->m_local_options.obj_update_level == OBJ_UPDATE_LOW){
2551 code = multi_join_warn_update_low(code);
2552 } while((code != CW_CODE_CANCEL) && (code != CW_CODE_OK));
2557 // if the player is set for medium updates
2558 else if(Player->m_local_options.obj_update_level == OBJ_UPDATE_MEDIUM){
2561 code = multi_join_warn_update_medium(code);
2562 } while((code != CW_CODE_CANCEL) && (code != CW_CODE_OK));
2570 int multi_join_warn_pxo()
2572 // 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;
2576 void multi_join_blit_protocol()
2578 gr_set_color_fast(&Color_bright);
2580 switch(Socket_type){
2583 gr_string(5, 2, "TCP");
2587 gr_string(5, 2, "IPX");
2593 // -------------------------------------------------------------------------------------------------
2595 // MULTIPLAYER START GAME screen
2600 #define MULTI_SG_PALETTE "InterfacePalette"
2602 static char *Multi_sg_bitmap_fname[GR_NUM_RESOLUTIONS] = {
2603 "MultiStartGame", // GR_640
2604 "2_MultiStartGame" // GR_1024
2607 static char *Multi_sg_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
2608 "MultiStartGame-M", // GR_640
2609 "2_MultiStartGame-M" // GR_1024
2614 int Multi_sg_rank_max_display[GR_NUM_RESOLUTIONS] = {
2619 // constants for coordinate look ups
2620 #define MSG_X_COORD 0
2621 #define MSG_Y_COORD 1
2622 #define MSG_W_COORD 2
2623 #define MSG_H_COORD 3
2627 // input password field
2628 int Msg_passwd_coords[GR_NUM_RESOLUTIONS][4] = {
2641 // input game title field
2642 int Msg_title_coords[GR_NUM_RESOLUTIONS][4] = {
2655 // rank selected field
2656 int Msg_rank_sel_coords[GR_NUM_RESOLUTIONS][4] = {
2670 int Msg_rank_list_coords[GR_NUM_RESOLUTIONS][4] = {
2686 #define MULTI_SG_NUM_BUTTONS 12
2687 #define MSG_OPEN_GAME 0
2688 #define MSG_CLOSED_GAME 1
2689 #define MSG_PASSWD_GAME 2
2690 #define MSG_RESTRICTED_GAME 3
2691 #define MSG_RANK_SET_GAME 4
2692 #define MSG_RANK_SCROLL_UP 5
2693 #define MSG_RANK_SCROLL_DOWN 6
2694 #define MSG_RANK_ABOVE 7
2695 #define MSG_RANK_BELOW 8
2697 #define MSG_OPTIONS 10
2698 #define MSG_ACCEPT 11
2700 #define MULTI_SG_NUM_BUTTONS 10
2701 #define MSG_OPEN_GAME 0
2702 //#define MSG_CLOSED_GAME 1
2703 //#define MSG_RESTRICTED_GAME 2
2704 #define MSG_PASSWD_GAME 1
2705 #define MSG_RANK_SET_GAME 2
2706 #define MSG_RANK_SCROLL_UP 3
2707 #define MSG_RANK_SCROLL_DOWN 4
2708 #define MSG_RANK_ABOVE 5
2709 #define MSG_RANK_BELOW 6
2711 #define MSG_OPTIONS 8
2712 #define MSG_ACCEPT 9
2715 UI_WINDOW Multi_sg_window; // the window object for the join screen
2716 UI_BUTTON Multi_sg_rank_button; // for selecting the rank marker
2717 UI_INPUTBOX Multi_sg_game_name; // for Netgame.name
2718 UI_INPUTBOX Multi_sg_game_passwd; // for Netgame.passwd
2719 int Multi_sg_bitmap; // the background bitmap
2721 ui_button_info Multi_sg_buttons[GR_NUM_RESOLUTIONS][MULTI_SG_NUM_BUTTONS] = {
2724 ui_button_info("MSG_00", 75, 111, -1, -1, 0),
2725 ui_button_info("MSG_01", 75, 139, -1, -1, 1),
2726 ui_button_info("MSG_02", 75, 164, -1, -1, 2),
2727 ui_button_info("MSG_03", 75, 199, -1, -1, 3),
2728 ui_button_info("MSG_04", 75, 243, -1, -1, 4),
2729 ui_button_info("MSG_05", 376, 231, -1, -1, 5),
2730 ui_button_info("MSG_06", 376, 258, -1, -1, 6),
2731 ui_button_info("MSG_07", 376, 291, -1, -1, 7),
2732 ui_button_info("MSG_08", 376, 320, -1, -1, 8),
2733 ui_button_info("MSG_09", 469, 427, -1, -1, 9),
2734 ui_button_info("MSG_10", 447, 452, -1, -1, 10),
2735 ui_button_info("MSG_11", 561, 411, -1, -1, 11),
2737 ui_button_info("MSG_00", 1, 184, 34, 191, 2), // open
2738 // ui_button_info("MSG_01", 1, 159, 34, 166, 1), // closed
2739 // ui_button_info("MSG_02", 1, 184, 34, 191, 2), // restricted
2740 ui_button_info("MSG_03", 1, 209, 34, 218, 3), // password
2741 ui_button_info("MSG_04", 1, 257, 34, 266, 4), // rank set
2742 ui_button_info("MSG_05", 1, 282, -1, -1, 5), // rank scroll up
2743 ui_button_info("MSG_06", 1, 307, -1, -1, 6), // rank scroll down
2744 ui_button_info("MSG_07", 177, 282, 210, 290, 7), // rank above
2745 ui_button_info("MSG_08", 177, 307, 210, 315, 8), // rank below
2746 ui_button_info("MSG_09", 536, 429, 500, 440, 9), // help
2747 ui_button_info("MSG_10", 536, 454, 479, 464, 10), // options
2748 ui_button_info("MSG_11", 576, 432, 571, 415, 11), // accept
2752 ui_button_info("2_MSG_00", 2, 295, 51, 307, 2), // open
2753 // ui_button_info("2_MSG_01", 2, 254, 51, 267, 1), // closed
2754 // ui_button_info("2_MSG_02", 2, 295, 51, 307, 2), // restricted
2755 ui_button_info("2_MSG_03", 2, 335, 51, 350, 3), // password
2756 ui_button_info("2_MSG_04", 2, 412, 51, 426, 4), // rank set
2757 ui_button_info("2_MSG_05", 2, 452, -1, -1, 5), // rank scroll up
2758 ui_button_info("2_MSG_06", 2, 492, -1, -1, 6), // rank scroll down
2759 ui_button_info("2_MSG_07", 284, 452, 335, 465, 7), // rank above
2760 ui_button_info("2_MSG_08", 284, 492, 335, 505, 8), // rank below
2761 ui_button_info("2_MSG_09", 858, 687, 817, 706, 9), // help
2762 ui_button_info("2_MSG_10", 858, 728, 797, 743, 10), // options
2763 ui_button_info("2_MSG_11", 921, 692, 921, 664, 11), // accept
2764 #ifdef MAKE_FS1 // filler for extra FS1 buttons
2765 ui_button_info("none", -1, -1, -1, -1, -1),
2766 ui_button_info("none", -1, -1, -1, -1, -1),
2772 #define MULTI_SG_NUM_TEXT 0
2774 #define MULTI_SG_NUM_TEXT 11
2776 UI_XSTR Multi_sg_text[GR_NUM_RESOLUTIONS][MULTI_SG_NUM_TEXT] = {
2778 // not needed for FS1
2780 {"Open", 1322, 34, 191, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_OPEN_GAME].button},
2781 // {"Closed", 1323, 34, 166, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_CLOSED_GAME].button},
2782 // {"Restricted", 1324, 34, 191, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RESTRICTED_GAME].button},
2783 {"Password Protected", 1325, 34, 218, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_PASSWD_GAME].button},
2784 {"Allow Rank", 1326, 34, 266, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_SET_GAME].button},
2785 {"Above", 1327, 210, 290, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_ABOVE].button},
2786 {"Below", 1328, 210, 315, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_BELOW].button},
2787 {"Help", 928, 500, 440, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_HELP].button},
2788 {"Options", 1036, 479, 464, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_OPTIONS].button},
2789 {"Accept", 1035, 571, 415, UI_XSTR_COLOR_PINK, -1, &Multi_sg_buttons[0][MSG_ACCEPT].button},
2790 {"Start Game", 1329, 26, 10, UI_XSTR_COLOR_GREEN, -1, NULL},
2791 {"Title", 1330, 26, 31, UI_XSTR_COLOR_GREEN, -1, NULL},
2792 {"Game Type", 1331, 12, 165, UI_XSTR_COLOR_GREEN, -1, NULL},
2796 // not needed for FS1
2798 {"Open", 1322, 51, 307, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_OPEN_GAME].button},
2799 // {"Closed", 1323, 51, 267, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_CLOSED_GAME].button},
2800 // {"Restricted", 1324, 51, 307, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RESTRICTED_GAME].button},
2801 {"Password Protected", 1325, 51, 350, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_PASSWD_GAME].button},
2802 {"Allow Rank", 1326, 51, 426, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_SET_GAME].button},
2803 {"Above", 1327, 335, 465, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_ABOVE].button},
2804 {"Below", 1328, 335, 505, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_BELOW].button},
2805 {"Help", 928, 817, 706, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_HELP].button},
2806 {"Options", 1036, 797, 743, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_OPTIONS].button},
2807 {"Accept", 1035, 921, 664, UI_XSTR_COLOR_PINK, -1, &Multi_sg_buttons[1][MSG_ACCEPT].button},
2808 {"Start Game", 1329, 42, 22, UI_XSTR_COLOR_GREEN, -1, NULL},
2809 {"Title", 1330, 42, 50, UI_XSTR_COLOR_GREEN, -1, NULL},
2810 {"Game Type", 1331, 20, 264, UI_XSTR_COLOR_GREEN, -1, NULL},
2815 // starting index for displaying ranks
2816 int Multi_sg_rank_start;
2817 int Multi_sg_rank_select;
2819 // netgame pointer to indirect through
2820 netgame_info *Multi_sg_netgame;
2822 // hold temporary values in this structure when on a standalone server
2823 netgame_info Multi_sg_netgame_temp;
2825 // forward declarations
2826 void multi_sg_check_buttons();
2827 void multi_sg_button_pressed(int n);
2828 void multi_sg_init_gamenet();
2829 void multi_sg_draw_radio_buttons();
2830 void multi_sg_rank_scroll_up();
2831 void multi_sg_rank_scroll_down();
2832 void multi_sg_rank_display_stuff();
2833 void multi_sg_rank_process_select();
2834 void multi_sg_rank_build_name(char *in,char *out);
2835 void multi_sg_check_passwd();
2836 void multi_sg_check_name();
2837 void multi_sg_release_passwd();
2838 int multi_sg_rank_select_valid(int rank);
2839 void multi_sg_select_rank_default();
2841 // function which takes a rank name and returns the index. Useful for commandline options
2842 // for above and below rank. We return the index of the rank in the Ranks[] array. If
2843 // the rank isn't found, we return -1
2844 int multi_start_game_rank_from_name( char *rank ) {
2848 for ( i = 0; i <= MAX_FREESPACE1_RANK; i++ ) {
2850 for ( i = 0; i <= MAX_FREESPACE2_RANK; i++ ) {
2852 if ( !stricmp(Ranks[i].name, rank) ) {
2860 void multi_start_game_init()
2864 // initialize the gamenet
2865 multi_sg_init_gamenet();
2867 // create the interface window
2868 Multi_sg_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
2869 Multi_sg_window.set_mask_bmap(Multi_sg_bitmap_mask_fname[gr_screen.res]);
2871 // load the background bitmap
2872 Multi_sg_bitmap = bm_load(Multi_sg_bitmap_fname[gr_screen.res]);
2873 if(Multi_sg_bitmap < 0){
2874 // we failed to load the bitmap - this is very bad
2878 // initialize the common notification messaging
2879 multi_common_notify_init();
2881 // initialize the common text area
2882 multi_common_set_text("");
2884 // use the common interface palette
2885 multi_common_set_palette();
2887 // create the interface buttons
2888 for(idx=0; idx<MULTI_SG_NUM_BUTTONS; idx++){
2889 // create the object
2890 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);
2892 // set the sound to play when highlighted
2893 Multi_sg_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
2895 // set the ani for the button
2896 Multi_sg_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sg_buttons[gr_screen.res][idx].filename);
2899 Multi_sg_buttons[gr_screen.res][idx].button.link_hotspot(Multi_sg_buttons[gr_screen.res][idx].hotspot);
2903 for(idx=0; idx<MULTI_SG_NUM_TEXT; idx++){
2904 Multi_sg_window.add_XSTR(&Multi_sg_text[gr_screen.res][idx]);
2907 // load the help overlay
2908 help_overlay_load(MULTI_START_OVERLAY);
2909 help_overlay_set_state(MULTI_START_OVERLAY,0);
2911 // intiialize the rank selection items
2912 multi_sg_select_rank_default();
2913 Multi_sg_rank_start = Multi_sg_rank_select;
2915 // create the rank select button
2916 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);
2917 Multi_sg_rank_button.hide();
2919 // create the netgame name input box
2920 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);
2922 // create the netgame password input box, and disable it by default
2923 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);
2924 Multi_sg_game_passwd.hide();
2925 Multi_sg_game_passwd.disable();
2927 // set the netgame text to this gadget and make it have focus
2928 Multi_sg_game_name.set_text(Multi_sg_netgame->name);
2929 Multi_sg_game_name.set_focus();
2931 // if starting a netgame, set the name of the game and any other options that are appropriate
2932 if ( Cmdline_start_netgame ) {
2933 if ( Cmdline_game_name != NULL ) {
2934 strcpy( Multi_sg_netgame->name, Cmdline_game_name );
2935 Multi_sg_game_name.set_text(Multi_sg_netgame->name);
2938 // deal with the different game types -- only one should even be active, so we will just go down
2939 // the line. Last one wins.
2940 if ( Cmdline_closed_game ) {
2941 Multi_sg_netgame->mode = NG_MODE_CLOSED;
2942 } else if ( Cmdline_restricted_game ) {
2943 Multi_sg_netgame->mode = NG_MODE_RESTRICTED;
2944 } else if ( Cmdline_game_password != NULL ) {
2945 Multi_sg_netgame->mode = NG_MODE_PASSWORD;
2946 strcpy(Multi_sg_netgame->passwd, Cmdline_game_password);
2947 Multi_sg_game_passwd.set_text(Multi_sg_netgame->passwd);
2950 // deal with rank above and rank below
2951 if ( (Cmdline_rank_above != NULL) || (Cmdline_rank_below != NULL) ) {
2955 if ( Cmdline_rank_above != NULL ) {
2956 rank_str = Cmdline_rank_above;
2958 rank_str = Cmdline_rank_below;
2961 // try and get the rank index from the name -- if found, then set the rank base
2962 // and the game type. apparently we only support either above or below, not both
2963 // together, so I make a random choice
2964 rank = multi_start_game_rank_from_name( rank_str );
2966 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
2968 // now an arbitrary decision
2969 if ( Cmdline_rank_above != NULL ) {
2970 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
2972 Multi_sg_netgame->mode = NG_MODE_RANK_BELOW;
2977 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
2981 void multi_start_game_do()
2983 // return here since we will be moving to the next stage anyway -- I don't want to see the backgrounds of
2984 // all the screens for < 1 second for every screen we automatically move to.
2985 if ( Cmdline_start_netgame ) {
2989 int k = Multi_sg_window.process();
2991 // process any keypresses
2994 if(help_overlay_active(MULTI_START_OVERLAY)){
2995 help_overlay_set_state(MULTI_START_OVERLAY,0);
2997 gamesnd_play_iface(SND_USER_SELECT);
2998 multi_quit_game(PROMPT_NONE);
3003 case KEY_LCTRL + KEY_ENTER :
3004 case KEY_RCTRL + KEY_ENTER :
3005 gamesnd_play_iface(SND_COMMIT_PRESSED);
3006 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
3010 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
3011 help_overlay_set_state(MULTI_START_OVERLAY, 0);
3014 // check to see if the user has selected a different rank
3015 multi_sg_rank_process_select();
3017 // check any button presses
3018 multi_sg_check_buttons();
3020 // check to see if any of the input boxes have changed, and update the appropriate Netgame fields if necessary
3021 multi_sg_check_passwd();
3022 multi_sg_check_name();
3024 // draw the background, etc
3026 GR_MAYBE_CLEAR_RES(Multi_sg_bitmap);
3027 if(Multi_sg_bitmap != -1){
3028 gr_set_bitmap(Multi_sg_bitmap);
3031 Multi_sg_window.draw();
3033 // display rank stuff
3034 multi_sg_rank_display_stuff();
3036 // display any pending notification messages
3037 multi_common_notify_do();
3039 // draw all radio button
3040 multi_sg_draw_radio_buttons();
3042 // draw the help overlay
3043 help_overlay_maybe_blit(MULTI_START_OVERLAY);
3049 void multi_start_game_close()
3051 // if i'm the host on a standalone server, send him my start game options (passwd, mode, etc)
3052 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
3053 multi_options_update_start_game(Multi_sg_netgame);
3056 // unload any bitmaps
3057 if(!bm_unload(Multi_sg_bitmap)){
3058 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_sg_bitmap_fname[gr_screen.res]));
3061 // unload the help overlay
3062 help_overlay_unload(MULTI_START_OVERLAY);
3064 // destroy the UI_WINDOW
3065 Multi_sg_window.destroy();
3068 void multi_sg_check_buttons()
3071 for(idx=0;idx<MULTI_SG_NUM_BUTTONS;idx++){
3072 // we only really need to check for one button pressed at a time, so we can break after
3074 if(Multi_sg_buttons[gr_screen.res][idx].button.pressed()){
3075 multi_sg_button_pressed(idx);
3081 void multi_sg_button_pressed(int n)
3084 // go to the options screen
3086 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
3091 if(!help_overlay_active(MULTI_START_OVERLAY)){
3092 help_overlay_set_state(MULTI_START_OVERLAY,1);
3094 help_overlay_set_state(MULTI_START_OVERLAY,0);
3098 // the open button was pressed
3100 // if the closed option is selected
3101 if(Multi_sg_netgame->mode != NG_MODE_OPEN){
3102 Multi_sg_netgame->mode = NG_MODE_OPEN;
3104 gamesnd_play_iface(SND_USER_SELECT);
3106 // release the password control if necessary
3107 multi_sg_release_passwd();
3109 // if its already selected
3111 gamesnd_play_iface(SND_GENERAL_FAIL);
3116 // the open button was pressed
3117 case MSG_CLOSED_GAME:
3118 // if the closed option is selected
3119 if(Multi_sg_netgame->mode != NG_MODE_CLOSED){
3120 Multi_sg_netgame->mode = NG_MODE_CLOSED;
3122 gamesnd_play_iface(SND_USER_SELECT);
3124 // release the password control if necessary
3125 multi_sg_release_passwd();
3127 // if its already selected
3129 gamesnd_play_iface(SND_GENERAL_FAIL);
3134 // toggle password protection
3135 case MSG_PASSWD_GAME:
3136 // if we selected it
3137 if(Multi_sg_netgame->mode != NG_MODE_PASSWORD){
3138 gamesnd_play_iface(SND_USER_SELECT);
3140 Multi_sg_game_passwd.enable();
3141 Multi_sg_game_passwd.unhide();
3142 Multi_sg_game_passwd.set_focus();
3144 Multi_sg_netgame->mode = NG_MODE_PASSWORD;
3146 // copy in the current network password
3147 Multi_sg_game_passwd.set_text(Multi_sg_netgame->passwd);
3149 gamesnd_play_iface(SND_GENERAL_FAIL);
3154 // toggle "restricted" on or off
3155 case MSG_RESTRICTED_GAME:
3156 if(Multi_sg_netgame->mode != NG_MODE_RESTRICTED){
3157 gamesnd_play_iface(SND_USER_SELECT);
3158 Multi_sg_netgame->mode = NG_MODE_RESTRICTED;
3160 // release the password control if necessary
3161 multi_sg_release_passwd();
3163 gamesnd_play_iface(SND_GENERAL_FAIL);
3168 // turn off all rank requirements
3169 case MSG_RANK_SET_GAME:
3170 // if either is set, then turn then both off
3171 if((Multi_sg_netgame->mode != NG_MODE_RANK_BELOW) && (Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE)){
3172 gamesnd_play_iface(SND_USER_SELECT);
3174 // set it to the default case if we're turning it off
3175 multi_sg_select_rank_default();
3176 Multi_sg_rank_start = Multi_sg_rank_select;
3178 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
3180 // release the password control if necessary
3181 multi_sg_release_passwd();
3183 gamesnd_play_iface(SND_GENERAL_FAIL);
3187 // rank above was pressed
3188 case MSG_RANK_ABOVE :
3189 if((Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE) || (Multi_sg_netgame->mode == NG_MODE_RANK_BELOW)){
3190 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
3192 // select the first item
3193 multi_sg_select_rank_default();
3194 Multi_sg_rank_start = Multi_sg_rank_select;
3197 gamesnd_play_iface(SND_USER_SELECT);
3199 gamesnd_play_iface(SND_GENERAL_FAIL);
3203 // rank below was pressed
3204 case MSG_RANK_BELOW :
3205 if((Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE) || (Multi_sg_netgame->mode == NG_MODE_RANK_BELOW)){
3206 Multi_sg_netgame->mode = NG_MODE_RANK_BELOW;
3208 // select the first item
3209 multi_sg_select_rank_default();
3210 Multi_sg_rank_start = Multi_sg_rank_select;
3213 gamesnd_play_iface(SND_USER_SELECT);
3215 gamesnd_play_iface(SND_GENERAL_FAIL);
3219 // scroll the rank list up
3220 case MSG_RANK_SCROLL_UP:
3221 multi_sg_rank_scroll_up();
3224 // scroll the rank list down
3225 case MSG_RANK_SCROLL_DOWN:
3226 multi_sg_rank_scroll_down();
3229 // move to the create game screen
3231 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
3232 gamesnd_play_iface(SND_COMMIT_PRESSED);
3236 gamesnd_play_iface(SND_GENERAL_FAIL);
3237 multi_common_add_notify(XSTR("Not implemented yet!",760));
3242 // NOTE : this is where all Netgame initialization should take place on the host
3243 void multi_sg_init_gamenet()
3245 char buf[128],out_name[128];
3247 net_player *server_save;
3249 // back this data up in case we are already connected to a standalone
3250 memcpy(&save,&Netgame.server_addr,sizeof(net_addr));
3251 server_save = Netgame.server;
3253 // remove campaign flags
3254 Game_mode &= ~(GM_CAMPAIGN_MODE);
3256 // clear out the Netgame structure and start filling in the values
3257 memset( &Netgame, 0, sizeof(Netgame) );
3258 memset( &Multi_sg_netgame_temp, 0, sizeof(netgame_info) );
3260 // if we're on the standalone, we're not the server, so we don't care about setting the netgame state
3261 if(Net_player->state != NETPLAYER_STATE_STD_HOST_SETUP){
3262 Netgame.game_state = NETGAME_STATE_HOST_SETUP;
3263 Multi_sg_netgame = &Netgame;
3266 ml_string(NOX("Starting netgame as Host/Server"));
3268 Multi_sg_netgame = &Multi_sg_netgame_temp;
3272 memcpy(&temp_addr.s_addr, &Netgame.server_addr, 4);
3273 char *server_addr = inet_ntoa(temp_addr);
3274 ml_printf(NOX("Starting netgame as Host on Standalone server : %s"), (server_addr == NULL) ? NOX("Unknown") : server_addr);
3277 Net_player->tracker_player_id = Multi_tracker_id;
3279 Multi_sg_netgame->security = (rand() % 32766) + 1; // get some random security number
3280 Multi_sg_netgame->mode = NG_MODE_OPEN;
3281 Multi_sg_netgame->rank_base = RANK_ENSIGN;
3282 if(Multi_sg_netgame->security < 16){
3283 Multi_sg_netgame->security += 16;
3286 // set the version_info field
3287 Multi_sg_netgame->version_info = NG_VERSION_ID;
3289 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
3290 Netgame.host = Net_player;
3293 // set the default netgame flags
3294 Multi_sg_netgame->flags = 0;
3296 // intialize endgame stuff
3297 multi_endgame_init();
3299 // load in my netgame options
3300 multi_options_netgame_load(&Netgame.options);
3302 // load my local netplayer options
3303 multi_options_local_load(&Net_player->p_info.options, Net_player);
3305 // setup the default game name, taking care of string length and player callsigns
3306 memset(out_name,0,128);
3308 pilot_format_callsign_personal(Player->callsign,out_name);
3309 sprintf(buf, XSTR("%s game",782), out_name); // [[ %s will be a pilot's name ]]
3310 if ( strlen(buf) > MAX_GAMENAME_LEN ){
3311 strcpy(buf, XSTR("Temporary name",783));
3313 strcpy(Multi_sg_netgame->name, buf);
3315 // set the default qos and duration
3316 multi_voice_maybe_update_vars(Netgame.options.voice_qos,Netgame.options.voice_record_time);
3318 // make sure to set the server correctly (me or the standalone)
3319 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3320 memcpy(&Netgame.server_addr, &Psnet_my_addr,sizeof(net_addr));
3321 Netgame.server = Net_player;
3322 Net_player->player_id = multi_get_new_id();
3324 // setup debug flags
3325 Netgame.debug_flags = 0;
3327 if(!Cmdline_server_firing){
3328 Netgame.debug_flags |= NETD_FLAG_CLIENT_FIRING;
3330 if(!Cmdline_client_dodamage){
3331 Netgame.debug_flags |= NETD_FLAG_CLIENT_NODAMAGE;
3335 memcpy(&Netgame.server_addr,&save,sizeof(net_addr));
3336 Netgame.server = server_save;
3339 // if I have a cd or not
3341 Net_player->flags |= NETINFO_FLAG_HAS_CD;
3344 // if I have hacked data
3345 if(game_hacked_data()){
3346 Net_player->flags |= NETINFO_FLAG_HAXOR;
3349 // assign my player struct and other data
3350 Net_player->flags |= (NETINFO_FLAG_CONNECTED | NETINFO_FLAG_DO_NETWORKING);
3351 Net_player->s_info.voice_token_timestamp = -1;
3353 // if we're supposed to flush our cache directory, do so now
3354 if(Net_player->p_info.options.flags & MLO_FLAG_FLUSH_CACHE){
3355 multi_flush_multidata_cache();
3358 ml_string(NOX("Flushing multi-data cache"));
3364 void multi_sg_draw_radio_buttons()
3366 // draw the appropriate radio button
3367 switch(Multi_sg_netgame->mode){
3369 Multi_sg_buttons[gr_screen.res][MSG_OPEN_GAME].button.draw_forced(2);
3373 case NG_MODE_CLOSED:
3374 Multi_sg_buttons[gr_screen.res][MSG_CLOSED_GAME].button.draw_forced(2);
3378 case NG_MODE_PASSWORD:
3379 Multi_sg_buttons[gr_screen.res][MSG_PASSWD_GAME].button.draw_forced(2);
3383 case NG_MODE_RESTRICTED:
3384 Multi_sg_buttons[gr_screen.res][MSG_RESTRICTED_GAME].button.draw_forced(2);
3388 case NG_MODE_RANK_ABOVE:
3389 Multi_sg_buttons[gr_screen.res][MSG_RANK_SET_GAME].button.draw_forced(2);
3390 Multi_sg_buttons[gr_screen.res][MSG_RANK_ABOVE].button.draw_forced(2);
3392 case NG_MODE_RANK_BELOW:
3393 Multi_sg_buttons[gr_screen.res][MSG_RANK_SET_GAME].button.draw_forced(2);
3394 Multi_sg_buttons[gr_screen.res][MSG_RANK_BELOW].button.draw_forced(2);
3399 void multi_sg_rank_scroll_up()
3401 // if he doesn't have either of the rank flags set, then ignore this
3402 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3406 if(Multi_sg_rank_start > 0){
3407 Multi_sg_rank_start--;
3408 gamesnd_play_iface(SND_SCROLL);
3410 gamesnd_play_iface(SND_GENERAL_FAIL);
3414 void multi_sg_rank_scroll_down()
3416 // if he doesn't have either of the rank flags set, then ignore this
3417 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3421 if((NUM_RANKS - Multi_sg_rank_start) > Multi_sg_rank_max_display[gr_screen.res]){
3422 Multi_sg_rank_start++;
3423 gamesnd_play_iface(SND_SCROLL);
3425 gamesnd_play_iface(SND_GENERAL_FAIL);
3429 void multi_sg_rank_display_stuff()
3434 // if he doesn't have either of the rank flags set, then ignore this
3435 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3439 // display the list of ranks
3440 y = Msg_rank_list_coords[gr_screen.res][MSG_Y_COORD];
3441 idx = Multi_sg_rank_start;
3443 while((count < NUM_RANKS) && (count < Multi_sg_rank_max_display[gr_screen.res]) && (idx < NUM_RANKS)){
3444 // if its the selected item, then color it differently
3445 if(idx == Multi_sg_rank_select){
3446 gr_set_color_fast(&Color_text_selected);
3448 gr_set_color_fast(&Color_text_normal);
3452 multi_sg_rank_build_name(Ranks[idx].name,rank_name);
3453 gr_string(Msg_rank_list_coords[gr_screen.res][MSG_X_COORD],y,rank_name);
3461 // display the selected rank
3463 gr_set_color_fast(&Color_bright);
3464 multi_sg_rank_build_name(Ranks[Multi_sg_netgame->rank_base].name,rank_name);
3465 gr_string(Msg_rank_sel_coords[gr_screen.res][MSG_X_COORD],Msg_rank_sel_coords[gr_screen.res][MSG_Y_COORD],rank_name);
3469 void multi_sg_rank_process_select()
3473 // if he doesn't have either of the rank flags set, then ignore this
3474 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3478 // see if he's clicked on an item on the rank list
3479 if(Multi_sg_rank_button.pressed()){
3481 Multi_sg_rank_button.get_mouse_pos(NULL,&y);
3484 if(item + Multi_sg_rank_start < NUM_RANKS){
3485 // evaluate whether this rank is valid for the guy to pick
3486 if(multi_sg_rank_select_valid(item + Multi_sg_rank_start)){
3487 gamesnd_play_iface(SND_USER_SELECT);
3489 Multi_sg_rank_select = item + Multi_sg_rank_start;
3491 // set the Netgame rank
3492 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
3494 gamesnd_play_iface(SND_GENERAL_FAIL);
3496 memset(string,0,255);
3497 sprintf(string,XSTR("Illegal value for a host of your rank (%s)\n",784),Ranks[Net_player->player->stats.rank].name);
3498 multi_common_add_notify(string);
3504 void multi_sg_rank_build_name(char *in,char *out)
3510 first = strtok(use," ");
3512 // just copy the string
3517 // if the first part of the string is lieutenant, then abbreivate it and tack on the rest of the string
3518 if (stricmp(first,XSTR("lieutenant",785)) == 0) {
3519 first = strtok(NULL, NOX("\n"));
3521 // if he's not just a plain lieutenant
3523 strcpy(out,XSTR("Lt. ",786)); // [[ lieutenant ]]
3526 // if he _is_ just a plain lieutenant
3535 void multi_sg_check_passwd()
3537 // check to see if the password input box has been pressed
3538 if(Multi_sg_game_passwd.changed()){
3539 Multi_sg_game_passwd.get_text(Multi_sg_netgame->passwd);
3543 void multi_sg_check_name()
3545 // check to see if the game name input box has been pressed
3546 if(Multi_sg_game_name.changed()){
3547 Multi_sg_game_name.get_text(Multi_sg_netgame->name);
3551 void multi_sg_release_passwd()
3553 // hide and disable the password input box
3554 Multi_sg_game_passwd.hide();
3555 Multi_sg_game_passwd.disable();
3557 // set the focus back to the name input box
3558 Multi_sg_game_name.set_focus();
3561 int multi_sg_rank_select_valid(int rank)
3564 if(Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE){
3565 if(Net_player->player->stats.rank >= rank){
3571 if(Net_player->player->stats.rank <= rank){
3579 void multi_sg_select_rank_default()
3581 // pick our rank for now
3582 Multi_sg_rank_select = Net_player->player->stats.rank;
3584 // set the Netgame rank
3585 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
3588 // -------------------------------------------------------------------------------------------------
3590 // MULTIPLAYER CREATE GAME screen
3595 char *Multi_create_bitmap_fname[GR_NUM_RESOLUTIONS] = {
3596 "MultiCreate", // GR_640
3597 "2_MultiCreate" // GR_1024
3600 char *Multi_create_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
3601 "MultiCreate-M", // GR_640
3602 "2_MultiCreate-M" // GR_1024
3605 char *Multi_create_loading_fname[GR_NUM_RESOLUTIONS] = {
3606 "PleaseWait", // GR_640
3607 "2_PleaseWait" // GR_1024
3611 #define MULTI_CREATE_NUM_BUTTONS 23
3614 #define MC_SHOW_ALL 0
3615 #define MC_SHOW_COOP 1
3616 #define MC_SHOW_TEAM 2
3617 #define MC_SHOW_DOGFIGHT 3
3618 #define MC_PXO_REFRESH 4
3619 #define MC_PILOT_INFO 5
3620 #define MC_SCROLL_LIST_UP 6
3621 #define MC_SCROLL_LIST_DOWN 7
3622 #define MC_SCROLL_PLAYERS_UP 8
3623 #define MC_SCROLL_PLAYERS_DOWN 9
3624 #define MC_MISSION_FILTER 10
3625 #define MC_CAMPAIGN_FILTER 11
3626 #define MC_CANCEL 12
3631 #define MC_SCROLL_INFO_UP 17
3632 #define MC_SCROLL_INFO_DOWN 18
3633 #define MC_HOST_OPTIONS 19
3635 #define MC_OPTIONS 21
3636 #define MC_ACCEPT 22
3639 UI_WINDOW Multi_create_window; // the window object for the create screen
3640 UI_BUTTON Multi_create_player_select_button; // for selecting players
3641 UI_BUTTON Multi_create_list_select_button; // for selecting missions/campaigns
3642 int Multi_create_bitmap; // the background bitmap
3643 UI_SLIDER2 Multi_create_slider; // for create list
3645 // constants for coordinate look ups
3646 #define MC_X_COORD 0
3647 #define MC_Y_COORD 1
3648 #define MC_W_COORD 2
3649 #define MC_H_COORD 3
3651 ui_button_info Multi_create_buttons[GR_NUM_RESOLUTIONS][MULTI_CREATE_NUM_BUTTONS] = {
3654 ui_button_info("MC_18", 34, 131, -1, -1, 18), // all
3655 ui_button_info("MC_19", 72, 131, -1, -1, 19), // coop
3656 ui_button_info("MC_20", 120, 131, -1, -1, 20), // team
3657 // ui_button_info("MC_21", 166, 131, -1, -1, 21), // dogfight
3658 ui_button_info("none", -1, -1, -1, -1, -1), // dogfight (not used)
3659 ui_button_info("none", -1, -1, -1, -1, -1), // pxo?
3660 ui_button_info("MC_26", 540, 114, -1, -1, 26), // pilot info
3661 ui_button_info("MC_03", 0, 187, -1, -1, 2), // scroll list up
3662 ui_button_info("MC_02", 0, 227, -1, -1, 3), // scroll list down
3663 ui_button_info("MC_04", 611, 182, -1, -1, 4), // scroll players up
3664 ui_button_info("MC_05", 611, 221, -1, -1, 5), // scroll players down
3665 ui_button_info("MC_06", 18, 322, -1, -1, 6), // mission filter
3666 ui_button_info("MC_07", 18, 344, -1, -1, 7), // campaign filter
3667 ui_button_info("MC_10", 317, 339, -1, -1, 10), // cancel
3668 ui_button_info("MC_14", 464, 350, -1, -1, 14), // team 1
3669 ui_button_info("MC_15", 498, 350, -1, -1, 15), // team 2
3670 ui_button_info("MC_16", 527, 346, -1, -1, 16), // kick
3671 ui_button_info("MC_17", 572, 346, -1, -1, 17), // close
3672 ui_button_info("MC_08", 0, 398, -1, -1, 8), // scroll mission info up
3673 ui_button_info("MC_09", 0, 435, -1, -1, 9), // scroll mission info down
3674 ui_button_info("MC_27", 447, 402, -1, -1, 27), // host options
3675 ui_button_info("MC_11", 510, 428, -1, -1, 11), // help
3676 ui_button_info("MC_12", 510, 453, -1, -1, 12), // options
3677 ui_button_info("Mc_13", 562, 412, -1, -1, 13), // commit
3679 ui_button_info("MC_00", 32, 129, 36, 158, 0), // show all missions
3680 ui_button_info("MC_01", 76, 129, 71, 158, 1), // show coop missions
3681 ui_button_info("MC_02", 121, 129, 119, 158, 2), // show team missions
3682 ui_button_info("MC_03", 164, 129, 166, 158, 3), // show dogfight missions
3683 ui_button_info("MC_04", 399, 129, 229, 130, 4), // pxo mission refresh
3684 ui_button_info("MC_05", 567, 123, 467, 132, 5), // pilot info
3685 ui_button_info("MC_06", 1, 161, -1, -1, 6), // scroll mission info up
3686 ui_button_info("MC_08", 1, 304, -1, -1, 8), // scroll mission info down
3687 ui_button_info("MC_09", 613, 160, -1, -1, 9), // scroll players up
3688 ui_button_info("MC_10", 613, 202, -1, -1, 10), // scroll players down
3689 ui_button_info("MC_11", 22, 346, 27, 376, 11), // mission filter
3690 ui_button_info("MC_12", 104, 346, 110, 376, 12), // campaign filter
3691 ui_button_info("MC_13", 392, 341, 328, 364, 13), // cancel
3692 ui_button_info("MC_14", 472, 352, 482, 381, 14), // team 0
3693 ui_button_info("MC_15", 506, 352, 514, 381, 15), // team 1
3694 ui_button_info("MC_16", 539, 346, 539, 381, 16), // kick
3695 ui_button_info("MC_17", 589, 346, 582, 381, 17), // close
3696 ui_button_info("MC_18", 1, 406, -1, -1, 18), // scroll list up
3697 ui_button_info("MC_19", 1, 447, -1, -1, 19), // scroll list down
3698 ui_button_info("MC_20", 499, 434, 436, 423, 20), // host options
3699 ui_button_info("MC_21", 534, 426, -1, -1, 21), // help
3700 ui_button_info("MC_22", 534, 452, -1, -1, 22), // options
3701 ui_button_info("MC_23", 571, 426, 572, 413, 23), // commit
3705 ui_button_info("2_MC_00", 51, 207, 61, 253, 0), // show all missions
3706 ui_button_info("2_MC_01", 122, 207, 124, 253, 1), // show coop missions
3707 ui_button_info("2_MC_02", 193, 207, 194, 253, 2), // show team missions
3708 ui_button_info("2_MC_03", 263, 207, 261, 253, 3), // show dogfight missions
3709 ui_button_info("2_MC_04", 639, 207, 479, 218, 4), // pxo mission refresh
3710 ui_button_info("2_MC_05", 907, 197, 748, 216, 5), // pilot info
3711 ui_button_info("2_MC_06", 1, 258, -1, -1, 6), // scroll mission info up
3712 ui_button_info("2_MC_08", 1, 487, -1, -1, 8), // scroll mission info down
3713 ui_button_info("2_MC_09", 981, 256, -1, -1, 9), // scroll players up
3714 ui_button_info("2_MC_10", 981, 323, -1, -1, 10), // scroll players down
3715 ui_button_info("2_MC_11", 35, 554, 46, 601, 11), // mission filter
3716 ui_button_info("2_MC_12", 166, 554, 174, 601, 12), // campaign filter
3717 ui_button_info("2_MC_13", 628, 545, 559, 582, 13), // cancel
3718 ui_button_info("2_MC_14", 756, 564, 772, 610, 14), // team 0
3719 ui_button_info("2_MC_15", 810, 564, 826, 610, 15), // team 1
3720 ui_button_info("2_MC_16", 862, 554, 872, 610, 16), // kick
3721 ui_button_info("2_MC_17", 943, 554, 949, 610, 17), // close
3722 ui_button_info("2_MC_18", 1, 649, -1, -1, 18), // scroll list up
3723 ui_button_info("2_MC_19", 1, 716, -1, -1, 19), // scroll list down
3724 ui_button_info("2_MC_20", 798, 695, 726, 667, 20), // host options
3725 ui_button_info("2_MC_21", 854, 681, -1, -1, 21), // help
3726 ui_button_info("2_MC_22", 854, 724, -1, -1, 22), // options
3727 ui_button_info("2_MC_23", 914, 681, 932, 667, 23), // commit
3732 #define MULTI_CREATE_NUM_TEXT 0
3734 #define MULTI_CREATE_NUM_TEXT 15
3736 UI_XSTR Multi_create_text[GR_NUM_RESOLUTIONS][MULTI_CREATE_NUM_BUTTONS] = {
3738 // not needed for FS1
3740 {"All", 1256, 36, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_ALL].button},
3741 {"Coop", 1257, 71, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_COOP].button},
3742 {"Team", 1258, 119, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_TEAM].button},
3743 {"Dogfight", 1259, 166, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_DOGFIGHT].button},
3744 {"Refresh Missions", 1260, 229, 130, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_PXO_REFRESH].button},
3745 {"Pilot Info", 1261, 467, 132, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_PILOT_INFO].button},
3746 {"Missions", 1262, 27, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_MISSION_FILTER].button},
3747 {"Campaigns", 1263, 110, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_CAMPAIGN_FILTER].button},
3748 {"Cancel", 387, 328, 364, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_CANCEL].button},
3749 {"1", 1264, 482, 381, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_TEAM0].button},
3750 {"2", 1265, 514, 381, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_TEAM1].button},
3751 {"Kick", 1266, 539, 381, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_KICK].button},
3752 {"Close", 1508, 582, 381, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_CLOSE].button},
3753 {"Host Options", 1267, 436, 423, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_HOST_OPTIONS].button},
3754 {"Commit", 1062, 572, 413, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_ACCEPT].button}
3758 // not needed for FS1
3760 {"All", 1256, 61, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_ALL].button},
3761 {"Coop", 1257, 124, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_COOP].button},
3762 {"Team", 1258, 194, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_TEAM].button},
3763 {"Dogfight", 1259, 261, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_DOGFIGHT].button},
3764 {"Refresh Missions", 1260, 501, 218, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_PXO_REFRESH].button},
3765 {"Pilot Info", 1261, 814, 216, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_PILOT_INFO].button},
3766 {"Missions", 1262, 46, 601, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_MISSION_FILTER].button},
3767 {"Campaigns", 1263, 174, 601, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_CAMPAIGN_FILTER].button},
3768 {"Cancel", 387, 559, 582, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_CANCEL].button},
3769 {"1", 1264, 772, 610, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_TEAM0].button},
3770 {"2", 1265, 826, 610, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_TEAM1].button},
3771 {"Kick", 1266, 872, 610, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_KICK].button},
3772 {"Close", 1508, 949, 610, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_CLOSE].button},
3773 {"Host Options", 1267, 755, 683, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_HOST_OPTIONS].button},
3774 {"Commit", 1062, 932, 667, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_ACCEPT].button}
3779 // squad war checkbox
3780 UI_CHECKBOX Multi_create_sw_checkbox;
3781 char *Multi_create_sw_checkbox_fname[GR_NUM_RESOLUTIONS] = {
3785 int Multi_create_sw_checkbox_coords[GR_NUM_RESOLUTIONS][2] = {
3793 int Multi_create_sw_checkbox_text[GR_NUM_RESOLUTIONS][2] = {
3802 // game information text areas
3803 int Mc_list_coords[GR_NUM_RESOLUTIONS][4] = {
3816 int Mc_players_coords[GR_NUM_RESOLUTIONS][4] = {
3829 int Mc_info_coords[GR_NUM_RESOLUTIONS][4] = {
3842 // mission icon stuff
3843 int Mc_icon_type_coords[GR_NUM_RESOLUTIONS][2] = {
3845 38, -2 // y is an offset
3848 61, -2 // y is an offset
3852 int Mc_icon_volition_coords[GR_NUM_RESOLUTIONS][2] = {
3854 61, -1 // y is an offset
3857 98, 1 // y is an offset
3861 int Mc_icon_silent_coords[GR_NUM_RESOLUTIONS][2] = {
3863 72, 0 // y is an offset
3866 115, 0 // y is an offset
3870 int Mc_icon_valid_coords[GR_NUM_RESOLUTIONS][2] = {
3872 91, 0 // y is an offset
3875 146, 0 // y is an offset
3879 // mission/campaign list column areas
3880 int Mc_column1_w[GR_NUM_RESOLUTIONS] = {
3885 int Mc_column2_w[GR_NUM_RESOLUTIONS] = {
3890 int Mc_column3_w[GR_NUM_RESOLUTIONS] = {
3895 int Mc_mission_name_x[GR_NUM_RESOLUTIONS] = {
3900 int Mc_mission_count_x[GR_NUM_RESOLUTIONS] = {
3905 int Mc_mission_fname_x[GR_NUM_RESOLUTIONS] = {
3910 int Mc_create_game_text[GR_NUM_RESOLUTIONS][2] = {
3911 {13, 116}, // GR_640
3912 {21, 186} // GR_1024
3915 int Mc_players_text[GR_NUM_RESOLUTIONS][2] = {
3916 {467, 150}, // GR_640
3917 {747, 240} // GR_1024
3920 int Mc_team_text[GR_NUM_RESOLUTIONS][2] = {
3921 {484, 342}, // GR_640
3922 {774, 547} // GR_1024
3925 int Mc_slider_coords[GR_NUM_RESOLUTIONS][4] = {
3927 3, 197, 13, 105 // GR_640
3930 5, 316, 20, 168 // GR_1024
3934 char *Mc_slider_bitmap[GR_NUM_RESOLUTIONS] = {
3939 // player list control thingie defs
3940 #define MULTI_CREATE_PLIST_MAX_DISPLAY 20
3941 int Multi_create_plist_select_flag; // flag indicating if we have a play selected
3942 short Multi_create_plist_select_id; // the net address of the currently selected player (for lookup)
3944 // master tracker details
3945 int Multi_create_frame_count; // framecount
3946 int Multi_create_mt_tried_login; // attempted to login this server on the MT
3948 // mission filter settings
3949 int Multi_create_filter; // what mode we're in
3951 // game/campaign list control defs
3952 int Multi_create_list_max_display[GR_NUM_RESOLUTIONS] = {
3958 int Multi_create_list_count; // number of items in listbox
3959 int Multi_create_list_mode; // 0 == mission mode, 1 == campaign mode
3960 int Multi_create_list_start; // where to start displaying from
3961 int Multi_create_list_select; // which item is currently highlighted
3962 int Multi_create_files_loaded;
3964 char Multi_create_files_array[MULTI_CREATE_MAX_LIST_ITEMS][MAX_FILENAME_LEN];
3966 int Multi_create_mission_count; // how many we have
3967 int Multi_create_campaign_count;
3968 multi_create_info Multi_create_mission_list[MULTI_CREATE_MAX_LIST_ITEMS];
3969 multi_create_info Multi_create_campaign_list[MULTI_CREATE_MAX_LIST_ITEMS];
3971 // use a pointer for the file list. Will point to either the missions or the campaigns
3972 multi_create_info *Multi_create_file_list;
3974 // LOCAL function definitions
3975 void multi_create_check_buttons();
3976 void multi_create_button_pressed(int n);
3977 void multi_create_init_as_server();
3978 void multi_create_init_as_client();
3979 void multi_create_do_netstuff();
3980 void multi_create_plist_scroll_up();
3981 void multi_create_plist_scroll_down();
3982 void multi_create_plist_process();
3983 void multi_create_plist_blit_normal();
3984 void multi_create_plist_blit_team();
3985 void multi_create_list_scroll_up();
3986 void multi_create_list_scroll_down();
3987 void multi_create_list_do();
3988 void multi_create_list_select_item(int n);
3989 void multi_create_list_blit_icons(int list_index, int y_start);
3990 void multi_create_accept_hit();
3991 void multi_create_draw_filter_buttons();
3992 void multi_create_set_selected_team(int team);
3993 short multi_create_get_mouse_id();
3994 int multi_create_ok_to_commit();
3995 int multi_create_verify_cds();
3996 void multi_create_refresh_pxo();
3997 void multi_create_sw_clicked();
3999 // since we can selectively filter out mission/campaign types we always need to map a selected index (which is relative
4000 // to the displayed list), to an absolute index (which is relative to the total file list - some of which may filtered out)
4001 void multi_create_select_to_filename(int select_index,char *filename);
4002 int multi_create_select_to_index(int select_index);
4004 int Multi_create_should_show_popup = 0;
4007 // sorting function to sort mission lists.. Basic sorting on mission name
4008 int multi_create_sort_func(const void *a, const void *b)
4010 multi_create_info *m1, *m2;
4012 m1 = (multi_create_info *)a;
4013 m2 = (multi_create_info *)b;
4015 return ( strcmp(m1->name, m2->name) );
4018 void multi_create_setup_list_data(int mode)
4020 int idx,should_sort,switched_modes;
4022 // set the current mode
4025 if((Multi_create_list_mode != mode) && (mode != -1)){
4026 Multi_create_list_mode = mode;
4029 // set up the list pointers
4030 if ( Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ) {
4031 Multi_create_file_list = Multi_create_mission_list;
4032 } else if ( Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ) {
4033 Multi_create_file_list = Multi_create_campaign_list;
4039 // get the mission count based upon the filter selected
4040 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
4041 switch(Multi_create_filter){
4042 case MISSION_TYPE_MULTI:
4043 Multi_create_list_count = Multi_create_mission_count;
4046 Multi_create_list_count = 0;
4047 // find all missions which match
4048 for(idx=0;idx<Multi_create_mission_count;idx++){
4049 if(Multi_create_mission_list[idx].flags & Multi_create_filter){
4050 Multi_create_list_count++;
4054 // if we switched modes and we have more than 0 items, sort them
4055 if(switched_modes && (Multi_create_list_count > 0)){
4060 } else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
4061 switch(Multi_create_filter){
4062 case MISSION_TYPE_MULTI:
4063 Multi_create_list_count = Multi_create_campaign_count;
4066 Multi_create_list_count = 0;
4067 // find all missions which match
4068 for(idx=0;idx<Multi_create_campaign_count;idx++){
4069 if(Multi_create_campaign_list[idx].flags & Multi_create_filter){
4070 Multi_create_list_count++;
4074 // if we switched modes and we have more than 0 items, sort them
4075 if(switched_modes && (Multi_create_list_count > 0)){
4082 // reset the list start and selected indices
4083 Multi_create_list_start = 0;
4084 Multi_create_list_select = -1;
4085 multi_create_list_select_item(Multi_create_list_start);
4087 // sort the list of missions if necessary
4089 qsort(Multi_create_file_list, Multi_create_list_count, sizeof(multi_create_info), multi_create_sort_func);
4094 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);
4098 void multi_create_game_init()
4103 // now make sure to initialze various netgame stuff based upon whether we're on a standalone or not
4104 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4105 multi_create_init_as_server();
4107 multi_create_init_as_client();
4110 // initialize the player list data
4111 Multi_create_plist_select_flag = 0;
4112 Multi_create_plist_select_id = -1;
4114 // create the interface window
4115 Multi_create_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
4116 Multi_create_window.set_mask_bmap(Multi_create_bitmap_mask_fname[gr_screen.res]);
4118 // load the background bitmap
4119 Multi_create_bitmap = bm_load(Multi_create_bitmap_fname[gr_screen.res]);
4120 if(Multi_create_bitmap < 0){
4121 // we failed to load the bitmap - this is very bad
4125 // close any previous existing instances of the chatbox and create a new one
4129 // load the help overlay
4130 help_overlay_load(MULTI_CREATE_OVERLAY);
4131 help_overlay_set_state(MULTI_CREATE_OVERLAY, 0);
4133 // initialize the common notification messaging
4134 multi_common_notify_init();
4136 // use the common interface palette
4137 multi_common_set_palette();
4139 // create the interface buttons
4140 for(idx=0; idx<MULTI_CREATE_NUM_BUTTONS; idx++){
4141 b = &Multi_create_buttons[gr_screen.res][idx];
4143 // create the object
4144 b->button.create(&Multi_create_window, "", b->x, b->y, 1, 1, ((idx == MC_SCROLL_LIST_UP) || (idx == MC_SCROLL_LIST_DOWN)), 1);
4146 // set the sound to play when highlighted
4147 b->button.set_highlight_action(common_play_highlight_sound);
4149 // set the ani for the button
4150 b->button.set_bmaps(b->filename);
4153 b->button.link_hotspot(b->hotspot);
4155 // some special case stuff for the pxo refresh button
4156 if(idx == MC_PXO_REFRESH){
4157 // if not a PXO game, or if I'm not a server disable and hide the button
4158 if(!MULTI_IS_TRACKER_GAME || !MULTIPLAYER_MASTER){
4160 b->button.disable();
4166 for(idx=0; idx<MULTI_CREATE_NUM_TEXT; idx++){
4167 Multi_create_window.add_XSTR(&Multi_create_text[gr_screen.res][idx]);
4170 // if this is a PXO game, enable the squadwar checkbox
4171 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);
4172 Multi_create_sw_checkbox.set_bmaps(Multi_create_sw_checkbox_fname[gr_screen.res], 6, 0);
4173 if(!MULTI_IS_TRACKER_GAME){
4174 Multi_create_sw_checkbox.hide();
4175 Multi_create_sw_checkbox.disable();
4179 // disable squad war button in demo
4180 Multi_create_sw_checkbox.hide();
4181 Multi_create_sw_checkbox.disable();
4184 // initialize the mission type filtering mode
4185 Multi_create_filter = MISSION_TYPE_MULTI;
4187 // initialize the list mode, and load in a list
4188 memset(Multi_create_mission_list, 0, sizeof(multi_create_info) * MULTI_CREATE_MAX_LIST_ITEMS);
4189 memset(Multi_create_campaign_list, 0, sizeof(multi_create_info) * MULTI_CREATE_MAX_LIST_ITEMS);
4190 for(idx=0; idx<MULTI_CREATE_MAX_LIST_ITEMS; idx++){
4191 Multi_create_mission_list[idx].valid_status = MVALID_STATUS_UNKNOWN;
4192 Multi_create_campaign_list[idx].valid_status = MVALID_STATUS_UNKNOWN;
4194 Multi_create_list_mode = MULTI_CREATE_SHOW_MISSIONS;
4195 Multi_create_list_start = -1;
4196 Multi_create_list_select = -1;
4197 Multi_create_list_count = 0;
4200 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);
4203 // create the player list select button
4204 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);
4205 Multi_create_player_select_button.hide();
4207 // create the mission/campaign list select button
4208 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);
4209 Multi_create_list_select_button.hide();
4211 // set hotkeys for a couple of things.
4212 Multi_create_buttons[gr_screen.res][MC_ACCEPT].button.set_hotkey(KEY_CTRLED+KEY_ENTER);
4214 // init some master tracker stuff
4215 Multi_create_frame_count = 0;
4216 Multi_create_mt_tried_login = 0;
4218 // remove campaign flags
4219 Game_mode &= ~(GM_CAMPAIGN_MODE);
4221 // send any pilots as appropriate
4222 multi_data_send_my_junk();
4223 Multi_create_file_list = Multi_create_mission_list;
4225 Multi_create_campaign_count = 0;
4226 Multi_create_mission_count = 0;
4227 Multi_create_files_loaded = 0;
4230 void multi_create_game_do()
4234 char *loading_str = XSTR("Loading", 1336);
4238 // set this if we want to show the pilot info popup
4239 Multi_create_should_show_popup = 0;
4241 // first thing is to load the files
4242 if ( !Multi_create_files_loaded ) {
4243 // if I am a client, send a list request to the server for the missions
4244 if ( MULTIPLAYER_CLIENT ) {
4245 send_mission_list_request( MISSION_LIST_REQUEST );
4249 loading_bitmap = bm_load(Multi_create_loading_fname[gr_screen.res]);
4251 // draw the background, etc
4253 GR_MAYBE_CLEAR_RES(Multi_create_bitmap);
4254 if(Multi_create_bitmap != -1){
4255 gr_set_bitmap(Multi_create_bitmap);
4259 if ( loading_bitmap > -1 ){
4260 gr_set_bitmap(loading_bitmap);
4262 gr_bitmap( Please_wait_coords[gr_screen.res][MC_X_COORD], Please_wait_coords[gr_screen.res][MC_Y_COORD] );
4264 // draw "Loading" on it
4266 gr_set_color_fast(&Color_normal);
4268 gr_get_string_size(&str_w, &str_h, loading_str);
4269 gr_string((gr_screen.max_w - str_w) / 2, (gr_screen.max_h - str_h) / 2, loading_str);
4275 multi_create_list_load_missions();
4276 multi_create_list_load_campaigns();
4278 // if this is a tracker game, validate missions
4279 if(MULTI_IS_TRACKER_GAME){
4280 multi_update_valid_missions();
4283 // update the file list
4284 multi_create_setup_list_data(MULTI_CREATE_SHOW_MISSIONS);
4287 // don't bother setting netgame state if ont the server
4288 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4289 Netgame.game_state = NETGAME_STATE_FORMING;
4290 send_netgame_update_packet();
4293 // if we're on the standalone we have to tell him that we're now in the host setup screen
4294 Net_player->state = NETPLAYER_STATE_HOST_SETUP;
4295 send_netplayer_update_packet();
4297 Multi_create_files_loaded = 1;
4300 int k = chatbox_process();
4301 k = Multi_create_window.process(k,0);
4304 // same as the cancel button
4306 if(help_overlay_active(MULTI_CREATE_OVERLAY)){
4307 help_overlay_set_state(MULTI_CREATE_OVERLAY,0);
4309 gamesnd_play_iface(SND_USER_SELECT);
4310 multi_quit_game(PROMPT_HOST);
4315 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
4316 help_overlay_set_state(MULTI_CREATE_OVERLAY, 0);
4319 // process any button clicks
4320 multi_create_check_buttons();
4322 // do any network related stuff
4323 multi_create_do_netstuff();
4325 // draw the background, etc
4327 GR_MAYBE_CLEAR_RES(Multi_create_bitmap);
4328 if(Multi_create_bitmap != -1){
4329 gr_set_bitmap(Multi_create_bitmap);
4333 // if we're not in team vs. team mode, don't draw the team buttons
4334 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
4335 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.hide();
4336 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.hide();
4337 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.disable();
4338 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.disable();
4340 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.enable();
4341 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.enable();
4342 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.unhide();
4343 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.unhide();
4346 // draw the window itself
4347 Multi_create_window.draw();
4349 gr_set_color_fast(&Color_normal);
4352 // draw Create Game text
4353 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));
4355 // draw players text
4356 gr_string(Mc_players_text[gr_screen.res][MC_X_COORD], Mc_players_text[gr_screen.res][MC_Y_COORD], XSTR("Players", 1269));
4358 // draw players text
4359 gr_string(Mc_team_text[gr_screen.res][MC_X_COORD], Mc_team_text[gr_screen.res][MC_Y_COORD], XSTR("Team", 1258));
4362 // process and display the player list
4363 // NOTE : this must be done before the buttons are checked to insure that a player hasn't dropped
4364 multi_create_plist_process();
4365 if(Netgame.type_flags & NG_TYPE_TEAM){
4366 multi_create_plist_blit_team();
4368 multi_create_plist_blit_normal();
4371 // process and display the game/campaign list
4372 multi_create_list_do();
4374 // draw the correct mission filter button
4375 multi_create_draw_filter_buttons();
4377 // display any text in the info area
4378 multi_common_render_text();
4380 // display any pending notification messages
4381 multi_common_notify_do();
4383 // force the correct mission/campaign button to light up
4384 if( Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ){
4385 Multi_create_buttons[gr_screen.res][MC_MISSION_FILTER].button.draw_forced(2);
4387 Multi_create_buttons[gr_screen.res][MC_CAMPAIGN_FILTER].button.draw_forced(2);
4390 // force draw the closed button if it is toggled on
4391 if(Netgame.flags & NG_FLAG_TEMP_CLOSED){
4392 Multi_create_buttons[gr_screen.res][MC_CLOSE].button.draw_forced(2);
4395 // process and show the chatbox thingie
4399 Multi_create_window.draw_tooltip();
4401 // display the voice status indicator
4402 multi_common_voice_display_status();
4404 // blit the help overlay if necessary
4405 help_overlay_maybe_blit(MULTI_CREATE_OVERLAY);
4408 if(MULTI_IS_TRACKER_GAME){
4409 if(Netgame.type_flags & NG_TYPE_SW){
4410 gr_set_color_fast(&Color_bright);
4412 gr_set_color_fast(&Color_normal);
4414 gr_string(Multi_create_sw_checkbox_text[gr_screen.res][0], Multi_create_sw_checkbox_text[gr_screen.res][1], "SquadWar");
4420 // if we're supposed to show the pilot info popup, do it now
4421 if(Multi_create_should_show_popup){
4422 // get the player index and address of the player item the mouse is currently over
4423 if(Multi_create_plist_select_flag){
4424 player_index = find_player_id(Multi_create_plist_select_id);
4425 if(player_index != -1){
4426 multi_pinfo_popup(&Net_players[player_index]);
4431 // increment the frame count
4432 Multi_create_frame_count++;
4435 void multi_create_game_close()
4437 // unload any bitmaps
4438 if(!bm_unload(Multi_create_bitmap)){
4439 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_create_bitmap_fname[gr_screen.res]));
4442 // unload the help overlay
4443 help_overlay_unload(MULTI_CREATE_OVERLAY);
4445 // destroy the chatbox
4448 // destroy the UI_WINDOW
4449 Multi_create_window.destroy();
4452 void multi_create_check_buttons()
4455 for(idx=0;idx<MULTI_CREATE_NUM_BUTTONS;idx++){
4456 // we only really need to check for one button pressed at a time, so we can break after
4458 if(Multi_create_buttons[gr_screen.res][idx].button.pressed()){
4459 multi_create_button_pressed(idx);
4464 // if the squad war checkbox was clicked
4465 if(Multi_create_sw_checkbox.changed()){
4466 multi_create_sw_clicked();
4470 void multi_create_button_pressed(int n)
4476 gamesnd_play_iface(SND_USER_SELECT);
4477 multi_quit_game(PROMPT_HOST);
4480 // if valid commit conditions have not been met
4481 if(!multi_create_ok_to_commit()){
4486 multi_create_accept_hit();
4491 if(!help_overlay_active(MULTI_CREATE_OVERLAY)){
4492 help_overlay_set_state(MULTI_CREATE_OVERLAY,1);
4494 help_overlay_set_state(MULTI_CREATE_OVERLAY,0);
4498 // scroll the info text box up
4499 case MC_SCROLL_INFO_UP:
4500 multi_common_scroll_text_up();
4503 // scroll the info text box down
4504 case MC_SCROLL_INFO_DOWN:
4505 multi_common_scroll_text_down();
4508 // scroll the player list up
4509 case MC_SCROLL_PLAYERS_UP:
4510 multi_create_plist_scroll_up();
4513 // scroll the player list down
4514 case MC_SCROLL_PLAYERS_DOWN:
4515 multi_create_plist_scroll_down();
4518 // scroll the game/campaign list up
4519 case MC_SCROLL_LIST_UP:
4520 multi_create_list_scroll_up();
4522 Multi_create_slider.forceUp(); // move slider up
4526 // scroll the game/campaign list down
4527 case MC_SCROLL_LIST_DOWN:
4528 multi_create_list_scroll_down();
4530 Multi_create_slider.forceDown(); // move slider down
4534 // go to the options screen
4536 gamesnd_play_iface(SND_USER_SELECT);
4537 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
4540 // show all missions
4542 if(Multi_create_filter != MISSION_TYPE_MULTI){
4543 gamesnd_play_iface(SND_USER_SELECT);
4544 Multi_create_filter = MISSION_TYPE_MULTI;
4545 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4547 gamesnd_play_iface(SND_GENERAL_FAIL);
4551 // show cooperative missions
4553 if(Multi_create_filter != MISSION_TYPE_MULTI_COOP){
4554 gamesnd_play_iface(SND_USER_SELECT);
4555 Multi_create_filter = MISSION_TYPE_MULTI_COOP;
4556 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4558 gamesnd_play_iface(SND_GENERAL_FAIL);
4562 // show team vs. team missions
4564 if(Multi_create_filter != MISSION_TYPE_MULTI_TEAMS){
4565 gamesnd_play_iface(SND_USER_SELECT);
4566 Multi_create_filter = MISSION_TYPE_MULTI_TEAMS;
4567 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4569 gamesnd_play_iface(SND_GENERAL_FAIL);
4573 // show dogfight missions
4575 case MC_SHOW_DOGFIGHT:
4576 if (Multi_create_filter != MISSION_TYPE_MULTI_DOGFIGHT){
4577 gamesnd_play_iface(SND_USER_SELECT);
4578 Multi_create_filter = MISSION_TYPE_MULTI_DOGFIGHT;
4579 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4581 gamesnd_play_iface(SND_GENERAL_FAIL);
4586 // toggle temporary netgame closed on/off
4588 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4589 Netgame.flags ^= NG_FLAG_TEMP_CLOSED;
4591 Netgame.options.flags |= MLO_FLAG_TEMP_CLOSED;
4592 multi_options_update_netgame();
4594 gamesnd_play_iface(SND_USER_SELECT);
4597 // kick the currently selected player (if possible)
4599 // lookup the player at the specified index
4600 if(Multi_create_plist_select_flag){
4601 idx = find_player_id(Multi_create_plist_select_id);
4602 // kick him - but don't ban him
4604 multi_kick_player(idx,0);
4609 // switch to individual mission mode and load in a list
4610 case MC_MISSION_FILTER:
4611 if(Multi_create_list_mode != MULTI_CREATE_SHOW_MISSIONS){
4612 Netgame.campaign_mode = MP_SINGLE;
4614 gamesnd_play_iface(SND_USER_SELECT);
4616 // update the file list
4617 multi_create_setup_list_data(MULTI_CREATE_SHOW_MISSIONS);
4619 gamesnd_play_iface(SND_GENERAL_FAIL);
4623 // switch to campaign mode and load in a list
4624 case MC_CAMPAIGN_FILTER:
4625 // switch off squad war
4626 Multi_create_sw_checkbox.set_state(0);
4627 Netgame.type_flags = NG_TYPE_COOP;
4629 if(Multi_create_list_mode != MULTI_CREATE_SHOW_CAMPAIGNS){
4630 Netgame.campaign_mode = MP_CAMPAIGN;
4632 gamesnd_play_iface(SND_USER_SELECT);
4634 // update the file list
4635 multi_create_setup_list_data(MULTI_CREATE_SHOW_CAMPAIGNS);
4637 gamesnd_play_iface(SND_GENERAL_FAIL);
4641 // attempt to set the selected player's team
4643 multi_create_set_selected_team(0);
4644 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4645 multi_team_send_update();
4649 // attempt to set the selected player's team
4651 multi_create_set_selected_team(1);
4652 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4653 multi_team_send_update();
4657 // popup the pilot info dialog for the currently selected pilot (will occur at the end of the frame)
4659 Multi_create_should_show_popup = 1;
4662 // go to the host options screen
4663 case MC_HOST_OPTIONS:
4664 gamesnd_play_iface(SND_USER_SELECT);
4665 gameseq_post_event(GS_EVENT_MULTI_HOST_OPTIONS);
4668 // refresh PXO file list
4669 case MC_PXO_REFRESH:
4670 if(!MULTI_IS_TRACKER_GAME){
4673 multi_create_refresh_pxo();
4677 gamesnd_play_iface(SND_GENERAL_FAIL);
4678 multi_common_add_notify(XSTR("Not implemented yet!",760));
4683 // do stuff like pinging servers, sending out requests, etc
4684 void multi_create_do_netstuff()
4688 // if not on a standalone
4689 void multi_create_init_as_server()
4691 // set me up as the host and master
4692 Net_player->flags |= (NETINFO_FLAG_AM_MASTER | NETINFO_FLAG_GAME_HOST);
4695 // if on a standalone
4696 void multi_create_init_as_client()
4698 Net_player->flags |= NETINFO_FLAG_GAME_HOST;
4701 // scroll up through the player list
4702 void multi_create_plist_scroll_up()
4704 gamesnd_play_iface(SND_GENERAL_FAIL);
4707 // scroll down through the player list
4708 void multi_create_plist_scroll_down()
4710 gamesnd_play_iface(SND_GENERAL_FAIL);
4713 void multi_create_plist_process()
4715 int test_count,idx,player_index;
4717 // first determine if there are 0 players in the game. This should never happen since the host is _always_ in the game
4719 for(idx=0;idx<MAX_PLAYERS;idx++){
4720 // count anyone except the standalone server (if applicable)
4721 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
4725 if(test_count <= 0){
4729 // if we had a selected item but that player has left, select myself instead
4730 if(Multi_create_plist_select_flag){
4731 player_index = find_player_id(Multi_create_plist_select_id);
4732 if(player_index == -1){
4733 Multi_create_plist_select_id = Net_player->player_id;
4736 Multi_create_plist_select_flag = 1;
4737 Multi_create_plist_select_id = Net_player->player_id;
4740 // if the player has clicked somewhere in the player list area
4741 if(Multi_create_player_select_button.pressed()){
4744 // get the player index and address of the player item the mouse is currently over
4745 player_id = multi_create_get_mouse_id();
4746 player_index = find_player_id(player_id);
4747 if(player_index != -1){
4748 Multi_create_plist_select_flag = 1;
4749 Multi_create_plist_select_id = player_id;
4754 void multi_create_plist_blit_normal()
4757 char str[CALLSIGN_LEN+5];
4758 int y_start = Mc_players_coords[gr_screen.res][MC_Y_COORD];
4761 // display all the players
4762 for(idx=0;idx<MAX_PLAYERS;idx++){
4763 // count anyone except the standalone server (if applicable)
4764 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
4768 // highlight him if he's the host
4769 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4770 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4771 gr_set_color_fast(&Color_text_active_hi);
4773 gr_set_color_fast(&Color_bright);
4776 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4777 gr_set_color_fast(&Color_text_active);
4779 gr_set_color_fast(&Color_text_normal);
4783 // optionally draw his CD status
4784 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4785 gr_set_bitmap(Multi_common_icons[MICON_CD]);
4786 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4788 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4791 // make sure the string will fit, then display it
4792 strcpy(str,Net_players[idx].player->callsign);
4793 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4794 strcat(str,XSTR("(O)",787)); // [[ Observer ]]
4796 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4797 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4804 void multi_create_plist_blit_team()
4807 char str[CALLSIGN_LEN+1];
4808 int y_start = Mc_players_coords[gr_screen.res][MC_Y_COORD];
4811 // display all the red players first
4812 for(idx=0;idx<MAX_PLAYERS;idx++){
4813 // count anyone except the standalone server (if applicable)
4814 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
4815 // reset total offset
4818 // highlight him if he's the host
4819 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4820 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4821 gr_set_color_fast(&Color_text_active_hi);
4823 // be sure to blit the correct team button
4824 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.draw_forced(2);
4826 gr_set_color_fast(&Color_bright);
4829 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4830 gr_set_color_fast(&Color_text_active);
4832 // be sure to blit the correct team button
4833 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.draw_forced(2);
4835 gr_set_color_fast(&Color_text_normal);
4839 // optionally draw his CD status
4840 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4841 gr_set_bitmap(Multi_common_icons[MICON_CD]);
4842 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4844 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4847 // blit the red team indicator
4848 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
4849 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
4850 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT]);
4851 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4853 total_offset += Multi_common_icon_dims[MICON_TEAM0_SELECT][0] + 1;
4856 if(Multi_common_icons[MICON_TEAM0] != -1){
4857 gr_set_bitmap(Multi_common_icons[MICON_TEAM0]);
4858 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4860 total_offset += Multi_common_icon_dims[MICON_TEAM0][0] + 1;
4864 // make sure the string will fit
4865 strcpy(str,Net_players[idx].player->callsign);
4866 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4867 strcat(str,XSTR("(O)",787));
4869 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4871 // display him in the correct half of the list depending on his team
4872 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4877 // display all the green players next
4878 for(idx=0;idx<MAX_PLAYERS;idx++){
4879 // count anyone except the standalone server (if applicable)
4880 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
4881 // reset total offset
4884 // highlight him if he's the host
4885 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4886 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4887 gr_set_color_fast(&Color_text_active_hi);
4889 // be sure to blit the correct team button
4890 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.draw_forced(2);
4892 gr_set_color_fast(&Color_bright);
4895 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4896 gr_set_color_fast(&Color_text_active);
4898 // be sure to blit the correct team button
4899 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.draw_forced(2);
4901 gr_set_color_fast(&Color_text_normal);
4905 // optionally draw his CD status
4906 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4907 gr_set_bitmap(Multi_common_icons[MICON_CD]);
4908 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4910 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4913 // blit the red team indicator
4914 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
4915 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
4916 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT]);
4917 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4919 total_offset += Multi_common_icon_dims[MICON_TEAM1_SELECT][0] + 1;
4922 if(Multi_common_icons[MICON_TEAM1] != -1){
4923 gr_set_bitmap(Multi_common_icons[MICON_TEAM1]);
4924 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4926 total_offset += Multi_common_icon_dims[MICON_TEAM1][0] + 1;
4930 // make sure the string will fit
4931 strcpy(str,Net_players[idx].player->callsign);
4932 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4933 strcat(str,XSTR("(O)",787));
4935 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4937 // display him in the correct half of the list depending on his team
4938 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4944 void multi_create_list_scroll_up()
4946 if(Multi_create_list_start > 0){
4947 Multi_create_list_start--;
4949 gamesnd_play_iface(SND_SCROLL);
4951 gamesnd_play_iface(SND_GENERAL_FAIL);
4955 void multi_create_list_scroll_down()
4957 if((Multi_create_list_count - Multi_create_list_start) > Multi_create_list_max_display[gr_screen.res]){
4958 Multi_create_list_start++;
4960 gamesnd_play_iface(SND_SCROLL);
4962 gamesnd_play_iface(SND_GENERAL_FAIL);
4966 // gets a list of multiplayer misisons
4967 void multi_create_list_load_missions()
4969 char *fname, mission_name[NAME_LENGTH+1];
4970 char wild_card[256];
4973 memset(wild_card, 0, 256);
4974 strcpy(wild_card, NOX("*"));
4975 strcat(wild_card, FS_MISSION_FILE_EXT);
4976 file_count = cf_get_file_list_preallocated(MULTI_CREATE_MAX_LIST_ITEMS, Multi_create_files_array, NULL, CF_TYPE_MISSIONS, wild_card);
4977 Multi_create_mission_count = 0;
4979 // maybe create a standalone dialog
4980 if(Game_mode & GM_STANDALONE_SERVER){
4981 std_create_gen_dialog("Loading missions");
4982 std_gen_set_text("Mission:", 1);
4985 for(idx = 0; idx < file_count; idx++){
4986 int flags,max_players;
4990 fname = Multi_create_files_array[idx];
4992 // tack on any necessary file extension
4993 filename = cf_add_ext( fname, FS_MISSION_FILE_EXT );
4995 // for multiplayer beta builds, only accept builtin missions
4996 #if defined(MULTIPLAYER_BETA_BUILD) || defined(FS2_DEMO)
4997 if(game_find_builtin_mission(filename) == NULL){
5000 #elif defined(PD_BUILD)
5001 if((game_find_builtin_mission(filename) == NULL) && !strstr(filename, "peterdrake")){
5006 if(Game_mode & GM_STANDALONE_SERVER){
5007 std_gen_set_text(filename, 2);
5010 flags = mission_parse_is_multi(filename, mission_name);
5012 // if the mission is a multiplayer mission, then add it to the mission list
5014 max_players = mission_parse_get_multi_mission_info( filename );
5015 m_respawn = The_mission.num_respawns;
5017 if ( Multi_create_mission_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
5018 multi_create_info *mcip;
5020 mcip = &Multi_create_mission_list[Multi_create_mission_count];
5021 strcpy(mcip->filename, filename );
5022 strcpy(mcip->name, mission_name );
5023 mcip->flags = flags;
5024 mcip->respawn = m_respawn;
5025 mcip->max_players = (ubyte)max_players;
5027 // get any additional information for possibly builtin missions
5028 fs_builtin_mission *fb = game_find_builtin_mission(filename);
5032 Multi_create_mission_count++;
5038 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);
5041 // maybe create a standalone dialog
5042 if(Game_mode & GM_STANDALONE_SERVER){
5043 std_destroy_gen_dialog();
5047 void multi_create_list_load_campaigns()
5050 int idx, file_count;
5051 int campaign_type,max_players;
5053 char wild_card[256];
5055 // maybe create a standalone dialog
5056 if(Game_mode & GM_STANDALONE_SERVER){
5057 std_create_gen_dialog("Loading campaigns");
5058 std_gen_set_text("Campaign:", 1);
5061 Multi_create_campaign_count = 0;
5062 memset(wild_card, 0, 256);
5063 strcpy(wild_card, NOX("*"));
5064 strcat(wild_card, FS_CAMPAIGN_FILE_EXT);
5065 file_count = cf_get_file_list_preallocated(MULTI_CREATE_MAX_LIST_ITEMS, Multi_create_files_array, NULL, CF_TYPE_MISSIONS, wild_card);
5066 for(idx = 0; idx < file_count; idx++){
5068 char *filename, name[NAME_LENGTH];
5070 fname = Multi_create_files_array[idx];
5072 // tack on any necessary file extension
5073 filename = cf_add_ext( fname, FS_CAMPAIGN_FILE_EXT );
5075 // for multiplayer beta builds, only accept builtin missions
5076 #if defined(MULTIPLAYER_BETA_BUILD) || defined(FS2_DEMO)
5077 if(game_find_builtin_mission(filename) == NULL){
5080 #elif defined(PD_BUILD)
5081 if((game_find_builtin_mission(filename) == NULL) && !strstr(filename, "peterdrake")){
5086 if(Game_mode & GM_STANDALONE_SERVER){
5087 std_gen_set_text(filename, 2);
5090 // if the campaign is a multiplayer campaign, then add the data to the campaign list items
5091 flags = mission_campaign_parse_is_multi( filename, name );
5092 if( flags != CAMPAIGN_TYPE_SINGLE && mission_campaign_get_info(filename,title,&campaign_type,&max_players)) {
5093 if ( Multi_create_campaign_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
5094 multi_create_info *mcip;
5096 mcip = &Multi_create_campaign_list[Multi_create_campaign_count];
5097 strcpy(mcip->filename, filename );
5098 strcpy(mcip->name, name );
5100 // setup various flags
5101 if ( flags == CAMPAIGN_TYPE_MULTI_COOP ){
5102 mcip->flags = MISSION_TYPE_MULTI_COOP | MISSION_TYPE_MULTI;
5103 } else if ( flags == CAMPAIGN_TYPE_MULTI_TEAMS ) {
5104 mcip->flags = MISSION_TYPE_MULTI_TEAMS | MISSION_TYPE_MULTI;
5106 Int3(); // bogus campaign multi type -- find allender
5109 // 0 respawns for campaign files (should be contained within the mission files themselves)
5112 // 0 max players for campaign files
5113 mcip->max_players = (unsigned char)max_players;
5115 // get any additional information for possibly builtin missions
5116 fs_builtin_mission *fb = game_find_builtin_mission(filename);
5120 Multi_create_campaign_count++;
5125 // maybe create a standalone dialog
5126 if(Game_mode & GM_STANDALONE_SERVER){
5127 std_destroy_gen_dialog();
5131 void multi_create_list_do()
5134 int start_index,stop_index;
5135 char selected_name[255];
5137 // bail early if there aren't any selectable items
5138 if(Multi_create_list_count == 0){
5142 // first check to see if the user has clicked on an item
5143 if(Multi_create_list_select_button.pressed()){
5145 Multi_create_list_select_button.get_mouse_pos(NULL,&y);
5148 // make sure we are selectedin valid indices
5149 if((item < Multi_create_list_max_display[gr_screen.res]) && (item >= 0)){
5150 item += Multi_create_list_start;
5152 if(item < Multi_create_list_count){
5153 multi_create_list_select_item(item);
5154 gamesnd_play_iface(SND_IFACE_MOUSE_CLICK);
5159 // bail early if we don't have a start position
5160 if(Multi_create_list_start == -1){
5164 // display the list of individual campaigns/missions
5166 int y_start = Mc_list_coords[gr_screen.res][MC_Y_COORD];
5168 start_index = multi_create_select_to_index(Multi_create_list_start);
5169 stop_index = Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ? Multi_create_mission_count : Multi_create_campaign_count;
5170 for(idx=start_index; idx<stop_index; idx++){
5171 // see if we should drop out
5172 if(count == Multi_create_list_max_display[gr_screen.res]){
5176 // see if we should filter out this mission
5177 if ( !(Multi_create_file_list[idx].flags & Multi_create_filter) ){
5181 // highlight the selected item
5182 multi_create_select_to_filename(Multi_create_list_select,selected_name);
5183 if(!strcmp(selected_name,Multi_create_file_list[idx].filename)){
5184 gr_set_color_fast(&Color_text_selected);
5186 gr_set_color_fast(&Color_text_normal);
5189 // draw the type icon
5190 multi_create_list_blit_icons(idx, y_start);
5192 // force fit the mission name string
5193 strcpy(selected_name,Multi_create_file_list[idx].name);
5194 gr_force_fit_string(selected_name,255,Mc_column1_w[gr_screen.res]);
5195 gr_string(Mc_mission_name_x[gr_screen.res],y_start,selected_name);
5197 // draw the max players if in mission mode
5198 sprintf(selected_name,"%d",(int)Multi_create_file_list[idx].max_players);
5199 gr_string(Mc_mission_count_x[gr_screen.res],y_start,selected_name);
5201 // force fit the mission filename string
5202 strcpy(selected_name,Multi_create_file_list[idx].filename);
5203 gr_force_fit_string(selected_name,255,Mc_column3_w[gr_screen.res]);
5204 gr_string(Mc_mission_fname_x[gr_screen.res],y_start,selected_name);
5211 // takes care of stuff like changing indices around and setting up the netgame structure
5212 void multi_create_list_select_item(int n)
5214 int abs_index,campaign_type,max_players;
5215 char title[NAME_LENGTH+1];
5216 netgame_info ng_temp;
5219 char *campaign_desc;
5221 // if not on the standalone server
5222 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5225 // on the standalone
5227 memset(&ng_temp,0,sizeof(netgame_info));
5231 if ( n != Multi_create_list_select ) {
5232 // check to see if this is a valid index, and bail if it is not
5233 abs_index = multi_create_select_to_index(n);
5234 if(abs_index == -1){
5238 Multi_create_list_select = n;
5240 // set the mission name
5241 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5242 multi_create_select_to_filename(n,ng->mission_name);
5244 multi_create_select_to_filename(n,ng->campaign_name);
5247 // make sure the netgame type is properly set
5248 int old_type = Netgame.type_flags;
5249 abs_index = multi_create_select_to_index(n);
5250 if(abs_index != -1){
5251 if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_TEAMS){
5252 // if we're in squad war mode, leave it as squad war
5253 if(old_type & NG_TYPE_SW){
5254 ng->type_flags = NG_TYPE_SW;
5256 ng->type_flags = NG_TYPE_TVT;
5258 } else if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_COOP){
5259 ng->type_flags = NG_TYPE_COOP;
5260 } else if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_DOGFIGHT){
5261 ng->type_flags = NG_TYPE_DOGFIGHT;
5265 // if we're no longer in a TvT game, just uncheck the squadwar checkbox
5266 if(!(ng->type_flags & NG_TYPE_TEAM)){
5267 Multi_create_sw_checkbox.set_state(0);
5270 // if we switched from something else to team vs. team mode, do some special processing
5271 if((ng->type_flags & NG_TYPE_TEAM) && (ng->type_flags != old_type) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
5275 switch(Multi_create_list_mode){
5276 case MULTI_CREATE_SHOW_MISSIONS:
5277 // don't forget to update the info box window thingie
5278 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5279 ship_init(); // mwa -- 10/15/97. Call this function to reset number of ships in mission
5280 ng->max_players = mission_parse_get_multi_mission_info( ng->mission_name );
5282 Assert(ng->max_players > 0);
5283 strcpy(ng->title,The_mission.name);
5285 // set the information area text
5286 multi_common_set_text(The_mission.mission_desc);
5288 // if we're on the standalone, send a request for the description
5290 send_netgame_descript_packet(&Netgame.server_addr,0);
5291 multi_common_set_text("");
5294 // set the respawns as appropriate
5295 if(Netgame.options.respawn <= Multi_create_file_list[abs_index].respawn){
5296 ng->respawn = Netgame.options.respawn;
5297 nprintf(("Network","Using netgame options for respawn count (%d %d)\n",Netgame.options.respawn,Multi_create_file_list[abs_index].respawn));
5299 ng->respawn = Multi_create_file_list[abs_index].respawn;
5300 nprintf(("Network","Using mission settings for respawn count (%d %d)\n",Netgame.options.respawn,Multi_create_file_list[abs_index].respawn));
5303 case MULTI_CREATE_SHOW_CAMPAIGNS:
5304 // if not on the standalone server
5305 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5306 // get the campaign info
5307 memset(title,0,NAME_LENGTH+1);
5308 if(!mission_campaign_get_info(ng->campaign_name,title,&campaign_type,&max_players, &campaign_desc)) {
5309 memset(ng->campaign_name,0,NAME_LENGTH+1);
5310 ng->max_players = 0;
5312 // if we successfully got the # of players
5314 memset(ng->title,0,NAME_LENGTH+1);
5315 strcpy(ng->title,title);
5316 ng->max_players = max_players;
5319 nprintf(("Network","MC MAX PLAYERS : %d\n",ng->max_players));
5321 // set the information area text
5322 // multi_common_set_text(ng->title);
5323 multi_common_set_text(campaign_desc);
5325 // if on the standalone server, send a request for the description
5327 // no descriptions currently kept for campaigns
5330 // netgame respawns are always 0 for campaigns (until the first mission is loaded)
5335 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5337 send_netgame_update_packet();
5339 // update all machines about stuff like respawns, etc.
5340 multi_options_update_netgame();
5342 multi_options_update_mission(ng, Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ? 1 : 0);
5347 void multi_create_list_blit_icons(int list_index, int y_start)
5349 multi_create_info *mcip;
5350 fs_builtin_mission *fb;
5353 // get a pointer to the list item
5354 max_index = Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ? Multi_create_mission_count - 1 : Multi_create_campaign_count - 1;
5355 if((list_index < 0) || (list_index > max_index)){
5358 mcip = &Multi_create_file_list[list_index];
5360 // blit the multiplayer type icons
5361 if(mcip->flags & MISSION_TYPE_MULTI_COOP){
5362 if(Multi_common_icons[MICON_COOP] >= 0){
5363 gr_set_bitmap(Multi_common_icons[MICON_COOP]);
5364 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5366 } else if(mcip->flags & MISSION_TYPE_MULTI_TEAMS){
5367 if(Multi_common_icons[MICON_TVT] >= 0){
5368 gr_set_bitmap(Multi_common_icons[MICON_TVT]);
5369 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5371 } else if(mcip->flags & MISSION_TYPE_MULTI_DOGFIGHT){
5372 if(Multi_common_icons[MICON_DOGFIGHT] >= 0){
5373 gr_set_bitmap(Multi_common_icons[MICON_DOGFIGHT]);
5374 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5378 // if its a valid mission, blit the valid mission icon
5379 if(MULTI_IS_TRACKER_GAME && (mcip->valid_status == MVALID_STATUS_VALID)){
5380 if(Multi_common_icons[MICON_VALID] >= 0){
5381 gr_set_bitmap(Multi_common_icons[MICON_VALID]);
5382 gr_bitmap(Mc_icon_valid_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_valid_coords[gr_screen.res][MC_Y_COORD]);
5386 // now see if its a builtin mission
5387 fb = game_find_builtin_mission(mcip->filename);
5388 // if the mission is from volition, blit the volition icon
5389 if((fb != NULL) && (fb->flags & FSB_FROM_VOLITION)){
5390 if(Multi_common_icons[MICON_VOLITION] >= 0){
5391 gr_set_bitmap(Multi_common_icons[MICON_VOLITION]);
5392 gr_bitmap(Mc_icon_volition_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_volition_coords[gr_screen.res][MC_Y_COORD]);
5397 // check for mdisk mission
5398 fb = game_find_builtin_mission(mcip->filename);
5399 if((fb != NULL) && (fb->flags & FSB_FROM_MDISK)){
5400 if(Multi_common_icons[MICON_MDISK] >= 0){
5401 gr_set_bitmap(Multi_common_icons[MICON_MDISK]);
5402 gr_bitmap(Mc_icon_silent_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_silent_coords[gr_screen.res][MC_Y_COORD]);
5408 void multi_create_accept_hit()
5410 char selected_name[255];
5411 int start_campaign = 0;
5413 // make sure all players have finished joining
5414 if(!multi_netplayer_state_check(NETPLAYER_STATE_JOINED,1)){
5415 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("Please wait until all clients have finished joining",788));
5416 gamesnd_play_iface(SND_GENERAL_FAIL);
5419 gamesnd_play_iface(SND_COMMIT_PRESSED);
5422 // do single mission stuff
5423 switch(Multi_create_list_mode){
5424 case MULTI_CREATE_SHOW_MISSIONS:
5425 if(Multi_create_list_select != -1){
5426 // set the netgame mode
5427 Netgame.campaign_mode = MP_SINGLE;
5429 // setup various filenames and mission names
5430 multi_create_select_to_filename(Multi_create_list_select,selected_name);
5431 strncpy( Game_current_mission_filename, selected_name, MAX_FILENAME_LEN );
5432 strncpy(Netgame.mission_name,selected_name,MAX_FILENAME_LEN);
5435 ml_printf(NOX("Starting single mission %s, with %d players"), Game_current_mission_filename, multi_num_players());
5437 multi_common_add_notify(XSTR("No mission selected!",789));
5442 case MULTI_CREATE_SHOW_CAMPAIGNS:
5443 // do campaign related stuff
5444 if(Multi_create_list_select != -1){
5445 // set the netgame mode
5446 Netgame.campaign_mode = MP_CAMPAIGN;
5448 // start a campaign instead of a single mission
5449 multi_create_select_to_filename(Multi_create_list_select,selected_name);
5450 multi_campaign_start(selected_name);
5454 ml_printf(NOX("Starting campaign %s, with %d players"), selected_name, multi_num_players());
5456 multi_common_add_notify(XSTR("No campaign selected!",790));
5462 // if this is a team vs team situation, lock the players send a final team update
5463 if((Netgame.type_flags & NG_TYPE_TEAM) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
5464 multi_team_host_lock_all();
5465 multi_team_send_update();
5468 // if not on the standalone, move to the mission sync state which will take care of everything
5469 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5470 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
5471 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
5473 // otherwise tell the standalone to do so
5475 // when the standalone receives this, he'll do the mission syncing himself
5476 send_mission_sync_packet(MULTI_SYNC_PRE_BRIEFING,start_campaign);
5480 void multi_create_draw_filter_buttons()
5482 // highlight the correct filter button
5483 if ( Multi_create_filter == MISSION_TYPE_MULTI ){
5484 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL].button.draw_forced(2);
5485 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_COOP ) {
5486 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 1].button.draw_forced(2);
5487 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_TEAMS ) {
5488 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 2].button.draw_forced(2);
5489 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_DOGFIGHT ){
5490 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 3].button.draw_forced(2);
5496 void multi_create_set_selected_team(int team)
5500 // if we don't currently have a player selected, don't do anything
5501 if(!Multi_create_plist_select_flag){
5502 gamesnd_play_iface(SND_GENERAL_FAIL);
5505 gamesnd_play_iface(SND_USER_SELECT);
5507 // otherwise attempt to set the team for this guy
5508 player_index = find_player_id(Multi_create_plist_select_id);
5509 if(player_index != -1){
5510 multi_team_set_team(&Net_players[player_index],team);
5514 void multi_create_handle_join(net_player *pl)
5516 // for now just play a bloop sound
5517 gamesnd_play_iface(SND_ICON_DROP_ON_WING);
5520 // fill in net address of player the mouse is over, return player index (or -1 if none)
5521 short multi_create_get_mouse_id()
5523 // determine where he clicked (y pixel value)
5525 Multi_create_player_select_button.get_mouse_pos(NULL,&y);
5527 // select things a little differently if we're in team vs. team or non-team vs. team mode
5529 if(Netgame.type_flags & NG_TYPE_TEAM){
5530 int player_index = -1;
5532 // look through all of team red first
5533 for(idx=0;idx<MAX_PLAYERS;idx++){
5534 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
5537 // if this is the _nth_ guy
5545 // if we still haven't found him yet, look through the green team
5546 if(player_index == -1){
5547 for(idx=0;idx<MAX_PLAYERS;idx++){
5548 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
5550 // if this is the _nth_ guy
5559 if(player_index != -1){
5560 return Net_players[player_index].player_id;
5563 // select the nth active player if possible, disregarding the standalone server
5564 for(idx=0;idx<MAX_PLAYERS;idx++){
5565 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
5568 // if this is the _nth_ guy
5570 return Net_players[idx].player_id;
5579 void multi_create_select_to_filename(int select_index,char *filename)
5583 // look through the mission list
5584 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5585 for(idx=0;idx<Multi_create_mission_count;idx++){
5586 if(Multi_create_file_list[idx].flags & Multi_create_filter){
5590 // if we found the item
5591 if(select_index < 0){
5592 strcpy(filename,Multi_create_file_list[idx].filename);
5597 // look through the campaign list
5598 else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
5599 for(idx=0;idx<Multi_create_campaign_count;idx++){
5602 // if we found the item
5603 if(select_index < 0){
5604 strcpy(filename,Multi_create_file_list[idx].filename);
5610 strcpy(filename,"");
5613 int multi_create_select_to_index(int select_index)
5616 int lookup_index = 0;
5618 // look through the mission list
5619 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5620 for(idx=0;idx<Multi_create_mission_count;idx++){
5621 if(Multi_create_file_list[idx].flags & Multi_create_filter){
5625 // if we found the item
5626 if(select_index < lookup_index){
5631 // look through the campaign list
5632 else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
5633 for(idx=0;idx<Multi_create_campaign_count;idx++){
5636 // if we found the item
5637 if(select_index < 0){
5646 int multi_create_ok_to_commit()
5648 int player_count, observer_count, idx;
5649 int notify_of_hacked_ships_tbl = 0;
5650 int notify_of_hacked_weapons_tbl = 0;
5651 char err_string[255];
5655 // make sure we have a valid mission selected
5656 if(Multi_create_list_select < 0){
5660 // if this is not a valid mission, let the player know
5661 abs_index = multi_create_select_to_index(Multi_create_list_select);
5666 // if we're playing with a hacked ships.tbl (on PXO)
5667 notify_of_hacked_ships_tbl = 0;
5668 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5669 if(!Game_ships_tbl_valid){
5670 notify_of_hacked_ships_tbl = 1;
5673 if(Netgame.flags & NG_FLAG_HACKED_SHIPS_TBL){
5674 notify_of_hacked_ships_tbl = 1;
5677 if(!MULTI_IS_TRACKER_GAME){
5678 notify_of_hacked_ships_tbl = 0;
5680 if(notify_of_hacked_ships_tbl){
5681 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){
5686 // if we're playing with a hacked weapons.tbl (on PXO)
5687 notify_of_hacked_weapons_tbl = 0;
5688 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5689 if(!Game_weapons_tbl_valid){
5690 notify_of_hacked_weapons_tbl = 1;
5693 if(Netgame.flags & NG_FLAG_HACKED_WEAPONS_TBL){
5694 notify_of_hacked_weapons_tbl = 1;
5697 if(!MULTI_IS_TRACKER_GAME){
5698 notify_of_hacked_weapons_tbl = 0;
5700 if(notify_of_hacked_weapons_tbl){
5701 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){
5706 // if any of the players have hacked data
5708 for(idx=0; idx<MAX_PLAYERS; idx++){
5709 // look for hacked players
5710 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAXOR)){
5714 // message everyone - haha
5715 if(Net_players[idx].player != NULL){
5716 sprintf(err_string, "%s %s", Net_players[idx].player->callsign, XSTR("has hacked tables/data", 1271));
5718 sprintf(err_string, "somebody %s", XSTR("has hacked tables/data", 1271));
5720 send_game_chat_packet(Net_player, err_string, MULTI_MSG_ALL, NULL, NULL, 1);
5723 // if we found a hacked set of data
5726 if(MULTI_IS_TRACKER_GAME){
5727 // don't allow squad war matches to continue
5728 if(Netgame.type_flags & NG_TYPE_SW){
5730 // if this is squad war, don't allow it to continue
5731 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));
5736 // otherwise, warn the players that stats will not saved
5738 // if this is squad war, don't allow it to continue
5739 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){
5744 // non-pxo, just give a notice
5746 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){
5752 // check to see that we don't have too many observers
5753 observer_count = multi_num_observers();
5754 if(observer_count > Netgame.options.max_observers){
5755 // print up the error string
5756 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);
5758 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, err_string);
5762 // check to see that we have a valid # of players for the the # of ships in the game
5763 player_count = multi_num_players();
5764 if(player_count > Netgame.max_players){
5765 // print up the error string
5766 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);
5768 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, err_string);
5772 // check to see if teams are assigned properly in a team vs. team situation
5773 if(Netgame.type_flags & NG_TYPE_TEAM){
5774 if(!multi_team_ok_to_commit()){
5775 gamesnd_play_iface(SND_GENERAL_FAIL);
5776 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("Teams and/or team captains are not assigned properly", 793));
5782 if(!multi_create_verify_cds()){
5783 gamesnd_play_iface(SND_GENERAL_FAIL);
5785 #ifdef MULTIPLAYER_BETA_BUILD
5786 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, "You need 1 CD for every player!");
5788 #ifdef DVD_MESSAGE_HACK
5789 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You need 1 DVD for every 4 players!", 794));
5791 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You need 1 CD for every 4 players!", 794));
5797 // if we're playing on the tracker
5798 if(MULTI_IS_TRACKER_GAME){
5799 #ifdef PXO_CHECK_VALID_MISSIONS
5800 if((Multi_create_file_list == Multi_create_mission_list) && (Multi_create_file_list[abs_index].valid_status != MVALID_STATUS_VALID)){
5801 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){
5808 if(!(Netgame.type_flags & NG_TYPE_SW)){
5809 // if he is playing by himself, tell him stats will not be accepted
5810 if(multi_num_players() == 1){
5811 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){
5824 int multi_create_verify_cds()
5826 int player_count = multi_num_players();
5830 // count how many cds we have
5832 for(idx=0;idx<MAX_PLAYERS;idx++){
5833 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAS_CD)){
5838 // for the beta, everyone must have a CD
5839 #ifdef MULTIPLAYER_BETA_BUILD
5840 if(multi_cd_count < player_count){
5844 // determine if we have enough
5845 float ratio = (float)player_count / (float)multi_cd_count;
5846 // greater than a 4 to 1 ratio
5852 // we meet the conditions
5856 // returns an index into Multi_create_mission_list
5857 int multi_create_lookup_mission(char *fname)
5861 for(idx=0; idx<Multi_create_mission_count; idx++){
5862 if(!stricmp(fname, Multi_create_mission_list[idx].filename)){
5867 // couldn't find the mission
5871 // returns an index into Multi_create_campaign_list
5872 int multi_create_lookup_campaign(char *fname)
5876 for(idx=0; idx<Multi_create_campaign_count; idx++){
5877 if(!stricmp(fname, Multi_create_campaign_list[idx].filename)){
5882 // couldn't find the campaign
5886 void multi_create_refresh_pxo()
5888 // delete mvalid.cfg if it exists
5889 cf_delete(MULTI_VALID_MISSION_FILE, CF_TYPE_DATA);
5891 // refresh missions from the tracker
5892 multi_update_valid_missions();
5895 void multi_create_sw_clicked()
5897 netgame_info ng_temp;
5900 int file_index = multi_create_select_to_index(Multi_create_list_select);
5902 // either a temporary netgame or the real one
5903 if(MULTIPLAYER_MASTER){
5910 // maybe switch squad war off
5911 if(!Multi_create_sw_checkbox.checked()){
5912 // if the mission selected is a coop mission, go back to coop mode
5913 Assert(file_index != -1);
5914 if(file_index == -1){
5915 ng->type_flags = NG_TYPE_COOP;
5917 if(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_TEAMS){
5918 ng->type_flags = NG_TYPE_TVT;
5919 } else if(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_DOGFIGHT){
5920 ng->type_flags = NG_TYPE_DOGFIGHT;
5922 ng->type_flags = NG_TYPE_COOP;
5925 // switch squad war on
5927 Assert(file_index != -1);
5928 if((file_index == -1) || !(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_TEAMS)){
5929 Multi_create_sw_checkbox.set_state(0);
5931 // at this point we know its safe to switch squad war on
5932 ng->type_flags = NG_TYPE_SW;
5937 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5939 send_netgame_update_packet();
5941 // update all machines about stuff like respawns, etc.
5942 multi_options_update_netgame();
5944 // on the standalone
5946 // standalone will take care of polling the usertracker
5947 multi_options_update_mission(ng, Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ? 1 : 0);
5952 // -------------------------------------------------------------------------------------------------------------
5954 // MULTIPLAYER HOST OPTIONS SCREEN
5957 #define MULTI_HO_NUM_BUTTONS 12
5958 #define MULTI_HO_NUM_RADIO_BUTTONS 10
5962 #define MULTI_HO_PALETTE "InterfacePalette"
5964 static char *Multi_ho_bitmap_fname[GR_NUM_RESOLUTIONS] = {
5965 "MultiHost", // GR_640
5966 "2_MultiHost" // GR_1024
5969 static char *Multi_ho_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
5970 "MultiHost-M", // GR_640
5971 "2_MultiHost-M" // GR_1024
5975 UI_WINDOW Multi_ho_window; // the window object for the join screen
5976 UI_INPUTBOX Multi_ho_respawns; // the # of respawns allowed in the game
5977 UI_INPUTBOX Multi_ho_time_limit; // mission time limit
5978 UI_INPUTBOX Multi_ho_voice_wait; // wait time between tokens
5979 UI_INPUTBOX Multi_ho_kill_limit; // kill limit in a furball mission
5980 UI_INPUTBOX Multi_ho_obs; // # of observers we'll allow
5981 int Multi_ho_bitmap; // the background bitmap
5983 // constants for coordinate lookup
5984 #define MULTI_HO_X_COORD 0
5985 #define MULTI_HO_Y_COORD 1
5986 #define MULTI_HO_W_COORD 2
5987 #define MULTI_HO_H_COORD 3
5988 #define MULTI_HO_TEXT_X_COORD 4
5989 #define MULTI_HO_TEXT_Y_COORD 5
5992 #define MULTI_HO_MSG_RANK 0 // highest ranking players can do messaging
5993 #define MULTI_HO_MSG_LEADER 1 // wing/team leaders can do messaging
5994 #define MULTI_HO_MSG_ANY 2 // any player can do messaging
5995 #define MULTI_HO_MSG_HOST 3 // only the host can do messaging
5996 #define MULTI_HO_END_RANK 4 // highest rank can and host can end mission
5997 #define MULTI_HO_END_LEADER 5 // wing/team leaders and host can end the mission
5998 #define MULTI_HO_END_ANY 6 // any player can end the mission
5999 #define MULTI_HO_END_HOST 7 // only host can end the mission
6000 #define MULTI_HO_VOICE_ON 8 // voice toggled on
6001 #define MULTI_HO_VOICE_OFF 9 // voice toggled off
6002 #define MULTI_HO_HOST_MODIFIES 10 // only the host or team captains can modify ships/weapons in briefing
6003 #define MULTI_HO_ACCEPT 11 // accept button
6005 ui_button_info Multi_ho_buttons[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_BUTTONS] = {
6008 // who is allowed to message
6009 ui_button_info("MH_00", 13, 157, -1, -1, 0), // highest rank
6010 ui_button_info("MH_01", 13, 179, -1, -1, 1), // team/wing leader
6011 ui_button_info("MH_02", 13, 200, -1, -1, 2), // any
6012 ui_button_info("MH_03", 13, 223, -1, -1, 3), // host
6014 // who is allowed to end the mission
6015 ui_button_info("MH_09", 13, 273, -1, -1, 9), // highest rank
6016 ui_button_info("MH_10", 13, 295, -1, -1, 10), // team/wing leader
6017 ui_button_info("MH_11", 13, 317, -1, -1, 11), // any
6018 ui_button_info("MH_12", 13, 339, -1, -1, 12), // host
6020 // voice on/off button
6021 ui_button_info("MH_14", 396, 156, -1, -1, 14),
6022 ui_button_info("MH_15", 453, 156, -1, -1, 15),
6024 // host modifies ships
6025 ui_button_info("MH_19", 215, 410, -1, -1, 19),
6028 ui_button_info("MH_18", 560, 411, -1, -1, 18),
6030 // who is allowed to message
6031 ui_button_info("MH_00", 3, 160, 46, 166, 0), // highest rank
6032 ui_button_info("MH_01", 3, 179, 46, 185, 1), // team/wing leader
6033 ui_button_info("MH_02", 3, 196, 46, 203, 2), // any
6034 ui_button_info("MH_03", 3, 214, 46, 220, 3), // host
6036 // who is allowed to end the mission
6037 ui_button_info("MH_04", 3, 257, 46, 265, 4), // highest rank
6038 ui_button_info("MH_05", 3, 276, 46, 283, 5), // team/wing leader
6039 ui_button_info("MH_06", 3, 294, 46, 300, 6), // any
6040 ui_button_info("MH_07", 3, 311, 46, 317, 7), // host
6042 // voice on/off button
6043 ui_button_info("MH_09", 542, 158, 545, 185, 9),
6044 ui_button_info("MH_10", 598, 158, 604, 185, 10),
6046 // host modifies ships
6047 ui_button_info("MH_13", 542, 377, 437, 363, 13),
6050 ui_button_info("MH_14", 572, 428, 580, 414, 14),
6054 // who is allowed to message
6055 ui_button_info("2_MH_00", 5, 256, 73, 269, 0), // highest rank
6056 ui_button_info("2_MH_01", 5, 286, 73, 297, 1), // team/wing leader
6057 ui_button_info("2_MH_02", 5, 314, 73, 325, 2), // any
6058 ui_button_info("2_MH_03", 5, 341, 73, 352, 3), // host
6060 // who is allowed to end the mission
6061 ui_button_info("2_MH_04", 5, 412, 73, 425, 4), // highest rank
6062 ui_button_info("2_MH_05", 5, 442, 73, 452, 5), // team/wing leader
6063 ui_button_info("2_MH_06", 5, 470, 73, 480, 6), // any
6064 ui_button_info("2_MH_07", 5, 497, 73, 508, 7), // host
6066 // voice on/off button
6067 ui_button_info("2_MH_09", 867, 253, 872, 296, 9),
6068 ui_button_info("2_MH_10", 957, 253, 966, 296, 10),
6070 // host modifies ships
6071 ui_button_info("2_MH_13", 867, 603, 784, 581, 13),
6074 ui_button_info("2_MH_14", 916, 685, 925, 665, 14),
6077 UI_XSTR Multi_ho_text[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_BUTTONS] = {
6079 // not needed for FS1
6081 {"Highest rank", 1280, 46, 166, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_RANK].button},
6082 {"Team / wing-leader", 1281, 46, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_LEADER].button},
6083 {"Any", 1282, 46, 203, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_ANY].button},
6084 {"Host", 1283, 46, 220, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_HOST].button},
6085 {"Highest rank", 1280, 46, 265, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_RANK].button},
6086 {"Team / wing-leader", 1281, 46, 283, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_LEADER].button},
6087 {"Any", 1282, 46, 300, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_ANY].button},
6088 {"Host", 1283, 46, 317, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_HOST].button},
6089 {"On", 1285, 545, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_VOICE_ON].button},
6090 {"Off", 1286, 604, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_VOICE_OFF].button},
6091 {"Host modifies ships", 1287, 437, 363, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_HOST_MODIFIES].button},
6092 {"Exit", 1417, 572, 418, UI_XSTR_COLOR_PINK, -1, &Multi_ho_buttons[0][MULTI_HO_ACCEPT].button},
6096 // not needed for FS1
6098 {"Highest rank", 1280, 62, 269, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_RANK].button},
6099 {"Team / wing-leader", 1281, 62, 297, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_LEADER].button},
6100 {"Any", 1282, 62, 325, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_ANY].button},
6101 {"Host", 1283, 62, 352, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_HOST].button},
6102 {"Highest rank", 1280, 62, 425, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_RANK].button},
6103 {"Team / wing-leader", 1281, 62, 452, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_LEADER].button},
6104 {"Any", 1282, 62, 480, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_ANY].button},
6105 {"Host", 1283, 62, 508, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_HOST].button},
6106 {"On", 1285, 877, 294, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_VOICE_ON].button},
6107 {"Off", 1286, 967, 293, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_VOICE_OFF].button},
6108 {"Host modifies ships", 1287, 869, 589, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_HOST_MODIFIES].button},
6109 {"Exit", 1417, 953, 672, UI_XSTR_COLOR_PINK, -1, &Multi_ho_buttons[1][MULTI_HO_ACCEPT].button},
6114 // radio button controls
6115 #define MULTI_HO_NUM_RADIO_GROUPS 3
6116 #define MULTI_HO_MSG_GROUP 0 // group dealing with squadmate messaging
6117 #define MULTI_HO_END_GROUP 1 // group dealing with ending the mission
6118 #define MULTI_HO_VOICE_GROUP 2 // group dealing with voice stuff
6119 int Multi_ho_radio_groups[MULTI_HO_NUM_RADIO_GROUPS] = { // currently selected button in the radio button group
6122 int Multi_ho_radio_info[MULTI_HO_NUM_RADIO_BUTTONS][3] = { // info related to each of the radio buttons themselves
6123 // { group #, value, button id# }
6124 {0, 0, 0}, // highest ranking players can do messaging
6125 {0, 1, 1}, // wing/team leaders can do messaging
6126 {0, 2, 2}, // any player can do messaging
6127 {0, 3, 3}, // only host can do messaging
6128 {1, 0, 4}, // highest rank and host can end the mission
6129 {1, 1, 5}, // team/wing leader can end the mission
6130 {1, 2, 6}, // any player can end the mission
6131 {1, 3, 7}, // only the host can end the mission
6132 {2, 0, 8}, // voice toggled on
6133 {2, 1, 9} // voice toggled off
6137 #define MULTI_HO_NUM_SLIDERS 3
6138 #define MULTI_HO_SLIDER_VOICE_QOS 0 // voice quality of sound
6139 #define MULTI_HO_SLIDER_VOICE_DUR 1 // max duration of voice recording
6140 #define MULTI_HO_SLIDER_SKILL 2 // skill level
6147 UI_DOT_SLIDER_NEW slider; // because we have a class inside this struct, we need the constructor below..
6149 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){}
6151 ho_sliders Multi_ho_sliders[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_SLIDERS] = {
6154 ho_sliders("MH_16", 396, 210, -1, -1, 16, 19, 10), // voice qos
6155 ho_sliders("MH_17", 396, 263, -1, -1, 17, 19, 10), // voice duration
6156 ho_sliders("MH_13", 10, 387, -1, -1, 13, 36, 5), // skill level
6158 ho_sliders("MH_11", 428, 214, 437, 199, 11, 19, 10), // voice qos
6159 ho_sliders("MH_12", 428, 261, 437, 246, 12, 19, 10), // voice duration
6160 ho_sliders("MH_08", 237, 454, 230, 411, 8, 36, 5), // skill level
6164 ho_sliders("2_MH_11", 684, 343, 690, 323, 11, 32, 10), // voice qos
6165 ho_sliders("2_MH_12", 685, 418, 837, 468, 12, 32, 10), // voice duration
6166 ho_sliders("2_MH_08", 379, 727, 369, 663, 8, 60, 5), // skill level
6170 int Multi_ho_mission_respawn;
6172 int Multi_ho_host_modifies;
6174 // whether or not any of the inputboxes on this screen had focus last frame
6175 int Multi_ho_lastframe_input = 0;
6177 // game information text areas
6181 #define MULTI_HO_NUM_TITLES 0
6183 #define MULTI_HO_NUM_TITLES 14
6185 UI_XSTR Multi_ho_titles[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_TITLES] = {
6187 // not needed for FS1
6189 { "AI Orders", 1289, 32, 144, UI_XSTR_COLOR_GREEN, -1, NULL },
6190 { "End Mission", 1290, 32, 242, UI_XSTR_COLOR_GREEN, -1, NULL },
6191 { "Time Limit", 1291, 32, 347, UI_XSTR_COLOR_GREEN, -1, NULL },
6192 { "Min", 1292, 74, 362, UI_XSTR_COLOR_GREEN, -1, NULL },
6193 { "Respawn Limit", 1288, 32, 378, UI_XSTR_COLOR_GREEN, -1, NULL },
6194 { "Kill Limit", 1293, 32, 409, UI_XSTR_COLOR_GREEN, -1, NULL },
6195 { "Observers", 1294, 32, 441, UI_XSTR_COLOR_GREEN, -1, NULL },
6196 { "Skill Level", 1284, 230, 411, UI_XSTR_COLOR_GREEN, -1, NULL },
6197 { "Voice Transmission", 1295, 437, 144, UI_XSTR_COLOR_GREEN, -1, NULL },
6198 { "Voice Quality", 1296, 437, 199, UI_XSTR_COLOR_GREEN, -1, NULL },
6199 { "Message Duration", 1297, 437, 246, UI_XSTR_COLOR_GREEN, -1, NULL },
6200 { "sec", 1522, 523, 292, UI_XSTR_COLOR_GREEN, -1, NULL },
6201 { "sec", 1523, 523, 332, UI_XSTR_COLOR_GREEN, -1, NULL },
6202 { "Voice Wait", 1298, 437, 313, UI_XSTR_COLOR_GREEN, -1, NULL },
6206 // not needed for FS1
6208 { "AI Orders", 1289, 48, 238, UI_XSTR_COLOR_GREEN, -1, NULL },
6209 { "End Mission", 1290, 48, 394, UI_XSTR_COLOR_GREEN, -1, NULL },
6210 { "Time Limit", 1291, 50, 568, UI_XSTR_COLOR_GREEN, -1, NULL },
6211 { "Min", 1292, 119, 581, UI_XSTR_COLOR_GREEN, -1, NULL },
6212 { "Respawn Limit", 1288, 50, 618, UI_XSTR_COLOR_GREEN, -1, NULL },
6213 { "Kill Limit", 1293, 50, 668, UI_XSTR_COLOR_GREEN, -1, NULL },
6214 { "Observers", 1294, 50, 718, UI_XSTR_COLOR_GREEN, -1, NULL },
6215 { "Skill Level", 1284, 398, 670, UI_XSTR_COLOR_GREEN, -1, NULL },
6216 { "Voice Transmission", 1295, 869, 239, UI_XSTR_COLOR_GREEN, -1, NULL },
6217 { "Voice Quality", 1296, 690, 331, UI_XSTR_COLOR_GREEN, -1, NULL },
6218 { "Message Duration", 1297, 690, 405, UI_XSTR_COLOR_GREEN, -1, NULL },
6219 { "sec", 1522, 837, 467, UI_XSTR_COLOR_GREEN, -1, NULL },
6220 { "sec", 1523, 837, 534, UI_XSTR_COLOR_GREEN, -1, NULL },
6221 { "Voice Wait", 1298, 742, 510, UI_XSTR_COLOR_GREEN, -1, NULL },
6226 // mission time limit input box
6227 int Ho_time_coords[GR_NUM_RESOLUTIONS][4] = {
6240 // furball kill limit input box
6241 int Ho_kill_coords[GR_NUM_RESOLUTIONS][4] = {
6254 // voice recording duration text display area
6255 int Ho_vd_coords[GR_NUM_RESOLUTIONS][4] = {
6268 // voice token wait input box
6269 int Ho_vw_coords[GR_NUM_RESOLUTIONS][6] = {
6282 // observer count input box
6283 int Ho_obs_coords[GR_NUM_RESOLUTIONS][4] = {
6296 // skill text description area
6297 int Ho_st_coords[GR_NUM_RESOLUTIONS][4] = {
6310 // respawn input box
6311 int Ho_rsp_coords[GR_NUM_RESOLUTIONS][6] = {
6324 // respawn max text area
6325 int Ho_max_rsp_coords[GR_NUM_RESOLUTIONS][2] = {
6338 // maximum values for various input boxes (to notify user of overruns)
6339 #define MULTI_HO_MAX_TIME_LIMIT 500
6340 #define MULTI_HO_MAX_TOKEN_WAIT 5
6341 #define MULTI_HO_MAX_KILL_LIMIT 9999
6342 #define MULTI_HO_MAX_OBS 4
6344 // LOCAL function definitions
6345 void multi_ho_check_buttons();
6346 void multi_ho_button_pressed(int n);
6347 void multi_ho_draw_radio_groups();
6348 void multi_ho_accept_hit();
6349 void multi_ho_get_options();
6350 void multi_ho_apply_options();
6351 void multi_ho_display_record_time();
6352 int multi_ho_check_values();
6353 void multi_ho_check_focus();
6354 void multi_ho_blit_max_respawns();
6355 void multi_ho_display_skill_level();
6357 void multi_host_options_init()
6361 // create the interface window
6362 Multi_ho_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
6363 Multi_ho_window.set_mask_bmap(Multi_ho_bitmap_mask_fname[gr_screen.res]);
6365 // load the background bitmap
6366 Multi_ho_bitmap = bm_load(Multi_ho_bitmap_fname[gr_screen.res]);
6367 if(Multi_ho_bitmap < 0){
6368 // we failed to load the bitmap - this is very bad
6372 // initialize the common notification messaging
6373 multi_common_notify_init();
6375 // use the common interface palette
6376 multi_common_set_palette();
6378 // create the interface buttons
6379 for(idx=0;idx<MULTI_HO_NUM_BUTTONS;idx++){
6380 // create the object
6381 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);
6383 // set the sound to play when highlighted
6384 Multi_ho_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
6386 // set the ani for the button
6387 Multi_ho_buttons[gr_screen.res][idx].button.set_bmaps(Multi_ho_buttons[gr_screen.res][idx].filename);
6389 // set the hotspot, ignoring the skill level button
6390 Multi_ho_buttons[gr_screen.res][idx].button.link_hotspot(Multi_ho_buttons[gr_screen.res][idx].hotspot);
6394 Multi_ho_window.add_XSTR(&Multi_ho_text[gr_screen.res][idx]);
6399 for(idx=0; idx<MULTI_HO_NUM_TITLES; idx++){
6400 Multi_ho_window.add_XSTR(&Multi_ho_titles[gr_screen.res][idx]);
6403 // create the interface sliders
6404 for(idx=0; idx<MULTI_HO_NUM_SLIDERS; idx++){
6405 // create the object
6406 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);
6409 // create the respawn count input box
6410 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);
6411 // if we're in campaign mode, disable it
6412 if(Netgame.campaign_mode == MP_CAMPAIGN){
6413 Multi_ho_respawns.set_text(XSTR("NA",795)); // [[ Not applicable ]]
6414 Multi_ho_respawns.disable();
6416 Multi_ho_respawns.set_valid_chars("0123456789");
6419 // create the time limit input box
6420 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);
6421 Multi_ho_time_limit.set_valid_chars("-0123456789");
6423 // create the voice token wait input box
6424 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);
6425 Multi_ho_voice_wait.set_valid_chars("01243456789");
6427 // create the furball kill limit input box
6428 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);
6429 Multi_ho_kill_limit.set_valid_chars("0123456789");
6431 // create the observer limit input box
6432 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);
6433 Multi_ho_obs.set_valid_chars("01234");
6435 // load in the current netgame defaults
6436 multi_ho_get_options();
6438 // whether or not any of the inputboxes on this screen had focus last frame
6439 Multi_ho_lastframe_input = 0;
6441 // get the # of respawns for the currently selected mission (if any)
6442 if(Multi_create_list_select != -1){
6443 int abs_index = multi_create_select_to_index(Multi_create_list_select);
6445 // if he has a valid mission selected
6447 Multi_ho_mission_respawn = (int)Multi_create_file_list[abs_index].respawn;
6449 Multi_ho_mission_respawn = -1;
6452 Multi_ho_mission_respawn = -1;
6456 void multi_ho_update_sliders()
6458 // game skill slider
6459 if (Game_skill_level != Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos) {
6460 if ( !(Netgame.type_flags & NG_TYPE_TEAM) ){
6461 Game_skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6462 gamesnd_play_iface(SND_USER_SELECT);
6464 Game_skill_level = NUM_SKILL_LEVELS / 2;
6468 // get the voice qos options
6469 if (Netgame.options.voice_qos != (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1)) {
6470 Netgame.options.voice_qos = (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1);
6471 gamesnd_play_iface(SND_USER_SELECT);
6474 // get the voice duration options
6475 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)) {
6476 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);
6477 gamesnd_play_iface(SND_USER_SELECT);
6482 void multi_host_options_do()
6487 k = Multi_ho_window.process();
6490 // process any keypresses
6493 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
6496 case KEY_CTRLED + KEY_ENTER :
6497 gamesnd_play_iface(SND_COMMIT_PRESSED);
6498 multi_ho_accept_hit();
6502 // process any button clicks
6503 multi_ho_check_buttons();
6505 // update the sliders
6506 multi_ho_update_sliders();
6508 // make sure that the chatbox inputbox and any inputbox on this screen are mutually exclusive in terms of focus
6509 multi_ho_check_focus();
6511 // draw the background, etc
6513 GR_MAYBE_CLEAR_RES(Multi_ho_bitmap);
6514 if(Multi_ho_bitmap != -1){
6515 gr_set_bitmap(Multi_ho_bitmap);
6518 Multi_ho_window.draw();
6520 // draw all the radio buttons properly
6521 multi_ho_draw_radio_groups();
6523 // display any pending notification messages
6524 multi_common_notify_do();
6526 // display the voice record time settings
6527 multi_ho_display_record_time();
6529 // maybe display the max # of respawns next to the respawn input box
6530 multi_ho_blit_max_respawns();
6532 // blit the proper skill level
6533 multi_ho_display_skill_level();
6535 // blit the "host modifies button"
6536 if(Multi_ho_host_modifies){
6537 Multi_ho_buttons[gr_screen.res][MULTI_HO_HOST_MODIFIES].button.draw_forced(2);
6540 // process and show the chatbox thingie
6544 Multi_ho_window.draw_tooltip();
6546 // display the voice status indicator
6547 multi_common_voice_display_status();
6553 void multi_host_options_close()
6555 // unload any bitmaps
6556 if(!bm_unload(Multi_ho_bitmap)){
6557 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_ho_bitmap_fname[gr_screen.res]));
6560 // destroy the UI_WINDOW
6561 Multi_ho_window.destroy();
6564 void multi_ho_check_buttons()
6567 for(idx=0;idx<MULTI_HO_NUM_BUTTONS;idx++){
6568 // we only really need to check for one button pressed at a time, so we can break after
6570 if(Multi_ho_buttons[gr_screen.res][idx].button.pressed()){
6571 multi_ho_button_pressed(idx);
6577 void multi_ho_button_pressed(int n)
6579 int radio_index,idx;
6580 int x_pixel,y_pixel;
6582 // get the pixel position of the click
6583 Multi_ho_buttons[gr_screen.res][n].button.get_mouse_pos(&x_pixel,&y_pixel);
6586 // clicked on the accept button
6587 case MULTI_HO_ACCEPT:
6588 gamesnd_play_iface(SND_COMMIT_PRESSED);
6589 multi_ho_accept_hit();
6592 // clicked on the host/captains only modify button
6593 case MULTI_HO_HOST_MODIFIES:
6594 // toggle it on or off
6595 Multi_ho_host_modifies = !Multi_ho_host_modifies;
6596 gamesnd_play_iface(SND_USER_SELECT);
6600 // look through the radio buttons and see which one this corresponds to
6602 for(idx=0;idx<MULTI_HO_NUM_RADIO_BUTTONS;idx++){
6603 if(Multi_ho_radio_info[idx][2] == n){
6608 Assert(radio_index != -1);
6610 // check to see if a radio button was pressed
6611 if(radio_index < MULTI_HO_NUM_RADIO_BUTTONS){
6612 // see if this value is already picked for this radio group
6613 if(Multi_ho_radio_groups[Multi_ho_radio_info[radio_index][0]] != Multi_ho_radio_info[radio_index][1]){
6614 gamesnd_play_iface(SND_USER_SELECT);
6615 Multi_ho_radio_groups[Multi_ho_radio_info[radio_index][0]] = Multi_ho_radio_info[radio_index][1];
6617 gamesnd_play_iface(SND_GENERAL_FAIL);
6622 void multi_ho_draw_radio_groups()
6626 // go through each item and draw it if it is the selected button in its respective group
6627 for(idx=0;idx<MULTI_HO_NUM_RADIO_BUTTONS;idx++){
6628 /// if this button is the currently selected one in its group
6629 if(Multi_ho_radio_info[idx][1] == Multi_ho_radio_groups[Multi_ho_radio_info[idx][0]]){
6630 Multi_ho_buttons[gr_screen.res][Multi_ho_radio_info[idx][2]].button.draw_forced(2);
6635 void multi_ho_accept_hit()
6639 // check the values in the input boxes
6640 if(!multi_ho_check_values()){
6644 // zero out the netgame flags
6647 // set default options
6648 Netgame.options.flags = (MSO_FLAG_INGAME_XFER | MSO_FLAG_ACCEPT_PIX);
6650 // set the squadmate messaging flags
6651 switch(Multi_ho_radio_groups[MULTI_HO_MSG_GROUP]){
6653 Netgame.options.squad_set = MSO_SQUAD_RANK;
6656 Netgame.options.squad_set = MSO_SQUAD_LEADER;
6659 Netgame.options.squad_set = MSO_SQUAD_ANY;
6662 Netgame.options.squad_set = MSO_SQUAD_HOST;
6668 // set the end mission flags
6669 switch(Multi_ho_radio_groups[MULTI_HO_END_GROUP]){
6671 Netgame.options.endgame_set = MSO_END_RANK;
6674 Netgame.options.endgame_set = MSO_END_LEADER;
6677 Netgame.options.endgame_set = MSO_END_ANY;
6680 Netgame.options.endgame_set = MSO_END_HOST;
6686 // set the voice toggle
6687 switch(Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP]){
6689 Netgame.options.flags &= ~(MSO_FLAG_NO_VOICE);
6692 Netgame.options.flags |= MSO_FLAG_NO_VOICE;
6698 // get the voice qos options
6699 Netgame.options.voice_qos = (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1);
6701 // get the voice duration options
6702 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);
6704 // set the skill level. If in team vs. team mode, preserve the old setting before saving
6705 // the pilot file. I'll bet that this doesn't work though because the pilot file gets
6706 // written in a bunch of locations....sigh.
6707 if ( !(Netgame.type_flags & NG_TYPE_TEAM) ){
6708 Game_skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6710 Game_skill_level = NUM_SKILL_LEVELS / 2;
6713 // set the netgame respawn count
6714 // maybe warn the user that respawns will not be used for a campaign mission
6715 if(Netgame.campaign_mode == MP_SINGLE){
6716 Multi_ho_respawns.get_text(resp_str);
6717 uint temp_respawn = (uint)atoi(resp_str);
6718 // if he currently has no mission selected, let the user set any # of respawns
6719 if((int)temp_respawn > Multi_ho_mission_respawn){
6720 if(Multi_ho_mission_respawn == -1){
6721 Netgame.respawn = temp_respawn;
6722 Netgame.options.respawn = temp_respawn;
6724 // this should have been taken care of by the interface code
6729 Netgame.options.respawn = temp_respawn;
6730 Netgame.respawn = temp_respawn;
6734 // get the mission time limit
6735 Multi_ho_time_limit.get_text(resp_str);
6736 int temp_time = atoi(resp_str);
6738 Netgame.options.mission_time_limit = fl2f(-1.0f);
6739 } else if(temp_time > MULTI_HO_MAX_TIME_LIMIT){
6742 Netgame.options.mission_time_limit = fl2f(60.0f * (float)temp_time);
6745 // get observer count options
6746 Multi_ho_obs.get_text(resp_str);
6747 int temp_obs = atoi(resp_str);
6748 if(temp_obs > MULTI_HO_MAX_OBS){
6751 Netgame.options.max_observers = (ubyte)temp_obs;
6753 // get the furball kill limit
6754 Multi_ho_kill_limit.get_text(resp_str);
6755 int temp_kills = atoi(resp_str);
6756 if(temp_kills > MULTI_HO_MAX_KILL_LIMIT){
6759 Netgame.options.kill_limit = temp_kills;
6761 // get the token wait limit
6762 Multi_ho_voice_wait.get_text(resp_str);
6763 int temp_wait = atoi(resp_str);
6764 if(temp_wait > MULTI_HO_MAX_TOKEN_WAIT){
6767 Netgame.options.voice_token_wait = (temp_wait * 1000);
6769 // set the netgame option
6770 Netgame.options.skill_level = (ubyte)Game_skill_level;
6772 // get whether we're in host/captains only modify mode
6773 Netgame.options.flags &= ~(MSO_FLAG_SS_LEADERS);
6774 if(Multi_ho_host_modifies){
6775 Netgame.options.flags |= MSO_FLAG_SS_LEADERS;
6778 // store these values locally
6779 memcpy(&Player->m_local_options,&Net_player->p_info.options,sizeof(multi_local_options));
6780 memcpy(&Player->m_server_options,&Netgame.options,sizeof(multi_server_options));
6781 write_pilot_file(Player);
6783 // apply any changes in settings (notify everyone of voice qos changes, etc)
6784 multi_ho_apply_options();
6786 // move back to the create game screen
6787 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
6790 void multi_ho_get_options()
6794 // set the squadmate messaging buttons
6795 switch(Netgame.options.squad_set){
6796 case MSO_SQUAD_RANK :
6797 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 0;
6799 case MSO_SQUAD_LEADER:
6800 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 1;
6803 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 2;
6805 case MSO_SQUAD_HOST:
6806 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 3;
6812 // set the mission end buttons
6813 switch(Netgame.options.endgame_set){
6815 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 0;
6817 case MSO_END_LEADER:
6818 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 1;
6821 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 2;
6824 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 3;
6830 // set the voice toggle buttons
6831 if(Netgame.options.flags & MSO_FLAG_NO_VOICE){
6832 Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP] = 1;
6834 Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP] = 0;
6837 // get the voice qos options
6838 Assert((Netgame.options.voice_qos >= 1) && (Netgame.options.voice_qos <= 10));
6839 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos = (Netgame.options.voice_qos - 1);
6841 // get the voice duration options
6842 Assert((Netgame.options.voice_record_time > 0) && (Netgame.options.voice_record_time <= MULTI_VOICE_MAX_TIME));
6843 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos = ((int)((float)Netgame.options.voice_record_time / 500.0f)) - 1;
6845 // get the current skill level
6846 Assert((Game_skill_level >= 0) && (Game_skill_level < NUM_SKILL_LEVELS));
6847 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos = Game_skill_level;
6849 // get the # of observers
6850 memset(resp_str,0,10);
6851 sprintf(resp_str,"%d",Netgame.options.max_observers);
6852 Multi_ho_obs.set_text(resp_str);
6854 // set the respawn count
6855 if(Netgame.campaign_mode == MP_SINGLE){
6856 memset(resp_str,0,10);
6857 sprintf(resp_str,"%d",Netgame.respawn);
6858 Multi_ho_respawns.set_text(resp_str);
6861 // set the mission time limit
6862 memset(resp_str,0,10);
6863 float tl = f2fl(Netgame.options.mission_time_limit);
6864 sprintf(resp_str,"%d",(int)(tl / 60.0f));
6865 Multi_ho_time_limit.set_text(resp_str);
6867 // set the furball kill limit
6868 memset(resp_str,0,10);
6869 sprintf(resp_str,"%d",Netgame.options.kill_limit);
6870 Multi_ho_kill_limit.set_text(resp_str);
6872 // set the token wait time
6873 memset(resp_str,0,10);
6874 sprintf(resp_str,"%d",Netgame.options.voice_token_wait / 1000);
6875 Multi_ho_voice_wait.set_text(resp_str);
6877 // get whether we're in host/captains only modify mode
6878 if(Netgame.options.flags & MSO_FLAG_SS_LEADERS){
6879 Multi_ho_host_modifies = 1;
6881 Multi_ho_host_modifies = 0;
6885 void multi_ho_apply_options()
6887 // if the voice qos or duration has changed, apply the change
6888 multi_voice_maybe_update_vars(Netgame.options.voice_qos,Netgame.options.voice_record_time);
6890 // send an options update
6891 multi_options_update_netgame();
6894 // display the voice record time settings
6895 void multi_ho_display_record_time()
6898 int full_seconds, half_seconds;
6901 memset(time_str,0,30);
6904 full_seconds = (((Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 500) / 1000);
6906 // get the half-seconds
6907 half_seconds = ((((Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 500) % 1000) / 500) * 5;
6909 // format the string
6910 sprintf(time_str,"%d.%d",full_seconds,half_seconds);
6911 gr_set_color_fast(&Color_bright);
6912 gr_string(Ho_vd_coords[gr_screen.res][MULTI_HO_X_COORD],Ho_vd_coords[gr_screen.res][MULTI_HO_Y_COORD],time_str);
6915 int multi_ho_check_values()
6919 memset(val_txt,0,255);
6921 // check against respawn settings
6922 if(Multi_ho_mission_respawn != -1){
6923 Multi_ho_respawns.get_text(val_txt);
6924 // if the value is invalid, let the user know
6925 if(atoi(val_txt) > Multi_ho_mission_respawn){
6926 memset(val_txt,0,255);
6927 sprintf(val_txt,XSTR("Warning\nRespawn count in greater than mission specified max (%d)",796),Multi_ho_mission_respawn);
6928 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6933 // check against mission time limit max
6934 Multi_ho_time_limit.get_text(val_txt);
6935 // if the value is invalid, force it to be valid
6936 if(atoi(val_txt) > MULTI_HO_MAX_TIME_LIMIT){
6937 memset(val_txt,0,255);
6938 sprintf(val_txt,XSTR("Warning\nMission time limit is greater than max allowed (%d)",797),MULTI_HO_MAX_TIME_LIMIT);
6939 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6943 // check against max observer limit
6944 Multi_ho_obs.get_text(val_txt);
6945 // if the value is invalid, force it to be valid
6946 if(atoi(val_txt) > MULTI_HO_MAX_OBS){
6947 memset(val_txt,0,255);
6948 sprintf(val_txt,XSTR("Warning\nObserver count is greater than max allowed (%d)",798),MULTI_HO_MAX_OBS);
6949 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6953 // check against furball kill limit
6954 Multi_ho_kill_limit.get_text(val_txt);
6955 // if the value is invalid, force it to be valid
6956 if(atoi(val_txt) > MULTI_HO_MAX_KILL_LIMIT){
6957 memset(val_txt,0,255);
6958 sprintf(val_txt,XSTR("Warning\nMission kill limit is greater than max allowed (%d)",799),MULTI_HO_MAX_KILL_LIMIT);
6959 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6963 // check against the token wait limit
6964 Multi_ho_voice_wait.get_text(val_txt);
6965 if(atoi(val_txt) > MULTI_HO_MAX_TOKEN_WAIT){
6966 memset(val_txt,0,255);
6967 sprintf(val_txt,XSTR("Warning\nvoice wait time is greater than max allowed (%d)",800),MULTI_HO_MAX_TOKEN_WAIT);
6968 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6972 // all values are valid
6976 void multi_ho_check_focus()
6978 // if an inputbox has been pressed (hit enter), lose its focus
6979 if (Multi_ho_respawns.pressed() || Multi_ho_time_limit.pressed() || Multi_ho_voice_wait.pressed() || Multi_ho_kill_limit.pressed() || Multi_ho_obs.pressed()) {
6980 Multi_ho_respawns.clear_focus();
6981 Multi_ho_time_limit.clear_focus();
6982 Multi_ho_voice_wait.clear_focus();
6983 Multi_ho_kill_limit.clear_focus();
6984 Multi_ho_obs.clear_focus();
6985 gamesnd_play_iface(SND_COMMIT_PRESSED);
6986 chatbox_set_focus();
6987 Multi_ho_lastframe_input = 0;
6989 } else if(!Multi_ho_lastframe_input) {
6990 // if we didn't have focus last frame
6991 if(Multi_ho_respawns.has_focus() || Multi_ho_time_limit.has_focus() || Multi_ho_kill_limit.has_focus() || Multi_ho_voice_wait.has_focus() ){
6992 chatbox_lose_focus();
6994 Multi_ho_lastframe_input = 1;
6997 // if we _did_ have focus last frame
6999 // if we no longer have focus on any of the input boxes, set the focus on the chatbox
7000 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()){
7001 chatbox_set_focus();
7003 // if the chatbox now has focus, clear all focus from our inputboxes
7004 else if (chatbox_has_focus()) {
7005 Multi_ho_respawns.clear_focus();
7006 Multi_ho_time_limit.clear_focus();
7007 Multi_ho_kill_limit.clear_focus();
7008 Multi_ho_voice_wait.clear_focus();
7010 Multi_ho_lastframe_input = 0;
7015 void multi_ho_blit_max_respawns()
7019 // if we're in campaign mode, do nothing
7020 if(Netgame.campaign_mode == MP_CAMPAIGN){
7024 // otherwise blit the max as specified by the current mission file
7025 sprintf(string,"(%d)",Multi_ho_mission_respawn);
7026 gr_set_color_fast(&Color_normal);
7027 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);
7030 void multi_ho_display_skill_level()
7032 int skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
7035 Assert((skill_level >= 0) && (skill_level < NUM_SKILL_LEVELS));
7036 if((skill_level < 0) || (skill_level >= NUM_SKILL_LEVELS)){
7040 gr_set_color_fast(&Color_bright);
7041 gr_string(Ho_st_coords[gr_screen.res][0], Ho_st_coords[gr_screen.res][1], Skill_level_names(skill_level, 1));
7044 // -------------------------------------------------------------------------------------------------------------
7046 // MULTIPLAYER JOIN SCREEN
7049 #define MULTI_JW_NUM_BUTTONS 8
7053 #define MULTI_JW_PALETTE "InterfacePalette"
7055 static char *Multi_jw_bitmap_fname[GR_NUM_RESOLUTIONS] = {
7056 "MultiJoinWait", // GR_640
7057 "2_MultiJoinWait" // GR_1024
7060 static char *Multi_jw_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
7061 "MultiJoinWait-M", // GR_640
7062 "2_MultiJoinWait-M" // GR_1024
7068 #define MJW_SCROLL_PLAYERS_UP 0
7069 #define MJW_SCROLL_PLAYERS_DOWN 1
7072 #define MJW_PILOT_INFO 4
7073 #define MJW_SCROLL_INFO_UP 5
7074 #define MJW_SCROLL_INFO_DOWN 6
7075 #define MJW_CANCEL 7
7077 UI_WINDOW Multi_jw_window; // the window object for the join screen
7078 int Multi_jw_bitmap; // the background bitmap
7080 // constants for coordinate lookup
7081 #define MJW_X_COORD 0
7082 #define MJW_Y_COORD 1
7083 #define MJW_W_COORD 2
7084 #define MJW_H_COORD 3
7086 ui_button_info Multi_jw_buttons[GR_NUM_RESOLUTIONS][MULTI_JW_NUM_BUTTONS] = {
7088 ui_button_info("MJW_00", 1, 24, -1, -1, 0),
7089 ui_button_info("MJW_01", 1, 66, -1, -1, 1),
7090 ui_button_info("MJW_02", 30, 244, 20, 272, 2),
7091 ui_button_info("MJW_03", 84, 244, 73, 272, 3),
7092 ui_button_info("MJW_04", 139, 242, 134, 272, 4),
7093 ui_button_info("MJW_05", 1, 406, -1, -1, 5),
7094 ui_button_info("MJW_06", 1, 447, -1, -1, 6),
7095 ui_button_info("MJW_07", 577, 428, 570, 414, 7),
7098 ui_button_info("2_MJW_00", 2, 38, -1, -1, 0),
7099 ui_button_info("2_MJW_01", 2, 106, -1, -1, 1),
7100 ui_button_info("2_MJW_02", 48, 390, 47, 435, 2),
7101 ui_button_info("2_MJW_03", 134, 390, 133, 435, 3),
7102 ui_button_info("2_MJW_04", 223, 388, 225, 435, 4),
7103 ui_button_info("2_MJW_05", 2, 649, -1, -1, 5),
7104 ui_button_info("2_MJW_06", 2, 715, -1, -1, 6),
7105 ui_button_info("2_MJW_07", 923, 685, 931, 667, 7),
7110 #define MULTI_JW_NUM_TEXT 0
7112 #define MULTI_JW_NUM_TEXT 7
7115 UI_XSTR Multi_jw_text[GR_NUM_RESOLUTIONS][MULTI_JW_NUM_TEXT] = {
7117 // not needed for FS1
7119 { "Team 1", 1308, 20, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_TEAM0].button },
7120 { "Team 2", 1309, 73, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_TEAM1].button },
7121 { "Pilot", 1310, 134, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_PILOT_INFO].button },
7122 { "Info", 1311, 134, 283, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_PILOT_INFO].button },
7123 { "Cancel", 387, 570, 414, UI_XSTR_COLOR_PINK, -1, &Multi_jw_buttons[0][MJW_CANCEL].button },
7124 { "Players", 1269, 38, 8, UI_XSTR_COLOR_GREEN, -1, NULL },
7125 { "Choose Team", 1312, 27, 231, UI_XSTR_COLOR_GREEN, -1, NULL },
7129 // not needed for FS1
7131 { "Team 1", 1308, 47, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_TEAM0].button },
7132 { "Team 2", 1309, 133, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_TEAM1].button },
7133 { "Pilot", 1310, 225, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_PILOT_INFO].button },
7134 { "Info", 1311, 225, 446, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_PILOT_INFO].button },
7135 { "Cancel", 387, 931, 667, UI_XSTR_COLOR_PINK, -1, &Multi_jw_buttons[1][MJW_CANCEL].button },
7136 { "Players", 1269, 165, 12, UI_XSTR_COLOR_GREEN, -1, NULL },
7137 { "Choose Team", 1312, 45, 373, UI_XSTR_COLOR_GREEN, -1, NULL },
7142 int Mjw_players_coords[GR_NUM_RESOLUTIONS][4] = {
7151 int Mjw_mission_name_coords[GR_NUM_RESOLUTIONS][2] = {
7160 // squad war checkbox
7161 UI_CHECKBOX Multi_jw_sw_checkbox;
7162 char *Multi_jw_sw_checkbox_fname[GR_NUM_RESOLUTIONS] = {
7166 int Multi_jw_sw_checkbox_coords[GR_NUM_RESOLUTIONS][2] = {
7174 int Multi_jw_sw_checkbox_text[GR_NUM_RESOLUTIONS][2] = {
7184 // player list control thingie defs
7185 #define MULTI_JW_PLIST_MAX_DISPLAY 19
7186 int Multi_jw_plist_select_flag; // indicates whether we currently have a selected player
7187 short Multi_jw_plist_select_id; // id of the current selected player
7188 UI_BUTTON Multi_jw_plist_select_button; // for selecting a player
7190 int Multi_jw_should_show_popup = 0;
7192 // LOCAL function definitions
7193 void multi_jw_check_buttons();
7194 void multi_jw_button_pressed(int n);
7195 void multi_jw_do_netstuff();
7196 void multi_jw_scroll_players_up();
7197 void multi_jw_scroll_players_down();
7198 void multi_jw_plist_process();
7199 void multi_jw_plist_blit_normal();
7200 void multi_jw_plist_blit_team();
7201 short multi_jw_get_mouse_id();
7203 void multi_game_client_setup_init()
7207 // create the interface window
7208 Multi_jw_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
7209 Multi_jw_window.set_mask_bmap(Multi_jw_bitmap_mask_fname[gr_screen.res]);
7211 // load the background bitmap
7212 Multi_jw_bitmap = bm_load(Multi_jw_bitmap_fname[gr_screen.res]);
7213 if(Multi_jw_bitmap < 0){
7214 // we failed to load the bitmap - this is very bad
7218 // initialize the player list data
7219 Multi_jw_plist_select_flag = 0;
7220 Multi_jw_plist_select_id = -1;
7222 // kill any old instances of the chatbox and create a new one
7224 chatbox_create(CHATBOX_FLAG_BIG | CHATBOX_FLAG_DRAW_BOX | CHATBOX_FLAG_BUTTONS);
7226 // initialize the common notification messaging
7227 multi_common_notify_init();
7229 // initialize the common mission info display area.
7230 multi_common_set_text("");
7232 // use the common interface palette
7233 multi_common_set_palette();
7235 // create the interface buttons
7236 for(idx=0; idx<MULTI_JW_NUM_BUTTONS; idx++){
7237 // create the object
7238 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);
7240 // set the sound to play when highlighted
7241 Multi_jw_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
7243 // set the ani for the button
7244 Multi_jw_buttons[gr_screen.res][idx].button.set_bmaps(Multi_jw_buttons[gr_screen.res][idx].filename);
7247 Multi_jw_buttons[gr_screen.res][idx].button.link_hotspot(Multi_jw_buttons[gr_screen.res][idx].hotspot);
7250 // if this is a PXO game, enable the squadwar checkbox
7251 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);
7252 Multi_jw_sw_checkbox.set_bmaps(Multi_jw_sw_checkbox_fname[gr_screen.res], 6, 0);
7253 Multi_jw_sw_checkbox.disable();
7254 if(!MULTI_IS_TRACKER_GAME){
7255 Multi_jw_sw_checkbox.hide();
7259 for(idx=0; idx<MULTI_JW_NUM_TEXT; idx++){
7260 Multi_jw_window.add_XSTR(&Multi_jw_text[gr_screen.res][idx]);
7263 // create the player select list button and hide it
7264 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);
7265 Multi_jw_plist_select_button.hide();
7268 Multi_jw_buttons[gr_screen.res][MJW_CANCEL].button.set_hotkey(KEY_ESC);
7270 // remove campaign flags
7271 Game_mode &= ~(GM_CAMPAIGN_MODE);
7273 // tell the server we have finished joining
7274 Net_player->state = NETPLAYER_STATE_JOINED;
7275 send_netplayer_update_packet();
7278 ml_printf(NOX("Joined netgame %s"), Netgame.name);
7280 // send any appropriate files
7281 multi_data_send_my_junk();
7284 void multi_game_client_setup_do_frame()
7287 int k = chatbox_process();
7288 char mission_text[255];
7289 k = Multi_jw_window.process(k,0);
7291 Multi_jw_should_show_popup = 0;
7293 // process any button clicks
7294 multi_jw_check_buttons();
7296 // do any network related stuff
7297 multi_jw_do_netstuff();
7299 // draw the background, etc
7301 GR_MAYBE_CLEAR_RES(Multi_jw_bitmap);
7302 if(Multi_jw_bitmap != -1){
7303 gr_set_bitmap(Multi_jw_bitmap);
7307 // if we're not in team vs. team mode, don't draw the team buttons
7308 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
7309 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.hide();
7310 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.hide();
7311 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.disable();
7312 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.disable();
7314 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.enable();
7315 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.enable();
7316 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.unhide();
7317 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.unhide();
7320 if(MULTI_IS_TRACKER_GAME){
7321 // maybe check the squadwar button
7322 if(Netgame.type_flags & NG_TYPE_SW){
7323 Multi_jw_sw_checkbox.set_state(1);
7324 gr_set_color_fast(&Color_bright);
7326 Multi_jw_sw_checkbox.set_state(0);
7327 gr_set_color_fast(&Color_normal);
7330 gr_string(Multi_jw_sw_checkbox_text[gr_screen.res][0], Multi_jw_sw_checkbox_text[gr_screen.res][1], "SquadWar");
7333 // draw the UI window
7334 Multi_jw_window.draw();
7336 // process and display the player list
7337 // NOTE : this must be done before the buttons are checked to insure that a player hasn't dropped
7338 multi_jw_plist_process();
7339 if(Netgame.type_flags & NG_TYPE_TEAM){
7340 multi_jw_plist_blit_team();
7342 multi_jw_plist_blit_normal();
7345 // display any text in the info area
7346 multi_common_render_text();
7348 // display any pending notification messages
7349 multi_common_notify_do();
7351 // blit the mission filename if possible
7352 if(Netgame.campaign_mode){
7353 if(strlen(Netgame.campaign_name) > 0){
7354 strcpy(mission_text,Netgame.campaign_name);
7356 if(strlen(Netgame.title) > 0){
7357 strcat(mission_text,", ");
7358 strcat(mission_text,Netgame.title);
7361 gr_set_color_fast(&Color_bright_white);
7362 gr_string(Mjw_mission_name_coords[gr_screen.res][MJW_X_COORD],Mjw_mission_name_coords[gr_screen.res][MJW_Y_COORD],mission_text);
7365 if(strlen(Netgame.mission_name) > 0){
7366 strcpy(mission_text,Netgame.mission_name);
7368 if(strlen(Netgame.title) > 0){
7369 strcat(mission_text,", ");
7370 strcat(mission_text,Netgame.title);
7373 gr_set_color_fast(&Color_bright_white);
7374 gr_string(Mjw_mission_name_coords[gr_screen.res][MJW_X_COORD],Mjw_mission_name_coords[gr_screen.res][MJW_Y_COORD],mission_text);
7378 // process and show the chatbox thingie
7382 Multi_jw_window.draw_tooltip();
7384 // display the voice status indicator
7385 multi_common_voice_display_status();
7390 // if we're supposed to be displaying a pilot info popup
7391 if(Multi_jw_should_show_popup){
7392 player_index = find_player_id(Multi_jw_plist_select_id);
7393 if(player_index != -1){
7394 multi_pinfo_popup(&Net_players[player_index]);
7399 void multi_game_client_setup_close()
7401 // unload any bitmaps
7402 if(!bm_unload(Multi_jw_bitmap)){
7403 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_jw_bitmap_fname[gr_screen.res]));
7406 // destroy the chatbox
7409 // destroy the UI_WINDOW
7410 Multi_jw_window.destroy();
7413 if(Netgame.game_state == NETGAME_STATE_MISSION_SYNC){
7414 gamesnd_play_iface(SND_COMMIT_PRESSED);
7419 void multi_jw_check_buttons()
7422 for(idx=0;idx<MULTI_JW_NUM_BUTTONS;idx++){
7423 // we only really need to check for one button pressed at a time, so we can break after
7425 if(Multi_jw_buttons[gr_screen.res][idx].button.pressed()){
7426 multi_jw_button_pressed(idx);
7432 void multi_jw_button_pressed(int n)
7436 gamesnd_play_iface(SND_USER_SELECT);
7437 multi_quit_game(PROMPT_CLIENT);
7439 case MJW_SCROLL_PLAYERS_UP:
7440 multi_jw_scroll_players_up();
7442 case MJW_SCROLL_PLAYERS_DOWN:
7443 multi_jw_scroll_players_down();
7445 case MJW_SCROLL_INFO_UP:
7446 multi_common_scroll_text_up();
7448 case MJW_SCROLL_INFO_DOWN:
7449 multi_common_scroll_text_down();
7452 // request to set myself to team 0
7454 gamesnd_play_iface(SND_USER_SELECT);
7455 multi_team_set_team(Net_player,0);
7458 // request to set myself to team 1
7460 gamesnd_play_iface(SND_USER_SELECT);
7461 multi_team_set_team(Net_player,1);
7465 case MJW_PILOT_INFO:
7466 Multi_jw_should_show_popup = 1;
7470 multi_common_add_notify(XSTR("Not implemented yet!",760));
7475 // do stuff like pinging servers, sending out requests, etc
7476 void multi_jw_do_netstuff()
7480 void multi_jw_scroll_players_up()
7482 gamesnd_play_iface(SND_GENERAL_FAIL);
7485 // scroll down through the player list
7486 void multi_jw_scroll_players_down()
7488 gamesnd_play_iface(SND_GENERAL_FAIL);
7491 void multi_jw_plist_process()
7493 int test_count,player_index,idx;
7495 // first determine if there are 0 players in the game. This should never happen since the host is _always_ in the game
7497 for(idx=0;idx<MAX_PLAYERS;idx++){
7498 // count anyone except the standalone server (if applicable)
7499 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7503 if(test_count <= 0){
7507 // if we had a selected item but that player has left, select myself instead
7508 if(Multi_jw_plist_select_flag){
7509 player_index = find_player_id(Multi_jw_plist_select_id);
7510 if(player_index == -1){
7511 Multi_jw_plist_select_id = Net_player->player_id;
7514 Multi_jw_plist_select_flag = 1;
7515 Multi_jw_plist_select_id = Net_player->player_id;
7518 // if the player has clicked somewhere in the player list area
7519 if(Multi_jw_plist_select_button.pressed()){
7523 player_id = multi_jw_get_mouse_id();
7524 player_index = find_player_id(player_id);
7525 if(player_index != -1){
7526 Multi_jw_plist_select_id = player_id;
7527 Multi_jw_plist_select_flag = 1;
7532 void multi_jw_plist_blit_normal()
7535 char str[CALLSIGN_LEN+1];
7536 int y_start = Mjw_players_coords[gr_screen.res][MJW_Y_COORD];
7539 // display all the players
7540 for(idx=0;idx<MAX_PLAYERS;idx++){
7541 // reset total offset
7544 // count anyone except the standalone server (if applicable)
7545 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7546 // highlight him if he's the host
7547 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7548 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7549 gr_set_color_fast(&Color_text_active_hi);
7551 gr_set_color_fast(&Color_bright);
7554 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7555 gr_set_color_fast(&Color_text_active);
7557 gr_set_color_fast(&Color_text_normal);
7561 // optionally draw his CD status
7562 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7563 gr_set_bitmap(Multi_common_icons[MICON_CD]);
7564 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7566 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7569 // make sure the string will fit, then display it
7570 strcpy(str,Net_players[idx].player->callsign);
7571 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
7574 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7575 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7582 void multi_jw_plist_blit_team()
7585 char str[CALLSIGN_LEN+1];
7586 int y_start = Mjw_players_coords[gr_screen.res][MJW_Y_COORD];
7589 // always blit the proper team button based on _my_ team status
7590 if(Net_player->p_info.team == 0){
7591 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.draw_forced(2);
7593 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.draw_forced(2);
7596 // display all the red players first
7597 for(idx=0;idx<MAX_PLAYERS;idx++){
7598 // reset total offset
7601 // count anyone except the standalone server (if applicable)
7602 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
7603 // highlight him if he's the host
7604 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7605 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7606 gr_set_color_fast(&Color_text_active_hi);
7608 gr_set_color_fast(&Color_bright);
7611 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7612 gr_set_color_fast(&Color_text_active);
7614 gr_set_color_fast(&Color_text_normal);
7618 // optionally draw his CD status
7619 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7620 gr_set_bitmap(Multi_common_icons[MICON_CD]);
7621 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7623 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7626 // blit the red team indicator
7627 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
7628 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
7629 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT]);
7630 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7632 total_offset += Multi_common_icon_dims[MICON_TEAM0_SELECT][0] + 1;
7635 if(Multi_common_icons[MICON_TEAM0] != -1){
7636 gr_set_bitmap(Multi_common_icons[MICON_TEAM0]);
7637 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7639 total_offset += Multi_common_icon_dims[MICON_TEAM0][0] + 1;
7643 // make sure the string will fit
7644 strcpy(str,Net_players[idx].player->callsign);
7645 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7647 // display him in the correct half of the list depending on his team
7648 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7653 // display all the green players next
7654 for(idx=0;idx<MAX_PLAYERS;idx++){
7655 // reset total offset
7658 // count anyone except the standalone server (if applicable)
7659 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
7660 // highlight him if he's the host
7661 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7662 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7663 gr_set_color_fast(&Color_text_active_hi);
7665 gr_set_color_fast(&Color_bright);
7668 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7669 gr_set_color_fast(&Color_text_active);
7671 gr_set_color_fast(&Color_text_normal);
7675 // optionally draw his CD status
7676 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7677 gr_set_bitmap(Multi_common_icons[MICON_CD]);
7678 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7680 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7683 // blit the red team indicator
7684 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
7685 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
7686 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT]);
7687 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7689 total_offset += Multi_common_icon_dims[MICON_TEAM1_SELECT][0] + 1;
7692 if(Multi_common_icons[MICON_TEAM1] != -1){
7693 gr_set_bitmap(Multi_common_icons[MICON_TEAM1]);
7694 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7696 total_offset += Multi_common_icon_dims[MICON_TEAM1][0] + 1;
7700 // make sure the string will fit
7701 strcpy(str,Net_players[idx].player->callsign);
7702 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
7705 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7707 // display him in the correct half of the list depending on his team
7708 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7714 void multi_jw_handle_join(net_player *pl)
7716 // for now just play a bloop sound
7717 gamesnd_play_iface(SND_ICON_DROP_ON_WING);
7720 short multi_jw_get_mouse_id()
7722 // determine where he clicked (y pixel value)
7724 Multi_jw_plist_select_button.get_mouse_pos(NULL,&y);
7726 // select things a little differently if we're in team vs. team or non-team vs. team mode
7728 if(Netgame.type_flags & NG_TYPE_TEAM){
7729 int player_index = -1;
7731 // look through all of team red first
7732 for(idx=0;idx<MAX_PLAYERS;idx++){
7733 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
7736 // if this is the _nth_ guy
7744 // if we still haven't found him yet, look through the green team
7745 if(player_index == -1){
7746 for(idx=0;idx<MAX_PLAYERS;idx++){
7747 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
7749 // if this is the _nth_ guy
7757 if(player_index != -1){
7758 return Net_players[idx].player_id;
7761 // select the nth active player if possible, disregarding the standalone server
7762 for(idx=0;idx<MAX_PLAYERS;idx++){
7763 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7766 // if this is the _nth_ guy
7768 return Net_players[idx].player_id;
7779 // -------------------------------------------------------------------------------------------------------------
7781 // MULTIPLAYER WAIT/SYNCH SCREEN
7784 #define MULTI_SYNC_HOST_COUNT 4 // host uses 4 buttons (and sometimes 5)
7785 #define MULTI_SYNC_CLIENT_COUNT 3 // client only uses 3 buttons
7787 char *Multi_sync_bitmap_fname[GR_NUM_RESOLUTIONS] = {
7788 "MultiSynch", // GR_640
7789 "2_MultiSynch" // GR_1024
7792 char *Multi_sync_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
7793 "MultiSynch-M", // GR_640
7794 "2_MultiSynch-M" // GR_1024
7799 // constants for coordinate lookup
7800 #define MS_X_COORD 0
7801 #define MS_Y_COORD 1
7802 #define MS_W_COORD 2
7803 #define MS_H_COORD 3
7805 UI_WINDOW Multi_sync_window; // the window object for the join screen
7806 int Multi_sync_button_count; // the # of buttons to use for this instance of mission sync
7807 int Multi_sync_bitmap; // the background bitmap
7810 #define MULTI_SYNC_NUM_BUTTONS 5
7811 #define MS_SCROLL_INFO_UP 0
7812 #define MS_SCROLL_INFO_DOWN 1
7816 ui_button_info Multi_sync_buttons[GR_NUM_RESOLUTIONS][MULTI_SYNC_NUM_BUTTONS] = {
7819 ui_button_info("MS_00", 0, 396, -1, -1, 0),
7820 ui_button_info("MS_01", 0, 435, -1, -1, 1),
7821 ui_button_info("MS_02", 496, 411, -1, -1, 2),
7822 ui_button_info("MS_04", 436, 403, -1, -1, 4),
7823 ui_button_info("MS_03", 556, 398, -1, -1, 3),
7825 ui_button_info("MS_00", 1, 404, -1, -1, 0),
7826 ui_button_info("MS_01", 1, 446, -1, -1, 1),
7827 ui_button_info("MS_03", 518, 426, 519, 416, 3),
7828 ui_button_info("MS_02", 469, 426, 479, 416, 2),
7829 ui_button_info("MS_04", 571, 420, 577, 416, 4),
7833 ui_button_info("2_MS_00", 2, 647, -1, -1, 0),
7834 ui_button_info("2_MS_01", 2, 713, -1, -1, 1),
7835 ui_button_info("2_MS_03", 829, 682, 831, 667, 3),
7836 ui_button_info("2_MS_02", 751, 682, 766, 667, 2),
7837 ui_button_info("2_MS_04", 914, 672, 924, 667, 4),
7843 #define MULTI_SYNC_NUM_TEXT 0
7845 #define MULTI_SYNC_NUM_TEXT 5
7848 #define MST_LAUNCH 2
7849 UI_XSTR Multi_sync_text[GR_NUM_RESOLUTIONS][MULTI_SYNC_NUM_TEXT] = {
7851 // not needed for FS1
7853 { "Kick", 1266, 479, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_KICK].button },
7854 { "Cancel", 387, 519, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_CANCEL].button },
7855 { "Launch", 801, 577, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_LAUNCH].button },
7856 { "Players", 1269, 23, 133, UI_XSTR_COLOR_GREEN, -1, NULL },
7857 { "Status", 1304, 228, 133, UI_XSTR_COLOR_GREEN, -1, NULL }
7861 // not needed for FS1
7863 { "Kick", 1266, 766, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_KICK].button },
7864 { "Cancel", 387, 831, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_CANCEL].button },
7865 { "Launch", 801, 924, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_LAUNCH].button },
7866 { "Players", 1269, 38, 214, UI_XSTR_COLOR_GREEN, -1, NULL },
7867 { "Status", 1304, 366, 214, UI_XSTR_COLOR_GREEN, -1, NULL }
7873 int Ms_status_coords[GR_NUM_RESOLUTIONS][4] = {
7882 // player status coords
7883 int Ms_status2_coords[GR_NUM_RESOLUTIONS][4] = {
7892 int Ms_cd_icon_offset[GR_NUM_RESOLUTIONS] = {
7897 int Ms_team_icon_offset[GR_NUM_RESOLUTIONS] = {
7902 // player currently selected, index into Net_players[]
7903 int Multi_sync_player_select = -1;
7905 // player list control thingie defs
7906 #define MULTI_SYNC_PLIST_MAX_DISPLAY 15
7907 int Multi_sync_plist_start; // where to start displaying from
7908 int Multi_sync_plist_count; // how many we have
7910 // list select button
7911 UI_BUTTON Multi_sync_plist_button;
7913 int Multi_sync_mode = -1;
7915 #define MULTI_SYNC_COUNTDOWN_TIME 5 // in seconds
7916 float Multi_sync_countdown_timer;
7917 int Multi_sync_countdown = -1;
7919 int Multi_launch_button_created;
7922 // countdown animation timer
7923 char* Multi_sync_countdown_fname[GR_NUM_RESOLUTIONS] = {
7925 "2_Count" // GR_1024
7928 int Multi_sync_countdown_coords[GR_NUM_RESOLUTIONS][2] = {
7939 anim *Multi_sync_countdown_anim = NULL;
7940 anim_instance *Multi_sync_countdown_instance = NULL;
7943 // PREBRIEFING STUFF
7944 // syncing flags used by the server
7945 int Mission_sync_flags = 0;
7946 #define MS_FLAG_SENT_FILESIG (1<<0) // sent filesig requests
7947 #define MS_FLAG_SENT_LOAD (1<<1) // sent load packets
7948 #define MS_FLAG_PUSHED_BRIEFING (1<<2) // pushed everyone else into the briefing
7949 #define MS_FLAG_POST_DATA (1<<3) // sent the post data block
7950 #define MS_FLAG_WSS_SLOTS (1<<4) // all players have received wss slot data
7951 #define MS_FLAG_PSETTINGS (1<<5) // send the player settings packet
7952 #define MS_FLAG_MT_STATS_START (1<<6) // server has started getting player stats from the tracker
7953 #define MS_FLAG_MT_STATS_DONE (1<<7) // server has finished getting player stats from the tracker (success or fail)
7954 #define MS_FLAG_TS_SLOTS (1<<8) // team/ship slots have been sent
7955 #define MS_FLAG_DATA_DONE (1<<9) // done transferring all necessary data
7956 #define MS_FLAG_CAMP_DONE (1<<10) // send campaign pool/goal/event stuff
7958 // POSTBRIEFING STUFF
7959 int Multi_state_timestamp;
7960 int Multi_sync_launch_pressed;
7962 // LOCAL function definitions
7963 void multi_sync_check_buttons();
7964 void multi_sync_button_pressed(int n);
7965 void multi_sync_scroll_info_up();
7966 void multi_sync_scroll_info_down();
7967 void multi_sync_display_name(char *name,int index,int np_index); // display info on the left hand portion of the status window thingie
7968 void multi_sync_display_status(char *status,int index); // display info on the right hand portion of the status window thingie
7969 void multi_sync_force_start_pre();
7970 void multi_sync_force_start_post();
7971 void multi_sync_launch();
7972 void multi_sync_create_launch_button();
7973 void multi_sync_blit_screen_all();
7974 void multi_sync_handle_plist();
7976 void multi_sync_common_init();
7977 void multi_sync_common_do();
7978 void multi_sync_common_close();
7980 void multi_sync_pre_init();
7981 void multi_sync_pre_do();
7982 void multi_sync_pre_close();
7984 void multi_sync_post_init();
7985 void multi_sync_post_do();
7986 void multi_sync_post_close();
7991 // perform the correct init functions
7992 void multi_sync_init()
7994 Multi_sync_countdown = -1;
7998 // reset all timestamp
7999 multi_reset_timestamps();
8001 extern int Player_multi_died_check;
8002 Player_multi_died_check = -1;
8004 if(!(Game_mode & GM_STANDALONE_SERVER)){
8005 multi_sync_common_init();
8008 switch(Multi_sync_mode){
8009 case MULTI_SYNC_PRE_BRIEFING:
8010 multi_sync_pre_init();
8012 case MULTI_SYNC_POST_BRIEFING:
8013 multi_sync_post_init();
8015 case MULTI_SYNC_INGAME:
8016 multi_ingame_sync_init();
8021 // perform the correct do frame functions
8022 void multi_sync_do()
8024 if(!(Game_mode & GM_STANDALONE_SERVER)){
8025 multi_sync_common_do();
8028 // if the netgame is ending, don't do any sync processing
8029 if(multi_endgame_ending()){
8033 // process appropriateliy
8034 switch(Multi_sync_mode){
8035 case MULTI_SYNC_PRE_BRIEFING:
8036 multi_sync_pre_do();
8038 case MULTI_SYNC_POST_BRIEFING:
8039 multi_sync_post_do();
8041 case MULTI_SYNC_INGAME:
8042 multi_ingame_sync_do();
8045 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8046 if(Multi_sync_bitmap != -1){
8047 gr_set_bitmap(Multi_sync_bitmap);
8050 Multi_sync_window.draw();
8052 multi_sync_blit_screen_all();
8059 // perform the correct close functions
8060 void multi_sync_close()
8062 switch(Multi_sync_mode){
8063 case MULTI_SYNC_PRE_BRIEFING:
8064 multi_sync_pre_close();
8066 case MULTI_SYNC_POST_BRIEFING:
8067 multi_sync_post_close();
8069 case MULTI_SYNC_INGAME:
8070 multi_ingame_sync_close();
8074 if(!(Game_mode & GM_STANDALONE_SERVER)){
8075 multi_sync_common_close();
8079 char *multi_sync_tooltip_handler(char *str)
8081 if (!stricmp(str, NOX("@launch"))) {
8082 if (Multi_launch_button_created){
8083 return XSTR("Launch",801);
8090 void multi_sync_common_init()
8094 // create the interface window
8095 Multi_sync_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
8096 Multi_sync_window.set_mask_bmap(Multi_sync_bitmap_mask_fname[gr_screen.res]);
8097 Multi_sync_window.tooltip_handler = multi_sync_tooltip_handler;
8099 // load the background bitmap
8100 Multi_sync_bitmap = bm_load(Multi_sync_bitmap_fname[gr_screen.res]);
8101 if (Multi_sync_bitmap < 0) {
8102 // we failed to load the bitmap - this is very bad
8106 // initialize the player list data
8107 Multi_sync_plist_start = 0;
8108 Multi_sync_plist_count = 1; // we can pretty safely assume that there's one player in the game - me.
8110 Multi_launch_button_created = 0;
8112 // create the chatbox thingie (shouldn't be necesary to do this, but we'll put it in for good measure)
8115 // force the chatbox to be small
8116 chatbox_force_small();
8118 // initialize the common notification messaging
8119 multi_common_notify_init();
8121 // initialize the common mission info display area.
8122 multi_common_set_text("");
8124 // use the common interface palette
8125 multi_common_set_palette();
8127 // don't select any player yet.
8128 Multi_sync_player_select = -1;
8130 // determine how many of the 5 buttons to create
8131 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8132 Multi_sync_button_count = MULTI_SYNC_HOST_COUNT;
8134 Multi_sync_button_count = MULTI_SYNC_CLIENT_COUNT;
8136 // create the interface buttons
8137 for(idx=0; idx<Multi_sync_button_count; idx++){
8138 // create the object
8139 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);
8141 // set the sound to play when highlighted
8142 Multi_sync_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
8144 // set the ani for the button
8145 // this wierdness is necessary because cancel and kick buttons aren't drawn on the background bitmap,
8146 // so we have to load in frame 0, too (the file should exist)
8147 if ((idx == MS_CANCEL) || (idx == MS_KICK) || (idx == MS_LAUNCH)) {
8148 Multi_sync_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sync_buttons[gr_screen.res][idx].filename, 3, 0);
8150 Multi_sync_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sync_buttons[gr_screen.res][idx].filename);
8154 Multi_sync_buttons[gr_screen.res][idx].button.link_hotspot(Multi_sync_buttons[gr_screen.res][idx].hotspot);
8158 for(idx=0; idx<MULTI_SYNC_NUM_TEXT; idx++) {
8159 // don't create the "launch" button text just yet
8160 if(idx == MST_LAUNCH) {
8163 // multiplayer clients should ignore the kick button
8164 if(!MULTIPLAYER_MASTER && !MULTIPLAYER_HOST && (idx == MST_KICK)) {
8168 Multi_sync_window.add_XSTR(&Multi_sync_text[gr_screen.res][idx]);
8171 // create the player list select button and hide it
8172 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);
8173 Multi_sync_plist_button.hide();
8175 // set up hotkeys for certain common functions
8176 Multi_sync_buttons[gr_screen.res][MS_CANCEL].button.set_hotkey(KEY_ESC);
8179 void multi_sync_common_do()
8181 int k = chatbox_process();
8182 k = Multi_sync_window.process(k);
8184 // process the player list
8185 multi_sync_handle_plist();
8187 // process any button clicks
8188 multi_sync_check_buttons();
8190 // process any keypresses
8194 gamesnd_play_iface(SND_USER_SELECT);
8195 multi_quit_game(PROMPT_ALL);
8200 void multi_sync_common_close()
8202 // unload any bitmaps
8203 if(!bm_unload(Multi_sync_bitmap)){
8204 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_sync_bitmap_fname[gr_screen.res]));
8207 extern int Player_multi_died_check;
8208 Player_multi_died_check = -1;
8210 // destroy the UI_WINDOW
8211 Multi_sync_window.destroy();
8214 void multi_sync_blit_screen_all()
8221 // display any text in the info area
8222 multi_common_render_text();
8224 // display any pending notification messages
8225 multi_common_notify_do();
8227 // display any info about visible players
8229 for(idx=0;idx<MAX_PLAYERS;idx++){
8230 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
8231 // display his name and status
8232 multi_sync_display_name(Net_players[idx].player->callsign,count,idx);
8234 // get the player state
8235 state = Net_players[idx].state;
8237 // if we're ingame joining, show all other players except myself as "playing"
8238 if((Net_player != NULL) && (&Net_players[idx] != Net_player) && ((Multi_sync_mode == MULTI_SYNC_INGAME) || (Net_player->flags & NETINFO_FLAG_INGAME_JOIN)) ){
8239 state = NETPLAYER_STATE_IN_MISSION;
8243 case NETPLAYER_STATE_MISSION_LOADING:
8244 multi_sync_display_status(XSTR("Mission Loading",802),count);
8246 case NETPLAYER_STATE_INGAME_SHIP_SELECT: // I don't think its possible to see this state, but...
8247 multi_sync_display_status(XSTR("Ingame Ship Select",803),count);
8249 case NETPLAYER_STATE_DEBRIEF:
8250 multi_sync_display_status(XSTR("Debriefing",804),count);
8252 case NETPLAYER_STATE_MISSION_SYNC:
8253 multi_sync_display_status(XSTR("Mission Sync",805),count);
8255 case NETPLAYER_STATE_JOINING:
8256 multi_sync_display_status(XSTR("Joining",806),count);
8258 case NETPLAYER_STATE_JOINED:
8259 multi_sync_display_status(XSTR("Joined",807),count);
8261 case NETPLAYER_STATE_SLOT_ACK :
8262 multi_sync_display_status(XSTR("Slot Ack",808),count);
8264 case NETPLAYER_STATE_BRIEFING:
8265 multi_sync_display_status(XSTR("Briefing",765),count);
8267 case NETPLAYER_STATE_SHIP_SELECT:
8268 multi_sync_display_status(XSTR("Ship Select",809),count);
8270 case NETPLAYER_STATE_WEAPON_SELECT:
8271 multi_sync_display_status(XSTR("Weapon Select",810),count);
8273 case NETPLAYER_STATE_WAITING:
8274 multi_sync_display_status(XSTR("Waiting",811),count);
8276 case NETPLAYER_STATE_IN_MISSION:
8277 multi_sync_display_status(XSTR("In Mission",812),count);
8279 case NETPLAYER_STATE_MISSION_LOADED:
8280 multi_sync_display_status(XSTR("Mission Loaded",813),count);
8282 case NETPLAYER_STATE_DATA_LOAD:
8283 multi_sync_display_status(XSTR("Data loading",814),count);
8285 case NETPLAYER_STATE_SETTINGS_ACK:
8286 multi_sync_display_status(XSTR("Ready To Enter Mission",815),count);
8288 case NETPLAYER_STATE_INGAME_SHIPS:
8289 multi_sync_display_status(XSTR("Ingame Ships Packet Ack",816),count);
8291 case NETPLAYER_STATE_INGAME_WINGS:
8292 multi_sync_display_status(XSTR("Ingame Wings Packet Ack",817),count);
8294 case NETPLAYER_STATE_INGAME_RPTS:
8295 multi_sync_display_status(XSTR("Ingame Respawn Points Ack",818),count);
8297 case NETPLAYER_STATE_SLOTS_ACK:
8298 multi_sync_display_status(XSTR("Ingame Weapon Slots Ack",819),count);
8300 case NETPLAYER_STATE_POST_DATA_ACK:
8301 multi_sync_display_status(XSTR("Post Briefing Data Block Ack",820),count);
8303 case NETPLAYER_STATE_FLAG_ACK :
8304 multi_sync_display_status(XSTR("Flags Ack",821),count);
8306 case NETPLAYER_STATE_MT_STATS :
8307 multi_sync_display_status(XSTR("Parallax Online Stats Updating",822),count);
8309 case NETPLAYER_STATE_WSS_ACK :
8310 multi_sync_display_status(XSTR("Weapon Slots Ack",823),count);
8312 case NETPLAYER_STATE_HOST_SETUP :
8313 multi_sync_display_status(XSTR("Host setup",824),count);
8315 case NETPLAYER_STATE_DEBRIEF_ACCEPT:
8316 multi_sync_display_status(XSTR("Debrief accept",825),count);
8318 case NETPLAYER_STATE_DEBRIEF_REPLAY:
8319 multi_sync_display_status(XSTR("Debrief replay",826),count);
8321 case NETPLAYER_STATE_CPOOL_ACK:
8322 multi_sync_display_status(XSTR("Campaign ship/weapon ack",827),count);
8324 case NETPLAYER_STATE_MISSION_XFER :
8326 // server should display the pct completion of all clients
8327 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8328 if(Net_players[idx].s_info.xfer_handle != -1){
8329 pct_complete = multi_xfer_pct_complete(Net_players[idx].s_info.xfer_handle);
8331 // if we've got a valid xfer handle
8332 if((pct_complete >= 0.0) && (pct_complete <= 1.0)){
8333 sprintf(txt,XSTR("Mission file xfer %d%%",828),(int)(pct_complete * 100.0f));
8337 strcpy(txt,XSTR("Mission file xfer",829));
8340 strcpy(txt,XSTR("Mission file xfer",829));
8343 // clients should display only for themselves (which is the only thing they know)
8345 // if we've got a valid file xfer handle
8346 if((&Net_players[idx] == Net_player) && (Net_player->s_info.xfer_handle != -1)){
8347 pct_complete = multi_xfer_pct_complete(Net_player->s_info.xfer_handle);
8349 // if we've got a valid xfer handle
8350 if((pct_complete >= 0.0) && (pct_complete <= 1.0)){
8351 sprintf(txt,XSTR("Mission file xfer %d%%",828),(int)(pct_complete * 100.0f));
8355 strcpy(txt,XSTR("Mission file xfer",829));
8360 strcpy(txt,XSTR("Mission file xfer",829));
8365 multi_sync_display_status(txt,count);
8368 nprintf(("Network","Unhandled player state : %d !\n",Net_players[idx].state));
8375 // display the mission start countdown timer (if any)
8376 anim_render_all(GS_STATE_MULTI_MISSION_SYNC,flFrametime);
8378 // process and show the chatbox thingie
8382 Multi_sync_window.draw_tooltip();
8384 // display the voice status indicator
8385 multi_common_voice_display_status();
8388 void multi_sync_check_buttons()
8391 for(idx=0;idx<Multi_sync_button_count;idx++){
8392 // we only really need to check for one button pressed at a time, so we can break after
8394 if(Multi_sync_buttons[gr_screen.res][idx].button.pressed()){
8395 multi_sync_button_pressed(idx);
8401 void multi_sync_button_pressed(int n)
8406 gamesnd_play_iface(SND_USER_SELECT);
8407 multi_quit_game(PROMPT_ALL);
8410 // scroll the info box up
8411 case MS_SCROLL_INFO_UP:
8412 multi_common_scroll_text_up();
8415 // scroll the info box down
8416 case MS_SCROLL_INFO_DOWN:
8417 multi_common_scroll_text_down();
8422 // if we have a currently selected player, kick him
8423 if(Multi_sync_player_select >= 0){
8424 multi_kick_player(Multi_sync_player_select);
8428 // start the final launch countdown (post-sync only)
8430 multi_sync_start_countdown();
8433 // doesn't do anything
8439 void multi_sync_pre_init()
8443 Netgame.game_state = NETGAME_STATE_MISSION_SYNC;
8445 // if we're in teamplay mode, always force skill level to be medium
8446 if((Netgame.type_flags & NG_TYPE_TEAM) && (Net_player->flags & NETINFO_FLAG_GAME_HOST)){
8447 Netgame.options.skill_level = NUM_SKILL_LEVELS / 2;
8448 Game_skill_level = NUM_SKILL_LEVELS / 2;
8449 multi_options_update_netgame();
8452 // notify everyone of when we get here
8453 if(!(Game_mode & GM_STANDALONE_SERVER)){
8454 Net_player->state = NETPLAYER_STATE_MISSION_SYNC;
8455 send_netplayer_update_packet();
8458 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8460 ml_string(NOX("Server performing pre-briefing data sync"));
8462 if(!(Game_mode & GM_STANDALONE_SERVER)){
8463 multi_common_set_text(XSTR("Server performing sync\n",830),1);
8466 // maybe initialize tvt and squad war stuff
8467 if(Netgame.type_flags & NG_TYPE_TEAM){
8468 multi_team_level_init();
8471 // force everyone into this state
8472 send_netgame_update_packet();
8474 if(!(Game_mode & GM_STANDALONE_SERVER)){
8475 multi_common_add_text(XSTR("Send update packet\n",831),1);
8478 // setup some of my own data
8479 Net_player->flags |= NETINFO_FLAG_MISSION_OK;
8481 // do any output stuff
8482 if(Game_mode & GM_STANDALONE_SERVER){
8483 std_debug_set_standalone_state_string("Mission Sync");
8486 // do this here to insure we have the most up to date file checksum info
8487 multi_get_mission_checksum(Game_current_mission_filename);
8488 // parse_get_file_signature(Game_current_mission_filename);
8490 if(!(Game_mode & GM_STANDALONE_SERVER)){
8491 multi_common_add_text(XSTR("Got file signatures\n",832),1);
8494 if(!(Game_mode & GM_STANDALONE_SERVER)){
8495 multi_common_add_text(XSTR("Sending update state packet\n",833),1);
8499 // if we're not in team vs. team mode - set all player teams to be 0, and unset all captaincy bits
8500 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
8501 for(idx=0;idx<MAX_PLAYERS;idx++){
8502 Net_players[idx].p_info.team = 0;
8503 Net_players[idx].flags &= ~(NETINFO_FLAG_TEAM_CAPTAIN);
8507 // we aren't necessarily xferring the mission file yet
8508 Assert(Net_player->s_info.xfer_handle == -1);
8510 // always call this for good measure
8511 multi_campaign_flush_data();
8513 Mission_sync_flags = 0;
8514 Multi_mission_loaded = 0;
8517 void multi_sync_pre_do()
8521 // If I'm the server, wait for everyone to arrive in this state, then begin transferring data, etc.
8522 // all servers (standalone or no, go through this)
8523 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8524 // wait for everyone to arrive, then request filesig from all of them
8525 if(multi_netplayer_state_check(NETPLAYER_STATE_MISSION_SYNC) && !(Mission_sync_flags & MS_FLAG_SENT_FILESIG) && !multi_endgame_ending()){
8526 send_file_sig_request(Netgame.mission_name);
8527 Mission_sync_flags |= MS_FLAG_SENT_FILESIG;
8529 if(!(Game_mode & GM_STANDALONE_SERVER)){
8530 multi_common_add_text(XSTR("Sent filesig request\n",834),1);
8534 // if we're waiting for players to receive files, then check on their status
8535 if((Mission_sync_flags & MS_FLAG_SENT_FILESIG) && !multi_netplayer_flag_check(NETINFO_FLAG_MISSION_OK) && !multi_endgame_ending()){
8536 for(idx=0;idx<MAX_PLAYERS;idx++){
8537 // if this player is in the process of xferring a file
8538 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].s_info.xfer_handle != -1)){
8539 switch(multi_xfer_get_status(Net_players[idx].s_info.xfer_handle)){
8540 // if it has successfully completed, set his ok flag
8541 case MULTI_XFER_SUCCESS :
8543 Net_players[idx].flags |= NETINFO_FLAG_MISSION_OK;
8545 // release the xfer instance handle
8546 multi_xfer_release_handle(Net_players[idx].s_info.xfer_handle);
8547 Net_players[idx].s_info.xfer_handle = -1;
8549 // if it has failed or timed-out, kick the player
8550 case MULTI_XFER_TIMEDOUT:
8551 case MULTI_XFER_FAIL:
8552 // release the xfer handle
8553 multi_xfer_release_handle(Net_players[idx].s_info.xfer_handle);
8554 Net_players[idx].s_info.xfer_handle = -1;
8557 multi_kick_player(idx, 0, KICK_REASON_BAD_XFER);
8564 // NOTE : this is now obsolete
8565 // once everyone is verified, do any data transfer necessary
8566 if(multi_netplayer_flag_check(NETINFO_FLAG_MISSION_OK) && !(Mission_sync_flags & MS_FLAG_DATA_DONE) && !multi_endgame_ending()){
8567 // do nothing for now
8568 Mission_sync_flags |= MS_FLAG_DATA_DONE;
8570 // send campaign pool data
8571 multi_campaign_send_pool_status();
8574 // wait for everyone to ack on campaign pool data (even in non-campaign situations)
8575 if((Mission_sync_flags & MS_FLAG_DATA_DONE) && !(Mission_sync_flags & MS_FLAG_CAMP_DONE) && !multi_endgame_ending()){
8576 // check to see if everyone has acked the campaign pool data
8577 if(multi_netplayer_state_check(NETPLAYER_STATE_CPOOL_ACK)){
8578 Mission_sync_flags |= MS_FLAG_CAMP_DONE;
8582 // once everyone is verified, tell them to load the mission
8583 // also make sure to load the mission myself _AFTER_ telling everyone to do so. This makes the whole process
8584 // move along faster
8585 if((Mission_sync_flags & MS_FLAG_CAMP_DONE) && !(Mission_sync_flags & MS_FLAG_SENT_LOAD) && !multi_endgame_ending()){
8586 send_netplayer_load_packet(NULL);
8587 Mission_sync_flags |= MS_FLAG_SENT_LOAD;
8589 if(!(Game_mode & GM_STANDALONE_SERVER)){
8590 multi_common_add_text(XSTR("Sent load packet\n",835),1);
8593 // load the mission myself, as soon as possible
8594 if(!Multi_mission_loaded){
8595 nprintf(("Network","Server loading mission..."));
8597 // update everyone about my status
8598 Net_player->state = NETPLAYER_STATE_MISSION_LOADING;
8599 send_netplayer_update_packet();
8601 game_start_mission();
8603 nprintf(("Network","Done\n"));
8604 Multi_mission_loaded = 1;
8606 // update everyone about my status
8607 Net_player->state = NETPLAYER_STATE_MISSION_LOADED;
8608 send_netplayer_update_packet();
8610 if(!(Game_mode & GM_STANDALONE_SERVER)){
8611 multi_common_add_text(XSTR("Loaded mission locally\n",836),1);
8616 // if everyone has loaded the mission, randomly assign players to ships
8617 if(multi_netplayer_state_check(NETPLAYER_STATE_MISSION_LOADED) && !(Mission_sync_flags & MS_FLAG_TS_SLOTS) && !multi_endgame_ending()){
8618 // call the team select function to assign players to their ships, wings, etc
8619 multi_ts_assign_players_all();
8620 send_netplayer_slot_packet();
8623 Mission_sync_flags |= MS_FLAG_TS_SLOTS;
8626 // if everyone has loaded the mission, move to the team select stage
8627 if(Sync_test && multi_netplayer_state_check(NETPLAYER_STATE_SLOT_ACK) && !(Mission_sync_flags & MS_FLAG_PUSHED_BRIEFING) && !multi_endgame_ending()){
8628 Netgame.game_state = NETGAME_STATE_BRIEFING;
8629 send_netgame_update_packet(); // this will push everyone into the next state
8631 // the standalone moves to his own wait state, whereas in the normal game mode, the server/host moves in to the
8632 // team select state
8633 if(Game_mode & GM_STANDALONE_SERVER){
8634 gameseq_post_event(GS_EVENT_MULTI_STD_WAIT);
8636 gameseq_post_event(GS_EVENT_START_GAME);
8639 Mission_sync_flags |= MS_FLAG_PUSHED_BRIEFING;
8641 if(!(Game_mode & GM_STANDALONE_SERVER)){
8642 multi_common_add_text(XSTR("Moving to team select\n",837),1);
8646 // clients should detect here if they are doing a file xfer and do error processing
8647 if((Net_player->state == NETPLAYER_STATE_MISSION_XFER) && (Net_player->s_info.xfer_handle != -1) && !multi_endgame_ending()){
8648 switch(multi_xfer_get_status(Net_player->s_info.xfer_handle)){
8649 // if it has successfully completed, set his ok flag
8650 case MULTI_XFER_SUCCESS :
8651 // release my xfer handle
8652 multi_xfer_release_handle(Net_player->s_info.xfer_handle);
8653 Net_player->s_info.xfer_handle = -1;
8656 // if it has failed or timed-out, kick the player
8657 case MULTI_XFER_TIMEDOUT:
8658 case MULTI_XFER_FAIL:
8659 // release my xfer handle
8660 multi_xfer_release_handle(Net_player->s_info.xfer_handle);
8661 Net_player->s_info.xfer_handle = -1;
8663 // leave the game qith an error code
8664 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_XFER_FAIL);
8671 if(!(Game_mode & GM_STANDALONE_SERVER)){
8673 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8674 if(Multi_sync_bitmap != -1){
8675 gr_set_bitmap(Multi_sync_bitmap);
8678 Multi_sync_window.draw();
8680 multi_sync_blit_screen_all();
8686 void multi_sync_pre_close()
8688 // at this point, we should shut down any file xfers...
8689 if(Net_player->s_info.xfer_handle != -1){
8690 nprintf(("Network","WARNING - killing file xfer while leaving mission sync state!!!\n"));
8692 multi_xfer_abort(Net_player->s_info.xfer_handle);
8693 Net_player->s_info.xfer_handle = -1;
8697 void multi_sync_post_init()
8699 multi_reset_timestamps();
8701 Multi_state_timestamp = timestamp(0);
8704 ml_string(NOX("Performing post-briefing data sync"));
8706 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8707 multi_common_add_text(XSTR("Server performing sync\n",830),1);
8709 multi_common_add_text(XSTR("Client performing sync\n",838),1);
8712 // everyone should re-initialize these
8713 init_multiplayer_stats();
8715 // reset all sequencing info
8716 multi_oo_reset_sequencing();
8718 // if I am not the master of the game, then send the firing information for my ship
8720 if ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) ){
8721 send_firing_info_packet();
8724 // if I'm not a standalone server, load up the countdown stuff
8725 if(!(Game_mode & GM_STANDALONE_SERVER)){
8726 Multi_sync_countdown_anim = NULL;
8727 Multi_sync_countdown_instance = NULL;
8728 Multi_sync_countdown_anim = anim_load(Multi_sync_countdown_fname[gr_screen.res]);
8729 if(Multi_sync_countdown_anim == NULL){
8730 nprintf(("General","WARNING!, Could not load countdown animation %s!\n",Multi_sync_countdown_fname[gr_screen.res]));
8734 // create objects for all permanent observers
8735 multi_obs_level_init();
8737 // clear the game start countdown timer
8738 Multi_sync_countdown_timer = -1.0f;
8739 Multi_sync_countdown = -1;
8741 // if this is a team vs. team mission, mark all ship teams appropriately
8742 if(Netgame.type_flags & NG_TYPE_TEAM){
8743 multi_team_mark_all_ships();
8746 Mission_sync_flags = 0;
8747 Multi_sync_launch_pressed = 0;
8750 #define MULTI_POST_TIMESTAMP 7000
8752 extern int create_wings();
8754 void multi_sync_post_do()
8758 // only if the host is also the master should he be doing this (non-standalone situation)
8759 if ( Net_player->flags & NETINFO_FLAG_AM_MASTER ) {
8761 // once everyone gets to this screen, send them the ship classes of all ships.
8762 if(multi_netplayer_state_check(NETPLAYER_STATE_WAITING) && !(Mission_sync_flags & MS_FLAG_POST_DATA)) {
8763 // only the host should ever do this
8764 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8765 // at this point we want to delete all necessary ships, change all necessary ship classes, and set all weapons up
8766 multi_ts_create_wings();
8768 // update player ets settings
8769 for(idx=0;idx<MAX_PLAYERS;idx++){
8770 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].player->objnum != -1)){
8771 multi_server_update_player_weapons(&Net_players[idx],&Ships[Objects[Net_players[idx].player->objnum].instance]);
8776 // note that this is done a little differently for standalones and nonstandalones
8777 send_post_sync_data_packet();
8779 multi_common_add_text(XSTR("Sending post briefing block information\n",839),1);
8781 Mission_sync_flags |= MS_FLAG_POST_DATA;
8784 // send weapon slots data
8785 if(multi_netplayer_state_check(NETPLAYER_STATE_POST_DATA_ACK) && !(Mission_sync_flags & MS_FLAG_WSS_SLOTS)) {
8786 // note that this is done a little differently for standalones and nonstandalones
8787 if(Netgame.type_flags & NG_TYPE_TEAM){
8788 send_wss_slots_data_packet(0,0);
8789 send_wss_slots_data_packet(1,1);
8791 send_wss_slots_data_packet(0,1);
8794 multi_common_add_text(XSTR("Sending weapon slots information\n",840),1);
8796 Mission_sync_flags |= MS_FLAG_WSS_SLOTS;
8799 // once weapon information is received, send player settings info
8800 if ( multi_netplayer_state_check(NETPLAYER_STATE_WSS_ACK) && !(Mission_sync_flags & MS_FLAG_PSETTINGS)) {
8801 send_player_settings_packet();
8803 // server (specifically, the standalone), should set this here
8804 Net_player->state = NETPLAYER_STATE_SETTINGS_ACK;
8805 send_netplayer_update_packet();
8807 multi_common_add_text(XSTR("Sending player settings packets\n",841),1);
8809 Mission_sync_flags |= MS_FLAG_PSETTINGS;
8812 // check to see if the countdown timer has started and act appropriately
8813 if( Multi_sync_countdown_timer > -1.0f ) {
8815 // increment by frametime.
8816 Multi_sync_countdown_timer += flFrametime;
8818 // if the animation is not playing, start it
8819 if(!(Game_mode & GM_STANDALONE_SERVER) && (Multi_sync_countdown_instance == NULL) && (Multi_sync_countdown_anim != NULL)){
8820 anim_play_struct aps;
8822 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]);
8823 aps.screen_id = GS_STATE_MULTI_MISSION_SYNC;
8824 aps.framerate_independent = 1;
8826 Multi_sync_countdown_instance = anim_play(&aps);
8829 // if the next second has expired
8830 if( Multi_sync_countdown_timer >= 1.0f ) {
8832 Multi_sync_countdown--;
8833 Multi_sync_countdown_timer = 0.0f;
8835 // if the countdown has reached 0, launch the mission
8836 if(Multi_sync_countdown == 0){
8837 Multi_sync_countdown_timer = -1.0f;
8839 Multi_sync_launch_pressed = 0;
8840 multi_sync_launch();
8842 // otherwise send a countdown packet
8844 send_countdown_packet(Multi_sync_countdown);
8849 // jump into the mission myself
8850 if((Multi_sync_countdown == 0) && multi_netplayer_state_check(NETPLAYER_STATE_IN_MISSION)){
8851 if(!((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !Multi_sync_launch_pressed)){
8852 Net_player->state = NETPLAYER_STATE_IN_MISSION;
8853 Netgame.game_state = NETGAME_STATE_IN_MISSION;
8854 gameseq_post_event(GS_EVENT_ENTER_GAME);
8856 multi_common_add_text(XSTR("Moving into game\n",842),1);
8860 // maybe start the animation countdown
8861 if((Multi_sync_countdown >= 0) && (Multi_sync_countdown_instance == NULL) && (Multi_sync_countdown_anim != NULL)){
8862 anim_play_struct aps;
8864 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]);
8865 aps.screen_id = GS_STATE_MULTI_MISSION_SYNC;
8866 aps.framerate_independent = 1;
8868 Multi_sync_countdown_instance = anim_play(&aps);
8872 // host - specific stuff
8873 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8874 // create the launch button so the host can click
8875 if( Sync_test && multi_netplayer_state_check(NETPLAYER_STATE_SETTINGS_ACK) ){
8876 multi_sync_create_launch_button();
8881 if(!(Game_mode & GM_STANDALONE_SERVER)){
8883 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8884 if(Multi_sync_bitmap != -1){
8885 gr_set_bitmap(Multi_sync_bitmap);
8888 Multi_sync_window.draw();
8890 multi_sync_blit_screen_all();
8896 void multi_sync_post_close()
8900 // if I'm not a standalone server, unload up the countdown stuff
8901 if(!(Game_mode & GM_STANDALONE_SERVER)){
8902 // release all rendering animation instances (should only be 1)
8903 anim_release_all_instances(GS_STATE_MULTI_MISSION_SYNC);
8904 Multi_sync_countdown_instance = NULL;
8906 // free up the countdown animation
8907 if(Multi_sync_countdown_anim != NULL){
8908 anim_free(Multi_sync_countdown_anim);
8909 Multi_sync_countdown_anim = NULL;
8913 // all players should reset sequencing
8914 for(idx=0;idx<MAX_PLAYERS;idx++){
8915 if(Net_player->flags & NETINFO_FLAG_CONNECTED){
8916 Net_players[idx].client_cinfo_seq = 0;
8917 Net_players[idx].client_server_seq = 0;
8921 // multiplayer dogfight
8922 multi_df_level_pre_enter();
8924 // clients should clear obj_pair array and add pair for themselves
8926 if ( MULTIPLAYER_CLIENT ) {
8928 obj_add_pairs( OBJ_INDEX(Player_obj) );
8933 void multi_sync_display_name(char *name,int index,int np_index)
8935 char fit[CALLSIGN_LEN];
8937 // make sure the string actually fits
8940 // if we're in team vs. team mode
8941 if(Netgame.type_flags & NG_TYPE_TEAM){
8942 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]);
8944 // if this is the currently selected player, draw him highlighted
8945 if(np_index == Multi_sync_player_select){
8946 gr_set_color_fast(&Color_text_selected);
8948 gr_set_color_fast(&Color_text_normal);
8952 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);
8954 // blit his team icon
8956 if(Net_players[np_index].p_info.team == 0){
8957 // blit the team captain icon
8958 if(Net_players[np_index].flags & NETINFO_FLAG_TEAM_CAPTAIN){
8959 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
8960 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT]);
8961 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);
8964 // normal team member icon
8966 if(Multi_common_icons[MICON_TEAM0] != -1){
8967 gr_set_bitmap(Multi_common_icons[MICON_TEAM0]);
8968 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);
8973 else if(Net_players[np_index].p_info.team == 1){
8974 // blit the team captain icon
8975 if(Net_players[np_index].flags & NETINFO_FLAG_TEAM_CAPTAIN){
8976 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
8977 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT]);
8978 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);
8981 // normal team member icon
8983 if(Multi_common_icons[MICON_TEAM1] != -1){
8984 gr_set_bitmap(Multi_common_icons[MICON_TEAM1]);
8985 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);
8990 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]);
8992 // if this is the currently selected player, draw him highlighted
8993 if(np_index == Multi_sync_player_select){
8994 gr_set_color_fast(&Color_text_selected);
8996 gr_set_color_fast(&Color_text_normal);
9000 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);
9003 // maybe blit his CD status icon
9004 if((Net_players[np_index].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
9005 gr_set_bitmap(Multi_common_icons[MICON_CD]);
9006 gr_bitmap(Ms_status_coords[gr_screen.res][MS_X_COORD], Ms_status_coords[gr_screen.res][MS_Y_COORD] + (index * 10));
9010 void multi_sync_display_status(char *status,int index)
9014 // make sure the string actually fits
9015 strcpy(fit, status);
9016 gr_force_fit_string(fit, 250, Ms_status2_coords[gr_screen.res][MS_W_COORD] - 20);
9017 gr_set_color_fast(&Color_bright);
9018 gr_string(Ms_status2_coords[gr_screen.res][MS_X_COORD], Ms_status2_coords[gr_screen.res][MS_Y_COORD] + (index * 10), fit);
9021 void multi_sync_force_start_pre()
9024 int want_state = NETPLAYER_STATE_SLOT_ACK; // kick any players who are still in this state
9026 // go through the player list and boot anyone who isn't in the right state
9027 for(idx=0;idx<MAX_PLAYERS;idx++){
9028 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx]) && (Net_players[idx].state == want_state)){
9029 multi_kick_player(idx,0);
9034 void multi_sync_force_start_post()
9038 int num_kill_states;
9040 // determine the state we want all players in so that we can find those who are not in the state
9041 kill_state[0] = NETPLAYER_STATE_BRIEFING;
9042 kill_state[1] = NETPLAYER_STATE_SHIP_SELECT;
9043 kill_state[2] = NETPLAYER_STATE_WEAPON_SELECT;
9044 num_kill_states = 3;
9046 // go through the player list and boot anyone who isn't in the right state
9047 for(idx=0;idx<MAX_PLAYERS;idx++){
9048 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx])){
9049 // check against all kill state
9050 for(idx2 = 0;idx2<num_kill_states;idx2++){
9051 if(Net_players[idx].state == kill_state[idx2]){
9052 multi_kick_player(idx,0);
9060 void multi_sync_start_countdown()
9062 // don't allow repeat button presses
9063 if(Multi_sync_launch_pressed){
9067 Multi_sync_launch_pressed = 1;
9069 // if I'm the server, begin the countdown
9070 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
9071 gamesnd_play_iface(SND_COMMIT_PRESSED);
9072 Multi_sync_countdown_timer = 0.0f;
9073 Multi_sync_countdown = MULTI_SYNC_COUNTDOWN_TIME;
9075 // send an initial countdown value
9076 send_countdown_packet(Multi_sync_countdown);
9078 // otherwise send the "start countdown" packet to the standalone
9080 Assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
9081 send_countdown_packet(-1);
9085 void multi_sync_launch()
9087 // don't allow repeat button presses
9088 if(Multi_sync_launch_pressed){
9092 Multi_sync_launch_pressed = 1;
9095 ml_printf(NOX("Entering mission %s"), Game_current_mission_filename);
9097 // tell everyone to jump into the mission
9098 send_jump_into_mission_packet();
9099 Multi_state_timestamp = timestamp(MULTI_POST_TIMESTAMP);
9101 // set the # of players at the start of the mission
9102 Multi_num_players_at_start = multi_num_players();
9103 nprintf(("Network","# of players at start of mission : %d\n", Multi_num_players_at_start));
9105 // initialize datarate limiting for all clients
9106 multi_oo_rate_init_all();
9108 multi_common_add_text(XSTR("Sending mission start packet\n",843),1);
9111 void multi_sync_create_launch_button()
9113 if (!Multi_launch_button_created) {
9114 // create the object
9115 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);
9117 // set the sound to play when highlighted
9118 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_highlight_action(common_play_highlight_sound);
9120 // set the ani for the button
9121 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_bmaps(Multi_sync_buttons[gr_screen.res][MS_LAUNCH].filename, 3, 0);
9124 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.link_hotspot(Multi_sync_buttons[gr_screen.res][MS_LAUNCH].hotspot);
9127 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_hotkey(KEY_CTRLED+KEY_ENTER);
9130 // create the text for the button
9131 Multi_sync_window.add_XSTR(&Multi_sync_text[gr_screen.res][MST_LAUNCH]);
9134 // increment the button count so we start checking this one
9135 Multi_sync_button_count++;
9137 Multi_launch_button_created = 1;
9141 void multi_sync_handle_plist()
9147 // if we don't have a currently selected player, select one
9148 if((Multi_sync_player_select < 0) || !MULTI_CONNECTED(Net_players[Multi_sync_player_select])){
9149 for(idx=0;idx<MAX_PLAYERS;idx++){
9150 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
9151 Multi_sync_player_select = idx;
9157 // check for button list presses
9158 if(Multi_sync_plist_button.pressed()){
9159 // get the y mouse coords
9160 Multi_sync_plist_button.get_mouse_pos(NULL,&my);
9162 // get the index of the item selected
9163 select_index = my / 10;
9165 // if the index is greater than the current # connections, do nothing
9166 if(select_index > (multi_num_connections() - 1)){
9170 // translate into an absolute Net_players[] index (get the Nth net player)
9171 Multi_sync_player_select = -1;
9172 for(idx=0;idx<MAX_PLAYERS;idx++){
9173 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
9177 // if we've found the item we're looking for
9178 if(select_index < 0){
9179 Multi_sync_player_select = idx;
9184 // if for some bizarre reason, this is an invalid player, unselect him and wait for the next interation
9186 if((Multi_sync_player_select >= 0) && (!MULTI_CONNECTED(Net_players[Multi_sync_player_select]) || MULTI_STANDALONE(Net_players[Multi_sync_player_select])) ){
9187 Multi_sync_player_select = -1;
9193 // -------------------------------------------------------------------------------------------------------------
9195 // MULTIPLAYER DEBRIEF SCREEN
9198 // other relevant data
9199 int Multi_debrief_accept_hit;
9200 int Multi_debrief_replay_hit;
9202 // set if the server has left the game
9203 int Multi_debrief_server_left = 0;
9205 // if we've reported on TvT status all players are in the debrief
9206 int Multi_debrief_reported_tvt = 0;
9208 // whether stats are being accepted
9209 // -1 == no decision yet
9212 int Multi_debrief_stats_accept_code = -1;
9214 int Multi_debrief_server_framecount = 0;
9216 float Multi_debrief_time = 0.0f;
9217 float Multi_debrief_resend_time = 10.0f;
9219 void multi_debrief_init()
9223 Multi_debrief_time = 0.0f;
9224 Multi_debrief_resend_time = 10.0f;
9226 // do this to notify the standalone or the normal server that we're in the debrief state and ready to receive packets
9227 if (!(Net_player->flags & NETINFO_FLAG_AM_MASTER)) {
9228 Net_player->state = NETPLAYER_STATE_DEBRIEF;
9229 send_netplayer_update_packet();
9232 // unflag some stuff
9233 for(idx=0;idx<MAX_PLAYERS;idx++){
9234 if(MULTI_CONNECTED(Net_players[idx])){
9235 Net_players[idx].flags &= ~(NETINFO_FLAG_RESPAWNING | NETINFO_FLAG_LIMBO | NETINFO_FLAG_WARPING_OUT);
9239 // if text input mode is active, clear it
9240 multi_msg_text_flush();
9242 // the server has not left yet
9243 Multi_debrief_server_left = 0;
9245 // have not hit accept or replay yet
9246 Multi_debrief_accept_hit = 0;
9247 Multi_debrief_replay_hit = 0;
9249 // stats have not been accepted yet
9250 Multi_debrief_stats_accept_code = -1;
9252 // mark stats as not being store yet
9253 Netgame.flags &= ~(NG_FLAG_STORED_MT_STATS);
9255 // no report on TvT yet
9256 Multi_debrief_reported_tvt = 0;
9258 Multi_debrief_server_framecount = 0;
9261 void multi_debrief_do_frame()
9263 Multi_debrief_time += flFrametime;
9265 // set the netgame state to be debriefing when appropriate
9266 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)){
9267 Netgame.game_state = NETGAME_STATE_DEBRIEF;
9268 send_netgame_update_packet();
9271 // evaluate all server stuff
9272 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
9273 multi_debrief_server_process();
9277 void multi_debrief_close()
9279 if ( MULTIPLAYER_CLIENT && (Netgame.game_state == NETGAME_STATE_MISSION_SYNC) ){
9280 gamesnd_play_iface( SND_COMMIT_PRESSED );
9284 // handle optional mission loop
9285 void multi_maybe_set_mission_loop()
9287 int cur = Campaign.current_mission;
9288 if (Campaign.missions[cur].has_mission_loop) {
9289 Assert(Campaign.loop_mission != CAMPAIGN_LOOP_MISSION_UNINITIALIZED);
9291 bool require_repeat_mission = (Campaign.current_mission == Campaign.next_mission);
9293 // check for (1) mission loop available, (2) dont have to repeat last mission
9294 if ( (Campaign.missions[cur].has_mission_loop && (Campaign.loop_mission != -1)) && !require_repeat_mission ) {
9297 debrief_assemble_optional_mission_popup_text(buffer, Campaign.missions[cur].mission_loop_desc);
9299 int choice = popup(0 , 2, POPUP_NO, POPUP_YES, buffer);
9301 Campaign.loop_enabled = 1;
9302 Campaign.next_mission = Campaign.loop_mission;
9307 // handle all cases for when the accept key is hit in a multiplayer debriefing
9308 void multi_debrief_accept_hit()
9310 // if we already accepted, do nothing
9311 // but he may need to hit accept again after the server has left the game, so allow this
9312 if(Multi_debrief_accept_hit){
9316 // mark this so that we don't hit it again
9317 Multi_debrief_accept_hit = 1;
9319 gamesnd_play_iface(SND_COMMIT_PRESSED);
9321 // if the server has left the game, always just end the game.
9322 if(Multi_debrief_server_left){
9323 if(!multi_quit_game(PROMPT_ALL)){
9324 Multi_debrief_server_left = 1;
9325 Multi_debrief_accept_hit = 0;
9327 Multi_debrief_server_left = 0;
9330 // query the host and see if he wants to accept stats
9331 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
9332 // if we're on a tracker game, he gets no choice for storing stats
9333 if(MULTI_IS_TRACKER_GAME){
9334 multi_maybe_set_mission_loop();
9336 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));
9338 // evaluate the result
9343 Multi_debrief_accept_hit = 0;
9346 // set the accept code to be "not accepting"
9348 multi_debrief_stats_toss();
9349 multi_maybe_set_mission_loop();
9352 // accept the stats and continue
9354 multi_debrief_stats_accept();
9355 multi_maybe_set_mission_loop();
9361 // set my netplayer state to be "debrief_accept", and be done with it
9362 Net_player->state = NETPLAYER_STATE_DEBRIEF_ACCEPT;
9363 send_netplayer_update_packet();
9367 // handle all cases for when the escape key is hit in a multiplayer debriefing
9368 void multi_debrief_esc_hit()
9372 // if the server has left
9373 if(Multi_debrief_server_left){
9374 multi_quit_game(PROMPT_ALL);
9379 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
9380 // if the stats have already been accepted
9381 if((Multi_debrief_stats_accept_code != -1) || (MULTI_IS_TRACKER_GAME)){
9382 multi_quit_game(PROMPT_HOST);
9384 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));
9386 // evaluate the result
9393 // set the accept code to be "not accepting"
9395 multi_debrief_stats_toss();
9396 multi_quit_game(PROMPT_NONE);
9399 // accept the stats and continue
9401 multi_debrief_stats_accept();
9402 multi_quit_game(PROMPT_NONE);
9407 // if the stats haven't been accepted yet, or this is a tracker game
9408 if((Multi_debrief_stats_accept_code == -1) && !(MULTI_IS_TRACKER_GAME)){
9409 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));
9411 // evaluate the result
9413 multi_quit_game(PROMPT_NONE);
9416 // otherwise go through the normal endgame channels
9418 multi_quit_game(PROMPT_ALL);
9423 void multi_debrief_replay_hit()
9425 // only the host should ever get here
9426 Assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
9428 // if the button was already pressed, do nothing
9429 if(Multi_debrief_accept_hit){
9433 // same as hittin the except button except no stats are kept
9434 Multi_debrief_accept_hit = 1;
9436 // mark myself as being in the replay state so we know what to do next
9437 Net_player->state = NETPLAYER_STATE_DEBRIEF_REPLAY;
9438 send_netplayer_update_packet();
9441 // call this when the server has left and we would otherwise be saying "contact lost with server
9442 void multi_debrief_server_left()
9445 Multi_debrief_server_left = 1;
9447 // undo any "accept" hit so that clients can hit accept again to leave
9448 Multi_debrief_accept_hit = 0;
9451 void multi_debrief_stats_accept()
9453 // don't do anything if we've already accepted
9454 if(Multi_debrief_stats_accept_code != -1){
9458 Multi_debrief_stats_accept_code = 1;
9460 // if we're the host, and we're on a standalone, tell the standalone to begin the stats storing process
9461 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) || (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
9462 // send a packet to the players telling them to store their stats
9463 send_store_stats_packet(1);
9466 // add a chat line saying "stats have been accepted"
9467 multi_display_chat_msg(XSTR("<stats have been accepted>",850),0,0);
9470 ml_string(NOX("Stats stored"));
9473 void multi_debrief_stats_toss()
9475 // don't do anything if we've already accepted
9476 if(Multi_debrief_stats_accept_code != -1){
9480 Multi_debrief_stats_accept_code = 0;
9482 // if we're the host, and we're on a standalone, tell everyone to "toss" stats
9483 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) || (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
9484 // send a packet to the players telling them to store their stats
9485 send_store_stats_packet(0);
9488 // add a chat line saying "stats have been accepted"
9489 multi_display_chat_msg(XSTR("<stats have been tossed>",851),0,0);
9492 ml_string(NOX("Stats tossed"));
9495 int multi_debrief_stats_accept_code()
9497 return Multi_debrief_stats_accept_code;
9500 void multi_debrief_server_process()
9503 int player_status,other_status;
9505 Multi_debrief_server_framecount++;
9507 // if we're > 10 seconds into the debrief and not everyone is here, try warping everyone out again
9508 if((Multi_debrief_time >= Multi_debrief_resend_time) && !multi_netplayer_state_check3(NETPLAYER_STATE_DEBRIEF, NETPLAYER_STATE_DEBRIEF_ACCEPT, NETPLAYER_STATE_DEBRIEF_REPLAY, 1)){
9509 // find all players who are not in the debrief state and hit them with the endgame packet
9510 for(idx=0; idx<MAX_PLAYERS; idx++){
9511 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)) ){
9512 send_endgame_packet(&Net_players[idx]);
9517 Multi_debrief_resend_time += 7.0f;
9520 // evaluate the status of all players in the game (0 == not ready, 1 == ready to continue, 2 == ready to replay)
9523 // check all players
9524 for(idx=0;idx<MAX_PLAYERS;idx++){
9525 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_HOST(Net_players[idx])){
9526 if(Net_players[idx].state != NETPLAYER_STATE_DEBRIEF_ACCEPT){
9533 // if we haven't already reported TvT results
9534 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)){
9535 multi_team_report();
9536 Multi_debrief_reported_tvt = 1;
9539 // if all other players are good to go, check the host
9541 // if he is ready to continue
9542 if(Netgame.host->state == NETPLAYER_STATE_DEBRIEF_ACCEPT){
9545 // if he wants to replay the mission
9546 else if(Netgame.host->state == NETPLAYER_STATE_DEBRIEF_REPLAY){
9549 // if he is not ready
9554 // if all players are _not_ good to go
9559 // if we're in the debriefing state in a campaign mode, process accordingly
9560 if(Netgame.campaign_mode == MP_CAMPAIGN){
9561 multi_campaign_do_debrief(player_status);
9563 // otherwise process as normal (looking for all players to be ready to go to the next mission
9565 if(player_status == 1){
9566 multi_flush_mission_stuff();
9568 // set the netgame state to be forming and continue
9569 Netgame.game_state = NETGAME_STATE_FORMING;
9570 send_netgame_update_packet();
9572 // move to the proper state
9573 if(Game_mode & GM_STANDALONE_SERVER){
9574 gameseq_post_event(GS_EVENT_STANDALONE_MAIN);
9576 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
9579 multi_reset_timestamps();
9580 } else if(player_status == 2){
9581 multi_flush_mission_stuff();
9583 // tell everyone to move into the pre-briefing sync state
9584 Netgame.game_state = NETGAME_STATE_MISSION_SYNC;
9585 send_netgame_update_packet();
9587 // move back to the mission sync screen for the same mission again
9588 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
9589 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
9591 multi_reset_timestamps();
9597 // -------------------------------------------------------------------------------------------------------------
9599 // MULTIPLAYER PASSWORD POPUP
9604 static char *Multi_pwd_bitmap_fname[GR_NUM_RESOLUTIONS] = {
9605 "Password", // GR_640
9606 "2_Password" // GR_1024
9609 static char *Multi_pwd_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
9610 "Password-M", // GR_640
9611 "2_Password-M" // GR_1024
9616 // constants for coordinate lookup
9617 #define MPWD_X_COORD 0
9618 #define MPWD_Y_COORD 1
9619 #define MPWD_W_COORD 2
9620 #define MPWD_H_COORD 3
9623 #define MULTI_PWD_NUM_BUTTONS 2
9624 #define MPWD_CANCEL 0
9625 #define MPWD_COMMIT 1
9627 // password area defs
9628 int Mpwd_coords[GR_NUM_RESOLUTIONS][4] = {
9641 UI_WINDOW Multi_pwd_window; // the window object for the join screen
9642 UI_INPUTBOX Multi_pwd_passwd; // for Netgame.passwd
9643 int Multi_pwd_bitmap; // the background bitmap
9644 int Multi_passwd_background = -1;
9645 int Multi_passwd_done = -1;
9646 int Multi_passwd_running = 0;
9649 ui_button_info Multi_pwd_buttons[GR_NUM_RESOLUTIONS][MULTI_PWD_NUM_BUTTONS] = {
9652 ui_button_info("PWB_00", 402, 134, -1, -1, 0),
9653 ui_button_info("PWB_01", 450, 134, -1, -1, 1),
9655 ui_button_info("PWB_00", 411, 151, 405, 141, 0),
9656 ui_button_info("PWB_01", 460, 151, 465, 141, 1),
9660 ui_button_info("2_PWB_00", 659, 242, 649, 225, 0),
9661 ui_button_info("2_PWB_01", 737, 242, 736, 225, 1),
9667 #define MULTI_PWD_NUM_TEXT 0
9669 #define MULTI_PWD_NUM_TEXT 3
9671 UI_XSTR Multi_pwd_text[GR_NUM_RESOLUTIONS][MULTI_PWD_NUM_TEXT] = {
9673 // not needed for FS1
9675 { "Cancel", 387, 400, 141, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[0][MPWD_CANCEL].button},
9676 { "Commit", 1062, 455, 141, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[0][MPWD_COMMIT].button},
9677 { "Enter Password", 1332, 149, 92, UI_XSTR_COLOR_GREEN, -1, NULL},
9681 // not needed for FS1
9683 { "Cancel", 387, 649, 225, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[1][MPWD_CANCEL].button},
9684 { "Commit", 1062, 736, 225, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[1][MPWD_COMMIT].button},
9685 { "Enter Password", 1332, 239, 148, UI_XSTR_COLOR_GREEN, -1, NULL},
9690 // initialize all graphics, etc
9691 void multi_passwd_init()
9695 // store the background as it currently is
9696 Multi_passwd_background = gr_save_screen();
9698 // create the interface window
9699 Multi_pwd_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
9700 Multi_pwd_window.set_mask_bmap(Multi_pwd_bitmap_mask_fname[gr_screen.res]);
9702 // load the background bitmap
9703 Multi_pwd_bitmap = bm_load(Multi_pwd_bitmap_fname[gr_screen.res]);
9704 if(Multi_pwd_bitmap < 0){
9705 // we failed to load the bitmap - this is very bad
9709 // create the interface buttons
9710 for(idx=0; idx<MULTI_PWD_NUM_BUTTONS; idx++){
9711 // create the object
9712 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);
9714 // set the sound to play when highlighted
9715 Multi_pwd_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
9717 // set the ani for the button
9718 Multi_pwd_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pwd_buttons[gr_screen.res][idx].filename);
9721 Multi_pwd_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pwd_buttons[gr_screen.res][idx].hotspot);
9725 for(idx=0; idx<MULTI_PWD_NUM_TEXT; idx++){
9726 Multi_pwd_window.add_XSTR(&Multi_pwd_text[gr_screen.res][idx]);
9729 // create the password input box
9730 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);
9731 Multi_pwd_passwd.set_focus();
9733 // link the enter key to ACCEPT
9734 Multi_pwd_buttons[gr_screen.res][MPWD_COMMIT].button.set_hotkey(KEY_ENTER);
9736 Multi_passwd_done = -1;
9737 Multi_passwd_running = 1;
9740 // close down all graphics, etc
9741 void multi_passwd_close()
9743 // unload any bitmaps
9744 bm_release(Multi_pwd_bitmap);
9746 // destroy the UI_WINDOW
9747 Multi_pwd_window.destroy();
9749 // free up the saved background screen
9750 if(Multi_passwd_background >= 0){
9751 gr_free_screen(Multi_passwd_background);
9752 Multi_passwd_background = -1;
9755 Multi_passwd_running = 0;
9758 // process any button pressed
9759 void multi_passwd_process_buttons()
9761 // if the accept button was pressed
9762 if(Multi_pwd_buttons[gr_screen.res][MPWD_COMMIT].button.pressed()){
9763 gamesnd_play_iface(SND_USER_SELECT);
9764 Multi_passwd_done = 1;
9767 // if the cancel button was pressed
9768 if(Multi_pwd_buttons[gr_screen.res][MPWD_CANCEL].button.pressed()){
9769 gamesnd_play_iface(SND_USER_SELECT);
9770 Multi_passwd_done = 0;
9774 // run the passwd popup
9775 void multi_passwd_do(char *passwd)
9779 while(Multi_passwd_done == -1){
9780 // set frametime and run background stuff
9781 game_set_frametime(-1);
9782 game_do_state_common(gameseq_get_state());
9784 k = Multi_pwd_window.process();
9786 // process any keypresses
9789 // set this to indicate the user has cancelled for one reason or another
9790 Multi_passwd_done = 0;
9794 // if the input box text has changed
9795 if(Multi_pwd_passwd.changed()){
9797 Multi_pwd_passwd.get_text(passwd);
9800 // process any button pressed
9801 multi_passwd_process_buttons();
9803 // draw the background, etc
9806 if(Multi_passwd_background >= 0){
9807 gr_restore_screen(Multi_passwd_background);
9809 gr_set_bitmap(Multi_pwd_bitmap);
9811 Multi_pwd_window.draw();
9818 // bring up the password string popup, fill in passwd (return 1 if accept was pressed, 0 if cancel was pressed)
9819 int multi_passwd_popup(char *passwd)
9821 // if the popup is already running for some reason, don't do anything
9822 if(Multi_passwd_running){
9826 // initialize all graphics
9827 multi_passwd_init();
9830 multi_passwd_do(passwd);
9832 // shut everything down
9833 multi_passwd_close();
9835 return Multi_passwd_done;