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.12 2005/10/02 09:27:49 taylor
19 * fix interface problems with MultiJoinWait screen in FS1
21 * Revision 1.11 2005/03/29 02:18:47 taylor
22 * Various 64-bit platform fixes
23 * Fix compiler errors with MAKE_FS1 and fix gr_set_bitmap() too
24 * Make sure that turrets can fire at asteroids for FS1 (needed for a couple missions)
25 * Streaming audio support (big thanks to Pierre Willenbrock!!)
26 * Removed dependance on strings.tbl for FS1 since we don't actually need it now
28 * Revision 1.10 2004/09/20 01:31:44 theoddone33
31 * Revision 1.9 2004/07/04 11:39:06 taylor
32 * fix missing debrief text, crash on exit, path separator's, warning fixes, no GR_SOFT
34 * Revision 1.8 2003/05/25 02:30:43 taylor
37 * Revision 1.7 2002/06/09 04:41:24 relnev
38 * added copyright header
40 * Revision 1.6 2002/06/02 06:02:59 relnev
43 * Revision 1.5 2002/06/02 00:31:35 relnev
44 * implemented osregistry
46 * Revision 1.4 2002/06/01 07:12:33 relnev
47 * a few NDEBUG updates.
49 * removed a few warnings.
51 * Revision 1.3 2002/05/26 20:49:54 theoddone33
54 * Revision 1.2 2002/05/07 03:16:47 theoddone33
55 * The Great Newline Fix
57 * Revision 1.1.1.1 2002/05/03 03:28:10 root
61 * 94 6/16/00 3:16p Jefff
62 * sim of the year dvd version changes, a few german soty localization
65 * 93 10/14/99 2:51p Jefff
68 * 92 10/13/99 3:50p Jefff
69 * fixed unnumbered XSTRs
71 * 91 9/15/99 1:45a Dave
72 * Don't init joystick on standalone. Fixed campaign mode on standalone.
73 * Fixed no-score-report problem in TvT
75 * 90 9/14/99 12:51a Jefff
78 * 89 9/13/99 4:52p Dave
81 * 88 9/13/99 11:30a Dave
82 * Added checkboxes and functionality for disabling PXO banners as well as
83 * disabling d3d zbuffer biasing.
85 * 87 9/12/99 10:06p Jefff
86 * changed instances of "Squad War" to "SquadWar"
88 * 86 9/03/99 1:32a Dave
89 * CD checking by act. Added support to play 2 cutscenes in a row
90 * seamlessly. Fixed super low level cfile bug related to files in the
91 * root directory of a CD. Added cheat code to set campaign mission # in
94 * 85 9/01/99 10:49p Dave
95 * Added nice SquadWar checkbox to the client join wait screen.
97 * 84 8/30/99 2:49p Jefff
99 * 83 8/26/99 8:49p Jefff
100 * Updated medals screen and about everything that ever touches medals in
101 * one way or another. Sheesh.
103 * 82 8/25/99 4:38p Dave
104 * Updated PXO stuff. Make squad war report stuff much more nicely.
106 * 81 8/20/99 2:09p Dave
107 * PXO banner cycling.
109 * 80 8/20/99 10:06a Jefff
110 * removed closed/rstricted buttons from multi start screen
112 * 79 8/18/99 11:30a Jefff
114 * 78 8/18/99 10:38a Jefff
116 * 77 8/16/99 4:06p Dave
117 * Big honking checkin.
119 * 76 8/16/99 1:08p Jefff
120 * added sounds to a few controls, made input boxes lose focus on ENTER
122 * 75 8/16/99 9:52a Jefff
123 * fixed bitmap loading on buttons in multi-sync screen
125 * 74 8/11/99 5:54p Dave
126 * Fixed collision problem. Fixed standalone ghost problem.
128 * 73 8/10/99 4:35p Jefff
129 * fixed hi-res coords
131 * 72 8/06/99 12:29a Dave
132 * Multiple bug fixes.
134 * 71 8/05/99 3:13p Jasenw
135 * tweaked some text placement coords.
137 * 70 8/04/99 1:38p Jefff
138 * moved some text in multi join wait
140 * 69 8/03/99 12:45p Dave
143 * 68 7/25/99 5:17p Jefff
144 * campaign descriptions show up on multicreate screen
146 * 67 7/20/99 1:49p Dave
147 * Peter Drake build. Fixed some release build warnings.
149 * 66 7/19/99 2:13p Dave
150 * Added some new strings for Heiko.
152 * 65 7/15/99 9:20a Andsager
153 * FS2_DEMO initial checkin
155 * 64 7/08/99 10:53a Dave
156 * New multiplayer interpolation scheme. Not 100% done yet, but still
157 * better than the old way.
159 * 63 6/30/99 10:49a Jasenw
160 * Fixed coords for new launch countdown ani
162 * 62 6/29/99 7:39p Dave
163 * Lots of small bug fixes.
165 * 61 6/25/99 11:59a Dave
166 * Multi options screen.
168 * 60 6/09/99 2:17p Dave
169 * Fixed up pleasewait bitmap rendering.
171 * 59 6/05/99 3:42p Dave
172 * New multi sync screen.
174 * 58 6/01/99 6:07p Dave
175 * New loading/pause/please wait bar.
177 * 57 5/21/99 6:45p Dave
178 * Sped up ui loading a bit. Sped up localization disk access stuff. Multi
179 * start game screen, multi password, and multi pxo-help screen.
181 * 56 5/06/99 11:10a Dave
182 * Fixed coord on multi create screen.
184 * 55 5/04/99 6:38p Dave
185 * Finished multi join-wait screen.
187 * 54 5/04/99 5:20p Dave
188 * Fixed up multiplayer join screen and host options screen. Should both
191 * 53 5/03/99 11:04p Dave
192 * Most of the way done with the multi join screen.
194 * 52 5/03/99 8:32p Dave
195 * New version of multi host options screen.
197 * 51 4/29/99 2:15p Neilk
198 * slider2 code got modified; changed parameters for create
200 * 50 4/25/99 3:02p Dave
201 * Build defines for the E3 build.
203 * 49 4/21/99 6:15p Dave
204 * Did some serious housecleaning in the beam code. Made it ready to go
205 * for anti-fighter "pulse" weapons. Fixed collision pair creation. Added
206 * a handy macro for recalculating collision pairs for a given object.
208 * 48 4/16/99 5:27p Neilk
209 * added slider support and hir res for multi_create
211 * 47 4/14/99 6:37p Dave
212 * Fixed scroll button bug on host create screen.
214 * 46 4/14/99 5:28p Dave
217 * 45 4/12/99 10:07p Dave
218 * Made network startup more forgiving. Added checkmarks to dogfight
219 * screen for players who hit commit.
221 * 44 4/09/99 2:21p Dave
222 * Multiplayer beta stuff. CD checking.
224 * 43 4/08/99 1:28p Dave
225 * Small bug fixes for refresh button on the multi create screen.
227 * 42 4/08/99 11:55a Neilk
228 * Converted Multi_Create to new artwork (just lowres)
230 * 41 4/08/99 2:10a Dave
231 * Numerous bug fixes for the beta. Added builtin mission info for the
234 * 40 3/20/99 3:48p Andsager
235 * Do mission_loop stuff for PXO
237 * 39 3/10/99 6:50p Dave
238 * Changed the way we buffer packets for all clients. Optimized turret
239 * fired packets. Did some weapon firing optimizations.
241 * 38 3/09/99 6:24p Dave
242 * More work on object update revamping. Identified several sources of
243 * unnecessary bandwidth.
245 * 37 3/08/99 7:03p Dave
246 * First run of new object update system. Looks very promising.
248 * 36 2/25/99 4:19p Dave
249 * Added multiplayer_beta defines. Added cd_check define. Fixed a few
250 * release build warnings. Added more data to the squad war request and
253 * 35 2/24/99 3:26p Anoop
254 * Make sure the host is the only guy who bashes skill level for TvT.
256 * 34 2/24/99 2:25p Dave
257 * Fixed up chatbox bugs. Made squad war reporting better. Fixed a respawn
258 * bug for dogfight more.
260 * 33 2/23/99 2:29p Dave
261 * First run of oldschool dogfight mode.
263 * 32 2/17/99 2:11p Dave
264 * First full run of squad war. All freespace and tracker side stuff
267 * 31 2/12/99 6:16p Dave
268 * Pre-mission Squad War code is 95% done.
270 * 30 2/11/99 3:08p Dave
271 * PXO refresh button. Very preliminary squad war support.
273 * 29 2/08/99 5:07p Dave
274 * FS2 chat server support. FS2 specific validated missions.
276 * 28 2/04/99 6:29p Dave
277 * First full working rev of FS2 PXO support. Fixed Glide lighting
280 * 27 1/30/99 5:08p Dave
281 * More new hi-res stuff.Support for nice D3D textures.
283 * 26 1/29/99 2:08a Dave
284 * Fixed beam weapon collisions with players. Reduced size of scoring
285 * struct for multiplayer. Disabled PXO.
287 * 25 1/15/99 2:36p Neilk
288 * fixed multi_jw coordinates
290 * 24 1/13/99 7:19p Neilk
291 * Converted Mission Brief, Barracks, Synch to high res support
293 * 23 1/12/99 7:17p Neilk
295 * 22 1/12/99 5:45p Dave
296 * Moved weapon pipeline in multiplayer to almost exclusively client side.
297 * Very good results. Bandwidth goes down, playability goes up for crappy
298 * connections. Fixed object update problem for ship subsystems.
300 * 21 1/12/99 4:07a Dave
301 * Put in barracks code support for selecting squad logos. Properly
302 * distribute squad logos in a multiplayer game.
304 * 20 1/11/99 7:19p Neilk
305 * Converted multi_join interface to support multiple resolutions
307 * 19 12/18/98 1:13a Dave
308 * Rough 1024x768 support for Direct3D. Proper detection and usage through
311 * 18 12/17/98 4:50p Andsager
312 * Added debrief_assemble_optional_mission_popup_text() for single and
315 * 17 12/14/98 12:13p Dave
316 * Spiffed up xfer system a bit. Put in support for squad logo file xfer.
319 * 16 12/10/98 10:19a Andsager
320 * Fix mission loop assert
322 * 15 12/10/98 9:59a Andsager
323 * Fix some bugs with mission loops
325 * 14 12/09/98 1:56p Andsager
326 * Initial checkin of mission loop
328 * 13 12/03/98 5:22p Dave
329 * Ported over Freespace 1 multiplayer ships.tbl and weapons.tbl
332 * 12 11/30/98 1:07p Dave
333 * 16 bit conversion, first run.
335 * 11 11/20/98 11:16a Dave
336 * Fixed up IPX support a bit. Making sure that switching modes and
337 * loading/saving pilot files maintains proper state.
339 * 10 11/19/98 4:57p Dave
340 * Ignore PXO option if IPX is selected.
342 * 9 11/19/98 4:19p Dave
343 * Put IPX sockets back in psnet. Consolidated all multiplayer config
346 * 8 11/19/98 8:04a Dave
347 * Full support for D3-style reliable sockets. Revamped packet lag/loss
348 * system, made it receiver side and at the lowest possible level.
350 * 7 11/17/98 11:12a Dave
351 * Removed player identification by address. Now assign explicit id #'s.
353 * 6 10/19/98 11:15a Dave
354 * Changed requirements for stats storing in PXO mode.
356 * 5 10/16/98 9:40a Andsager
357 * Remove ".h" files from model.h
359 * 4 10/13/98 9:29a Dave
360 * Started neatening up freespace.h. Many variables renamed and
361 * reorganized. Added AlphaColors.[h,cpp]
363 * 3 10/07/98 6:27p Dave
364 * Globalized mission and campaign file extensions. Removed Silent Threat
365 * special code. Moved \cache \players and \multidata into the \data
368 * 2 10/07/98 10:53a Dave
371 * 1 10/07/98 10:50a Dave
373 * 333 10/02/98 3:22p Allender
374 * fix up the -connect option and fix the -port option
376 * 332 9/17/98 9:26p Dave
377 * Externalized new string.
379 * 331 9/17/98 3:08p Dave
380 * PXO to non-pxo game warning popup. Player icon stuff in create and join
381 * game screens. Upped server count refresh time in PXO to 35 secs (from
384 * 330 9/17/98 9:43a Allender
385 * removed an Assert that Dave called bogus.
387 * 329 9/16/98 6:54p Dave
388 * Upped max sexpression nodes to 1800 (from 1600). Changed FRED to sort
389 * the ship list box. Added code so that tracker stats are not stored with
392 * 328 9/15/98 7:24p Dave
393 * Minor UI changes. Localized bunch of new text.
395 * 327 9/15/98 4:03p Dave
396 * Changed readyroom and multi screens to display "st" icon for all
397 * missions with mission disk content (not necessarily just those that
398 * come with Silent Threat).
400 * 326 9/15/98 11:44a Dave
401 * Renamed builtin ships and wepaons appropriately in FRED. Put in scoring
402 * scale factors. Fixed standalone filtering of MD missions to non-MD
405 * 325 9/13/98 9:36p Dave
406 * Support for new info icons for multiplayer missions (from-volition,
407 * valid, mission disk, etc).
409 * 324 9/11/98 4:14p Dave
410 * Fixed file checksumming of < file_size. Put in more verbose kicking and
411 * PXO stats store reporting.
413 * 323 9/10/98 1:17p Dave
414 * Put in code to flag missions and campaigns as being MD or not in Fred
415 * and Freespace. Put in multiplayer support for filtering out MD
416 * missions. Put in multiplayer popups for warning of non-valid missions.
418 * 322 9/04/98 3:51p Dave
419 * Put in validated mission updating and application during stats
422 * 321 8/31/98 2:06p Dave
423 * Make cfile sort the ordering or vp files. Added support/checks for
424 * recognizing "mission disk" players.
426 * 320 8/21/98 1:15p Dave
427 * Put in log system hooks in useful places.
429 * 319 8/20/98 5:31p Dave
430 * Put in handy multiplayer logfile system. Now need to put in useful
431 * applications of it all over the code.
433 * 318 8/12/98 4:53p Dave
434 * Put in 32 bit checksumming for PXO missions. No validation on the
435 * actual tracker yet, though.
437 * 317 8/07/98 10:40a Allender
438 * new command line flags for starting netgames. Only starting currently
439 * works, and PXO isn't implemented yet
441 * 316 7/24/98 9:27a Dave
442 * Tidied up endgame sequencing by removing several old flags and
443 * standardizing _all_ endgame stuff with a single function call.
445 * 315 7/14/98 10:04a Allender
446 * fixed the countdown code to not be reliant on timer_get_fixed_seconds
448 * 314 7/10/98 5:04p Dave
449 * Fix connection speed bug on standalone server.
451 * 313 7/09/98 6:01p Dave
452 * Firsts full version of PXO updater. Put in stub for displaying
455 * 312 7/07/98 2:49p Dave
458 * 311 6/30/98 2:17p Dave
459 * Revised object update system. Removed updates for all weapons. Put
460 * button info back into control info packet.
462 * 310 6/13/98 9:32p Mike
463 * Kill last character in file which caused "Find in Files" to report the
464 * file as "not a text file."
471 #include <winsock.h> // for inet_addr()
473 #include <sys/types.h>
474 #include <sys/socket.h>
475 #include <netinet/in.h>
476 #include <arpa/inet.h>
481 #include "multiutil.h"
482 #include "multimsgs.h"
488 #include "gamesequence.h"
489 #include "freespace.h"
490 #include "contexthelp.h"
495 #include "missionshipchoice.h"
496 #include "multi_xfer.h"
498 #include "stand_gui.h"
499 #include "linklist.h"
500 #include "multiteamselect.h"
501 #include "missioncampaign.h"
508 #include "missiondebrief.h"
509 #include "multi_ingame.h"
510 #include "multi_kick.h"
511 #include "multi_data.h"
512 #include "multi_campaign.h"
513 #include "multi_team.h"
514 #include "multi_pinfo.h"
515 #include "multi_observer.h"
516 #include "multi_voice.h"
517 #include "multi_endgame.h"
518 #include "managepilot.h"
521 #include "objcollide.h"
523 #include "multi_pmsg.h"
524 #include "multi_obj.h"
525 #include "multi_log.h"
526 #include "alphacolors.h"
527 #include "animplay.h"
528 #include "multi_dogfight.h"
529 #include "missionpause.h"
531 // -------------------------------------------------------------------------------------------------------------
533 // MULTIPLAYER COMMON interface controls
536 // the common text info box stuff. This is lifted almost directly from Alans briefing code (minus the spiffy colored, scrolling
538 int Multi_common_text_coords[GR_NUM_RESOLUTIONS][4] = {
551 int Multi_common_text_max_display[GR_NUM_RESOLUTIONS] = {
560 #define MULTI_COMMON_TEXT_META_CHAR '$'
561 #define MULTI_COMMON_TEXT_MAX_LINE_LENGTH 100
562 #define MULTI_COMMON_TEXT_MAX_LINES 20
563 #define MULTI_COMMON_MAX_TEXT (MULTI_COMMON_TEXT_MAX_LINES * MULTI_COMMON_TEXT_MAX_LINE_LENGTH)
565 char Multi_common_all_text[MULTI_COMMON_MAX_TEXT];
566 char Multi_common_text[MULTI_COMMON_TEXT_MAX_LINES][MULTI_COMMON_TEXT_MAX_LINE_LENGTH];
568 int Multi_common_top_text_line = -1; // where to start displaying from
569 int Multi_common_num_text_lines = 0; // how many lines we have
571 void multi_common_scroll_text_up();
572 void multi_common_scroll_text_down();
573 void multi_common_move_to_bottom();
574 void multi_common_render_text();
575 void multi_common_split_text();
577 #define MAX_IP_STRING 255 // maximum length for ip string
579 void multi_common_scroll_text_up()
581 Multi_common_top_text_line--;
582 if ( Multi_common_top_text_line < 0 ) {
583 Multi_common_top_text_line = 0;
584 if ( !mouse_down(MOUSE_LEFT_BUTTON) )
585 gamesnd_play_iface(SND_GENERAL_FAIL);
588 gamesnd_play_iface(SND_SCROLL);
592 void multi_common_scroll_text_down()
594 Multi_common_top_text_line++;
595 if ( (Multi_common_num_text_lines - Multi_common_top_text_line) < Multi_common_text_max_display[gr_screen.res] ) {
596 Multi_common_top_text_line--;
597 if ( !mouse_down(MOUSE_LEFT_BUTTON) ){
598 gamesnd_play_iface(SND_GENERAL_FAIL);
601 gamesnd_play_iface(SND_SCROLL);
605 void multi_common_move_to_bottom()
607 // if there's nowhere to scroll down, do nothing
608 if(Multi_common_num_text_lines <= Multi_common_text_max_display[gr_screen.res]){
612 Multi_common_top_text_line = Multi_common_num_text_lines - Multi_common_text_max_display[gr_screen.res];
615 void multi_common_set_text(const char *str, int auto_scroll)
618 // store the entire string as well
619 if(strlen(str) > MULTI_COMMON_MAX_TEXT){
622 strcpy(Multi_common_all_text,str);
625 // split the whole thing up
626 multi_common_split_text();
628 // scroll to the bottom if we're supposed to
630 multi_common_move_to_bottom();
634 void multi_common_add_text(const char *str, int auto_scroll)
637 // store the entire string as well
638 if((strlen(str) + strlen(Multi_common_all_text)) > MULTI_COMMON_MAX_TEXT){
641 strcat(Multi_common_all_text,str);
644 // split the whole thing up
645 multi_common_split_text();
647 // scroll to the bottom if we're supposed to
649 multi_common_move_to_bottom();
653 void multi_common_split_text()
656 int n_chars[MAX_BRIEF_LINES];
657 char *p_str[MAX_BRIEF_LINES];
659 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);
660 Assert(n_lines != -1);
662 for ( i = 0; i < n_lines; i++ ) {
663 Assert(n_chars[i] < MULTI_COMMON_TEXT_MAX_LINE_LENGTH);
664 strncpy(Multi_common_text[i], p_str[i], n_chars[i]);
665 Multi_common_text[i][n_chars[i]] = 0;
666 drop_leading_white_space(Multi_common_text[i]);
669 Multi_common_top_text_line = 0;
670 Multi_common_num_text_lines = n_lines;
673 void multi_common_render_text()
675 int i, fh, line_count;
677 fh = gr_get_font_height();
680 gr_set_color_fast(&Color_text_normal);
681 for ( i = Multi_common_top_text_line; i < Multi_common_num_text_lines; i++ ) {
682 if ( line_count >= Multi_common_text_max_display[gr_screen.res] ){
685 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]);
689 if ( (Multi_common_num_text_lines - Multi_common_top_text_line) > Multi_common_text_max_display[gr_screen.res] ) {
690 gr_set_color_fast(&Color_bright_red);
691 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));
695 // common notification messaging stuff
696 #define MULTI_COMMON_NOTIFY_TIME 3500
697 int Multi_common_join_y[GR_NUM_RESOLUTIONS] = {
701 int Multi_common_create_y[GR_NUM_RESOLUTIONS] = {
706 int Multi_common_jw_y[GR_NUM_RESOLUTIONS] = {
711 int Multi_common_msg_y[GR_NUM_RESOLUTIONS] = {
716 char Multi_common_notify_text[200];
717 int Multi_common_notify_stamp;
719 void multi_common_notify_init()
721 strcpy(Multi_common_notify_text,"");
722 Multi_common_notify_stamp = -1;
725 // add a notification string, drawing appropriately depending on the state/screen we're in
726 void multi_common_add_notify(const char *str)
729 strcpy(Multi_common_notify_text,str);
730 Multi_common_notify_stamp = timestamp(MULTI_COMMON_NOTIFY_TIME);
734 // process/display notification messages
735 void multi_common_notify_do()
737 if(Multi_common_notify_stamp != -1){
738 if(timestamp_elapsed(Multi_common_notify_stamp)){
739 Multi_common_notify_stamp = -1;
742 gr_get_string_size(&w,&h,Multi_common_notify_text);
743 gr_set_color_fast(&Color_white);
745 // determine where it should be placed based upon which screen we're on
747 switch(gameseq_get_state()){
748 case GS_STATE_MULTI_JOIN_GAME :
749 y = Multi_common_join_y[gr_screen.res];
751 case GS_STATE_MULTI_HOST_SETUP :
752 y = Multi_common_create_y[gr_screen.res];
754 case GS_STATE_MULTI_CLIENT_SETUP :
755 y = Multi_common_jw_y[gr_screen.res];
757 case GS_STATE_MULTI_START_GAME :
758 y = Multi_common_msg_y[gr_screen.res];
762 gr_string((gr_screen.max_w - w)/2, y, Multi_common_notify_text);
769 int Multi_common_icons[MULTI_NUM_COMMON_ICONS];
771 const char *Multi_common_icon_names[MULTI_NUM_COMMON_ICONS] = {
772 "DotRed", // voice denied
773 "DotGreen", // voice recording
774 "OvalGreen", // team 0
775 "OvalGreen01", // team 0 select
777 "OvalRed01", // team 1 select
778 "mp_coop", // coop mission
779 "mp_teams", // TvT mission
780 "mp_furball", // furball mission
781 "icon-volition", // volition mission
782 "icon-valid", // mission is valid
787 "icon-silent" // SilentThreat
791 // width and height of the icons
792 int Multi_common_icon_dims[MULTI_NUM_COMMON_ICONS][2] = {
793 {11, 11}, // voice denied
794 {11, 11}, // voice recording
796 {11, 11}, // team 0 select
798 {11, 11}, // team 1 select
801 {18, 11}, // mp furball
802 {9, 9}, // volition mission
803 {8, 8}, // mission is valid
808 {16, 7} // silent threat
812 void multi_load_common_icons()
817 for(idx=0; idx<MULTI_NUM_COMMON_ICONS; idx++){
818 Multi_common_icons[idx] = -1;
819 Multi_common_icons[idx] = bm_load(Multi_common_icon_names[idx]);
823 void multi_unload_common_icons()
828 for(idx=0; idx<MULTI_NUM_COMMON_ICONS; idx++){
829 if(Multi_common_icons[idx] != -1){
830 bm_unload(Multi_common_icons[idx]);
831 Multi_common_icons[idx] = -1;
836 // display any relevant voice status icons
837 void multi_common_voice_display_status()
839 switch(multi_voice_status()){
840 // i have been denied the voice token
841 case MULTI_VOICE_STATUS_DENIED:
842 if(Multi_common_icons[MICON_VOICE_DENIED] != -1){
843 gr_set_bitmap(Multi_common_icons[MICON_VOICE_DENIED], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
848 // i am currently recording
849 case MULTI_VOICE_STATUS_RECORDING:
850 if(Multi_common_icons[MICON_VOICE_RECORDING] != -1){
851 gr_set_bitmap(Multi_common_icons[MICON_VOICE_RECORDING], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
856 // i am currently playing back sound
857 case MULTI_VOICE_STATUS_PLAYING:
860 // the system is currently idle
861 case MULTI_VOICE_STATUS_IDLE:
867 // palette initialization stuff
868 #define MULTI_COMMON_PALETTE_FNAME "InterfacePalette"
871 int Multi_common_interface_palette = -1;
873 void multi_common_load_palette();
874 void multi_common_set_palette();
875 void multi_common_unload_palette();
877 // load in the palette if it doesn't already exist
878 void multi_common_load_palette()
880 if(Multi_common_interface_palette != -1){
884 Multi_common_interface_palette = bm_load(MULTI_COMMON_PALETTE_FNAME);
885 if(Multi_common_interface_palette == -1){
886 nprintf(("Network","Error loading multiplayer common palette!\n"));
890 // set the common palette to be the active one
891 void multi_common_set_palette()
893 // if the palette is not loaded yet, do so now
894 if(Multi_common_interface_palette == -1){
895 multi_common_load_palette();
898 if(Multi_common_interface_palette != -1){
899 #ifndef HARDWARE_ONLY
900 palette_use_bm_palette(Multi_common_interface_palette);
905 // unload the bitmap palette
906 void multi_common_unload_palette()
908 if(Multi_common_interface_palette != -1){
909 bm_unload(Multi_common_interface_palette);
910 Multi_common_interface_palette = -1;
914 void multi_common_verify_cd()
917 // otherwise, call the freespace function to determine if we have a cd
920 if((find_freespace_cd(FS_CDROM_VOLUME_1) >= 0) || (find_freespace_cd(FS_CDROM_VOLUME_2) >= 0) ){
922 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) ){
932 // -------------------------------------------------------------------------------------------------------------
934 // MULTIPLAYER JOIN SCREEN
937 #define MULTI_JOIN_NUM_BUTTONS 11
941 #define MULTI_JOIN_PALETTE "InterfacePalette"
943 static const char *Multi_join_bitmap_fname[GR_NUM_RESOLUTIONS] = {
944 "MultiJoin", // GR_640
945 "2_MultiJoin" // GR_1024
948 static const char *Multi_join_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
949 "MultiJoin-M", // GR_640
950 "2_MultiJoin-M" // GR_1024
956 const char *Mj_slider_name[GR_NUM_RESOLUTIONS] = {
960 int Mj_slider_coords[GR_NUM_RESOLUTIONS][4] = {
971 #define MJ_SCROLL_UP 0
972 #define MJ_SCROLL_DOWN 1
974 #define MJ_SCROLL_INFO_UP 3
975 #define MJ_SCROLL_INFO_DOWN 4
976 #define MJ_JOIN_OBSERVER 5
977 #define MJ_START_GAME 6
983 // uses MULTI_JOIN_REFRESH_TIME as its timestamp
984 int Multi_join_glr_stamp;
986 #define MULTI_JOIN_PING_TIME 15000 // how often we ping all the known servers
987 int Multi_join_ping_stamp;
988 UI_WINDOW Multi_join_window; // the window object for the join screen
989 UI_BUTTON Multi_join_select_button; // for selecting list items
991 UI_SLIDER2 Multi_join_slider; // handy dandy slider
993 int Multi_join_bitmap; // the background bitmap
995 ui_button_info Multi_join_buttons[GR_NUM_RESOLUTIONS][MULTI_JOIN_NUM_BUTTONS] = {
998 ui_button_info("MJ_00", 0, 85, -1, -1, 0),
999 ui_button_info("MJ_01", 0, 125, -1, -1, 1),
1000 ui_button_info("MJ_03", 20, 324, -1, -1, 3),
1001 ui_button_info("MJ_04", 0, 399, -1, -1, 4),
1002 ui_button_info("MJ_05", 0, 436, -1, -1, 5),
1003 ui_button_info("MJ_15", 450, 323, -1, -1, 15),
1004 ui_button_info("MJ_10", 519, 323, -1, -1, 10),
1005 ui_button_info("MJ_06", 574, 323, -1, -1, 6),
1006 ui_button_info("MJ_08", 470, 427, -1, -1, 8),
1007 ui_button_info("MJ_09", 448, 454, -1, -1, 9),
1008 ui_button_info("MJ_07", 563, 411, -1, -1, 7),
1010 ui_button_info( "MJ_00", 1, 57, -1, -1, 0 ), // scroll up
1011 ui_button_info( "MJ_02", 1, 297, -1, -1, 2 ), // scroll down
1012 ui_button_info( "MJ_03", 10, 338, 65, 364, 3 ), // refresh
1013 ui_button_info( "MJ_04", 1, 405, -1, -1, 4 ), // scroll info up
1014 ui_button_info( "MJ_05", 1, 446, -1, -1, 5 ), // scroll info down
1015 ui_button_info( "MJ_06", 489, 339, -1, -1, 6 ), // join as observer
1016 ui_button_info( "MJ_07", 538, 339, -1, -1, 7 ), // create game
1017 ui_button_info( "MJ_08", 583, 339, 588, 376, 8 ), // cancel
1018 ui_button_info( "MJ_09", 534, 426, -1, -1, 9 ), // help
1019 ui_button_info( "MJ_10", 534, 454, -1, -1, 10 ), // options
1020 ui_button_info( "MJ_11", 571, 426, 589, 416, 11 ), // join
1024 ui_button_info( "2_MJ_00", 2, 92, -1, -1, 0 ), // scroll up
1025 ui_button_info( "2_MJ_02", 2, 475, -1, -1, 2 ), // scroll down
1026 ui_button_info( "2_MJ_03", 16, 541, 104, 582, 3 ), // refresh
1027 ui_button_info( "2_MJ_04", 2, 648, -1, -1, 4 ), // scroll info up
1028 ui_button_info( "2_MJ_05", 2, 713, -1, -1, 5 ), // scroll info down
1029 ui_button_info( "2_MJ_06", 783, 542, -1, -1, 6 ), // join as observer
1030 ui_button_info( "2_MJ_07", 861, 542, -1, -1, 7 ), // create game
1031 ui_button_info( "2_MJ_08", 933, 542, 588, 376, 8 ), // cancel
1032 ui_button_info( "2_MJ_09", 854, 681, -1, -1, 9 ), // help
1033 ui_button_info( "2_MJ_10", 854, 727, -1, -1, 10 ), // options
1034 ui_button_info( "2_MJ_11", 914, 681, 937, 668, 11 ), // join
1039 #define MULTI_JOIN_NUM_TEXT 13
1041 UI_XSTR Multi_join_text[GR_NUM_RESOLUTIONS][MULTI_JOIN_NUM_TEXT] = {
1043 {"Refresh", 1299, 65, 364, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_REFRESH].button},
1044 {"Join as", 1300, 476, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_JOIN_OBSERVER].button},
1045 {"Observer", 1301, 467, 385, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_JOIN_OBSERVER].button},
1046 {"Create", 1408, 535, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_START_GAME].button},
1047 {"Game", 1302, 541, 385, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_START_GAME].button},
1048 {"Cancel", 387, 588, 376, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[0][MJ_CANCEL].button},
1049 {"Help", 928, 479, 436, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_HELP].button},
1050 {"Options", 1036, 479, 460, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_OPTIONS].button},
1051 {"Join", 1303, 589, 416, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[0][MJ_ACCEPT].button},
1052 {"Status", 1304, 37, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
1053 {"Server", 1305, 116, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
1054 {"Players", 1306, 471, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
1055 {"Ping", 1307, 555, 37, UI_XSTR_COLOR_GREEN, -1, NULL}
1058 {"Refresh", 1299, 104, 582, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_REFRESH].button},
1059 {"Join as", 1300, 783, 602, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_JOIN_OBSERVER].button},
1060 {"Observer", 1301, 774, 611, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_JOIN_OBSERVER].button},
1061 {"Create", 1408, 868, 602, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_START_GAME].button},
1062 {"Game", 1302, 872, 611, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_START_GAME].button},
1063 {"Cancel", 387, 941, 602, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[1][MJ_CANCEL].button},
1064 {"Help", 928, 782, 699, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_HELP].button},
1065 {"Options", 1036, 782, 736, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_OPTIONS].button},
1066 {"Join", 1303, 937, 668, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[1][MJ_ACCEPT].button},
1067 {"Status", 1304, 60, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
1068 {"Server", 1305, 186, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
1069 {"Players", 1306, 753, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
1070 {"Ping", 1307, 888, 60, UI_XSTR_COLOR_GREEN, -1, NULL}
1075 // constants for coordinate look ups
1076 #define MJ_X_COORD 0
1077 #define MJ_Y_COORD 1
1078 #define MJ_W_COORD 2
1079 #define MJ_H_COORD 3
1081 #define MULTI_JOIN_SENT_WAIT 10000 // wait this long since a join was sent to allow another
1082 int Multi_join_sent_stamp;
1084 // game information text areas
1085 int Mj_max_game_items[GR_NUM_RESOLUTIONS] = {
1090 int Mj_list_y[GR_NUM_RESOLUTIONS] = {
1095 int Mj_status_coords[GR_NUM_RESOLUTIONS][4] = {
1104 int Mj_game_icon_coords[GR_NUM_RESOLUTIONS][3] = {
1113 int Mj_speed_coords[GR_NUM_RESOLUTIONS][4] = {
1122 int Mj_game_name_coords[GR_NUM_RESOLUTIONS][4] = {
1131 int Mj_players_coords[GR_NUM_RESOLUTIONS][4] = {
1140 int Mj_ping_coords[GR_NUM_RESOLUTIONS][4] = {
1149 // game speed labels
1150 #define MJ_NUM_SPEED_LABELS 5
1151 const char *Multi_join_speed_labels[MJ_NUM_SPEED_LABELS] = {
1158 color *Multi_join_speed_colors[MJ_NUM_SPEED_LABELS] = {
1161 &Color_bright_green,
1162 &Color_bright_green,
1166 int Mj_cd_coords[GR_NUM_RESOLUTIONS] = {
1171 // extents of the entire boundable game info region
1172 // NOTE : these numbers are completely empirical
1173 #define MJ_PING_GREEN 160
1174 #define MJ_PING_YELLOW 300
1176 int Mj_list_area_coords[GR_NUM_RESOLUTIONS][4] = {
1185 // PXO channel filter
1186 #define MJ_PXO_FILTER_Y 0
1188 // special chars to indicate various status modes for servers
1189 #define MJ_CHAR_STANDALONE "*"
1190 #define MJ_CHAR_CAMPAIGN "c"
1193 // various interface indices
1194 int Multi_join_list_start; // where to start displaying from
1195 active_game *Multi_join_list_start_item; // a pointer to the corresponding active_game
1196 int Multi_join_list_selected; // which item we have selected
1197 active_game *Multi_join_selected_item; // a pointer to the corresponding active_game
1199 // use this macro to modify the list start
1200 #define MJ_LIST_START_INC() do { Multi_join_list_start++; } while(0);
1201 #define MJ_LIST_START_DEC() do { Multi_join_list_start--; } while(0);
1202 #define MJ_LIST_START_SET(vl) do { Multi_join_list_start = vl; } while(0);
1204 // if we should be sending a join request at the end of the frame
1205 int Multi_join_should_send = -1;
1207 // master tracker details
1208 int Multi_join_frame_count; // keep a count of frames displayed
1209 int Multi_join_mt_tried_verify; // already tried verifying the pilot with the tracker
1211 // data stuff for auto joining a game
1212 #define MULTI_AUTOJOIN_JOIN_STAMP 2000
1213 #define MULTI_AUTOJOIN_QUERY_STAMP 2000
1215 int Multi_did_autojoin;
1216 net_addr Multi_autojoin_addr;
1217 int Multi_autojoin_join_stamp;
1218 int Multi_autojoin_query_stamp;
1221 join_request Multi_join_request;
1223 // LOCAL function definitions
1224 void multi_join_check_buttons();
1225 void multi_join_button_pressed(int n);
1226 void multi_join_display_games();
1227 void multi_join_blit_game_status(active_game *game, int y);
1228 void multi_join_load_tcp_addrs();
1229 void multi_join_do_netstuff();
1230 void multi_join_ping_all();
1231 void multi_join_process_select();
1232 void multi_join_list_scroll_up();
1233 void multi_join_list_scroll_down();
1234 void multi_join_list_page_up();
1235 void multi_join_list_page_down();
1236 active_game *multi_join_get_game(int n);
1237 void multi_join_cull_timeouts();
1238 void multi_join_handle_item_cull(active_game *item, int item_index);
1239 void multi_join_send_join_request(int as_observer);
1240 void multi_join_create_game();
1241 void multi_join_blit_top_stuff();
1242 int multi_join_maybe_warn();
1243 int multi_join_warn_pxo();
1244 void multi_join_blit_protocol();
1248 active_game ag, *newitem;;
1251 dc_get_arg(ARG_INT);
1252 for(idx=0; idx<Dc_arg_int; idx++){
1253 // stuff some fake info
1254 memset(&ag, 0, sizeof(active_game));
1255 sprintf(ag.name, "Game %d", idx);
1256 ag.version = MULTI_FS_SERVER_VERSION;
1257 ag.comp_version = MULTI_FS_SERVER_VERSION;
1258 ag.server_addr.addr[0] = (char)idx;
1259 ag.flags = (AG_FLAG_COOP | AG_FLAG_FORMING | AG_FLAG_STANDALONE);
1262 newitem = multi_update_active_games(&ag);
1264 // timestamp it so we get random timeouts
1265 if(newitem != NULL){
1266 // newitem->heard_from_timer = timestamp((int)frand_range(500.0f, 10000.0f));
1271 void multi_join_notify_new_game()
1274 // reset the # of items
1275 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);
1276 Multi_join_slider.force_currentItem(Multi_join_list_start);
1280 int multi_join_autojoin_do()
1282 // if we have an active game on the list, then return a positive value so that we
1283 // can join the game
1284 if ( Active_game_head && (Active_game_count > 0) ) {
1285 Multi_join_selected_item = Active_game_head;
1289 // send out a server_query again
1290 if ( timestamp_elapsed(Multi_autojoin_query_stamp) ) {
1291 send_server_query(&Multi_autojoin_addr);
1292 Multi_autojoin_query_stamp = timestamp(MULTI_AUTOJOIN_QUERY_STAMP);
1298 void multi_join_game_init()
1302 // do the multiplayer init stuff - multi_level_init() now does all net_player zeroing.
1303 // setup various multiplayer things
1304 Assert( Game_mode & GM_MULTIPLAYER );
1305 Assert( Net_player != NULL );
1307 switch (Multi_options_g.protocol) {
1309 ADDRESS_LENGTH = IPX_ADDRESS_LENGTH;
1310 PORT_LENGTH = IPX_PORT_LENGTH;
1314 ADDRESS_LENGTH = IP_ADDRESS_LENGTH;
1315 PORT_LENGTH = IP_PORT_LENGTH;
1324 memset( &Netgame, 0, sizeof(Netgame) );
1327 Net_player->flags |= NETINFO_FLAG_DO_NETWORKING;
1328 Net_player->player = Player;
1329 memcpy(&Net_player->p_info.addr,&Psnet_my_addr,sizeof(net_addr));
1331 // check for the existence of a CD
1332 multi_common_verify_cd();
1334 // load my local netplayer options
1335 multi_options_local_load(&Net_player->p_info.options, Net_player);
1341 common_set_interface_palette(MULTI_JOIN_PALETTE);
1344 // destroy any chatbox contents which previously existed (from another game)
1347 // create the interface window
1348 Multi_join_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
1349 Multi_join_window.set_mask_bmap(Multi_join_bitmap_mask_fname[gr_screen.res]);
1351 // load the background bitmap
1352 Multi_join_bitmap = bm_load(Multi_join_bitmap_fname[gr_screen.res]);
1353 if(Multi_join_bitmap < 0){
1354 // we failed to load the bitmap - this is very bad
1358 // intialize the endgame system
1359 multi_endgame_init();
1361 // initialize the common notification messaging
1362 multi_common_notify_init();
1364 // initialize the common text area
1365 multi_common_set_text("");
1367 // load and use the common interface palette
1368 multi_common_load_palette();
1369 multi_common_set_palette();
1371 // load the help overlay
1372 help_overlay_load(MULTI_JOIN_OVERLAY);
1373 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1375 // do TCP and VMT specific initialization
1376 if(Multi_options_g.protocol == NET_TCP){
1377 // if this is a TCP (non tracker) game, we'll load up our default address list right now
1378 multi_join_load_tcp_addrs();
1381 // initialize any and all timestamps
1382 Multi_join_glr_stamp = -1;
1383 Multi_join_ping_stamp = -1;
1384 Multi_join_sent_stamp = -1;
1386 // reset frame count
1387 Multi_join_frame_count = 0;
1389 // haven't tried to verify on the tracker yet.
1390 Multi_join_mt_tried_verify = 0;
1392 // clear our all game lists to save hassles
1393 multi_join_clear_game_list();
1395 // create the interface buttons
1396 for(idx=0; idx<MULTI_JOIN_NUM_BUTTONS; idx++){
1397 // create the object
1398 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);
1400 // set the sound to play when highlighted
1401 Multi_join_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
1403 // set the ani for the button
1404 Multi_join_buttons[gr_screen.res][idx].button.set_bmaps(Multi_join_buttons[gr_screen.res][idx].filename);
1407 Multi_join_buttons[gr_screen.res][idx].button.link_hotspot(Multi_join_buttons[gr_screen.res][idx].hotspot);
1412 for(idx=0; idx<MULTI_JOIN_NUM_TEXT; idx++){
1413 Multi_join_window.add_XSTR(&Multi_join_text[gr_screen.res][idx]);
1417 Multi_join_should_send = -1;
1419 // close any previously open chatbox
1422 // create the list item select button
1423 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);
1424 Multi_join_select_button.hide();
1428 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);
1431 // if starting a network game, then go to the create game screen
1432 if ( Cmdline_start_netgame ) {
1433 multi_join_create_game();
1434 } else if ( Cmdline_connect_addr != NULL ) {
1439 // joining a game. Send a join request to the given IP address, and wait for the return.
1440 memset( &Multi_autojoin_addr, 0, sizeof(net_addr) );
1441 Multi_autojoin_addr.type = NET_TCP;
1443 // create the address, looking out for port number at the end
1444 port_num = DEFAULT_GAME_PORT;
1445 p = strrchr(Cmdline_connect_addr, ':');
1449 port_num = (short)atoi(p);
1451 ip_addr = inet_addr(Cmdline_connect_addr);
1452 memcpy(Multi_autojoin_addr.addr, &ip_addr, 4);
1453 Multi_autojoin_addr.port = port_num;
1455 send_server_query(&Multi_autojoin_addr);
1456 Multi_autojoin_query_stamp = timestamp(MULTI_AUTOJOIN_QUERY_STAMP);
1457 Multi_did_autojoin = 0;
1461 void multi_join_clear_game_list()
1464 Multi_join_list_selected = -1;
1465 Multi_join_selected_item = NULL;
1466 MJ_LIST_START_SET(-1);
1467 Multi_join_list_start_item = NULL;
1469 // free up the active game list
1470 multi_free_active_games();
1472 // initialize the active game list
1473 Active_game_head = NULL;
1474 Active_game_count = 0;
1477 void multi_join_game_do_frame()
1479 // check the status of our reliable socket. If not valid, popup error and return to main menu
1480 // I put this code here to avoid nasty gameseq issues with states. Also, we will have nice
1481 // background for the popup
1482 if ( !psnet_rel_check() ) {
1483 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));
1484 gameseq_post_event(GS_EVENT_MAIN_MENU);
1488 // return here since we will be moving to the next stage anyway -- I don't want to see the backgrounds of
1489 // all the screens for < 1 second for every screen we automatically move to.
1490 if ( Cmdline_start_netgame ) {
1494 // when joining a network game, wait for the server query to come back, and then join the game
1495 if ( Cmdline_connect_addr != NULL ) {
1498 if ( !Multi_did_autojoin ) {
1499 rval = popup_till_condition(multi_join_autojoin_do, XSTR("&Cancel", 779), XSTR("Joining netgame", 1500) );
1501 // cancel was hit. Send the user back to the main hall
1502 gameseq_post_event(GS_EVENT_MAIN_MENU);
1503 Cmdline_connect_addr = NULL; // reset this value.
1506 // when we get here, we have the data -- join the game.
1507 multi_join_send_join_request(0);
1508 Multi_autojoin_join_stamp = timestamp(MULTI_AUTOJOIN_JOIN_STAMP);
1509 Multi_did_autojoin = 1;
1512 if ( timestamp_elapsed(Multi_autojoin_join_stamp) ) {
1513 multi_join_send_join_request(0);
1514 Multi_autojoin_join_stamp = timestamp(MULTI_AUTOJOIN_JOIN_STAMP);
1520 // reset the should send var
1521 Multi_join_should_send = -1;
1523 int k = Multi_join_window.process();
1525 // process any keypresses
1528 if(help_overlay_active(MULTI_JOIN_OVERLAY)){
1529 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1531 gameseq_post_event(GS_EVENT_MAIN_MENU);
1532 gamesnd_play_iface(SND_USER_SELECT);
1536 // page up the game list
1538 multi_join_list_page_up();
1540 Multi_join_slider.force_currentItem(Multi_join_list_start);
1545 multi_pinfo_popup(Net_player);
1548 // page down the game list
1550 multi_join_list_page_down();
1552 Multi_join_slider.force_currentItem(Multi_join_list_start);
1556 // send out a ping-all
1558 multi_join_ping_all();
1559 Multi_join_ping_stamp = timestamp(MULTI_JOIN_PING_TIME);
1562 // shortcut to start a game
1564 multi_join_create_game();
1567 // scroll the game list up
1569 multi_join_list_scroll_up();
1571 Multi_join_slider.force_currentItem(Multi_join_list_start);
1575 // scroll the game list down
1577 multi_join_list_scroll_down();
1579 Multi_join_slider.force_currentItem(Multi_join_list_start);
1584 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
1585 help_overlay_set_state(MULTI_JOIN_OVERLAY, 0);
1588 // do any network related stuff
1589 multi_join_do_netstuff();
1591 // process any button clicks
1592 multi_join_check_buttons();
1594 // process any list selection stuff
1595 multi_join_process_select();
1597 // draw the background, etc
1599 GR_MAYBE_CLEAR_RES(Multi_join_bitmap);
1600 if(Multi_join_bitmap != -1){
1601 gr_set_bitmap(Multi_join_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1604 Multi_join_window.draw();
1606 // display the active games
1607 multi_join_display_games();
1609 // display any text in the info area
1610 multi_common_render_text();
1612 // display any pending notification messages
1613 multi_common_notify_do();
1615 // blit the CD icon and any PXO filter stuff
1616 multi_join_blit_top_stuff();
1618 // draw the help overlay
1619 help_overlay_maybe_blit(MULTI_JOIN_OVERLAY);
1624 // if we are supposed to be sending a join request
1625 if(Multi_join_should_send != -1){
1626 multi_join_send_join_request(Multi_join_should_send);
1628 Multi_join_should_send = -1;
1630 // increment the frame count
1631 Multi_join_frame_count++;
1634 void multi_join_game_close()
1636 // unload any bitmaps
1637 if(!bm_unload(Multi_join_bitmap)){
1638 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_join_bitmap_fname[gr_screen.res]));
1641 // unload the help overlay
1642 help_overlay_unload(MULTI_JOIN_OVERLAY);
1644 // free up the active game list
1645 multi_free_active_games();
1647 // destroy the UI_WINDOW
1648 Multi_join_window.destroy();
1651 common_free_interface_palette();
1655 void multi_join_check_buttons()
1658 for(idx=0;idx<MULTI_JOIN_NUM_BUTTONS;idx++){
1659 // we only really need to check for one button pressed at a time, so we can break after
1661 if(Multi_join_buttons[gr_screen.res][idx].button.pressed()){
1662 multi_join_button_pressed(idx);
1668 void multi_join_button_pressed(int n)
1672 // if we're player PXO, go back there
1673 gameseq_post_event(GS_EVENT_MAIN_MENU);
1674 gamesnd_play_iface(SND_USER_SELECT);
1677 if(Active_game_count <= 0){
1678 multi_common_add_notify(XSTR("No games found!",757));
1679 gamesnd_play_iface(SND_GENERAL_FAIL);
1680 } else if(Multi_join_list_selected == -1){
1681 multi_common_add_notify(XSTR("No game selected!",758));
1682 gamesnd_play_iface(SND_GENERAL_FAIL);
1683 } else if((Multi_join_sent_stamp != -1) && !timestamp_elapsed(Multi_join_sent_stamp)){
1684 multi_common_add_notify(XSTR("Still waiting on previous join request!",759));
1685 gamesnd_play_iface(SND_GENERAL_FAIL);
1687 // otherwise, if he's already played PXO games, warn him
1689 if(Player->flags & PLAYER_FLAGS_HAS_PLAYED_PXO){
1690 if(!multi_join_warn_pxo()){
1696 // send the join request here
1697 Assert(Multi_join_selected_item != NULL);
1699 // send a join request packet
1700 Multi_join_should_send = 0;
1702 gamesnd_play_iface(SND_COMMIT_PRESSED);
1708 if(!help_overlay_active(MULTI_JOIN_OVERLAY)){
1709 help_overlay_set_state(MULTI_JOIN_OVERLAY,1);
1711 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1715 // scroll the game list up
1717 multi_join_list_scroll_up();
1719 Multi_join_slider.force_currentItem(Multi_join_list_start);
1723 // scroll the game list down
1724 case MJ_SCROLL_DOWN:
1725 multi_join_list_scroll_down();
1727 Multi_join_slider.force_currentItem(Multi_join_list_start);
1731 // scroll the info text box up
1732 case MJ_SCROLL_INFO_UP:
1733 multi_common_scroll_text_up();
1736 // scroll the info text box down
1737 case MJ_SCROLL_INFO_DOWN:
1738 multi_common_scroll_text_down();
1741 // go to the options screen
1743 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
1746 // go to the start game screen
1748 multi_join_create_game();
1751 // refresh the game/server list
1753 gamesnd_play_iface(SND_USER_SELECT);
1754 broadcast_game_query();
1757 // join a game as an observer
1758 case MJ_JOIN_OBSERVER:
1759 if(Active_game_count <= 0){
1760 multi_common_add_notify(XSTR("No games found!",757));
1761 gamesnd_play_iface(SND_GENERAL_FAIL);
1762 } else if(Multi_join_list_selected == -1){
1763 multi_common_add_notify(XSTR("No game selected!",758));
1764 gamesnd_play_iface(SND_GENERAL_FAIL);
1765 } else if((Multi_join_sent_stamp != -1) && !timestamp_elapsed(Multi_join_sent_stamp)){
1766 multi_common_add_notify(XSTR("Still waiting on previous join request!",759));
1767 gamesnd_play_iface(SND_GENERAL_FAIL);
1769 // send the join request here
1770 Assert(Multi_join_selected_item != NULL);
1772 Multi_join_should_send = 1;
1774 gamesnd_play_iface(SND_COMMIT_PRESSED);
1779 multi_common_add_notify(XSTR("Not implemented yet!",760));
1780 gamesnd_play_iface(SND_GENERAL_FAIL);
1785 // display all relevant info for active games
1786 void multi_join_display_games()
1788 active_game *moveup = Multi_join_list_start_item;
1792 int y_start = Mj_list_y[gr_screen.res];
1797 // blit the game status (including text and type icon)
1798 multi_join_blit_game_status(moveup,y_start);
1800 // get the connection type
1801 con_type = (moveup->flags & AG_FLAG_CONNECTION_SPEED_MASK) >> AG_FLAG_CONNECTION_BIT;
1802 if((con_type > 4) || (con_type < 0)){
1806 // display the connection speed
1808 strcpy(str, Multi_join_speed_labels[con_type]);
1809 gr_set_color_fast(Multi_join_speed_colors[con_type]);
1810 gr_string(Mj_speed_coords[gr_screen.res][MJ_X_COORD], y_start, str);
1812 // we'll want to have different colors for highlighted items, etc.
1813 if(moveup == Multi_join_selected_item){
1814 gr_set_color_fast(&Color_text_selected);
1816 gr_set_color_fast(&Color_text_normal);
1819 // display the game name, adding appropriate status chars
1821 if(moveup->flags & AG_FLAG_STANDALONE){
1822 strcat(str,MJ_CHAR_STANDALONE);
1824 if(moveup->flags & AG_FLAG_CAMPAIGN){
1825 strcat(str,MJ_CHAR_CAMPAIGN);
1828 // tack on the actual server name
1830 strcat(str,moveup->name);
1831 if(strlen(moveup->mission_name) > 0){
1833 strcat(str,moveup->mission_name);
1836 // make sure the string fits in the display area and draw it
1837 gr_force_fit_string(str,200,Mj_game_name_coords[gr_screen.res][MJ_W_COORD]);
1838 gr_string(Mj_game_name_coords[gr_screen.res][MJ_X_COORD],y_start,str);
1840 // display the ping time
1841 if(moveup->ping.ping_avg > 0){
1842 if(moveup->ping.ping_avg > 1000){
1843 gr_set_color_fast(&Color_bright_red);
1844 strcpy(str,XSTR("> 1 sec",761));
1846 // set the appropriate ping time color indicator
1847 if(moveup->ping.ping_avg > MJ_PING_YELLOW){
1848 gr_set_color_fast(&Color_bright_red);
1849 } else if(moveup->ping.ping_avg > MJ_PING_YELLOW){
1850 gr_set_color_fast(&Color_bright_yellow);
1852 gr_set_color_fast(&Color_bright_green);
1855 sprintf(str,"%d",moveup->ping.ping_avg);
1856 strcat(str,XSTR(" ms",762)); // [[ Milliseconds ]]
1859 gr_string(Mj_ping_coords[gr_screen.res][MJ_X_COORD],y_start,str);
1862 // display the number of players (be sure to center it)
1863 if(moveup == Multi_join_selected_item){
1864 gr_set_color_fast(&Color_text_selected);
1866 gr_set_color_fast(&Color_text_normal);
1868 sprintf(str,"%d",moveup->num_players);
1869 gr_get_string_size(&w,&h,str);
1870 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);
1874 moveup = moveup->next;
1875 } while((moveup != Active_game_head) && (count < Mj_max_game_items[gr_screen.res]));
1877 // if there are no items on the list, display this info
1879 gr_set_color_fast(&Color_bright);
1880 gr_string(Mj_game_name_coords[gr_screen.res][MJ_X_COORD] - 30,y_start,XSTR("<No game servers found>",763));
1884 void multi_join_blit_game_status(active_game *game, int y)
1887 char status_text[25];
1889 // blit the proper icon
1891 switch( game->flags & AG_FLAG_TYPE_MASK ){
1894 if(Multi_common_icons[MICON_COOP] != -1){
1895 gr_set_bitmap(Multi_common_icons[MICON_COOP], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1900 // team vs. team game
1902 if(Multi_common_icons[MICON_TVT] != -1){
1903 gr_set_bitmap(Multi_common_icons[MICON_TVT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1910 case AG_FLAG_DOGFIGHT:
1911 if(Multi_common_icons[MICON_DOGFIGHT] != -1){
1912 gr_set_bitmap(Multi_common_icons[MICON_DOGFIGHT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1918 // if we're supposed to draw a bitmap
1920 gr_bitmap(Mj_game_icon_coords[gr_screen.res][MJ_X_COORD],y-1);
1923 // blit the proper status text
1924 memset(status_text,0,25);
1926 switch( game->flags & AG_FLAG_STATE_MASK ){
1927 case AG_FLAG_FORMING:
1928 gr_set_color_fast(&Color_bright_green);
1929 strcpy(status_text,XSTR("Forming",764));
1931 case AG_FLAG_BRIEFING:
1932 gr_set_color_fast(&Color_bright_red);
1933 strcpy(status_text,XSTR("Briefing",765));
1935 case AG_FLAG_DEBRIEF:
1936 gr_set_color_fast(&Color_bright_red);
1937 strcpy(status_text,XSTR("Debrief",766));
1940 gr_set_color_fast(&Color_bright_red);
1941 strcpy(status_text,XSTR("Paused",767));
1943 case AG_FLAG_IN_MISSION:
1944 gr_set_color_fast(&Color_bright_red);
1945 strcpy(status_text,XSTR("Playing",768));
1948 gr_set_color_fast(&Color_bright);
1949 strcpy(status_text,XSTR("Unknown",769));
1952 gr_get_string_size(&str_w,NULL,status_text);
1953 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);
1956 // load in a list of active games from our tcp.cfg file
1957 void multi_join_load_tcp_addrs()
1959 char line[MAX_IP_STRING];
1964 // attempt to open the ip list file
1965 file = cfopen(IP_CONFIG_FNAME,"rt",CFILE_NORMAL,CF_TYPE_DATA);
1967 nprintf(("Network","Error loading tcp.cfg file!\n"));
1971 // free up any existing server list
1972 multi_free_server_list();
1974 // read in all the strings in the file
1975 while(!cfeof(file)){
1977 cfgets(line,MAX_IP_STRING,file);
1979 // strip off any newline character
1980 if(line[strlen(line) - 1] == '\n'){
1981 line[strlen(line) - 1] = '\0';
1984 // empty lines don't get processed
1985 if( (line[0] == '\0') || (line[0] == '\n') ){
1989 if ( !psnet_is_valid_ip_string(line) ) {
1990 nprintf(("Network","Invalid ip string (%s)\n",line));
1992 // copy the server ip address
1993 memset(&addr,0,sizeof(net_addr));
1994 addr.type = NET_TCP;
1995 psnet_string_to_addr(&addr,line);
1996 if ( addr.port == 0 ){
1997 addr.port = DEFAULT_GAME_PORT;
2000 // create a new server item on the list
2001 item = multi_new_server_item();
2003 memcpy(&item->server_addr,&addr,sizeof(net_addr));
2011 // do stuff like pinging servers, sending out requests, etc
2012 void multi_join_do_netstuff()
2014 // handle game query stuff
2015 if(Multi_join_glr_stamp == -1){
2016 broadcast_game_query();
2018 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
2019 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME_LOCAL);
2021 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME);
2024 // otherwise send out game query and restamp
2025 else if(timestamp_elapsed(Multi_join_glr_stamp)){
2026 broadcast_game_query();
2028 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
2029 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME_LOCAL);
2031 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME);
2035 // check to see if we've been accepted. If so, put up message saying so
2036 if ( Net_player->flags & (NETINFO_FLAG_ACCEPT_INGAME|NETINFO_FLAG_ACCEPT_CLIENT|NETINFO_FLAG_ACCEPT_HOST|NETINFO_FLAG_ACCEPT_OBSERVER) ) {
2037 multi_common_add_notify(XSTR("Accepted. Waiting for player data.",770));
2040 // check to see if any join packets we have sent have timed out
2041 if((Multi_join_sent_stamp != -1) && (timestamp_elapsed(Multi_join_sent_stamp))){
2042 Multi_join_sent_stamp = -1;
2043 multi_common_add_notify(XSTR("Join request timed out!",771));
2046 // check to see if we should be pinging everyone
2047 if((Multi_join_ping_stamp == -1) || (timestamp_elapsed(Multi_join_ping_stamp))){
2048 multi_join_ping_all();
2049 Multi_join_ping_stamp = timestamp(MULTI_JOIN_PING_TIME);
2053 multi_join_cull_timeouts();
2056 // evaluate a returned pong.
2057 void multi_join_eval_pong(net_addr *addr, fix pong_time)
2060 active_game *moveup = Active_game_head;
2065 if(psnet_same(&moveup->server_addr,addr)){
2067 multi_ping_eval_pong(&moveup->ping);
2071 moveup = moveup->next;
2073 } while(moveup != Active_game_head);
2076 // update the game's ping
2078 if(found && (moveup->ping_end > moveup->ping_start)){
2079 moveup->ping_time = f2fl(moveup->ping_end - moveup->ping_start);
2080 moveup->ping_start = -1;
2081 moveup->ping_end = -1;
2086 // ping all the server on the list
2087 void multi_join_ping_all()
2089 active_game *moveup = Active_game_head;
2094 moveup->ping_start = timer_get_fixed_seconds();
2095 moveup->ping_end = -1;
2096 send_ping(&moveup->server_addr);
2098 multi_ping_send(&moveup->server_addr,&moveup->ping);
2100 moveup = moveup->next;
2101 } while(moveup != Active_game_head);
2105 void multi_join_process_select()
2107 // if we don't have anything selected and there are items on the list - select the first one
2108 if((Multi_join_list_selected == -1) && (Active_game_count > 0)){
2109 Multi_join_list_selected = 0;
2110 Multi_join_selected_item = multi_join_get_game(0);
2111 MJ_LIST_START_SET(0);
2112 Multi_join_list_start_item = Multi_join_selected_item;
2114 // send a mission description request to this guy
2115 send_netgame_descript_packet(&Multi_join_selected_item->server_addr,0);
2116 multi_common_set_text("");
2118 // I sure hope this doesn't happen
2119 Assert(Multi_join_selected_item != NULL);
2122 // otherwise see if he's clicked on an item
2123 else if(Multi_join_select_button.pressed() && (Active_game_count > 0)){
2125 Multi_join_select_button.get_mouse_pos(NULL,&y);
2127 if(item + Multi_join_list_start < Active_game_count){
2128 gamesnd_play_iface(SND_IFACE_MOUSE_CLICK);
2130 Multi_join_list_selected = item + Multi_join_list_start;
2131 Multi_join_selected_item = multi_join_get_game(Multi_join_list_selected);
2133 // I sure hope this doesn't happen
2134 Assert(Multi_join_selected_item != NULL);
2136 // send a mission description request to this guy
2137 send_netgame_descript_packet(&Multi_join_selected_item->server_addr,0);
2138 multi_common_set_text("");
2142 // if he's double clicked, then select it and accept
2143 if(Multi_join_select_button.double_clicked()){
2145 Multi_join_select_button.get_mouse_pos(NULL,&y);
2147 if(item == Multi_join_list_selected){
2148 multi_join_button_pressed(MJ_ACCEPT);
2153 // return game n (0 based index)
2154 active_game *multi_join_get_game(int n)
2156 active_game *moveup = Active_game_head;
2163 moveup = moveup->next;
2164 while((moveup != Active_game_head) && (count != n)){
2165 moveup = moveup->next;
2168 if(moveup == Active_game_head){
2169 nprintf(("Network","Warning, couldn't find game item %d!\n",n));
2179 // scroll through the game list
2180 void multi_join_list_scroll_up()
2182 // if we're not at the beginning of the list, scroll up
2183 if(Multi_join_list_start_item != Active_game_head){
2184 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2186 MJ_LIST_START_DEC();
2188 gamesnd_play_iface(SND_SCROLL);
2190 gamesnd_play_iface(SND_GENERAL_FAIL);
2194 // scroll through the game list
2195 void multi_join_list_scroll_down()
2197 if((Active_game_count - Multi_join_list_start) > Mj_max_game_items[gr_screen.res]){
2198 Multi_join_list_start_item = Multi_join_list_start_item->next;
2200 MJ_LIST_START_INC();
2202 gamesnd_play_iface(SND_SCROLL);
2204 gamesnd_play_iface(SND_GENERAL_FAIL);
2208 void multi_join_list_page_up()
2210 // in this case, just set us to the beginning of the list
2211 if((Multi_join_list_start - Mj_max_game_items[gr_screen.res]) < 0){
2212 Multi_join_list_start_item = Active_game_head;
2214 MJ_LIST_START_SET(0);
2216 gamesnd_play_iface(SND_SCROLL);
2218 // otherwise page the whole thing up
2220 for(idx=0; idx<Mj_max_game_items[gr_screen.res]; idx++){
2221 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2223 MJ_LIST_START_DEC();
2225 gamesnd_play_iface(SND_SCROLL);
2229 void multi_join_list_page_down()
2233 // page the whole thing down
2234 while((count < Mj_max_game_items[gr_screen.res]) && ((Active_game_count - Multi_join_list_start) > Mj_max_game_items[gr_screen.res])){
2235 Multi_join_list_start_item = Multi_join_list_start_item->next;
2236 MJ_LIST_START_INC();
2241 gamesnd_play_iface(SND_SCROLL);
2244 void multi_join_cull_timeouts()
2246 active_game *backup;
2248 active_game *moveup = Active_game_head;
2250 // traverse through the entire list if any items exist
2254 if((moveup->heard_from_timer != -1) && (timestamp_elapsed(moveup->heard_from_timer))){
2255 Active_game_count--;
2257 // if this is the head of the list
2258 if(moveup == Active_game_head){
2259 // if this is the _only_ item on the list
2260 if(moveup->next == Active_game_head){
2261 // handle any gui details related to deleting this item
2262 multi_join_handle_item_cull(Active_game_head, count);
2264 free(Active_game_head);
2265 Active_game_head = NULL;
2268 // if there are other items on the list
2270 // handle any gui details related to deleting this item
2271 multi_join_handle_item_cull(moveup, count);
2273 Active_game_head = moveup->next;
2274 Active_game_head->prev = moveup->prev;
2275 Active_game_head->prev->next = Active_game_head;
2277 moveup = Active_game_head;
2280 // if its somewhere else on the list
2282 // handle any gui details related to deleting this item
2283 multi_join_handle_item_cull(moveup, count);
2285 // if its the last item on the list
2286 moveup->next->prev = moveup->prev;
2287 moveup->prev->next = moveup->next;
2289 // if it was the last element on the list, return
2290 if(moveup->next == Active_game_head){
2294 backup = moveup->next;
2300 moveup = moveup->next;
2303 } while(moveup != Active_game_head);
2307 // deep magic begins here.
2308 void multi_join_handle_item_cull(active_game *item, int item_index)
2310 // if this is the only item on the list, unset everything
2311 if(item->next == item){
2312 Multi_join_list_selected = -1;
2313 Multi_join_selected_item = NULL;
2316 Multi_join_slider.set_numberItems(0);
2318 MJ_LIST_START_SET(-1);
2319 Multi_join_list_start_item = NULL;
2325 // see if we should be adjusting our currently selected item
2326 if(item_index <= Multi_join_list_selected){
2327 // the selected item is the head of the list
2328 if(Multi_join_selected_item == Active_game_head){
2329 // move the pointer up since this item is about to be destroyed
2330 Multi_join_selected_item = Multi_join_selected_item->next;
2332 // if this is the item being deleted, select the previous one
2333 if(item == Multi_join_selected_item){
2335 Multi_join_selected_item = Multi_join_selected_item->prev;
2337 // decrement the selected index by 1
2338 Multi_join_list_selected--;
2340 // now we know its a previous item, so our pointer stays the same but our index goes down by one, since there will be
2341 // 1 less item on the list
2343 // decrement the selected index by 1
2344 Multi_join_list_selected--;
2349 // see if we should be adjusting out current start position
2350 if(item_index <= Multi_join_list_start){
2351 // the start position is the head of the list
2352 if(Multi_join_list_start_item == Active_game_head){
2353 // move the pointer up since this item is about to be destroyed
2354 Multi_join_list_start_item = Multi_join_list_start_item->next;
2356 // if this is the item being deleted, select the previous one
2357 if(item == Multi_join_list_start_item){
2358 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2360 // decrement the starting index by 1
2361 MJ_LIST_START_DEC();
2363 // but decrement the starting index by 1
2364 MJ_LIST_START_DEC();
2369 // maybe go back up a bit so that we always have a full page of items
2370 if(Active_game_count > Mj_max_game_items[gr_screen.res]){
2371 while((Active_game_count - Multi_join_list_start) < Mj_max_game_items[gr_screen.res]){
2372 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2373 MJ_LIST_START_DEC();
2377 // set slider location
2379 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);
2380 Multi_join_slider.force_currentItem(Multi_join_list_start);
2384 void multi_join_send_join_request(int as_observer)
2386 // don't do anything if we have no items selected
2387 if(Multi_join_selected_item == NULL){
2391 // 5/26/98 -- for team v team games, don't allow ingame joining :-(
2392 if ( (Multi_join_selected_item->flags & AG_FLAG_TEAMS) && (Multi_join_selected_item->flags & (AG_FLAG_PAUSE|AG_FLAG_IN_MISSION)) ) {
2393 popup(0, 1, POPUP_OK, XSTR("Joining ingame is currently not allowed for team vs. team games",772));
2397 memset(&Multi_join_request,0,sizeof(join_request));
2399 // if the netgame is in password mode, put up a request for the password
2400 if(Multi_join_selected_item->flags & AG_FLAG_PASSWD){
2401 if(!multi_passwd_popup(Multi_join_request.passwd)){
2405 nprintf(("Password : %s\n",Multi_join_request.passwd));
2408 // fill out the join request struct
2409 strcpy(Multi_join_request.callsign,Player->callsign);
2410 if(strlen(Player->image_filename) > 0){
2411 strcpy(Multi_join_request.image_filename, Player->image_filename);
2414 if(strlen(Player->squad_filename) > 0){
2415 strcpy(Multi_join_request.squad_filename, Player->squad_filename);
2419 // tracker id (if any)
2420 Multi_join_request.tracker_id = Multi_tracker_id;
2422 // player's rank (at least, what he wants you to _believe_)
2423 Multi_join_request.player_rank = (ubyte)Player->stats.rank;
2426 Multi_join_request.flags = 0;
2428 Multi_join_request.flags |= JOIN_FLAG_AS_OBSERVER;
2431 // if the player has hacked data
2432 if(game_hacked_data()){
2433 Multi_join_request.flags |= JOIN_FLAG_HAXOR;
2438 strncpy(Multi_join_request.pxo_squad_name, Multi_tracker_squad_name, LOGIN_LEN);
2441 // version of this server
2442 Multi_join_request.version = MULTI_FS_SERVER_VERSION;
2444 // server compatible version
2445 Multi_join_request.comp_version = MULTI_FS_SERVER_COMPATIBLE_VERSION;
2447 // his local player options
2448 memcpy(&Multi_join_request.player_options,&Player->m_local_options,sizeof(multi_local_options));
2450 // set the server address for the netgame
2451 memcpy(&Netgame.server_addr,&Multi_join_selected_item->server_addr,sizeof(net_addr));
2453 // send a join request to the guy
2454 send_join_packet(&Multi_join_selected_item->server_addr,&Multi_join_request);
2457 Multi_join_sent_stamp = timestamp(MULTI_JOIN_SENT_WAIT);
2460 multi_common_add_notify(XSTR("Sending join request...",773));
2463 void multi_join_create_game()
2465 // maybe warn the player about possible crappy server conditions
2466 if(!multi_join_maybe_warn()){
2470 // make sure to flag ourself as being the master
2471 Net_player->flags |= (NETINFO_FLAG_AM_MASTER | NETINFO_FLAG_GAME_HOST);
2472 Net_player->state = NETPLAYER_STATE_HOST_SETUP;
2474 // if we're in PXO mode, mark it down in our player struct
2475 if(MULTI_IS_TRACKER_GAME){
2476 Player->flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
2477 Player->save_flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
2479 // otherwise, if he's already played PXO games, warn him
2482 if(Player->flags & PLAYER_FLAGS_HAS_PLAYED_PXO){
2483 if(!multi_join_warn_pxo()){
2490 gameseq_post_event(GS_EVENT_MULTI_START_GAME);
2491 gamesnd_play_iface(SND_USER_SELECT);
2494 void multi_join_reset_join_stamp()
2496 // unset the timestamp here so the user can immediately send another join request
2497 Multi_join_sent_stamp = -1;
2498 multi_common_add_notify("");
2501 void multi_join_blit_top_stuff()
2503 // blit the cd icon if he has one
2504 if(Multi_has_cd && (Multi_common_icons[MICON_CD] != -1)){
2507 bm_get_info(Multi_common_icons[MICON_CD], &cd_w, NULL, NULL, NULL, NULL);
2509 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
2510 gr_bitmap((gr_screen.max_w / 2) - (cd_w / 2), Mj_cd_coords[gr_screen.res]);
2514 #define CW_CODE_CANCEL 0 // cancel the action
2515 #define CW_CODE_OK 1 // continue anyway
2516 #define CW_CODE_INFO 2 // gimme some more information
2518 #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)
2519 #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)
2521 #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)
2522 #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)
2524 int multi_join_warn_update_low(int code)
2528 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),LOW_WARN_TEXT);
2531 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),LOW_INFO_TEXT);
2534 return CW_CODE_CANCEL;
2537 int multi_join_warn_update_medium(int code)
2541 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),MED_WARN_TEXT);
2544 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),MED_INFO_TEXT);
2547 return CW_CODE_CANCEL;
2550 int multi_join_maybe_warn()
2554 // if the player is set for low updates
2555 if(Player->m_local_options.obj_update_level == OBJ_UPDATE_LOW){
2558 code = multi_join_warn_update_low(code);
2559 } while((code != CW_CODE_CANCEL) && (code != CW_CODE_OK));
2564 // if the player is set for medium updates
2565 else if(Player->m_local_options.obj_update_level == OBJ_UPDATE_MEDIUM){
2568 code = multi_join_warn_update_medium(code);
2569 } while((code != CW_CODE_CANCEL) && (code != CW_CODE_OK));
2577 int multi_join_warn_pxo()
2579 // 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;
2583 void multi_join_blit_protocol()
2585 gr_set_color_fast(&Color_bright);
2587 switch(Socket_type){
2590 gr_string(5, 2, "TCP");
2594 gr_string(5, 2, "IPX");
2600 // -------------------------------------------------------------------------------------------------
2602 // MULTIPLAYER START GAME screen
2607 #define MULTI_SG_PALETTE "InterfacePalette"
2609 static const char *Multi_sg_bitmap_fname[GR_NUM_RESOLUTIONS] = {
2610 "MultiStartGame", // GR_640
2611 "2_MultiStartGame" // GR_1024
2614 static const char *Multi_sg_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
2615 "MultiStartGame-M", // GR_640
2616 "2_MultiStartGame-M" // GR_1024
2621 int Multi_sg_rank_max_display[GR_NUM_RESOLUTIONS] = {
2626 // constants for coordinate look ups
2627 #define MSG_X_COORD 0
2628 #define MSG_Y_COORD 1
2629 #define MSG_W_COORD 2
2630 #define MSG_H_COORD 3
2634 // input password field
2635 int Msg_passwd_coords[GR_NUM_RESOLUTIONS][4] = {
2648 // input game title field
2649 int Msg_title_coords[GR_NUM_RESOLUTIONS][4] = {
2662 // rank selected field
2663 int Msg_rank_sel_coords[GR_NUM_RESOLUTIONS][4] = {
2677 int Msg_rank_list_coords[GR_NUM_RESOLUTIONS][4] = {
2693 #define MULTI_SG_NUM_BUTTONS 12
2694 #define MSG_OPEN_GAME 0
2695 #define MSG_CLOSED_GAME 1
2696 #define MSG_PASSWD_GAME 2
2697 #define MSG_RESTRICTED_GAME 3
2698 #define MSG_RANK_SET_GAME 4
2699 #define MSG_RANK_SCROLL_UP 5
2700 #define MSG_RANK_SCROLL_DOWN 6
2701 #define MSG_RANK_ABOVE 7
2702 #define MSG_RANK_BELOW 8
2704 #define MSG_OPTIONS 10
2705 #define MSG_ACCEPT 11
2707 #define MULTI_SG_NUM_BUTTONS 10
2708 #define MSG_OPEN_GAME 0
2709 //#define MSG_CLOSED_GAME 1
2710 //#define MSG_RESTRICTED_GAME 2
2711 #define MSG_PASSWD_GAME 1
2712 #define MSG_RANK_SET_GAME 2
2713 #define MSG_RANK_SCROLL_UP 3
2714 #define MSG_RANK_SCROLL_DOWN 4
2715 #define MSG_RANK_ABOVE 5
2716 #define MSG_RANK_BELOW 6
2718 #define MSG_OPTIONS 8
2719 #define MSG_ACCEPT 9
2722 UI_WINDOW Multi_sg_window; // the window object for the join screen
2723 UI_BUTTON Multi_sg_rank_button; // for selecting the rank marker
2724 UI_INPUTBOX Multi_sg_game_name; // for Netgame.name
2725 UI_INPUTBOX Multi_sg_game_passwd; // for Netgame.passwd
2726 int Multi_sg_bitmap; // the background bitmap
2728 ui_button_info Multi_sg_buttons[GR_NUM_RESOLUTIONS][MULTI_SG_NUM_BUTTONS] = {
2731 ui_button_info("MSG_00", 75, 111, -1, -1, 0),
2732 ui_button_info("MSG_01", 75, 139, -1, -1, 1),
2733 ui_button_info("MSG_02", 75, 164, -1, -1, 2),
2734 ui_button_info("MSG_03", 75, 199, -1, -1, 3),
2735 ui_button_info("MSG_04", 75, 243, -1, -1, 4),
2736 ui_button_info("MSG_05", 376, 231, -1, -1, 5),
2737 ui_button_info("MSG_06", 376, 258, -1, -1, 6),
2738 ui_button_info("MSG_07", 376, 291, -1, -1, 7),
2739 ui_button_info("MSG_08", 376, 320, -1, -1, 8),
2740 ui_button_info("MSG_09", 469, 427, -1, -1, 9),
2741 ui_button_info("MSG_10", 447, 452, -1, -1, 10),
2742 ui_button_info("MSG_11", 561, 411, -1, -1, 11),
2744 ui_button_info("MSG_00", 1, 184, 34, 191, 2), // open
2745 // ui_button_info("MSG_01", 1, 159, 34, 166, 1), // closed
2746 // ui_button_info("MSG_02", 1, 184, 34, 191, 2), // restricted
2747 ui_button_info("MSG_03", 1, 209, 34, 218, 3), // password
2748 ui_button_info("MSG_04", 1, 257, 34, 266, 4), // rank set
2749 ui_button_info("MSG_05", 1, 282, -1, -1, 5), // rank scroll up
2750 ui_button_info("MSG_06", 1, 307, -1, -1, 6), // rank scroll down
2751 ui_button_info("MSG_07", 177, 282, 210, 290, 7), // rank above
2752 ui_button_info("MSG_08", 177, 307, 210, 315, 8), // rank below
2753 ui_button_info("MSG_09", 536, 429, 500, 440, 9), // help
2754 ui_button_info("MSG_10", 536, 454, 479, 464, 10), // options
2755 ui_button_info("MSG_11", 576, 432, 571, 415, 11), // accept
2759 ui_button_info("2_MSG_00", 2, 295, 51, 307, 2), // open
2760 // ui_button_info("2_MSG_01", 2, 254, 51, 267, 1), // closed
2761 // ui_button_info("2_MSG_02", 2, 295, 51, 307, 2), // restricted
2762 ui_button_info("2_MSG_03", 2, 335, 51, 350, 3), // password
2763 ui_button_info("2_MSG_04", 2, 412, 51, 426, 4), // rank set
2764 ui_button_info("2_MSG_05", 2, 452, -1, -1, 5), // rank scroll up
2765 ui_button_info("2_MSG_06", 2, 492, -1, -1, 6), // rank scroll down
2766 ui_button_info("2_MSG_07", 284, 452, 335, 465, 7), // rank above
2767 ui_button_info("2_MSG_08", 284, 492, 335, 505, 8), // rank below
2768 ui_button_info("2_MSG_09", 858, 687, 817, 706, 9), // help
2769 ui_button_info("2_MSG_10", 858, 728, 797, 743, 10), // options
2770 ui_button_info("2_MSG_11", 921, 692, 921, 664, 11), // accept
2771 #ifdef MAKE_FS1 // filler for extra FS1 buttons
2772 ui_button_info("none", -1, -1, -1, -1, -1),
2773 ui_button_info("none", -1, -1, -1, -1, -1),
2779 #define MULTI_SG_NUM_TEXT 11
2781 UI_XSTR Multi_sg_text[GR_NUM_RESOLUTIONS][MULTI_SG_NUM_TEXT] = {
2783 {"Open", 1322, 34, 191, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_OPEN_GAME].button},
2784 // {"Closed", 1323, 34, 166, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_CLOSED_GAME].button},
2785 // {"Restricted", 1324, 34, 191, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RESTRICTED_GAME].button},
2786 {"Password Protected", 1325, 34, 218, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_PASSWD_GAME].button},
2787 {"Allow Rank", 1326, 34, 266, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_SET_GAME].button},
2788 {"Above", 1327, 210, 290, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_ABOVE].button},
2789 {"Below", 1328, 210, 315, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_BELOW].button},
2790 {"Help", 928, 500, 440, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_HELP].button},
2791 {"Options", 1036, 479, 464, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_OPTIONS].button},
2792 {"Accept", 1035, 571, 415, UI_XSTR_COLOR_PINK, -1, &Multi_sg_buttons[0][MSG_ACCEPT].button},
2793 {"Start Game", 1329, 26, 10, UI_XSTR_COLOR_GREEN, -1, NULL},
2794 {"Title", 1330, 26, 31, UI_XSTR_COLOR_GREEN, -1, NULL},
2795 {"Game Type", 1331, 12, 165, UI_XSTR_COLOR_GREEN, -1, NULL},
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);
2904 for(idx=0; idx<MULTI_SG_NUM_TEXT; idx++){
2905 Multi_sg_window.add_XSTR(&Multi_sg_text[gr_screen.res][idx]);
2909 // load the help overlay
2910 help_overlay_load(MULTI_START_OVERLAY);
2911 help_overlay_set_state(MULTI_START_OVERLAY,0);
2913 // intiialize the rank selection items
2914 multi_sg_select_rank_default();
2915 Multi_sg_rank_start = Multi_sg_rank_select;
2917 // create the rank select button
2918 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);
2919 Multi_sg_rank_button.hide();
2921 // create the netgame name input box
2922 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);
2924 // create the netgame password input box, and disable it by default
2925 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);
2926 Multi_sg_game_passwd.hide();
2927 Multi_sg_game_passwd.disable();
2929 // set the netgame text to this gadget and make it have focus
2930 Multi_sg_game_name.set_text(Multi_sg_netgame->name);
2931 Multi_sg_game_name.set_focus();
2933 // if starting a netgame, set the name of the game and any other options that are appropriate
2934 if ( Cmdline_start_netgame ) {
2935 if ( Cmdline_game_name != NULL ) {
2936 strcpy( Multi_sg_netgame->name, Cmdline_game_name );
2937 Multi_sg_game_name.set_text(Multi_sg_netgame->name);
2940 // deal with the different game types -- only one should even be active, so we will just go down
2941 // the line. Last one wins.
2942 if ( Cmdline_closed_game ) {
2943 Multi_sg_netgame->mode = NG_MODE_CLOSED;
2944 } else if ( Cmdline_restricted_game ) {
2945 Multi_sg_netgame->mode = NG_MODE_RESTRICTED;
2946 } else if ( Cmdline_game_password != NULL ) {
2947 Multi_sg_netgame->mode = NG_MODE_PASSWORD;
2948 strcpy(Multi_sg_netgame->passwd, Cmdline_game_password);
2949 Multi_sg_game_passwd.set_text(Multi_sg_netgame->passwd);
2952 // deal with rank above and rank below
2953 if ( (Cmdline_rank_above != NULL) || (Cmdline_rank_below != NULL) ) {
2957 if ( Cmdline_rank_above != NULL ) {
2958 rank_str = Cmdline_rank_above;
2960 rank_str = Cmdline_rank_below;
2963 // try and get the rank index from the name -- if found, then set the rank base
2964 // and the game type. apparently we only support either above or below, not both
2965 // together, so I make a random choice
2966 rank = multi_start_game_rank_from_name( rank_str );
2968 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
2970 // now an arbitrary decision
2971 if ( Cmdline_rank_above != NULL ) {
2972 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
2974 Multi_sg_netgame->mode = NG_MODE_RANK_BELOW;
2979 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
2983 void multi_start_game_do()
2985 // return here since we will be moving to the next stage anyway -- I don't want to see the backgrounds of
2986 // all the screens for < 1 second for every screen we automatically move to.
2987 if ( Cmdline_start_netgame ) {
2991 int k = Multi_sg_window.process();
2993 // process any keypresses
2996 if(help_overlay_active(MULTI_START_OVERLAY)){
2997 help_overlay_set_state(MULTI_START_OVERLAY,0);
2999 gamesnd_play_iface(SND_USER_SELECT);
3000 multi_quit_game(PROMPT_NONE);
3005 case SDLK_LCTRL + SDLK_RETURN :
3006 case SDLK_RCTRL + SDLK_RETURN :
3007 gamesnd_play_iface(SND_COMMIT_PRESSED);
3008 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
3012 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
3013 help_overlay_set_state(MULTI_START_OVERLAY, 0);
3016 // check to see if the user has selected a different rank
3017 multi_sg_rank_process_select();
3019 // check any button presses
3020 multi_sg_check_buttons();
3022 // check to see if any of the input boxes have changed, and update the appropriate Netgame fields if necessary
3023 multi_sg_check_passwd();
3024 multi_sg_check_name();
3026 // draw the background, etc
3028 GR_MAYBE_CLEAR_RES(Multi_sg_bitmap);
3029 if(Multi_sg_bitmap != -1){
3030 gr_set_bitmap(Multi_sg_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
3033 Multi_sg_window.draw();
3035 // display rank stuff
3036 multi_sg_rank_display_stuff();
3038 // display any pending notification messages
3039 multi_common_notify_do();
3041 // draw all radio button
3042 multi_sg_draw_radio_buttons();
3044 // draw the help overlay
3045 help_overlay_maybe_blit(MULTI_START_OVERLAY);
3051 void multi_start_game_close()
3053 // if i'm the host on a standalone server, send him my start game options (passwd, mode, etc)
3054 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
3055 multi_options_update_start_game(Multi_sg_netgame);
3058 // unload any bitmaps
3059 if(!bm_unload(Multi_sg_bitmap)){
3060 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_sg_bitmap_fname[gr_screen.res]));
3063 // unload the help overlay
3064 help_overlay_unload(MULTI_START_OVERLAY);
3066 // destroy the UI_WINDOW
3067 Multi_sg_window.destroy();
3070 void multi_sg_check_buttons()
3073 for(idx=0;idx<MULTI_SG_NUM_BUTTONS;idx++){
3074 // we only really need to check for one button pressed at a time, so we can break after
3076 if(Multi_sg_buttons[gr_screen.res][idx].button.pressed()){
3077 multi_sg_button_pressed(idx);
3083 void multi_sg_button_pressed(int n)
3086 // go to the options screen
3088 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
3093 if(!help_overlay_active(MULTI_START_OVERLAY)){
3094 help_overlay_set_state(MULTI_START_OVERLAY,1);
3096 help_overlay_set_state(MULTI_START_OVERLAY,0);
3100 // the open button was pressed
3102 // if the closed option is selected
3103 if(Multi_sg_netgame->mode != NG_MODE_OPEN){
3104 Multi_sg_netgame->mode = NG_MODE_OPEN;
3106 gamesnd_play_iface(SND_USER_SELECT);
3108 // release the password control if necessary
3109 multi_sg_release_passwd();
3111 // if its already selected
3113 gamesnd_play_iface(SND_GENERAL_FAIL);
3118 // the open button was pressed
3119 case MSG_CLOSED_GAME:
3120 // if the closed option is selected
3121 if(Multi_sg_netgame->mode != NG_MODE_CLOSED){
3122 Multi_sg_netgame->mode = NG_MODE_CLOSED;
3124 gamesnd_play_iface(SND_USER_SELECT);
3126 // release the password control if necessary
3127 multi_sg_release_passwd();
3129 // if its already selected
3131 gamesnd_play_iface(SND_GENERAL_FAIL);
3136 // toggle password protection
3137 case MSG_PASSWD_GAME:
3138 // if we selected it
3139 if(Multi_sg_netgame->mode != NG_MODE_PASSWORD){
3140 gamesnd_play_iface(SND_USER_SELECT);
3142 Multi_sg_game_passwd.enable();
3143 Multi_sg_game_passwd.unhide();
3144 Multi_sg_game_passwd.set_focus();
3146 Multi_sg_netgame->mode = NG_MODE_PASSWORD;
3148 // copy in the current network password
3149 Multi_sg_game_passwd.set_text(Multi_sg_netgame->passwd);
3151 gamesnd_play_iface(SND_GENERAL_FAIL);
3156 // toggle "restricted" on or off
3157 case MSG_RESTRICTED_GAME:
3158 if(Multi_sg_netgame->mode != NG_MODE_RESTRICTED){
3159 gamesnd_play_iface(SND_USER_SELECT);
3160 Multi_sg_netgame->mode = NG_MODE_RESTRICTED;
3162 // release the password control if necessary
3163 multi_sg_release_passwd();
3165 gamesnd_play_iface(SND_GENERAL_FAIL);
3170 // turn off all rank requirements
3171 case MSG_RANK_SET_GAME:
3172 // if either is set, then turn then both off
3173 if((Multi_sg_netgame->mode != NG_MODE_RANK_BELOW) && (Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE)){
3174 gamesnd_play_iface(SND_USER_SELECT);
3176 // set it to the default case if we're turning it off
3177 multi_sg_select_rank_default();
3178 Multi_sg_rank_start = Multi_sg_rank_select;
3180 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
3182 // release the password control if necessary
3183 multi_sg_release_passwd();
3185 gamesnd_play_iface(SND_GENERAL_FAIL);
3189 // rank above was pressed
3190 case MSG_RANK_ABOVE :
3191 if((Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE) || (Multi_sg_netgame->mode == NG_MODE_RANK_BELOW)){
3192 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
3194 // select the first item
3195 multi_sg_select_rank_default();
3196 Multi_sg_rank_start = Multi_sg_rank_select;
3199 gamesnd_play_iface(SND_USER_SELECT);
3201 gamesnd_play_iface(SND_GENERAL_FAIL);
3205 // rank below was pressed
3206 case MSG_RANK_BELOW :
3207 if((Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE) || (Multi_sg_netgame->mode == NG_MODE_RANK_BELOW)){
3208 Multi_sg_netgame->mode = NG_MODE_RANK_BELOW;
3210 // select the first item
3211 multi_sg_select_rank_default();
3212 Multi_sg_rank_start = Multi_sg_rank_select;
3215 gamesnd_play_iface(SND_USER_SELECT);
3217 gamesnd_play_iface(SND_GENERAL_FAIL);
3221 // scroll the rank list up
3222 case MSG_RANK_SCROLL_UP:
3223 multi_sg_rank_scroll_up();
3226 // scroll the rank list down
3227 case MSG_RANK_SCROLL_DOWN:
3228 multi_sg_rank_scroll_down();
3231 // move to the create game screen
3233 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
3234 gamesnd_play_iface(SND_COMMIT_PRESSED);
3238 gamesnd_play_iface(SND_GENERAL_FAIL);
3239 multi_common_add_notify(XSTR("Not implemented yet!",760));
3244 // NOTE : this is where all Netgame initialization should take place on the host
3245 void multi_sg_init_gamenet()
3247 char buf[128],out_name[128];
3249 net_player *server_save;
3251 // back this data up in case we are already connected to a standalone
3252 memcpy(&save,&Netgame.server_addr,sizeof(net_addr));
3253 server_save = Netgame.server;
3255 // remove campaign flags
3256 Game_mode &= ~(GM_CAMPAIGN_MODE);
3258 // clear out the Netgame structure and start filling in the values
3259 memset( &Netgame, 0, sizeof(Netgame) );
3260 memset( &Multi_sg_netgame_temp, 0, sizeof(netgame_info) );
3262 // if we're on the standalone, we're not the server, so we don't care about setting the netgame state
3263 if(Net_player->state != NETPLAYER_STATE_STD_HOST_SETUP){
3264 Netgame.game_state = NETGAME_STATE_HOST_SETUP;
3265 Multi_sg_netgame = &Netgame;
3268 ml_string(NOX("Starting netgame as Host/Server"));
3270 Multi_sg_netgame = &Multi_sg_netgame_temp;
3274 memcpy(&temp_addr.s_addr, &Netgame.server_addr, 4);
3275 char *server_addr = inet_ntoa(temp_addr);
3276 ml_printf(NOX("Starting netgame as Host on Standalone server : %s"), (server_addr == NULL) ? NOX("Unknown") : server_addr);
3279 Net_player->tracker_player_id = Multi_tracker_id;
3281 Multi_sg_netgame->security = (rand() % 32766) + 1; // get some random security number
3282 Multi_sg_netgame->mode = NG_MODE_OPEN;
3283 Multi_sg_netgame->rank_base = RANK_ENSIGN;
3284 if(Multi_sg_netgame->security < 16){
3285 Multi_sg_netgame->security += 16;
3288 // set the version_info field
3289 Multi_sg_netgame->version_info = NG_VERSION_ID;
3291 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
3292 Netgame.host = Net_player;
3295 // set the default netgame flags
3296 Multi_sg_netgame->flags = 0;
3298 // intialize endgame stuff
3299 multi_endgame_init();
3301 // load in my netgame options
3302 multi_options_netgame_load(&Netgame.options);
3304 // load my local netplayer options
3305 multi_options_local_load(&Net_player->p_info.options, Net_player);
3307 // setup the default game name, taking care of string length and player callsigns
3308 memset(out_name,0,128);
3310 pilot_format_callsign_personal(Player->callsign,out_name);
3311 sprintf(buf, XSTR("%s game",782), out_name); // [[ %s will be a pilot's name ]]
3312 if ( strlen(buf) > MAX_GAMENAME_LEN ){
3313 strcpy(buf, XSTR("Temporary name",783));
3315 strcpy(Multi_sg_netgame->name, buf);
3317 // set the default qos and duration
3318 multi_voice_maybe_update_vars(Netgame.options.voice_qos,Netgame.options.voice_record_time);
3320 // make sure to set the server correctly (me or the standalone)
3321 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3322 memcpy(&Netgame.server_addr, &Psnet_my_addr,sizeof(net_addr));
3323 Netgame.server = Net_player;
3324 Net_player->player_id = multi_get_new_id();
3326 // setup debug flags
3327 Netgame.debug_flags = 0;
3329 if(!Cmdline_server_firing){
3330 Netgame.debug_flags |= NETD_FLAG_CLIENT_FIRING;
3332 if(!Cmdline_client_dodamage){
3333 Netgame.debug_flags |= NETD_FLAG_CLIENT_NODAMAGE;
3337 memcpy(&Netgame.server_addr,&save,sizeof(net_addr));
3338 Netgame.server = server_save;
3341 // if I have a cd or not
3343 Net_player->flags |= NETINFO_FLAG_HAS_CD;
3346 // if I have hacked data
3347 if(game_hacked_data()){
3348 Net_player->flags |= NETINFO_FLAG_HAXOR;
3351 // assign my player struct and other data
3352 Net_player->flags |= (NETINFO_FLAG_CONNECTED | NETINFO_FLAG_DO_NETWORKING);
3353 Net_player->s_info.voice_token_timestamp = -1;
3355 // if we're supposed to flush our cache directory, do so now
3356 if(Net_player->p_info.options.flags & MLO_FLAG_FLUSH_CACHE){
3357 multi_flush_multidata_cache();
3360 ml_string(NOX("Flushing multi-data cache"));
3366 void multi_sg_draw_radio_buttons()
3368 // draw the appropriate radio button
3369 switch(Multi_sg_netgame->mode){
3371 Multi_sg_buttons[gr_screen.res][MSG_OPEN_GAME].button.draw_forced(2);
3375 case NG_MODE_CLOSED:
3376 Multi_sg_buttons[gr_screen.res][MSG_CLOSED_GAME].button.draw_forced(2);
3380 case NG_MODE_PASSWORD:
3381 Multi_sg_buttons[gr_screen.res][MSG_PASSWD_GAME].button.draw_forced(2);
3385 case NG_MODE_RESTRICTED:
3386 Multi_sg_buttons[gr_screen.res][MSG_RESTRICTED_GAME].button.draw_forced(2);
3390 case NG_MODE_RANK_ABOVE:
3391 Multi_sg_buttons[gr_screen.res][MSG_RANK_SET_GAME].button.draw_forced(2);
3392 Multi_sg_buttons[gr_screen.res][MSG_RANK_ABOVE].button.draw_forced(2);
3394 case NG_MODE_RANK_BELOW:
3395 Multi_sg_buttons[gr_screen.res][MSG_RANK_SET_GAME].button.draw_forced(2);
3396 Multi_sg_buttons[gr_screen.res][MSG_RANK_BELOW].button.draw_forced(2);
3401 void multi_sg_rank_scroll_up()
3403 // if he doesn't have either of the rank flags set, then ignore this
3404 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3408 if(Multi_sg_rank_start > 0){
3409 Multi_sg_rank_start--;
3410 gamesnd_play_iface(SND_SCROLL);
3412 gamesnd_play_iface(SND_GENERAL_FAIL);
3416 void multi_sg_rank_scroll_down()
3418 // if he doesn't have either of the rank flags set, then ignore this
3419 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3423 if((NUM_RANKS - Multi_sg_rank_start) > Multi_sg_rank_max_display[gr_screen.res]){
3424 Multi_sg_rank_start++;
3425 gamesnd_play_iface(SND_SCROLL);
3427 gamesnd_play_iface(SND_GENERAL_FAIL);
3431 void multi_sg_rank_display_stuff()
3436 // if he doesn't have either of the rank flags set, then ignore this
3437 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3441 // display the list of ranks
3442 y = Msg_rank_list_coords[gr_screen.res][MSG_Y_COORD];
3443 idx = Multi_sg_rank_start;
3445 while((count < NUM_RANKS) && (count < Multi_sg_rank_max_display[gr_screen.res]) && (idx < NUM_RANKS)){
3446 // if its the selected item, then color it differently
3447 if(idx == Multi_sg_rank_select){
3448 gr_set_color_fast(&Color_text_selected);
3450 gr_set_color_fast(&Color_text_normal);
3454 multi_sg_rank_build_name(Ranks[idx].name,rank_name);
3455 gr_string(Msg_rank_list_coords[gr_screen.res][MSG_X_COORD],y,rank_name);
3463 // display the selected rank
3465 gr_set_color_fast(&Color_bright);
3466 multi_sg_rank_build_name(Ranks[Multi_sg_netgame->rank_base].name,rank_name);
3467 gr_string(Msg_rank_sel_coords[gr_screen.res][MSG_X_COORD],Msg_rank_sel_coords[gr_screen.res][MSG_Y_COORD],rank_name);
3471 void multi_sg_rank_process_select()
3475 // if he doesn't have either of the rank flags set, then ignore this
3476 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3480 // see if he's clicked on an item on the rank list
3481 if(Multi_sg_rank_button.pressed()){
3483 Multi_sg_rank_button.get_mouse_pos(NULL,&y);
3486 if(item + Multi_sg_rank_start < NUM_RANKS){
3487 // evaluate whether this rank is valid for the guy to pick
3488 if(multi_sg_rank_select_valid(item + Multi_sg_rank_start)){
3489 gamesnd_play_iface(SND_USER_SELECT);
3491 Multi_sg_rank_select = item + Multi_sg_rank_start;
3493 // set the Netgame rank
3494 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
3496 gamesnd_play_iface(SND_GENERAL_FAIL);
3498 memset(string,0,255);
3499 sprintf(string,XSTR("Illegal value for a host of your rank (%s)\n",784),Ranks[Net_player->player->stats.rank].name);
3500 multi_common_add_notify(string);
3506 void multi_sg_rank_build_name(char *in,char *out)
3512 first = strtok(use," ");
3514 // just copy the string
3519 // if the first part of the string is lieutenant, then abbreivate it and tack on the rest of the string
3520 if (stricmp(first,XSTR("lieutenant",785)) == 0) {
3521 first = strtok(NULL, NOX("\n"));
3523 // if he's not just a plain lieutenant
3525 strcpy(out,XSTR("Lt. ",786)); // [[ lieutenant ]]
3528 // if he _is_ just a plain lieutenant
3537 void multi_sg_check_passwd()
3539 // check to see if the password input box has been pressed
3540 if(Multi_sg_game_passwd.changed()){
3541 Multi_sg_game_passwd.get_text(Multi_sg_netgame->passwd);
3545 void multi_sg_check_name()
3547 // check to see if the game name input box has been pressed
3548 if(Multi_sg_game_name.changed()){
3549 Multi_sg_game_name.get_text(Multi_sg_netgame->name);
3553 void multi_sg_release_passwd()
3555 // hide and disable the password input box
3556 Multi_sg_game_passwd.hide();
3557 Multi_sg_game_passwd.disable();
3559 // set the focus back to the name input box
3560 Multi_sg_game_name.set_focus();
3563 int multi_sg_rank_select_valid(int rank)
3566 if(Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE){
3567 if(Net_player->player->stats.rank >= rank){
3573 if(Net_player->player->stats.rank <= rank){
3581 void multi_sg_select_rank_default()
3583 // pick our rank for now
3584 Multi_sg_rank_select = Net_player->player->stats.rank;
3586 // set the Netgame rank
3587 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
3590 // -------------------------------------------------------------------------------------------------
3592 // MULTIPLAYER CREATE GAME screen
3597 const char *Multi_create_bitmap_fname[GR_NUM_RESOLUTIONS] = {
3598 "MultiCreate", // GR_640
3599 "2_MultiCreate" // GR_1024
3602 const char *Multi_create_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
3603 "MultiCreate-M", // GR_640
3604 "2_MultiCreate-M" // GR_1024
3607 const char *Multi_create_loading_fname[GR_NUM_RESOLUTIONS] = {
3608 "PleaseWait", // GR_640
3609 "2_PleaseWait" // GR_1024
3613 #define MULTI_CREATE_NUM_BUTTONS 23
3616 #define MC_SHOW_ALL 0
3617 #define MC_SHOW_COOP 1
3618 #define MC_SHOW_TEAM 2
3619 #define MC_SHOW_DOGFIGHT 3
3620 #define MC_PXO_REFRESH 4
3621 #define MC_PILOT_INFO 5
3622 #define MC_SCROLL_LIST_UP 6
3623 #define MC_SCROLL_LIST_DOWN 7
3624 #define MC_SCROLL_PLAYERS_UP 8
3625 #define MC_SCROLL_PLAYERS_DOWN 9
3626 #define MC_MISSION_FILTER 10
3627 #define MC_CAMPAIGN_FILTER 11
3628 #define MC_CANCEL 12
3633 #define MC_SCROLL_INFO_UP 17
3634 #define MC_SCROLL_INFO_DOWN 18
3635 #define MC_HOST_OPTIONS 19
3637 #define MC_OPTIONS 21
3638 #define MC_ACCEPT 22
3641 UI_WINDOW Multi_create_window; // the window object for the create screen
3642 UI_BUTTON Multi_create_player_select_button; // for selecting players
3643 UI_BUTTON Multi_create_list_select_button; // for selecting missions/campaigns
3644 int Multi_create_bitmap; // the background bitmap
3645 UI_SLIDER2 Multi_create_slider; // for create list
3647 // constants for coordinate look ups
3648 #define MC_X_COORD 0
3649 #define MC_Y_COORD 1
3650 #define MC_W_COORD 2
3651 #define MC_H_COORD 3
3653 ui_button_info Multi_create_buttons[GR_NUM_RESOLUTIONS][MULTI_CREATE_NUM_BUTTONS] = {
3656 ui_button_info("MC_18", 34, 131, -1, -1, 18), // all
3657 ui_button_info("MC_19", 72, 131, -1, -1, 19), // coop
3658 ui_button_info("MC_20", 120, 131, -1, -1, 20), // team
3659 // ui_button_info("MC_21", 166, 131, -1, -1, 21), // dogfight
3660 ui_button_info("none", -1, -1, -1, -1, -1), // dogfight (not used)
3661 ui_button_info("none", -1, -1, -1, -1, -1), // pxo?
3662 ui_button_info("MC_26", 540, 114, -1, -1, 26), // pilot info
3663 ui_button_info("MC_03", 0, 187, -1, -1, 2), // scroll list up
3664 ui_button_info("MC_02", 0, 227, -1, -1, 3), // scroll list down
3665 ui_button_info("MC_04", 611, 182, -1, -1, 4), // scroll players up
3666 ui_button_info("MC_05", 611, 221, -1, -1, 5), // scroll players down
3667 ui_button_info("MC_06", 18, 322, -1, -1, 6), // mission filter
3668 ui_button_info("MC_07", 18, 344, -1, -1, 7), // campaign filter
3669 ui_button_info("MC_10", 317, 339, -1, -1, 10), // cancel
3670 ui_button_info("MC_14", 464, 350, -1, -1, 14), // team 1
3671 ui_button_info("MC_15", 498, 350, -1, -1, 15), // team 2
3672 ui_button_info("MC_16", 527, 346, -1, -1, 16), // kick
3673 ui_button_info("MC_17", 572, 346, -1, -1, 17), // close
3674 ui_button_info("MC_08", 0, 398, -1, -1, 8), // scroll mission info up
3675 ui_button_info("MC_09", 0, 435, -1, -1, 9), // scroll mission info down
3676 ui_button_info("MC_27", 447, 402, -1, -1, 27), // host options
3677 ui_button_info("MC_11", 510, 428, -1, -1, 11), // help
3678 ui_button_info("MC_12", 510, 453, -1, -1, 12), // options
3679 ui_button_info("Mc_13", 562, 412, -1, -1, 13), // commit
3681 ui_button_info("MC_00", 32, 129, 36, 158, 0), // show all missions
3682 ui_button_info("MC_01", 76, 129, 71, 158, 1), // show coop missions
3683 ui_button_info("MC_02", 121, 129, 119, 158, 2), // show team missions
3684 ui_button_info("MC_03", 164, 129, 166, 158, 3), // show dogfight missions
3685 ui_button_info("MC_04", 399, 129, 229, 130, 4), // pxo mission refresh
3686 ui_button_info("MC_05", 567, 123, 467, 132, 5), // pilot info
3687 ui_button_info("MC_06", 1, 161, -1, -1, 6), // scroll mission info up
3688 ui_button_info("MC_08", 1, 304, -1, -1, 8), // scroll mission info down
3689 ui_button_info("MC_09", 613, 160, -1, -1, 9), // scroll players up
3690 ui_button_info("MC_10", 613, 202, -1, -1, 10), // scroll players down
3691 ui_button_info("MC_11", 22, 346, 27, 376, 11), // mission filter
3692 ui_button_info("MC_12", 104, 346, 110, 376, 12), // campaign filter
3693 ui_button_info("MC_13", 392, 341, 328, 364, 13), // cancel
3694 ui_button_info("MC_14", 472, 352, 482, 381, 14), // team 0
3695 ui_button_info("MC_15", 506, 352, 514, 381, 15), // team 1
3696 ui_button_info("MC_16", 539, 346, 539, 381, 16), // kick
3697 ui_button_info("MC_17", 589, 346, 582, 381, 17), // close
3698 ui_button_info("MC_18", 1, 406, -1, -1, 18), // scroll list up
3699 ui_button_info("MC_19", 1, 447, -1, -1, 19), // scroll list down
3700 ui_button_info("MC_20", 499, 434, 436, 423, 20), // host options
3701 ui_button_info("MC_21", 534, 426, -1, -1, 21), // help
3702 ui_button_info("MC_22", 534, 452, -1, -1, 22), // options
3703 ui_button_info("MC_23", 571, 426, 572, 413, 23), // commit
3707 ui_button_info("2_MC_00", 51, 207, 61, 253, 0), // show all missions
3708 ui_button_info("2_MC_01", 122, 207, 124, 253, 1), // show coop missions
3709 ui_button_info("2_MC_02", 193, 207, 194, 253, 2), // show team missions
3710 ui_button_info("2_MC_03", 263, 207, 261, 253, 3), // show dogfight missions
3711 ui_button_info("2_MC_04", 639, 207, 479, 218, 4), // pxo mission refresh
3712 ui_button_info("2_MC_05", 907, 197, 748, 216, 5), // pilot info
3713 ui_button_info("2_MC_06", 1, 258, -1, -1, 6), // scroll mission info up
3714 ui_button_info("2_MC_08", 1, 487, -1, -1, 8), // scroll mission info down
3715 ui_button_info("2_MC_09", 981, 256, -1, -1, 9), // scroll players up
3716 ui_button_info("2_MC_10", 981, 323, -1, -1, 10), // scroll players down
3717 ui_button_info("2_MC_11", 35, 554, 46, 601, 11), // mission filter
3718 ui_button_info("2_MC_12", 166, 554, 174, 601, 12), // campaign filter
3719 ui_button_info("2_MC_13", 628, 545, 559, 582, 13), // cancel
3720 ui_button_info("2_MC_14", 756, 564, 772, 610, 14), // team 0
3721 ui_button_info("2_MC_15", 810, 564, 826, 610, 15), // team 1
3722 ui_button_info("2_MC_16", 862, 554, 872, 610, 16), // kick
3723 ui_button_info("2_MC_17", 943, 554, 949, 610, 17), // close
3724 ui_button_info("2_MC_18", 1, 649, -1, -1, 18), // scroll list up
3725 ui_button_info("2_MC_19", 1, 716, -1, -1, 19), // scroll list down
3726 ui_button_info("2_MC_20", 798, 695, 726, 667, 20), // host options
3727 ui_button_info("2_MC_21", 854, 681, -1, -1, 21), // help
3728 ui_button_info("2_MC_22", 854, 724, -1, -1, 22), // options
3729 ui_button_info("2_MC_23", 914, 681, 932, 667, 23), // commit
3734 #define MULTI_CREATE_NUM_TEXT 0
3736 #define MULTI_CREATE_NUM_TEXT 15
3738 UI_XSTR Multi_create_text[GR_NUM_RESOLUTIONS][MULTI_CREATE_NUM_BUTTONS] = {
3740 // not needed for FS1
3742 {"All", 1256, 36, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_ALL].button},
3743 {"Coop", 1257, 71, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_COOP].button},
3744 {"Team", 1258, 119, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_TEAM].button},
3745 {"Dogfight", 1259, 166, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_DOGFIGHT].button},
3746 {"Refresh Missions", 1260, 229, 130, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_PXO_REFRESH].button},
3747 {"Pilot Info", 1261, 467, 132, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_PILOT_INFO].button},
3748 {"Missions", 1262, 27, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_MISSION_FILTER].button},
3749 {"Campaigns", 1263, 110, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_CAMPAIGN_FILTER].button},
3750 {"Cancel", 387, 328, 364, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_CANCEL].button},
3751 {"1", 1264, 482, 381, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_TEAM0].button},
3752 {"2", 1265, 514, 381, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_TEAM1].button},
3753 {"Kick", 1266, 539, 381, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_KICK].button},
3754 {"Close", 1508, 582, 381, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_CLOSE].button},
3755 {"Host Options", 1267, 436, 423, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_HOST_OPTIONS].button},
3756 {"Commit", 1062, 572, 413, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_ACCEPT].button}
3760 // not needed for FS1
3762 {"All", 1256, 61, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_ALL].button},
3763 {"Coop", 1257, 124, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_COOP].button},
3764 {"Team", 1258, 194, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_TEAM].button},
3765 {"Dogfight", 1259, 261, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_DOGFIGHT].button},
3766 {"Refresh Missions", 1260, 501, 218, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_PXO_REFRESH].button},
3767 {"Pilot Info", 1261, 814, 216, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_PILOT_INFO].button},
3768 {"Missions", 1262, 46, 601, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_MISSION_FILTER].button},
3769 {"Campaigns", 1263, 174, 601, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_CAMPAIGN_FILTER].button},
3770 {"Cancel", 387, 559, 582, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_CANCEL].button},
3771 {"1", 1264, 772, 610, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_TEAM0].button},
3772 {"2", 1265, 826, 610, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_TEAM1].button},
3773 {"Kick", 1266, 872, 610, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_KICK].button},
3774 {"Close", 1508, 949, 610, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_CLOSE].button},
3775 {"Host Options", 1267, 755, 683, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_HOST_OPTIONS].button},
3776 {"Commit", 1062, 932, 667, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_ACCEPT].button}
3781 // squad war checkbox
3782 UI_CHECKBOX Multi_create_sw_checkbox;
3783 const char *Multi_create_sw_checkbox_fname[GR_NUM_RESOLUTIONS] = {
3787 int Multi_create_sw_checkbox_coords[GR_NUM_RESOLUTIONS][2] = {
3795 int Multi_create_sw_checkbox_text[GR_NUM_RESOLUTIONS][2] = {
3804 // game information text areas
3805 int Mc_list_coords[GR_NUM_RESOLUTIONS][4] = {
3818 int Mc_players_coords[GR_NUM_RESOLUTIONS][4] = {
3831 int Mc_info_coords[GR_NUM_RESOLUTIONS][4] = {
3844 // mission icon stuff
3845 int Mc_icon_type_coords[GR_NUM_RESOLUTIONS][2] = {
3847 38, -2 // y is an offset
3850 61, -2 // y is an offset
3854 int Mc_icon_volition_coords[GR_NUM_RESOLUTIONS][2] = {
3856 61, -1 // y is an offset
3859 98, 1 // y is an offset
3863 int Mc_icon_silent_coords[GR_NUM_RESOLUTIONS][2] = {
3865 72, 0 // y is an offset
3868 115, 0 // y is an offset
3872 int Mc_icon_valid_coords[GR_NUM_RESOLUTIONS][2] = {
3874 91, 0 // y is an offset
3877 146, 0 // y is an offset
3881 // mission/campaign list column areas
3882 int Mc_column1_w[GR_NUM_RESOLUTIONS] = {
3887 int Mc_column2_w[GR_NUM_RESOLUTIONS] = {
3892 int Mc_column3_w[GR_NUM_RESOLUTIONS] = {
3897 int Mc_mission_name_x[GR_NUM_RESOLUTIONS] = {
3902 int Mc_mission_count_x[GR_NUM_RESOLUTIONS] = {
3907 int Mc_mission_fname_x[GR_NUM_RESOLUTIONS] = {
3912 int Mc_create_game_text[GR_NUM_RESOLUTIONS][2] = {
3913 {13, 116}, // GR_640
3914 {21, 186} // GR_1024
3917 int Mc_players_text[GR_NUM_RESOLUTIONS][2] = {
3918 {467, 150}, // GR_640
3919 {747, 240} // GR_1024
3922 int Mc_team_text[GR_NUM_RESOLUTIONS][2] = {
3923 {484, 342}, // GR_640
3924 {774, 547} // GR_1024
3927 int Mc_slider_coords[GR_NUM_RESOLUTIONS][4] = {
3929 3, 197, 13, 105 // GR_640
3932 5, 316, 20, 168 // GR_1024
3936 const char *Mc_slider_bitmap[GR_NUM_RESOLUTIONS] = {
3941 // player list control thingie defs
3942 #define MULTI_CREATE_PLIST_MAX_DISPLAY 20
3943 int Multi_create_plist_select_flag; // flag indicating if we have a play selected
3944 short Multi_create_plist_select_id; // the net address of the currently selected player (for lookup)
3946 // master tracker details
3947 int Multi_create_frame_count; // framecount
3948 int Multi_create_mt_tried_login; // attempted to login this server on the MT
3950 // mission filter settings
3951 int Multi_create_filter; // what mode we're in
3953 // game/campaign list control defs
3954 int Multi_create_list_max_display[GR_NUM_RESOLUTIONS] = {
3960 int Multi_create_list_count; // number of items in listbox
3961 int Multi_create_list_mode; // 0 == mission mode, 1 == campaign mode
3962 int Multi_create_list_start; // where to start displaying from
3963 int Multi_create_list_select; // which item is currently highlighted
3964 int Multi_create_files_loaded;
3966 char Multi_create_files_array[MULTI_CREATE_MAX_LIST_ITEMS][MAX_FILENAME_LEN];
3968 int Multi_create_mission_count; // how many we have
3969 int Multi_create_campaign_count;
3970 multi_create_info Multi_create_mission_list[MULTI_CREATE_MAX_LIST_ITEMS];
3971 multi_create_info Multi_create_campaign_list[MULTI_CREATE_MAX_LIST_ITEMS];
3973 // use a pointer for the file list. Will point to either the missions or the campaigns
3974 multi_create_info *Multi_create_file_list;
3976 // LOCAL function definitions
3977 void multi_create_check_buttons();
3978 void multi_create_button_pressed(int n);
3979 void multi_create_init_as_server();
3980 void multi_create_init_as_client();
3981 void multi_create_do_netstuff();
3982 void multi_create_plist_scroll_up();
3983 void multi_create_plist_scroll_down();
3984 void multi_create_plist_process();
3985 void multi_create_plist_blit_normal();
3986 void multi_create_plist_blit_team();
3987 void multi_create_list_scroll_up();
3988 void multi_create_list_scroll_down();
3989 void multi_create_list_do();
3990 void multi_create_list_select_item(int n);
3991 void multi_create_list_blit_icons(int list_index, int y_start);
3992 void multi_create_accept_hit();
3993 void multi_create_draw_filter_buttons();
3994 void multi_create_set_selected_team(int team);
3995 short multi_create_get_mouse_id();
3996 int multi_create_ok_to_commit();
3997 int multi_create_verify_cds();
3998 void multi_create_refresh_pxo();
3999 void multi_create_sw_clicked();
4001 // since we can selectively filter out mission/campaign types we always need to map a selected index (which is relative
4002 // to the displayed list), to an absolute index (which is relative to the total file list - some of which may filtered out)
4003 void multi_create_select_to_filename(int select_index,char *filename);
4004 int multi_create_select_to_index(int select_index);
4006 int Multi_create_should_show_popup = 0;
4009 // sorting function to sort mission lists.. Basic sorting on mission name
4010 int multi_create_sort_func(const void *a, const void *b)
4012 multi_create_info *m1, *m2;
4014 m1 = (multi_create_info *)a;
4015 m2 = (multi_create_info *)b;
4017 return ( strcmp(m1->name, m2->name) );
4020 void multi_create_setup_list_data(int mode)
4022 int idx,should_sort,switched_modes;
4024 // set the current mode
4027 if((Multi_create_list_mode != mode) && (mode != -1)){
4028 Multi_create_list_mode = mode;
4031 // set up the list pointers
4032 if ( Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ) {
4033 Multi_create_file_list = Multi_create_mission_list;
4034 } else if ( Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ) {
4035 Multi_create_file_list = Multi_create_campaign_list;
4041 // get the mission count based upon the filter selected
4042 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
4043 switch(Multi_create_filter){
4044 case MISSION_TYPE_MULTI:
4045 Multi_create_list_count = Multi_create_mission_count;
4048 Multi_create_list_count = 0;
4049 // find all missions which match
4050 for(idx=0;idx<Multi_create_mission_count;idx++){
4051 if(Multi_create_mission_list[idx].flags & Multi_create_filter){
4052 Multi_create_list_count++;
4056 // if we switched modes and we have more than 0 items, sort them
4057 if(switched_modes && (Multi_create_list_count > 0)){
4062 } else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
4063 switch(Multi_create_filter){
4064 case MISSION_TYPE_MULTI:
4065 Multi_create_list_count = Multi_create_campaign_count;
4068 Multi_create_list_count = 0;
4069 // find all missions which match
4070 for(idx=0;idx<Multi_create_campaign_count;idx++){
4071 if(Multi_create_campaign_list[idx].flags & Multi_create_filter){
4072 Multi_create_list_count++;
4076 // if we switched modes and we have more than 0 items, sort them
4077 if(switched_modes && (Multi_create_list_count > 0)){
4084 // reset the list start and selected indices
4085 Multi_create_list_start = 0;
4086 Multi_create_list_select = -1;
4087 multi_create_list_select_item(Multi_create_list_start);
4089 // sort the list of missions if necessary
4091 qsort(Multi_create_file_list, Multi_create_list_count, sizeof(multi_create_info), multi_create_sort_func);
4096 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);
4100 void multi_create_game_init()
4105 // now make sure to initialze various netgame stuff based upon whether we're on a standalone or not
4106 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4107 multi_create_init_as_server();
4109 multi_create_init_as_client();
4112 // initialize the player list data
4113 Multi_create_plist_select_flag = 0;
4114 Multi_create_plist_select_id = -1;
4116 // create the interface window
4117 Multi_create_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
4118 Multi_create_window.set_mask_bmap(Multi_create_bitmap_mask_fname[gr_screen.res]);
4120 // load the background bitmap
4121 Multi_create_bitmap = bm_load(Multi_create_bitmap_fname[gr_screen.res]);
4122 if(Multi_create_bitmap < 0){
4123 // we failed to load the bitmap - this is very bad
4127 // close any previous existing instances of the chatbox and create a new one
4131 // load the help overlay
4132 help_overlay_load(MULTI_CREATE_OVERLAY);
4133 help_overlay_set_state(MULTI_CREATE_OVERLAY, 0);
4135 // initialize the common notification messaging
4136 multi_common_notify_init();
4138 // use the common interface palette
4139 multi_common_set_palette();
4141 // create the interface buttons
4142 for(idx=0; idx<MULTI_CREATE_NUM_BUTTONS; idx++){
4143 b = &Multi_create_buttons[gr_screen.res][idx];
4145 // create the object
4146 b->button.create(&Multi_create_window, "", b->x, b->y, 1, 1, ((idx == MC_SCROLL_LIST_UP) || (idx == MC_SCROLL_LIST_DOWN)), 1);
4148 // set the sound to play when highlighted
4149 b->button.set_highlight_action(common_play_highlight_sound);
4151 // set the ani for the button
4152 b->button.set_bmaps(b->filename);
4155 b->button.link_hotspot(b->hotspot);
4157 // some special case stuff for the pxo refresh button
4158 if(idx == MC_PXO_REFRESH){
4159 // if not a PXO game, or if I'm not a server disable and hide the button
4160 if(!MULTI_IS_TRACKER_GAME || !MULTIPLAYER_MASTER){
4162 b->button.disable();
4168 for(idx=0; idx<MULTI_CREATE_NUM_TEXT; idx++){
4169 Multi_create_window.add_XSTR(&Multi_create_text[gr_screen.res][idx]);
4172 // if this is a PXO game, enable the squadwar checkbox
4173 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);
4174 Multi_create_sw_checkbox.set_bmaps(Multi_create_sw_checkbox_fname[gr_screen.res], 6, 0);
4175 if(!MULTI_IS_TRACKER_GAME){
4176 Multi_create_sw_checkbox.hide();
4177 Multi_create_sw_checkbox.disable();
4181 // disable squad war button in demo
4182 Multi_create_sw_checkbox.hide();
4183 Multi_create_sw_checkbox.disable();
4186 // initialize the mission type filtering mode
4187 Multi_create_filter = MISSION_TYPE_MULTI;
4189 // initialize the list mode, and load in a list
4190 memset(Multi_create_mission_list, 0, sizeof(multi_create_info) * MULTI_CREATE_MAX_LIST_ITEMS);
4191 memset(Multi_create_campaign_list, 0, sizeof(multi_create_info) * MULTI_CREATE_MAX_LIST_ITEMS);
4192 for(idx=0; idx<MULTI_CREATE_MAX_LIST_ITEMS; idx++){
4193 Multi_create_mission_list[idx].valid_status = MVALID_STATUS_UNKNOWN;
4194 Multi_create_campaign_list[idx].valid_status = MVALID_STATUS_UNKNOWN;
4196 Multi_create_list_mode = MULTI_CREATE_SHOW_MISSIONS;
4197 Multi_create_list_start = -1;
4198 Multi_create_list_select = -1;
4199 Multi_create_list_count = 0;
4202 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);
4205 // create the player list select button
4206 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);
4207 Multi_create_player_select_button.hide();
4209 // create the mission/campaign list select button
4210 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);
4211 Multi_create_list_select_button.hide();
4213 // set hotkeys for a couple of things.
4214 Multi_create_buttons[gr_screen.res][MC_ACCEPT].button.set_hotkey(KEY_CTRLED+SDLK_RETURN);
4216 // init some master tracker stuff
4217 Multi_create_frame_count = 0;
4218 Multi_create_mt_tried_login = 0;
4220 // remove campaign flags
4221 Game_mode &= ~(GM_CAMPAIGN_MODE);
4223 // send any pilots as appropriate
4224 multi_data_send_my_junk();
4225 Multi_create_file_list = Multi_create_mission_list;
4227 Multi_create_campaign_count = 0;
4228 Multi_create_mission_count = 0;
4229 Multi_create_files_loaded = 0;
4232 void multi_create_game_do()
4236 const char *loading_str = XSTR("Loading", 1336);
4240 // set this if we want to show the pilot info popup
4241 Multi_create_should_show_popup = 0;
4243 // first thing is to load the files
4244 if ( !Multi_create_files_loaded ) {
4245 // if I am a client, send a list request to the server for the missions
4246 if ( MULTIPLAYER_CLIENT ) {
4247 send_mission_list_request( MISSION_LIST_REQUEST );
4251 loading_bitmap = bm_load(Multi_create_loading_fname[gr_screen.res]);
4253 // draw the background, etc
4255 GR_MAYBE_CLEAR_RES(Multi_create_bitmap);
4256 if(Multi_create_bitmap != -1){
4257 gr_set_bitmap(Multi_create_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4261 if ( loading_bitmap > -1 ){
4262 gr_set_bitmap(loading_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4264 gr_bitmap( Please_wait_coords[gr_screen.res][MC_X_COORD], Please_wait_coords[gr_screen.res][MC_Y_COORD] );
4266 // draw "Loading" on it
4268 gr_set_color_fast(&Color_normal);
4270 gr_get_string_size(&str_w, &str_h, loading_str);
4271 gr_string((gr_screen.max_w - str_w) / 2, (gr_screen.max_h - str_h) / 2, loading_str);
4277 multi_create_list_load_missions();
4278 multi_create_list_load_campaigns();
4280 // if this is a tracker game, validate missions
4281 if(MULTI_IS_TRACKER_GAME){
4282 multi_update_valid_missions();
4285 // update the file list
4286 multi_create_setup_list_data(MULTI_CREATE_SHOW_MISSIONS);
4289 // don't bother setting netgame state if ont the server
4290 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4291 Netgame.game_state = NETGAME_STATE_FORMING;
4292 send_netgame_update_packet();
4295 // if we're on the standalone we have to tell him that we're now in the host setup screen
4296 Net_player->state = NETPLAYER_STATE_HOST_SETUP;
4297 send_netplayer_update_packet();
4299 Multi_create_files_loaded = 1;
4302 int k = chatbox_process();
4303 k = Multi_create_window.process(k,0);
4306 // same as the cancel button
4308 if(help_overlay_active(MULTI_CREATE_OVERLAY)){
4309 help_overlay_set_state(MULTI_CREATE_OVERLAY,0);
4311 gamesnd_play_iface(SND_USER_SELECT);
4312 multi_quit_game(PROMPT_HOST);
4317 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
4318 help_overlay_set_state(MULTI_CREATE_OVERLAY, 0);
4321 // process any button clicks
4322 multi_create_check_buttons();
4324 // do any network related stuff
4325 multi_create_do_netstuff();
4327 // draw the background, etc
4329 GR_MAYBE_CLEAR_RES(Multi_create_bitmap);
4330 if(Multi_create_bitmap != -1){
4331 gr_set_bitmap(Multi_create_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4335 // if we're not in team vs. team mode, don't draw the team buttons
4336 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
4337 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.hide();
4338 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.hide();
4339 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.disable();
4340 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.disable();
4342 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.enable();
4343 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.enable();
4344 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.unhide();
4345 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.unhide();
4348 // draw the window itself
4349 Multi_create_window.draw();
4351 gr_set_color_fast(&Color_normal);
4354 // draw Create Game text
4355 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));
4357 // draw players text
4358 gr_string(Mc_players_text[gr_screen.res][MC_X_COORD], Mc_players_text[gr_screen.res][MC_Y_COORD], XSTR("Players", 1269));
4360 // draw players text
4361 gr_string(Mc_team_text[gr_screen.res][MC_X_COORD], Mc_team_text[gr_screen.res][MC_Y_COORD], XSTR("Team", 1258));
4364 // process and display the player list
4365 // NOTE : this must be done before the buttons are checked to insure that a player hasn't dropped
4366 multi_create_plist_process();
4367 if(Netgame.type_flags & NG_TYPE_TEAM){
4368 multi_create_plist_blit_team();
4370 multi_create_plist_blit_normal();
4373 // process and display the game/campaign list
4374 multi_create_list_do();
4376 // draw the correct mission filter button
4377 multi_create_draw_filter_buttons();
4379 // display any text in the info area
4380 multi_common_render_text();
4382 // display any pending notification messages
4383 multi_common_notify_do();
4385 // force the correct mission/campaign button to light up
4386 if( Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ){
4387 Multi_create_buttons[gr_screen.res][MC_MISSION_FILTER].button.draw_forced(2);
4389 Multi_create_buttons[gr_screen.res][MC_CAMPAIGN_FILTER].button.draw_forced(2);
4392 // force draw the closed button if it is toggled on
4393 if(Netgame.flags & NG_FLAG_TEMP_CLOSED){
4394 Multi_create_buttons[gr_screen.res][MC_CLOSE].button.draw_forced(2);
4397 // process and show the chatbox thingie
4401 Multi_create_window.draw_tooltip();
4403 // display the voice status indicator
4404 multi_common_voice_display_status();
4406 // blit the help overlay if necessary
4407 help_overlay_maybe_blit(MULTI_CREATE_OVERLAY);
4410 if(MULTI_IS_TRACKER_GAME){
4411 if(Netgame.type_flags & NG_TYPE_SW){
4412 gr_set_color_fast(&Color_bright);
4414 gr_set_color_fast(&Color_normal);
4416 gr_string(Multi_create_sw_checkbox_text[gr_screen.res][0], Multi_create_sw_checkbox_text[gr_screen.res][1], "SquadWar");
4422 // if we're supposed to show the pilot info popup, do it now
4423 if(Multi_create_should_show_popup){
4424 // get the player index and address of the player item the mouse is currently over
4425 if(Multi_create_plist_select_flag){
4426 player_index = find_player_id(Multi_create_plist_select_id);
4427 if(player_index != -1){
4428 multi_pinfo_popup(&Net_players[player_index]);
4433 // increment the frame count
4434 Multi_create_frame_count++;
4437 void multi_create_game_close()
4439 // unload any bitmaps
4440 if(!bm_unload(Multi_create_bitmap)){
4441 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_create_bitmap_fname[gr_screen.res]));
4444 // unload the help overlay
4445 help_overlay_unload(MULTI_CREATE_OVERLAY);
4447 // destroy the chatbox
4450 // destroy the UI_WINDOW
4451 Multi_create_window.destroy();
4454 void multi_create_check_buttons()
4457 for(idx=0;idx<MULTI_CREATE_NUM_BUTTONS;idx++){
4458 // we only really need to check for one button pressed at a time, so we can break after
4460 if(Multi_create_buttons[gr_screen.res][idx].button.pressed()){
4461 multi_create_button_pressed(idx);
4466 // if the squad war checkbox was clicked
4467 if(Multi_create_sw_checkbox.changed()){
4468 multi_create_sw_clicked();
4472 void multi_create_button_pressed(int n)
4478 gamesnd_play_iface(SND_USER_SELECT);
4479 multi_quit_game(PROMPT_HOST);
4482 // if valid commit conditions have not been met
4483 if(!multi_create_ok_to_commit()){
4488 multi_create_accept_hit();
4493 if(!help_overlay_active(MULTI_CREATE_OVERLAY)){
4494 help_overlay_set_state(MULTI_CREATE_OVERLAY,1);
4496 help_overlay_set_state(MULTI_CREATE_OVERLAY,0);
4500 // scroll the info text box up
4501 case MC_SCROLL_INFO_UP:
4502 multi_common_scroll_text_up();
4505 // scroll the info text box down
4506 case MC_SCROLL_INFO_DOWN:
4507 multi_common_scroll_text_down();
4510 // scroll the player list up
4511 case MC_SCROLL_PLAYERS_UP:
4512 multi_create_plist_scroll_up();
4515 // scroll the player list down
4516 case MC_SCROLL_PLAYERS_DOWN:
4517 multi_create_plist_scroll_down();
4520 // scroll the game/campaign list up
4521 case MC_SCROLL_LIST_UP:
4522 multi_create_list_scroll_up();
4524 Multi_create_slider.forceUp(); // move slider up
4528 // scroll the game/campaign list down
4529 case MC_SCROLL_LIST_DOWN:
4530 multi_create_list_scroll_down();
4532 Multi_create_slider.forceDown(); // move slider down
4536 // go to the options screen
4538 gamesnd_play_iface(SND_USER_SELECT);
4539 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
4542 // show all missions
4544 if(Multi_create_filter != MISSION_TYPE_MULTI){
4545 gamesnd_play_iface(SND_USER_SELECT);
4546 Multi_create_filter = MISSION_TYPE_MULTI;
4547 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4549 gamesnd_play_iface(SND_GENERAL_FAIL);
4553 // show cooperative missions
4555 if(Multi_create_filter != MISSION_TYPE_MULTI_COOP){
4556 gamesnd_play_iface(SND_USER_SELECT);
4557 Multi_create_filter = MISSION_TYPE_MULTI_COOP;
4558 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4560 gamesnd_play_iface(SND_GENERAL_FAIL);
4564 // show team vs. team missions
4566 if(Multi_create_filter != MISSION_TYPE_MULTI_TEAMS){
4567 gamesnd_play_iface(SND_USER_SELECT);
4568 Multi_create_filter = MISSION_TYPE_MULTI_TEAMS;
4569 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4571 gamesnd_play_iface(SND_GENERAL_FAIL);
4575 // show dogfight missions
4577 case MC_SHOW_DOGFIGHT:
4578 if (Multi_create_filter != MISSION_TYPE_MULTI_DOGFIGHT){
4579 gamesnd_play_iface(SND_USER_SELECT);
4580 Multi_create_filter = MISSION_TYPE_MULTI_DOGFIGHT;
4581 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4583 gamesnd_play_iface(SND_GENERAL_FAIL);
4588 // toggle temporary netgame closed on/off
4590 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4591 Netgame.flags ^= NG_FLAG_TEMP_CLOSED;
4593 Netgame.options.flags |= MLO_FLAG_TEMP_CLOSED;
4594 multi_options_update_netgame();
4596 gamesnd_play_iface(SND_USER_SELECT);
4599 // kick the currently selected player (if possible)
4601 // lookup the player at the specified index
4602 if(Multi_create_plist_select_flag){
4603 idx = find_player_id(Multi_create_plist_select_id);
4604 // kick him - but don't ban him
4606 multi_kick_player(idx,0);
4611 // switch to individual mission mode and load in a list
4612 case MC_MISSION_FILTER:
4613 if(Multi_create_list_mode != MULTI_CREATE_SHOW_MISSIONS){
4614 Netgame.campaign_mode = MP_SINGLE;
4616 gamesnd_play_iface(SND_USER_SELECT);
4618 // update the file list
4619 multi_create_setup_list_data(MULTI_CREATE_SHOW_MISSIONS);
4621 gamesnd_play_iface(SND_GENERAL_FAIL);
4625 // switch to campaign mode and load in a list
4626 case MC_CAMPAIGN_FILTER:
4627 // switch off squad war
4628 Multi_create_sw_checkbox.set_state(0);
4629 Netgame.type_flags = NG_TYPE_COOP;
4631 if(Multi_create_list_mode != MULTI_CREATE_SHOW_CAMPAIGNS){
4632 Netgame.campaign_mode = MP_CAMPAIGN;
4634 gamesnd_play_iface(SND_USER_SELECT);
4636 // update the file list
4637 multi_create_setup_list_data(MULTI_CREATE_SHOW_CAMPAIGNS);
4639 gamesnd_play_iface(SND_GENERAL_FAIL);
4643 // attempt to set the selected player's team
4645 multi_create_set_selected_team(0);
4646 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4647 multi_team_send_update();
4651 // attempt to set the selected player's team
4653 multi_create_set_selected_team(1);
4654 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4655 multi_team_send_update();
4659 // popup the pilot info dialog for the currently selected pilot (will occur at the end of the frame)
4661 Multi_create_should_show_popup = 1;
4664 // go to the host options screen
4665 case MC_HOST_OPTIONS:
4666 gamesnd_play_iface(SND_USER_SELECT);
4667 gameseq_post_event(GS_EVENT_MULTI_HOST_OPTIONS);
4670 // refresh PXO file list
4671 case MC_PXO_REFRESH:
4672 if(!MULTI_IS_TRACKER_GAME){
4675 multi_create_refresh_pxo();
4679 gamesnd_play_iface(SND_GENERAL_FAIL);
4680 multi_common_add_notify(XSTR("Not implemented yet!",760));
4685 // do stuff like pinging servers, sending out requests, etc
4686 void multi_create_do_netstuff()
4690 // if not on a standalone
4691 void multi_create_init_as_server()
4693 // set me up as the host and master
4694 Net_player->flags |= (NETINFO_FLAG_AM_MASTER | NETINFO_FLAG_GAME_HOST);
4697 // if on a standalone
4698 void multi_create_init_as_client()
4700 Net_player->flags |= NETINFO_FLAG_GAME_HOST;
4703 // scroll up through the player list
4704 void multi_create_plist_scroll_up()
4706 gamesnd_play_iface(SND_GENERAL_FAIL);
4709 // scroll down through the player list
4710 void multi_create_plist_scroll_down()
4712 gamesnd_play_iface(SND_GENERAL_FAIL);
4715 void multi_create_plist_process()
4717 int test_count,idx,player_index;
4719 // first determine if there are 0 players in the game. This should never happen since the host is _always_ in the game
4721 for(idx=0;idx<MAX_PLAYERS;idx++){
4722 // count anyone except the standalone server (if applicable)
4723 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
4727 if(test_count <= 0){
4731 // if we had a selected item but that player has left, select myself instead
4732 if(Multi_create_plist_select_flag){
4733 player_index = find_player_id(Multi_create_plist_select_id);
4734 if(player_index == -1){
4735 Multi_create_plist_select_id = Net_player->player_id;
4738 Multi_create_plist_select_flag = 1;
4739 Multi_create_plist_select_id = Net_player->player_id;
4742 // if the player has clicked somewhere in the player list area
4743 if(Multi_create_player_select_button.pressed()){
4746 // get the player index and address of the player item the mouse is currently over
4747 player_id = multi_create_get_mouse_id();
4748 player_index = find_player_id(player_id);
4749 if(player_index != -1){
4750 Multi_create_plist_select_flag = 1;
4751 Multi_create_plist_select_id = player_id;
4756 void multi_create_plist_blit_normal()
4759 char str[CALLSIGN_LEN+5];
4760 int y_start = Mc_players_coords[gr_screen.res][MC_Y_COORD];
4763 // display all the players
4764 for(idx=0;idx<MAX_PLAYERS;idx++){
4765 // count anyone except the standalone server (if applicable)
4766 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
4770 // highlight him if he's the host
4771 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4772 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4773 gr_set_color_fast(&Color_text_active_hi);
4775 gr_set_color_fast(&Color_bright);
4778 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4779 gr_set_color_fast(&Color_text_active);
4781 gr_set_color_fast(&Color_text_normal);
4785 // optionally draw his CD status
4786 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4787 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4788 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4790 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4793 // make sure the string will fit, then display it
4794 strcpy(str,Net_players[idx].player->callsign);
4795 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4796 strcat(str,XSTR("(O)",787)); // [[ Observer ]]
4798 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4799 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4806 void multi_create_plist_blit_team()
4809 char str[CALLSIGN_LEN+1];
4810 int y_start = Mc_players_coords[gr_screen.res][MC_Y_COORD];
4813 // display all the red players first
4814 for(idx=0;idx<MAX_PLAYERS;idx++){
4815 // count anyone except the standalone server (if applicable)
4816 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
4817 // reset total offset
4820 // highlight him if he's the host
4821 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4822 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4823 gr_set_color_fast(&Color_text_active_hi);
4825 // be sure to blit the correct team button
4826 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.draw_forced(2);
4828 gr_set_color_fast(&Color_bright);
4831 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4832 gr_set_color_fast(&Color_text_active);
4834 // be sure to blit the correct team button
4835 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.draw_forced(2);
4837 gr_set_color_fast(&Color_text_normal);
4841 // optionally draw his CD status
4842 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4843 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4844 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4846 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4849 // blit the red team indicator
4850 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
4851 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
4852 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4853 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4855 total_offset += Multi_common_icon_dims[MICON_TEAM0_SELECT][0] + 1;
4858 if(Multi_common_icons[MICON_TEAM0] != -1){
4859 gr_set_bitmap(Multi_common_icons[MICON_TEAM0], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4860 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4862 total_offset += Multi_common_icon_dims[MICON_TEAM0][0] + 1;
4866 // make sure the string will fit
4867 strcpy(str,Net_players[idx].player->callsign);
4868 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4869 strcat(str,XSTR("(O)",787));
4871 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4873 // display him in the correct half of the list depending on his team
4874 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4879 // display all the green players next
4880 for(idx=0;idx<MAX_PLAYERS;idx++){
4881 // count anyone except the standalone server (if applicable)
4882 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
4883 // reset total offset
4886 // highlight him if he's the host
4887 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4888 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4889 gr_set_color_fast(&Color_text_active_hi);
4891 // be sure to blit the correct team button
4892 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.draw_forced(2);
4894 gr_set_color_fast(&Color_bright);
4897 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4898 gr_set_color_fast(&Color_text_active);
4900 // be sure to blit the correct team button
4901 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.draw_forced(2);
4903 gr_set_color_fast(&Color_text_normal);
4907 // optionally draw his CD status
4908 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4909 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4910 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4912 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4915 // blit the red team indicator
4916 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
4917 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
4918 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4919 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4921 total_offset += Multi_common_icon_dims[MICON_TEAM1_SELECT][0] + 1;
4924 if(Multi_common_icons[MICON_TEAM1] != -1){
4925 gr_set_bitmap(Multi_common_icons[MICON_TEAM1], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4926 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4928 total_offset += Multi_common_icon_dims[MICON_TEAM1][0] + 1;
4932 // make sure the string will fit
4933 strcpy(str,Net_players[idx].player->callsign);
4934 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4935 strcat(str,XSTR("(O)",787));
4937 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4939 // display him in the correct half of the list depending on his team
4940 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4946 void multi_create_list_scroll_up()
4948 if(Multi_create_list_start > 0){
4949 Multi_create_list_start--;
4951 gamesnd_play_iface(SND_SCROLL);
4953 gamesnd_play_iface(SND_GENERAL_FAIL);
4957 void multi_create_list_scroll_down()
4959 if((Multi_create_list_count - Multi_create_list_start) > Multi_create_list_max_display[gr_screen.res]){
4960 Multi_create_list_start++;
4962 gamesnd_play_iface(SND_SCROLL);
4964 gamesnd_play_iface(SND_GENERAL_FAIL);
4968 // gets a list of multiplayer misisons
4969 void multi_create_list_load_missions()
4971 char *fname, mission_name[NAME_LENGTH+1];
4972 char wild_card[256];
4975 memset(wild_card, 0, 256);
4976 strcpy(wild_card, NOX("*"));
4977 strcat(wild_card, FS_MISSION_FILE_EXT);
4978 file_count = cf_get_file_list_preallocated(MULTI_CREATE_MAX_LIST_ITEMS, Multi_create_files_array, NULL, CF_TYPE_MISSIONS, wild_card);
4979 Multi_create_mission_count = 0;
4981 // maybe create a standalone dialog
4982 if(Game_mode & GM_STANDALONE_SERVER){
4983 std_create_gen_dialog("Loading missions");
4984 std_gen_set_text("Mission:", 1);
4987 for(idx = 0; idx < file_count; idx++){
4988 int flags,max_players;
4992 fname = Multi_create_files_array[idx];
4994 // tack on any necessary file extension
4995 filename = cf_add_ext( fname, FS_MISSION_FILE_EXT );
4997 // for multiplayer beta builds, only accept builtin missions
4998 #if defined(MULTIPLAYER_BETA_BUILD) || defined(FS2_DEMO)
4999 if(game_find_builtin_mission(filename) == NULL){
5002 #elif defined(PD_BUILD)
5003 if((game_find_builtin_mission(filename) == NULL) && !strstr(filename, "peterdrake")){
5008 if(Game_mode & GM_STANDALONE_SERVER){
5009 std_gen_set_text(filename, 2);
5012 flags = mission_parse_is_multi(filename, mission_name);
5014 // if the mission is a multiplayer mission, then add it to the mission list
5016 max_players = mission_parse_get_multi_mission_info( filename );
5017 m_respawn = The_mission.num_respawns;
5019 if ( Multi_create_mission_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
5020 multi_create_info *mcip;
5022 mcip = &Multi_create_mission_list[Multi_create_mission_count];
5023 strcpy(mcip->filename, filename );
5024 strcpy(mcip->name, mission_name );
5025 mcip->flags = flags;
5026 mcip->respawn = m_respawn;
5027 mcip->max_players = (ubyte)max_players;
5029 // get any additional information for possibly builtin missions
5030 fs_builtin_mission *fb = game_find_builtin_mission(filename);
5034 Multi_create_mission_count++;
5040 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);
5043 // maybe create a standalone dialog
5044 if(Game_mode & GM_STANDALONE_SERVER){
5045 std_destroy_gen_dialog();
5049 void multi_create_list_load_campaigns()
5052 int idx, file_count;
5053 int campaign_type,max_players;
5055 char wild_card[256];
5057 // maybe create a standalone dialog
5058 if(Game_mode & GM_STANDALONE_SERVER){
5059 std_create_gen_dialog("Loading campaigns");
5060 std_gen_set_text("Campaign:", 1);
5063 Multi_create_campaign_count = 0;
5064 memset(wild_card, 0, 256);
5065 strcpy(wild_card, NOX("*"));
5066 strcat(wild_card, FS_CAMPAIGN_FILE_EXT);
5067 file_count = cf_get_file_list_preallocated(MULTI_CREATE_MAX_LIST_ITEMS, Multi_create_files_array, NULL, CF_TYPE_MISSIONS, wild_card);
5068 for(idx = 0; idx < file_count; idx++){
5070 char *filename, name[NAME_LENGTH];
5072 fname = Multi_create_files_array[idx];
5074 // tack on any necessary file extension
5075 filename = cf_add_ext( fname, FS_CAMPAIGN_FILE_EXT );
5077 // for multiplayer beta builds, only accept builtin missions
5078 #if defined(MULTIPLAYER_BETA_BUILD) || defined(FS2_DEMO)
5079 if(game_find_builtin_mission(filename) == NULL){
5082 #elif defined(PD_BUILD)
5083 if((game_find_builtin_mission(filename) == NULL) && !strstr(filename, "peterdrake")){
5088 if(Game_mode & GM_STANDALONE_SERVER){
5089 std_gen_set_text(filename, 2);
5092 // if the campaign is a multiplayer campaign, then add the data to the campaign list items
5093 flags = mission_campaign_parse_is_multi( filename, name );
5094 if( flags != CAMPAIGN_TYPE_SINGLE && mission_campaign_get_info(filename,title,&campaign_type,&max_players)) {
5095 if ( Multi_create_campaign_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
5096 multi_create_info *mcip;
5098 mcip = &Multi_create_campaign_list[Multi_create_campaign_count];
5099 strcpy(mcip->filename, filename );
5100 strcpy(mcip->name, name );
5102 // setup various flags
5103 if ( flags == CAMPAIGN_TYPE_MULTI_COOP ){
5104 mcip->flags = MISSION_TYPE_MULTI_COOP | MISSION_TYPE_MULTI;
5105 } else if ( flags == CAMPAIGN_TYPE_MULTI_TEAMS ) {
5106 mcip->flags = MISSION_TYPE_MULTI_TEAMS | MISSION_TYPE_MULTI;
5108 Int3(); // bogus campaign multi type -- find allender
5111 // 0 respawns for campaign files (should be contained within the mission files themselves)
5114 // 0 max players for campaign files
5115 mcip->max_players = (unsigned char)max_players;
5117 // get any additional information for possibly builtin missions
5118 fs_builtin_mission *fb = game_find_builtin_mission(filename);
5122 Multi_create_campaign_count++;
5127 // maybe create a standalone dialog
5128 if(Game_mode & GM_STANDALONE_SERVER){
5129 std_destroy_gen_dialog();
5133 void multi_create_list_do()
5136 int start_index,stop_index;
5137 char selected_name[255];
5139 // bail early if there aren't any selectable items
5140 if(Multi_create_list_count == 0){
5144 // first check to see if the user has clicked on an item
5145 if(Multi_create_list_select_button.pressed()){
5147 Multi_create_list_select_button.get_mouse_pos(NULL,&y);
5150 // make sure we are selectedin valid indices
5151 if((item < Multi_create_list_max_display[gr_screen.res]) && (item >= 0)){
5152 item += Multi_create_list_start;
5154 if(item < Multi_create_list_count){
5155 multi_create_list_select_item(item);
5156 gamesnd_play_iface(SND_IFACE_MOUSE_CLICK);
5161 // bail early if we don't have a start position
5162 if(Multi_create_list_start == -1){
5166 // display the list of individual campaigns/missions
5168 int y_start = Mc_list_coords[gr_screen.res][MC_Y_COORD];
5170 start_index = multi_create_select_to_index(Multi_create_list_start);
5171 stop_index = Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ? Multi_create_mission_count : Multi_create_campaign_count;
5172 for(idx=start_index; idx<stop_index; idx++){
5173 // see if we should drop out
5174 if(count == Multi_create_list_max_display[gr_screen.res]){
5178 // see if we should filter out this mission
5179 if ( !(Multi_create_file_list[idx].flags & Multi_create_filter) ){
5183 // highlight the selected item
5184 multi_create_select_to_filename(Multi_create_list_select,selected_name);
5185 if(!strcmp(selected_name,Multi_create_file_list[idx].filename)){
5186 gr_set_color_fast(&Color_text_selected);
5188 gr_set_color_fast(&Color_text_normal);
5191 // draw the type icon
5192 multi_create_list_blit_icons(idx, y_start);
5194 // force fit the mission name string
5195 strcpy(selected_name,Multi_create_file_list[idx].name);
5196 gr_force_fit_string(selected_name,255,Mc_column1_w[gr_screen.res]);
5197 gr_string(Mc_mission_name_x[gr_screen.res],y_start,selected_name);
5199 // draw the max players if in mission mode
5200 sprintf(selected_name,"%d",(int)Multi_create_file_list[idx].max_players);
5201 gr_string(Mc_mission_count_x[gr_screen.res],y_start,selected_name);
5203 // force fit the mission filename string
5204 strcpy(selected_name,Multi_create_file_list[idx].filename);
5205 gr_force_fit_string(selected_name,255,Mc_column3_w[gr_screen.res]);
5206 gr_string(Mc_mission_fname_x[gr_screen.res],y_start,selected_name);
5213 // takes care of stuff like changing indices around and setting up the netgame structure
5214 void multi_create_list_select_item(int n)
5216 int abs_index,campaign_type,max_players;
5217 char title[NAME_LENGTH+1];
5218 netgame_info ng_temp;
5221 char *campaign_desc;
5223 // if not on the standalone server
5224 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5227 // on the standalone
5229 memset(&ng_temp,0,sizeof(netgame_info));
5233 if ( n != Multi_create_list_select ) {
5234 // check to see if this is a valid index, and bail if it is not
5235 abs_index = multi_create_select_to_index(n);
5236 if(abs_index == -1){
5240 Multi_create_list_select = n;
5242 // set the mission name
5243 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5244 multi_create_select_to_filename(n,ng->mission_name);
5246 multi_create_select_to_filename(n,ng->campaign_name);
5249 // make sure the netgame type is properly set
5250 int old_type = Netgame.type_flags;
5251 abs_index = multi_create_select_to_index(n);
5252 if(abs_index != -1){
5253 if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_TEAMS){
5254 // if we're in squad war mode, leave it as squad war
5255 if(old_type & NG_TYPE_SW){
5256 ng->type_flags = NG_TYPE_SW;
5258 ng->type_flags = NG_TYPE_TVT;
5260 } else if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_COOP){
5261 ng->type_flags = NG_TYPE_COOP;
5262 } else if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_DOGFIGHT){
5263 ng->type_flags = NG_TYPE_DOGFIGHT;
5267 // if we're no longer in a TvT game, just uncheck the squadwar checkbox
5268 if(!(ng->type_flags & NG_TYPE_TEAM)){
5269 Multi_create_sw_checkbox.set_state(0);
5272 // if we switched from something else to team vs. team mode, do some special processing
5273 if((ng->type_flags & NG_TYPE_TEAM) && (ng->type_flags != old_type) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
5277 switch(Multi_create_list_mode){
5278 case MULTI_CREATE_SHOW_MISSIONS:
5279 // don't forget to update the info box window thingie
5280 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5281 ship_init(); // mwa -- 10/15/97. Call this function to reset number of ships in mission
5282 ng->max_players = mission_parse_get_multi_mission_info( ng->mission_name );
5284 Assert(ng->max_players > 0);
5285 strcpy(ng->title,The_mission.name);
5287 // set the information area text
5288 multi_common_set_text(The_mission.mission_desc);
5290 // if we're on the standalone, send a request for the description
5292 send_netgame_descript_packet(&Netgame.server_addr,0);
5293 multi_common_set_text("");
5296 // set the respawns as appropriate
5297 if(Netgame.options.respawn <= Multi_create_file_list[abs_index].respawn){
5298 ng->respawn = Netgame.options.respawn;
5299 nprintf(("Network","Using netgame options for respawn count (%d %d)\n",Netgame.options.respawn,Multi_create_file_list[abs_index].respawn));
5301 ng->respawn = Multi_create_file_list[abs_index].respawn;
5302 nprintf(("Network","Using mission settings for respawn count (%d %d)\n",Netgame.options.respawn,Multi_create_file_list[abs_index].respawn));
5305 case MULTI_CREATE_SHOW_CAMPAIGNS:
5306 // if not on the standalone server
5307 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5308 // get the campaign info
5309 memset(title,0,NAME_LENGTH+1);
5310 if(!mission_campaign_get_info(ng->campaign_name,title,&campaign_type,&max_players, &campaign_desc)) {
5311 memset(ng->campaign_name,0,NAME_LENGTH+1);
5312 ng->max_players = 0;
5314 // if we successfully got the # of players
5316 memset(ng->title,0,NAME_LENGTH+1);
5317 strcpy(ng->title,title);
5318 ng->max_players = max_players;
5321 nprintf(("Network","MC MAX PLAYERS : %d\n",ng->max_players));
5323 // set the information area text
5324 // multi_common_set_text(ng->title);
5325 multi_common_set_text(campaign_desc);
5327 // if on the standalone server, send a request for the description
5329 // no descriptions currently kept for campaigns
5332 // netgame respawns are always 0 for campaigns (until the first mission is loaded)
5337 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5339 send_netgame_update_packet();
5341 // update all machines about stuff like respawns, etc.
5342 multi_options_update_netgame();
5344 multi_options_update_mission(ng, Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ? 1 : 0);
5349 void multi_create_list_blit_icons(int list_index, int y_start)
5351 multi_create_info *mcip;
5352 fs_builtin_mission *fb;
5355 // get a pointer to the list item
5356 max_index = Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ? Multi_create_mission_count - 1 : Multi_create_campaign_count - 1;
5357 if((list_index < 0) || (list_index > max_index)){
5360 mcip = &Multi_create_file_list[list_index];
5362 // blit the multiplayer type icons
5363 if(mcip->flags & MISSION_TYPE_MULTI_COOP){
5364 if(Multi_common_icons[MICON_COOP] >= 0){
5365 gr_set_bitmap(Multi_common_icons[MICON_COOP], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5366 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5368 } else if(mcip->flags & MISSION_TYPE_MULTI_TEAMS){
5369 if(Multi_common_icons[MICON_TVT] >= 0){
5370 gr_set_bitmap(Multi_common_icons[MICON_TVT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5371 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5373 } else if(mcip->flags & MISSION_TYPE_MULTI_DOGFIGHT){
5374 if(Multi_common_icons[MICON_DOGFIGHT] >= 0){
5375 gr_set_bitmap(Multi_common_icons[MICON_DOGFIGHT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5376 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5380 // if its a valid mission, blit the valid mission icon
5381 if(MULTI_IS_TRACKER_GAME && (mcip->valid_status == MVALID_STATUS_VALID)){
5382 if(Multi_common_icons[MICON_VALID] >= 0){
5383 gr_set_bitmap(Multi_common_icons[MICON_VALID], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5384 gr_bitmap(Mc_icon_valid_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_valid_coords[gr_screen.res][MC_Y_COORD]);
5388 // now see if its a builtin mission
5389 fb = game_find_builtin_mission(mcip->filename);
5390 // if the mission is from volition, blit the volition icon
5391 if((fb != NULL) && (fb->flags & FSB_FROM_VOLITION)){
5392 if(Multi_common_icons[MICON_VOLITION] >= 0){
5393 gr_set_bitmap(Multi_common_icons[MICON_VOLITION], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5394 gr_bitmap(Mc_icon_volition_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_volition_coords[gr_screen.res][MC_Y_COORD]);
5399 // check for mdisk mission
5400 fb = game_find_builtin_mission(mcip->filename);
5401 if((fb != NULL) && (fb->flags & FSB_FROM_MDISK)){
5402 if(Multi_common_icons[MICON_MDISK] >= 0){
5403 gr_set_bitmap(Multi_common_icons[MICON_MDISK], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5404 gr_bitmap(Mc_icon_silent_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_silent_coords[gr_screen.res][MC_Y_COORD]);
5410 void multi_create_accept_hit()
5412 char selected_name[255];
5413 int start_campaign = 0;
5415 // make sure all players have finished joining
5416 if(!multi_netplayer_state_check(NETPLAYER_STATE_JOINED,1)){
5417 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("Please wait until all clients have finished joining",788));
5418 gamesnd_play_iface(SND_GENERAL_FAIL);
5421 gamesnd_play_iface(SND_COMMIT_PRESSED);
5424 // do single mission stuff
5425 switch(Multi_create_list_mode){
5426 case MULTI_CREATE_SHOW_MISSIONS:
5427 if(Multi_create_list_select != -1){
5428 // set the netgame mode
5429 Netgame.campaign_mode = MP_SINGLE;
5431 // setup various filenames and mission names
5432 multi_create_select_to_filename(Multi_create_list_select,selected_name);
5433 strncpy( Game_current_mission_filename, selected_name, MAX_FILENAME_LEN );
5434 strncpy(Netgame.mission_name,selected_name,MAX_FILENAME_LEN);
5437 ml_printf(NOX("Starting single mission %s, with %d players"), Game_current_mission_filename, multi_num_players());
5439 multi_common_add_notify(XSTR("No mission selected!",789));
5444 case MULTI_CREATE_SHOW_CAMPAIGNS:
5445 // do campaign related stuff
5446 if(Multi_create_list_select != -1){
5447 // set the netgame mode
5448 Netgame.campaign_mode = MP_CAMPAIGN;
5450 // start a campaign instead of a single mission
5451 multi_create_select_to_filename(Multi_create_list_select,selected_name);
5452 multi_campaign_start(selected_name);
5456 ml_printf(NOX("Starting campaign %s, with %d players"), selected_name, multi_num_players());
5458 multi_common_add_notify(XSTR("No campaign selected!",790));
5464 // if this is a team vs team situation, lock the players send a final team update
5465 if((Netgame.type_flags & NG_TYPE_TEAM) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
5466 multi_team_host_lock_all();
5467 multi_team_send_update();
5470 // if not on the standalone, move to the mission sync state which will take care of everything
5471 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5472 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
5473 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
5475 // otherwise tell the standalone to do so
5477 // when the standalone receives this, he'll do the mission syncing himself
5478 send_mission_sync_packet(MULTI_SYNC_PRE_BRIEFING,start_campaign);
5482 void multi_create_draw_filter_buttons()
5484 // highlight the correct filter button
5485 if ( Multi_create_filter == MISSION_TYPE_MULTI ){
5486 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL].button.draw_forced(2);
5487 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_COOP ) {
5488 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 1].button.draw_forced(2);
5489 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_TEAMS ) {
5490 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 2].button.draw_forced(2);
5491 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_DOGFIGHT ){
5492 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 3].button.draw_forced(2);
5498 void multi_create_set_selected_team(int team)
5502 // if we don't currently have a player selected, don't do anything
5503 if(!Multi_create_plist_select_flag){
5504 gamesnd_play_iface(SND_GENERAL_FAIL);
5507 gamesnd_play_iface(SND_USER_SELECT);
5509 // otherwise attempt to set the team for this guy
5510 player_index = find_player_id(Multi_create_plist_select_id);
5511 if(player_index != -1){
5512 multi_team_set_team(&Net_players[player_index],team);
5516 void multi_create_handle_join(net_player *pl)
5518 // for now just play a bloop sound
5519 gamesnd_play_iface(SND_ICON_DROP_ON_WING);
5522 // fill in net address of player the mouse is over, return player index (or -1 if none)
5523 short multi_create_get_mouse_id()
5525 // determine where he clicked (y pixel value)
5527 Multi_create_player_select_button.get_mouse_pos(NULL,&y);
5529 // select things a little differently if we're in team vs. team or non-team vs. team mode
5531 if(Netgame.type_flags & NG_TYPE_TEAM){
5532 int player_index = -1;
5534 // look through all of team red first
5535 for(idx=0;idx<MAX_PLAYERS;idx++){
5536 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
5539 // if this is the _nth_ guy
5547 // if we still haven't found him yet, look through the green team
5548 if(player_index == -1){
5549 for(idx=0;idx<MAX_PLAYERS;idx++){
5550 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
5552 // if this is the _nth_ guy
5561 if(player_index != -1){
5562 return Net_players[player_index].player_id;
5565 // select the nth active player if possible, disregarding the standalone server
5566 for(idx=0;idx<MAX_PLAYERS;idx++){
5567 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
5570 // if this is the _nth_ guy
5572 return Net_players[idx].player_id;
5581 void multi_create_select_to_filename(int select_index,char *filename)
5585 // look through the mission list
5586 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5587 for(idx=0;idx<Multi_create_mission_count;idx++){
5588 if(Multi_create_file_list[idx].flags & Multi_create_filter){
5592 // if we found the item
5593 if(select_index < 0){
5594 strcpy(filename,Multi_create_file_list[idx].filename);
5599 // look through the campaign list
5600 else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
5601 for(idx=0;idx<Multi_create_campaign_count;idx++){
5604 // if we found the item
5605 if(select_index < 0){
5606 strcpy(filename,Multi_create_file_list[idx].filename);
5612 strcpy(filename,"");
5615 int multi_create_select_to_index(int select_index)
5618 int lookup_index = 0;
5620 // look through the mission list
5621 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5622 for(idx=0;idx<Multi_create_mission_count;idx++){
5623 if(Multi_create_file_list[idx].flags & Multi_create_filter){
5627 // if we found the item
5628 if(select_index < lookup_index){
5633 // look through the campaign list
5634 else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
5635 for(idx=0;idx<Multi_create_campaign_count;idx++){
5638 // if we found the item
5639 if(select_index < 0){
5648 int multi_create_ok_to_commit()
5650 int player_count, observer_count, idx;
5651 int notify_of_hacked_ships_tbl = 0;
5652 int notify_of_hacked_weapons_tbl = 0;
5653 char err_string[255];
5657 // make sure we have a valid mission selected
5658 if(Multi_create_list_select < 0){
5662 // if this is not a valid mission, let the player know
5663 abs_index = multi_create_select_to_index(Multi_create_list_select);
5668 // if we're playing with a hacked ships.tbl (on PXO)
5669 notify_of_hacked_ships_tbl = 0;
5670 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5671 if(!Game_ships_tbl_valid){
5672 notify_of_hacked_ships_tbl = 1;
5675 if(Netgame.flags & NG_FLAG_HACKED_SHIPS_TBL){
5676 notify_of_hacked_ships_tbl = 1;
5679 if(!MULTI_IS_TRACKER_GAME){
5680 notify_of_hacked_ships_tbl = 0;
5682 if(notify_of_hacked_ships_tbl){
5683 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){
5688 // if we're playing with a hacked weapons.tbl (on PXO)
5689 notify_of_hacked_weapons_tbl = 0;
5690 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5691 if(!Game_weapons_tbl_valid){
5692 notify_of_hacked_weapons_tbl = 1;
5695 if(Netgame.flags & NG_FLAG_HACKED_WEAPONS_TBL){
5696 notify_of_hacked_weapons_tbl = 1;
5699 if(!MULTI_IS_TRACKER_GAME){
5700 notify_of_hacked_weapons_tbl = 0;
5702 if(notify_of_hacked_weapons_tbl){
5703 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){
5708 // if any of the players have hacked data
5710 for(idx=0; idx<MAX_PLAYERS; idx++){
5711 // look for hacked players
5712 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAXOR)){
5716 // message everyone - haha
5717 if(Net_players[idx].player != NULL){
5718 sprintf(err_string, "%s %s", Net_players[idx].player->callsign, XSTR("has hacked tables/data", 1271));
5720 sprintf(err_string, "somebody %s", XSTR("has hacked tables/data", 1271));
5722 send_game_chat_packet(Net_player, err_string, MULTI_MSG_ALL, NULL, NULL, 1);
5725 // if we found a hacked set of data
5728 if(MULTI_IS_TRACKER_GAME){
5729 // don't allow squad war matches to continue
5730 if(Netgame.type_flags & NG_TYPE_SW){
5732 // if this is squad war, don't allow it to continue
5733 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));
5738 // otherwise, warn the players that stats will not saved
5740 // if this is squad war, don't allow it to continue
5741 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){
5746 // non-pxo, just give a notice
5748 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){
5754 // check to see that we don't have too many observers
5755 observer_count = multi_num_observers();
5756 if(observer_count > Netgame.options.max_observers){
5757 // print up the error string
5758 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);
5760 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, err_string);
5764 // check to see that we have a valid # of players for the the # of ships in the game
5765 player_count = multi_num_players();
5766 if(player_count > Netgame.max_players){
5767 // print up the error string
5768 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);
5770 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, err_string);
5774 // check to see if teams are assigned properly in a team vs. team situation
5775 if(Netgame.type_flags & NG_TYPE_TEAM){
5776 if(!multi_team_ok_to_commit()){
5777 gamesnd_play_iface(SND_GENERAL_FAIL);
5778 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("Teams and/or team captains are not assigned properly", 793));
5784 if(!multi_create_verify_cds()){
5785 gamesnd_play_iface(SND_GENERAL_FAIL);
5787 #ifdef MULTIPLAYER_BETA_BUILD
5788 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, "You need 1 CD for every player!");
5790 #ifdef DVD_MESSAGE_HACK
5791 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You need 1 DVD for every 4 players!", 794));
5793 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You need 1 CD for every 4 players!", 794));
5799 // if we're playing on the tracker
5800 if(MULTI_IS_TRACKER_GAME){
5801 #ifdef PXO_CHECK_VALID_MISSIONS
5802 if((Multi_create_file_list == Multi_create_mission_list) && (Multi_create_file_list[abs_index].valid_status != MVALID_STATUS_VALID)){
5803 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){
5810 if(!(Netgame.type_flags & NG_TYPE_SW)){
5811 // if he is playing by himself, tell him stats will not be accepted
5812 if(multi_num_players() == 1){
5813 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){
5826 int multi_create_verify_cds()
5828 int player_count = multi_num_players();
5832 // count how many cds we have
5834 for(idx=0;idx<MAX_PLAYERS;idx++){
5835 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAS_CD)){
5840 // for the beta, everyone must have a CD
5841 #ifdef MULTIPLAYER_BETA_BUILD
5842 if(multi_cd_count < player_count){
5846 // determine if we have enough
5847 float ratio = (float)player_count / (float)multi_cd_count;
5848 // greater than a 4 to 1 ratio
5854 // we meet the conditions
5858 // returns an index into Multi_create_mission_list
5859 int multi_create_lookup_mission(char *fname)
5863 for(idx=0; idx<Multi_create_mission_count; idx++){
5864 if(!stricmp(fname, Multi_create_mission_list[idx].filename)){
5869 // couldn't find the mission
5873 // returns an index into Multi_create_campaign_list
5874 int multi_create_lookup_campaign(char *fname)
5878 for(idx=0; idx<Multi_create_campaign_count; idx++){
5879 if(!stricmp(fname, Multi_create_campaign_list[idx].filename)){
5884 // couldn't find the campaign
5888 void multi_create_refresh_pxo()
5890 // delete mvalid.cfg if it exists
5891 cf_delete(MULTI_VALID_MISSION_FILE, CF_TYPE_DATA);
5893 // refresh missions from the tracker
5894 multi_update_valid_missions();
5897 void multi_create_sw_clicked()
5899 netgame_info ng_temp;
5902 int file_index = multi_create_select_to_index(Multi_create_list_select);
5904 // either a temporary netgame or the real one
5905 if(MULTIPLAYER_MASTER){
5912 // maybe switch squad war off
5913 if(!Multi_create_sw_checkbox.checked()){
5914 // if the mission selected is a coop mission, go back to coop mode
5915 Assert(file_index != -1);
5916 if(file_index == -1){
5917 ng->type_flags = NG_TYPE_COOP;
5919 if(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_TEAMS){
5920 ng->type_flags = NG_TYPE_TVT;
5921 } else if(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_DOGFIGHT){
5922 ng->type_flags = NG_TYPE_DOGFIGHT;
5924 ng->type_flags = NG_TYPE_COOP;
5927 // switch squad war on
5929 Assert(file_index != -1);
5930 if((file_index == -1) || !(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_TEAMS)){
5931 Multi_create_sw_checkbox.set_state(0);
5933 // at this point we know its safe to switch squad war on
5934 ng->type_flags = NG_TYPE_SW;
5939 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5941 send_netgame_update_packet();
5943 // update all machines about stuff like respawns, etc.
5944 multi_options_update_netgame();
5946 // on the standalone
5948 // standalone will take care of polling the usertracker
5949 multi_options_update_mission(ng, Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ? 1 : 0);
5954 // -------------------------------------------------------------------------------------------------------------
5956 // MULTIPLAYER HOST OPTIONS SCREEN
5959 #define MULTI_HO_NUM_BUTTONS 12
5960 #define MULTI_HO_NUM_RADIO_BUTTONS 10
5964 #define MULTI_HO_PALETTE "InterfacePalette"
5966 static const char *Multi_ho_bitmap_fname[GR_NUM_RESOLUTIONS] = {
5967 "MultiHost", // GR_640
5968 "2_MultiHost" // GR_1024
5971 static const char *Multi_ho_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
5972 "MultiHost-M", // GR_640
5973 "2_MultiHost-M" // GR_1024
5977 UI_WINDOW Multi_ho_window; // the window object for the join screen
5978 UI_INPUTBOX Multi_ho_respawns; // the # of respawns allowed in the game
5979 UI_INPUTBOX Multi_ho_time_limit; // mission time limit
5980 UI_INPUTBOX Multi_ho_voice_wait; // wait time between tokens
5981 UI_INPUTBOX Multi_ho_kill_limit; // kill limit in a furball mission
5982 UI_INPUTBOX Multi_ho_obs; // # of observers we'll allow
5983 int Multi_ho_bitmap; // the background bitmap
5985 // constants for coordinate lookup
5986 #define MULTI_HO_X_COORD 0
5987 #define MULTI_HO_Y_COORD 1
5988 #define MULTI_HO_W_COORD 2
5989 #define MULTI_HO_H_COORD 3
5990 #define MULTI_HO_TEXT_X_COORD 4
5991 #define MULTI_HO_TEXT_Y_COORD 5
5994 #define MULTI_HO_MSG_RANK 0 // highest ranking players can do messaging
5995 #define MULTI_HO_MSG_LEADER 1 // wing/team leaders can do messaging
5996 #define MULTI_HO_MSG_ANY 2 // any player can do messaging
5997 #define MULTI_HO_MSG_HOST 3 // only the host can do messaging
5998 #define MULTI_HO_END_RANK 4 // highest rank can and host can end mission
5999 #define MULTI_HO_END_LEADER 5 // wing/team leaders and host can end the mission
6000 #define MULTI_HO_END_ANY 6 // any player can end the mission
6001 #define MULTI_HO_END_HOST 7 // only host can end the mission
6002 #define MULTI_HO_VOICE_ON 8 // voice toggled on
6003 #define MULTI_HO_VOICE_OFF 9 // voice toggled off
6004 #define MULTI_HO_HOST_MODIFIES 10 // only the host or team captains can modify ships/weapons in briefing
6005 #define MULTI_HO_ACCEPT 11 // accept button
6007 ui_button_info Multi_ho_buttons[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_BUTTONS] = {
6010 // who is allowed to message
6011 ui_button_info("MH_00", 13, 157, -1, -1, 0), // highest rank
6012 ui_button_info("MH_01", 13, 179, -1, -1, 1), // team/wing leader
6013 ui_button_info("MH_02", 13, 200, -1, -1, 2), // any
6014 ui_button_info("MH_03", 13, 223, -1, -1, 3), // host
6016 // who is allowed to end the mission
6017 ui_button_info("MH_09", 13, 273, -1, -1, 9), // highest rank
6018 ui_button_info("MH_10", 13, 295, -1, -1, 10), // team/wing leader
6019 ui_button_info("MH_11", 13, 317, -1, -1, 11), // any
6020 ui_button_info("MH_12", 13, 339, -1, -1, 12), // host
6022 // voice on/off button
6023 ui_button_info("MH_14", 396, 156, -1, -1, 14),
6024 ui_button_info("MH_15", 453, 156, -1, -1, 15),
6026 // host modifies ships
6027 ui_button_info("MH_19", 215, 410, -1, -1, 19),
6030 ui_button_info("MH_18", 560, 411, -1, -1, 18),
6032 // who is allowed to message
6033 ui_button_info("MH_00", 3, 160, 46, 166, 0), // highest rank
6034 ui_button_info("MH_01", 3, 179, 46, 185, 1), // team/wing leader
6035 ui_button_info("MH_02", 3, 196, 46, 203, 2), // any
6036 ui_button_info("MH_03", 3, 214, 46, 220, 3), // host
6038 // who is allowed to end the mission
6039 ui_button_info("MH_04", 3, 257, 46, 265, 4), // highest rank
6040 ui_button_info("MH_05", 3, 276, 46, 283, 5), // team/wing leader
6041 ui_button_info("MH_06", 3, 294, 46, 300, 6), // any
6042 ui_button_info("MH_07", 3, 311, 46, 317, 7), // host
6044 // voice on/off button
6045 ui_button_info("MH_09", 542, 158, 545, 185, 9),
6046 ui_button_info("MH_10", 598, 158, 604, 185, 10),
6048 // host modifies ships
6049 ui_button_info("MH_13", 542, 377, 437, 363, 13),
6052 ui_button_info("MH_14", 572, 428, 580, 414, 14),
6056 // who is allowed to message
6057 ui_button_info("2_MH_00", 5, 256, 73, 269, 0), // highest rank
6058 ui_button_info("2_MH_01", 5, 286, 73, 297, 1), // team/wing leader
6059 ui_button_info("2_MH_02", 5, 314, 73, 325, 2), // any
6060 ui_button_info("2_MH_03", 5, 341, 73, 352, 3), // host
6062 // who is allowed to end the mission
6063 ui_button_info("2_MH_04", 5, 412, 73, 425, 4), // highest rank
6064 ui_button_info("2_MH_05", 5, 442, 73, 452, 5), // team/wing leader
6065 ui_button_info("2_MH_06", 5, 470, 73, 480, 6), // any
6066 ui_button_info("2_MH_07", 5, 497, 73, 508, 7), // host
6068 // voice on/off button
6069 ui_button_info("2_MH_09", 867, 253, 872, 296, 9),
6070 ui_button_info("2_MH_10", 957, 253, 966, 296, 10),
6072 // host modifies ships
6073 ui_button_info("2_MH_13", 867, 603, 784, 581, 13),
6076 ui_button_info("2_MH_14", 916, 685, 925, 665, 14),
6079 UI_XSTR Multi_ho_text[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_BUTTONS] = {
6081 // not needed for FS1
6083 {"Highest rank", 1280, 46, 166, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_RANK].button},
6084 {"Team / wing-leader", 1281, 46, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_LEADER].button},
6085 {"Any", 1282, 46, 203, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_ANY].button},
6086 {"Host", 1283, 46, 220, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_HOST].button},
6087 {"Highest rank", 1280, 46, 265, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_RANK].button},
6088 {"Team / wing-leader", 1281, 46, 283, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_LEADER].button},
6089 {"Any", 1282, 46, 300, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_ANY].button},
6090 {"Host", 1283, 46, 317, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_HOST].button},
6091 {"On", 1285, 545, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_VOICE_ON].button},
6092 {"Off", 1286, 604, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_VOICE_OFF].button},
6093 {"Host modifies ships", 1287, 437, 363, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_HOST_MODIFIES].button},
6094 {"Exit", 1417, 572, 418, UI_XSTR_COLOR_PINK, -1, &Multi_ho_buttons[0][MULTI_HO_ACCEPT].button},
6098 // not needed for FS1
6100 {"Highest rank", 1280, 62, 269, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_RANK].button},
6101 {"Team / wing-leader", 1281, 62, 297, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_LEADER].button},
6102 {"Any", 1282, 62, 325, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_ANY].button},
6103 {"Host", 1283, 62, 352, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_HOST].button},
6104 {"Highest rank", 1280, 62, 425, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_RANK].button},
6105 {"Team / wing-leader", 1281, 62, 452, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_LEADER].button},
6106 {"Any", 1282, 62, 480, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_ANY].button},
6107 {"Host", 1283, 62, 508, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_HOST].button},
6108 {"On", 1285, 877, 294, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_VOICE_ON].button},
6109 {"Off", 1286, 967, 293, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_VOICE_OFF].button},
6110 {"Host modifies ships", 1287, 869, 589, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_HOST_MODIFIES].button},
6111 {"Exit", 1417, 953, 672, UI_XSTR_COLOR_PINK, -1, &Multi_ho_buttons[1][MULTI_HO_ACCEPT].button},
6116 // radio button controls
6117 #define MULTI_HO_NUM_RADIO_GROUPS 3
6118 #define MULTI_HO_MSG_GROUP 0 // group dealing with squadmate messaging
6119 #define MULTI_HO_END_GROUP 1 // group dealing with ending the mission
6120 #define MULTI_HO_VOICE_GROUP 2 // group dealing with voice stuff
6121 int Multi_ho_radio_groups[MULTI_HO_NUM_RADIO_GROUPS] = { // currently selected button in the radio button group
6124 int Multi_ho_radio_info[MULTI_HO_NUM_RADIO_BUTTONS][3] = { // info related to each of the radio buttons themselves
6125 // { group #, value, button id# }
6126 {0, 0, 0}, // highest ranking players can do messaging
6127 {0, 1, 1}, // wing/team leaders can do messaging
6128 {0, 2, 2}, // any player can do messaging
6129 {0, 3, 3}, // only host can do messaging
6130 {1, 0, 4}, // highest rank and host can end the mission
6131 {1, 1, 5}, // team/wing leader can end the mission
6132 {1, 2, 6}, // any player can end the mission
6133 {1, 3, 7}, // only the host can end the mission
6134 {2, 0, 8}, // voice toggled on
6135 {2, 1, 9} // voice toggled off
6139 #define MULTI_HO_NUM_SLIDERS 3
6140 #define MULTI_HO_SLIDER_VOICE_QOS 0 // voice quality of sound
6141 #define MULTI_HO_SLIDER_VOICE_DUR 1 // max duration of voice recording
6142 #define MULTI_HO_SLIDER_SKILL 2 // skill level
6144 const char *filename;
6149 UI_DOT_SLIDER_NEW slider; // because we have a class inside this struct, we need the constructor below..
6151 ho_sliders(const 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){}
6153 ho_sliders Multi_ho_sliders[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_SLIDERS] = {
6156 ho_sliders("MH_16", 396, 210, -1, -1, 16, 19, 10), // voice qos
6157 ho_sliders("MH_17", 396, 263, -1, -1, 17, 19, 10), // voice duration
6158 ho_sliders("MH_13", 10, 387, -1, -1, 13, 36, 5), // skill level
6160 ho_sliders("MH_11", 428, 214, 437, 199, 11, 19, 10), // voice qos
6161 ho_sliders("MH_12", 428, 261, 437, 246, 12, 19, 10), // voice duration
6162 ho_sliders("MH_08", 237, 454, 230, 411, 8, 36, 5), // skill level
6166 ho_sliders("2_MH_11", 684, 343, 690, 323, 11, 32, 10), // voice qos
6167 ho_sliders("2_MH_12", 685, 418, 837, 468, 12, 32, 10), // voice duration
6168 ho_sliders("2_MH_08", 379, 727, 369, 663, 8, 60, 5), // skill level
6172 int Multi_ho_mission_respawn;
6174 int Multi_ho_host_modifies;
6176 // whether or not any of the inputboxes on this screen had focus last frame
6177 int Multi_ho_lastframe_input = 0;
6179 // game information text areas
6183 #define MULTI_HO_NUM_TITLES 14
6185 UI_XSTR Multi_ho_titles[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_TITLES] = {
6187 { "AI Orders", 1289, 32, 144, UI_XSTR_COLOR_GREEN, -1, NULL },
6188 { "End Mission", 1290, 32, 242, UI_XSTR_COLOR_GREEN, -1, NULL },
6189 { "Time Limit", 1291, 32, 347, UI_XSTR_COLOR_GREEN, -1, NULL },
6190 { "Min", 1292, 74, 362, UI_XSTR_COLOR_GREEN, -1, NULL },
6191 { "Respawn Limit", 1288, 32, 378, UI_XSTR_COLOR_GREEN, -1, NULL },
6192 { "Kill Limit", 1293, 32, 409, UI_XSTR_COLOR_GREEN, -1, NULL },
6193 { "Observers", 1294, 32, 441, UI_XSTR_COLOR_GREEN, -1, NULL },
6194 { "Skill Level", 1284, 230, 411, UI_XSTR_COLOR_GREEN, -1, NULL },
6195 { "Voice Transmission", 1295, 437, 144, UI_XSTR_COLOR_GREEN, -1, NULL },
6196 { "Voice Quality", 1296, 437, 199, UI_XSTR_COLOR_GREEN, -1, NULL },
6197 { "Message Duration", 1297, 437, 246, UI_XSTR_COLOR_GREEN, -1, NULL },
6198 { "sec", 1522, 523, 292, UI_XSTR_COLOR_GREEN, -1, NULL },
6199 { "sec", 1523, 523, 332, UI_XSTR_COLOR_GREEN, -1, NULL },
6200 { "Voice Wait", 1298, 437, 313, UI_XSTR_COLOR_GREEN, -1, NULL },
6203 { "AI Orders", 1289, 48, 238, UI_XSTR_COLOR_GREEN, -1, NULL },
6204 { "End Mission", 1290, 48, 394, UI_XSTR_COLOR_GREEN, -1, NULL },
6205 { "Time Limit", 1291, 50, 568, UI_XSTR_COLOR_GREEN, -1, NULL },
6206 { "Min", 1292, 119, 581, UI_XSTR_COLOR_GREEN, -1, NULL },
6207 { "Respawn Limit", 1288, 50, 618, UI_XSTR_COLOR_GREEN, -1, NULL },
6208 { "Kill Limit", 1293, 50, 668, UI_XSTR_COLOR_GREEN, -1, NULL },
6209 { "Observers", 1294, 50, 718, UI_XSTR_COLOR_GREEN, -1, NULL },
6210 { "Skill Level", 1284, 398, 670, UI_XSTR_COLOR_GREEN, -1, NULL },
6211 { "Voice Transmission", 1295, 869, 239, UI_XSTR_COLOR_GREEN, -1, NULL },
6212 { "Voice Quality", 1296, 690, 331, UI_XSTR_COLOR_GREEN, -1, NULL },
6213 { "Message Duration", 1297, 690, 405, UI_XSTR_COLOR_GREEN, -1, NULL },
6214 { "sec", 1522, 837, 467, UI_XSTR_COLOR_GREEN, -1, NULL },
6215 { "sec", 1523, 837, 534, UI_XSTR_COLOR_GREEN, -1, NULL },
6216 { "Voice Wait", 1298, 742, 510, UI_XSTR_COLOR_GREEN, -1, NULL },
6221 // mission time limit input box
6222 int Ho_time_coords[GR_NUM_RESOLUTIONS][4] = {
6235 // furball kill limit input box
6236 int Ho_kill_coords[GR_NUM_RESOLUTIONS][4] = {
6249 // voice recording duration text display area
6250 int Ho_vd_coords[GR_NUM_RESOLUTIONS][4] = {
6263 // voice token wait input box
6264 int Ho_vw_coords[GR_NUM_RESOLUTIONS][6] = {
6277 // observer count input box
6278 int Ho_obs_coords[GR_NUM_RESOLUTIONS][4] = {
6291 // skill text description area
6292 int Ho_st_coords[GR_NUM_RESOLUTIONS][4] = {
6305 // respawn input box
6306 int Ho_rsp_coords[GR_NUM_RESOLUTIONS][6] = {
6319 // respawn max text area
6320 int Ho_max_rsp_coords[GR_NUM_RESOLUTIONS][2] = {
6333 // maximum values for various input boxes (to notify user of overruns)
6334 #define MULTI_HO_MAX_TIME_LIMIT 500
6335 #define MULTI_HO_MAX_TOKEN_WAIT 5
6336 #define MULTI_HO_MAX_KILL_LIMIT 9999
6337 #define MULTI_HO_MAX_OBS 4
6339 // LOCAL function definitions
6340 void multi_ho_check_buttons();
6341 void multi_ho_button_pressed(int n);
6342 void multi_ho_draw_radio_groups();
6343 void multi_ho_accept_hit();
6344 void multi_ho_get_options();
6345 void multi_ho_apply_options();
6346 void multi_ho_display_record_time();
6347 int multi_ho_check_values();
6348 void multi_ho_check_focus();
6349 void multi_ho_blit_max_respawns();
6350 void multi_ho_display_skill_level();
6352 void multi_host_options_init()
6356 // create the interface window
6357 Multi_ho_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
6358 Multi_ho_window.set_mask_bmap(Multi_ho_bitmap_mask_fname[gr_screen.res]);
6360 // load the background bitmap
6361 Multi_ho_bitmap = bm_load(Multi_ho_bitmap_fname[gr_screen.res]);
6362 if(Multi_ho_bitmap < 0){
6363 // we failed to load the bitmap - this is very bad
6367 // initialize the common notification messaging
6368 multi_common_notify_init();
6370 // use the common interface palette
6371 multi_common_set_palette();
6373 // create the interface buttons
6374 for(idx=0;idx<MULTI_HO_NUM_BUTTONS;idx++){
6375 // create the object
6376 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);
6378 // set the sound to play when highlighted
6379 Multi_ho_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
6381 // set the ani for the button
6382 Multi_ho_buttons[gr_screen.res][idx].button.set_bmaps(Multi_ho_buttons[gr_screen.res][idx].filename);
6384 // set the hotspot, ignoring the skill level button
6385 Multi_ho_buttons[gr_screen.res][idx].button.link_hotspot(Multi_ho_buttons[gr_screen.res][idx].hotspot);
6389 Multi_ho_window.add_XSTR(&Multi_ho_text[gr_screen.res][idx]);
6395 for(idx=0; idx<MULTI_HO_NUM_TITLES; idx++){
6396 Multi_ho_window.add_XSTR(&Multi_ho_titles[gr_screen.res][idx]);
6400 // create the interface sliders
6401 for(idx=0; idx<MULTI_HO_NUM_SLIDERS; idx++){
6402 // create the object
6403 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);
6406 // create the respawn count input box
6407 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);
6408 // if we're in campaign mode, disable it
6409 if(Netgame.campaign_mode == MP_CAMPAIGN){
6410 Multi_ho_respawns.set_text(XSTR("NA",795)); // [[ Not applicable ]]
6411 Multi_ho_respawns.disable();
6413 Multi_ho_respawns.set_valid_chars("0123456789");
6416 // create the time limit input box
6417 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);
6418 Multi_ho_time_limit.set_valid_chars("-0123456789");
6420 // create the voice token wait input box
6421 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);
6422 Multi_ho_voice_wait.set_valid_chars("01243456789");
6424 // create the furball kill limit input box
6425 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);
6426 Multi_ho_kill_limit.set_valid_chars("0123456789");
6428 // create the observer limit input box
6429 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);
6430 Multi_ho_obs.set_valid_chars("01234");
6432 // load in the current netgame defaults
6433 multi_ho_get_options();
6435 // whether or not any of the inputboxes on this screen had focus last frame
6436 Multi_ho_lastframe_input = 0;
6438 // get the # of respawns for the currently selected mission (if any)
6439 if(Multi_create_list_select != -1){
6440 int abs_index = multi_create_select_to_index(Multi_create_list_select);
6442 // if he has a valid mission selected
6444 Multi_ho_mission_respawn = (int)Multi_create_file_list[abs_index].respawn;
6446 Multi_ho_mission_respawn = -1;
6449 Multi_ho_mission_respawn = -1;
6453 void multi_ho_update_sliders()
6455 // game skill slider
6456 if (Game_skill_level != Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos) {
6457 if ( !(Netgame.type_flags & NG_TYPE_TEAM) ){
6458 Game_skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6459 gamesnd_play_iface(SND_USER_SELECT);
6461 Game_skill_level = NUM_SKILL_LEVELS / 2;
6465 // get the voice qos options
6466 if (Netgame.options.voice_qos != (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1)) {
6467 Netgame.options.voice_qos = (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1);
6468 gamesnd_play_iface(SND_USER_SELECT);
6471 // get the voice duration options
6472 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)) {
6473 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);
6474 gamesnd_play_iface(SND_USER_SELECT);
6479 void multi_host_options_do()
6484 k = Multi_ho_window.process();
6487 // process any keypresses
6490 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
6493 case KEY_CTRLED + SDLK_RETURN :
6494 gamesnd_play_iface(SND_COMMIT_PRESSED);
6495 multi_ho_accept_hit();
6499 // process any button clicks
6500 multi_ho_check_buttons();
6502 // update the sliders
6503 multi_ho_update_sliders();
6505 // make sure that the chatbox inputbox and any inputbox on this screen are mutually exclusive in terms of focus
6506 multi_ho_check_focus();
6508 // draw the background, etc
6510 GR_MAYBE_CLEAR_RES(Multi_ho_bitmap);
6511 if(Multi_ho_bitmap != -1){
6512 gr_set_bitmap(Multi_ho_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
6515 Multi_ho_window.draw();
6517 // draw all the radio buttons properly
6518 multi_ho_draw_radio_groups();
6520 // display any pending notification messages
6521 multi_common_notify_do();
6523 // display the voice record time settings
6524 multi_ho_display_record_time();
6526 // maybe display the max # of respawns next to the respawn input box
6527 multi_ho_blit_max_respawns();
6529 // blit the proper skill level
6530 multi_ho_display_skill_level();
6532 // blit the "host modifies button"
6533 if(Multi_ho_host_modifies){
6534 Multi_ho_buttons[gr_screen.res][MULTI_HO_HOST_MODIFIES].button.draw_forced(2);
6537 // process and show the chatbox thingie
6541 Multi_ho_window.draw_tooltip();
6543 // display the voice status indicator
6544 multi_common_voice_display_status();
6550 void multi_host_options_close()
6552 // unload any bitmaps
6553 if(!bm_unload(Multi_ho_bitmap)){
6554 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_ho_bitmap_fname[gr_screen.res]));
6557 // destroy the UI_WINDOW
6558 Multi_ho_window.destroy();
6561 void multi_ho_check_buttons()
6564 for(idx=0;idx<MULTI_HO_NUM_BUTTONS;idx++){
6565 // we only really need to check for one button pressed at a time, so we can break after
6567 if(Multi_ho_buttons[gr_screen.res][idx].button.pressed()){
6568 multi_ho_button_pressed(idx);
6574 void multi_ho_button_pressed(int n)
6576 int radio_index,idx;
6577 int x_pixel,y_pixel;
6579 // get the pixel position of the click
6580 Multi_ho_buttons[gr_screen.res][n].button.get_mouse_pos(&x_pixel,&y_pixel);
6583 // clicked on the accept button
6584 case MULTI_HO_ACCEPT:
6585 gamesnd_play_iface(SND_COMMIT_PRESSED);
6586 multi_ho_accept_hit();
6589 // clicked on the host/captains only modify button
6590 case MULTI_HO_HOST_MODIFIES:
6591 // toggle it on or off
6592 Multi_ho_host_modifies = !Multi_ho_host_modifies;
6593 gamesnd_play_iface(SND_USER_SELECT);
6597 // look through the radio buttons and see which one this corresponds to
6599 for(idx=0;idx<MULTI_HO_NUM_RADIO_BUTTONS;idx++){
6600 if(Multi_ho_radio_info[idx][2] == n){
6605 Assert(radio_index != -1);
6607 // check to see if a radio button was pressed
6608 if(radio_index < MULTI_HO_NUM_RADIO_BUTTONS){
6609 // see if this value is already picked for this radio group
6610 if(Multi_ho_radio_groups[Multi_ho_radio_info[radio_index][0]] != Multi_ho_radio_info[radio_index][1]){
6611 gamesnd_play_iface(SND_USER_SELECT);
6612 Multi_ho_radio_groups[Multi_ho_radio_info[radio_index][0]] = Multi_ho_radio_info[radio_index][1];
6614 gamesnd_play_iface(SND_GENERAL_FAIL);
6619 void multi_ho_draw_radio_groups()
6623 // go through each item and draw it if it is the selected button in its respective group
6624 for(idx=0;idx<MULTI_HO_NUM_RADIO_BUTTONS;idx++){
6625 /// if this button is the currently selected one in its group
6626 if(Multi_ho_radio_info[idx][1] == Multi_ho_radio_groups[Multi_ho_radio_info[idx][0]]){
6627 Multi_ho_buttons[gr_screen.res][Multi_ho_radio_info[idx][2]].button.draw_forced(2);
6632 void multi_ho_accept_hit()
6636 // check the values in the input boxes
6637 if(!multi_ho_check_values()){
6641 // zero out the netgame flags
6644 // set default options
6645 Netgame.options.flags = (MSO_FLAG_INGAME_XFER | MSO_FLAG_ACCEPT_PIX);
6647 // set the squadmate messaging flags
6648 switch(Multi_ho_radio_groups[MULTI_HO_MSG_GROUP]){
6650 Netgame.options.squad_set = MSO_SQUAD_RANK;
6653 Netgame.options.squad_set = MSO_SQUAD_LEADER;
6656 Netgame.options.squad_set = MSO_SQUAD_ANY;
6659 Netgame.options.squad_set = MSO_SQUAD_HOST;
6665 // set the end mission flags
6666 switch(Multi_ho_radio_groups[MULTI_HO_END_GROUP]){
6668 Netgame.options.endgame_set = MSO_END_RANK;
6671 Netgame.options.endgame_set = MSO_END_LEADER;
6674 Netgame.options.endgame_set = MSO_END_ANY;
6677 Netgame.options.endgame_set = MSO_END_HOST;
6683 // set the voice toggle
6684 switch(Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP]){
6686 Netgame.options.flags &= ~(MSO_FLAG_NO_VOICE);
6689 Netgame.options.flags |= MSO_FLAG_NO_VOICE;
6695 // get the voice qos options
6696 Netgame.options.voice_qos = (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1);
6698 // get the voice duration options
6699 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);
6701 // set the skill level. If in team vs. team mode, preserve the old setting before saving
6702 // the pilot file. I'll bet that this doesn't work though because the pilot file gets
6703 // written in a bunch of locations....sigh.
6704 if ( !(Netgame.type_flags & NG_TYPE_TEAM) ){
6705 Game_skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6707 Game_skill_level = NUM_SKILL_LEVELS / 2;
6710 // set the netgame respawn count
6711 // maybe warn the user that respawns will not be used for a campaign mission
6712 if(Netgame.campaign_mode == MP_SINGLE){
6713 Multi_ho_respawns.get_text(resp_str);
6714 uint temp_respawn = (uint)atoi(resp_str);
6715 // if he currently has no mission selected, let the user set any # of respawns
6716 if((int)temp_respawn > Multi_ho_mission_respawn){
6717 if(Multi_ho_mission_respawn == -1){
6718 Netgame.respawn = temp_respawn;
6719 Netgame.options.respawn = temp_respawn;
6721 // this should have been taken care of by the interface code
6726 Netgame.options.respawn = temp_respawn;
6727 Netgame.respawn = temp_respawn;
6731 // get the mission time limit
6732 Multi_ho_time_limit.get_text(resp_str);
6733 int temp_time = atoi(resp_str);
6735 Netgame.options.mission_time_limit = fl2f(-1.0f);
6736 } else if(temp_time > MULTI_HO_MAX_TIME_LIMIT){
6739 Netgame.options.mission_time_limit = fl2f(60.0f * (float)temp_time);
6742 // get observer count options
6743 Multi_ho_obs.get_text(resp_str);
6744 int temp_obs = atoi(resp_str);
6745 if(temp_obs > MULTI_HO_MAX_OBS){
6748 Netgame.options.max_observers = (ubyte)temp_obs;
6750 // get the furball kill limit
6751 Multi_ho_kill_limit.get_text(resp_str);
6752 int temp_kills = atoi(resp_str);
6753 if(temp_kills > MULTI_HO_MAX_KILL_LIMIT){
6756 Netgame.options.kill_limit = temp_kills;
6758 // get the token wait limit
6759 Multi_ho_voice_wait.get_text(resp_str);
6760 int temp_wait = atoi(resp_str);
6761 if(temp_wait > MULTI_HO_MAX_TOKEN_WAIT){
6764 Netgame.options.voice_token_wait = (temp_wait * 1000);
6766 // set the netgame option
6767 Netgame.options.skill_level = (ubyte)Game_skill_level;
6769 // get whether we're in host/captains only modify mode
6770 Netgame.options.flags &= ~(MSO_FLAG_SS_LEADERS);
6771 if(Multi_ho_host_modifies){
6772 Netgame.options.flags |= MSO_FLAG_SS_LEADERS;
6775 // store these values locally
6776 memcpy(&Player->m_local_options,&Net_player->p_info.options,sizeof(multi_local_options));
6777 memcpy(&Player->m_server_options,&Netgame.options,sizeof(multi_server_options));
6778 write_pilot_file(Player);
6780 // apply any changes in settings (notify everyone of voice qos changes, etc)
6781 multi_ho_apply_options();
6783 // move back to the create game screen
6784 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
6787 void multi_ho_get_options()
6791 // set the squadmate messaging buttons
6792 switch(Netgame.options.squad_set){
6793 case MSO_SQUAD_RANK :
6794 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 0;
6796 case MSO_SQUAD_LEADER:
6797 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 1;
6800 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 2;
6802 case MSO_SQUAD_HOST:
6803 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 3;
6809 // set the mission end buttons
6810 switch(Netgame.options.endgame_set){
6812 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 0;
6814 case MSO_END_LEADER:
6815 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 1;
6818 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 2;
6821 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 3;
6827 // set the voice toggle buttons
6828 if(Netgame.options.flags & MSO_FLAG_NO_VOICE){
6829 Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP] = 1;
6831 Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP] = 0;
6834 // get the voice qos options
6835 Assert((Netgame.options.voice_qos >= 1) && (Netgame.options.voice_qos <= 10));
6836 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos = (Netgame.options.voice_qos - 1);
6838 // get the voice duration options
6839 Assert((Netgame.options.voice_record_time > 0) && (Netgame.options.voice_record_time <= MULTI_VOICE_MAX_TIME));
6840 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos = ((int)((float)Netgame.options.voice_record_time / 500.0f)) - 1;
6842 // get the current skill level
6843 Assert((Game_skill_level >= 0) && (Game_skill_level < NUM_SKILL_LEVELS));
6844 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos = Game_skill_level;
6846 // get the # of observers
6847 memset(resp_str,0,10);
6848 sprintf(resp_str,"%d",Netgame.options.max_observers);
6849 Multi_ho_obs.set_text(resp_str);
6851 // set the respawn count
6852 if(Netgame.campaign_mode == MP_SINGLE){
6853 memset(resp_str,0,10);
6854 sprintf(resp_str,"%d",Netgame.respawn);
6855 Multi_ho_respawns.set_text(resp_str);
6858 // set the mission time limit
6859 memset(resp_str,0,10);
6860 float tl = f2fl(Netgame.options.mission_time_limit);
6861 sprintf(resp_str,"%d",(int)(tl / 60.0f));
6862 Multi_ho_time_limit.set_text(resp_str);
6864 // set the furball kill limit
6865 memset(resp_str,0,10);
6866 sprintf(resp_str,"%d",Netgame.options.kill_limit);
6867 Multi_ho_kill_limit.set_text(resp_str);
6869 // set the token wait time
6870 memset(resp_str,0,10);
6871 sprintf(resp_str,"%d",Netgame.options.voice_token_wait / 1000);
6872 Multi_ho_voice_wait.set_text(resp_str);
6874 // get whether we're in host/captains only modify mode
6875 if(Netgame.options.flags & MSO_FLAG_SS_LEADERS){
6876 Multi_ho_host_modifies = 1;
6878 Multi_ho_host_modifies = 0;
6882 void multi_ho_apply_options()
6884 // if the voice qos or duration has changed, apply the change
6885 multi_voice_maybe_update_vars(Netgame.options.voice_qos,Netgame.options.voice_record_time);
6887 // send an options update
6888 multi_options_update_netgame();
6891 // display the voice record time settings
6892 void multi_ho_display_record_time()
6895 int full_seconds, half_seconds;
6898 memset(time_str,0,30);
6901 full_seconds = (((Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 500) / 1000);
6903 // get the half-seconds
6904 half_seconds = ((((Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 500) % 1000) / 500) * 5;
6906 // format the string
6907 sprintf(time_str,"%d.%d",full_seconds,half_seconds);
6908 gr_set_color_fast(&Color_bright);
6909 gr_string(Ho_vd_coords[gr_screen.res][MULTI_HO_X_COORD],Ho_vd_coords[gr_screen.res][MULTI_HO_Y_COORD],time_str);
6912 int multi_ho_check_values()
6916 memset(val_txt,0,255);
6918 // check against respawn settings
6919 if(Multi_ho_mission_respawn != -1){
6920 Multi_ho_respawns.get_text(val_txt);
6921 // if the value is invalid, let the user know
6922 if(atoi(val_txt) > Multi_ho_mission_respawn){
6923 memset(val_txt,0,255);
6924 sprintf(val_txt,XSTR("Warning\nRespawn count in greater than mission specified max (%d)",796),Multi_ho_mission_respawn);
6925 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6930 // check against mission time limit max
6931 Multi_ho_time_limit.get_text(val_txt);
6932 // if the value is invalid, force it to be valid
6933 if(atoi(val_txt) > MULTI_HO_MAX_TIME_LIMIT){
6934 memset(val_txt,0,255);
6935 sprintf(val_txt,XSTR("Warning\nMission time limit is greater than max allowed (%d)",797),MULTI_HO_MAX_TIME_LIMIT);
6936 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6940 // check against max observer limit
6941 Multi_ho_obs.get_text(val_txt);
6942 // if the value is invalid, force it to be valid
6943 if(atoi(val_txt) > MULTI_HO_MAX_OBS){
6944 memset(val_txt,0,255);
6945 sprintf(val_txt,XSTR("Warning\nObserver count is greater than max allowed (%d)",798),MULTI_HO_MAX_OBS);
6946 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6950 // check against furball kill limit
6951 Multi_ho_kill_limit.get_text(val_txt);
6952 // if the value is invalid, force it to be valid
6953 if(atoi(val_txt) > MULTI_HO_MAX_KILL_LIMIT){
6954 memset(val_txt,0,255);
6955 sprintf(val_txt,XSTR("Warning\nMission kill limit is greater than max allowed (%d)",799),MULTI_HO_MAX_KILL_LIMIT);
6956 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6960 // check against the token wait limit
6961 Multi_ho_voice_wait.get_text(val_txt);
6962 if(atoi(val_txt) > MULTI_HO_MAX_TOKEN_WAIT){
6963 memset(val_txt,0,255);
6964 sprintf(val_txt,XSTR("Warning\nvoice wait time is greater than max allowed (%d)",800),MULTI_HO_MAX_TOKEN_WAIT);
6965 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6969 // all values are valid
6973 void multi_ho_check_focus()
6975 // if an inputbox has been pressed (hit enter), lose its focus
6976 if (Multi_ho_respawns.pressed() || Multi_ho_time_limit.pressed() || Multi_ho_voice_wait.pressed() || Multi_ho_kill_limit.pressed() || Multi_ho_obs.pressed()) {
6977 Multi_ho_respawns.clear_focus();
6978 Multi_ho_time_limit.clear_focus();
6979 Multi_ho_voice_wait.clear_focus();
6980 Multi_ho_kill_limit.clear_focus();
6981 Multi_ho_obs.clear_focus();
6982 gamesnd_play_iface(SND_COMMIT_PRESSED);
6983 chatbox_set_focus();
6984 Multi_ho_lastframe_input = 0;
6986 } else if(!Multi_ho_lastframe_input) {
6987 // if we didn't have focus last frame
6988 if(Multi_ho_respawns.has_focus() || Multi_ho_time_limit.has_focus() || Multi_ho_kill_limit.has_focus() || Multi_ho_voice_wait.has_focus() ){
6989 chatbox_lose_focus();
6991 Multi_ho_lastframe_input = 1;
6994 // if we _did_ have focus last frame
6996 // if we no longer have focus on any of the input boxes, set the focus on the chatbox
6997 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()){
6998 chatbox_set_focus();
7000 // if the chatbox now has focus, clear all focus from our inputboxes
7001 else if (chatbox_has_focus()) {
7002 Multi_ho_respawns.clear_focus();
7003 Multi_ho_time_limit.clear_focus();
7004 Multi_ho_kill_limit.clear_focus();
7005 Multi_ho_voice_wait.clear_focus();
7007 Multi_ho_lastframe_input = 0;
7012 void multi_ho_blit_max_respawns()
7016 // if we're in campaign mode, do nothing
7017 if(Netgame.campaign_mode == MP_CAMPAIGN){
7021 // otherwise blit the max as specified by the current mission file
7022 sprintf(string,"(%d)",Multi_ho_mission_respawn);
7023 gr_set_color_fast(&Color_normal);
7024 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);
7027 void multi_ho_display_skill_level()
7029 int skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
7032 Assert((skill_level >= 0) && (skill_level < NUM_SKILL_LEVELS));
7033 if((skill_level < 0) || (skill_level >= NUM_SKILL_LEVELS)){
7037 gr_set_color_fast(&Color_bright);
7038 gr_string(Ho_st_coords[gr_screen.res][0], Ho_st_coords[gr_screen.res][1], Skill_level_names(skill_level, 1));
7041 // -------------------------------------------------------------------------------------------------------------
7043 // MULTIPLAYER JOIN SCREEN
7046 #define MULTI_JW_NUM_BUTTONS 8
7050 #define MULTI_JW_PALETTE "InterfacePalette"
7052 static const char *Multi_jw_bitmap_fname[GR_NUM_RESOLUTIONS] = {
7053 "MultiJoinWait", // GR_640
7054 "2_MultiJoinWait" // GR_1024
7057 static const char *Multi_jw_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
7058 "MultiJoinWait-M", // GR_640
7059 "2_MultiJoinWait-M" // GR_1024
7065 #define MJW_SCROLL_PLAYERS_UP 0
7066 #define MJW_SCROLL_PLAYERS_DOWN 1
7069 #define MJW_PILOT_INFO 4
7070 #define MJW_SCROLL_INFO_UP 5
7071 #define MJW_SCROLL_INFO_DOWN 6
7072 #define MJW_CANCEL 7
7074 UI_WINDOW Multi_jw_window; // the window object for the join screen
7075 int Multi_jw_bitmap; // the background bitmap
7077 // constants for coordinate lookup
7078 #define MJW_X_COORD 0
7079 #define MJW_Y_COORD 1
7080 #define MJW_W_COORD 2
7081 #define MJW_H_COORD 3
7083 ui_button_info Multi_jw_buttons[GR_NUM_RESOLUTIONS][MULTI_JW_NUM_BUTTONS] = {
7086 ui_button_info("MJW_00", 0, 50, -1, -1, 0),
7087 ui_button_info("MJW_01", 0, 87, -1, -1, 1),
7088 ui_button_info("MJW_02", 20, 219, -1, -1, 2),
7089 ui_button_info("MJW_03", 73, 219, -1, -1, 3),
7090 ui_button_info("MJW_09", 131, 213, -1, -1, 9),
7091 ui_button_info("MJW_05", 0, 398, -1, -1, 5),
7092 ui_button_info("MJW_06", 0, 435, -1, -1, 6),
7093 ui_button_info("MJW_04", 559, 411, -1, -1, 4),
7095 ui_button_info("MJW_00", 1, 24, -1, -1, 0),
7096 ui_button_info("MJW_01", 1, 66, -1, -1, 1),
7097 ui_button_info("MJW_02", 30, 244, 20, 272, 2),
7098 ui_button_info("MJW_03", 84, 244, 73, 272, 3),
7099 ui_button_info("MJW_04", 139, 242, 134, 272, 4),
7100 ui_button_info("MJW_05", 1, 406, -1, -1, 5),
7101 ui_button_info("MJW_06", 1, 447, -1, -1, 6),
7102 ui_button_info("MJW_07", 577, 428, 570, 414, 7),
7106 ui_button_info("2_MJW_00", 2, 38, -1, -1, 0),
7107 ui_button_info("2_MJW_01", 2, 106, -1, -1, 1),
7108 ui_button_info("2_MJW_02", 48, 390, 47, 435, 2),
7109 ui_button_info("2_MJW_03", 134, 390, 133, 435, 3),
7110 ui_button_info("2_MJW_04", 223, 388, 225, 435, 4),
7111 ui_button_info("2_MJW_05", 2, 649, -1, -1, 5),
7112 ui_button_info("2_MJW_06", 2, 715, -1, -1, 6),
7113 ui_button_info("2_MJW_07", 923, 685, 931, 667, 7),
7118 #define MULTI_JW_NUM_TEXT 7
7120 UI_XSTR Multi_jw_text[GR_NUM_RESOLUTIONS][MULTI_JW_NUM_TEXT] = {
7122 { "Team 1", 1308, 20, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_TEAM0].button },
7123 { "Team 2", 1309, 73, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_TEAM1].button },
7124 { "Pilot", 1310, 134, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_PILOT_INFO].button },
7125 { "Info", 1311, 134, 283, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_PILOT_INFO].button },
7126 { "Cancel", 387, 570, 414, UI_XSTR_COLOR_PINK, -1, &Multi_jw_buttons[0][MJW_CANCEL].button },
7127 { "Players", 1269, 38, 8, UI_XSTR_COLOR_GREEN, -1, NULL },
7128 { "Choose Team", 1312, 27, 231, UI_XSTR_COLOR_GREEN, -1, NULL },
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] = {
7155 int Mjw_mission_name_coords[GR_NUM_RESOLUTIONS][2] = {
7164 // squad war checkbox
7165 UI_CHECKBOX Multi_jw_sw_checkbox;
7166 const char *Multi_jw_sw_checkbox_fname[GR_NUM_RESOLUTIONS] = {
7170 int Multi_jw_sw_checkbox_coords[GR_NUM_RESOLUTIONS][2] = {
7178 int Multi_jw_sw_checkbox_text[GR_NUM_RESOLUTIONS][2] = {
7188 // player list control thingie defs
7189 #define MULTI_JW_PLIST_MAX_DISPLAY 19
7190 int Multi_jw_plist_select_flag; // indicates whether we currently have a selected player
7191 short Multi_jw_plist_select_id; // id of the current selected player
7192 UI_BUTTON Multi_jw_plist_select_button; // for selecting a player
7194 int Multi_jw_should_show_popup = 0;
7196 // LOCAL function definitions
7197 void multi_jw_check_buttons();
7198 void multi_jw_button_pressed(int n);
7199 void multi_jw_do_netstuff();
7200 void multi_jw_scroll_players_up();
7201 void multi_jw_scroll_players_down();
7202 void multi_jw_plist_process();
7203 void multi_jw_plist_blit_normal();
7204 void multi_jw_plist_blit_team();
7205 short multi_jw_get_mouse_id();
7207 void multi_game_client_setup_init()
7211 // create the interface window
7212 Multi_jw_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
7213 Multi_jw_window.set_mask_bmap(Multi_jw_bitmap_mask_fname[gr_screen.res]);
7215 // load the background bitmap
7216 Multi_jw_bitmap = bm_load(Multi_jw_bitmap_fname[gr_screen.res]);
7217 if(Multi_jw_bitmap < 0){
7218 // we failed to load the bitmap - this is very bad
7222 // initialize the player list data
7223 Multi_jw_plist_select_flag = 0;
7224 Multi_jw_plist_select_id = -1;
7226 // kill any old instances of the chatbox and create a new one
7228 chatbox_create(CHATBOX_FLAG_BIG | CHATBOX_FLAG_DRAW_BOX | CHATBOX_FLAG_BUTTONS);
7230 // initialize the common notification messaging
7231 multi_common_notify_init();
7233 // initialize the common mission info display area.
7234 multi_common_set_text("");
7236 // use the common interface palette
7237 multi_common_set_palette();
7239 // create the interface buttons
7240 for(idx=0; idx<MULTI_JW_NUM_BUTTONS; idx++){
7241 // create the object
7242 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);
7244 // set the sound to play when highlighted
7245 Multi_jw_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
7247 // set the ani for the button
7248 Multi_jw_buttons[gr_screen.res][idx].button.set_bmaps(Multi_jw_buttons[gr_screen.res][idx].filename);
7251 Multi_jw_buttons[gr_screen.res][idx].button.link_hotspot(Multi_jw_buttons[gr_screen.res][idx].hotspot);
7254 // if this is a PXO game, enable the squadwar checkbox
7255 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);
7256 Multi_jw_sw_checkbox.set_bmaps(Multi_jw_sw_checkbox_fname[gr_screen.res], 6, 0);
7257 Multi_jw_sw_checkbox.disable();
7258 if(!MULTI_IS_TRACKER_GAME){
7259 Multi_jw_sw_checkbox.hide();
7264 for(idx=0; idx<MULTI_JW_NUM_TEXT; idx++){
7265 Multi_jw_window.add_XSTR(&Multi_jw_text[gr_screen.res][idx]);
7269 // create the player select list button and hide it
7270 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);
7271 Multi_jw_plist_select_button.hide();
7274 Multi_jw_buttons[gr_screen.res][MJW_CANCEL].button.set_hotkey(SDLK_ESCAPE);
7276 // remove campaign flags
7277 Game_mode &= ~(GM_CAMPAIGN_MODE);
7279 // tell the server we have finished joining
7280 Net_player->state = NETPLAYER_STATE_JOINED;
7281 send_netplayer_update_packet();
7284 ml_printf(NOX("Joined netgame %s"), Netgame.name);
7286 // send any appropriate files
7287 multi_data_send_my_junk();
7290 void multi_game_client_setup_do_frame()
7293 int k = chatbox_process();
7294 char mission_text[255];
7295 k = Multi_jw_window.process(k,0);
7297 Multi_jw_should_show_popup = 0;
7299 // process any button clicks
7300 multi_jw_check_buttons();
7302 // do any network related stuff
7303 multi_jw_do_netstuff();
7305 // draw the background, etc
7307 GR_MAYBE_CLEAR_RES(Multi_jw_bitmap);
7308 if(Multi_jw_bitmap != -1){
7309 gr_set_bitmap(Multi_jw_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7313 // if we're not in team vs. team mode, don't draw the team buttons
7314 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
7315 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.hide();
7316 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.hide();
7317 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.disable();
7318 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.disable();
7320 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.enable();
7321 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.enable();
7322 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.unhide();
7323 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.unhide();
7326 if(MULTI_IS_TRACKER_GAME){
7327 // maybe check the squadwar button
7328 if(Netgame.type_flags & NG_TYPE_SW){
7329 Multi_jw_sw_checkbox.set_state(1);
7330 gr_set_color_fast(&Color_bright);
7332 Multi_jw_sw_checkbox.set_state(0);
7333 gr_set_color_fast(&Color_normal);
7336 gr_string(Multi_jw_sw_checkbox_text[gr_screen.res][0], Multi_jw_sw_checkbox_text[gr_screen.res][1], "SquadWar");
7339 // draw the UI window
7340 Multi_jw_window.draw();
7342 // process and display the player list
7343 // NOTE : this must be done before the buttons are checked to insure that a player hasn't dropped
7344 multi_jw_plist_process();
7345 if(Netgame.type_flags & NG_TYPE_TEAM){
7346 multi_jw_plist_blit_team();
7348 multi_jw_plist_blit_normal();
7351 // display any text in the info area
7352 multi_common_render_text();
7354 // display any pending notification messages
7355 multi_common_notify_do();
7357 // blit the mission filename if possible
7358 if(Netgame.campaign_mode){
7359 if(strlen(Netgame.campaign_name) > 0){
7360 strcpy(mission_text,Netgame.campaign_name);
7362 if(strlen(Netgame.title) > 0){
7363 strcat(mission_text,", ");
7364 strcat(mission_text,Netgame.title);
7367 gr_set_color_fast(&Color_bright_white);
7368 gr_string(Mjw_mission_name_coords[gr_screen.res][MJW_X_COORD],Mjw_mission_name_coords[gr_screen.res][MJW_Y_COORD],mission_text);
7371 if(strlen(Netgame.mission_name) > 0){
7372 strcpy(mission_text,Netgame.mission_name);
7374 if(strlen(Netgame.title) > 0){
7375 strcat(mission_text,", ");
7376 strcat(mission_text,Netgame.title);
7379 gr_set_color_fast(&Color_bright_white);
7380 gr_string(Mjw_mission_name_coords[gr_screen.res][MJW_X_COORD],Mjw_mission_name_coords[gr_screen.res][MJW_Y_COORD],mission_text);
7384 // process and show the chatbox thingie
7388 Multi_jw_window.draw_tooltip();
7390 // display the voice status indicator
7391 multi_common_voice_display_status();
7396 // if we're supposed to be displaying a pilot info popup
7397 if(Multi_jw_should_show_popup){
7398 player_index = find_player_id(Multi_jw_plist_select_id);
7399 if(player_index != -1){
7400 multi_pinfo_popup(&Net_players[player_index]);
7405 void multi_game_client_setup_close()
7407 // unload any bitmaps
7408 if(!bm_unload(Multi_jw_bitmap)){
7409 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_jw_bitmap_fname[gr_screen.res]));
7412 // destroy the chatbox
7415 // destroy the UI_WINDOW
7416 Multi_jw_window.destroy();
7419 if(Netgame.game_state == NETGAME_STATE_MISSION_SYNC){
7420 gamesnd_play_iface(SND_COMMIT_PRESSED);
7425 void multi_jw_check_buttons()
7428 for(idx=0;idx<MULTI_JW_NUM_BUTTONS;idx++){
7429 // we only really need to check for one button pressed at a time, so we can break after
7431 if(Multi_jw_buttons[gr_screen.res][idx].button.pressed()){
7432 multi_jw_button_pressed(idx);
7438 void multi_jw_button_pressed(int n)
7442 gamesnd_play_iface(SND_USER_SELECT);
7443 multi_quit_game(PROMPT_CLIENT);
7445 case MJW_SCROLL_PLAYERS_UP:
7446 multi_jw_scroll_players_up();
7448 case MJW_SCROLL_PLAYERS_DOWN:
7449 multi_jw_scroll_players_down();
7451 case MJW_SCROLL_INFO_UP:
7452 multi_common_scroll_text_up();
7454 case MJW_SCROLL_INFO_DOWN:
7455 multi_common_scroll_text_down();
7458 // request to set myself to team 0
7460 gamesnd_play_iface(SND_USER_SELECT);
7461 multi_team_set_team(Net_player,0);
7464 // request to set myself to team 1
7466 gamesnd_play_iface(SND_USER_SELECT);
7467 multi_team_set_team(Net_player,1);
7471 case MJW_PILOT_INFO:
7472 Multi_jw_should_show_popup = 1;
7476 multi_common_add_notify(XSTR("Not implemented yet!",760));
7481 // do stuff like pinging servers, sending out requests, etc
7482 void multi_jw_do_netstuff()
7486 void multi_jw_scroll_players_up()
7488 gamesnd_play_iface(SND_GENERAL_FAIL);
7491 // scroll down through the player list
7492 void multi_jw_scroll_players_down()
7494 gamesnd_play_iface(SND_GENERAL_FAIL);
7497 void multi_jw_plist_process()
7499 int test_count,player_index,idx;
7501 // first determine if there are 0 players in the game. This should never happen since the host is _always_ in the game
7503 for(idx=0;idx<MAX_PLAYERS;idx++){
7504 // count anyone except the standalone server (if applicable)
7505 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7509 if(test_count <= 0){
7513 // if we had a selected item but that player has left, select myself instead
7514 if(Multi_jw_plist_select_flag){
7515 player_index = find_player_id(Multi_jw_plist_select_id);
7516 if(player_index == -1){
7517 Multi_jw_plist_select_id = Net_player->player_id;
7520 Multi_jw_plist_select_flag = 1;
7521 Multi_jw_plist_select_id = Net_player->player_id;
7524 // if the player has clicked somewhere in the player list area
7525 if(Multi_jw_plist_select_button.pressed()){
7529 player_id = multi_jw_get_mouse_id();
7530 player_index = find_player_id(player_id);
7531 if(player_index != -1){
7532 Multi_jw_plist_select_id = player_id;
7533 Multi_jw_plist_select_flag = 1;
7538 void multi_jw_plist_blit_normal()
7541 char str[CALLSIGN_LEN+1];
7542 int y_start = Mjw_players_coords[gr_screen.res][MJW_Y_COORD];
7545 // display all the players
7546 for(idx=0;idx<MAX_PLAYERS;idx++){
7547 // reset total offset
7550 // count anyone except the standalone server (if applicable)
7551 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7552 // highlight him if he's the host
7553 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7554 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7555 gr_set_color_fast(&Color_text_active_hi);
7557 gr_set_color_fast(&Color_bright);
7560 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7561 gr_set_color_fast(&Color_text_active);
7563 gr_set_color_fast(&Color_text_normal);
7567 // optionally draw his CD status
7568 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7569 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7570 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7572 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7575 // make sure the string will fit, then display it
7576 strcpy(str,Net_players[idx].player->callsign);
7577 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
7580 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7581 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7588 void multi_jw_plist_blit_team()
7591 char str[CALLSIGN_LEN+1];
7592 int y_start = Mjw_players_coords[gr_screen.res][MJW_Y_COORD];
7595 // always blit the proper team button based on _my_ team status
7596 if(Net_player->p_info.team == 0){
7597 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.draw_forced(2);
7599 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.draw_forced(2);
7602 // display all the red players first
7603 for(idx=0;idx<MAX_PLAYERS;idx++){
7604 // reset total offset
7607 // count anyone except the standalone server (if applicable)
7608 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
7609 // highlight him if he's the host
7610 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7611 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7612 gr_set_color_fast(&Color_text_active_hi);
7614 gr_set_color_fast(&Color_bright);
7617 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7618 gr_set_color_fast(&Color_text_active);
7620 gr_set_color_fast(&Color_text_normal);
7624 // optionally draw his CD status
7625 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7626 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7627 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7629 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7632 // blit the red team indicator
7633 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
7634 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
7635 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7636 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7638 total_offset += Multi_common_icon_dims[MICON_TEAM0_SELECT][0] + 1;
7641 if(Multi_common_icons[MICON_TEAM0] != -1){
7642 gr_set_bitmap(Multi_common_icons[MICON_TEAM0], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7643 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7645 total_offset += Multi_common_icon_dims[MICON_TEAM0][0] + 1;
7649 // make sure the string will fit
7650 strcpy(str,Net_players[idx].player->callsign);
7651 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7653 // display him in the correct half of the list depending on his team
7654 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7659 // display all the green players next
7660 for(idx=0;idx<MAX_PLAYERS;idx++){
7661 // reset total offset
7664 // count anyone except the standalone server (if applicable)
7665 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
7666 // highlight him if he's the host
7667 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7668 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7669 gr_set_color_fast(&Color_text_active_hi);
7671 gr_set_color_fast(&Color_bright);
7674 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7675 gr_set_color_fast(&Color_text_active);
7677 gr_set_color_fast(&Color_text_normal);
7681 // optionally draw his CD status
7682 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7683 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7684 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7686 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7689 // blit the red team indicator
7690 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
7691 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
7692 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7693 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7695 total_offset += Multi_common_icon_dims[MICON_TEAM1_SELECT][0] + 1;
7698 if(Multi_common_icons[MICON_TEAM1] != -1){
7699 gr_set_bitmap(Multi_common_icons[MICON_TEAM1], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7700 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7702 total_offset += Multi_common_icon_dims[MICON_TEAM1][0] + 1;
7706 // make sure the string will fit
7707 strcpy(str,Net_players[idx].player->callsign);
7708 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
7711 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7713 // display him in the correct half of the list depending on his team
7714 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7720 void multi_jw_handle_join(net_player *pl)
7722 // for now just play a bloop sound
7723 gamesnd_play_iface(SND_ICON_DROP_ON_WING);
7726 short multi_jw_get_mouse_id()
7728 // determine where he clicked (y pixel value)
7730 Multi_jw_plist_select_button.get_mouse_pos(NULL,&y);
7732 // select things a little differently if we're in team vs. team or non-team vs. team mode
7734 if(Netgame.type_flags & NG_TYPE_TEAM){
7735 int player_index = -1;
7737 // look through all of team red first
7738 for(idx=0;idx<MAX_PLAYERS;idx++){
7739 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
7742 // if this is the _nth_ guy
7750 // if we still haven't found him yet, look through the green team
7751 if(player_index == -1){
7752 for(idx=0;idx<MAX_PLAYERS;idx++){
7753 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
7755 // if this is the _nth_ guy
7763 if(player_index != -1){
7764 return Net_players[idx].player_id;
7767 // select the nth active player if possible, disregarding the standalone server
7768 for(idx=0;idx<MAX_PLAYERS;idx++){
7769 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7772 // if this is the _nth_ guy
7774 return Net_players[idx].player_id;
7785 // -------------------------------------------------------------------------------------------------------------
7787 // MULTIPLAYER WAIT/SYNCH SCREEN
7790 #define MULTI_SYNC_HOST_COUNT 4 // host uses 4 buttons (and sometimes 5)
7791 #define MULTI_SYNC_CLIENT_COUNT 3 // client only uses 3 buttons
7793 const char *Multi_sync_bitmap_fname[GR_NUM_RESOLUTIONS] = {
7794 "MultiSynch", // GR_640
7795 "2_MultiSynch" // GR_1024
7798 const char *Multi_sync_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
7799 "MultiSynch-M", // GR_640
7800 "2_MultiSynch-M" // GR_1024
7805 // constants for coordinate lookup
7806 #define MS_X_COORD 0
7807 #define MS_Y_COORD 1
7808 #define MS_W_COORD 2
7809 #define MS_H_COORD 3
7811 UI_WINDOW Multi_sync_window; // the window object for the join screen
7812 int Multi_sync_button_count; // the # of buttons to use for this instance of mission sync
7813 int Multi_sync_bitmap; // the background bitmap
7816 #define MULTI_SYNC_NUM_BUTTONS 5
7817 #define MS_SCROLL_INFO_UP 0
7818 #define MS_SCROLL_INFO_DOWN 1
7822 ui_button_info Multi_sync_buttons[GR_NUM_RESOLUTIONS][MULTI_SYNC_NUM_BUTTONS] = {
7825 ui_button_info("MS_00", 0, 396, -1, -1, 0),
7826 ui_button_info("MS_01", 0, 435, -1, -1, 1),
7827 ui_button_info("MS_02", 496, 411, -1, -1, 2),
7828 ui_button_info("MS_04", 436, 403, -1, -1, 4),
7829 ui_button_info("MS_03", 556, 398, -1, -1, 3),
7831 ui_button_info("MS_00", 1, 404, -1, -1, 0),
7832 ui_button_info("MS_01", 1, 446, -1, -1, 1),
7833 ui_button_info("MS_03", 518, 426, 519, 416, 3),
7834 ui_button_info("MS_02", 469, 426, 479, 416, 2),
7835 ui_button_info("MS_04", 571, 420, 577, 416, 4),
7839 ui_button_info("2_MS_00", 2, 647, -1, -1, 0),
7840 ui_button_info("2_MS_01", 2, 713, -1, -1, 1),
7841 ui_button_info("2_MS_03", 829, 682, 831, 667, 3),
7842 ui_button_info("2_MS_02", 751, 682, 766, 667, 2),
7843 ui_button_info("2_MS_04", 914, 672, 924, 667, 4),
7849 #define MULTI_SYNC_NUM_TEXT 5
7851 #define MST_LAUNCH 2
7852 UI_XSTR Multi_sync_text[GR_NUM_RESOLUTIONS][MULTI_SYNC_NUM_TEXT] = {
7854 { "Kick", 1266, 479, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_KICK].button },
7855 { "Cancel", 387, 519, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_CANCEL].button },
7856 { "Launch", 801, 577, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_LAUNCH].button },
7857 { "Players", 1269, 23, 133, UI_XSTR_COLOR_GREEN, -1, NULL },
7858 { "Status", 1304, 228, 133, UI_XSTR_COLOR_GREEN, -1, NULL }
7861 { "Kick", 1266, 766, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_KICK].button },
7862 { "Cancel", 387, 831, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_CANCEL].button },
7863 { "Launch", 801, 924, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_LAUNCH].button },
7864 { "Players", 1269, 38, 214, UI_XSTR_COLOR_GREEN, -1, NULL },
7865 { "Status", 1304, 366, 214, UI_XSTR_COLOR_GREEN, -1, NULL }
7871 int Ms_status_coords[GR_NUM_RESOLUTIONS][4] = {
7880 // player status coords
7881 int Ms_status2_coords[GR_NUM_RESOLUTIONS][4] = {
7890 int Ms_cd_icon_offset[GR_NUM_RESOLUTIONS] = {
7895 int Ms_team_icon_offset[GR_NUM_RESOLUTIONS] = {
7900 // player currently selected, index into Net_players[]
7901 int Multi_sync_player_select = -1;
7903 // player list control thingie defs
7904 #define MULTI_SYNC_PLIST_MAX_DISPLAY 15
7905 int Multi_sync_plist_start; // where to start displaying from
7906 int Multi_sync_plist_count; // how many we have
7908 // list select button
7909 UI_BUTTON Multi_sync_plist_button;
7911 int Multi_sync_mode = -1;
7913 #define MULTI_SYNC_COUNTDOWN_TIME 5 // in seconds
7914 float Multi_sync_countdown_timer;
7915 int Multi_sync_countdown = -1;
7917 int Multi_launch_button_created;
7920 // countdown animation timer
7921 const char* Multi_sync_countdown_fname[GR_NUM_RESOLUTIONS] = {
7923 "2_Count" // GR_1024
7926 int Multi_sync_countdown_coords[GR_NUM_RESOLUTIONS][2] = {
7937 anim *Multi_sync_countdown_anim = NULL;
7938 anim_instance *Multi_sync_countdown_instance = NULL;
7941 // PREBRIEFING STUFF
7942 // syncing flags used by the server
7943 int Mission_sync_flags = 0;
7944 #define MS_FLAG_SENT_FILESIG (1<<0) // sent filesig requests
7945 #define MS_FLAG_SENT_LOAD (1<<1) // sent load packets
7946 #define MS_FLAG_PUSHED_BRIEFING (1<<2) // pushed everyone else into the briefing
7947 #define MS_FLAG_POST_DATA (1<<3) // sent the post data block
7948 #define MS_FLAG_WSS_SLOTS (1<<4) // all players have received wss slot data
7949 #define MS_FLAG_PSETTINGS (1<<5) // send the player settings packet
7950 #define MS_FLAG_MT_STATS_START (1<<6) // server has started getting player stats from the tracker
7951 #define MS_FLAG_MT_STATS_DONE (1<<7) // server has finished getting player stats from the tracker (success or fail)
7952 #define MS_FLAG_TS_SLOTS (1<<8) // team/ship slots have been sent
7953 #define MS_FLAG_DATA_DONE (1<<9) // done transferring all necessary data
7954 #define MS_FLAG_CAMP_DONE (1<<10) // send campaign pool/goal/event stuff
7956 // POSTBRIEFING STUFF
7957 int Multi_state_timestamp;
7958 int Multi_sync_launch_pressed;
7960 // LOCAL function definitions
7961 void multi_sync_check_buttons();
7962 void multi_sync_button_pressed(int n);
7963 void multi_sync_scroll_info_up();
7964 void multi_sync_scroll_info_down();
7965 void multi_sync_display_name(const char *name, int index, int np_index); // display info on the left hand portion of the status window thingie
7966 void multi_sync_display_status(const char *status, int index); // display info on the right hand portion of the status window thingie
7967 void multi_sync_force_start_pre();
7968 void multi_sync_force_start_post();
7969 void multi_sync_launch();
7970 void multi_sync_create_launch_button();
7971 void multi_sync_blit_screen_all();
7972 void multi_sync_handle_plist();
7974 void multi_sync_common_init();
7975 void multi_sync_common_do();
7976 void multi_sync_common_close();
7978 void multi_sync_pre_init();
7979 void multi_sync_pre_do();
7980 void multi_sync_pre_close();
7982 void multi_sync_post_init();
7983 void multi_sync_post_do();
7984 void multi_sync_post_close();
7989 // perform the correct init functions
7990 void multi_sync_init()
7992 Multi_sync_countdown = -1;
7996 // reset all timestamp
7997 multi_reset_timestamps();
7999 extern int Player_multi_died_check;
8000 Player_multi_died_check = -1;
8002 if(!(Game_mode & GM_STANDALONE_SERVER)){
8003 multi_sync_common_init();
8006 switch(Multi_sync_mode){
8007 case MULTI_SYNC_PRE_BRIEFING:
8008 multi_sync_pre_init();
8010 case MULTI_SYNC_POST_BRIEFING:
8011 multi_sync_post_init();
8013 case MULTI_SYNC_INGAME:
8014 multi_ingame_sync_init();
8019 // perform the correct do frame functions
8020 void multi_sync_do()
8022 if(!(Game_mode & GM_STANDALONE_SERVER)){
8023 multi_sync_common_do();
8026 // if the netgame is ending, don't do any sync processing
8027 if(multi_endgame_ending()){
8031 // process appropriateliy
8032 switch(Multi_sync_mode){
8033 case MULTI_SYNC_PRE_BRIEFING:
8034 multi_sync_pre_do();
8036 case MULTI_SYNC_POST_BRIEFING:
8037 multi_sync_post_do();
8039 case MULTI_SYNC_INGAME:
8040 multi_ingame_sync_do();
8043 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8044 if(Multi_sync_bitmap != -1){
8045 gr_set_bitmap(Multi_sync_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8048 Multi_sync_window.draw();
8050 multi_sync_blit_screen_all();
8057 // perform the correct close functions
8058 void multi_sync_close()
8060 switch(Multi_sync_mode){
8061 case MULTI_SYNC_PRE_BRIEFING:
8062 multi_sync_pre_close();
8064 case MULTI_SYNC_POST_BRIEFING:
8065 multi_sync_post_close();
8067 case MULTI_SYNC_INGAME:
8068 multi_ingame_sync_close();
8072 if(!(Game_mode & GM_STANDALONE_SERVER)){
8073 multi_sync_common_close();
8077 const char *multi_sync_tooltip_handler(const char *str)
8079 if (!stricmp(str, NOX("@launch"))) {
8080 if (Multi_launch_button_created){
8081 return XSTR("Launch",801);
8088 void multi_sync_common_init()
8092 // create the interface window
8093 Multi_sync_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
8094 Multi_sync_window.set_mask_bmap(Multi_sync_bitmap_mask_fname[gr_screen.res]);
8095 Multi_sync_window.tooltip_handler = multi_sync_tooltip_handler;
8097 // load the background bitmap
8098 Multi_sync_bitmap = bm_load(Multi_sync_bitmap_fname[gr_screen.res]);
8099 if (Multi_sync_bitmap < 0) {
8100 // we failed to load the bitmap - this is very bad
8104 // initialize the player list data
8105 Multi_sync_plist_start = 0;
8106 Multi_sync_plist_count = 1; // we can pretty safely assume that there's one player in the game - me.
8108 Multi_launch_button_created = 0;
8110 // create the chatbox thingie (shouldn't be necesary to do this, but we'll put it in for good measure)
8113 // force the chatbox to be small
8114 chatbox_force_small();
8116 // initialize the common notification messaging
8117 multi_common_notify_init();
8119 // initialize the common mission info display area.
8120 multi_common_set_text("");
8122 // use the common interface palette
8123 multi_common_set_palette();
8125 // don't select any player yet.
8126 Multi_sync_player_select = -1;
8128 // determine how many of the 5 buttons to create
8129 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8130 Multi_sync_button_count = MULTI_SYNC_HOST_COUNT;
8132 Multi_sync_button_count = MULTI_SYNC_CLIENT_COUNT;
8134 // create the interface buttons
8135 for(idx=0; idx<Multi_sync_button_count; idx++){
8136 // create the object
8137 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);
8139 // set the sound to play when highlighted
8140 Multi_sync_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
8142 // set the ani for the button
8143 // this wierdness is necessary because cancel and kick buttons aren't drawn on the background bitmap,
8144 // so we have to load in frame 0, too (the file should exist)
8145 if ((idx == MS_CANCEL) || (idx == MS_KICK) || (idx == MS_LAUNCH)) {
8146 Multi_sync_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sync_buttons[gr_screen.res][idx].filename, 3, 0);
8148 Multi_sync_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sync_buttons[gr_screen.res][idx].filename);
8152 Multi_sync_buttons[gr_screen.res][idx].button.link_hotspot(Multi_sync_buttons[gr_screen.res][idx].hotspot);
8157 for(idx=0; idx<MULTI_SYNC_NUM_TEXT; idx++) {
8158 // don't create the "launch" button text just yet
8159 if(idx == MST_LAUNCH) {
8162 // multiplayer clients should ignore the kick button
8163 if(!MULTIPLAYER_MASTER && !MULTIPLAYER_HOST && (idx == MST_KICK)) {
8167 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(SDLK_ESCAPE);
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, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
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, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
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(const 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], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
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], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
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], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
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], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
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], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
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(const 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+SDLK_RETURN);
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 const char *Multi_pwd_bitmap_fname[GR_NUM_RESOLUTIONS] = {
9605 "Password", // GR_640
9606 "2_Password" // GR_1024
9609 static const 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 3
9669 UI_XSTR Multi_pwd_text[GR_NUM_RESOLUTIONS][MULTI_PWD_NUM_TEXT] = {
9671 { "Cancel", 387, 400, 141, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[0][MPWD_CANCEL].button},
9672 { "Commit", 1062, 455, 141, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[0][MPWD_COMMIT].button},
9673 { "Enter Password", 1332, 149, 92, UI_XSTR_COLOR_GREEN, -1, NULL},
9676 { "Cancel", 387, 649, 225, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[1][MPWD_CANCEL].button},
9677 { "Commit", 1062, 736, 225, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[1][MPWD_COMMIT].button},
9678 { "Enter Password", 1332, 239, 148, UI_XSTR_COLOR_GREEN, -1, NULL},
9683 // initialize all graphics, etc
9684 void multi_passwd_init()
9688 // store the background as it currently is
9689 Multi_passwd_background = gr_save_screen();
9691 // create the interface window
9692 Multi_pwd_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
9693 Multi_pwd_window.set_mask_bmap(Multi_pwd_bitmap_mask_fname[gr_screen.res]);
9695 // load the background bitmap
9696 Multi_pwd_bitmap = bm_load(Multi_pwd_bitmap_fname[gr_screen.res]);
9697 if(Multi_pwd_bitmap < 0){
9698 // we failed to load the bitmap - this is very bad
9702 // create the interface buttons
9703 for(idx=0; idx<MULTI_PWD_NUM_BUTTONS; idx++){
9704 // create the object
9705 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);
9707 // set the sound to play when highlighted
9708 Multi_pwd_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
9710 // set the ani for the button
9711 Multi_pwd_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pwd_buttons[gr_screen.res][idx].filename);
9714 Multi_pwd_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pwd_buttons[gr_screen.res][idx].hotspot);
9719 for(idx=0; idx<MULTI_PWD_NUM_TEXT; idx++){
9720 Multi_pwd_window.add_XSTR(&Multi_pwd_text[gr_screen.res][idx]);
9724 // create the password input box
9725 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);
9726 Multi_pwd_passwd.set_focus();
9728 // link the enter key to ACCEPT
9729 Multi_pwd_buttons[gr_screen.res][MPWD_COMMIT].button.set_hotkey(SDLK_RETURN);
9731 Multi_passwd_done = -1;
9732 Multi_passwd_running = 1;
9735 // close down all graphics, etc
9736 void multi_passwd_close()
9738 // unload any bitmaps
9739 bm_release(Multi_pwd_bitmap);
9741 // destroy the UI_WINDOW
9742 Multi_pwd_window.destroy();
9744 // free up the saved background screen
9745 if(Multi_passwd_background >= 0){
9746 gr_free_screen(Multi_passwd_background);
9747 Multi_passwd_background = -1;
9750 Multi_passwd_running = 0;
9753 // process any button pressed
9754 void multi_passwd_process_buttons()
9756 // if the accept button was pressed
9757 if(Multi_pwd_buttons[gr_screen.res][MPWD_COMMIT].button.pressed()){
9758 gamesnd_play_iface(SND_USER_SELECT);
9759 Multi_passwd_done = 1;
9762 // if the cancel button was pressed
9763 if(Multi_pwd_buttons[gr_screen.res][MPWD_CANCEL].button.pressed()){
9764 gamesnd_play_iface(SND_USER_SELECT);
9765 Multi_passwd_done = 0;
9769 // run the passwd popup
9770 void multi_passwd_do(char *passwd)
9774 while(Multi_passwd_done == -1){
9775 // set frametime and run background stuff
9776 game_set_frametime(-1);
9777 game_do_state_common(gameseq_get_state());
9779 k = Multi_pwd_window.process();
9781 // process any keypresses
9784 // set this to indicate the user has cancelled for one reason or another
9785 Multi_passwd_done = 0;
9789 // if the input box text has changed
9790 if(Multi_pwd_passwd.changed()){
9792 Multi_pwd_passwd.get_text(passwd);
9795 // process any button pressed
9796 multi_passwd_process_buttons();
9798 // draw the background, etc
9801 if(Multi_passwd_background >= 0){
9802 gr_restore_screen(Multi_passwd_background);
9804 gr_set_bitmap(Multi_pwd_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
9806 Multi_pwd_window.draw();
9813 // bring up the password string popup, fill in passwd (return 1 if accept was pressed, 0 if cancel was pressed)
9814 int multi_passwd_popup(char *passwd)
9816 // if the popup is already running for some reason, don't do anything
9817 if(Multi_passwd_running){
9821 // initialize all graphics
9822 multi_passwd_init();
9825 multi_passwd_do(passwd);
9827 // shut everything down
9828 multi_passwd_close();
9830 return Multi_passwd_done;