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.11 2005/03/29 02:18:47 taylor
19 * Various 64-bit platform fixes
20 * Fix compiler errors with MAKE_FS1 and fix gr_set_bitmap() too
21 * Make sure that turrets can fire at asteroids for FS1 (needed for a couple missions)
22 * Streaming audio support (big thanks to Pierre Willenbrock!!)
23 * Removed dependance on strings.tbl for FS1 since we don't actually need it now
25 * Revision 1.10 2004/09/20 01:31:44 theoddone33
28 * Revision 1.9 2004/07/04 11:39:06 taylor
29 * fix missing debrief text, crash on exit, path separator's, warning fixes, no GR_SOFT
31 * Revision 1.8 2003/05/25 02:30:43 taylor
34 * Revision 1.7 2002/06/09 04:41:24 relnev
35 * added copyright header
37 * Revision 1.6 2002/06/02 06:02:59 relnev
40 * Revision 1.5 2002/06/02 00:31:35 relnev
41 * implemented osregistry
43 * Revision 1.4 2002/06/01 07:12:33 relnev
44 * a few NDEBUG updates.
46 * removed a few warnings.
48 * Revision 1.3 2002/05/26 20:49:54 theoddone33
51 * Revision 1.2 2002/05/07 03:16:47 theoddone33
52 * The Great Newline Fix
54 * Revision 1.1.1.1 2002/05/03 03:28:10 root
58 * 94 6/16/00 3:16p Jefff
59 * sim of the year dvd version changes, a few german soty localization
62 * 93 10/14/99 2:51p Jefff
65 * 92 10/13/99 3:50p Jefff
66 * fixed unnumbered XSTRs
68 * 91 9/15/99 1:45a Dave
69 * Don't init joystick on standalone. Fixed campaign mode on standalone.
70 * Fixed no-score-report problem in TvT
72 * 90 9/14/99 12:51a Jefff
75 * 89 9/13/99 4:52p Dave
78 * 88 9/13/99 11:30a Dave
79 * Added checkboxes and functionality for disabling PXO banners as well as
80 * disabling d3d zbuffer biasing.
82 * 87 9/12/99 10:06p Jefff
83 * changed instances of "Squad War" to "SquadWar"
85 * 86 9/03/99 1:32a Dave
86 * CD checking by act. Added support to play 2 cutscenes in a row
87 * seamlessly. Fixed super low level cfile bug related to files in the
88 * root directory of a CD. Added cheat code to set campaign mission # in
91 * 85 9/01/99 10:49p Dave
92 * Added nice SquadWar checkbox to the client join wait screen.
94 * 84 8/30/99 2:49p Jefff
96 * 83 8/26/99 8:49p Jefff
97 * Updated medals screen and about everything that ever touches medals in
98 * one way or another. Sheesh.
100 * 82 8/25/99 4:38p Dave
101 * Updated PXO stuff. Make squad war report stuff much more nicely.
103 * 81 8/20/99 2:09p Dave
104 * PXO banner cycling.
106 * 80 8/20/99 10:06a Jefff
107 * removed closed/rstricted buttons from multi start screen
109 * 79 8/18/99 11:30a Jefff
111 * 78 8/18/99 10:38a Jefff
113 * 77 8/16/99 4:06p Dave
114 * Big honking checkin.
116 * 76 8/16/99 1:08p Jefff
117 * added sounds to a few controls, made input boxes lose focus on ENTER
119 * 75 8/16/99 9:52a Jefff
120 * fixed bitmap loading on buttons in multi-sync screen
122 * 74 8/11/99 5:54p Dave
123 * Fixed collision problem. Fixed standalone ghost problem.
125 * 73 8/10/99 4:35p Jefff
126 * fixed hi-res coords
128 * 72 8/06/99 12:29a Dave
129 * Multiple bug fixes.
131 * 71 8/05/99 3:13p Jasenw
132 * tweaked some text placement coords.
134 * 70 8/04/99 1:38p Jefff
135 * moved some text in multi join wait
137 * 69 8/03/99 12:45p Dave
140 * 68 7/25/99 5:17p Jefff
141 * campaign descriptions show up on multicreate screen
143 * 67 7/20/99 1:49p Dave
144 * Peter Drake build. Fixed some release build warnings.
146 * 66 7/19/99 2:13p Dave
147 * Added some new strings for Heiko.
149 * 65 7/15/99 9:20a Andsager
150 * FS2_DEMO initial checkin
152 * 64 7/08/99 10:53a Dave
153 * New multiplayer interpolation scheme. Not 100% done yet, but still
154 * better than the old way.
156 * 63 6/30/99 10:49a Jasenw
157 * Fixed coords for new launch countdown ani
159 * 62 6/29/99 7:39p Dave
160 * Lots of small bug fixes.
162 * 61 6/25/99 11:59a Dave
163 * Multi options screen.
165 * 60 6/09/99 2:17p Dave
166 * Fixed up pleasewait bitmap rendering.
168 * 59 6/05/99 3:42p Dave
169 * New multi sync screen.
171 * 58 6/01/99 6:07p Dave
172 * New loading/pause/please wait bar.
174 * 57 5/21/99 6:45p Dave
175 * Sped up ui loading a bit. Sped up localization disk access stuff. Multi
176 * start game screen, multi password, and multi pxo-help screen.
178 * 56 5/06/99 11:10a Dave
179 * Fixed coord on multi create screen.
181 * 55 5/04/99 6:38p Dave
182 * Finished multi join-wait screen.
184 * 54 5/04/99 5:20p Dave
185 * Fixed up multiplayer join screen and host options screen. Should both
188 * 53 5/03/99 11:04p Dave
189 * Most of the way done with the multi join screen.
191 * 52 5/03/99 8:32p Dave
192 * New version of multi host options screen.
194 * 51 4/29/99 2:15p Neilk
195 * slider2 code got modified; changed parameters for create
197 * 50 4/25/99 3:02p Dave
198 * Build defines for the E3 build.
200 * 49 4/21/99 6:15p Dave
201 * Did some serious housecleaning in the beam code. Made it ready to go
202 * for anti-fighter "pulse" weapons. Fixed collision pair creation. Added
203 * a handy macro for recalculating collision pairs for a given object.
205 * 48 4/16/99 5:27p Neilk
206 * added slider support and hir res for multi_create
208 * 47 4/14/99 6:37p Dave
209 * Fixed scroll button bug on host create screen.
211 * 46 4/14/99 5:28p Dave
214 * 45 4/12/99 10:07p Dave
215 * Made network startup more forgiving. Added checkmarks to dogfight
216 * screen for players who hit commit.
218 * 44 4/09/99 2:21p Dave
219 * Multiplayer beta stuff. CD checking.
221 * 43 4/08/99 1:28p Dave
222 * Small bug fixes for refresh button on the multi create screen.
224 * 42 4/08/99 11:55a Neilk
225 * Converted Multi_Create to new artwork (just lowres)
227 * 41 4/08/99 2:10a Dave
228 * Numerous bug fixes for the beta. Added builtin mission info for the
231 * 40 3/20/99 3:48p Andsager
232 * Do mission_loop stuff for PXO
234 * 39 3/10/99 6:50p Dave
235 * Changed the way we buffer packets for all clients. Optimized turret
236 * fired packets. Did some weapon firing optimizations.
238 * 38 3/09/99 6:24p Dave
239 * More work on object update revamping. Identified several sources of
240 * unnecessary bandwidth.
242 * 37 3/08/99 7:03p Dave
243 * First run of new object update system. Looks very promising.
245 * 36 2/25/99 4:19p Dave
246 * Added multiplayer_beta defines. Added cd_check define. Fixed a few
247 * release build warnings. Added more data to the squad war request and
250 * 35 2/24/99 3:26p Anoop
251 * Make sure the host is the only guy who bashes skill level for TvT.
253 * 34 2/24/99 2:25p Dave
254 * Fixed up chatbox bugs. Made squad war reporting better. Fixed a respawn
255 * bug for dogfight more.
257 * 33 2/23/99 2:29p Dave
258 * First run of oldschool dogfight mode.
260 * 32 2/17/99 2:11p Dave
261 * First full run of squad war. All freespace and tracker side stuff
264 * 31 2/12/99 6:16p Dave
265 * Pre-mission Squad War code is 95% done.
267 * 30 2/11/99 3:08p Dave
268 * PXO refresh button. Very preliminary squad war support.
270 * 29 2/08/99 5:07p Dave
271 * FS2 chat server support. FS2 specific validated missions.
273 * 28 2/04/99 6:29p Dave
274 * First full working rev of FS2 PXO support. Fixed Glide lighting
277 * 27 1/30/99 5:08p Dave
278 * More new hi-res stuff.Support for nice D3D textures.
280 * 26 1/29/99 2:08a Dave
281 * Fixed beam weapon collisions with players. Reduced size of scoring
282 * struct for multiplayer. Disabled PXO.
284 * 25 1/15/99 2:36p Neilk
285 * fixed multi_jw coordinates
287 * 24 1/13/99 7:19p Neilk
288 * Converted Mission Brief, Barracks, Synch to high res support
290 * 23 1/12/99 7:17p Neilk
292 * 22 1/12/99 5:45p Dave
293 * Moved weapon pipeline in multiplayer to almost exclusively client side.
294 * Very good results. Bandwidth goes down, playability goes up for crappy
295 * connections. Fixed object update problem for ship subsystems.
297 * 21 1/12/99 4:07a Dave
298 * Put in barracks code support for selecting squad logos. Properly
299 * distribute squad logos in a multiplayer game.
301 * 20 1/11/99 7:19p Neilk
302 * Converted multi_join interface to support multiple resolutions
304 * 19 12/18/98 1:13a Dave
305 * Rough 1024x768 support for Direct3D. Proper detection and usage through
308 * 18 12/17/98 4:50p Andsager
309 * Added debrief_assemble_optional_mission_popup_text() for single and
312 * 17 12/14/98 12:13p Dave
313 * Spiffed up xfer system a bit. Put in support for squad logo file xfer.
316 * 16 12/10/98 10:19a Andsager
317 * Fix mission loop assert
319 * 15 12/10/98 9:59a Andsager
320 * Fix some bugs with mission loops
322 * 14 12/09/98 1:56p Andsager
323 * Initial checkin of mission loop
325 * 13 12/03/98 5:22p Dave
326 * Ported over Freespace 1 multiplayer ships.tbl and weapons.tbl
329 * 12 11/30/98 1:07p Dave
330 * 16 bit conversion, first run.
332 * 11 11/20/98 11:16a Dave
333 * Fixed up IPX support a bit. Making sure that switching modes and
334 * loading/saving pilot files maintains proper state.
336 * 10 11/19/98 4:57p Dave
337 * Ignore PXO option if IPX is selected.
339 * 9 11/19/98 4:19p Dave
340 * Put IPX sockets back in psnet. Consolidated all multiplayer config
343 * 8 11/19/98 8:04a Dave
344 * Full support for D3-style reliable sockets. Revamped packet lag/loss
345 * system, made it receiver side and at the lowest possible level.
347 * 7 11/17/98 11:12a Dave
348 * Removed player identification by address. Now assign explicit id #'s.
350 * 6 10/19/98 11:15a Dave
351 * Changed requirements for stats storing in PXO mode.
353 * 5 10/16/98 9:40a Andsager
354 * Remove ".h" files from model.h
356 * 4 10/13/98 9:29a Dave
357 * Started neatening up freespace.h. Many variables renamed and
358 * reorganized. Added AlphaColors.[h,cpp]
360 * 3 10/07/98 6:27p Dave
361 * Globalized mission and campaign file extensions. Removed Silent Threat
362 * special code. Moved \cache \players and \multidata into the \data
365 * 2 10/07/98 10:53a Dave
368 * 1 10/07/98 10:50a Dave
370 * 333 10/02/98 3:22p Allender
371 * fix up the -connect option and fix the -port option
373 * 332 9/17/98 9:26p Dave
374 * Externalized new string.
376 * 331 9/17/98 3:08p Dave
377 * PXO to non-pxo game warning popup. Player icon stuff in create and join
378 * game screens. Upped server count refresh time in PXO to 35 secs (from
381 * 330 9/17/98 9:43a Allender
382 * removed an Assert that Dave called bogus.
384 * 329 9/16/98 6:54p Dave
385 * Upped max sexpression nodes to 1800 (from 1600). Changed FRED to sort
386 * the ship list box. Added code so that tracker stats are not stored with
389 * 328 9/15/98 7:24p Dave
390 * Minor UI changes. Localized bunch of new text.
392 * 327 9/15/98 4:03p Dave
393 * Changed readyroom and multi screens to display "st" icon for all
394 * missions with mission disk content (not necessarily just those that
395 * come with Silent Threat).
397 * 326 9/15/98 11:44a Dave
398 * Renamed builtin ships and wepaons appropriately in FRED. Put in scoring
399 * scale factors. Fixed standalone filtering of MD missions to non-MD
402 * 325 9/13/98 9:36p Dave
403 * Support for new info icons for multiplayer missions (from-volition,
404 * valid, mission disk, etc).
406 * 324 9/11/98 4:14p Dave
407 * Fixed file checksumming of < file_size. Put in more verbose kicking and
408 * PXO stats store reporting.
410 * 323 9/10/98 1:17p Dave
411 * Put in code to flag missions and campaigns as being MD or not in Fred
412 * and Freespace. Put in multiplayer support for filtering out MD
413 * missions. Put in multiplayer popups for warning of non-valid missions.
415 * 322 9/04/98 3:51p Dave
416 * Put in validated mission updating and application during stats
419 * 321 8/31/98 2:06p Dave
420 * Make cfile sort the ordering or vp files. Added support/checks for
421 * recognizing "mission disk" players.
423 * 320 8/21/98 1:15p Dave
424 * Put in log system hooks in useful places.
426 * 319 8/20/98 5:31p Dave
427 * Put in handy multiplayer logfile system. Now need to put in useful
428 * applications of it all over the code.
430 * 318 8/12/98 4:53p Dave
431 * Put in 32 bit checksumming for PXO missions. No validation on the
432 * actual tracker yet, though.
434 * 317 8/07/98 10:40a Allender
435 * new command line flags for starting netgames. Only starting currently
436 * works, and PXO isn't implemented yet
438 * 316 7/24/98 9:27a Dave
439 * Tidied up endgame sequencing by removing several old flags and
440 * standardizing _all_ endgame stuff with a single function call.
442 * 315 7/14/98 10:04a Allender
443 * fixed the countdown code to not be reliant on timer_get_fixed_seconds
445 * 314 7/10/98 5:04p Dave
446 * Fix connection speed bug on standalone server.
448 * 313 7/09/98 6:01p Dave
449 * Firsts full version of PXO updater. Put in stub for displaying
452 * 312 7/07/98 2:49p Dave
455 * 311 6/30/98 2:17p Dave
456 * Revised object update system. Removed updates for all weapons. Put
457 * button info back into control info packet.
459 * 310 6/13/98 9:32p Mike
460 * Kill last character in file which caused "Find in Files" to report the
461 * file as "not a text file."
468 #include <winsock.h> // for inet_addr()
470 #include <sys/types.h>
471 #include <sys/socket.h>
472 #include <netinet/in.h>
473 #include <arpa/inet.h>
478 #include "multiutil.h"
479 #include "multimsgs.h"
485 #include "gamesequence.h"
486 #include "freespace.h"
487 #include "contexthelp.h"
492 #include "missionshipchoice.h"
493 #include "multi_xfer.h"
495 #include "stand_gui.h"
496 #include "linklist.h"
497 #include "multiteamselect.h"
498 #include "missioncampaign.h"
505 #include "missiondebrief.h"
506 #include "multi_ingame.h"
507 #include "multi_kick.h"
508 #include "multi_data.h"
509 #include "multi_campaign.h"
510 #include "multi_team.h"
511 #include "multi_pinfo.h"
512 #include "multi_observer.h"
513 #include "multi_voice.h"
514 #include "multi_endgame.h"
515 #include "managepilot.h"
518 #include "objcollide.h"
520 #include "multi_pmsg.h"
521 #include "multi_obj.h"
522 #include "multi_log.h"
523 #include "alphacolors.h"
524 #include "animplay.h"
525 #include "multi_dogfight.h"
526 #include "missionpause.h"
528 // -------------------------------------------------------------------------------------------------------------
530 // MULTIPLAYER COMMON interface controls
533 // the common text info box stuff. This is lifted almost directly from Alans briefing code (minus the spiffy colored, scrolling
535 int Multi_common_text_coords[GR_NUM_RESOLUTIONS][4] = {
548 int Multi_common_text_max_display[GR_NUM_RESOLUTIONS] = {
557 #define MULTI_COMMON_TEXT_META_CHAR '$'
558 #define MULTI_COMMON_TEXT_MAX_LINE_LENGTH 100
559 #define MULTI_COMMON_TEXT_MAX_LINES 20
560 #define MULTI_COMMON_MAX_TEXT (MULTI_COMMON_TEXT_MAX_LINES * MULTI_COMMON_TEXT_MAX_LINE_LENGTH)
562 char Multi_common_all_text[MULTI_COMMON_MAX_TEXT];
563 char Multi_common_text[MULTI_COMMON_TEXT_MAX_LINES][MULTI_COMMON_TEXT_MAX_LINE_LENGTH];
565 int Multi_common_top_text_line = -1; // where to start displaying from
566 int Multi_common_num_text_lines = 0; // how many lines we have
568 void multi_common_scroll_text_up();
569 void multi_common_scroll_text_down();
570 void multi_common_move_to_bottom();
571 void multi_common_render_text();
572 void multi_common_split_text();
574 #define MAX_IP_STRING 255 // maximum length for ip string
576 void multi_common_scroll_text_up()
578 Multi_common_top_text_line--;
579 if ( Multi_common_top_text_line < 0 ) {
580 Multi_common_top_text_line = 0;
581 if ( !mouse_down(MOUSE_LEFT_BUTTON) )
582 gamesnd_play_iface(SND_GENERAL_FAIL);
585 gamesnd_play_iface(SND_SCROLL);
589 void multi_common_scroll_text_down()
591 Multi_common_top_text_line++;
592 if ( (Multi_common_num_text_lines - Multi_common_top_text_line) < Multi_common_text_max_display[gr_screen.res] ) {
593 Multi_common_top_text_line--;
594 if ( !mouse_down(MOUSE_LEFT_BUTTON) ){
595 gamesnd_play_iface(SND_GENERAL_FAIL);
598 gamesnd_play_iface(SND_SCROLL);
602 void multi_common_move_to_bottom()
604 // if there's nowhere to scroll down, do nothing
605 if(Multi_common_num_text_lines <= Multi_common_text_max_display[gr_screen.res]){
609 Multi_common_top_text_line = Multi_common_num_text_lines - Multi_common_text_max_display[gr_screen.res];
612 void multi_common_set_text(char *str,int auto_scroll)
615 // store the entire string as well
616 if(strlen(str) > MULTI_COMMON_MAX_TEXT){
619 strcpy(Multi_common_all_text,str);
622 // split the whole thing up
623 multi_common_split_text();
625 // scroll to the bottom if we're supposed to
627 multi_common_move_to_bottom();
631 void multi_common_add_text(char *str,int auto_scroll)
634 // store the entire string as well
635 if((strlen(str) + strlen(Multi_common_all_text)) > MULTI_COMMON_MAX_TEXT){
638 strcat(Multi_common_all_text,str);
641 // split the whole thing up
642 multi_common_split_text();
644 // scroll to the bottom if we're supposed to
646 multi_common_move_to_bottom();
650 void multi_common_split_text()
653 int n_chars[MAX_BRIEF_LINES];
654 char *p_str[MAX_BRIEF_LINES];
656 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);
657 Assert(n_lines != -1);
659 for ( i = 0; i < n_lines; i++ ) {
660 Assert(n_chars[i] < MULTI_COMMON_TEXT_MAX_LINE_LENGTH);
661 strncpy(Multi_common_text[i], p_str[i], n_chars[i]);
662 Multi_common_text[i][n_chars[i]] = 0;
663 drop_leading_white_space(Multi_common_text[i]);
666 Multi_common_top_text_line = 0;
667 Multi_common_num_text_lines = n_lines;
670 void multi_common_render_text()
672 int i, fh, line_count;
674 fh = gr_get_font_height();
677 gr_set_color_fast(&Color_text_normal);
678 for ( i = Multi_common_top_text_line; i < Multi_common_num_text_lines; i++ ) {
679 if ( line_count >= Multi_common_text_max_display[gr_screen.res] ){
682 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]);
686 if ( (Multi_common_num_text_lines - Multi_common_top_text_line) > Multi_common_text_max_display[gr_screen.res] ) {
687 gr_set_color_fast(&Color_bright_red);
688 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));
692 // common notification messaging stuff
693 #define MULTI_COMMON_NOTIFY_TIME 3500
694 int Multi_common_join_y[GR_NUM_RESOLUTIONS] = {
698 int Multi_common_create_y[GR_NUM_RESOLUTIONS] = {
703 int Multi_common_jw_y[GR_NUM_RESOLUTIONS] = {
708 int Multi_common_msg_y[GR_NUM_RESOLUTIONS] = {
713 char Multi_common_notify_text[200];
714 int Multi_common_notify_stamp;
716 void multi_common_notify_init()
718 strcpy(Multi_common_notify_text,"");
719 Multi_common_notify_stamp = -1;
722 // add a notification string, drawing appropriately depending on the state/screen we're in
723 void multi_common_add_notify(char *str)
726 strcpy(Multi_common_notify_text,str);
727 Multi_common_notify_stamp = timestamp(MULTI_COMMON_NOTIFY_TIME);
731 // process/display notification messages
732 void multi_common_notify_do()
734 if(Multi_common_notify_stamp != -1){
735 if(timestamp_elapsed(Multi_common_notify_stamp)){
736 Multi_common_notify_stamp = -1;
739 gr_get_string_size(&w,&h,Multi_common_notify_text);
740 gr_set_color_fast(&Color_white);
742 // determine where it should be placed based upon which screen we're on
744 switch(gameseq_get_state()){
745 case GS_STATE_MULTI_JOIN_GAME :
746 y = Multi_common_join_y[gr_screen.res];
748 case GS_STATE_MULTI_HOST_SETUP :
749 y = Multi_common_create_y[gr_screen.res];
751 case GS_STATE_MULTI_CLIENT_SETUP :
752 y = Multi_common_jw_y[gr_screen.res];
754 case GS_STATE_MULTI_START_GAME :
755 y = Multi_common_msg_y[gr_screen.res];
759 gr_string((gr_screen.max_w - w)/2, y, Multi_common_notify_text);
766 int Multi_common_icons[MULTI_NUM_COMMON_ICONS];
768 char *Multi_common_icon_names[MULTI_NUM_COMMON_ICONS] = {
769 "DotRed", // voice denied
770 "DotGreen", // voice recording
771 "OvalGreen", // team 0
772 "OvalGreen01", // team 0 select
774 "OvalRed01", // team 1 select
775 "mp_coop", // coop mission
776 "mp_teams", // TvT mission
777 "mp_furball", // furball mission
778 "icon-volition", // volition mission
779 "icon-valid", // mission is valid
784 "icon-silent" // SilentThreat
788 // width and height of the icons
789 int Multi_common_icon_dims[MULTI_NUM_COMMON_ICONS][2] = {
790 {11, 11}, // voice denied
791 {11, 11}, // voice recording
793 {11, 11}, // team 0 select
795 {11, 11}, // team 1 select
798 {18, 11}, // mp furball
799 {9, 9}, // volition mission
800 {8, 8}, // mission is valid
805 {16, 7} // silent threat
809 void multi_load_common_icons()
814 for(idx=0; idx<MULTI_NUM_COMMON_ICONS; idx++){
815 Multi_common_icons[idx] = -1;
816 Multi_common_icons[idx] = bm_load(Multi_common_icon_names[idx]);
820 void multi_unload_common_icons()
825 for(idx=0; idx<MULTI_NUM_COMMON_ICONS; idx++){
826 if(Multi_common_icons[idx] != -1){
827 bm_unload(Multi_common_icons[idx]);
828 Multi_common_icons[idx] = -1;
833 // display any relevant voice status icons
834 void multi_common_voice_display_status()
836 switch(multi_voice_status()){
837 // i have been denied the voice token
838 case MULTI_VOICE_STATUS_DENIED:
839 if(Multi_common_icons[MICON_VOICE_DENIED] != -1){
840 gr_set_bitmap(Multi_common_icons[MICON_VOICE_DENIED], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
845 // i am currently recording
846 case MULTI_VOICE_STATUS_RECORDING:
847 if(Multi_common_icons[MICON_VOICE_RECORDING] != -1){
848 gr_set_bitmap(Multi_common_icons[MICON_VOICE_RECORDING], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
853 // i am currently playing back sound
854 case MULTI_VOICE_STATUS_PLAYING:
857 // the system is currently idle
858 case MULTI_VOICE_STATUS_IDLE:
864 // palette initialization stuff
865 #define MULTI_COMMON_PALETTE_FNAME "InterfacePalette"
868 int Multi_common_interface_palette = -1;
870 void multi_common_load_palette();
871 void multi_common_set_palette();
872 void multi_common_unload_palette();
874 // load in the palette if it doesn't already exist
875 void multi_common_load_palette()
877 if(Multi_common_interface_palette != -1){
881 Multi_common_interface_palette = bm_load(MULTI_COMMON_PALETTE_FNAME);
882 if(Multi_common_interface_palette == -1){
883 nprintf(("Network","Error loading multiplayer common palette!\n"));
887 // set the common palette to be the active one
888 void multi_common_set_palette()
890 // if the palette is not loaded yet, do so now
891 if(Multi_common_interface_palette == -1){
892 multi_common_load_palette();
895 if(Multi_common_interface_palette != -1){
896 #ifndef HARDWARE_ONLY
897 palette_use_bm_palette(Multi_common_interface_palette);
902 // unload the bitmap palette
903 void multi_common_unload_palette()
905 if(Multi_common_interface_palette != -1){
906 bm_unload(Multi_common_interface_palette);
907 Multi_common_interface_palette = -1;
911 void multi_common_verify_cd()
914 // otherwise, call the freespace function to determine if we have a cd
917 if((find_freespace_cd(FS_CDROM_VOLUME_1) >= 0) || (find_freespace_cd(FS_CDROM_VOLUME_2) >= 0) ){
919 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) ){
929 // -------------------------------------------------------------------------------------------------------------
931 // MULTIPLAYER JOIN SCREEN
934 #define MULTI_JOIN_NUM_BUTTONS 11
938 #define MULTI_JOIN_PALETTE "InterfacePalette"
940 static char *Multi_join_bitmap_fname[GR_NUM_RESOLUTIONS] = {
941 "MultiJoin", // GR_640
942 "2_MultiJoin" // GR_1024
945 static char *Multi_join_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
946 "MultiJoin-M", // GR_640
947 "2_MultiJoin-M" // GR_1024
953 char *Mj_slider_name[GR_NUM_RESOLUTIONS] = {
957 int Mj_slider_coords[GR_NUM_RESOLUTIONS][4] = {
968 #define MJ_SCROLL_UP 0
969 #define MJ_SCROLL_DOWN 1
971 #define MJ_SCROLL_INFO_UP 3
972 #define MJ_SCROLL_INFO_DOWN 4
973 #define MJ_JOIN_OBSERVER 5
974 #define MJ_START_GAME 6
980 // uses MULTI_JOIN_REFRESH_TIME as its timestamp
981 int Multi_join_glr_stamp;
983 #define MULTI_JOIN_PING_TIME 15000 // how often we ping all the known servers
984 int Multi_join_ping_stamp;
985 UI_WINDOW Multi_join_window; // the window object for the join screen
986 UI_BUTTON Multi_join_select_button; // for selecting list items
988 UI_SLIDER2 Multi_join_slider; // handy dandy slider
990 int Multi_join_bitmap; // the background bitmap
992 ui_button_info Multi_join_buttons[GR_NUM_RESOLUTIONS][MULTI_JOIN_NUM_BUTTONS] = {
995 ui_button_info("MJ_00", 0, 85, -1, -1, 0),
996 ui_button_info("MJ_01", 0, 125, -1, -1, 1),
997 ui_button_info("MJ_03", 20, 324, -1, -1, 3),
998 ui_button_info("MJ_04", 0, 399, -1, -1, 4),
999 ui_button_info("MJ_05", 0, 436, -1, -1, 5),
1000 ui_button_info("MJ_15", 450, 323, -1, -1, 15),
1001 ui_button_info("MJ_10", 519, 323, -1, -1, 10),
1002 ui_button_info("MJ_06", 574, 323, -1, -1, 6),
1003 ui_button_info("MJ_08", 470, 427, -1, -1, 8),
1004 ui_button_info("MJ_09", 448, 454, -1, -1, 9),
1005 ui_button_info("MJ_07", 563, 411, -1, -1, 7),
1007 ui_button_info( "MJ_00", 1, 57, -1, -1, 0 ), // scroll up
1008 ui_button_info( "MJ_02", 1, 297, -1, -1, 2 ), // scroll down
1009 ui_button_info( "MJ_03", 10, 338, 65, 364, 3 ), // refresh
1010 ui_button_info( "MJ_04", 1, 405, -1, -1, 4 ), // scroll info up
1011 ui_button_info( "MJ_05", 1, 446, -1, -1, 5 ), // scroll info down
1012 ui_button_info( "MJ_06", 489, 339, -1, -1, 6 ), // join as observer
1013 ui_button_info( "MJ_07", 538, 339, -1, -1, 7 ), // create game
1014 ui_button_info( "MJ_08", 583, 339, 588, 376, 8 ), // cancel
1015 ui_button_info( "MJ_09", 534, 426, -1, -1, 9 ), // help
1016 ui_button_info( "MJ_10", 534, 454, -1, -1, 10 ), // options
1017 ui_button_info( "MJ_11", 571, 426, 589, 416, 11 ), // join
1021 ui_button_info( "2_MJ_00", 2, 92, -1, -1, 0 ), // scroll up
1022 ui_button_info( "2_MJ_02", 2, 475, -1, -1, 2 ), // scroll down
1023 ui_button_info( "2_MJ_03", 16, 541, 104, 582, 3 ), // refresh
1024 ui_button_info( "2_MJ_04", 2, 648, -1, -1, 4 ), // scroll info up
1025 ui_button_info( "2_MJ_05", 2, 713, -1, -1, 5 ), // scroll info down
1026 ui_button_info( "2_MJ_06", 783, 542, -1, -1, 6 ), // join as observer
1027 ui_button_info( "2_MJ_07", 861, 542, -1, -1, 7 ), // create game
1028 ui_button_info( "2_MJ_08", 933, 542, 588, 376, 8 ), // cancel
1029 ui_button_info( "2_MJ_09", 854, 681, -1, -1, 9 ), // help
1030 ui_button_info( "2_MJ_10", 854, 727, -1, -1, 10 ), // options
1031 ui_button_info( "2_MJ_11", 914, 681, 937, 668, 11 ), // join
1036 #define MULTI_JOIN_NUM_TEXT 13
1038 UI_XSTR Multi_join_text[GR_NUM_RESOLUTIONS][MULTI_JOIN_NUM_TEXT] = {
1040 {"Refresh", 1299, 65, 364, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_REFRESH].button},
1041 {"Join as", 1300, 476, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_JOIN_OBSERVER].button},
1042 {"Observer", 1301, 467, 385, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_JOIN_OBSERVER].button},
1043 {"Create", 1408, 535, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_START_GAME].button},
1044 {"Game", 1302, 541, 385, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_START_GAME].button},
1045 {"Cancel", 387, 588, 376, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[0][MJ_CANCEL].button},
1046 {"Help", 928, 479, 436, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_HELP].button},
1047 {"Options", 1036, 479, 460, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_OPTIONS].button},
1048 {"Join", 1303, 589, 416, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[0][MJ_ACCEPT].button},
1049 {"Status", 1304, 37, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
1050 {"Server", 1305, 116, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
1051 {"Players", 1306, 471, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
1052 {"Ping", 1307, 555, 37, UI_XSTR_COLOR_GREEN, -1, NULL}
1055 {"Refresh", 1299, 104, 582, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_REFRESH].button},
1056 {"Join as", 1300, 783, 602, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_JOIN_OBSERVER].button},
1057 {"Observer", 1301, 774, 611, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_JOIN_OBSERVER].button},
1058 {"Create", 1408, 868, 602, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_START_GAME].button},
1059 {"Game", 1302, 872, 611, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_START_GAME].button},
1060 {"Cancel", 387, 941, 602, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[1][MJ_CANCEL].button},
1061 {"Help", 928, 782, 699, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_HELP].button},
1062 {"Options", 1036, 782, 736, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_OPTIONS].button},
1063 {"Join", 1303, 937, 668, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[1][MJ_ACCEPT].button},
1064 {"Status", 1304, 60, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
1065 {"Server", 1305, 186, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
1066 {"Players", 1306, 753, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
1067 {"Ping", 1307, 888, 60, UI_XSTR_COLOR_GREEN, -1, NULL}
1072 // constants for coordinate look ups
1073 #define MJ_X_COORD 0
1074 #define MJ_Y_COORD 1
1075 #define MJ_W_COORD 2
1076 #define MJ_H_COORD 3
1078 #define MULTI_JOIN_SENT_WAIT 10000 // wait this long since a join was sent to allow another
1079 int Multi_join_sent_stamp;
1081 // game information text areas
1082 int Mj_max_game_items[GR_NUM_RESOLUTIONS] = {
1087 int Mj_list_y[GR_NUM_RESOLUTIONS] = {
1092 int Mj_status_coords[GR_NUM_RESOLUTIONS][4] = {
1101 int Mj_game_icon_coords[GR_NUM_RESOLUTIONS][3] = {
1110 int Mj_speed_coords[GR_NUM_RESOLUTIONS][4] = {
1119 int Mj_game_name_coords[GR_NUM_RESOLUTIONS][4] = {
1128 int Mj_players_coords[GR_NUM_RESOLUTIONS][4] = {
1137 int Mj_ping_coords[GR_NUM_RESOLUTIONS][4] = {
1146 // game speed labels
1147 #define MJ_NUM_SPEED_LABELS 5
1148 char *Multi_join_speed_labels[MJ_NUM_SPEED_LABELS] = {
1155 color *Multi_join_speed_colors[MJ_NUM_SPEED_LABELS] = {
1158 &Color_bright_green,
1159 &Color_bright_green,
1163 int Mj_cd_coords[GR_NUM_RESOLUTIONS] = {
1168 // extents of the entire boundable game info region
1169 // NOTE : these numbers are completely empirical
1170 #define MJ_PING_GREEN 160
1171 #define MJ_PING_YELLOW 300
1173 int Mj_list_area_coords[GR_NUM_RESOLUTIONS][4] = {
1182 // PXO channel filter
1183 #define MJ_PXO_FILTER_Y 0
1185 // special chars to indicate various status modes for servers
1186 #define MJ_CHAR_STANDALONE "*"
1187 #define MJ_CHAR_CAMPAIGN "c"
1190 // various interface indices
1191 int Multi_join_list_start; // where to start displaying from
1192 active_game *Multi_join_list_start_item; // a pointer to the corresponding active_game
1193 int Multi_join_list_selected; // which item we have selected
1194 active_game *Multi_join_selected_item; // a pointer to the corresponding active_game
1196 // use this macro to modify the list start
1197 #define MJ_LIST_START_INC() do { Multi_join_list_start++; } while(0);
1198 #define MJ_LIST_START_DEC() do { Multi_join_list_start--; } while(0);
1199 #define MJ_LIST_START_SET(vl) do { Multi_join_list_start = vl; } while(0);
1201 // if we should be sending a join request at the end of the frame
1202 int Multi_join_should_send = -1;
1204 // master tracker details
1205 int Multi_join_frame_count; // keep a count of frames displayed
1206 int Multi_join_mt_tried_verify; // already tried verifying the pilot with the tracker
1208 // data stuff for auto joining a game
1209 #define MULTI_AUTOJOIN_JOIN_STAMP 2000
1210 #define MULTI_AUTOJOIN_QUERY_STAMP 2000
1212 int Multi_did_autojoin;
1213 net_addr Multi_autojoin_addr;
1214 int Multi_autojoin_join_stamp;
1215 int Multi_autojoin_query_stamp;
1218 join_request Multi_join_request;
1220 // LOCAL function definitions
1221 void multi_join_check_buttons();
1222 void multi_join_button_pressed(int n);
1223 void multi_join_display_games();
1224 void multi_join_blit_game_status(active_game *game, int y);
1225 void multi_join_load_tcp_addrs();
1226 void multi_join_do_netstuff();
1227 void multi_join_ping_all();
1228 void multi_join_process_select();
1229 void multi_join_list_scroll_up();
1230 void multi_join_list_scroll_down();
1231 void multi_join_list_page_up();
1232 void multi_join_list_page_down();
1233 active_game *multi_join_get_game(int n);
1234 void multi_join_cull_timeouts();
1235 void multi_join_handle_item_cull(active_game *item, int item_index);
1236 void multi_join_send_join_request(int as_observer);
1237 void multi_join_create_game();
1238 void multi_join_blit_top_stuff();
1239 int multi_join_maybe_warn();
1240 int multi_join_warn_pxo();
1241 void multi_join_blit_protocol();
1245 active_game ag, *newitem;;
1248 dc_get_arg(ARG_INT);
1249 for(idx=0; idx<Dc_arg_int; idx++){
1250 // stuff some fake info
1251 memset(&ag, 0, sizeof(active_game));
1252 sprintf(ag.name, "Game %d", idx);
1253 ag.version = MULTI_FS_SERVER_VERSION;
1254 ag.comp_version = MULTI_FS_SERVER_VERSION;
1255 ag.server_addr.addr[0] = (char)idx;
1256 ag.flags = (AG_FLAG_COOP | AG_FLAG_FORMING | AG_FLAG_STANDALONE);
1259 newitem = multi_update_active_games(&ag);
1261 // timestamp it so we get random timeouts
1262 if(newitem != NULL){
1263 // newitem->heard_from_timer = timestamp((int)frand_range(500.0f, 10000.0f));
1268 void multi_join_notify_new_game()
1271 // reset the # of items
1272 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);
1273 Multi_join_slider.force_currentItem(Multi_join_list_start);
1277 int multi_join_autojoin_do()
1279 // if we have an active game on the list, then return a positive value so that we
1280 // can join the game
1281 if ( Active_game_head && (Active_game_count > 0) ) {
1282 Multi_join_selected_item = Active_game_head;
1286 // send out a server_query again
1287 if ( timestamp_elapsed(Multi_autojoin_query_stamp) ) {
1288 send_server_query(&Multi_autojoin_addr);
1289 Multi_autojoin_query_stamp = timestamp(MULTI_AUTOJOIN_QUERY_STAMP);
1295 void multi_join_game_init()
1299 // do the multiplayer init stuff - multi_level_init() now does all net_player zeroing.
1300 // setup various multiplayer things
1301 Assert( Game_mode & GM_MULTIPLAYER );
1302 Assert( Net_player != NULL );
1304 switch (Multi_options_g.protocol) {
1306 ADDRESS_LENGTH = IPX_ADDRESS_LENGTH;
1307 PORT_LENGTH = IPX_PORT_LENGTH;
1311 ADDRESS_LENGTH = IP_ADDRESS_LENGTH;
1312 PORT_LENGTH = IP_PORT_LENGTH;
1321 memset( &Netgame, 0, sizeof(Netgame) );
1324 Net_player->flags |= NETINFO_FLAG_DO_NETWORKING;
1325 Net_player->player = Player;
1326 memcpy(&Net_player->p_info.addr,&Psnet_my_addr,sizeof(net_addr));
1328 // check for the existence of a CD
1329 multi_common_verify_cd();
1331 // load my local netplayer options
1332 multi_options_local_load(&Net_player->p_info.options, Net_player);
1338 common_set_interface_palette(MULTI_JOIN_PALETTE);
1341 // destroy any chatbox contents which previously existed (from another game)
1344 // create the interface window
1345 Multi_join_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
1346 Multi_join_window.set_mask_bmap(Multi_join_bitmap_mask_fname[gr_screen.res]);
1348 // load the background bitmap
1349 Multi_join_bitmap = bm_load(Multi_join_bitmap_fname[gr_screen.res]);
1350 if(Multi_join_bitmap < 0){
1351 // we failed to load the bitmap - this is very bad
1355 // intialize the endgame system
1356 multi_endgame_init();
1358 // initialize the common notification messaging
1359 multi_common_notify_init();
1361 // initialize the common text area
1362 multi_common_set_text("");
1364 // load and use the common interface palette
1365 multi_common_load_palette();
1366 multi_common_set_palette();
1368 // load the help overlay
1369 help_overlay_load(MULTI_JOIN_OVERLAY);
1370 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1372 // do TCP and VMT specific initialization
1373 if(Multi_options_g.protocol == NET_TCP){
1374 // if this is a TCP (non tracker) game, we'll load up our default address list right now
1375 multi_join_load_tcp_addrs();
1378 // initialize any and all timestamps
1379 Multi_join_glr_stamp = -1;
1380 Multi_join_ping_stamp = -1;
1381 Multi_join_sent_stamp = -1;
1383 // reset frame count
1384 Multi_join_frame_count = 0;
1386 // haven't tried to verify on the tracker yet.
1387 Multi_join_mt_tried_verify = 0;
1389 // clear our all game lists to save hassles
1390 multi_join_clear_game_list();
1392 // create the interface buttons
1393 for(idx=0; idx<MULTI_JOIN_NUM_BUTTONS; idx++){
1394 // create the object
1395 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);
1397 // set the sound to play when highlighted
1398 Multi_join_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
1400 // set the ani for the button
1401 Multi_join_buttons[gr_screen.res][idx].button.set_bmaps(Multi_join_buttons[gr_screen.res][idx].filename);
1404 Multi_join_buttons[gr_screen.res][idx].button.link_hotspot(Multi_join_buttons[gr_screen.res][idx].hotspot);
1409 for(idx=0; idx<MULTI_JOIN_NUM_TEXT; idx++){
1410 Multi_join_window.add_XSTR(&Multi_join_text[gr_screen.res][idx]);
1414 Multi_join_should_send = -1;
1416 // close any previously open chatbox
1419 // create the list item select button
1420 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);
1421 Multi_join_select_button.hide();
1425 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);
1428 // if starting a network game, then go to the create game screen
1429 if ( Cmdline_start_netgame ) {
1430 multi_join_create_game();
1431 } else if ( Cmdline_connect_addr != NULL ) {
1436 // joining a game. Send a join request to the given IP address, and wait for the return.
1437 memset( &Multi_autojoin_addr, 0, sizeof(net_addr) );
1438 Multi_autojoin_addr.type = NET_TCP;
1440 // create the address, looking out for port number at the end
1441 port_num = DEFAULT_GAME_PORT;
1442 p = strrchr(Cmdline_connect_addr, ':');
1446 port_num = (short)atoi(p);
1448 ip_addr = inet_addr(Cmdline_connect_addr);
1449 memcpy(Multi_autojoin_addr.addr, &ip_addr, 4);
1450 Multi_autojoin_addr.port = port_num;
1452 send_server_query(&Multi_autojoin_addr);
1453 Multi_autojoin_query_stamp = timestamp(MULTI_AUTOJOIN_QUERY_STAMP);
1454 Multi_did_autojoin = 0;
1458 void multi_join_clear_game_list()
1461 Multi_join_list_selected = -1;
1462 Multi_join_selected_item = NULL;
1463 MJ_LIST_START_SET(-1);
1464 Multi_join_list_start_item = NULL;
1466 // free up the active game list
1467 multi_free_active_games();
1469 // initialize the active game list
1470 Active_game_head = NULL;
1471 Active_game_count = 0;
1474 void multi_join_game_do_frame()
1476 // check the status of our reliable socket. If not valid, popup error and return to main menu
1477 // I put this code here to avoid nasty gameseq issues with states. Also, we will have nice
1478 // background for the popup
1479 if ( !psnet_rel_check() ) {
1480 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));
1481 gameseq_post_event(GS_EVENT_MAIN_MENU);
1485 // return here since we will be moving to the next stage anyway -- I don't want to see the backgrounds of
1486 // all the screens for < 1 second for every screen we automatically move to.
1487 if ( Cmdline_start_netgame ) {
1491 // when joining a network game, wait for the server query to come back, and then join the game
1492 if ( Cmdline_connect_addr != NULL ) {
1495 if ( !Multi_did_autojoin ) {
1496 rval = popup_till_condition(multi_join_autojoin_do, XSTR("&Cancel", 779), XSTR("Joining netgame", 1500) );
1498 // cancel was hit. Send the user back to the main hall
1499 gameseq_post_event(GS_EVENT_MAIN_MENU);
1500 Cmdline_connect_addr = NULL; // reset this value.
1503 // when we get here, we have the data -- join the game.
1504 multi_join_send_join_request(0);
1505 Multi_autojoin_join_stamp = timestamp(MULTI_AUTOJOIN_JOIN_STAMP);
1506 Multi_did_autojoin = 1;
1509 if ( timestamp_elapsed(Multi_autojoin_join_stamp) ) {
1510 multi_join_send_join_request(0);
1511 Multi_autojoin_join_stamp = timestamp(MULTI_AUTOJOIN_JOIN_STAMP);
1517 // reset the should send var
1518 Multi_join_should_send = -1;
1520 int k = Multi_join_window.process();
1522 // process any keypresses
1525 if(help_overlay_active(MULTI_JOIN_OVERLAY)){
1526 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1528 gameseq_post_event(GS_EVENT_MAIN_MENU);
1529 gamesnd_play_iface(SND_USER_SELECT);
1533 // page up the game list
1535 multi_join_list_page_up();
1537 Multi_join_slider.force_currentItem(Multi_join_list_start);
1542 multi_pinfo_popup(Net_player);
1545 // page down the game list
1547 multi_join_list_page_down();
1549 Multi_join_slider.force_currentItem(Multi_join_list_start);
1553 // send out a ping-all
1555 multi_join_ping_all();
1556 Multi_join_ping_stamp = timestamp(MULTI_JOIN_PING_TIME);
1559 // shortcut to start a game
1561 multi_join_create_game();
1564 // scroll the game list up
1566 multi_join_list_scroll_up();
1568 Multi_join_slider.force_currentItem(Multi_join_list_start);
1572 // scroll the game list down
1574 multi_join_list_scroll_down();
1576 Multi_join_slider.force_currentItem(Multi_join_list_start);
1581 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
1582 help_overlay_set_state(MULTI_JOIN_OVERLAY, 0);
1585 // do any network related stuff
1586 multi_join_do_netstuff();
1588 // process any button clicks
1589 multi_join_check_buttons();
1591 // process any list selection stuff
1592 multi_join_process_select();
1594 // draw the background, etc
1596 GR_MAYBE_CLEAR_RES(Multi_join_bitmap);
1597 if(Multi_join_bitmap != -1){
1598 gr_set_bitmap(Multi_join_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1601 Multi_join_window.draw();
1603 // display the active games
1604 multi_join_display_games();
1606 // display any text in the info area
1607 multi_common_render_text();
1609 // display any pending notification messages
1610 multi_common_notify_do();
1612 // blit the CD icon and any PXO filter stuff
1613 multi_join_blit_top_stuff();
1615 // draw the help overlay
1616 help_overlay_maybe_blit(MULTI_JOIN_OVERLAY);
1621 // if we are supposed to be sending a join request
1622 if(Multi_join_should_send != -1){
1623 multi_join_send_join_request(Multi_join_should_send);
1625 Multi_join_should_send = -1;
1627 // increment the frame count
1628 Multi_join_frame_count++;
1631 void multi_join_game_close()
1633 // unload any bitmaps
1634 if(!bm_unload(Multi_join_bitmap)){
1635 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_join_bitmap_fname[gr_screen.res]));
1638 // unload the help overlay
1639 help_overlay_unload(MULTI_JOIN_OVERLAY);
1641 // free up the active game list
1642 multi_free_active_games();
1644 // destroy the UI_WINDOW
1645 Multi_join_window.destroy();
1648 common_free_interface_palette();
1652 void multi_join_check_buttons()
1655 for(idx=0;idx<MULTI_JOIN_NUM_BUTTONS;idx++){
1656 // we only really need to check for one button pressed at a time, so we can break after
1658 if(Multi_join_buttons[gr_screen.res][idx].button.pressed()){
1659 multi_join_button_pressed(idx);
1665 void multi_join_button_pressed(int n)
1669 // if we're player PXO, go back there
1670 gameseq_post_event(GS_EVENT_MAIN_MENU);
1671 gamesnd_play_iface(SND_USER_SELECT);
1674 if(Active_game_count <= 0){
1675 multi_common_add_notify(XSTR("No games found!",757));
1676 gamesnd_play_iface(SND_GENERAL_FAIL);
1677 } else if(Multi_join_list_selected == -1){
1678 multi_common_add_notify(XSTR("No game selected!",758));
1679 gamesnd_play_iface(SND_GENERAL_FAIL);
1680 } else if((Multi_join_sent_stamp != -1) && !timestamp_elapsed(Multi_join_sent_stamp)){
1681 multi_common_add_notify(XSTR("Still waiting on previous join request!",759));
1682 gamesnd_play_iface(SND_GENERAL_FAIL);
1684 // otherwise, if he's already played PXO games, warn him
1686 if(Player->flags & PLAYER_FLAGS_HAS_PLAYED_PXO){
1687 if(!multi_join_warn_pxo()){
1693 // send the join request here
1694 Assert(Multi_join_selected_item != NULL);
1696 // send a join request packet
1697 Multi_join_should_send = 0;
1699 gamesnd_play_iface(SND_COMMIT_PRESSED);
1705 if(!help_overlay_active(MULTI_JOIN_OVERLAY)){
1706 help_overlay_set_state(MULTI_JOIN_OVERLAY,1);
1708 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1712 // scroll the game list up
1714 multi_join_list_scroll_up();
1716 Multi_join_slider.force_currentItem(Multi_join_list_start);
1720 // scroll the game list down
1721 case MJ_SCROLL_DOWN:
1722 multi_join_list_scroll_down();
1724 Multi_join_slider.force_currentItem(Multi_join_list_start);
1728 // scroll the info text box up
1729 case MJ_SCROLL_INFO_UP:
1730 multi_common_scroll_text_up();
1733 // scroll the info text box down
1734 case MJ_SCROLL_INFO_DOWN:
1735 multi_common_scroll_text_down();
1738 // go to the options screen
1740 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
1743 // go to the start game screen
1745 multi_join_create_game();
1748 // refresh the game/server list
1750 gamesnd_play_iface(SND_USER_SELECT);
1751 broadcast_game_query();
1754 // join a game as an observer
1755 case MJ_JOIN_OBSERVER:
1756 if(Active_game_count <= 0){
1757 multi_common_add_notify(XSTR("No games found!",757));
1758 gamesnd_play_iface(SND_GENERAL_FAIL);
1759 } else if(Multi_join_list_selected == -1){
1760 multi_common_add_notify(XSTR("No game selected!",758));
1761 gamesnd_play_iface(SND_GENERAL_FAIL);
1762 } else if((Multi_join_sent_stamp != -1) && !timestamp_elapsed(Multi_join_sent_stamp)){
1763 multi_common_add_notify(XSTR("Still waiting on previous join request!",759));
1764 gamesnd_play_iface(SND_GENERAL_FAIL);
1766 // send the join request here
1767 Assert(Multi_join_selected_item != NULL);
1769 Multi_join_should_send = 1;
1771 gamesnd_play_iface(SND_COMMIT_PRESSED);
1776 multi_common_add_notify(XSTR("Not implemented yet!",760));
1777 gamesnd_play_iface(SND_GENERAL_FAIL);
1782 // display all relevant info for active games
1783 void multi_join_display_games()
1785 active_game *moveup = Multi_join_list_start_item;
1789 int y_start = Mj_list_y[gr_screen.res];
1794 // blit the game status (including text and type icon)
1795 multi_join_blit_game_status(moveup,y_start);
1797 // get the connection type
1798 con_type = (moveup->flags & AG_FLAG_CONNECTION_SPEED_MASK) >> AG_FLAG_CONNECTION_BIT;
1799 if((con_type > 4) || (con_type < 0)){
1803 // display the connection speed
1805 strcpy(str, Multi_join_speed_labels[con_type]);
1806 gr_set_color_fast(Multi_join_speed_colors[con_type]);
1807 gr_string(Mj_speed_coords[gr_screen.res][MJ_X_COORD], y_start, str);
1809 // we'll want to have different colors for highlighted items, etc.
1810 if(moveup == Multi_join_selected_item){
1811 gr_set_color_fast(&Color_text_selected);
1813 gr_set_color_fast(&Color_text_normal);
1816 // display the game name, adding appropriate status chars
1818 if(moveup->flags & AG_FLAG_STANDALONE){
1819 strcat(str,MJ_CHAR_STANDALONE);
1821 if(moveup->flags & AG_FLAG_CAMPAIGN){
1822 strcat(str,MJ_CHAR_CAMPAIGN);
1825 // tack on the actual server name
1827 strcat(str,moveup->name);
1828 if(strlen(moveup->mission_name) > 0){
1830 strcat(str,moveup->mission_name);
1833 // make sure the string fits in the display area and draw it
1834 gr_force_fit_string(str,200,Mj_game_name_coords[gr_screen.res][MJ_W_COORD]);
1835 gr_string(Mj_game_name_coords[gr_screen.res][MJ_X_COORD],y_start,str);
1837 // display the ping time
1838 if(moveup->ping.ping_avg > 0){
1839 if(moveup->ping.ping_avg > 1000){
1840 gr_set_color_fast(&Color_bright_red);
1841 strcpy(str,XSTR("> 1 sec",761));
1843 // set the appropriate ping time color indicator
1844 if(moveup->ping.ping_avg > MJ_PING_YELLOW){
1845 gr_set_color_fast(&Color_bright_red);
1846 } else if(moveup->ping.ping_avg > MJ_PING_YELLOW){
1847 gr_set_color_fast(&Color_bright_yellow);
1849 gr_set_color_fast(&Color_bright_green);
1852 sprintf(str,"%d",moveup->ping.ping_avg);
1853 strcat(str,XSTR(" ms",762)); // [[ Milliseconds ]]
1856 gr_string(Mj_ping_coords[gr_screen.res][MJ_X_COORD],y_start,str);
1859 // display the number of players (be sure to center it)
1860 if(moveup == Multi_join_selected_item){
1861 gr_set_color_fast(&Color_text_selected);
1863 gr_set_color_fast(&Color_text_normal);
1865 sprintf(str,"%d",moveup->num_players);
1866 gr_get_string_size(&w,&h,str);
1867 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);
1871 moveup = moveup->next;
1872 } while((moveup != Active_game_head) && (count < Mj_max_game_items[gr_screen.res]));
1874 // if there are no items on the list, display this info
1876 gr_set_color_fast(&Color_bright);
1877 gr_string(Mj_game_name_coords[gr_screen.res][MJ_X_COORD] - 30,y_start,XSTR("<No game servers found>",763));
1881 void multi_join_blit_game_status(active_game *game, int y)
1884 char status_text[25];
1886 // blit the proper icon
1888 switch( game->flags & AG_FLAG_TYPE_MASK ){
1891 if(Multi_common_icons[MICON_COOP] != -1){
1892 gr_set_bitmap(Multi_common_icons[MICON_COOP], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1897 // team vs. team game
1899 if(Multi_common_icons[MICON_TVT] != -1){
1900 gr_set_bitmap(Multi_common_icons[MICON_TVT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1907 case AG_FLAG_DOGFIGHT:
1908 if(Multi_common_icons[MICON_DOGFIGHT] != -1){
1909 gr_set_bitmap(Multi_common_icons[MICON_DOGFIGHT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1915 // if we're supposed to draw a bitmap
1917 gr_bitmap(Mj_game_icon_coords[gr_screen.res][MJ_X_COORD],y-1);
1920 // blit the proper status text
1921 memset(status_text,0,25);
1923 switch( game->flags & AG_FLAG_STATE_MASK ){
1924 case AG_FLAG_FORMING:
1925 gr_set_color_fast(&Color_bright_green);
1926 strcpy(status_text,XSTR("Forming",764));
1928 case AG_FLAG_BRIEFING:
1929 gr_set_color_fast(&Color_bright_red);
1930 strcpy(status_text,XSTR("Briefing",765));
1932 case AG_FLAG_DEBRIEF:
1933 gr_set_color_fast(&Color_bright_red);
1934 strcpy(status_text,XSTR("Debrief",766));
1937 gr_set_color_fast(&Color_bright_red);
1938 strcpy(status_text,XSTR("Paused",767));
1940 case AG_FLAG_IN_MISSION:
1941 gr_set_color_fast(&Color_bright_red);
1942 strcpy(status_text,XSTR("Playing",768));
1945 gr_set_color_fast(&Color_bright);
1946 strcpy(status_text,XSTR("Unknown",769));
1949 gr_get_string_size(&str_w,NULL,status_text);
1950 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);
1953 // load in a list of active games from our tcp.cfg file
1954 void multi_join_load_tcp_addrs()
1956 char line[MAX_IP_STRING];
1961 // attempt to open the ip list file
1962 file = cfopen(IP_CONFIG_FNAME,"rt",CFILE_NORMAL,CF_TYPE_DATA);
1964 nprintf(("Network","Error loading tcp.cfg file!\n"));
1968 // free up any existing server list
1969 multi_free_server_list();
1971 // read in all the strings in the file
1972 while(!cfeof(file)){
1974 cfgets(line,MAX_IP_STRING,file);
1976 // strip off any newline character
1977 if(line[strlen(line) - 1] == '\n'){
1978 line[strlen(line) - 1] = '\0';
1981 // empty lines don't get processed
1982 if( (line[0] == '\0') || (line[0] == '\n') ){
1986 if ( !psnet_is_valid_ip_string(line) ) {
1987 nprintf(("Network","Invalid ip string (%s)\n",line));
1989 // copy the server ip address
1990 memset(&addr,0,sizeof(net_addr));
1991 addr.type = NET_TCP;
1992 psnet_string_to_addr(&addr,line);
1993 if ( addr.port == 0 ){
1994 addr.port = DEFAULT_GAME_PORT;
1997 // create a new server item on the list
1998 item = multi_new_server_item();
2000 memcpy(&item->server_addr,&addr,sizeof(net_addr));
2008 // do stuff like pinging servers, sending out requests, etc
2009 void multi_join_do_netstuff()
2011 // handle game query stuff
2012 if(Multi_join_glr_stamp == -1){
2013 broadcast_game_query();
2015 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
2016 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME_LOCAL);
2018 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME);
2021 // otherwise send out game query and restamp
2022 else if(timestamp_elapsed(Multi_join_glr_stamp)){
2023 broadcast_game_query();
2025 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
2026 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME_LOCAL);
2028 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME);
2032 // check to see if we've been accepted. If so, put up message saying so
2033 if ( Net_player->flags & (NETINFO_FLAG_ACCEPT_INGAME|NETINFO_FLAG_ACCEPT_CLIENT|NETINFO_FLAG_ACCEPT_HOST|NETINFO_FLAG_ACCEPT_OBSERVER) ) {
2034 multi_common_add_notify(XSTR("Accepted. Waiting for player data.",770));
2037 // check to see if any join packets we have sent have timed out
2038 if((Multi_join_sent_stamp != -1) && (timestamp_elapsed(Multi_join_sent_stamp))){
2039 Multi_join_sent_stamp = -1;
2040 multi_common_add_notify(XSTR("Join request timed out!",771));
2043 // check to see if we should be pinging everyone
2044 if((Multi_join_ping_stamp == -1) || (timestamp_elapsed(Multi_join_ping_stamp))){
2045 multi_join_ping_all();
2046 Multi_join_ping_stamp = timestamp(MULTI_JOIN_PING_TIME);
2050 multi_join_cull_timeouts();
2053 // evaluate a returned pong.
2054 void multi_join_eval_pong(net_addr *addr, fix pong_time)
2057 active_game *moveup = Active_game_head;
2062 if(psnet_same(&moveup->server_addr,addr)){
2064 multi_ping_eval_pong(&moveup->ping);
2068 moveup = moveup->next;
2070 } while(moveup != Active_game_head);
2073 // update the game's ping
2075 if(found && (moveup->ping_end > moveup->ping_start)){
2076 moveup->ping_time = f2fl(moveup->ping_end - moveup->ping_start);
2077 moveup->ping_start = -1;
2078 moveup->ping_end = -1;
2083 // ping all the server on the list
2084 void multi_join_ping_all()
2086 active_game *moveup = Active_game_head;
2091 moveup->ping_start = timer_get_fixed_seconds();
2092 moveup->ping_end = -1;
2093 send_ping(&moveup->server_addr);
2095 multi_ping_send(&moveup->server_addr,&moveup->ping);
2097 moveup = moveup->next;
2098 } while(moveup != Active_game_head);
2102 void multi_join_process_select()
2104 // if we don't have anything selected and there are items on the list - select the first one
2105 if((Multi_join_list_selected == -1) && (Active_game_count > 0)){
2106 Multi_join_list_selected = 0;
2107 Multi_join_selected_item = multi_join_get_game(0);
2108 MJ_LIST_START_SET(0);
2109 Multi_join_list_start_item = Multi_join_selected_item;
2111 // send a mission description request to this guy
2112 send_netgame_descript_packet(&Multi_join_selected_item->server_addr,0);
2113 multi_common_set_text("");
2115 // I sure hope this doesn't happen
2116 Assert(Multi_join_selected_item != NULL);
2119 // otherwise see if he's clicked on an item
2120 else if(Multi_join_select_button.pressed() && (Active_game_count > 0)){
2122 Multi_join_select_button.get_mouse_pos(NULL,&y);
2124 if(item + Multi_join_list_start < Active_game_count){
2125 gamesnd_play_iface(SND_IFACE_MOUSE_CLICK);
2127 Multi_join_list_selected = item + Multi_join_list_start;
2128 Multi_join_selected_item = multi_join_get_game(Multi_join_list_selected);
2130 // I sure hope this doesn't happen
2131 Assert(Multi_join_selected_item != NULL);
2133 // send a mission description request to this guy
2134 send_netgame_descript_packet(&Multi_join_selected_item->server_addr,0);
2135 multi_common_set_text("");
2139 // if he's double clicked, then select it and accept
2140 if(Multi_join_select_button.double_clicked()){
2142 Multi_join_select_button.get_mouse_pos(NULL,&y);
2144 if(item == Multi_join_list_selected){
2145 multi_join_button_pressed(MJ_ACCEPT);
2150 // return game n (0 based index)
2151 active_game *multi_join_get_game(int n)
2153 active_game *moveup = Active_game_head;
2160 moveup = moveup->next;
2161 while((moveup != Active_game_head) && (count != n)){
2162 moveup = moveup->next;
2165 if(moveup == Active_game_head){
2166 nprintf(("Network","Warning, couldn't find game item %d!\n",n));
2176 // scroll through the game list
2177 void multi_join_list_scroll_up()
2179 // if we're not at the beginning of the list, scroll up
2180 if(Multi_join_list_start_item != Active_game_head){
2181 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2183 MJ_LIST_START_DEC();
2185 gamesnd_play_iface(SND_SCROLL);
2187 gamesnd_play_iface(SND_GENERAL_FAIL);
2191 // scroll through the game list
2192 void multi_join_list_scroll_down()
2194 if((Active_game_count - Multi_join_list_start) > Mj_max_game_items[gr_screen.res]){
2195 Multi_join_list_start_item = Multi_join_list_start_item->next;
2197 MJ_LIST_START_INC();
2199 gamesnd_play_iface(SND_SCROLL);
2201 gamesnd_play_iface(SND_GENERAL_FAIL);
2205 void multi_join_list_page_up()
2207 // in this case, just set us to the beginning of the list
2208 if((Multi_join_list_start - Mj_max_game_items[gr_screen.res]) < 0){
2209 Multi_join_list_start_item = Active_game_head;
2211 MJ_LIST_START_SET(0);
2213 gamesnd_play_iface(SND_SCROLL);
2215 // otherwise page the whole thing up
2217 for(idx=0; idx<Mj_max_game_items[gr_screen.res]; idx++){
2218 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2220 MJ_LIST_START_DEC();
2222 gamesnd_play_iface(SND_SCROLL);
2226 void multi_join_list_page_down()
2230 // page the whole thing down
2231 while((count < Mj_max_game_items[gr_screen.res]) && ((Active_game_count - Multi_join_list_start) > Mj_max_game_items[gr_screen.res])){
2232 Multi_join_list_start_item = Multi_join_list_start_item->next;
2233 MJ_LIST_START_INC();
2238 gamesnd_play_iface(SND_SCROLL);
2241 void multi_join_cull_timeouts()
2243 active_game *backup;
2245 active_game *moveup = Active_game_head;
2247 // traverse through the entire list if any items exist
2251 if((moveup->heard_from_timer != -1) && (timestamp_elapsed(moveup->heard_from_timer))){
2252 Active_game_count--;
2254 // if this is the head of the list
2255 if(moveup == Active_game_head){
2256 // if this is the _only_ item on the list
2257 if(moveup->next == Active_game_head){
2258 // handle any gui details related to deleting this item
2259 multi_join_handle_item_cull(Active_game_head, count);
2261 free(Active_game_head);
2262 Active_game_head = NULL;
2265 // if there are other items on the list
2267 // handle any gui details related to deleting this item
2268 multi_join_handle_item_cull(moveup, count);
2270 Active_game_head = moveup->next;
2271 Active_game_head->prev = moveup->prev;
2272 Active_game_head->prev->next = Active_game_head;
2274 moveup = Active_game_head;
2277 // if its somewhere else on the list
2279 // handle any gui details related to deleting this item
2280 multi_join_handle_item_cull(moveup, count);
2282 // if its the last item on the list
2283 moveup->next->prev = moveup->prev;
2284 moveup->prev->next = moveup->next;
2286 // if it was the last element on the list, return
2287 if(moveup->next == Active_game_head){
2291 backup = moveup->next;
2297 moveup = moveup->next;
2300 } while(moveup != Active_game_head);
2304 // deep magic begins here.
2305 void multi_join_handle_item_cull(active_game *item, int item_index)
2307 // if this is the only item on the list, unset everything
2308 if(item->next == item){
2309 Multi_join_list_selected = -1;
2310 Multi_join_selected_item = NULL;
2313 Multi_join_slider.set_numberItems(0);
2315 MJ_LIST_START_SET(-1);
2316 Multi_join_list_start_item = NULL;
2322 // see if we should be adjusting our currently selected item
2323 if(item_index <= Multi_join_list_selected){
2324 // the selected item is the head of the list
2325 if(Multi_join_selected_item == Active_game_head){
2326 // move the pointer up since this item is about to be destroyed
2327 Multi_join_selected_item = Multi_join_selected_item->next;
2329 // if this is the item being deleted, select the previous one
2330 if(item == Multi_join_selected_item){
2332 Multi_join_selected_item = Multi_join_selected_item->prev;
2334 // decrement the selected index by 1
2335 Multi_join_list_selected--;
2337 // now we know its a previous item, so our pointer stays the same but our index goes down by one, since there will be
2338 // 1 less item on the list
2340 // decrement the selected index by 1
2341 Multi_join_list_selected--;
2346 // see if we should be adjusting out current start position
2347 if(item_index <= Multi_join_list_start){
2348 // the start position is the head of the list
2349 if(Multi_join_list_start_item == Active_game_head){
2350 // move the pointer up since this item is about to be destroyed
2351 Multi_join_list_start_item = Multi_join_list_start_item->next;
2353 // if this is the item being deleted, select the previous one
2354 if(item == Multi_join_list_start_item){
2355 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2357 // decrement the starting index by 1
2358 MJ_LIST_START_DEC();
2360 // but decrement the starting index by 1
2361 MJ_LIST_START_DEC();
2366 // maybe go back up a bit so that we always have a full page of items
2367 if(Active_game_count > Mj_max_game_items[gr_screen.res]){
2368 while((Active_game_count - Multi_join_list_start) < Mj_max_game_items[gr_screen.res]){
2369 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2370 MJ_LIST_START_DEC();
2374 // set slider location
2376 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);
2377 Multi_join_slider.force_currentItem(Multi_join_list_start);
2381 void multi_join_send_join_request(int as_observer)
2383 // don't do anything if we have no items selected
2384 if(Multi_join_selected_item == NULL){
2388 // 5/26/98 -- for team v team games, don't allow ingame joining :-(
2389 if ( (Multi_join_selected_item->flags & AG_FLAG_TEAMS) && (Multi_join_selected_item->flags & (AG_FLAG_PAUSE|AG_FLAG_IN_MISSION)) ) {
2390 popup(0, 1, POPUP_OK, XSTR("Joining ingame is currently not allowed for team vs. team games",772));
2394 memset(&Multi_join_request,0,sizeof(join_request));
2396 // if the netgame is in password mode, put up a request for the password
2397 if(Multi_join_selected_item->flags & AG_FLAG_PASSWD){
2398 if(!multi_passwd_popup(Multi_join_request.passwd)){
2402 nprintf(("Password : %s\n",Multi_join_request.passwd));
2405 // fill out the join request struct
2406 strcpy(Multi_join_request.callsign,Player->callsign);
2407 if(strlen(Player->image_filename) > 0){
2408 strcpy(Multi_join_request.image_filename, Player->image_filename);
2411 if(strlen(Player->squad_filename) > 0){
2412 strcpy(Multi_join_request.squad_filename, Player->squad_filename);
2416 // tracker id (if any)
2417 Multi_join_request.tracker_id = Multi_tracker_id;
2419 // player's rank (at least, what he wants you to _believe_)
2420 Multi_join_request.player_rank = (ubyte)Player->stats.rank;
2423 Multi_join_request.flags = 0;
2425 Multi_join_request.flags |= JOIN_FLAG_AS_OBSERVER;
2428 // if the player has hacked data
2429 if(game_hacked_data()){
2430 Multi_join_request.flags |= JOIN_FLAG_HAXOR;
2435 strncpy(Multi_join_request.pxo_squad_name, Multi_tracker_squad_name, LOGIN_LEN);
2438 // version of this server
2439 Multi_join_request.version = MULTI_FS_SERVER_VERSION;
2441 // server compatible version
2442 Multi_join_request.comp_version = MULTI_FS_SERVER_COMPATIBLE_VERSION;
2444 // his local player options
2445 memcpy(&Multi_join_request.player_options,&Player->m_local_options,sizeof(multi_local_options));
2447 // set the server address for the netgame
2448 memcpy(&Netgame.server_addr,&Multi_join_selected_item->server_addr,sizeof(net_addr));
2450 // send a join request to the guy
2451 send_join_packet(&Multi_join_selected_item->server_addr,&Multi_join_request);
2454 Multi_join_sent_stamp = timestamp(MULTI_JOIN_SENT_WAIT);
2457 multi_common_add_notify(XSTR("Sending join request...",773));
2460 void multi_join_create_game()
2462 // maybe warn the player about possible crappy server conditions
2463 if(!multi_join_maybe_warn()){
2467 // make sure to flag ourself as being the master
2468 Net_player->flags |= (NETINFO_FLAG_AM_MASTER | NETINFO_FLAG_GAME_HOST);
2469 Net_player->state = NETPLAYER_STATE_HOST_SETUP;
2471 // if we're in PXO mode, mark it down in our player struct
2472 if(MULTI_IS_TRACKER_GAME){
2473 Player->flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
2474 Player->save_flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
2476 // otherwise, if he's already played PXO games, warn him
2479 if(Player->flags & PLAYER_FLAGS_HAS_PLAYED_PXO){
2480 if(!multi_join_warn_pxo()){
2487 gameseq_post_event(GS_EVENT_MULTI_START_GAME);
2488 gamesnd_play_iface(SND_USER_SELECT);
2491 void multi_join_reset_join_stamp()
2493 // unset the timestamp here so the user can immediately send another join request
2494 Multi_join_sent_stamp = -1;
2495 multi_common_add_notify("");
2498 void multi_join_blit_top_stuff()
2500 // blit the cd icon if he has one
2501 if(Multi_has_cd && (Multi_common_icons[MICON_CD] != -1)){
2504 bm_get_info(Multi_common_icons[MICON_CD], &cd_w, NULL, NULL, NULL, NULL);
2506 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
2507 gr_bitmap((gr_screen.max_w / 2) - (cd_w / 2), Mj_cd_coords[gr_screen.res]);
2511 #define CW_CODE_CANCEL 0 // cancel the action
2512 #define CW_CODE_OK 1 // continue anyway
2513 #define CW_CODE_INFO 2 // gimme some more information
2515 #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)
2516 #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)
2518 #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)
2519 #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)
2521 int multi_join_warn_update_low(int code)
2525 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),LOW_WARN_TEXT);
2528 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),LOW_INFO_TEXT);
2531 return CW_CODE_CANCEL;
2534 int multi_join_warn_update_medium(int code)
2538 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),MED_WARN_TEXT);
2541 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),MED_INFO_TEXT);
2544 return CW_CODE_CANCEL;
2547 int multi_join_maybe_warn()
2551 // if the player is set for low updates
2552 if(Player->m_local_options.obj_update_level == OBJ_UPDATE_LOW){
2555 code = multi_join_warn_update_low(code);
2556 } while((code != CW_CODE_CANCEL) && (code != CW_CODE_OK));
2561 // if the player is set for medium updates
2562 else if(Player->m_local_options.obj_update_level == OBJ_UPDATE_MEDIUM){
2565 code = multi_join_warn_update_medium(code);
2566 } while((code != CW_CODE_CANCEL) && (code != CW_CODE_OK));
2574 int multi_join_warn_pxo()
2576 // 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;
2580 void multi_join_blit_protocol()
2582 gr_set_color_fast(&Color_bright);
2584 switch(Socket_type){
2587 gr_string(5, 2, "TCP");
2591 gr_string(5, 2, "IPX");
2597 // -------------------------------------------------------------------------------------------------
2599 // MULTIPLAYER START GAME screen
2604 #define MULTI_SG_PALETTE "InterfacePalette"
2606 static char *Multi_sg_bitmap_fname[GR_NUM_RESOLUTIONS] = {
2607 "MultiStartGame", // GR_640
2608 "2_MultiStartGame" // GR_1024
2611 static char *Multi_sg_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
2612 "MultiStartGame-M", // GR_640
2613 "2_MultiStartGame-M" // GR_1024
2618 int Multi_sg_rank_max_display[GR_NUM_RESOLUTIONS] = {
2623 // constants for coordinate look ups
2624 #define MSG_X_COORD 0
2625 #define MSG_Y_COORD 1
2626 #define MSG_W_COORD 2
2627 #define MSG_H_COORD 3
2631 // input password field
2632 int Msg_passwd_coords[GR_NUM_RESOLUTIONS][4] = {
2645 // input game title field
2646 int Msg_title_coords[GR_NUM_RESOLUTIONS][4] = {
2659 // rank selected field
2660 int Msg_rank_sel_coords[GR_NUM_RESOLUTIONS][4] = {
2674 int Msg_rank_list_coords[GR_NUM_RESOLUTIONS][4] = {
2690 #define MULTI_SG_NUM_BUTTONS 12
2691 #define MSG_OPEN_GAME 0
2692 #define MSG_CLOSED_GAME 1
2693 #define MSG_PASSWD_GAME 2
2694 #define MSG_RESTRICTED_GAME 3
2695 #define MSG_RANK_SET_GAME 4
2696 #define MSG_RANK_SCROLL_UP 5
2697 #define MSG_RANK_SCROLL_DOWN 6
2698 #define MSG_RANK_ABOVE 7
2699 #define MSG_RANK_BELOW 8
2701 #define MSG_OPTIONS 10
2702 #define MSG_ACCEPT 11
2704 #define MULTI_SG_NUM_BUTTONS 10
2705 #define MSG_OPEN_GAME 0
2706 //#define MSG_CLOSED_GAME 1
2707 //#define MSG_RESTRICTED_GAME 2
2708 #define MSG_PASSWD_GAME 1
2709 #define MSG_RANK_SET_GAME 2
2710 #define MSG_RANK_SCROLL_UP 3
2711 #define MSG_RANK_SCROLL_DOWN 4
2712 #define MSG_RANK_ABOVE 5
2713 #define MSG_RANK_BELOW 6
2715 #define MSG_OPTIONS 8
2716 #define MSG_ACCEPT 9
2719 UI_WINDOW Multi_sg_window; // the window object for the join screen
2720 UI_BUTTON Multi_sg_rank_button; // for selecting the rank marker
2721 UI_INPUTBOX Multi_sg_game_name; // for Netgame.name
2722 UI_INPUTBOX Multi_sg_game_passwd; // for Netgame.passwd
2723 int Multi_sg_bitmap; // the background bitmap
2725 ui_button_info Multi_sg_buttons[GR_NUM_RESOLUTIONS][MULTI_SG_NUM_BUTTONS] = {
2728 ui_button_info("MSG_00", 75, 111, -1, -1, 0),
2729 ui_button_info("MSG_01", 75, 139, -1, -1, 1),
2730 ui_button_info("MSG_02", 75, 164, -1, -1, 2),
2731 ui_button_info("MSG_03", 75, 199, -1, -1, 3),
2732 ui_button_info("MSG_04", 75, 243, -1, -1, 4),
2733 ui_button_info("MSG_05", 376, 231, -1, -1, 5),
2734 ui_button_info("MSG_06", 376, 258, -1, -1, 6),
2735 ui_button_info("MSG_07", 376, 291, -1, -1, 7),
2736 ui_button_info("MSG_08", 376, 320, -1, -1, 8),
2737 ui_button_info("MSG_09", 469, 427, -1, -1, 9),
2738 ui_button_info("MSG_10", 447, 452, -1, -1, 10),
2739 ui_button_info("MSG_11", 561, 411, -1, -1, 11),
2741 ui_button_info("MSG_00", 1, 184, 34, 191, 2), // open
2742 // ui_button_info("MSG_01", 1, 159, 34, 166, 1), // closed
2743 // ui_button_info("MSG_02", 1, 184, 34, 191, 2), // restricted
2744 ui_button_info("MSG_03", 1, 209, 34, 218, 3), // password
2745 ui_button_info("MSG_04", 1, 257, 34, 266, 4), // rank set
2746 ui_button_info("MSG_05", 1, 282, -1, -1, 5), // rank scroll up
2747 ui_button_info("MSG_06", 1, 307, -1, -1, 6), // rank scroll down
2748 ui_button_info("MSG_07", 177, 282, 210, 290, 7), // rank above
2749 ui_button_info("MSG_08", 177, 307, 210, 315, 8), // rank below
2750 ui_button_info("MSG_09", 536, 429, 500, 440, 9), // help
2751 ui_button_info("MSG_10", 536, 454, 479, 464, 10), // options
2752 ui_button_info("MSG_11", 576, 432, 571, 415, 11), // accept
2756 ui_button_info("2_MSG_00", 2, 295, 51, 307, 2), // open
2757 // ui_button_info("2_MSG_01", 2, 254, 51, 267, 1), // closed
2758 // ui_button_info("2_MSG_02", 2, 295, 51, 307, 2), // restricted
2759 ui_button_info("2_MSG_03", 2, 335, 51, 350, 3), // password
2760 ui_button_info("2_MSG_04", 2, 412, 51, 426, 4), // rank set
2761 ui_button_info("2_MSG_05", 2, 452, -1, -1, 5), // rank scroll up
2762 ui_button_info("2_MSG_06", 2, 492, -1, -1, 6), // rank scroll down
2763 ui_button_info("2_MSG_07", 284, 452, 335, 465, 7), // rank above
2764 ui_button_info("2_MSG_08", 284, 492, 335, 505, 8), // rank below
2765 ui_button_info("2_MSG_09", 858, 687, 817, 706, 9), // help
2766 ui_button_info("2_MSG_10", 858, 728, 797, 743, 10), // options
2767 ui_button_info("2_MSG_11", 921, 692, 921, 664, 11), // accept
2768 #ifdef MAKE_FS1 // filler for extra FS1 buttons
2769 ui_button_info("none", -1, -1, -1, -1, -1),
2770 ui_button_info("none", -1, -1, -1, -1, -1),
2776 #define MULTI_SG_NUM_TEXT 11
2778 UI_XSTR Multi_sg_text[GR_NUM_RESOLUTIONS][MULTI_SG_NUM_TEXT] = {
2780 {"Open", 1322, 34, 191, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_OPEN_GAME].button},
2781 // {"Closed", 1323, 34, 166, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_CLOSED_GAME].button},
2782 // {"Restricted", 1324, 34, 191, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RESTRICTED_GAME].button},
2783 {"Password Protected", 1325, 34, 218, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_PASSWD_GAME].button},
2784 {"Allow Rank", 1326, 34, 266, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_SET_GAME].button},
2785 {"Above", 1327, 210, 290, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_ABOVE].button},
2786 {"Below", 1328, 210, 315, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_BELOW].button},
2787 {"Help", 928, 500, 440, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_HELP].button},
2788 {"Options", 1036, 479, 464, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_OPTIONS].button},
2789 {"Accept", 1035, 571, 415, UI_XSTR_COLOR_PINK, -1, &Multi_sg_buttons[0][MSG_ACCEPT].button},
2790 {"Start Game", 1329, 26, 10, UI_XSTR_COLOR_GREEN, -1, NULL},
2791 {"Title", 1330, 26, 31, UI_XSTR_COLOR_GREEN, -1, NULL},
2792 {"Game Type", 1331, 12, 165, UI_XSTR_COLOR_GREEN, -1, NULL},
2795 {"Open", 1322, 51, 307, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_OPEN_GAME].button},
2796 // {"Closed", 1323, 51, 267, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_CLOSED_GAME].button},
2797 // {"Restricted", 1324, 51, 307, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RESTRICTED_GAME].button},
2798 {"Password Protected", 1325, 51, 350, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_PASSWD_GAME].button},
2799 {"Allow Rank", 1326, 51, 426, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_SET_GAME].button},
2800 {"Above", 1327, 335, 465, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_ABOVE].button},
2801 {"Below", 1328, 335, 505, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_BELOW].button},
2802 {"Help", 928, 817, 706, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_HELP].button},
2803 {"Options", 1036, 797, 743, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_OPTIONS].button},
2804 {"Accept", 1035, 921, 664, UI_XSTR_COLOR_PINK, -1, &Multi_sg_buttons[1][MSG_ACCEPT].button},
2805 {"Start Game", 1329, 42, 22, UI_XSTR_COLOR_GREEN, -1, NULL},
2806 {"Title", 1330, 42, 50, UI_XSTR_COLOR_GREEN, -1, NULL},
2807 {"Game Type", 1331, 20, 264, UI_XSTR_COLOR_GREEN, -1, NULL},
2812 // starting index for displaying ranks
2813 int Multi_sg_rank_start;
2814 int Multi_sg_rank_select;
2816 // netgame pointer to indirect through
2817 netgame_info *Multi_sg_netgame;
2819 // hold temporary values in this structure when on a standalone server
2820 netgame_info Multi_sg_netgame_temp;
2822 // forward declarations
2823 void multi_sg_check_buttons();
2824 void multi_sg_button_pressed(int n);
2825 void multi_sg_init_gamenet();
2826 void multi_sg_draw_radio_buttons();
2827 void multi_sg_rank_scroll_up();
2828 void multi_sg_rank_scroll_down();
2829 void multi_sg_rank_display_stuff();
2830 void multi_sg_rank_process_select();
2831 void multi_sg_rank_build_name(char *in,char *out);
2832 void multi_sg_check_passwd();
2833 void multi_sg_check_name();
2834 void multi_sg_release_passwd();
2835 int multi_sg_rank_select_valid(int rank);
2836 void multi_sg_select_rank_default();
2838 // function which takes a rank name and returns the index. Useful for commandline options
2839 // for above and below rank. We return the index of the rank in the Ranks[] array. If
2840 // the rank isn't found, we return -1
2841 int multi_start_game_rank_from_name( char *rank ) {
2845 for ( i = 0; i <= MAX_FREESPACE1_RANK; i++ ) {
2847 for ( i = 0; i <= MAX_FREESPACE2_RANK; i++ ) {
2849 if ( !stricmp(Ranks[i].name, rank) ) {
2857 void multi_start_game_init()
2861 // initialize the gamenet
2862 multi_sg_init_gamenet();
2864 // create the interface window
2865 Multi_sg_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
2866 Multi_sg_window.set_mask_bmap(Multi_sg_bitmap_mask_fname[gr_screen.res]);
2868 // load the background bitmap
2869 Multi_sg_bitmap = bm_load(Multi_sg_bitmap_fname[gr_screen.res]);
2870 if(Multi_sg_bitmap < 0){
2871 // we failed to load the bitmap - this is very bad
2875 // initialize the common notification messaging
2876 multi_common_notify_init();
2878 // initialize the common text area
2879 multi_common_set_text("");
2881 // use the common interface palette
2882 multi_common_set_palette();
2884 // create the interface buttons
2885 for(idx=0; idx<MULTI_SG_NUM_BUTTONS; idx++){
2886 // create the object
2887 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);
2889 // set the sound to play when highlighted
2890 Multi_sg_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
2892 // set the ani for the button
2893 Multi_sg_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sg_buttons[gr_screen.res][idx].filename);
2896 Multi_sg_buttons[gr_screen.res][idx].button.link_hotspot(Multi_sg_buttons[gr_screen.res][idx].hotspot);
2901 for(idx=0; idx<MULTI_SG_NUM_TEXT; idx++){
2902 Multi_sg_window.add_XSTR(&Multi_sg_text[gr_screen.res][idx]);
2906 // load the help overlay
2907 help_overlay_load(MULTI_START_OVERLAY);
2908 help_overlay_set_state(MULTI_START_OVERLAY,0);
2910 // intiialize the rank selection items
2911 multi_sg_select_rank_default();
2912 Multi_sg_rank_start = Multi_sg_rank_select;
2914 // create the rank select button
2915 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);
2916 Multi_sg_rank_button.hide();
2918 // create the netgame name input box
2919 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);
2921 // create the netgame password input box, and disable it by default
2922 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);
2923 Multi_sg_game_passwd.hide();
2924 Multi_sg_game_passwd.disable();
2926 // set the netgame text to this gadget and make it have focus
2927 Multi_sg_game_name.set_text(Multi_sg_netgame->name);
2928 Multi_sg_game_name.set_focus();
2930 // if starting a netgame, set the name of the game and any other options that are appropriate
2931 if ( Cmdline_start_netgame ) {
2932 if ( Cmdline_game_name != NULL ) {
2933 strcpy( Multi_sg_netgame->name, Cmdline_game_name );
2934 Multi_sg_game_name.set_text(Multi_sg_netgame->name);
2937 // deal with the different game types -- only one should even be active, so we will just go down
2938 // the line. Last one wins.
2939 if ( Cmdline_closed_game ) {
2940 Multi_sg_netgame->mode = NG_MODE_CLOSED;
2941 } else if ( Cmdline_restricted_game ) {
2942 Multi_sg_netgame->mode = NG_MODE_RESTRICTED;
2943 } else if ( Cmdline_game_password != NULL ) {
2944 Multi_sg_netgame->mode = NG_MODE_PASSWORD;
2945 strcpy(Multi_sg_netgame->passwd, Cmdline_game_password);
2946 Multi_sg_game_passwd.set_text(Multi_sg_netgame->passwd);
2949 // deal with rank above and rank below
2950 if ( (Cmdline_rank_above != NULL) || (Cmdline_rank_below != NULL) ) {
2954 if ( Cmdline_rank_above != NULL ) {
2955 rank_str = Cmdline_rank_above;
2957 rank_str = Cmdline_rank_below;
2960 // try and get the rank index from the name -- if found, then set the rank base
2961 // and the game type. apparently we only support either above or below, not both
2962 // together, so I make a random choice
2963 rank = multi_start_game_rank_from_name( rank_str );
2965 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
2967 // now an arbitrary decision
2968 if ( Cmdline_rank_above != NULL ) {
2969 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
2971 Multi_sg_netgame->mode = NG_MODE_RANK_BELOW;
2976 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
2980 void multi_start_game_do()
2982 // return here since we will be moving to the next stage anyway -- I don't want to see the backgrounds of
2983 // all the screens for < 1 second for every screen we automatically move to.
2984 if ( Cmdline_start_netgame ) {
2988 int k = Multi_sg_window.process();
2990 // process any keypresses
2993 if(help_overlay_active(MULTI_START_OVERLAY)){
2994 help_overlay_set_state(MULTI_START_OVERLAY,0);
2996 gamesnd_play_iface(SND_USER_SELECT);
2997 multi_quit_game(PROMPT_NONE);
3002 case KEY_LCTRL + KEY_ENTER :
3003 case KEY_RCTRL + KEY_ENTER :
3004 gamesnd_play_iface(SND_COMMIT_PRESSED);
3005 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
3009 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
3010 help_overlay_set_state(MULTI_START_OVERLAY, 0);
3013 // check to see if the user has selected a different rank
3014 multi_sg_rank_process_select();
3016 // check any button presses
3017 multi_sg_check_buttons();
3019 // check to see if any of the input boxes have changed, and update the appropriate Netgame fields if necessary
3020 multi_sg_check_passwd();
3021 multi_sg_check_name();
3023 // draw the background, etc
3025 GR_MAYBE_CLEAR_RES(Multi_sg_bitmap);
3026 if(Multi_sg_bitmap != -1){
3027 gr_set_bitmap(Multi_sg_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
3030 Multi_sg_window.draw();
3032 // display rank stuff
3033 multi_sg_rank_display_stuff();
3035 // display any pending notification messages
3036 multi_common_notify_do();
3038 // draw all radio button
3039 multi_sg_draw_radio_buttons();
3041 // draw the help overlay
3042 help_overlay_maybe_blit(MULTI_START_OVERLAY);
3048 void multi_start_game_close()
3050 // if i'm the host on a standalone server, send him my start game options (passwd, mode, etc)
3051 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
3052 multi_options_update_start_game(Multi_sg_netgame);
3055 // unload any bitmaps
3056 if(!bm_unload(Multi_sg_bitmap)){
3057 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_sg_bitmap_fname[gr_screen.res]));
3060 // unload the help overlay
3061 help_overlay_unload(MULTI_START_OVERLAY);
3063 // destroy the UI_WINDOW
3064 Multi_sg_window.destroy();
3067 void multi_sg_check_buttons()
3070 for(idx=0;idx<MULTI_SG_NUM_BUTTONS;idx++){
3071 // we only really need to check for one button pressed at a time, so we can break after
3073 if(Multi_sg_buttons[gr_screen.res][idx].button.pressed()){
3074 multi_sg_button_pressed(idx);
3080 void multi_sg_button_pressed(int n)
3083 // go to the options screen
3085 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
3090 if(!help_overlay_active(MULTI_START_OVERLAY)){
3091 help_overlay_set_state(MULTI_START_OVERLAY,1);
3093 help_overlay_set_state(MULTI_START_OVERLAY,0);
3097 // the open button was pressed
3099 // if the closed option is selected
3100 if(Multi_sg_netgame->mode != NG_MODE_OPEN){
3101 Multi_sg_netgame->mode = NG_MODE_OPEN;
3103 gamesnd_play_iface(SND_USER_SELECT);
3105 // release the password control if necessary
3106 multi_sg_release_passwd();
3108 // if its already selected
3110 gamesnd_play_iface(SND_GENERAL_FAIL);
3115 // the open button was pressed
3116 case MSG_CLOSED_GAME:
3117 // if the closed option is selected
3118 if(Multi_sg_netgame->mode != NG_MODE_CLOSED){
3119 Multi_sg_netgame->mode = NG_MODE_CLOSED;
3121 gamesnd_play_iface(SND_USER_SELECT);
3123 // release the password control if necessary
3124 multi_sg_release_passwd();
3126 // if its already selected
3128 gamesnd_play_iface(SND_GENERAL_FAIL);
3133 // toggle password protection
3134 case MSG_PASSWD_GAME:
3135 // if we selected it
3136 if(Multi_sg_netgame->mode != NG_MODE_PASSWORD){
3137 gamesnd_play_iface(SND_USER_SELECT);
3139 Multi_sg_game_passwd.enable();
3140 Multi_sg_game_passwd.unhide();
3141 Multi_sg_game_passwd.set_focus();
3143 Multi_sg_netgame->mode = NG_MODE_PASSWORD;
3145 // copy in the current network password
3146 Multi_sg_game_passwd.set_text(Multi_sg_netgame->passwd);
3148 gamesnd_play_iface(SND_GENERAL_FAIL);
3153 // toggle "restricted" on or off
3154 case MSG_RESTRICTED_GAME:
3155 if(Multi_sg_netgame->mode != NG_MODE_RESTRICTED){
3156 gamesnd_play_iface(SND_USER_SELECT);
3157 Multi_sg_netgame->mode = NG_MODE_RESTRICTED;
3159 // release the password control if necessary
3160 multi_sg_release_passwd();
3162 gamesnd_play_iface(SND_GENERAL_FAIL);
3167 // turn off all rank requirements
3168 case MSG_RANK_SET_GAME:
3169 // if either is set, then turn then both off
3170 if((Multi_sg_netgame->mode != NG_MODE_RANK_BELOW) && (Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE)){
3171 gamesnd_play_iface(SND_USER_SELECT);
3173 // set it to the default case if we're turning it off
3174 multi_sg_select_rank_default();
3175 Multi_sg_rank_start = Multi_sg_rank_select;
3177 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
3179 // release the password control if necessary
3180 multi_sg_release_passwd();
3182 gamesnd_play_iface(SND_GENERAL_FAIL);
3186 // rank above was pressed
3187 case MSG_RANK_ABOVE :
3188 if((Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE) || (Multi_sg_netgame->mode == NG_MODE_RANK_BELOW)){
3189 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
3191 // select the first item
3192 multi_sg_select_rank_default();
3193 Multi_sg_rank_start = Multi_sg_rank_select;
3196 gamesnd_play_iface(SND_USER_SELECT);
3198 gamesnd_play_iface(SND_GENERAL_FAIL);
3202 // rank below was pressed
3203 case MSG_RANK_BELOW :
3204 if((Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE) || (Multi_sg_netgame->mode == NG_MODE_RANK_BELOW)){
3205 Multi_sg_netgame->mode = NG_MODE_RANK_BELOW;
3207 // select the first item
3208 multi_sg_select_rank_default();
3209 Multi_sg_rank_start = Multi_sg_rank_select;
3212 gamesnd_play_iface(SND_USER_SELECT);
3214 gamesnd_play_iface(SND_GENERAL_FAIL);
3218 // scroll the rank list up
3219 case MSG_RANK_SCROLL_UP:
3220 multi_sg_rank_scroll_up();
3223 // scroll the rank list down
3224 case MSG_RANK_SCROLL_DOWN:
3225 multi_sg_rank_scroll_down();
3228 // move to the create game screen
3230 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
3231 gamesnd_play_iface(SND_COMMIT_PRESSED);
3235 gamesnd_play_iface(SND_GENERAL_FAIL);
3236 multi_common_add_notify(XSTR("Not implemented yet!",760));
3241 // NOTE : this is where all Netgame initialization should take place on the host
3242 void multi_sg_init_gamenet()
3244 char buf[128],out_name[128];
3246 net_player *server_save;
3248 // back this data up in case we are already connected to a standalone
3249 memcpy(&save,&Netgame.server_addr,sizeof(net_addr));
3250 server_save = Netgame.server;
3252 // remove campaign flags
3253 Game_mode &= ~(GM_CAMPAIGN_MODE);
3255 // clear out the Netgame structure and start filling in the values
3256 memset( &Netgame, 0, sizeof(Netgame) );
3257 memset( &Multi_sg_netgame_temp, 0, sizeof(netgame_info) );
3259 // if we're on the standalone, we're not the server, so we don't care about setting the netgame state
3260 if(Net_player->state != NETPLAYER_STATE_STD_HOST_SETUP){
3261 Netgame.game_state = NETGAME_STATE_HOST_SETUP;
3262 Multi_sg_netgame = &Netgame;
3265 ml_string(NOX("Starting netgame as Host/Server"));
3267 Multi_sg_netgame = &Multi_sg_netgame_temp;
3271 memcpy(&temp_addr.s_addr, &Netgame.server_addr, 4);
3272 char *server_addr = inet_ntoa(temp_addr);
3273 ml_printf(NOX("Starting netgame as Host on Standalone server : %s"), (server_addr == NULL) ? NOX("Unknown") : server_addr);
3276 Net_player->tracker_player_id = Multi_tracker_id;
3278 Multi_sg_netgame->security = (rand() % 32766) + 1; // get some random security number
3279 Multi_sg_netgame->mode = NG_MODE_OPEN;
3280 Multi_sg_netgame->rank_base = RANK_ENSIGN;
3281 if(Multi_sg_netgame->security < 16){
3282 Multi_sg_netgame->security += 16;
3285 // set the version_info field
3286 Multi_sg_netgame->version_info = NG_VERSION_ID;
3288 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
3289 Netgame.host = Net_player;
3292 // set the default netgame flags
3293 Multi_sg_netgame->flags = 0;
3295 // intialize endgame stuff
3296 multi_endgame_init();
3298 // load in my netgame options
3299 multi_options_netgame_load(&Netgame.options);
3301 // load my local netplayer options
3302 multi_options_local_load(&Net_player->p_info.options, Net_player);
3304 // setup the default game name, taking care of string length and player callsigns
3305 memset(out_name,0,128);
3307 pilot_format_callsign_personal(Player->callsign,out_name);
3308 sprintf(buf, XSTR("%s game",782), out_name); // [[ %s will be a pilot's name ]]
3309 if ( strlen(buf) > MAX_GAMENAME_LEN ){
3310 strcpy(buf, XSTR("Temporary name",783));
3312 strcpy(Multi_sg_netgame->name, buf);
3314 // set the default qos and duration
3315 multi_voice_maybe_update_vars(Netgame.options.voice_qos,Netgame.options.voice_record_time);
3317 // make sure to set the server correctly (me or the standalone)
3318 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3319 memcpy(&Netgame.server_addr, &Psnet_my_addr,sizeof(net_addr));
3320 Netgame.server = Net_player;
3321 Net_player->player_id = multi_get_new_id();
3323 // setup debug flags
3324 Netgame.debug_flags = 0;
3326 if(!Cmdline_server_firing){
3327 Netgame.debug_flags |= NETD_FLAG_CLIENT_FIRING;
3329 if(!Cmdline_client_dodamage){
3330 Netgame.debug_flags |= NETD_FLAG_CLIENT_NODAMAGE;
3334 memcpy(&Netgame.server_addr,&save,sizeof(net_addr));
3335 Netgame.server = server_save;
3338 // if I have a cd or not
3340 Net_player->flags |= NETINFO_FLAG_HAS_CD;
3343 // if I have hacked data
3344 if(game_hacked_data()){
3345 Net_player->flags |= NETINFO_FLAG_HAXOR;
3348 // assign my player struct and other data
3349 Net_player->flags |= (NETINFO_FLAG_CONNECTED | NETINFO_FLAG_DO_NETWORKING);
3350 Net_player->s_info.voice_token_timestamp = -1;
3352 // if we're supposed to flush our cache directory, do so now
3353 if(Net_player->p_info.options.flags & MLO_FLAG_FLUSH_CACHE){
3354 multi_flush_multidata_cache();
3357 ml_string(NOX("Flushing multi-data cache"));
3363 void multi_sg_draw_radio_buttons()
3365 // draw the appropriate radio button
3366 switch(Multi_sg_netgame->mode){
3368 Multi_sg_buttons[gr_screen.res][MSG_OPEN_GAME].button.draw_forced(2);
3372 case NG_MODE_CLOSED:
3373 Multi_sg_buttons[gr_screen.res][MSG_CLOSED_GAME].button.draw_forced(2);
3377 case NG_MODE_PASSWORD:
3378 Multi_sg_buttons[gr_screen.res][MSG_PASSWD_GAME].button.draw_forced(2);
3382 case NG_MODE_RESTRICTED:
3383 Multi_sg_buttons[gr_screen.res][MSG_RESTRICTED_GAME].button.draw_forced(2);
3387 case NG_MODE_RANK_ABOVE:
3388 Multi_sg_buttons[gr_screen.res][MSG_RANK_SET_GAME].button.draw_forced(2);
3389 Multi_sg_buttons[gr_screen.res][MSG_RANK_ABOVE].button.draw_forced(2);
3391 case NG_MODE_RANK_BELOW:
3392 Multi_sg_buttons[gr_screen.res][MSG_RANK_SET_GAME].button.draw_forced(2);
3393 Multi_sg_buttons[gr_screen.res][MSG_RANK_BELOW].button.draw_forced(2);
3398 void multi_sg_rank_scroll_up()
3400 // if he doesn't have either of the rank flags set, then ignore this
3401 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3405 if(Multi_sg_rank_start > 0){
3406 Multi_sg_rank_start--;
3407 gamesnd_play_iface(SND_SCROLL);
3409 gamesnd_play_iface(SND_GENERAL_FAIL);
3413 void multi_sg_rank_scroll_down()
3415 // if he doesn't have either of the rank flags set, then ignore this
3416 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3420 if((NUM_RANKS - Multi_sg_rank_start) > Multi_sg_rank_max_display[gr_screen.res]){
3421 Multi_sg_rank_start++;
3422 gamesnd_play_iface(SND_SCROLL);
3424 gamesnd_play_iface(SND_GENERAL_FAIL);
3428 void multi_sg_rank_display_stuff()
3433 // if he doesn't have either of the rank flags set, then ignore this
3434 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3438 // display the list of ranks
3439 y = Msg_rank_list_coords[gr_screen.res][MSG_Y_COORD];
3440 idx = Multi_sg_rank_start;
3442 while((count < NUM_RANKS) && (count < Multi_sg_rank_max_display[gr_screen.res]) && (idx < NUM_RANKS)){
3443 // if its the selected item, then color it differently
3444 if(idx == Multi_sg_rank_select){
3445 gr_set_color_fast(&Color_text_selected);
3447 gr_set_color_fast(&Color_text_normal);
3451 multi_sg_rank_build_name(Ranks[idx].name,rank_name);
3452 gr_string(Msg_rank_list_coords[gr_screen.res][MSG_X_COORD],y,rank_name);
3460 // display the selected rank
3462 gr_set_color_fast(&Color_bright);
3463 multi_sg_rank_build_name(Ranks[Multi_sg_netgame->rank_base].name,rank_name);
3464 gr_string(Msg_rank_sel_coords[gr_screen.res][MSG_X_COORD],Msg_rank_sel_coords[gr_screen.res][MSG_Y_COORD],rank_name);
3468 void multi_sg_rank_process_select()
3472 // if he doesn't have either of the rank flags set, then ignore this
3473 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3477 // see if he's clicked on an item on the rank list
3478 if(Multi_sg_rank_button.pressed()){
3480 Multi_sg_rank_button.get_mouse_pos(NULL,&y);
3483 if(item + Multi_sg_rank_start < NUM_RANKS){
3484 // evaluate whether this rank is valid for the guy to pick
3485 if(multi_sg_rank_select_valid(item + Multi_sg_rank_start)){
3486 gamesnd_play_iface(SND_USER_SELECT);
3488 Multi_sg_rank_select = item + Multi_sg_rank_start;
3490 // set the Netgame rank
3491 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
3493 gamesnd_play_iface(SND_GENERAL_FAIL);
3495 memset(string,0,255);
3496 sprintf(string,XSTR("Illegal value for a host of your rank (%s)\n",784),Ranks[Net_player->player->stats.rank].name);
3497 multi_common_add_notify(string);
3503 void multi_sg_rank_build_name(char *in,char *out)
3509 first = strtok(use," ");
3511 // just copy the string
3516 // if the first part of the string is lieutenant, then abbreivate it and tack on the rest of the string
3517 if (stricmp(first,XSTR("lieutenant",785)) == 0) {
3518 first = strtok(NULL, NOX("\n"));
3520 // if he's not just a plain lieutenant
3522 strcpy(out,XSTR("Lt. ",786)); // [[ lieutenant ]]
3525 // if he _is_ just a plain lieutenant
3534 void multi_sg_check_passwd()
3536 // check to see if the password input box has been pressed
3537 if(Multi_sg_game_passwd.changed()){
3538 Multi_sg_game_passwd.get_text(Multi_sg_netgame->passwd);
3542 void multi_sg_check_name()
3544 // check to see if the game name input box has been pressed
3545 if(Multi_sg_game_name.changed()){
3546 Multi_sg_game_name.get_text(Multi_sg_netgame->name);
3550 void multi_sg_release_passwd()
3552 // hide and disable the password input box
3553 Multi_sg_game_passwd.hide();
3554 Multi_sg_game_passwd.disable();
3556 // set the focus back to the name input box
3557 Multi_sg_game_name.set_focus();
3560 int multi_sg_rank_select_valid(int rank)
3563 if(Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE){
3564 if(Net_player->player->stats.rank >= rank){
3570 if(Net_player->player->stats.rank <= rank){
3578 void multi_sg_select_rank_default()
3580 // pick our rank for now
3581 Multi_sg_rank_select = Net_player->player->stats.rank;
3583 // set the Netgame rank
3584 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
3587 // -------------------------------------------------------------------------------------------------
3589 // MULTIPLAYER CREATE GAME screen
3594 char *Multi_create_bitmap_fname[GR_NUM_RESOLUTIONS] = {
3595 "MultiCreate", // GR_640
3596 "2_MultiCreate" // GR_1024
3599 char *Multi_create_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
3600 "MultiCreate-M", // GR_640
3601 "2_MultiCreate-M" // GR_1024
3604 char *Multi_create_loading_fname[GR_NUM_RESOLUTIONS] = {
3605 "PleaseWait", // GR_640
3606 "2_PleaseWait" // GR_1024
3610 #define MULTI_CREATE_NUM_BUTTONS 23
3613 #define MC_SHOW_ALL 0
3614 #define MC_SHOW_COOP 1
3615 #define MC_SHOW_TEAM 2
3616 #define MC_SHOW_DOGFIGHT 3
3617 #define MC_PXO_REFRESH 4
3618 #define MC_PILOT_INFO 5
3619 #define MC_SCROLL_LIST_UP 6
3620 #define MC_SCROLL_LIST_DOWN 7
3621 #define MC_SCROLL_PLAYERS_UP 8
3622 #define MC_SCROLL_PLAYERS_DOWN 9
3623 #define MC_MISSION_FILTER 10
3624 #define MC_CAMPAIGN_FILTER 11
3625 #define MC_CANCEL 12
3630 #define MC_SCROLL_INFO_UP 17
3631 #define MC_SCROLL_INFO_DOWN 18
3632 #define MC_HOST_OPTIONS 19
3634 #define MC_OPTIONS 21
3635 #define MC_ACCEPT 22
3638 UI_WINDOW Multi_create_window; // the window object for the create screen
3639 UI_BUTTON Multi_create_player_select_button; // for selecting players
3640 UI_BUTTON Multi_create_list_select_button; // for selecting missions/campaigns
3641 int Multi_create_bitmap; // the background bitmap
3642 UI_SLIDER2 Multi_create_slider; // for create list
3644 // constants for coordinate look ups
3645 #define MC_X_COORD 0
3646 #define MC_Y_COORD 1
3647 #define MC_W_COORD 2
3648 #define MC_H_COORD 3
3650 ui_button_info Multi_create_buttons[GR_NUM_RESOLUTIONS][MULTI_CREATE_NUM_BUTTONS] = {
3653 ui_button_info("MC_18", 34, 131, -1, -1, 18), // all
3654 ui_button_info("MC_19", 72, 131, -1, -1, 19), // coop
3655 ui_button_info("MC_20", 120, 131, -1, -1, 20), // team
3656 // ui_button_info("MC_21", 166, 131, -1, -1, 21), // dogfight
3657 ui_button_info("none", -1, -1, -1, -1, -1), // dogfight (not used)
3658 ui_button_info("none", -1, -1, -1, -1, -1), // pxo?
3659 ui_button_info("MC_26", 540, 114, -1, -1, 26), // pilot info
3660 ui_button_info("MC_03", 0, 187, -1, -1, 2), // scroll list up
3661 ui_button_info("MC_02", 0, 227, -1, -1, 3), // scroll list down
3662 ui_button_info("MC_04", 611, 182, -1, -1, 4), // scroll players up
3663 ui_button_info("MC_05", 611, 221, -1, -1, 5), // scroll players down
3664 ui_button_info("MC_06", 18, 322, -1, -1, 6), // mission filter
3665 ui_button_info("MC_07", 18, 344, -1, -1, 7), // campaign filter
3666 ui_button_info("MC_10", 317, 339, -1, -1, 10), // cancel
3667 ui_button_info("MC_14", 464, 350, -1, -1, 14), // team 1
3668 ui_button_info("MC_15", 498, 350, -1, -1, 15), // team 2
3669 ui_button_info("MC_16", 527, 346, -1, -1, 16), // kick
3670 ui_button_info("MC_17", 572, 346, -1, -1, 17), // close
3671 ui_button_info("MC_08", 0, 398, -1, -1, 8), // scroll mission info up
3672 ui_button_info("MC_09", 0, 435, -1, -1, 9), // scroll mission info down
3673 ui_button_info("MC_27", 447, 402, -1, -1, 27), // host options
3674 ui_button_info("MC_11", 510, 428, -1, -1, 11), // help
3675 ui_button_info("MC_12", 510, 453, -1, -1, 12), // options
3676 ui_button_info("Mc_13", 562, 412, -1, -1, 13), // commit
3678 ui_button_info("MC_00", 32, 129, 36, 158, 0), // show all missions
3679 ui_button_info("MC_01", 76, 129, 71, 158, 1), // show coop missions
3680 ui_button_info("MC_02", 121, 129, 119, 158, 2), // show team missions
3681 ui_button_info("MC_03", 164, 129, 166, 158, 3), // show dogfight missions
3682 ui_button_info("MC_04", 399, 129, 229, 130, 4), // pxo mission refresh
3683 ui_button_info("MC_05", 567, 123, 467, 132, 5), // pilot info
3684 ui_button_info("MC_06", 1, 161, -1, -1, 6), // scroll mission info up
3685 ui_button_info("MC_08", 1, 304, -1, -1, 8), // scroll mission info down
3686 ui_button_info("MC_09", 613, 160, -1, -1, 9), // scroll players up
3687 ui_button_info("MC_10", 613, 202, -1, -1, 10), // scroll players down
3688 ui_button_info("MC_11", 22, 346, 27, 376, 11), // mission filter
3689 ui_button_info("MC_12", 104, 346, 110, 376, 12), // campaign filter
3690 ui_button_info("MC_13", 392, 341, 328, 364, 13), // cancel
3691 ui_button_info("MC_14", 472, 352, 482, 381, 14), // team 0
3692 ui_button_info("MC_15", 506, 352, 514, 381, 15), // team 1
3693 ui_button_info("MC_16", 539, 346, 539, 381, 16), // kick
3694 ui_button_info("MC_17", 589, 346, 582, 381, 17), // close
3695 ui_button_info("MC_18", 1, 406, -1, -1, 18), // scroll list up
3696 ui_button_info("MC_19", 1, 447, -1, -1, 19), // scroll list down
3697 ui_button_info("MC_20", 499, 434, 436, 423, 20), // host options
3698 ui_button_info("MC_21", 534, 426, -1, -1, 21), // help
3699 ui_button_info("MC_22", 534, 452, -1, -1, 22), // options
3700 ui_button_info("MC_23", 571, 426, 572, 413, 23), // commit
3704 ui_button_info("2_MC_00", 51, 207, 61, 253, 0), // show all missions
3705 ui_button_info("2_MC_01", 122, 207, 124, 253, 1), // show coop missions
3706 ui_button_info("2_MC_02", 193, 207, 194, 253, 2), // show team missions
3707 ui_button_info("2_MC_03", 263, 207, 261, 253, 3), // show dogfight missions
3708 ui_button_info("2_MC_04", 639, 207, 479, 218, 4), // pxo mission refresh
3709 ui_button_info("2_MC_05", 907, 197, 748, 216, 5), // pilot info
3710 ui_button_info("2_MC_06", 1, 258, -1, -1, 6), // scroll mission info up
3711 ui_button_info("2_MC_08", 1, 487, -1, -1, 8), // scroll mission info down
3712 ui_button_info("2_MC_09", 981, 256, -1, -1, 9), // scroll players up
3713 ui_button_info("2_MC_10", 981, 323, -1, -1, 10), // scroll players down
3714 ui_button_info("2_MC_11", 35, 554, 46, 601, 11), // mission filter
3715 ui_button_info("2_MC_12", 166, 554, 174, 601, 12), // campaign filter
3716 ui_button_info("2_MC_13", 628, 545, 559, 582, 13), // cancel
3717 ui_button_info("2_MC_14", 756, 564, 772, 610, 14), // team 0
3718 ui_button_info("2_MC_15", 810, 564, 826, 610, 15), // team 1
3719 ui_button_info("2_MC_16", 862, 554, 872, 610, 16), // kick
3720 ui_button_info("2_MC_17", 943, 554, 949, 610, 17), // close
3721 ui_button_info("2_MC_18", 1, 649, -1, -1, 18), // scroll list up
3722 ui_button_info("2_MC_19", 1, 716, -1, -1, 19), // scroll list down
3723 ui_button_info("2_MC_20", 798, 695, 726, 667, 20), // host options
3724 ui_button_info("2_MC_21", 854, 681, -1, -1, 21), // help
3725 ui_button_info("2_MC_22", 854, 724, -1, -1, 22), // options
3726 ui_button_info("2_MC_23", 914, 681, 932, 667, 23), // commit
3731 #define MULTI_CREATE_NUM_TEXT 0
3733 #define MULTI_CREATE_NUM_TEXT 15
3735 UI_XSTR Multi_create_text[GR_NUM_RESOLUTIONS][MULTI_CREATE_NUM_BUTTONS] = {
3737 // not needed for FS1
3739 {"All", 1256, 36, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_ALL].button},
3740 {"Coop", 1257, 71, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_COOP].button},
3741 {"Team", 1258, 119, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_TEAM].button},
3742 {"Dogfight", 1259, 166, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_DOGFIGHT].button},
3743 {"Refresh Missions", 1260, 229, 130, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_PXO_REFRESH].button},
3744 {"Pilot Info", 1261, 467, 132, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_PILOT_INFO].button},
3745 {"Missions", 1262, 27, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_MISSION_FILTER].button},
3746 {"Campaigns", 1263, 110, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_CAMPAIGN_FILTER].button},
3747 {"Cancel", 387, 328, 364, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_CANCEL].button},
3748 {"1", 1264, 482, 381, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_TEAM0].button},
3749 {"2", 1265, 514, 381, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_TEAM1].button},
3750 {"Kick", 1266, 539, 381, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_KICK].button},
3751 {"Close", 1508, 582, 381, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_CLOSE].button},
3752 {"Host Options", 1267, 436, 423, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_HOST_OPTIONS].button},
3753 {"Commit", 1062, 572, 413, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_ACCEPT].button}
3757 // not needed for FS1
3759 {"All", 1256, 61, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_ALL].button},
3760 {"Coop", 1257, 124, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_COOP].button},
3761 {"Team", 1258, 194, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_TEAM].button},
3762 {"Dogfight", 1259, 261, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_DOGFIGHT].button},
3763 {"Refresh Missions", 1260, 501, 218, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_PXO_REFRESH].button},
3764 {"Pilot Info", 1261, 814, 216, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_PILOT_INFO].button},
3765 {"Missions", 1262, 46, 601, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_MISSION_FILTER].button},
3766 {"Campaigns", 1263, 174, 601, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_CAMPAIGN_FILTER].button},
3767 {"Cancel", 387, 559, 582, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_CANCEL].button},
3768 {"1", 1264, 772, 610, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_TEAM0].button},
3769 {"2", 1265, 826, 610, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_TEAM1].button},
3770 {"Kick", 1266, 872, 610, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_KICK].button},
3771 {"Close", 1508, 949, 610, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_CLOSE].button},
3772 {"Host Options", 1267, 755, 683, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_HOST_OPTIONS].button},
3773 {"Commit", 1062, 932, 667, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_ACCEPT].button}
3778 // squad war checkbox
3779 UI_CHECKBOX Multi_create_sw_checkbox;
3780 char *Multi_create_sw_checkbox_fname[GR_NUM_RESOLUTIONS] = {
3784 int Multi_create_sw_checkbox_coords[GR_NUM_RESOLUTIONS][2] = {
3792 int Multi_create_sw_checkbox_text[GR_NUM_RESOLUTIONS][2] = {
3801 // game information text areas
3802 int Mc_list_coords[GR_NUM_RESOLUTIONS][4] = {
3815 int Mc_players_coords[GR_NUM_RESOLUTIONS][4] = {
3828 int Mc_info_coords[GR_NUM_RESOLUTIONS][4] = {
3841 // mission icon stuff
3842 int Mc_icon_type_coords[GR_NUM_RESOLUTIONS][2] = {
3844 38, -2 // y is an offset
3847 61, -2 // y is an offset
3851 int Mc_icon_volition_coords[GR_NUM_RESOLUTIONS][2] = {
3853 61, -1 // y is an offset
3856 98, 1 // y is an offset
3860 int Mc_icon_silent_coords[GR_NUM_RESOLUTIONS][2] = {
3862 72, 0 // y is an offset
3865 115, 0 // y is an offset
3869 int Mc_icon_valid_coords[GR_NUM_RESOLUTIONS][2] = {
3871 91, 0 // y is an offset
3874 146, 0 // y is an offset
3878 // mission/campaign list column areas
3879 int Mc_column1_w[GR_NUM_RESOLUTIONS] = {
3884 int Mc_column2_w[GR_NUM_RESOLUTIONS] = {
3889 int Mc_column3_w[GR_NUM_RESOLUTIONS] = {
3894 int Mc_mission_name_x[GR_NUM_RESOLUTIONS] = {
3899 int Mc_mission_count_x[GR_NUM_RESOLUTIONS] = {
3904 int Mc_mission_fname_x[GR_NUM_RESOLUTIONS] = {
3909 int Mc_create_game_text[GR_NUM_RESOLUTIONS][2] = {
3910 {13, 116}, // GR_640
3911 {21, 186} // GR_1024
3914 int Mc_players_text[GR_NUM_RESOLUTIONS][2] = {
3915 {467, 150}, // GR_640
3916 {747, 240} // GR_1024
3919 int Mc_team_text[GR_NUM_RESOLUTIONS][2] = {
3920 {484, 342}, // GR_640
3921 {774, 547} // GR_1024
3924 int Mc_slider_coords[GR_NUM_RESOLUTIONS][4] = {
3926 3, 197, 13, 105 // GR_640
3929 5, 316, 20, 168 // GR_1024
3933 char *Mc_slider_bitmap[GR_NUM_RESOLUTIONS] = {
3938 // player list control thingie defs
3939 #define MULTI_CREATE_PLIST_MAX_DISPLAY 20
3940 int Multi_create_plist_select_flag; // flag indicating if we have a play selected
3941 short Multi_create_plist_select_id; // the net address of the currently selected player (for lookup)
3943 // master tracker details
3944 int Multi_create_frame_count; // framecount
3945 int Multi_create_mt_tried_login; // attempted to login this server on the MT
3947 // mission filter settings
3948 int Multi_create_filter; // what mode we're in
3950 // game/campaign list control defs
3951 int Multi_create_list_max_display[GR_NUM_RESOLUTIONS] = {
3957 int Multi_create_list_count; // number of items in listbox
3958 int Multi_create_list_mode; // 0 == mission mode, 1 == campaign mode
3959 int Multi_create_list_start; // where to start displaying from
3960 int Multi_create_list_select; // which item is currently highlighted
3961 int Multi_create_files_loaded;
3963 char Multi_create_files_array[MULTI_CREATE_MAX_LIST_ITEMS][MAX_FILENAME_LEN];
3965 int Multi_create_mission_count; // how many we have
3966 int Multi_create_campaign_count;
3967 multi_create_info Multi_create_mission_list[MULTI_CREATE_MAX_LIST_ITEMS];
3968 multi_create_info Multi_create_campaign_list[MULTI_CREATE_MAX_LIST_ITEMS];
3970 // use a pointer for the file list. Will point to either the missions or the campaigns
3971 multi_create_info *Multi_create_file_list;
3973 // LOCAL function definitions
3974 void multi_create_check_buttons();
3975 void multi_create_button_pressed(int n);
3976 void multi_create_init_as_server();
3977 void multi_create_init_as_client();
3978 void multi_create_do_netstuff();
3979 void multi_create_plist_scroll_up();
3980 void multi_create_plist_scroll_down();
3981 void multi_create_plist_process();
3982 void multi_create_plist_blit_normal();
3983 void multi_create_plist_blit_team();
3984 void multi_create_list_scroll_up();
3985 void multi_create_list_scroll_down();
3986 void multi_create_list_do();
3987 void multi_create_list_select_item(int n);
3988 void multi_create_list_blit_icons(int list_index, int y_start);
3989 void multi_create_accept_hit();
3990 void multi_create_draw_filter_buttons();
3991 void multi_create_set_selected_team(int team);
3992 short multi_create_get_mouse_id();
3993 int multi_create_ok_to_commit();
3994 int multi_create_verify_cds();
3995 void multi_create_refresh_pxo();
3996 void multi_create_sw_clicked();
3998 // since we can selectively filter out mission/campaign types we always need to map a selected index (which is relative
3999 // to the displayed list), to an absolute index (which is relative to the total file list - some of which may filtered out)
4000 void multi_create_select_to_filename(int select_index,char *filename);
4001 int multi_create_select_to_index(int select_index);
4003 int Multi_create_should_show_popup = 0;
4006 // sorting function to sort mission lists.. Basic sorting on mission name
4007 int multi_create_sort_func(const void *a, const void *b)
4009 multi_create_info *m1, *m2;
4011 m1 = (multi_create_info *)a;
4012 m2 = (multi_create_info *)b;
4014 return ( strcmp(m1->name, m2->name) );
4017 void multi_create_setup_list_data(int mode)
4019 int idx,should_sort,switched_modes;
4021 // set the current mode
4024 if((Multi_create_list_mode != mode) && (mode != -1)){
4025 Multi_create_list_mode = mode;
4028 // set up the list pointers
4029 if ( Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ) {
4030 Multi_create_file_list = Multi_create_mission_list;
4031 } else if ( Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ) {
4032 Multi_create_file_list = Multi_create_campaign_list;
4038 // get the mission count based upon the filter selected
4039 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
4040 switch(Multi_create_filter){
4041 case MISSION_TYPE_MULTI:
4042 Multi_create_list_count = Multi_create_mission_count;
4045 Multi_create_list_count = 0;
4046 // find all missions which match
4047 for(idx=0;idx<Multi_create_mission_count;idx++){
4048 if(Multi_create_mission_list[idx].flags & Multi_create_filter){
4049 Multi_create_list_count++;
4053 // if we switched modes and we have more than 0 items, sort them
4054 if(switched_modes && (Multi_create_list_count > 0)){
4059 } else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
4060 switch(Multi_create_filter){
4061 case MISSION_TYPE_MULTI:
4062 Multi_create_list_count = Multi_create_campaign_count;
4065 Multi_create_list_count = 0;
4066 // find all missions which match
4067 for(idx=0;idx<Multi_create_campaign_count;idx++){
4068 if(Multi_create_campaign_list[idx].flags & Multi_create_filter){
4069 Multi_create_list_count++;
4073 // if we switched modes and we have more than 0 items, sort them
4074 if(switched_modes && (Multi_create_list_count > 0)){
4081 // reset the list start and selected indices
4082 Multi_create_list_start = 0;
4083 Multi_create_list_select = -1;
4084 multi_create_list_select_item(Multi_create_list_start);
4086 // sort the list of missions if necessary
4088 qsort(Multi_create_file_list, Multi_create_list_count, sizeof(multi_create_info), multi_create_sort_func);
4093 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);
4097 void multi_create_game_init()
4102 // now make sure to initialze various netgame stuff based upon whether we're on a standalone or not
4103 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4104 multi_create_init_as_server();
4106 multi_create_init_as_client();
4109 // initialize the player list data
4110 Multi_create_plist_select_flag = 0;
4111 Multi_create_plist_select_id = -1;
4113 // create the interface window
4114 Multi_create_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
4115 Multi_create_window.set_mask_bmap(Multi_create_bitmap_mask_fname[gr_screen.res]);
4117 // load the background bitmap
4118 Multi_create_bitmap = bm_load(Multi_create_bitmap_fname[gr_screen.res]);
4119 if(Multi_create_bitmap < 0){
4120 // we failed to load the bitmap - this is very bad
4124 // close any previous existing instances of the chatbox and create a new one
4128 // load the help overlay
4129 help_overlay_load(MULTI_CREATE_OVERLAY);
4130 help_overlay_set_state(MULTI_CREATE_OVERLAY, 0);
4132 // initialize the common notification messaging
4133 multi_common_notify_init();
4135 // use the common interface palette
4136 multi_common_set_palette();
4138 // create the interface buttons
4139 for(idx=0; idx<MULTI_CREATE_NUM_BUTTONS; idx++){
4140 b = &Multi_create_buttons[gr_screen.res][idx];
4142 // create the object
4143 b->button.create(&Multi_create_window, "", b->x, b->y, 1, 1, ((idx == MC_SCROLL_LIST_UP) || (idx == MC_SCROLL_LIST_DOWN)), 1);
4145 // set the sound to play when highlighted
4146 b->button.set_highlight_action(common_play_highlight_sound);
4148 // set the ani for the button
4149 b->button.set_bmaps(b->filename);
4152 b->button.link_hotspot(b->hotspot);
4154 // some special case stuff for the pxo refresh button
4155 if(idx == MC_PXO_REFRESH){
4156 // if not a PXO game, or if I'm not a server disable and hide the button
4157 if(!MULTI_IS_TRACKER_GAME || !MULTIPLAYER_MASTER){
4159 b->button.disable();
4165 for(idx=0; idx<MULTI_CREATE_NUM_TEXT; idx++){
4166 Multi_create_window.add_XSTR(&Multi_create_text[gr_screen.res][idx]);
4169 // if this is a PXO game, enable the squadwar checkbox
4170 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);
4171 Multi_create_sw_checkbox.set_bmaps(Multi_create_sw_checkbox_fname[gr_screen.res], 6, 0);
4172 if(!MULTI_IS_TRACKER_GAME){
4173 Multi_create_sw_checkbox.hide();
4174 Multi_create_sw_checkbox.disable();
4178 // disable squad war button in demo
4179 Multi_create_sw_checkbox.hide();
4180 Multi_create_sw_checkbox.disable();
4183 // initialize the mission type filtering mode
4184 Multi_create_filter = MISSION_TYPE_MULTI;
4186 // initialize the list mode, and load in a list
4187 memset(Multi_create_mission_list, 0, sizeof(multi_create_info) * MULTI_CREATE_MAX_LIST_ITEMS);
4188 memset(Multi_create_campaign_list, 0, sizeof(multi_create_info) * MULTI_CREATE_MAX_LIST_ITEMS);
4189 for(idx=0; idx<MULTI_CREATE_MAX_LIST_ITEMS; idx++){
4190 Multi_create_mission_list[idx].valid_status = MVALID_STATUS_UNKNOWN;
4191 Multi_create_campaign_list[idx].valid_status = MVALID_STATUS_UNKNOWN;
4193 Multi_create_list_mode = MULTI_CREATE_SHOW_MISSIONS;
4194 Multi_create_list_start = -1;
4195 Multi_create_list_select = -1;
4196 Multi_create_list_count = 0;
4199 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);
4202 // create the player list select button
4203 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);
4204 Multi_create_player_select_button.hide();
4206 // create the mission/campaign list select button
4207 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);
4208 Multi_create_list_select_button.hide();
4210 // set hotkeys for a couple of things.
4211 Multi_create_buttons[gr_screen.res][MC_ACCEPT].button.set_hotkey(KEY_CTRLED+KEY_ENTER);
4213 // init some master tracker stuff
4214 Multi_create_frame_count = 0;
4215 Multi_create_mt_tried_login = 0;
4217 // remove campaign flags
4218 Game_mode &= ~(GM_CAMPAIGN_MODE);
4220 // send any pilots as appropriate
4221 multi_data_send_my_junk();
4222 Multi_create_file_list = Multi_create_mission_list;
4224 Multi_create_campaign_count = 0;
4225 Multi_create_mission_count = 0;
4226 Multi_create_files_loaded = 0;
4229 void multi_create_game_do()
4233 char *loading_str = XSTR("Loading", 1336);
4237 // set this if we want to show the pilot info popup
4238 Multi_create_should_show_popup = 0;
4240 // first thing is to load the files
4241 if ( !Multi_create_files_loaded ) {
4242 // if I am a client, send a list request to the server for the missions
4243 if ( MULTIPLAYER_CLIENT ) {
4244 send_mission_list_request( MISSION_LIST_REQUEST );
4248 loading_bitmap = bm_load(Multi_create_loading_fname[gr_screen.res]);
4250 // draw the background, etc
4252 GR_MAYBE_CLEAR_RES(Multi_create_bitmap);
4253 if(Multi_create_bitmap != -1){
4254 gr_set_bitmap(Multi_create_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4258 if ( loading_bitmap > -1 ){
4259 gr_set_bitmap(loading_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4261 gr_bitmap( Please_wait_coords[gr_screen.res][MC_X_COORD], Please_wait_coords[gr_screen.res][MC_Y_COORD] );
4263 // draw "Loading" on it
4265 gr_set_color_fast(&Color_normal);
4267 gr_get_string_size(&str_w, &str_h, loading_str);
4268 gr_string((gr_screen.max_w - str_w) / 2, (gr_screen.max_h - str_h) / 2, loading_str);
4274 multi_create_list_load_missions();
4275 multi_create_list_load_campaigns();
4277 // if this is a tracker game, validate missions
4278 if(MULTI_IS_TRACKER_GAME){
4279 multi_update_valid_missions();
4282 // update the file list
4283 multi_create_setup_list_data(MULTI_CREATE_SHOW_MISSIONS);
4286 // don't bother setting netgame state if ont the server
4287 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4288 Netgame.game_state = NETGAME_STATE_FORMING;
4289 send_netgame_update_packet();
4292 // if we're on the standalone we have to tell him that we're now in the host setup screen
4293 Net_player->state = NETPLAYER_STATE_HOST_SETUP;
4294 send_netplayer_update_packet();
4296 Multi_create_files_loaded = 1;
4299 int k = chatbox_process();
4300 k = Multi_create_window.process(k,0);
4303 // same as the cancel button
4305 if(help_overlay_active(MULTI_CREATE_OVERLAY)){
4306 help_overlay_set_state(MULTI_CREATE_OVERLAY,0);
4308 gamesnd_play_iface(SND_USER_SELECT);
4309 multi_quit_game(PROMPT_HOST);
4314 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
4315 help_overlay_set_state(MULTI_CREATE_OVERLAY, 0);
4318 // process any button clicks
4319 multi_create_check_buttons();
4321 // do any network related stuff
4322 multi_create_do_netstuff();
4324 // draw the background, etc
4326 GR_MAYBE_CLEAR_RES(Multi_create_bitmap);
4327 if(Multi_create_bitmap != -1){
4328 gr_set_bitmap(Multi_create_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4332 // if we're not in team vs. team mode, don't draw the team buttons
4333 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
4334 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.hide();
4335 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.hide();
4336 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.disable();
4337 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.disable();
4339 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.enable();
4340 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.enable();
4341 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.unhide();
4342 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.unhide();
4345 // draw the window itself
4346 Multi_create_window.draw();
4348 gr_set_color_fast(&Color_normal);
4351 // draw Create Game text
4352 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));
4354 // draw players text
4355 gr_string(Mc_players_text[gr_screen.res][MC_X_COORD], Mc_players_text[gr_screen.res][MC_Y_COORD], XSTR("Players", 1269));
4357 // draw players text
4358 gr_string(Mc_team_text[gr_screen.res][MC_X_COORD], Mc_team_text[gr_screen.res][MC_Y_COORD], XSTR("Team", 1258));
4361 // process and display the player list
4362 // NOTE : this must be done before the buttons are checked to insure that a player hasn't dropped
4363 multi_create_plist_process();
4364 if(Netgame.type_flags & NG_TYPE_TEAM){
4365 multi_create_plist_blit_team();
4367 multi_create_plist_blit_normal();
4370 // process and display the game/campaign list
4371 multi_create_list_do();
4373 // draw the correct mission filter button
4374 multi_create_draw_filter_buttons();
4376 // display any text in the info area
4377 multi_common_render_text();
4379 // display any pending notification messages
4380 multi_common_notify_do();
4382 // force the correct mission/campaign button to light up
4383 if( Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ){
4384 Multi_create_buttons[gr_screen.res][MC_MISSION_FILTER].button.draw_forced(2);
4386 Multi_create_buttons[gr_screen.res][MC_CAMPAIGN_FILTER].button.draw_forced(2);
4389 // force draw the closed button if it is toggled on
4390 if(Netgame.flags & NG_FLAG_TEMP_CLOSED){
4391 Multi_create_buttons[gr_screen.res][MC_CLOSE].button.draw_forced(2);
4394 // process and show the chatbox thingie
4398 Multi_create_window.draw_tooltip();
4400 // display the voice status indicator
4401 multi_common_voice_display_status();
4403 // blit the help overlay if necessary
4404 help_overlay_maybe_blit(MULTI_CREATE_OVERLAY);
4407 if(MULTI_IS_TRACKER_GAME){
4408 if(Netgame.type_flags & NG_TYPE_SW){
4409 gr_set_color_fast(&Color_bright);
4411 gr_set_color_fast(&Color_normal);
4413 gr_string(Multi_create_sw_checkbox_text[gr_screen.res][0], Multi_create_sw_checkbox_text[gr_screen.res][1], "SquadWar");
4419 // if we're supposed to show the pilot info popup, do it now
4420 if(Multi_create_should_show_popup){
4421 // get the player index and address of the player item the mouse is currently over
4422 if(Multi_create_plist_select_flag){
4423 player_index = find_player_id(Multi_create_plist_select_id);
4424 if(player_index != -1){
4425 multi_pinfo_popup(&Net_players[player_index]);
4430 // increment the frame count
4431 Multi_create_frame_count++;
4434 void multi_create_game_close()
4436 // unload any bitmaps
4437 if(!bm_unload(Multi_create_bitmap)){
4438 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_create_bitmap_fname[gr_screen.res]));
4441 // unload the help overlay
4442 help_overlay_unload(MULTI_CREATE_OVERLAY);
4444 // destroy the chatbox
4447 // destroy the UI_WINDOW
4448 Multi_create_window.destroy();
4451 void multi_create_check_buttons()
4454 for(idx=0;idx<MULTI_CREATE_NUM_BUTTONS;idx++){
4455 // we only really need to check for one button pressed at a time, so we can break after
4457 if(Multi_create_buttons[gr_screen.res][idx].button.pressed()){
4458 multi_create_button_pressed(idx);
4463 // if the squad war checkbox was clicked
4464 if(Multi_create_sw_checkbox.changed()){
4465 multi_create_sw_clicked();
4469 void multi_create_button_pressed(int n)
4475 gamesnd_play_iface(SND_USER_SELECT);
4476 multi_quit_game(PROMPT_HOST);
4479 // if valid commit conditions have not been met
4480 if(!multi_create_ok_to_commit()){
4485 multi_create_accept_hit();
4490 if(!help_overlay_active(MULTI_CREATE_OVERLAY)){
4491 help_overlay_set_state(MULTI_CREATE_OVERLAY,1);
4493 help_overlay_set_state(MULTI_CREATE_OVERLAY,0);
4497 // scroll the info text box up
4498 case MC_SCROLL_INFO_UP:
4499 multi_common_scroll_text_up();
4502 // scroll the info text box down
4503 case MC_SCROLL_INFO_DOWN:
4504 multi_common_scroll_text_down();
4507 // scroll the player list up
4508 case MC_SCROLL_PLAYERS_UP:
4509 multi_create_plist_scroll_up();
4512 // scroll the player list down
4513 case MC_SCROLL_PLAYERS_DOWN:
4514 multi_create_plist_scroll_down();
4517 // scroll the game/campaign list up
4518 case MC_SCROLL_LIST_UP:
4519 multi_create_list_scroll_up();
4521 Multi_create_slider.forceUp(); // move slider up
4525 // scroll the game/campaign list down
4526 case MC_SCROLL_LIST_DOWN:
4527 multi_create_list_scroll_down();
4529 Multi_create_slider.forceDown(); // move slider down
4533 // go to the options screen
4535 gamesnd_play_iface(SND_USER_SELECT);
4536 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
4539 // show all missions
4541 if(Multi_create_filter != MISSION_TYPE_MULTI){
4542 gamesnd_play_iface(SND_USER_SELECT);
4543 Multi_create_filter = MISSION_TYPE_MULTI;
4544 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4546 gamesnd_play_iface(SND_GENERAL_FAIL);
4550 // show cooperative missions
4552 if(Multi_create_filter != MISSION_TYPE_MULTI_COOP){
4553 gamesnd_play_iface(SND_USER_SELECT);
4554 Multi_create_filter = MISSION_TYPE_MULTI_COOP;
4555 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4557 gamesnd_play_iface(SND_GENERAL_FAIL);
4561 // show team vs. team missions
4563 if(Multi_create_filter != MISSION_TYPE_MULTI_TEAMS){
4564 gamesnd_play_iface(SND_USER_SELECT);
4565 Multi_create_filter = MISSION_TYPE_MULTI_TEAMS;
4566 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4568 gamesnd_play_iface(SND_GENERAL_FAIL);
4572 // show dogfight missions
4574 case MC_SHOW_DOGFIGHT:
4575 if (Multi_create_filter != MISSION_TYPE_MULTI_DOGFIGHT){
4576 gamesnd_play_iface(SND_USER_SELECT);
4577 Multi_create_filter = MISSION_TYPE_MULTI_DOGFIGHT;
4578 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4580 gamesnd_play_iface(SND_GENERAL_FAIL);
4585 // toggle temporary netgame closed on/off
4587 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4588 Netgame.flags ^= NG_FLAG_TEMP_CLOSED;
4590 Netgame.options.flags |= MLO_FLAG_TEMP_CLOSED;
4591 multi_options_update_netgame();
4593 gamesnd_play_iface(SND_USER_SELECT);
4596 // kick the currently selected player (if possible)
4598 // lookup the player at the specified index
4599 if(Multi_create_plist_select_flag){
4600 idx = find_player_id(Multi_create_plist_select_id);
4601 // kick him - but don't ban him
4603 multi_kick_player(idx,0);
4608 // switch to individual mission mode and load in a list
4609 case MC_MISSION_FILTER:
4610 if(Multi_create_list_mode != MULTI_CREATE_SHOW_MISSIONS){
4611 Netgame.campaign_mode = MP_SINGLE;
4613 gamesnd_play_iface(SND_USER_SELECT);
4615 // update the file list
4616 multi_create_setup_list_data(MULTI_CREATE_SHOW_MISSIONS);
4618 gamesnd_play_iface(SND_GENERAL_FAIL);
4622 // switch to campaign mode and load in a list
4623 case MC_CAMPAIGN_FILTER:
4624 // switch off squad war
4625 Multi_create_sw_checkbox.set_state(0);
4626 Netgame.type_flags = NG_TYPE_COOP;
4628 if(Multi_create_list_mode != MULTI_CREATE_SHOW_CAMPAIGNS){
4629 Netgame.campaign_mode = MP_CAMPAIGN;
4631 gamesnd_play_iface(SND_USER_SELECT);
4633 // update the file list
4634 multi_create_setup_list_data(MULTI_CREATE_SHOW_CAMPAIGNS);
4636 gamesnd_play_iface(SND_GENERAL_FAIL);
4640 // attempt to set the selected player's team
4642 multi_create_set_selected_team(0);
4643 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4644 multi_team_send_update();
4648 // attempt to set the selected player's team
4650 multi_create_set_selected_team(1);
4651 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4652 multi_team_send_update();
4656 // popup the pilot info dialog for the currently selected pilot (will occur at the end of the frame)
4658 Multi_create_should_show_popup = 1;
4661 // go to the host options screen
4662 case MC_HOST_OPTIONS:
4663 gamesnd_play_iface(SND_USER_SELECT);
4664 gameseq_post_event(GS_EVENT_MULTI_HOST_OPTIONS);
4667 // refresh PXO file list
4668 case MC_PXO_REFRESH:
4669 if(!MULTI_IS_TRACKER_GAME){
4672 multi_create_refresh_pxo();
4676 gamesnd_play_iface(SND_GENERAL_FAIL);
4677 multi_common_add_notify(XSTR("Not implemented yet!",760));
4682 // do stuff like pinging servers, sending out requests, etc
4683 void multi_create_do_netstuff()
4687 // if not on a standalone
4688 void multi_create_init_as_server()
4690 // set me up as the host and master
4691 Net_player->flags |= (NETINFO_FLAG_AM_MASTER | NETINFO_FLAG_GAME_HOST);
4694 // if on a standalone
4695 void multi_create_init_as_client()
4697 Net_player->flags |= NETINFO_FLAG_GAME_HOST;
4700 // scroll up through the player list
4701 void multi_create_plist_scroll_up()
4703 gamesnd_play_iface(SND_GENERAL_FAIL);
4706 // scroll down through the player list
4707 void multi_create_plist_scroll_down()
4709 gamesnd_play_iface(SND_GENERAL_FAIL);
4712 void multi_create_plist_process()
4714 int test_count,idx,player_index;
4716 // first determine if there are 0 players in the game. This should never happen since the host is _always_ in the game
4718 for(idx=0;idx<MAX_PLAYERS;idx++){
4719 // count anyone except the standalone server (if applicable)
4720 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
4724 if(test_count <= 0){
4728 // if we had a selected item but that player has left, select myself instead
4729 if(Multi_create_plist_select_flag){
4730 player_index = find_player_id(Multi_create_plist_select_id);
4731 if(player_index == -1){
4732 Multi_create_plist_select_id = Net_player->player_id;
4735 Multi_create_plist_select_flag = 1;
4736 Multi_create_plist_select_id = Net_player->player_id;
4739 // if the player has clicked somewhere in the player list area
4740 if(Multi_create_player_select_button.pressed()){
4743 // get the player index and address of the player item the mouse is currently over
4744 player_id = multi_create_get_mouse_id();
4745 player_index = find_player_id(player_id);
4746 if(player_index != -1){
4747 Multi_create_plist_select_flag = 1;
4748 Multi_create_plist_select_id = player_id;
4753 void multi_create_plist_blit_normal()
4756 char str[CALLSIGN_LEN+5];
4757 int y_start = Mc_players_coords[gr_screen.res][MC_Y_COORD];
4760 // display all the players
4761 for(idx=0;idx<MAX_PLAYERS;idx++){
4762 // count anyone except the standalone server (if applicable)
4763 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
4767 // highlight him if he's the host
4768 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4769 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4770 gr_set_color_fast(&Color_text_active_hi);
4772 gr_set_color_fast(&Color_bright);
4775 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4776 gr_set_color_fast(&Color_text_active);
4778 gr_set_color_fast(&Color_text_normal);
4782 // optionally draw his CD status
4783 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4784 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4785 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4787 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4790 // make sure the string will fit, then display it
4791 strcpy(str,Net_players[idx].player->callsign);
4792 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4793 strcat(str,XSTR("(O)",787)); // [[ Observer ]]
4795 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4796 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4803 void multi_create_plist_blit_team()
4806 char str[CALLSIGN_LEN+1];
4807 int y_start = Mc_players_coords[gr_screen.res][MC_Y_COORD];
4810 // display all the red players first
4811 for(idx=0;idx<MAX_PLAYERS;idx++){
4812 // count anyone except the standalone server (if applicable)
4813 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
4814 // reset total offset
4817 // highlight him if he's the host
4818 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4819 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4820 gr_set_color_fast(&Color_text_active_hi);
4822 // be sure to blit the correct team button
4823 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.draw_forced(2);
4825 gr_set_color_fast(&Color_bright);
4828 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4829 gr_set_color_fast(&Color_text_active);
4831 // be sure to blit the correct team button
4832 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.draw_forced(2);
4834 gr_set_color_fast(&Color_text_normal);
4838 // optionally draw his CD status
4839 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4840 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4841 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4843 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4846 // blit the red team indicator
4847 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
4848 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
4849 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4850 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4852 total_offset += Multi_common_icon_dims[MICON_TEAM0_SELECT][0] + 1;
4855 if(Multi_common_icons[MICON_TEAM0] != -1){
4856 gr_set_bitmap(Multi_common_icons[MICON_TEAM0], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4857 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4859 total_offset += Multi_common_icon_dims[MICON_TEAM0][0] + 1;
4863 // make sure the string will fit
4864 strcpy(str,Net_players[idx].player->callsign);
4865 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4866 strcat(str,XSTR("(O)",787));
4868 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4870 // display him in the correct half of the list depending on his team
4871 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4876 // display all the green players next
4877 for(idx=0;idx<MAX_PLAYERS;idx++){
4878 // count anyone except the standalone server (if applicable)
4879 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
4880 // reset total offset
4883 // highlight him if he's the host
4884 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4885 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4886 gr_set_color_fast(&Color_text_active_hi);
4888 // be sure to blit the correct team button
4889 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.draw_forced(2);
4891 gr_set_color_fast(&Color_bright);
4894 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4895 gr_set_color_fast(&Color_text_active);
4897 // be sure to blit the correct team button
4898 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.draw_forced(2);
4900 gr_set_color_fast(&Color_text_normal);
4904 // optionally draw his CD status
4905 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4906 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4907 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4909 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4912 // blit the red team indicator
4913 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
4914 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
4915 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4916 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4918 total_offset += Multi_common_icon_dims[MICON_TEAM1_SELECT][0] + 1;
4921 if(Multi_common_icons[MICON_TEAM1] != -1){
4922 gr_set_bitmap(Multi_common_icons[MICON_TEAM1], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4923 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4925 total_offset += Multi_common_icon_dims[MICON_TEAM1][0] + 1;
4929 // make sure the string will fit
4930 strcpy(str,Net_players[idx].player->callsign);
4931 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4932 strcat(str,XSTR("(O)",787));
4934 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4936 // display him in the correct half of the list depending on his team
4937 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4943 void multi_create_list_scroll_up()
4945 if(Multi_create_list_start > 0){
4946 Multi_create_list_start--;
4948 gamesnd_play_iface(SND_SCROLL);
4950 gamesnd_play_iface(SND_GENERAL_FAIL);
4954 void multi_create_list_scroll_down()
4956 if((Multi_create_list_count - Multi_create_list_start) > Multi_create_list_max_display[gr_screen.res]){
4957 Multi_create_list_start++;
4959 gamesnd_play_iface(SND_SCROLL);
4961 gamesnd_play_iface(SND_GENERAL_FAIL);
4965 // gets a list of multiplayer misisons
4966 void multi_create_list_load_missions()
4968 char *fname, mission_name[NAME_LENGTH+1];
4969 char wild_card[256];
4972 memset(wild_card, 0, 256);
4973 strcpy(wild_card, NOX("*"));
4974 strcat(wild_card, FS_MISSION_FILE_EXT);
4975 file_count = cf_get_file_list_preallocated(MULTI_CREATE_MAX_LIST_ITEMS, Multi_create_files_array, NULL, CF_TYPE_MISSIONS, wild_card);
4976 Multi_create_mission_count = 0;
4978 // maybe create a standalone dialog
4979 if(Game_mode & GM_STANDALONE_SERVER){
4980 std_create_gen_dialog("Loading missions");
4981 std_gen_set_text("Mission:", 1);
4984 for(idx = 0; idx < file_count; idx++){
4985 int flags,max_players;
4989 fname = Multi_create_files_array[idx];
4991 // tack on any necessary file extension
4992 filename = cf_add_ext( fname, FS_MISSION_FILE_EXT );
4994 // for multiplayer beta builds, only accept builtin missions
4995 #if defined(MULTIPLAYER_BETA_BUILD) || defined(FS2_DEMO)
4996 if(game_find_builtin_mission(filename) == NULL){
4999 #elif defined(PD_BUILD)
5000 if((game_find_builtin_mission(filename) == NULL) && !strstr(filename, "peterdrake")){
5005 if(Game_mode & GM_STANDALONE_SERVER){
5006 std_gen_set_text(filename, 2);
5009 flags = mission_parse_is_multi(filename, mission_name);
5011 // if the mission is a multiplayer mission, then add it to the mission list
5013 max_players = mission_parse_get_multi_mission_info( filename );
5014 m_respawn = The_mission.num_respawns;
5016 if ( Multi_create_mission_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
5017 multi_create_info *mcip;
5019 mcip = &Multi_create_mission_list[Multi_create_mission_count];
5020 strcpy(mcip->filename, filename );
5021 strcpy(mcip->name, mission_name );
5022 mcip->flags = flags;
5023 mcip->respawn = m_respawn;
5024 mcip->max_players = (ubyte)max_players;
5026 // get any additional information for possibly builtin missions
5027 fs_builtin_mission *fb = game_find_builtin_mission(filename);
5031 Multi_create_mission_count++;
5037 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);
5040 // maybe create a standalone dialog
5041 if(Game_mode & GM_STANDALONE_SERVER){
5042 std_destroy_gen_dialog();
5046 void multi_create_list_load_campaigns()
5049 int idx, file_count;
5050 int campaign_type,max_players;
5052 char wild_card[256];
5054 // maybe create a standalone dialog
5055 if(Game_mode & GM_STANDALONE_SERVER){
5056 std_create_gen_dialog("Loading campaigns");
5057 std_gen_set_text("Campaign:", 1);
5060 Multi_create_campaign_count = 0;
5061 memset(wild_card, 0, 256);
5062 strcpy(wild_card, NOX("*"));
5063 strcat(wild_card, FS_CAMPAIGN_FILE_EXT);
5064 file_count = cf_get_file_list_preallocated(MULTI_CREATE_MAX_LIST_ITEMS, Multi_create_files_array, NULL, CF_TYPE_MISSIONS, wild_card);
5065 for(idx = 0; idx < file_count; idx++){
5067 char *filename, name[NAME_LENGTH];
5069 fname = Multi_create_files_array[idx];
5071 // tack on any necessary file extension
5072 filename = cf_add_ext( fname, FS_CAMPAIGN_FILE_EXT );
5074 // for multiplayer beta builds, only accept builtin missions
5075 #if defined(MULTIPLAYER_BETA_BUILD) || defined(FS2_DEMO)
5076 if(game_find_builtin_mission(filename) == NULL){
5079 #elif defined(PD_BUILD)
5080 if((game_find_builtin_mission(filename) == NULL) && !strstr(filename, "peterdrake")){
5085 if(Game_mode & GM_STANDALONE_SERVER){
5086 std_gen_set_text(filename, 2);
5089 // if the campaign is a multiplayer campaign, then add the data to the campaign list items
5090 flags = mission_campaign_parse_is_multi( filename, name );
5091 if( flags != CAMPAIGN_TYPE_SINGLE && mission_campaign_get_info(filename,title,&campaign_type,&max_players)) {
5092 if ( Multi_create_campaign_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
5093 multi_create_info *mcip;
5095 mcip = &Multi_create_campaign_list[Multi_create_campaign_count];
5096 strcpy(mcip->filename, filename );
5097 strcpy(mcip->name, name );
5099 // setup various flags
5100 if ( flags == CAMPAIGN_TYPE_MULTI_COOP ){
5101 mcip->flags = MISSION_TYPE_MULTI_COOP | MISSION_TYPE_MULTI;
5102 } else if ( flags == CAMPAIGN_TYPE_MULTI_TEAMS ) {
5103 mcip->flags = MISSION_TYPE_MULTI_TEAMS | MISSION_TYPE_MULTI;
5105 Int3(); // bogus campaign multi type -- find allender
5108 // 0 respawns for campaign files (should be contained within the mission files themselves)
5111 // 0 max players for campaign files
5112 mcip->max_players = (unsigned char)max_players;
5114 // get any additional information for possibly builtin missions
5115 fs_builtin_mission *fb = game_find_builtin_mission(filename);
5119 Multi_create_campaign_count++;
5124 // maybe create a standalone dialog
5125 if(Game_mode & GM_STANDALONE_SERVER){
5126 std_destroy_gen_dialog();
5130 void multi_create_list_do()
5133 int start_index,stop_index;
5134 char selected_name[255];
5136 // bail early if there aren't any selectable items
5137 if(Multi_create_list_count == 0){
5141 // first check to see if the user has clicked on an item
5142 if(Multi_create_list_select_button.pressed()){
5144 Multi_create_list_select_button.get_mouse_pos(NULL,&y);
5147 // make sure we are selectedin valid indices
5148 if((item < Multi_create_list_max_display[gr_screen.res]) && (item >= 0)){
5149 item += Multi_create_list_start;
5151 if(item < Multi_create_list_count){
5152 multi_create_list_select_item(item);
5153 gamesnd_play_iface(SND_IFACE_MOUSE_CLICK);
5158 // bail early if we don't have a start position
5159 if(Multi_create_list_start == -1){
5163 // display the list of individual campaigns/missions
5165 int y_start = Mc_list_coords[gr_screen.res][MC_Y_COORD];
5167 start_index = multi_create_select_to_index(Multi_create_list_start);
5168 stop_index = Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ? Multi_create_mission_count : Multi_create_campaign_count;
5169 for(idx=start_index; idx<stop_index; idx++){
5170 // see if we should drop out
5171 if(count == Multi_create_list_max_display[gr_screen.res]){
5175 // see if we should filter out this mission
5176 if ( !(Multi_create_file_list[idx].flags & Multi_create_filter) ){
5180 // highlight the selected item
5181 multi_create_select_to_filename(Multi_create_list_select,selected_name);
5182 if(!strcmp(selected_name,Multi_create_file_list[idx].filename)){
5183 gr_set_color_fast(&Color_text_selected);
5185 gr_set_color_fast(&Color_text_normal);
5188 // draw the type icon
5189 multi_create_list_blit_icons(idx, y_start);
5191 // force fit the mission name string
5192 strcpy(selected_name,Multi_create_file_list[idx].name);
5193 gr_force_fit_string(selected_name,255,Mc_column1_w[gr_screen.res]);
5194 gr_string(Mc_mission_name_x[gr_screen.res],y_start,selected_name);
5196 // draw the max players if in mission mode
5197 sprintf(selected_name,"%d",(int)Multi_create_file_list[idx].max_players);
5198 gr_string(Mc_mission_count_x[gr_screen.res],y_start,selected_name);
5200 // force fit the mission filename string
5201 strcpy(selected_name,Multi_create_file_list[idx].filename);
5202 gr_force_fit_string(selected_name,255,Mc_column3_w[gr_screen.res]);
5203 gr_string(Mc_mission_fname_x[gr_screen.res],y_start,selected_name);
5210 // takes care of stuff like changing indices around and setting up the netgame structure
5211 void multi_create_list_select_item(int n)
5213 int abs_index,campaign_type,max_players;
5214 char title[NAME_LENGTH+1];
5215 netgame_info ng_temp;
5218 char *campaign_desc;
5220 // if not on the standalone server
5221 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5224 // on the standalone
5226 memset(&ng_temp,0,sizeof(netgame_info));
5230 if ( n != Multi_create_list_select ) {
5231 // check to see if this is a valid index, and bail if it is not
5232 abs_index = multi_create_select_to_index(n);
5233 if(abs_index == -1){
5237 Multi_create_list_select = n;
5239 // set the mission name
5240 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5241 multi_create_select_to_filename(n,ng->mission_name);
5243 multi_create_select_to_filename(n,ng->campaign_name);
5246 // make sure the netgame type is properly set
5247 int old_type = Netgame.type_flags;
5248 abs_index = multi_create_select_to_index(n);
5249 if(abs_index != -1){
5250 if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_TEAMS){
5251 // if we're in squad war mode, leave it as squad war
5252 if(old_type & NG_TYPE_SW){
5253 ng->type_flags = NG_TYPE_SW;
5255 ng->type_flags = NG_TYPE_TVT;
5257 } else if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_COOP){
5258 ng->type_flags = NG_TYPE_COOP;
5259 } else if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_DOGFIGHT){
5260 ng->type_flags = NG_TYPE_DOGFIGHT;
5264 // if we're no longer in a TvT game, just uncheck the squadwar checkbox
5265 if(!(ng->type_flags & NG_TYPE_TEAM)){
5266 Multi_create_sw_checkbox.set_state(0);
5269 // if we switched from something else to team vs. team mode, do some special processing
5270 if((ng->type_flags & NG_TYPE_TEAM) && (ng->type_flags != old_type) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
5274 switch(Multi_create_list_mode){
5275 case MULTI_CREATE_SHOW_MISSIONS:
5276 // don't forget to update the info box window thingie
5277 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5278 ship_init(); // mwa -- 10/15/97. Call this function to reset number of ships in mission
5279 ng->max_players = mission_parse_get_multi_mission_info( ng->mission_name );
5281 Assert(ng->max_players > 0);
5282 strcpy(ng->title,The_mission.name);
5284 // set the information area text
5285 multi_common_set_text(The_mission.mission_desc);
5287 // if we're on the standalone, send a request for the description
5289 send_netgame_descript_packet(&Netgame.server_addr,0);
5290 multi_common_set_text("");
5293 // set the respawns as appropriate
5294 if(Netgame.options.respawn <= Multi_create_file_list[abs_index].respawn){
5295 ng->respawn = Netgame.options.respawn;
5296 nprintf(("Network","Using netgame options for respawn count (%d %d)\n",Netgame.options.respawn,Multi_create_file_list[abs_index].respawn));
5298 ng->respawn = Multi_create_file_list[abs_index].respawn;
5299 nprintf(("Network","Using mission settings for respawn count (%d %d)\n",Netgame.options.respawn,Multi_create_file_list[abs_index].respawn));
5302 case MULTI_CREATE_SHOW_CAMPAIGNS:
5303 // if not on the standalone server
5304 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5305 // get the campaign info
5306 memset(title,0,NAME_LENGTH+1);
5307 if(!mission_campaign_get_info(ng->campaign_name,title,&campaign_type,&max_players, &campaign_desc)) {
5308 memset(ng->campaign_name,0,NAME_LENGTH+1);
5309 ng->max_players = 0;
5311 // if we successfully got the # of players
5313 memset(ng->title,0,NAME_LENGTH+1);
5314 strcpy(ng->title,title);
5315 ng->max_players = max_players;
5318 nprintf(("Network","MC MAX PLAYERS : %d\n",ng->max_players));
5320 // set the information area text
5321 // multi_common_set_text(ng->title);
5322 multi_common_set_text(campaign_desc);
5324 // if on the standalone server, send a request for the description
5326 // no descriptions currently kept for campaigns
5329 // netgame respawns are always 0 for campaigns (until the first mission is loaded)
5334 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5336 send_netgame_update_packet();
5338 // update all machines about stuff like respawns, etc.
5339 multi_options_update_netgame();
5341 multi_options_update_mission(ng, Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ? 1 : 0);
5346 void multi_create_list_blit_icons(int list_index, int y_start)
5348 multi_create_info *mcip;
5349 fs_builtin_mission *fb;
5352 // get a pointer to the list item
5353 max_index = Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ? Multi_create_mission_count - 1 : Multi_create_campaign_count - 1;
5354 if((list_index < 0) || (list_index > max_index)){
5357 mcip = &Multi_create_file_list[list_index];
5359 // blit the multiplayer type icons
5360 if(mcip->flags & MISSION_TYPE_MULTI_COOP){
5361 if(Multi_common_icons[MICON_COOP] >= 0){
5362 gr_set_bitmap(Multi_common_icons[MICON_COOP], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5363 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5365 } else if(mcip->flags & MISSION_TYPE_MULTI_TEAMS){
5366 if(Multi_common_icons[MICON_TVT] >= 0){
5367 gr_set_bitmap(Multi_common_icons[MICON_TVT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5368 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5370 } else if(mcip->flags & MISSION_TYPE_MULTI_DOGFIGHT){
5371 if(Multi_common_icons[MICON_DOGFIGHT] >= 0){
5372 gr_set_bitmap(Multi_common_icons[MICON_DOGFIGHT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5373 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5377 // if its a valid mission, blit the valid mission icon
5378 if(MULTI_IS_TRACKER_GAME && (mcip->valid_status == MVALID_STATUS_VALID)){
5379 if(Multi_common_icons[MICON_VALID] >= 0){
5380 gr_set_bitmap(Multi_common_icons[MICON_VALID], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5381 gr_bitmap(Mc_icon_valid_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_valid_coords[gr_screen.res][MC_Y_COORD]);
5385 // now see if its a builtin mission
5386 fb = game_find_builtin_mission(mcip->filename);
5387 // if the mission is from volition, blit the volition icon
5388 if((fb != NULL) && (fb->flags & FSB_FROM_VOLITION)){
5389 if(Multi_common_icons[MICON_VOLITION] >= 0){
5390 gr_set_bitmap(Multi_common_icons[MICON_VOLITION], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5391 gr_bitmap(Mc_icon_volition_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_volition_coords[gr_screen.res][MC_Y_COORD]);
5396 // check for mdisk mission
5397 fb = game_find_builtin_mission(mcip->filename);
5398 if((fb != NULL) && (fb->flags & FSB_FROM_MDISK)){
5399 if(Multi_common_icons[MICON_MDISK] >= 0){
5400 gr_set_bitmap(Multi_common_icons[MICON_MDISK], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5401 gr_bitmap(Mc_icon_silent_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_silent_coords[gr_screen.res][MC_Y_COORD]);
5407 void multi_create_accept_hit()
5409 char selected_name[255];
5410 int start_campaign = 0;
5412 // make sure all players have finished joining
5413 if(!multi_netplayer_state_check(NETPLAYER_STATE_JOINED,1)){
5414 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("Please wait until all clients have finished joining",788));
5415 gamesnd_play_iface(SND_GENERAL_FAIL);
5418 gamesnd_play_iface(SND_COMMIT_PRESSED);
5421 // do single mission stuff
5422 switch(Multi_create_list_mode){
5423 case MULTI_CREATE_SHOW_MISSIONS:
5424 if(Multi_create_list_select != -1){
5425 // set the netgame mode
5426 Netgame.campaign_mode = MP_SINGLE;
5428 // setup various filenames and mission names
5429 multi_create_select_to_filename(Multi_create_list_select,selected_name);
5430 strncpy( Game_current_mission_filename, selected_name, MAX_FILENAME_LEN );
5431 strncpy(Netgame.mission_name,selected_name,MAX_FILENAME_LEN);
5434 ml_printf(NOX("Starting single mission %s, with %d players"), Game_current_mission_filename, multi_num_players());
5436 multi_common_add_notify(XSTR("No mission selected!",789));
5441 case MULTI_CREATE_SHOW_CAMPAIGNS:
5442 // do campaign related stuff
5443 if(Multi_create_list_select != -1){
5444 // set the netgame mode
5445 Netgame.campaign_mode = MP_CAMPAIGN;
5447 // start a campaign instead of a single mission
5448 multi_create_select_to_filename(Multi_create_list_select,selected_name);
5449 multi_campaign_start(selected_name);
5453 ml_printf(NOX("Starting campaign %s, with %d players"), selected_name, multi_num_players());
5455 multi_common_add_notify(XSTR("No campaign selected!",790));
5461 // if this is a team vs team situation, lock the players send a final team update
5462 if((Netgame.type_flags & NG_TYPE_TEAM) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
5463 multi_team_host_lock_all();
5464 multi_team_send_update();
5467 // if not on the standalone, move to the mission sync state which will take care of everything
5468 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5469 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
5470 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
5472 // otherwise tell the standalone to do so
5474 // when the standalone receives this, he'll do the mission syncing himself
5475 send_mission_sync_packet(MULTI_SYNC_PRE_BRIEFING,start_campaign);
5479 void multi_create_draw_filter_buttons()
5481 // highlight the correct filter button
5482 if ( Multi_create_filter == MISSION_TYPE_MULTI ){
5483 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL].button.draw_forced(2);
5484 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_COOP ) {
5485 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 1].button.draw_forced(2);
5486 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_TEAMS ) {
5487 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 2].button.draw_forced(2);
5488 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_DOGFIGHT ){
5489 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 3].button.draw_forced(2);
5495 void multi_create_set_selected_team(int team)
5499 // if we don't currently have a player selected, don't do anything
5500 if(!Multi_create_plist_select_flag){
5501 gamesnd_play_iface(SND_GENERAL_FAIL);
5504 gamesnd_play_iface(SND_USER_SELECT);
5506 // otherwise attempt to set the team for this guy
5507 player_index = find_player_id(Multi_create_plist_select_id);
5508 if(player_index != -1){
5509 multi_team_set_team(&Net_players[player_index],team);
5513 void multi_create_handle_join(net_player *pl)
5515 // for now just play a bloop sound
5516 gamesnd_play_iface(SND_ICON_DROP_ON_WING);
5519 // fill in net address of player the mouse is over, return player index (or -1 if none)
5520 short multi_create_get_mouse_id()
5522 // determine where he clicked (y pixel value)
5524 Multi_create_player_select_button.get_mouse_pos(NULL,&y);
5526 // select things a little differently if we're in team vs. team or non-team vs. team mode
5528 if(Netgame.type_flags & NG_TYPE_TEAM){
5529 int player_index = -1;
5531 // look through all of team red first
5532 for(idx=0;idx<MAX_PLAYERS;idx++){
5533 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
5536 // if this is the _nth_ guy
5544 // if we still haven't found him yet, look through the green team
5545 if(player_index == -1){
5546 for(idx=0;idx<MAX_PLAYERS;idx++){
5547 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
5549 // if this is the _nth_ guy
5558 if(player_index != -1){
5559 return Net_players[player_index].player_id;
5562 // select the nth active player if possible, disregarding the standalone server
5563 for(idx=0;idx<MAX_PLAYERS;idx++){
5564 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
5567 // if this is the _nth_ guy
5569 return Net_players[idx].player_id;
5578 void multi_create_select_to_filename(int select_index,char *filename)
5582 // look through the mission list
5583 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5584 for(idx=0;idx<Multi_create_mission_count;idx++){
5585 if(Multi_create_file_list[idx].flags & Multi_create_filter){
5589 // if we found the item
5590 if(select_index < 0){
5591 strcpy(filename,Multi_create_file_list[idx].filename);
5596 // look through the campaign list
5597 else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
5598 for(idx=0;idx<Multi_create_campaign_count;idx++){
5601 // if we found the item
5602 if(select_index < 0){
5603 strcpy(filename,Multi_create_file_list[idx].filename);
5609 strcpy(filename,"");
5612 int multi_create_select_to_index(int select_index)
5615 int lookup_index = 0;
5617 // look through the mission list
5618 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5619 for(idx=0;idx<Multi_create_mission_count;idx++){
5620 if(Multi_create_file_list[idx].flags & Multi_create_filter){
5624 // if we found the item
5625 if(select_index < lookup_index){
5630 // look through the campaign list
5631 else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
5632 for(idx=0;idx<Multi_create_campaign_count;idx++){
5635 // if we found the item
5636 if(select_index < 0){
5645 int multi_create_ok_to_commit()
5647 int player_count, observer_count, idx;
5648 int notify_of_hacked_ships_tbl = 0;
5649 int notify_of_hacked_weapons_tbl = 0;
5650 char err_string[255];
5654 // make sure we have a valid mission selected
5655 if(Multi_create_list_select < 0){
5659 // if this is not a valid mission, let the player know
5660 abs_index = multi_create_select_to_index(Multi_create_list_select);
5665 // if we're playing with a hacked ships.tbl (on PXO)
5666 notify_of_hacked_ships_tbl = 0;
5667 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5668 if(!Game_ships_tbl_valid){
5669 notify_of_hacked_ships_tbl = 1;
5672 if(Netgame.flags & NG_FLAG_HACKED_SHIPS_TBL){
5673 notify_of_hacked_ships_tbl = 1;
5676 if(!MULTI_IS_TRACKER_GAME){
5677 notify_of_hacked_ships_tbl = 0;
5679 if(notify_of_hacked_ships_tbl){
5680 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){
5685 // if we're playing with a hacked weapons.tbl (on PXO)
5686 notify_of_hacked_weapons_tbl = 0;
5687 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5688 if(!Game_weapons_tbl_valid){
5689 notify_of_hacked_weapons_tbl = 1;
5692 if(Netgame.flags & NG_FLAG_HACKED_WEAPONS_TBL){
5693 notify_of_hacked_weapons_tbl = 1;
5696 if(!MULTI_IS_TRACKER_GAME){
5697 notify_of_hacked_weapons_tbl = 0;
5699 if(notify_of_hacked_weapons_tbl){
5700 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){
5705 // if any of the players have hacked data
5707 for(idx=0; idx<MAX_PLAYERS; idx++){
5708 // look for hacked players
5709 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAXOR)){
5713 // message everyone - haha
5714 if(Net_players[idx].player != NULL){
5715 sprintf(err_string, "%s %s", Net_players[idx].player->callsign, XSTR("has hacked tables/data", 1271));
5717 sprintf(err_string, "somebody %s", XSTR("has hacked tables/data", 1271));
5719 send_game_chat_packet(Net_player, err_string, MULTI_MSG_ALL, NULL, NULL, 1);
5722 // if we found a hacked set of data
5725 if(MULTI_IS_TRACKER_GAME){
5726 // don't allow squad war matches to continue
5727 if(Netgame.type_flags & NG_TYPE_SW){
5729 // if this is squad war, don't allow it to continue
5730 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));
5735 // otherwise, warn the players that stats will not saved
5737 // if this is squad war, don't allow it to continue
5738 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){
5743 // non-pxo, just give a notice
5745 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){
5751 // check to see that we don't have too many observers
5752 observer_count = multi_num_observers();
5753 if(observer_count > Netgame.options.max_observers){
5754 // print up the error string
5755 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);
5757 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, err_string);
5761 // check to see that we have a valid # of players for the the # of ships in the game
5762 player_count = multi_num_players();
5763 if(player_count > Netgame.max_players){
5764 // print up the error string
5765 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);
5767 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, err_string);
5771 // check to see if teams are assigned properly in a team vs. team situation
5772 if(Netgame.type_flags & NG_TYPE_TEAM){
5773 if(!multi_team_ok_to_commit()){
5774 gamesnd_play_iface(SND_GENERAL_FAIL);
5775 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("Teams and/or team captains are not assigned properly", 793));
5781 if(!multi_create_verify_cds()){
5782 gamesnd_play_iface(SND_GENERAL_FAIL);
5784 #ifdef MULTIPLAYER_BETA_BUILD
5785 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, "You need 1 CD for every player!");
5787 #ifdef DVD_MESSAGE_HACK
5788 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You need 1 DVD for every 4 players!", 794));
5790 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You need 1 CD for every 4 players!", 794));
5796 // if we're playing on the tracker
5797 if(MULTI_IS_TRACKER_GAME){
5798 #ifdef PXO_CHECK_VALID_MISSIONS
5799 if((Multi_create_file_list == Multi_create_mission_list) && (Multi_create_file_list[abs_index].valid_status != MVALID_STATUS_VALID)){
5800 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){
5807 if(!(Netgame.type_flags & NG_TYPE_SW)){
5808 // if he is playing by himself, tell him stats will not be accepted
5809 if(multi_num_players() == 1){
5810 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){
5823 int multi_create_verify_cds()
5825 int player_count = multi_num_players();
5829 // count how many cds we have
5831 for(idx=0;idx<MAX_PLAYERS;idx++){
5832 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAS_CD)){
5837 // for the beta, everyone must have a CD
5838 #ifdef MULTIPLAYER_BETA_BUILD
5839 if(multi_cd_count < player_count){
5843 // determine if we have enough
5844 float ratio = (float)player_count / (float)multi_cd_count;
5845 // greater than a 4 to 1 ratio
5851 // we meet the conditions
5855 // returns an index into Multi_create_mission_list
5856 int multi_create_lookup_mission(char *fname)
5860 for(idx=0; idx<Multi_create_mission_count; idx++){
5861 if(!stricmp(fname, Multi_create_mission_list[idx].filename)){
5866 // couldn't find the mission
5870 // returns an index into Multi_create_campaign_list
5871 int multi_create_lookup_campaign(char *fname)
5875 for(idx=0; idx<Multi_create_campaign_count; idx++){
5876 if(!stricmp(fname, Multi_create_campaign_list[idx].filename)){
5881 // couldn't find the campaign
5885 void multi_create_refresh_pxo()
5887 // delete mvalid.cfg if it exists
5888 cf_delete(MULTI_VALID_MISSION_FILE, CF_TYPE_DATA);
5890 // refresh missions from the tracker
5891 multi_update_valid_missions();
5894 void multi_create_sw_clicked()
5896 netgame_info ng_temp;
5899 int file_index = multi_create_select_to_index(Multi_create_list_select);
5901 // either a temporary netgame or the real one
5902 if(MULTIPLAYER_MASTER){
5909 // maybe switch squad war off
5910 if(!Multi_create_sw_checkbox.checked()){
5911 // if the mission selected is a coop mission, go back to coop mode
5912 Assert(file_index != -1);
5913 if(file_index == -1){
5914 ng->type_flags = NG_TYPE_COOP;
5916 if(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_TEAMS){
5917 ng->type_flags = NG_TYPE_TVT;
5918 } else if(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_DOGFIGHT){
5919 ng->type_flags = NG_TYPE_DOGFIGHT;
5921 ng->type_flags = NG_TYPE_COOP;
5924 // switch squad war on
5926 Assert(file_index != -1);
5927 if((file_index == -1) || !(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_TEAMS)){
5928 Multi_create_sw_checkbox.set_state(0);
5930 // at this point we know its safe to switch squad war on
5931 ng->type_flags = NG_TYPE_SW;
5936 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5938 send_netgame_update_packet();
5940 // update all machines about stuff like respawns, etc.
5941 multi_options_update_netgame();
5943 // on the standalone
5945 // standalone will take care of polling the usertracker
5946 multi_options_update_mission(ng, Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ? 1 : 0);
5951 // -------------------------------------------------------------------------------------------------------------
5953 // MULTIPLAYER HOST OPTIONS SCREEN
5956 #define MULTI_HO_NUM_BUTTONS 12
5957 #define MULTI_HO_NUM_RADIO_BUTTONS 10
5961 #define MULTI_HO_PALETTE "InterfacePalette"
5963 static char *Multi_ho_bitmap_fname[GR_NUM_RESOLUTIONS] = {
5964 "MultiHost", // GR_640
5965 "2_MultiHost" // GR_1024
5968 static char *Multi_ho_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
5969 "MultiHost-M", // GR_640
5970 "2_MultiHost-M" // GR_1024
5974 UI_WINDOW Multi_ho_window; // the window object for the join screen
5975 UI_INPUTBOX Multi_ho_respawns; // the # of respawns allowed in the game
5976 UI_INPUTBOX Multi_ho_time_limit; // mission time limit
5977 UI_INPUTBOX Multi_ho_voice_wait; // wait time between tokens
5978 UI_INPUTBOX Multi_ho_kill_limit; // kill limit in a furball mission
5979 UI_INPUTBOX Multi_ho_obs; // # of observers we'll allow
5980 int Multi_ho_bitmap; // the background bitmap
5982 // constants for coordinate lookup
5983 #define MULTI_HO_X_COORD 0
5984 #define MULTI_HO_Y_COORD 1
5985 #define MULTI_HO_W_COORD 2
5986 #define MULTI_HO_H_COORD 3
5987 #define MULTI_HO_TEXT_X_COORD 4
5988 #define MULTI_HO_TEXT_Y_COORD 5
5991 #define MULTI_HO_MSG_RANK 0 // highest ranking players can do messaging
5992 #define MULTI_HO_MSG_LEADER 1 // wing/team leaders can do messaging
5993 #define MULTI_HO_MSG_ANY 2 // any player can do messaging
5994 #define MULTI_HO_MSG_HOST 3 // only the host can do messaging
5995 #define MULTI_HO_END_RANK 4 // highest rank can and host can end mission
5996 #define MULTI_HO_END_LEADER 5 // wing/team leaders and host can end the mission
5997 #define MULTI_HO_END_ANY 6 // any player can end the mission
5998 #define MULTI_HO_END_HOST 7 // only host can end the mission
5999 #define MULTI_HO_VOICE_ON 8 // voice toggled on
6000 #define MULTI_HO_VOICE_OFF 9 // voice toggled off
6001 #define MULTI_HO_HOST_MODIFIES 10 // only the host or team captains can modify ships/weapons in briefing
6002 #define MULTI_HO_ACCEPT 11 // accept button
6004 ui_button_info Multi_ho_buttons[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_BUTTONS] = {
6007 // who is allowed to message
6008 ui_button_info("MH_00", 13, 157, -1, -1, 0), // highest rank
6009 ui_button_info("MH_01", 13, 179, -1, -1, 1), // team/wing leader
6010 ui_button_info("MH_02", 13, 200, -1, -1, 2), // any
6011 ui_button_info("MH_03", 13, 223, -1, -1, 3), // host
6013 // who is allowed to end the mission
6014 ui_button_info("MH_09", 13, 273, -1, -1, 9), // highest rank
6015 ui_button_info("MH_10", 13, 295, -1, -1, 10), // team/wing leader
6016 ui_button_info("MH_11", 13, 317, -1, -1, 11), // any
6017 ui_button_info("MH_12", 13, 339, -1, -1, 12), // host
6019 // voice on/off button
6020 ui_button_info("MH_14", 396, 156, -1, -1, 14),
6021 ui_button_info("MH_15", 453, 156, -1, -1, 15),
6023 // host modifies ships
6024 ui_button_info("MH_19", 215, 410, -1, -1, 19),
6027 ui_button_info("MH_18", 560, 411, -1, -1, 18),
6029 // who is allowed to message
6030 ui_button_info("MH_00", 3, 160, 46, 166, 0), // highest rank
6031 ui_button_info("MH_01", 3, 179, 46, 185, 1), // team/wing leader
6032 ui_button_info("MH_02", 3, 196, 46, 203, 2), // any
6033 ui_button_info("MH_03", 3, 214, 46, 220, 3), // host
6035 // who is allowed to end the mission
6036 ui_button_info("MH_04", 3, 257, 46, 265, 4), // highest rank
6037 ui_button_info("MH_05", 3, 276, 46, 283, 5), // team/wing leader
6038 ui_button_info("MH_06", 3, 294, 46, 300, 6), // any
6039 ui_button_info("MH_07", 3, 311, 46, 317, 7), // host
6041 // voice on/off button
6042 ui_button_info("MH_09", 542, 158, 545, 185, 9),
6043 ui_button_info("MH_10", 598, 158, 604, 185, 10),
6045 // host modifies ships
6046 ui_button_info("MH_13", 542, 377, 437, 363, 13),
6049 ui_button_info("MH_14", 572, 428, 580, 414, 14),
6053 // who is allowed to message
6054 ui_button_info("2_MH_00", 5, 256, 73, 269, 0), // highest rank
6055 ui_button_info("2_MH_01", 5, 286, 73, 297, 1), // team/wing leader
6056 ui_button_info("2_MH_02", 5, 314, 73, 325, 2), // any
6057 ui_button_info("2_MH_03", 5, 341, 73, 352, 3), // host
6059 // who is allowed to end the mission
6060 ui_button_info("2_MH_04", 5, 412, 73, 425, 4), // highest rank
6061 ui_button_info("2_MH_05", 5, 442, 73, 452, 5), // team/wing leader
6062 ui_button_info("2_MH_06", 5, 470, 73, 480, 6), // any
6063 ui_button_info("2_MH_07", 5, 497, 73, 508, 7), // host
6065 // voice on/off button
6066 ui_button_info("2_MH_09", 867, 253, 872, 296, 9),
6067 ui_button_info("2_MH_10", 957, 253, 966, 296, 10),
6069 // host modifies ships
6070 ui_button_info("2_MH_13", 867, 603, 784, 581, 13),
6073 ui_button_info("2_MH_14", 916, 685, 925, 665, 14),
6076 UI_XSTR Multi_ho_text[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_BUTTONS] = {
6078 // not needed for FS1
6080 {"Highest rank", 1280, 46, 166, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_RANK].button},
6081 {"Team / wing-leader", 1281, 46, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_LEADER].button},
6082 {"Any", 1282, 46, 203, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_ANY].button},
6083 {"Host", 1283, 46, 220, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_HOST].button},
6084 {"Highest rank", 1280, 46, 265, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_RANK].button},
6085 {"Team / wing-leader", 1281, 46, 283, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_LEADER].button},
6086 {"Any", 1282, 46, 300, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_ANY].button},
6087 {"Host", 1283, 46, 317, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_HOST].button},
6088 {"On", 1285, 545, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_VOICE_ON].button},
6089 {"Off", 1286, 604, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_VOICE_OFF].button},
6090 {"Host modifies ships", 1287, 437, 363, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_HOST_MODIFIES].button},
6091 {"Exit", 1417, 572, 418, UI_XSTR_COLOR_PINK, -1, &Multi_ho_buttons[0][MULTI_HO_ACCEPT].button},
6095 // not needed for FS1
6097 {"Highest rank", 1280, 62, 269, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_RANK].button},
6098 {"Team / wing-leader", 1281, 62, 297, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_LEADER].button},
6099 {"Any", 1282, 62, 325, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_ANY].button},
6100 {"Host", 1283, 62, 352, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_HOST].button},
6101 {"Highest rank", 1280, 62, 425, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_RANK].button},
6102 {"Team / wing-leader", 1281, 62, 452, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_LEADER].button},
6103 {"Any", 1282, 62, 480, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_ANY].button},
6104 {"Host", 1283, 62, 508, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_HOST].button},
6105 {"On", 1285, 877, 294, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_VOICE_ON].button},
6106 {"Off", 1286, 967, 293, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_VOICE_OFF].button},
6107 {"Host modifies ships", 1287, 869, 589, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_HOST_MODIFIES].button},
6108 {"Exit", 1417, 953, 672, UI_XSTR_COLOR_PINK, -1, &Multi_ho_buttons[1][MULTI_HO_ACCEPT].button},
6113 // radio button controls
6114 #define MULTI_HO_NUM_RADIO_GROUPS 3
6115 #define MULTI_HO_MSG_GROUP 0 // group dealing with squadmate messaging
6116 #define MULTI_HO_END_GROUP 1 // group dealing with ending the mission
6117 #define MULTI_HO_VOICE_GROUP 2 // group dealing with voice stuff
6118 int Multi_ho_radio_groups[MULTI_HO_NUM_RADIO_GROUPS] = { // currently selected button in the radio button group
6121 int Multi_ho_radio_info[MULTI_HO_NUM_RADIO_BUTTONS][3] = { // info related to each of the radio buttons themselves
6122 // { group #, value, button id# }
6123 {0, 0, 0}, // highest ranking players can do messaging
6124 {0, 1, 1}, // wing/team leaders can do messaging
6125 {0, 2, 2}, // any player can do messaging
6126 {0, 3, 3}, // only host can do messaging
6127 {1, 0, 4}, // highest rank and host can end the mission
6128 {1, 1, 5}, // team/wing leader can end the mission
6129 {1, 2, 6}, // any player can end the mission
6130 {1, 3, 7}, // only the host can end the mission
6131 {2, 0, 8}, // voice toggled on
6132 {2, 1, 9} // voice toggled off
6136 #define MULTI_HO_NUM_SLIDERS 3
6137 #define MULTI_HO_SLIDER_VOICE_QOS 0 // voice quality of sound
6138 #define MULTI_HO_SLIDER_VOICE_DUR 1 // max duration of voice recording
6139 #define MULTI_HO_SLIDER_SKILL 2 // skill level
6146 UI_DOT_SLIDER_NEW slider; // because we have a class inside this struct, we need the constructor below..
6148 ho_sliders(char *name, int x1, int y1, int xt1, int yt1, int h, int _dot_w, int _dots) : filename(name), x(x1), y(y1), xt(xt1), yt(yt1), hotspot(h), dot_w(_dot_w), dots(_dots){}
6150 ho_sliders Multi_ho_sliders[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_SLIDERS] = {
6153 ho_sliders("MH_16", 396, 210, -1, -1, 16, 19, 10), // voice qos
6154 ho_sliders("MH_17", 396, 263, -1, -1, 17, 19, 10), // voice duration
6155 ho_sliders("MH_13", 10, 387, -1, -1, 13, 36, 5), // skill level
6157 ho_sliders("MH_11", 428, 214, 437, 199, 11, 19, 10), // voice qos
6158 ho_sliders("MH_12", 428, 261, 437, 246, 12, 19, 10), // voice duration
6159 ho_sliders("MH_08", 237, 454, 230, 411, 8, 36, 5), // skill level
6163 ho_sliders("2_MH_11", 684, 343, 690, 323, 11, 32, 10), // voice qos
6164 ho_sliders("2_MH_12", 685, 418, 837, 468, 12, 32, 10), // voice duration
6165 ho_sliders("2_MH_08", 379, 727, 369, 663, 8, 60, 5), // skill level
6169 int Multi_ho_mission_respawn;
6171 int Multi_ho_host_modifies;
6173 // whether or not any of the inputboxes on this screen had focus last frame
6174 int Multi_ho_lastframe_input = 0;
6176 // game information text areas
6180 #define MULTI_HO_NUM_TITLES 14
6182 UI_XSTR Multi_ho_titles[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_TITLES] = {
6184 { "AI Orders", 1289, 32, 144, UI_XSTR_COLOR_GREEN, -1, NULL },
6185 { "End Mission", 1290, 32, 242, UI_XSTR_COLOR_GREEN, -1, NULL },
6186 { "Time Limit", 1291, 32, 347, UI_XSTR_COLOR_GREEN, -1, NULL },
6187 { "Min", 1292, 74, 362, UI_XSTR_COLOR_GREEN, -1, NULL },
6188 { "Respawn Limit", 1288, 32, 378, UI_XSTR_COLOR_GREEN, -1, NULL },
6189 { "Kill Limit", 1293, 32, 409, UI_XSTR_COLOR_GREEN, -1, NULL },
6190 { "Observers", 1294, 32, 441, UI_XSTR_COLOR_GREEN, -1, NULL },
6191 { "Skill Level", 1284, 230, 411, UI_XSTR_COLOR_GREEN, -1, NULL },
6192 { "Voice Transmission", 1295, 437, 144, UI_XSTR_COLOR_GREEN, -1, NULL },
6193 { "Voice Quality", 1296, 437, 199, UI_XSTR_COLOR_GREEN, -1, NULL },
6194 { "Message Duration", 1297, 437, 246, UI_XSTR_COLOR_GREEN, -1, NULL },
6195 { "sec", 1522, 523, 292, UI_XSTR_COLOR_GREEN, -1, NULL },
6196 { "sec", 1523, 523, 332, UI_XSTR_COLOR_GREEN, -1, NULL },
6197 { "Voice Wait", 1298, 437, 313, UI_XSTR_COLOR_GREEN, -1, NULL },
6200 { "AI Orders", 1289, 48, 238, UI_XSTR_COLOR_GREEN, -1, NULL },
6201 { "End Mission", 1290, 48, 394, UI_XSTR_COLOR_GREEN, -1, NULL },
6202 { "Time Limit", 1291, 50, 568, UI_XSTR_COLOR_GREEN, -1, NULL },
6203 { "Min", 1292, 119, 581, UI_XSTR_COLOR_GREEN, -1, NULL },
6204 { "Respawn Limit", 1288, 50, 618, UI_XSTR_COLOR_GREEN, -1, NULL },
6205 { "Kill Limit", 1293, 50, 668, UI_XSTR_COLOR_GREEN, -1, NULL },
6206 { "Observers", 1294, 50, 718, UI_XSTR_COLOR_GREEN, -1, NULL },
6207 { "Skill Level", 1284, 398, 670, UI_XSTR_COLOR_GREEN, -1, NULL },
6208 { "Voice Transmission", 1295, 869, 239, UI_XSTR_COLOR_GREEN, -1, NULL },
6209 { "Voice Quality", 1296, 690, 331, UI_XSTR_COLOR_GREEN, -1, NULL },
6210 { "Message Duration", 1297, 690, 405, UI_XSTR_COLOR_GREEN, -1, NULL },
6211 { "sec", 1522, 837, 467, UI_XSTR_COLOR_GREEN, -1, NULL },
6212 { "sec", 1523, 837, 534, UI_XSTR_COLOR_GREEN, -1, NULL },
6213 { "Voice Wait", 1298, 742, 510, UI_XSTR_COLOR_GREEN, -1, NULL },
6218 // mission time limit input box
6219 int Ho_time_coords[GR_NUM_RESOLUTIONS][4] = {
6232 // furball kill limit input box
6233 int Ho_kill_coords[GR_NUM_RESOLUTIONS][4] = {
6246 // voice recording duration text display area
6247 int Ho_vd_coords[GR_NUM_RESOLUTIONS][4] = {
6260 // voice token wait input box
6261 int Ho_vw_coords[GR_NUM_RESOLUTIONS][6] = {
6274 // observer count input box
6275 int Ho_obs_coords[GR_NUM_RESOLUTIONS][4] = {
6288 // skill text description area
6289 int Ho_st_coords[GR_NUM_RESOLUTIONS][4] = {
6302 // respawn input box
6303 int Ho_rsp_coords[GR_NUM_RESOLUTIONS][6] = {
6316 // respawn max text area
6317 int Ho_max_rsp_coords[GR_NUM_RESOLUTIONS][2] = {
6330 // maximum values for various input boxes (to notify user of overruns)
6331 #define MULTI_HO_MAX_TIME_LIMIT 500
6332 #define MULTI_HO_MAX_TOKEN_WAIT 5
6333 #define MULTI_HO_MAX_KILL_LIMIT 9999
6334 #define MULTI_HO_MAX_OBS 4
6336 // LOCAL function definitions
6337 void multi_ho_check_buttons();
6338 void multi_ho_button_pressed(int n);
6339 void multi_ho_draw_radio_groups();
6340 void multi_ho_accept_hit();
6341 void multi_ho_get_options();
6342 void multi_ho_apply_options();
6343 void multi_ho_display_record_time();
6344 int multi_ho_check_values();
6345 void multi_ho_check_focus();
6346 void multi_ho_blit_max_respawns();
6347 void multi_ho_display_skill_level();
6349 void multi_host_options_init()
6353 // create the interface window
6354 Multi_ho_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
6355 Multi_ho_window.set_mask_bmap(Multi_ho_bitmap_mask_fname[gr_screen.res]);
6357 // load the background bitmap
6358 Multi_ho_bitmap = bm_load(Multi_ho_bitmap_fname[gr_screen.res]);
6359 if(Multi_ho_bitmap < 0){
6360 // we failed to load the bitmap - this is very bad
6364 // initialize the common notification messaging
6365 multi_common_notify_init();
6367 // use the common interface palette
6368 multi_common_set_palette();
6370 // create the interface buttons
6371 for(idx=0;idx<MULTI_HO_NUM_BUTTONS;idx++){
6372 // create the object
6373 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);
6375 // set the sound to play when highlighted
6376 Multi_ho_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
6378 // set the ani for the button
6379 Multi_ho_buttons[gr_screen.res][idx].button.set_bmaps(Multi_ho_buttons[gr_screen.res][idx].filename);
6381 // set the hotspot, ignoring the skill level button
6382 Multi_ho_buttons[gr_screen.res][idx].button.link_hotspot(Multi_ho_buttons[gr_screen.res][idx].hotspot);
6386 Multi_ho_window.add_XSTR(&Multi_ho_text[gr_screen.res][idx]);
6392 for(idx=0; idx<MULTI_HO_NUM_TITLES; idx++){
6393 Multi_ho_window.add_XSTR(&Multi_ho_titles[gr_screen.res][idx]);
6397 // create the interface sliders
6398 for(idx=0; idx<MULTI_HO_NUM_SLIDERS; idx++){
6399 // create the object
6400 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);
6403 // create the respawn count input box
6404 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);
6405 // if we're in campaign mode, disable it
6406 if(Netgame.campaign_mode == MP_CAMPAIGN){
6407 Multi_ho_respawns.set_text(XSTR("NA",795)); // [[ Not applicable ]]
6408 Multi_ho_respawns.disable();
6410 Multi_ho_respawns.set_valid_chars("0123456789");
6413 // create the time limit input box
6414 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);
6415 Multi_ho_time_limit.set_valid_chars("-0123456789");
6417 // create the voice token wait input box
6418 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);
6419 Multi_ho_voice_wait.set_valid_chars("01243456789");
6421 // create the furball kill limit input box
6422 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);
6423 Multi_ho_kill_limit.set_valid_chars("0123456789");
6425 // create the observer limit input box
6426 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);
6427 Multi_ho_obs.set_valid_chars("01234");
6429 // load in the current netgame defaults
6430 multi_ho_get_options();
6432 // whether or not any of the inputboxes on this screen had focus last frame
6433 Multi_ho_lastframe_input = 0;
6435 // get the # of respawns for the currently selected mission (if any)
6436 if(Multi_create_list_select != -1){
6437 int abs_index = multi_create_select_to_index(Multi_create_list_select);
6439 // if he has a valid mission selected
6441 Multi_ho_mission_respawn = (int)Multi_create_file_list[abs_index].respawn;
6443 Multi_ho_mission_respawn = -1;
6446 Multi_ho_mission_respawn = -1;
6450 void multi_ho_update_sliders()
6452 // game skill slider
6453 if (Game_skill_level != Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos) {
6454 if ( !(Netgame.type_flags & NG_TYPE_TEAM) ){
6455 Game_skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6456 gamesnd_play_iface(SND_USER_SELECT);
6458 Game_skill_level = NUM_SKILL_LEVELS / 2;
6462 // get the voice qos options
6463 if (Netgame.options.voice_qos != (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1)) {
6464 Netgame.options.voice_qos = (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1);
6465 gamesnd_play_iface(SND_USER_SELECT);
6468 // get the voice duration options
6469 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)) {
6470 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);
6471 gamesnd_play_iface(SND_USER_SELECT);
6476 void multi_host_options_do()
6481 k = Multi_ho_window.process();
6484 // process any keypresses
6487 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
6490 case KEY_CTRLED + KEY_ENTER :
6491 gamesnd_play_iface(SND_COMMIT_PRESSED);
6492 multi_ho_accept_hit();
6496 // process any button clicks
6497 multi_ho_check_buttons();
6499 // update the sliders
6500 multi_ho_update_sliders();
6502 // make sure that the chatbox inputbox and any inputbox on this screen are mutually exclusive in terms of focus
6503 multi_ho_check_focus();
6505 // draw the background, etc
6507 GR_MAYBE_CLEAR_RES(Multi_ho_bitmap);
6508 if(Multi_ho_bitmap != -1){
6509 gr_set_bitmap(Multi_ho_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
6512 Multi_ho_window.draw();
6514 // draw all the radio buttons properly
6515 multi_ho_draw_radio_groups();
6517 // display any pending notification messages
6518 multi_common_notify_do();
6520 // display the voice record time settings
6521 multi_ho_display_record_time();
6523 // maybe display the max # of respawns next to the respawn input box
6524 multi_ho_blit_max_respawns();
6526 // blit the proper skill level
6527 multi_ho_display_skill_level();
6529 // blit the "host modifies button"
6530 if(Multi_ho_host_modifies){
6531 Multi_ho_buttons[gr_screen.res][MULTI_HO_HOST_MODIFIES].button.draw_forced(2);
6534 // process and show the chatbox thingie
6538 Multi_ho_window.draw_tooltip();
6540 // display the voice status indicator
6541 multi_common_voice_display_status();
6547 void multi_host_options_close()
6549 // unload any bitmaps
6550 if(!bm_unload(Multi_ho_bitmap)){
6551 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_ho_bitmap_fname[gr_screen.res]));
6554 // destroy the UI_WINDOW
6555 Multi_ho_window.destroy();
6558 void multi_ho_check_buttons()
6561 for(idx=0;idx<MULTI_HO_NUM_BUTTONS;idx++){
6562 // we only really need to check for one button pressed at a time, so we can break after
6564 if(Multi_ho_buttons[gr_screen.res][idx].button.pressed()){
6565 multi_ho_button_pressed(idx);
6571 void multi_ho_button_pressed(int n)
6573 int radio_index,idx;
6574 int x_pixel,y_pixel;
6576 // get the pixel position of the click
6577 Multi_ho_buttons[gr_screen.res][n].button.get_mouse_pos(&x_pixel,&y_pixel);
6580 // clicked on the accept button
6581 case MULTI_HO_ACCEPT:
6582 gamesnd_play_iface(SND_COMMIT_PRESSED);
6583 multi_ho_accept_hit();
6586 // clicked on the host/captains only modify button
6587 case MULTI_HO_HOST_MODIFIES:
6588 // toggle it on or off
6589 Multi_ho_host_modifies = !Multi_ho_host_modifies;
6590 gamesnd_play_iface(SND_USER_SELECT);
6594 // look through the radio buttons and see which one this corresponds to
6596 for(idx=0;idx<MULTI_HO_NUM_RADIO_BUTTONS;idx++){
6597 if(Multi_ho_radio_info[idx][2] == n){
6602 Assert(radio_index != -1);
6604 // check to see if a radio button was pressed
6605 if(radio_index < MULTI_HO_NUM_RADIO_BUTTONS){
6606 // see if this value is already picked for this radio group
6607 if(Multi_ho_radio_groups[Multi_ho_radio_info[radio_index][0]] != Multi_ho_radio_info[radio_index][1]){
6608 gamesnd_play_iface(SND_USER_SELECT);
6609 Multi_ho_radio_groups[Multi_ho_radio_info[radio_index][0]] = Multi_ho_radio_info[radio_index][1];
6611 gamesnd_play_iface(SND_GENERAL_FAIL);
6616 void multi_ho_draw_radio_groups()
6620 // go through each item and draw it if it is the selected button in its respective group
6621 for(idx=0;idx<MULTI_HO_NUM_RADIO_BUTTONS;idx++){
6622 /// if this button is the currently selected one in its group
6623 if(Multi_ho_radio_info[idx][1] == Multi_ho_radio_groups[Multi_ho_radio_info[idx][0]]){
6624 Multi_ho_buttons[gr_screen.res][Multi_ho_radio_info[idx][2]].button.draw_forced(2);
6629 void multi_ho_accept_hit()
6633 // check the values in the input boxes
6634 if(!multi_ho_check_values()){
6638 // zero out the netgame flags
6641 // set default options
6642 Netgame.options.flags = (MSO_FLAG_INGAME_XFER | MSO_FLAG_ACCEPT_PIX);
6644 // set the squadmate messaging flags
6645 switch(Multi_ho_radio_groups[MULTI_HO_MSG_GROUP]){
6647 Netgame.options.squad_set = MSO_SQUAD_RANK;
6650 Netgame.options.squad_set = MSO_SQUAD_LEADER;
6653 Netgame.options.squad_set = MSO_SQUAD_ANY;
6656 Netgame.options.squad_set = MSO_SQUAD_HOST;
6662 // set the end mission flags
6663 switch(Multi_ho_radio_groups[MULTI_HO_END_GROUP]){
6665 Netgame.options.endgame_set = MSO_END_RANK;
6668 Netgame.options.endgame_set = MSO_END_LEADER;
6671 Netgame.options.endgame_set = MSO_END_ANY;
6674 Netgame.options.endgame_set = MSO_END_HOST;
6680 // set the voice toggle
6681 switch(Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP]){
6683 Netgame.options.flags &= ~(MSO_FLAG_NO_VOICE);
6686 Netgame.options.flags |= MSO_FLAG_NO_VOICE;
6692 // get the voice qos options
6693 Netgame.options.voice_qos = (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1);
6695 // get the voice duration options
6696 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);
6698 // set the skill level. If in team vs. team mode, preserve the old setting before saving
6699 // the pilot file. I'll bet that this doesn't work though because the pilot file gets
6700 // written in a bunch of locations....sigh.
6701 if ( !(Netgame.type_flags & NG_TYPE_TEAM) ){
6702 Game_skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6704 Game_skill_level = NUM_SKILL_LEVELS / 2;
6707 // set the netgame respawn count
6708 // maybe warn the user that respawns will not be used for a campaign mission
6709 if(Netgame.campaign_mode == MP_SINGLE){
6710 Multi_ho_respawns.get_text(resp_str);
6711 uint temp_respawn = (uint)atoi(resp_str);
6712 // if he currently has no mission selected, let the user set any # of respawns
6713 if((int)temp_respawn > Multi_ho_mission_respawn){
6714 if(Multi_ho_mission_respawn == -1){
6715 Netgame.respawn = temp_respawn;
6716 Netgame.options.respawn = temp_respawn;
6718 // this should have been taken care of by the interface code
6723 Netgame.options.respawn = temp_respawn;
6724 Netgame.respawn = temp_respawn;
6728 // get the mission time limit
6729 Multi_ho_time_limit.get_text(resp_str);
6730 int temp_time = atoi(resp_str);
6732 Netgame.options.mission_time_limit = fl2f(-1.0f);
6733 } else if(temp_time > MULTI_HO_MAX_TIME_LIMIT){
6736 Netgame.options.mission_time_limit = fl2f(60.0f * (float)temp_time);
6739 // get observer count options
6740 Multi_ho_obs.get_text(resp_str);
6741 int temp_obs = atoi(resp_str);
6742 if(temp_obs > MULTI_HO_MAX_OBS){
6745 Netgame.options.max_observers = (ubyte)temp_obs;
6747 // get the furball kill limit
6748 Multi_ho_kill_limit.get_text(resp_str);
6749 int temp_kills = atoi(resp_str);
6750 if(temp_kills > MULTI_HO_MAX_KILL_LIMIT){
6753 Netgame.options.kill_limit = temp_kills;
6755 // get the token wait limit
6756 Multi_ho_voice_wait.get_text(resp_str);
6757 int temp_wait = atoi(resp_str);
6758 if(temp_wait > MULTI_HO_MAX_TOKEN_WAIT){
6761 Netgame.options.voice_token_wait = (temp_wait * 1000);
6763 // set the netgame option
6764 Netgame.options.skill_level = (ubyte)Game_skill_level;
6766 // get whether we're in host/captains only modify mode
6767 Netgame.options.flags &= ~(MSO_FLAG_SS_LEADERS);
6768 if(Multi_ho_host_modifies){
6769 Netgame.options.flags |= MSO_FLAG_SS_LEADERS;
6772 // store these values locally
6773 memcpy(&Player->m_local_options,&Net_player->p_info.options,sizeof(multi_local_options));
6774 memcpy(&Player->m_server_options,&Netgame.options,sizeof(multi_server_options));
6775 write_pilot_file(Player);
6777 // apply any changes in settings (notify everyone of voice qos changes, etc)
6778 multi_ho_apply_options();
6780 // move back to the create game screen
6781 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
6784 void multi_ho_get_options()
6788 // set the squadmate messaging buttons
6789 switch(Netgame.options.squad_set){
6790 case MSO_SQUAD_RANK :
6791 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 0;
6793 case MSO_SQUAD_LEADER:
6794 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 1;
6797 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 2;
6799 case MSO_SQUAD_HOST:
6800 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 3;
6806 // set the mission end buttons
6807 switch(Netgame.options.endgame_set){
6809 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 0;
6811 case MSO_END_LEADER:
6812 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 1;
6815 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 2;
6818 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 3;
6824 // set the voice toggle buttons
6825 if(Netgame.options.flags & MSO_FLAG_NO_VOICE){
6826 Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP] = 1;
6828 Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP] = 0;
6831 // get the voice qos options
6832 Assert((Netgame.options.voice_qos >= 1) && (Netgame.options.voice_qos <= 10));
6833 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos = (Netgame.options.voice_qos - 1);
6835 // get the voice duration options
6836 Assert((Netgame.options.voice_record_time > 0) && (Netgame.options.voice_record_time <= MULTI_VOICE_MAX_TIME));
6837 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos = ((int)((float)Netgame.options.voice_record_time / 500.0f)) - 1;
6839 // get the current skill level
6840 Assert((Game_skill_level >= 0) && (Game_skill_level < NUM_SKILL_LEVELS));
6841 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos = Game_skill_level;
6843 // get the # of observers
6844 memset(resp_str,0,10);
6845 sprintf(resp_str,"%d",Netgame.options.max_observers);
6846 Multi_ho_obs.set_text(resp_str);
6848 // set the respawn count
6849 if(Netgame.campaign_mode == MP_SINGLE){
6850 memset(resp_str,0,10);
6851 sprintf(resp_str,"%d",Netgame.respawn);
6852 Multi_ho_respawns.set_text(resp_str);
6855 // set the mission time limit
6856 memset(resp_str,0,10);
6857 float tl = f2fl(Netgame.options.mission_time_limit);
6858 sprintf(resp_str,"%d",(int)(tl / 60.0f));
6859 Multi_ho_time_limit.set_text(resp_str);
6861 // set the furball kill limit
6862 memset(resp_str,0,10);
6863 sprintf(resp_str,"%d",Netgame.options.kill_limit);
6864 Multi_ho_kill_limit.set_text(resp_str);
6866 // set the token wait time
6867 memset(resp_str,0,10);
6868 sprintf(resp_str,"%d",Netgame.options.voice_token_wait / 1000);
6869 Multi_ho_voice_wait.set_text(resp_str);
6871 // get whether we're in host/captains only modify mode
6872 if(Netgame.options.flags & MSO_FLAG_SS_LEADERS){
6873 Multi_ho_host_modifies = 1;
6875 Multi_ho_host_modifies = 0;
6879 void multi_ho_apply_options()
6881 // if the voice qos or duration has changed, apply the change
6882 multi_voice_maybe_update_vars(Netgame.options.voice_qos,Netgame.options.voice_record_time);
6884 // send an options update
6885 multi_options_update_netgame();
6888 // display the voice record time settings
6889 void multi_ho_display_record_time()
6892 int full_seconds, half_seconds;
6895 memset(time_str,0,30);
6898 full_seconds = (((Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 500) / 1000);
6900 // get the half-seconds
6901 half_seconds = ((((Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 500) % 1000) / 500) * 5;
6903 // format the string
6904 sprintf(time_str,"%d.%d",full_seconds,half_seconds);
6905 gr_set_color_fast(&Color_bright);
6906 gr_string(Ho_vd_coords[gr_screen.res][MULTI_HO_X_COORD],Ho_vd_coords[gr_screen.res][MULTI_HO_Y_COORD],time_str);
6909 int multi_ho_check_values()
6913 memset(val_txt,0,255);
6915 // check against respawn settings
6916 if(Multi_ho_mission_respawn != -1){
6917 Multi_ho_respawns.get_text(val_txt);
6918 // if the value is invalid, let the user know
6919 if(atoi(val_txt) > Multi_ho_mission_respawn){
6920 memset(val_txt,0,255);
6921 sprintf(val_txt,XSTR("Warning\nRespawn count in greater than mission specified max (%d)",796),Multi_ho_mission_respawn);
6922 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6927 // check against mission time limit max
6928 Multi_ho_time_limit.get_text(val_txt);
6929 // if the value is invalid, force it to be valid
6930 if(atoi(val_txt) > MULTI_HO_MAX_TIME_LIMIT){
6931 memset(val_txt,0,255);
6932 sprintf(val_txt,XSTR("Warning\nMission time limit is greater than max allowed (%d)",797),MULTI_HO_MAX_TIME_LIMIT);
6933 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6937 // check against max observer limit
6938 Multi_ho_obs.get_text(val_txt);
6939 // if the value is invalid, force it to be valid
6940 if(atoi(val_txt) > MULTI_HO_MAX_OBS){
6941 memset(val_txt,0,255);
6942 sprintf(val_txt,XSTR("Warning\nObserver count is greater than max allowed (%d)",798),MULTI_HO_MAX_OBS);
6943 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6947 // check against furball kill limit
6948 Multi_ho_kill_limit.get_text(val_txt);
6949 // if the value is invalid, force it to be valid
6950 if(atoi(val_txt) > MULTI_HO_MAX_KILL_LIMIT){
6951 memset(val_txt,0,255);
6952 sprintf(val_txt,XSTR("Warning\nMission kill limit is greater than max allowed (%d)",799),MULTI_HO_MAX_KILL_LIMIT);
6953 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6957 // check against the token wait limit
6958 Multi_ho_voice_wait.get_text(val_txt);
6959 if(atoi(val_txt) > MULTI_HO_MAX_TOKEN_WAIT){
6960 memset(val_txt,0,255);
6961 sprintf(val_txt,XSTR("Warning\nvoice wait time is greater than max allowed (%d)",800),MULTI_HO_MAX_TOKEN_WAIT);
6962 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6966 // all values are valid
6970 void multi_ho_check_focus()
6972 // if an inputbox has been pressed (hit enter), lose its focus
6973 if (Multi_ho_respawns.pressed() || Multi_ho_time_limit.pressed() || Multi_ho_voice_wait.pressed() || Multi_ho_kill_limit.pressed() || Multi_ho_obs.pressed()) {
6974 Multi_ho_respawns.clear_focus();
6975 Multi_ho_time_limit.clear_focus();
6976 Multi_ho_voice_wait.clear_focus();
6977 Multi_ho_kill_limit.clear_focus();
6978 Multi_ho_obs.clear_focus();
6979 gamesnd_play_iface(SND_COMMIT_PRESSED);
6980 chatbox_set_focus();
6981 Multi_ho_lastframe_input = 0;
6983 } else if(!Multi_ho_lastframe_input) {
6984 // if we didn't have focus last frame
6985 if(Multi_ho_respawns.has_focus() || Multi_ho_time_limit.has_focus() || Multi_ho_kill_limit.has_focus() || Multi_ho_voice_wait.has_focus() ){
6986 chatbox_lose_focus();
6988 Multi_ho_lastframe_input = 1;
6991 // if we _did_ have focus last frame
6993 // if we no longer have focus on any of the input boxes, set the focus on the chatbox
6994 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()){
6995 chatbox_set_focus();
6997 // if the chatbox now has focus, clear all focus from our inputboxes
6998 else if (chatbox_has_focus()) {
6999 Multi_ho_respawns.clear_focus();
7000 Multi_ho_time_limit.clear_focus();
7001 Multi_ho_kill_limit.clear_focus();
7002 Multi_ho_voice_wait.clear_focus();
7004 Multi_ho_lastframe_input = 0;
7009 void multi_ho_blit_max_respawns()
7013 // if we're in campaign mode, do nothing
7014 if(Netgame.campaign_mode == MP_CAMPAIGN){
7018 // otherwise blit the max as specified by the current mission file
7019 sprintf(string,"(%d)",Multi_ho_mission_respawn);
7020 gr_set_color_fast(&Color_normal);
7021 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);
7024 void multi_ho_display_skill_level()
7026 int skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
7029 Assert((skill_level >= 0) && (skill_level < NUM_SKILL_LEVELS));
7030 if((skill_level < 0) || (skill_level >= NUM_SKILL_LEVELS)){
7034 gr_set_color_fast(&Color_bright);
7035 gr_string(Ho_st_coords[gr_screen.res][0], Ho_st_coords[gr_screen.res][1], Skill_level_names(skill_level, 1));
7038 // -------------------------------------------------------------------------------------------------------------
7040 // MULTIPLAYER JOIN SCREEN
7043 #define MULTI_JW_NUM_BUTTONS 8
7047 #define MULTI_JW_PALETTE "InterfacePalette"
7049 static char *Multi_jw_bitmap_fname[GR_NUM_RESOLUTIONS] = {
7050 "MultiJoinWait", // GR_640
7051 "2_MultiJoinWait" // GR_1024
7054 static char *Multi_jw_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
7055 "MultiJoinWait-M", // GR_640
7056 "2_MultiJoinWait-M" // GR_1024
7062 #define MJW_SCROLL_PLAYERS_UP 0
7063 #define MJW_SCROLL_PLAYERS_DOWN 1
7066 #define MJW_PILOT_INFO 4
7067 #define MJW_SCROLL_INFO_UP 5
7068 #define MJW_SCROLL_INFO_DOWN 6
7069 #define MJW_CANCEL 7
7071 UI_WINDOW Multi_jw_window; // the window object for the join screen
7072 int Multi_jw_bitmap; // the background bitmap
7074 // constants for coordinate lookup
7075 #define MJW_X_COORD 0
7076 #define MJW_Y_COORD 1
7077 #define MJW_W_COORD 2
7078 #define MJW_H_COORD 3
7080 ui_button_info Multi_jw_buttons[GR_NUM_RESOLUTIONS][MULTI_JW_NUM_BUTTONS] = {
7082 ui_button_info("MJW_00", 1, 24, -1, -1, 0),
7083 ui_button_info("MJW_01", 1, 66, -1, -1, 1),
7084 ui_button_info("MJW_02", 30, 244, 20, 272, 2),
7085 ui_button_info("MJW_03", 84, 244, 73, 272, 3),
7086 ui_button_info("MJW_04", 139, 242, 134, 272, 4),
7087 ui_button_info("MJW_05", 1, 406, -1, -1, 5),
7088 ui_button_info("MJW_06", 1, 447, -1, -1, 6),
7089 ui_button_info("MJW_07", 577, 428, 570, 414, 7),
7092 ui_button_info("2_MJW_00", 2, 38, -1, -1, 0),
7093 ui_button_info("2_MJW_01", 2, 106, -1, -1, 1),
7094 ui_button_info("2_MJW_02", 48, 390, 47, 435, 2),
7095 ui_button_info("2_MJW_03", 134, 390, 133, 435, 3),
7096 ui_button_info("2_MJW_04", 223, 388, 225, 435, 4),
7097 ui_button_info("2_MJW_05", 2, 649, -1, -1, 5),
7098 ui_button_info("2_MJW_06", 2, 715, -1, -1, 6),
7099 ui_button_info("2_MJW_07", 923, 685, 931, 667, 7),
7104 #define MULTI_JW_NUM_TEXT 7
7106 UI_XSTR Multi_jw_text[GR_NUM_RESOLUTIONS][MULTI_JW_NUM_TEXT] = {
7108 { "Team 1", 1308, 20, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_TEAM0].button },
7109 { "Team 2", 1309, 73, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_TEAM1].button },
7110 { "Pilot", 1310, 134, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_PILOT_INFO].button },
7111 { "Info", 1311, 134, 283, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_PILOT_INFO].button },
7112 { "Cancel", 387, 570, 414, UI_XSTR_COLOR_PINK, -1, &Multi_jw_buttons[0][MJW_CANCEL].button },
7113 { "Players", 1269, 38, 8, UI_XSTR_COLOR_GREEN, -1, NULL },
7114 { "Choose Team", 1312, 27, 231, UI_XSTR_COLOR_GREEN, -1, NULL },
7117 { "Team 1", 1308, 47, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_TEAM0].button },
7118 { "Team 2", 1309, 133, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_TEAM1].button },
7119 { "Pilot", 1310, 225, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_PILOT_INFO].button },
7120 { "Info", 1311, 225, 446, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_PILOT_INFO].button },
7121 { "Cancel", 387, 931, 667, UI_XSTR_COLOR_PINK, -1, &Multi_jw_buttons[1][MJW_CANCEL].button },
7122 { "Players", 1269, 165, 12, UI_XSTR_COLOR_GREEN, -1, NULL },
7123 { "Choose Team", 1312, 45, 373, UI_XSTR_COLOR_GREEN, -1, NULL },
7128 int Mjw_players_coords[GR_NUM_RESOLUTIONS][4] = {
7137 int Mjw_mission_name_coords[GR_NUM_RESOLUTIONS][2] = {
7146 // squad war checkbox
7147 UI_CHECKBOX Multi_jw_sw_checkbox;
7148 char *Multi_jw_sw_checkbox_fname[GR_NUM_RESOLUTIONS] = {
7152 int Multi_jw_sw_checkbox_coords[GR_NUM_RESOLUTIONS][2] = {
7160 int Multi_jw_sw_checkbox_text[GR_NUM_RESOLUTIONS][2] = {
7170 // player list control thingie defs
7171 #define MULTI_JW_PLIST_MAX_DISPLAY 19
7172 int Multi_jw_plist_select_flag; // indicates whether we currently have a selected player
7173 short Multi_jw_plist_select_id; // id of the current selected player
7174 UI_BUTTON Multi_jw_plist_select_button; // for selecting a player
7176 int Multi_jw_should_show_popup = 0;
7178 // LOCAL function definitions
7179 void multi_jw_check_buttons();
7180 void multi_jw_button_pressed(int n);
7181 void multi_jw_do_netstuff();
7182 void multi_jw_scroll_players_up();
7183 void multi_jw_scroll_players_down();
7184 void multi_jw_plist_process();
7185 void multi_jw_plist_blit_normal();
7186 void multi_jw_plist_blit_team();
7187 short multi_jw_get_mouse_id();
7189 void multi_game_client_setup_init()
7193 // create the interface window
7194 Multi_jw_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
7195 Multi_jw_window.set_mask_bmap(Multi_jw_bitmap_mask_fname[gr_screen.res]);
7197 // load the background bitmap
7198 Multi_jw_bitmap = bm_load(Multi_jw_bitmap_fname[gr_screen.res]);
7199 if(Multi_jw_bitmap < 0){
7200 // we failed to load the bitmap - this is very bad
7204 // initialize the player list data
7205 Multi_jw_plist_select_flag = 0;
7206 Multi_jw_plist_select_id = -1;
7208 // kill any old instances of the chatbox and create a new one
7210 chatbox_create(CHATBOX_FLAG_BIG | CHATBOX_FLAG_DRAW_BOX | CHATBOX_FLAG_BUTTONS);
7212 // initialize the common notification messaging
7213 multi_common_notify_init();
7215 // initialize the common mission info display area.
7216 multi_common_set_text("");
7218 // use the common interface palette
7219 multi_common_set_palette();
7221 // create the interface buttons
7222 for(idx=0; idx<MULTI_JW_NUM_BUTTONS; idx++){
7223 // create the object
7224 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);
7226 // set the sound to play when highlighted
7227 Multi_jw_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
7229 // set the ani for the button
7230 Multi_jw_buttons[gr_screen.res][idx].button.set_bmaps(Multi_jw_buttons[gr_screen.res][idx].filename);
7233 Multi_jw_buttons[gr_screen.res][idx].button.link_hotspot(Multi_jw_buttons[gr_screen.res][idx].hotspot);
7236 // if this is a PXO game, enable the squadwar checkbox
7237 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);
7238 Multi_jw_sw_checkbox.set_bmaps(Multi_jw_sw_checkbox_fname[gr_screen.res], 6, 0);
7239 Multi_jw_sw_checkbox.disable();
7240 if(!MULTI_IS_TRACKER_GAME){
7241 Multi_jw_sw_checkbox.hide();
7246 for(idx=0; idx<MULTI_JW_NUM_TEXT; idx++){
7247 Multi_jw_window.add_XSTR(&Multi_jw_text[gr_screen.res][idx]);
7251 // create the player select list button and hide it
7252 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);
7253 Multi_jw_plist_select_button.hide();
7256 Multi_jw_buttons[gr_screen.res][MJW_CANCEL].button.set_hotkey(KEY_ESC);
7258 // remove campaign flags
7259 Game_mode &= ~(GM_CAMPAIGN_MODE);
7261 // tell the server we have finished joining
7262 Net_player->state = NETPLAYER_STATE_JOINED;
7263 send_netplayer_update_packet();
7266 ml_printf(NOX("Joined netgame %s"), Netgame.name);
7268 // send any appropriate files
7269 multi_data_send_my_junk();
7272 void multi_game_client_setup_do_frame()
7275 int k = chatbox_process();
7276 char mission_text[255];
7277 k = Multi_jw_window.process(k,0);
7279 Multi_jw_should_show_popup = 0;
7281 // process any button clicks
7282 multi_jw_check_buttons();
7284 // do any network related stuff
7285 multi_jw_do_netstuff();
7287 // draw the background, etc
7289 GR_MAYBE_CLEAR_RES(Multi_jw_bitmap);
7290 if(Multi_jw_bitmap != -1){
7291 gr_set_bitmap(Multi_jw_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7295 // if we're not in team vs. team mode, don't draw the team buttons
7296 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
7297 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.hide();
7298 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.hide();
7299 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.disable();
7300 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.disable();
7302 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.enable();
7303 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.enable();
7304 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.unhide();
7305 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.unhide();
7308 if(MULTI_IS_TRACKER_GAME){
7309 // maybe check the squadwar button
7310 if(Netgame.type_flags & NG_TYPE_SW){
7311 Multi_jw_sw_checkbox.set_state(1);
7312 gr_set_color_fast(&Color_bright);
7314 Multi_jw_sw_checkbox.set_state(0);
7315 gr_set_color_fast(&Color_normal);
7318 gr_string(Multi_jw_sw_checkbox_text[gr_screen.res][0], Multi_jw_sw_checkbox_text[gr_screen.res][1], "SquadWar");
7321 // draw the UI window
7322 Multi_jw_window.draw();
7324 // process and display the player list
7325 // NOTE : this must be done before the buttons are checked to insure that a player hasn't dropped
7326 multi_jw_plist_process();
7327 if(Netgame.type_flags & NG_TYPE_TEAM){
7328 multi_jw_plist_blit_team();
7330 multi_jw_plist_blit_normal();
7333 // display any text in the info area
7334 multi_common_render_text();
7336 // display any pending notification messages
7337 multi_common_notify_do();
7339 // blit the mission filename if possible
7340 if(Netgame.campaign_mode){
7341 if(strlen(Netgame.campaign_name) > 0){
7342 strcpy(mission_text,Netgame.campaign_name);
7344 if(strlen(Netgame.title) > 0){
7345 strcat(mission_text,", ");
7346 strcat(mission_text,Netgame.title);
7349 gr_set_color_fast(&Color_bright_white);
7350 gr_string(Mjw_mission_name_coords[gr_screen.res][MJW_X_COORD],Mjw_mission_name_coords[gr_screen.res][MJW_Y_COORD],mission_text);
7353 if(strlen(Netgame.mission_name) > 0){
7354 strcpy(mission_text,Netgame.mission_name);
7356 if(strlen(Netgame.title) > 0){
7357 strcat(mission_text,", ");
7358 strcat(mission_text,Netgame.title);
7361 gr_set_color_fast(&Color_bright_white);
7362 gr_string(Mjw_mission_name_coords[gr_screen.res][MJW_X_COORD],Mjw_mission_name_coords[gr_screen.res][MJW_Y_COORD],mission_text);
7366 // process and show the chatbox thingie
7370 Multi_jw_window.draw_tooltip();
7372 // display the voice status indicator
7373 multi_common_voice_display_status();
7378 // if we're supposed to be displaying a pilot info popup
7379 if(Multi_jw_should_show_popup){
7380 player_index = find_player_id(Multi_jw_plist_select_id);
7381 if(player_index != -1){
7382 multi_pinfo_popup(&Net_players[player_index]);
7387 void multi_game_client_setup_close()
7389 // unload any bitmaps
7390 if(!bm_unload(Multi_jw_bitmap)){
7391 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_jw_bitmap_fname[gr_screen.res]));
7394 // destroy the chatbox
7397 // destroy the UI_WINDOW
7398 Multi_jw_window.destroy();
7401 if(Netgame.game_state == NETGAME_STATE_MISSION_SYNC){
7402 gamesnd_play_iface(SND_COMMIT_PRESSED);
7407 void multi_jw_check_buttons()
7410 for(idx=0;idx<MULTI_JW_NUM_BUTTONS;idx++){
7411 // we only really need to check for one button pressed at a time, so we can break after
7413 if(Multi_jw_buttons[gr_screen.res][idx].button.pressed()){
7414 multi_jw_button_pressed(idx);
7420 void multi_jw_button_pressed(int n)
7424 gamesnd_play_iface(SND_USER_SELECT);
7425 multi_quit_game(PROMPT_CLIENT);
7427 case MJW_SCROLL_PLAYERS_UP:
7428 multi_jw_scroll_players_up();
7430 case MJW_SCROLL_PLAYERS_DOWN:
7431 multi_jw_scroll_players_down();
7433 case MJW_SCROLL_INFO_UP:
7434 multi_common_scroll_text_up();
7436 case MJW_SCROLL_INFO_DOWN:
7437 multi_common_scroll_text_down();
7440 // request to set myself to team 0
7442 gamesnd_play_iface(SND_USER_SELECT);
7443 multi_team_set_team(Net_player,0);
7446 // request to set myself to team 1
7448 gamesnd_play_iface(SND_USER_SELECT);
7449 multi_team_set_team(Net_player,1);
7453 case MJW_PILOT_INFO:
7454 Multi_jw_should_show_popup = 1;
7458 multi_common_add_notify(XSTR("Not implemented yet!",760));
7463 // do stuff like pinging servers, sending out requests, etc
7464 void multi_jw_do_netstuff()
7468 void multi_jw_scroll_players_up()
7470 gamesnd_play_iface(SND_GENERAL_FAIL);
7473 // scroll down through the player list
7474 void multi_jw_scroll_players_down()
7476 gamesnd_play_iface(SND_GENERAL_FAIL);
7479 void multi_jw_plist_process()
7481 int test_count,player_index,idx;
7483 // first determine if there are 0 players in the game. This should never happen since the host is _always_ in the game
7485 for(idx=0;idx<MAX_PLAYERS;idx++){
7486 // count anyone except the standalone server (if applicable)
7487 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7491 if(test_count <= 0){
7495 // if we had a selected item but that player has left, select myself instead
7496 if(Multi_jw_plist_select_flag){
7497 player_index = find_player_id(Multi_jw_plist_select_id);
7498 if(player_index == -1){
7499 Multi_jw_plist_select_id = Net_player->player_id;
7502 Multi_jw_plist_select_flag = 1;
7503 Multi_jw_plist_select_id = Net_player->player_id;
7506 // if the player has clicked somewhere in the player list area
7507 if(Multi_jw_plist_select_button.pressed()){
7511 player_id = multi_jw_get_mouse_id();
7512 player_index = find_player_id(player_id);
7513 if(player_index != -1){
7514 Multi_jw_plist_select_id = player_id;
7515 Multi_jw_plist_select_flag = 1;
7520 void multi_jw_plist_blit_normal()
7523 char str[CALLSIGN_LEN+1];
7524 int y_start = Mjw_players_coords[gr_screen.res][MJW_Y_COORD];
7527 // display all the players
7528 for(idx=0;idx<MAX_PLAYERS;idx++){
7529 // reset total offset
7532 // count anyone except the standalone server (if applicable)
7533 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7534 // highlight him if he's the host
7535 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7536 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7537 gr_set_color_fast(&Color_text_active_hi);
7539 gr_set_color_fast(&Color_bright);
7542 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7543 gr_set_color_fast(&Color_text_active);
7545 gr_set_color_fast(&Color_text_normal);
7549 // optionally draw his CD status
7550 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7551 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7552 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7554 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7557 // make sure the string will fit, then display it
7558 strcpy(str,Net_players[idx].player->callsign);
7559 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
7562 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7563 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7570 void multi_jw_plist_blit_team()
7573 char str[CALLSIGN_LEN+1];
7574 int y_start = Mjw_players_coords[gr_screen.res][MJW_Y_COORD];
7577 // always blit the proper team button based on _my_ team status
7578 if(Net_player->p_info.team == 0){
7579 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.draw_forced(2);
7581 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.draw_forced(2);
7584 // display all the red players first
7585 for(idx=0;idx<MAX_PLAYERS;idx++){
7586 // reset total offset
7589 // count anyone except the standalone server (if applicable)
7590 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
7591 // highlight him if he's the host
7592 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7593 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7594 gr_set_color_fast(&Color_text_active_hi);
7596 gr_set_color_fast(&Color_bright);
7599 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7600 gr_set_color_fast(&Color_text_active);
7602 gr_set_color_fast(&Color_text_normal);
7606 // optionally draw his CD status
7607 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7608 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7609 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7611 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7614 // blit the red team indicator
7615 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
7616 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
7617 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7618 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7620 total_offset += Multi_common_icon_dims[MICON_TEAM0_SELECT][0] + 1;
7623 if(Multi_common_icons[MICON_TEAM0] != -1){
7624 gr_set_bitmap(Multi_common_icons[MICON_TEAM0], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7625 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7627 total_offset += Multi_common_icon_dims[MICON_TEAM0][0] + 1;
7631 // make sure the string will fit
7632 strcpy(str,Net_players[idx].player->callsign);
7633 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7635 // display him in the correct half of the list depending on his team
7636 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7641 // display all the green players next
7642 for(idx=0;idx<MAX_PLAYERS;idx++){
7643 // reset total offset
7646 // count anyone except the standalone server (if applicable)
7647 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
7648 // highlight him if he's the host
7649 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7650 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7651 gr_set_color_fast(&Color_text_active_hi);
7653 gr_set_color_fast(&Color_bright);
7656 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7657 gr_set_color_fast(&Color_text_active);
7659 gr_set_color_fast(&Color_text_normal);
7663 // optionally draw his CD status
7664 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7665 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7666 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7668 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7671 // blit the red team indicator
7672 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
7673 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
7674 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7675 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7677 total_offset += Multi_common_icon_dims[MICON_TEAM1_SELECT][0] + 1;
7680 if(Multi_common_icons[MICON_TEAM1] != -1){
7681 gr_set_bitmap(Multi_common_icons[MICON_TEAM1], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7682 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7684 total_offset += Multi_common_icon_dims[MICON_TEAM1][0] + 1;
7688 // make sure the string will fit
7689 strcpy(str,Net_players[idx].player->callsign);
7690 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
7693 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7695 // display him in the correct half of the list depending on his team
7696 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7702 void multi_jw_handle_join(net_player *pl)
7704 // for now just play a bloop sound
7705 gamesnd_play_iface(SND_ICON_DROP_ON_WING);
7708 short multi_jw_get_mouse_id()
7710 // determine where he clicked (y pixel value)
7712 Multi_jw_plist_select_button.get_mouse_pos(NULL,&y);
7714 // select things a little differently if we're in team vs. team or non-team vs. team mode
7716 if(Netgame.type_flags & NG_TYPE_TEAM){
7717 int player_index = -1;
7719 // look through all of team red first
7720 for(idx=0;idx<MAX_PLAYERS;idx++){
7721 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
7724 // if this is the _nth_ guy
7732 // if we still haven't found him yet, look through the green team
7733 if(player_index == -1){
7734 for(idx=0;idx<MAX_PLAYERS;idx++){
7735 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
7737 // if this is the _nth_ guy
7745 if(player_index != -1){
7746 return Net_players[idx].player_id;
7749 // select the nth active player if possible, disregarding the standalone server
7750 for(idx=0;idx<MAX_PLAYERS;idx++){
7751 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7754 // if this is the _nth_ guy
7756 return Net_players[idx].player_id;
7767 // -------------------------------------------------------------------------------------------------------------
7769 // MULTIPLAYER WAIT/SYNCH SCREEN
7772 #define MULTI_SYNC_HOST_COUNT 4 // host uses 4 buttons (and sometimes 5)
7773 #define MULTI_SYNC_CLIENT_COUNT 3 // client only uses 3 buttons
7775 char *Multi_sync_bitmap_fname[GR_NUM_RESOLUTIONS] = {
7776 "MultiSynch", // GR_640
7777 "2_MultiSynch" // GR_1024
7780 char *Multi_sync_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
7781 "MultiSynch-M", // GR_640
7782 "2_MultiSynch-M" // GR_1024
7787 // constants for coordinate lookup
7788 #define MS_X_COORD 0
7789 #define MS_Y_COORD 1
7790 #define MS_W_COORD 2
7791 #define MS_H_COORD 3
7793 UI_WINDOW Multi_sync_window; // the window object for the join screen
7794 int Multi_sync_button_count; // the # of buttons to use for this instance of mission sync
7795 int Multi_sync_bitmap; // the background bitmap
7798 #define MULTI_SYNC_NUM_BUTTONS 5
7799 #define MS_SCROLL_INFO_UP 0
7800 #define MS_SCROLL_INFO_DOWN 1
7804 ui_button_info Multi_sync_buttons[GR_NUM_RESOLUTIONS][MULTI_SYNC_NUM_BUTTONS] = {
7807 ui_button_info("MS_00", 0, 396, -1, -1, 0),
7808 ui_button_info("MS_01", 0, 435, -1, -1, 1),
7809 ui_button_info("MS_02", 496, 411, -1, -1, 2),
7810 ui_button_info("MS_04", 436, 403, -1, -1, 4),
7811 ui_button_info("MS_03", 556, 398, -1, -1, 3),
7813 ui_button_info("MS_00", 1, 404, -1, -1, 0),
7814 ui_button_info("MS_01", 1, 446, -1, -1, 1),
7815 ui_button_info("MS_03", 518, 426, 519, 416, 3),
7816 ui_button_info("MS_02", 469, 426, 479, 416, 2),
7817 ui_button_info("MS_04", 571, 420, 577, 416, 4),
7821 ui_button_info("2_MS_00", 2, 647, -1, -1, 0),
7822 ui_button_info("2_MS_01", 2, 713, -1, -1, 1),
7823 ui_button_info("2_MS_03", 829, 682, 831, 667, 3),
7824 ui_button_info("2_MS_02", 751, 682, 766, 667, 2),
7825 ui_button_info("2_MS_04", 914, 672, 924, 667, 4),
7831 #define MULTI_SYNC_NUM_TEXT 5
7833 #define MST_LAUNCH 2
7834 UI_XSTR Multi_sync_text[GR_NUM_RESOLUTIONS][MULTI_SYNC_NUM_TEXT] = {
7836 { "Kick", 1266, 479, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_KICK].button },
7837 { "Cancel", 387, 519, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_CANCEL].button },
7838 { "Launch", 801, 577, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_LAUNCH].button },
7839 { "Players", 1269, 23, 133, UI_XSTR_COLOR_GREEN, -1, NULL },
7840 { "Status", 1304, 228, 133, UI_XSTR_COLOR_GREEN, -1, NULL }
7843 { "Kick", 1266, 766, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_KICK].button },
7844 { "Cancel", 387, 831, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_CANCEL].button },
7845 { "Launch", 801, 924, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_LAUNCH].button },
7846 { "Players", 1269, 38, 214, UI_XSTR_COLOR_GREEN, -1, NULL },
7847 { "Status", 1304, 366, 214, UI_XSTR_COLOR_GREEN, -1, NULL }
7853 int Ms_status_coords[GR_NUM_RESOLUTIONS][4] = {
7862 // player status coords
7863 int Ms_status2_coords[GR_NUM_RESOLUTIONS][4] = {
7872 int Ms_cd_icon_offset[GR_NUM_RESOLUTIONS] = {
7877 int Ms_team_icon_offset[GR_NUM_RESOLUTIONS] = {
7882 // player currently selected, index into Net_players[]
7883 int Multi_sync_player_select = -1;
7885 // player list control thingie defs
7886 #define MULTI_SYNC_PLIST_MAX_DISPLAY 15
7887 int Multi_sync_plist_start; // where to start displaying from
7888 int Multi_sync_plist_count; // how many we have
7890 // list select button
7891 UI_BUTTON Multi_sync_plist_button;
7893 int Multi_sync_mode = -1;
7895 #define MULTI_SYNC_COUNTDOWN_TIME 5 // in seconds
7896 float Multi_sync_countdown_timer;
7897 int Multi_sync_countdown = -1;
7899 int Multi_launch_button_created;
7902 // countdown animation timer
7903 char* Multi_sync_countdown_fname[GR_NUM_RESOLUTIONS] = {
7905 "2_Count" // GR_1024
7908 int Multi_sync_countdown_coords[GR_NUM_RESOLUTIONS][2] = {
7919 anim *Multi_sync_countdown_anim = NULL;
7920 anim_instance *Multi_sync_countdown_instance = NULL;
7923 // PREBRIEFING STUFF
7924 // syncing flags used by the server
7925 int Mission_sync_flags = 0;
7926 #define MS_FLAG_SENT_FILESIG (1<<0) // sent filesig requests
7927 #define MS_FLAG_SENT_LOAD (1<<1) // sent load packets
7928 #define MS_FLAG_PUSHED_BRIEFING (1<<2) // pushed everyone else into the briefing
7929 #define MS_FLAG_POST_DATA (1<<3) // sent the post data block
7930 #define MS_FLAG_WSS_SLOTS (1<<4) // all players have received wss slot data
7931 #define MS_FLAG_PSETTINGS (1<<5) // send the player settings packet
7932 #define MS_FLAG_MT_STATS_START (1<<6) // server has started getting player stats from the tracker
7933 #define MS_FLAG_MT_STATS_DONE (1<<7) // server has finished getting player stats from the tracker (success or fail)
7934 #define MS_FLAG_TS_SLOTS (1<<8) // team/ship slots have been sent
7935 #define MS_FLAG_DATA_DONE (1<<9) // done transferring all necessary data
7936 #define MS_FLAG_CAMP_DONE (1<<10) // send campaign pool/goal/event stuff
7938 // POSTBRIEFING STUFF
7939 int Multi_state_timestamp;
7940 int Multi_sync_launch_pressed;
7942 // LOCAL function definitions
7943 void multi_sync_check_buttons();
7944 void multi_sync_button_pressed(int n);
7945 void multi_sync_scroll_info_up();
7946 void multi_sync_scroll_info_down();
7947 void multi_sync_display_name(char *name,int index,int np_index); // display info on the left hand portion of the status window thingie
7948 void multi_sync_display_status(char *status,int index); // display info on the right hand portion of the status window thingie
7949 void multi_sync_force_start_pre();
7950 void multi_sync_force_start_post();
7951 void multi_sync_launch();
7952 void multi_sync_create_launch_button();
7953 void multi_sync_blit_screen_all();
7954 void multi_sync_handle_plist();
7956 void multi_sync_common_init();
7957 void multi_sync_common_do();
7958 void multi_sync_common_close();
7960 void multi_sync_pre_init();
7961 void multi_sync_pre_do();
7962 void multi_sync_pre_close();
7964 void multi_sync_post_init();
7965 void multi_sync_post_do();
7966 void multi_sync_post_close();
7971 // perform the correct init functions
7972 void multi_sync_init()
7974 Multi_sync_countdown = -1;
7978 // reset all timestamp
7979 multi_reset_timestamps();
7981 extern int Player_multi_died_check;
7982 Player_multi_died_check = -1;
7984 if(!(Game_mode & GM_STANDALONE_SERVER)){
7985 multi_sync_common_init();
7988 switch(Multi_sync_mode){
7989 case MULTI_SYNC_PRE_BRIEFING:
7990 multi_sync_pre_init();
7992 case MULTI_SYNC_POST_BRIEFING:
7993 multi_sync_post_init();
7995 case MULTI_SYNC_INGAME:
7996 multi_ingame_sync_init();
8001 // perform the correct do frame functions
8002 void multi_sync_do()
8004 if(!(Game_mode & GM_STANDALONE_SERVER)){
8005 multi_sync_common_do();
8008 // if the netgame is ending, don't do any sync processing
8009 if(multi_endgame_ending()){
8013 // process appropriateliy
8014 switch(Multi_sync_mode){
8015 case MULTI_SYNC_PRE_BRIEFING:
8016 multi_sync_pre_do();
8018 case MULTI_SYNC_POST_BRIEFING:
8019 multi_sync_post_do();
8021 case MULTI_SYNC_INGAME:
8022 multi_ingame_sync_do();
8025 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8026 if(Multi_sync_bitmap != -1){
8027 gr_set_bitmap(Multi_sync_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8030 Multi_sync_window.draw();
8032 multi_sync_blit_screen_all();
8039 // perform the correct close functions
8040 void multi_sync_close()
8042 switch(Multi_sync_mode){
8043 case MULTI_SYNC_PRE_BRIEFING:
8044 multi_sync_pre_close();
8046 case MULTI_SYNC_POST_BRIEFING:
8047 multi_sync_post_close();
8049 case MULTI_SYNC_INGAME:
8050 multi_ingame_sync_close();
8054 if(!(Game_mode & GM_STANDALONE_SERVER)){
8055 multi_sync_common_close();
8059 char *multi_sync_tooltip_handler(char *str)
8061 if (!stricmp(str, NOX("@launch"))) {
8062 if (Multi_launch_button_created){
8063 return XSTR("Launch",801);
8070 void multi_sync_common_init()
8074 // create the interface window
8075 Multi_sync_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
8076 Multi_sync_window.set_mask_bmap(Multi_sync_bitmap_mask_fname[gr_screen.res]);
8077 Multi_sync_window.tooltip_handler = multi_sync_tooltip_handler;
8079 // load the background bitmap
8080 Multi_sync_bitmap = bm_load(Multi_sync_bitmap_fname[gr_screen.res]);
8081 if (Multi_sync_bitmap < 0) {
8082 // we failed to load the bitmap - this is very bad
8086 // initialize the player list data
8087 Multi_sync_plist_start = 0;
8088 Multi_sync_plist_count = 1; // we can pretty safely assume that there's one player in the game - me.
8090 Multi_launch_button_created = 0;
8092 // create the chatbox thingie (shouldn't be necesary to do this, but we'll put it in for good measure)
8095 // force the chatbox to be small
8096 chatbox_force_small();
8098 // initialize the common notification messaging
8099 multi_common_notify_init();
8101 // initialize the common mission info display area.
8102 multi_common_set_text("");
8104 // use the common interface palette
8105 multi_common_set_palette();
8107 // don't select any player yet.
8108 Multi_sync_player_select = -1;
8110 // determine how many of the 5 buttons to create
8111 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8112 Multi_sync_button_count = MULTI_SYNC_HOST_COUNT;
8114 Multi_sync_button_count = MULTI_SYNC_CLIENT_COUNT;
8116 // create the interface buttons
8117 for(idx=0; idx<Multi_sync_button_count; idx++){
8118 // create the object
8119 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);
8121 // set the sound to play when highlighted
8122 Multi_sync_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
8124 // set the ani for the button
8125 // this wierdness is necessary because cancel and kick buttons aren't drawn on the background bitmap,
8126 // so we have to load in frame 0, too (the file should exist)
8127 if ((idx == MS_CANCEL) || (idx == MS_KICK) || (idx == MS_LAUNCH)) {
8128 Multi_sync_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sync_buttons[gr_screen.res][idx].filename, 3, 0);
8130 Multi_sync_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sync_buttons[gr_screen.res][idx].filename);
8134 Multi_sync_buttons[gr_screen.res][idx].button.link_hotspot(Multi_sync_buttons[gr_screen.res][idx].hotspot);
8139 for(idx=0; idx<MULTI_SYNC_NUM_TEXT; idx++) {
8140 // don't create the "launch" button text just yet
8141 if(idx == MST_LAUNCH) {
8144 // multiplayer clients should ignore the kick button
8145 if(!MULTIPLAYER_MASTER && !MULTIPLAYER_HOST && (idx == MST_KICK)) {
8149 Multi_sync_window.add_XSTR(&Multi_sync_text[gr_screen.res][idx]);
8153 // create the player list select button and hide it
8154 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);
8155 Multi_sync_plist_button.hide();
8157 // set up hotkeys for certain common functions
8158 Multi_sync_buttons[gr_screen.res][MS_CANCEL].button.set_hotkey(KEY_ESC);
8161 void multi_sync_common_do()
8163 int k = chatbox_process();
8164 k = Multi_sync_window.process(k);
8166 // process the player list
8167 multi_sync_handle_plist();
8169 // process any button clicks
8170 multi_sync_check_buttons();
8172 // process any keypresses
8176 gamesnd_play_iface(SND_USER_SELECT);
8177 multi_quit_game(PROMPT_ALL);
8182 void multi_sync_common_close()
8184 // unload any bitmaps
8185 if(!bm_unload(Multi_sync_bitmap)){
8186 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_sync_bitmap_fname[gr_screen.res]));
8189 extern int Player_multi_died_check;
8190 Player_multi_died_check = -1;
8192 // destroy the UI_WINDOW
8193 Multi_sync_window.destroy();
8196 void multi_sync_blit_screen_all()
8203 // display any text in the info area
8204 multi_common_render_text();
8206 // display any pending notification messages
8207 multi_common_notify_do();
8209 // display any info about visible players
8211 for(idx=0;idx<MAX_PLAYERS;idx++){
8212 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
8213 // display his name and status
8214 multi_sync_display_name(Net_players[idx].player->callsign,count,idx);
8216 // get the player state
8217 state = Net_players[idx].state;
8219 // if we're ingame joining, show all other players except myself as "playing"
8220 if((Net_player != NULL) && (&Net_players[idx] != Net_player) && ((Multi_sync_mode == MULTI_SYNC_INGAME) || (Net_player->flags & NETINFO_FLAG_INGAME_JOIN)) ){
8221 state = NETPLAYER_STATE_IN_MISSION;
8225 case NETPLAYER_STATE_MISSION_LOADING:
8226 multi_sync_display_status(XSTR("Mission Loading",802),count);
8228 case NETPLAYER_STATE_INGAME_SHIP_SELECT: // I don't think its possible to see this state, but...
8229 multi_sync_display_status(XSTR("Ingame Ship Select",803),count);
8231 case NETPLAYER_STATE_DEBRIEF:
8232 multi_sync_display_status(XSTR("Debriefing",804),count);
8234 case NETPLAYER_STATE_MISSION_SYNC:
8235 multi_sync_display_status(XSTR("Mission Sync",805),count);
8237 case NETPLAYER_STATE_JOINING:
8238 multi_sync_display_status(XSTR("Joining",806),count);
8240 case NETPLAYER_STATE_JOINED:
8241 multi_sync_display_status(XSTR("Joined",807),count);
8243 case NETPLAYER_STATE_SLOT_ACK :
8244 multi_sync_display_status(XSTR("Slot Ack",808),count);
8246 case NETPLAYER_STATE_BRIEFING:
8247 multi_sync_display_status(XSTR("Briefing",765),count);
8249 case NETPLAYER_STATE_SHIP_SELECT:
8250 multi_sync_display_status(XSTR("Ship Select",809),count);
8252 case NETPLAYER_STATE_WEAPON_SELECT:
8253 multi_sync_display_status(XSTR("Weapon Select",810),count);
8255 case NETPLAYER_STATE_WAITING:
8256 multi_sync_display_status(XSTR("Waiting",811),count);
8258 case NETPLAYER_STATE_IN_MISSION:
8259 multi_sync_display_status(XSTR("In Mission",812),count);
8261 case NETPLAYER_STATE_MISSION_LOADED:
8262 multi_sync_display_status(XSTR("Mission Loaded",813),count);
8264 case NETPLAYER_STATE_DATA_LOAD:
8265 multi_sync_display_status(XSTR("Data loading",814),count);
8267 case NETPLAYER_STATE_SETTINGS_ACK:
8268 multi_sync_display_status(XSTR("Ready To Enter Mission",815),count);
8270 case NETPLAYER_STATE_INGAME_SHIPS:
8271 multi_sync_display_status(XSTR("Ingame Ships Packet Ack",816),count);
8273 case NETPLAYER_STATE_INGAME_WINGS:
8274 multi_sync_display_status(XSTR("Ingame Wings Packet Ack",817),count);
8276 case NETPLAYER_STATE_INGAME_RPTS:
8277 multi_sync_display_status(XSTR("Ingame Respawn Points Ack",818),count);
8279 case NETPLAYER_STATE_SLOTS_ACK:
8280 multi_sync_display_status(XSTR("Ingame Weapon Slots Ack",819),count);
8282 case NETPLAYER_STATE_POST_DATA_ACK:
8283 multi_sync_display_status(XSTR("Post Briefing Data Block Ack",820),count);
8285 case NETPLAYER_STATE_FLAG_ACK :
8286 multi_sync_display_status(XSTR("Flags Ack",821),count);
8288 case NETPLAYER_STATE_MT_STATS :
8289 multi_sync_display_status(XSTR("Parallax Online Stats Updating",822),count);
8291 case NETPLAYER_STATE_WSS_ACK :
8292 multi_sync_display_status(XSTR("Weapon Slots Ack",823),count);
8294 case NETPLAYER_STATE_HOST_SETUP :
8295 multi_sync_display_status(XSTR("Host setup",824),count);
8297 case NETPLAYER_STATE_DEBRIEF_ACCEPT:
8298 multi_sync_display_status(XSTR("Debrief accept",825),count);
8300 case NETPLAYER_STATE_DEBRIEF_REPLAY:
8301 multi_sync_display_status(XSTR("Debrief replay",826),count);
8303 case NETPLAYER_STATE_CPOOL_ACK:
8304 multi_sync_display_status(XSTR("Campaign ship/weapon ack",827),count);
8306 case NETPLAYER_STATE_MISSION_XFER :
8308 // server should display the pct completion of all clients
8309 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8310 if(Net_players[idx].s_info.xfer_handle != -1){
8311 pct_complete = multi_xfer_pct_complete(Net_players[idx].s_info.xfer_handle);
8313 // if we've got a valid xfer handle
8314 if((pct_complete >= 0.0) && (pct_complete <= 1.0)){
8315 sprintf(txt,XSTR("Mission file xfer %d%%",828),(int)(pct_complete * 100.0f));
8319 strcpy(txt,XSTR("Mission file xfer",829));
8322 strcpy(txt,XSTR("Mission file xfer",829));
8325 // clients should display only for themselves (which is the only thing they know)
8327 // if we've got a valid file xfer handle
8328 if((&Net_players[idx] == Net_player) && (Net_player->s_info.xfer_handle != -1)){
8329 pct_complete = multi_xfer_pct_complete(Net_player->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));
8342 strcpy(txt,XSTR("Mission file xfer",829));
8347 multi_sync_display_status(txt,count);
8350 nprintf(("Network","Unhandled player state : %d !\n",Net_players[idx].state));
8357 // display the mission start countdown timer (if any)
8358 anim_render_all(GS_STATE_MULTI_MISSION_SYNC,flFrametime);
8360 // process and show the chatbox thingie
8364 Multi_sync_window.draw_tooltip();
8366 // display the voice status indicator
8367 multi_common_voice_display_status();
8370 void multi_sync_check_buttons()
8373 for(idx=0;idx<Multi_sync_button_count;idx++){
8374 // we only really need to check for one button pressed at a time, so we can break after
8376 if(Multi_sync_buttons[gr_screen.res][idx].button.pressed()){
8377 multi_sync_button_pressed(idx);
8383 void multi_sync_button_pressed(int n)
8388 gamesnd_play_iface(SND_USER_SELECT);
8389 multi_quit_game(PROMPT_ALL);
8392 // scroll the info box up
8393 case MS_SCROLL_INFO_UP:
8394 multi_common_scroll_text_up();
8397 // scroll the info box down
8398 case MS_SCROLL_INFO_DOWN:
8399 multi_common_scroll_text_down();
8404 // if we have a currently selected player, kick him
8405 if(Multi_sync_player_select >= 0){
8406 multi_kick_player(Multi_sync_player_select);
8410 // start the final launch countdown (post-sync only)
8412 multi_sync_start_countdown();
8415 // doesn't do anything
8421 void multi_sync_pre_init()
8425 Netgame.game_state = NETGAME_STATE_MISSION_SYNC;
8427 // if we're in teamplay mode, always force skill level to be medium
8428 if((Netgame.type_flags & NG_TYPE_TEAM) && (Net_player->flags & NETINFO_FLAG_GAME_HOST)){
8429 Netgame.options.skill_level = NUM_SKILL_LEVELS / 2;
8430 Game_skill_level = NUM_SKILL_LEVELS / 2;
8431 multi_options_update_netgame();
8434 // notify everyone of when we get here
8435 if(!(Game_mode & GM_STANDALONE_SERVER)){
8436 Net_player->state = NETPLAYER_STATE_MISSION_SYNC;
8437 send_netplayer_update_packet();
8440 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8442 ml_string(NOX("Server performing pre-briefing data sync"));
8444 if(!(Game_mode & GM_STANDALONE_SERVER)){
8445 multi_common_set_text(XSTR("Server performing sync\n",830),1);
8448 // maybe initialize tvt and squad war stuff
8449 if(Netgame.type_flags & NG_TYPE_TEAM){
8450 multi_team_level_init();
8453 // force everyone into this state
8454 send_netgame_update_packet();
8456 if(!(Game_mode & GM_STANDALONE_SERVER)){
8457 multi_common_add_text(XSTR("Send update packet\n",831),1);
8460 // setup some of my own data
8461 Net_player->flags |= NETINFO_FLAG_MISSION_OK;
8463 // do any output stuff
8464 if(Game_mode & GM_STANDALONE_SERVER){
8465 std_debug_set_standalone_state_string("Mission Sync");
8468 // do this here to insure we have the most up to date file checksum info
8469 multi_get_mission_checksum(Game_current_mission_filename);
8470 // parse_get_file_signature(Game_current_mission_filename);
8472 if(!(Game_mode & GM_STANDALONE_SERVER)){
8473 multi_common_add_text(XSTR("Got file signatures\n",832),1);
8476 if(!(Game_mode & GM_STANDALONE_SERVER)){
8477 multi_common_add_text(XSTR("Sending update state packet\n",833),1);
8481 // if we're not in team vs. team mode - set all player teams to be 0, and unset all captaincy bits
8482 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
8483 for(idx=0;idx<MAX_PLAYERS;idx++){
8484 Net_players[idx].p_info.team = 0;
8485 Net_players[idx].flags &= ~(NETINFO_FLAG_TEAM_CAPTAIN);
8489 // we aren't necessarily xferring the mission file yet
8490 Assert(Net_player->s_info.xfer_handle == -1);
8492 // always call this for good measure
8493 multi_campaign_flush_data();
8495 Mission_sync_flags = 0;
8496 Multi_mission_loaded = 0;
8499 void multi_sync_pre_do()
8503 // If I'm the server, wait for everyone to arrive in this state, then begin transferring data, etc.
8504 // all servers (standalone or no, go through this)
8505 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8506 // wait for everyone to arrive, then request filesig from all of them
8507 if(multi_netplayer_state_check(NETPLAYER_STATE_MISSION_SYNC) && !(Mission_sync_flags & MS_FLAG_SENT_FILESIG) && !multi_endgame_ending()){
8508 send_file_sig_request(Netgame.mission_name);
8509 Mission_sync_flags |= MS_FLAG_SENT_FILESIG;
8511 if(!(Game_mode & GM_STANDALONE_SERVER)){
8512 multi_common_add_text(XSTR("Sent filesig request\n",834),1);
8516 // if we're waiting for players to receive files, then check on their status
8517 if((Mission_sync_flags & MS_FLAG_SENT_FILESIG) && !multi_netplayer_flag_check(NETINFO_FLAG_MISSION_OK) && !multi_endgame_ending()){
8518 for(idx=0;idx<MAX_PLAYERS;idx++){
8519 // if this player is in the process of xferring a file
8520 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].s_info.xfer_handle != -1)){
8521 switch(multi_xfer_get_status(Net_players[idx].s_info.xfer_handle)){
8522 // if it has successfully completed, set his ok flag
8523 case MULTI_XFER_SUCCESS :
8525 Net_players[idx].flags |= NETINFO_FLAG_MISSION_OK;
8527 // release the xfer instance handle
8528 multi_xfer_release_handle(Net_players[idx].s_info.xfer_handle);
8529 Net_players[idx].s_info.xfer_handle = -1;
8531 // if it has failed or timed-out, kick the player
8532 case MULTI_XFER_TIMEDOUT:
8533 case MULTI_XFER_FAIL:
8534 // release the xfer handle
8535 multi_xfer_release_handle(Net_players[idx].s_info.xfer_handle);
8536 Net_players[idx].s_info.xfer_handle = -1;
8539 multi_kick_player(idx, 0, KICK_REASON_BAD_XFER);
8546 // NOTE : this is now obsolete
8547 // once everyone is verified, do any data transfer necessary
8548 if(multi_netplayer_flag_check(NETINFO_FLAG_MISSION_OK) && !(Mission_sync_flags & MS_FLAG_DATA_DONE) && !multi_endgame_ending()){
8549 // do nothing for now
8550 Mission_sync_flags |= MS_FLAG_DATA_DONE;
8552 // send campaign pool data
8553 multi_campaign_send_pool_status();
8556 // wait for everyone to ack on campaign pool data (even in non-campaign situations)
8557 if((Mission_sync_flags & MS_FLAG_DATA_DONE) && !(Mission_sync_flags & MS_FLAG_CAMP_DONE) && !multi_endgame_ending()){
8558 // check to see if everyone has acked the campaign pool data
8559 if(multi_netplayer_state_check(NETPLAYER_STATE_CPOOL_ACK)){
8560 Mission_sync_flags |= MS_FLAG_CAMP_DONE;
8564 // once everyone is verified, tell them to load the mission
8565 // also make sure to load the mission myself _AFTER_ telling everyone to do so. This makes the whole process
8566 // move along faster
8567 if((Mission_sync_flags & MS_FLAG_CAMP_DONE) && !(Mission_sync_flags & MS_FLAG_SENT_LOAD) && !multi_endgame_ending()){
8568 send_netplayer_load_packet(NULL);
8569 Mission_sync_flags |= MS_FLAG_SENT_LOAD;
8571 if(!(Game_mode & GM_STANDALONE_SERVER)){
8572 multi_common_add_text(XSTR("Sent load packet\n",835),1);
8575 // load the mission myself, as soon as possible
8576 if(!Multi_mission_loaded){
8577 nprintf(("Network","Server loading mission..."));
8579 // update everyone about my status
8580 Net_player->state = NETPLAYER_STATE_MISSION_LOADING;
8581 send_netplayer_update_packet();
8583 game_start_mission();
8585 nprintf(("Network","Done\n"));
8586 Multi_mission_loaded = 1;
8588 // update everyone about my status
8589 Net_player->state = NETPLAYER_STATE_MISSION_LOADED;
8590 send_netplayer_update_packet();
8592 if(!(Game_mode & GM_STANDALONE_SERVER)){
8593 multi_common_add_text(XSTR("Loaded mission locally\n",836),1);
8598 // if everyone has loaded the mission, randomly assign players to ships
8599 if(multi_netplayer_state_check(NETPLAYER_STATE_MISSION_LOADED) && !(Mission_sync_flags & MS_FLAG_TS_SLOTS) && !multi_endgame_ending()){
8600 // call the team select function to assign players to their ships, wings, etc
8601 multi_ts_assign_players_all();
8602 send_netplayer_slot_packet();
8605 Mission_sync_flags |= MS_FLAG_TS_SLOTS;
8608 // if everyone has loaded the mission, move to the team select stage
8609 if(Sync_test && multi_netplayer_state_check(NETPLAYER_STATE_SLOT_ACK) && !(Mission_sync_flags & MS_FLAG_PUSHED_BRIEFING) && !multi_endgame_ending()){
8610 Netgame.game_state = NETGAME_STATE_BRIEFING;
8611 send_netgame_update_packet(); // this will push everyone into the next state
8613 // the standalone moves to his own wait state, whereas in the normal game mode, the server/host moves in to the
8614 // team select state
8615 if(Game_mode & GM_STANDALONE_SERVER){
8616 gameseq_post_event(GS_EVENT_MULTI_STD_WAIT);
8618 gameseq_post_event(GS_EVENT_START_GAME);
8621 Mission_sync_flags |= MS_FLAG_PUSHED_BRIEFING;
8623 if(!(Game_mode & GM_STANDALONE_SERVER)){
8624 multi_common_add_text(XSTR("Moving to team select\n",837),1);
8628 // clients should detect here if they are doing a file xfer and do error processing
8629 if((Net_player->state == NETPLAYER_STATE_MISSION_XFER) && (Net_player->s_info.xfer_handle != -1) && !multi_endgame_ending()){
8630 switch(multi_xfer_get_status(Net_player->s_info.xfer_handle)){
8631 // if it has successfully completed, set his ok flag
8632 case MULTI_XFER_SUCCESS :
8633 // release my xfer handle
8634 multi_xfer_release_handle(Net_player->s_info.xfer_handle);
8635 Net_player->s_info.xfer_handle = -1;
8638 // if it has failed or timed-out, kick the player
8639 case MULTI_XFER_TIMEDOUT:
8640 case MULTI_XFER_FAIL:
8641 // release my xfer handle
8642 multi_xfer_release_handle(Net_player->s_info.xfer_handle);
8643 Net_player->s_info.xfer_handle = -1;
8645 // leave the game qith an error code
8646 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_XFER_FAIL);
8653 if(!(Game_mode & GM_STANDALONE_SERVER)){
8655 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8656 if(Multi_sync_bitmap != -1){
8657 gr_set_bitmap(Multi_sync_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8660 Multi_sync_window.draw();
8662 multi_sync_blit_screen_all();
8668 void multi_sync_pre_close()
8670 // at this point, we should shut down any file xfers...
8671 if(Net_player->s_info.xfer_handle != -1){
8672 nprintf(("Network","WARNING - killing file xfer while leaving mission sync state!!!\n"));
8674 multi_xfer_abort(Net_player->s_info.xfer_handle);
8675 Net_player->s_info.xfer_handle = -1;
8679 void multi_sync_post_init()
8681 multi_reset_timestamps();
8683 Multi_state_timestamp = timestamp(0);
8686 ml_string(NOX("Performing post-briefing data sync"));
8688 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8689 multi_common_add_text(XSTR("Server performing sync\n",830),1);
8691 multi_common_add_text(XSTR("Client performing sync\n",838),1);
8694 // everyone should re-initialize these
8695 init_multiplayer_stats();
8697 // reset all sequencing info
8698 multi_oo_reset_sequencing();
8700 // if I am not the master of the game, then send the firing information for my ship
8702 if ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) ){
8703 send_firing_info_packet();
8706 // if I'm not a standalone server, load up the countdown stuff
8707 if(!(Game_mode & GM_STANDALONE_SERVER)){
8708 Multi_sync_countdown_anim = NULL;
8709 Multi_sync_countdown_instance = NULL;
8710 Multi_sync_countdown_anim = anim_load(Multi_sync_countdown_fname[gr_screen.res]);
8711 if(Multi_sync_countdown_anim == NULL){
8712 nprintf(("General","WARNING!, Could not load countdown animation %s!\n",Multi_sync_countdown_fname[gr_screen.res]));
8716 // create objects for all permanent observers
8717 multi_obs_level_init();
8719 // clear the game start countdown timer
8720 Multi_sync_countdown_timer = -1.0f;
8721 Multi_sync_countdown = -1;
8723 // if this is a team vs. team mission, mark all ship teams appropriately
8724 if(Netgame.type_flags & NG_TYPE_TEAM){
8725 multi_team_mark_all_ships();
8728 Mission_sync_flags = 0;
8729 Multi_sync_launch_pressed = 0;
8732 #define MULTI_POST_TIMESTAMP 7000
8734 extern int create_wings();
8736 void multi_sync_post_do()
8740 // only if the host is also the master should he be doing this (non-standalone situation)
8741 if ( Net_player->flags & NETINFO_FLAG_AM_MASTER ) {
8743 // once everyone gets to this screen, send them the ship classes of all ships.
8744 if(multi_netplayer_state_check(NETPLAYER_STATE_WAITING) && !(Mission_sync_flags & MS_FLAG_POST_DATA)) {
8745 // only the host should ever do this
8746 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8747 // at this point we want to delete all necessary ships, change all necessary ship classes, and set all weapons up
8748 multi_ts_create_wings();
8750 // update player ets settings
8751 for(idx=0;idx<MAX_PLAYERS;idx++){
8752 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].player->objnum != -1)){
8753 multi_server_update_player_weapons(&Net_players[idx],&Ships[Objects[Net_players[idx].player->objnum].instance]);
8758 // note that this is done a little differently for standalones and nonstandalones
8759 send_post_sync_data_packet();
8761 multi_common_add_text(XSTR("Sending post briefing block information\n",839),1);
8763 Mission_sync_flags |= MS_FLAG_POST_DATA;
8766 // send weapon slots data
8767 if(multi_netplayer_state_check(NETPLAYER_STATE_POST_DATA_ACK) && !(Mission_sync_flags & MS_FLAG_WSS_SLOTS)) {
8768 // note that this is done a little differently for standalones and nonstandalones
8769 if(Netgame.type_flags & NG_TYPE_TEAM){
8770 send_wss_slots_data_packet(0,0);
8771 send_wss_slots_data_packet(1,1);
8773 send_wss_slots_data_packet(0,1);
8776 multi_common_add_text(XSTR("Sending weapon slots information\n",840),1);
8778 Mission_sync_flags |= MS_FLAG_WSS_SLOTS;
8781 // once weapon information is received, send player settings info
8782 if ( multi_netplayer_state_check(NETPLAYER_STATE_WSS_ACK) && !(Mission_sync_flags & MS_FLAG_PSETTINGS)) {
8783 send_player_settings_packet();
8785 // server (specifically, the standalone), should set this here
8786 Net_player->state = NETPLAYER_STATE_SETTINGS_ACK;
8787 send_netplayer_update_packet();
8789 multi_common_add_text(XSTR("Sending player settings packets\n",841),1);
8791 Mission_sync_flags |= MS_FLAG_PSETTINGS;
8794 // check to see if the countdown timer has started and act appropriately
8795 if( Multi_sync_countdown_timer > -1.0f ) {
8797 // increment by frametime.
8798 Multi_sync_countdown_timer += flFrametime;
8800 // if the animation is not playing, start it
8801 if(!(Game_mode & GM_STANDALONE_SERVER) && (Multi_sync_countdown_instance == NULL) && (Multi_sync_countdown_anim != NULL)){
8802 anim_play_struct aps;
8804 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]);
8805 aps.screen_id = GS_STATE_MULTI_MISSION_SYNC;
8806 aps.framerate_independent = 1;
8808 Multi_sync_countdown_instance = anim_play(&aps);
8811 // if the next second has expired
8812 if( Multi_sync_countdown_timer >= 1.0f ) {
8814 Multi_sync_countdown--;
8815 Multi_sync_countdown_timer = 0.0f;
8817 // if the countdown has reached 0, launch the mission
8818 if(Multi_sync_countdown == 0){
8819 Multi_sync_countdown_timer = -1.0f;
8821 Multi_sync_launch_pressed = 0;
8822 multi_sync_launch();
8824 // otherwise send a countdown packet
8826 send_countdown_packet(Multi_sync_countdown);
8831 // jump into the mission myself
8832 if((Multi_sync_countdown == 0) && multi_netplayer_state_check(NETPLAYER_STATE_IN_MISSION)){
8833 if(!((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !Multi_sync_launch_pressed)){
8834 Net_player->state = NETPLAYER_STATE_IN_MISSION;
8835 Netgame.game_state = NETGAME_STATE_IN_MISSION;
8836 gameseq_post_event(GS_EVENT_ENTER_GAME);
8838 multi_common_add_text(XSTR("Moving into game\n",842),1);
8842 // maybe start the animation countdown
8843 if((Multi_sync_countdown >= 0) && (Multi_sync_countdown_instance == NULL) && (Multi_sync_countdown_anim != NULL)){
8844 anim_play_struct aps;
8846 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]);
8847 aps.screen_id = GS_STATE_MULTI_MISSION_SYNC;
8848 aps.framerate_independent = 1;
8850 Multi_sync_countdown_instance = anim_play(&aps);
8854 // host - specific stuff
8855 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8856 // create the launch button so the host can click
8857 if( Sync_test && multi_netplayer_state_check(NETPLAYER_STATE_SETTINGS_ACK) ){
8858 multi_sync_create_launch_button();
8863 if(!(Game_mode & GM_STANDALONE_SERVER)){
8865 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8866 if(Multi_sync_bitmap != -1){
8867 gr_set_bitmap(Multi_sync_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8870 Multi_sync_window.draw();
8872 multi_sync_blit_screen_all();
8878 void multi_sync_post_close()
8882 // if I'm not a standalone server, unload up the countdown stuff
8883 if(!(Game_mode & GM_STANDALONE_SERVER)){
8884 // release all rendering animation instances (should only be 1)
8885 anim_release_all_instances(GS_STATE_MULTI_MISSION_SYNC);
8886 Multi_sync_countdown_instance = NULL;
8888 // free up the countdown animation
8889 if(Multi_sync_countdown_anim != NULL){
8890 anim_free(Multi_sync_countdown_anim);
8891 Multi_sync_countdown_anim = NULL;
8895 // all players should reset sequencing
8896 for(idx=0;idx<MAX_PLAYERS;idx++){
8897 if(Net_player->flags & NETINFO_FLAG_CONNECTED){
8898 Net_players[idx].client_cinfo_seq = 0;
8899 Net_players[idx].client_server_seq = 0;
8903 // multiplayer dogfight
8904 multi_df_level_pre_enter();
8906 // clients should clear obj_pair array and add pair for themselves
8908 if ( MULTIPLAYER_CLIENT ) {
8910 obj_add_pairs( OBJ_INDEX(Player_obj) );
8915 void multi_sync_display_name(char *name,int index,int np_index)
8917 char fit[CALLSIGN_LEN];
8919 // make sure the string actually fits
8922 // if we're in team vs. team mode
8923 if(Netgame.type_flags & NG_TYPE_TEAM){
8924 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]);
8926 // if this is the currently selected player, draw him highlighted
8927 if(np_index == Multi_sync_player_select){
8928 gr_set_color_fast(&Color_text_selected);
8930 gr_set_color_fast(&Color_text_normal);
8934 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);
8936 // blit his team icon
8938 if(Net_players[np_index].p_info.team == 0){
8939 // blit the team captain icon
8940 if(Net_players[np_index].flags & NETINFO_FLAG_TEAM_CAPTAIN){
8941 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
8942 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8943 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);
8946 // normal team member icon
8948 if(Multi_common_icons[MICON_TEAM0] != -1){
8949 gr_set_bitmap(Multi_common_icons[MICON_TEAM0], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8950 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);
8955 else if(Net_players[np_index].p_info.team == 1){
8956 // blit the team captain icon
8957 if(Net_players[np_index].flags & NETINFO_FLAG_TEAM_CAPTAIN){
8958 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
8959 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8960 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);
8963 // normal team member icon
8965 if(Multi_common_icons[MICON_TEAM1] != -1){
8966 gr_set_bitmap(Multi_common_icons[MICON_TEAM1], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8967 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);
8972 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]);
8974 // if this is the currently selected player, draw him highlighted
8975 if(np_index == Multi_sync_player_select){
8976 gr_set_color_fast(&Color_text_selected);
8978 gr_set_color_fast(&Color_text_normal);
8982 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);
8985 // maybe blit his CD status icon
8986 if((Net_players[np_index].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
8987 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8988 gr_bitmap(Ms_status_coords[gr_screen.res][MS_X_COORD], Ms_status_coords[gr_screen.res][MS_Y_COORD] + (index * 10));
8992 void multi_sync_display_status(char *status,int index)
8996 // make sure the string actually fits
8997 strcpy(fit, status);
8998 gr_force_fit_string(fit, 250, Ms_status2_coords[gr_screen.res][MS_W_COORD] - 20);
8999 gr_set_color_fast(&Color_bright);
9000 gr_string(Ms_status2_coords[gr_screen.res][MS_X_COORD], Ms_status2_coords[gr_screen.res][MS_Y_COORD] + (index * 10), fit);
9003 void multi_sync_force_start_pre()
9006 int want_state = NETPLAYER_STATE_SLOT_ACK; // kick any players who are still in this state
9008 // go through the player list and boot anyone who isn't in the right state
9009 for(idx=0;idx<MAX_PLAYERS;idx++){
9010 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx]) && (Net_players[idx].state == want_state)){
9011 multi_kick_player(idx,0);
9016 void multi_sync_force_start_post()
9020 int num_kill_states;
9022 // determine the state we want all players in so that we can find those who are not in the state
9023 kill_state[0] = NETPLAYER_STATE_BRIEFING;
9024 kill_state[1] = NETPLAYER_STATE_SHIP_SELECT;
9025 kill_state[2] = NETPLAYER_STATE_WEAPON_SELECT;
9026 num_kill_states = 3;
9028 // go through the player list and boot anyone who isn't in the right state
9029 for(idx=0;idx<MAX_PLAYERS;idx++){
9030 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx])){
9031 // check against all kill state
9032 for(idx2 = 0;idx2<num_kill_states;idx2++){
9033 if(Net_players[idx].state == kill_state[idx2]){
9034 multi_kick_player(idx,0);
9042 void multi_sync_start_countdown()
9044 // don't allow repeat button presses
9045 if(Multi_sync_launch_pressed){
9049 Multi_sync_launch_pressed = 1;
9051 // if I'm the server, begin the countdown
9052 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
9053 gamesnd_play_iface(SND_COMMIT_PRESSED);
9054 Multi_sync_countdown_timer = 0.0f;
9055 Multi_sync_countdown = MULTI_SYNC_COUNTDOWN_TIME;
9057 // send an initial countdown value
9058 send_countdown_packet(Multi_sync_countdown);
9060 // otherwise send the "start countdown" packet to the standalone
9062 Assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
9063 send_countdown_packet(-1);
9067 void multi_sync_launch()
9069 // don't allow repeat button presses
9070 if(Multi_sync_launch_pressed){
9074 Multi_sync_launch_pressed = 1;
9077 ml_printf(NOX("Entering mission %s"), Game_current_mission_filename);
9079 // tell everyone to jump into the mission
9080 send_jump_into_mission_packet();
9081 Multi_state_timestamp = timestamp(MULTI_POST_TIMESTAMP);
9083 // set the # of players at the start of the mission
9084 Multi_num_players_at_start = multi_num_players();
9085 nprintf(("Network","# of players at start of mission : %d\n", Multi_num_players_at_start));
9087 // initialize datarate limiting for all clients
9088 multi_oo_rate_init_all();
9090 multi_common_add_text(XSTR("Sending mission start packet\n",843),1);
9093 void multi_sync_create_launch_button()
9095 if (!Multi_launch_button_created) {
9096 // create the object
9097 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);
9099 // set the sound to play when highlighted
9100 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_highlight_action(common_play_highlight_sound);
9102 // set the ani for the button
9103 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_bmaps(Multi_sync_buttons[gr_screen.res][MS_LAUNCH].filename, 3, 0);
9106 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.link_hotspot(Multi_sync_buttons[gr_screen.res][MS_LAUNCH].hotspot);
9109 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_hotkey(KEY_CTRLED+KEY_ENTER);
9112 // create the text for the button
9113 Multi_sync_window.add_XSTR(&Multi_sync_text[gr_screen.res][MST_LAUNCH]);
9116 // increment the button count so we start checking this one
9117 Multi_sync_button_count++;
9119 Multi_launch_button_created = 1;
9123 void multi_sync_handle_plist()
9129 // if we don't have a currently selected player, select one
9130 if((Multi_sync_player_select < 0) || !MULTI_CONNECTED(Net_players[Multi_sync_player_select])){
9131 for(idx=0;idx<MAX_PLAYERS;idx++){
9132 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
9133 Multi_sync_player_select = idx;
9139 // check for button list presses
9140 if(Multi_sync_plist_button.pressed()){
9141 // get the y mouse coords
9142 Multi_sync_plist_button.get_mouse_pos(NULL,&my);
9144 // get the index of the item selected
9145 select_index = my / 10;
9147 // if the index is greater than the current # connections, do nothing
9148 if(select_index > (multi_num_connections() - 1)){
9152 // translate into an absolute Net_players[] index (get the Nth net player)
9153 Multi_sync_player_select = -1;
9154 for(idx=0;idx<MAX_PLAYERS;idx++){
9155 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
9159 // if we've found the item we're looking for
9160 if(select_index < 0){
9161 Multi_sync_player_select = idx;
9166 // if for some bizarre reason, this is an invalid player, unselect him and wait for the next interation
9168 if((Multi_sync_player_select >= 0) && (!MULTI_CONNECTED(Net_players[Multi_sync_player_select]) || MULTI_STANDALONE(Net_players[Multi_sync_player_select])) ){
9169 Multi_sync_player_select = -1;
9175 // -------------------------------------------------------------------------------------------------------------
9177 // MULTIPLAYER DEBRIEF SCREEN
9180 // other relevant data
9181 int Multi_debrief_accept_hit;
9182 int Multi_debrief_replay_hit;
9184 // set if the server has left the game
9185 int Multi_debrief_server_left = 0;
9187 // if we've reported on TvT status all players are in the debrief
9188 int Multi_debrief_reported_tvt = 0;
9190 // whether stats are being accepted
9191 // -1 == no decision yet
9194 int Multi_debrief_stats_accept_code = -1;
9196 int Multi_debrief_server_framecount = 0;
9198 float Multi_debrief_time = 0.0f;
9199 float Multi_debrief_resend_time = 10.0f;
9201 void multi_debrief_init()
9205 Multi_debrief_time = 0.0f;
9206 Multi_debrief_resend_time = 10.0f;
9208 // do this to notify the standalone or the normal server that we're in the debrief state and ready to receive packets
9209 if (!(Net_player->flags & NETINFO_FLAG_AM_MASTER)) {
9210 Net_player->state = NETPLAYER_STATE_DEBRIEF;
9211 send_netplayer_update_packet();
9214 // unflag some stuff
9215 for(idx=0;idx<MAX_PLAYERS;idx++){
9216 if(MULTI_CONNECTED(Net_players[idx])){
9217 Net_players[idx].flags &= ~(NETINFO_FLAG_RESPAWNING | NETINFO_FLAG_LIMBO | NETINFO_FLAG_WARPING_OUT);
9221 // if text input mode is active, clear it
9222 multi_msg_text_flush();
9224 // the server has not left yet
9225 Multi_debrief_server_left = 0;
9227 // have not hit accept or replay yet
9228 Multi_debrief_accept_hit = 0;
9229 Multi_debrief_replay_hit = 0;
9231 // stats have not been accepted yet
9232 Multi_debrief_stats_accept_code = -1;
9234 // mark stats as not being store yet
9235 Netgame.flags &= ~(NG_FLAG_STORED_MT_STATS);
9237 // no report on TvT yet
9238 Multi_debrief_reported_tvt = 0;
9240 Multi_debrief_server_framecount = 0;
9243 void multi_debrief_do_frame()
9245 Multi_debrief_time += flFrametime;
9247 // set the netgame state to be debriefing when appropriate
9248 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)){
9249 Netgame.game_state = NETGAME_STATE_DEBRIEF;
9250 send_netgame_update_packet();
9253 // evaluate all server stuff
9254 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
9255 multi_debrief_server_process();
9259 void multi_debrief_close()
9261 if ( MULTIPLAYER_CLIENT && (Netgame.game_state == NETGAME_STATE_MISSION_SYNC) ){
9262 gamesnd_play_iface( SND_COMMIT_PRESSED );
9266 // handle optional mission loop
9267 void multi_maybe_set_mission_loop()
9269 int cur = Campaign.current_mission;
9270 if (Campaign.missions[cur].has_mission_loop) {
9271 Assert(Campaign.loop_mission != CAMPAIGN_LOOP_MISSION_UNINITIALIZED);
9273 bool require_repeat_mission = (Campaign.current_mission == Campaign.next_mission);
9275 // check for (1) mission loop available, (2) dont have to repeat last mission
9276 if ( (Campaign.missions[cur].has_mission_loop && (Campaign.loop_mission != -1)) && !require_repeat_mission ) {
9279 debrief_assemble_optional_mission_popup_text(buffer, Campaign.missions[cur].mission_loop_desc);
9281 int choice = popup(0 , 2, POPUP_NO, POPUP_YES, buffer);
9283 Campaign.loop_enabled = 1;
9284 Campaign.next_mission = Campaign.loop_mission;
9289 // handle all cases for when the accept key is hit in a multiplayer debriefing
9290 void multi_debrief_accept_hit()
9292 // if we already accepted, do nothing
9293 // but he may need to hit accept again after the server has left the game, so allow this
9294 if(Multi_debrief_accept_hit){
9298 // mark this so that we don't hit it again
9299 Multi_debrief_accept_hit = 1;
9301 gamesnd_play_iface(SND_COMMIT_PRESSED);
9303 // if the server has left the game, always just end the game.
9304 if(Multi_debrief_server_left){
9305 if(!multi_quit_game(PROMPT_ALL)){
9306 Multi_debrief_server_left = 1;
9307 Multi_debrief_accept_hit = 0;
9309 Multi_debrief_server_left = 0;
9312 // query the host and see if he wants to accept stats
9313 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
9314 // if we're on a tracker game, he gets no choice for storing stats
9315 if(MULTI_IS_TRACKER_GAME){
9316 multi_maybe_set_mission_loop();
9318 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));
9320 // evaluate the result
9325 Multi_debrief_accept_hit = 0;
9328 // set the accept code to be "not accepting"
9330 multi_debrief_stats_toss();
9331 multi_maybe_set_mission_loop();
9334 // accept the stats and continue
9336 multi_debrief_stats_accept();
9337 multi_maybe_set_mission_loop();
9343 // set my netplayer state to be "debrief_accept", and be done with it
9344 Net_player->state = NETPLAYER_STATE_DEBRIEF_ACCEPT;
9345 send_netplayer_update_packet();
9349 // handle all cases for when the escape key is hit in a multiplayer debriefing
9350 void multi_debrief_esc_hit()
9354 // if the server has left
9355 if(Multi_debrief_server_left){
9356 multi_quit_game(PROMPT_ALL);
9361 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
9362 // if the stats have already been accepted
9363 if((Multi_debrief_stats_accept_code != -1) || (MULTI_IS_TRACKER_GAME)){
9364 multi_quit_game(PROMPT_HOST);
9366 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));
9368 // evaluate the result
9375 // set the accept code to be "not accepting"
9377 multi_debrief_stats_toss();
9378 multi_quit_game(PROMPT_NONE);
9381 // accept the stats and continue
9383 multi_debrief_stats_accept();
9384 multi_quit_game(PROMPT_NONE);
9389 // if the stats haven't been accepted yet, or this is a tracker game
9390 if((Multi_debrief_stats_accept_code == -1) && !(MULTI_IS_TRACKER_GAME)){
9391 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));
9393 // evaluate the result
9395 multi_quit_game(PROMPT_NONE);
9398 // otherwise go through the normal endgame channels
9400 multi_quit_game(PROMPT_ALL);
9405 void multi_debrief_replay_hit()
9407 // only the host should ever get here
9408 Assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
9410 // if the button was already pressed, do nothing
9411 if(Multi_debrief_accept_hit){
9415 // same as hittin the except button except no stats are kept
9416 Multi_debrief_accept_hit = 1;
9418 // mark myself as being in the replay state so we know what to do next
9419 Net_player->state = NETPLAYER_STATE_DEBRIEF_REPLAY;
9420 send_netplayer_update_packet();
9423 // call this when the server has left and we would otherwise be saying "contact lost with server
9424 void multi_debrief_server_left()
9427 Multi_debrief_server_left = 1;
9429 // undo any "accept" hit so that clients can hit accept again to leave
9430 Multi_debrief_accept_hit = 0;
9433 void multi_debrief_stats_accept()
9435 // don't do anything if we've already accepted
9436 if(Multi_debrief_stats_accept_code != -1){
9440 Multi_debrief_stats_accept_code = 1;
9442 // if we're the host, and we're on a standalone, tell the standalone to begin the stats storing process
9443 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) || (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
9444 // send a packet to the players telling them to store their stats
9445 send_store_stats_packet(1);
9448 // add a chat line saying "stats have been accepted"
9449 multi_display_chat_msg(XSTR("<stats have been accepted>",850),0,0);
9452 ml_string(NOX("Stats stored"));
9455 void multi_debrief_stats_toss()
9457 // don't do anything if we've already accepted
9458 if(Multi_debrief_stats_accept_code != -1){
9462 Multi_debrief_stats_accept_code = 0;
9464 // if we're the host, and we're on a standalone, tell everyone to "toss" stats
9465 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) || (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
9466 // send a packet to the players telling them to store their stats
9467 send_store_stats_packet(0);
9470 // add a chat line saying "stats have been accepted"
9471 multi_display_chat_msg(XSTR("<stats have been tossed>",851),0,0);
9474 ml_string(NOX("Stats tossed"));
9477 int multi_debrief_stats_accept_code()
9479 return Multi_debrief_stats_accept_code;
9482 void multi_debrief_server_process()
9485 int player_status,other_status;
9487 Multi_debrief_server_framecount++;
9489 // if we're > 10 seconds into the debrief and not everyone is here, try warping everyone out again
9490 if((Multi_debrief_time >= Multi_debrief_resend_time) && !multi_netplayer_state_check3(NETPLAYER_STATE_DEBRIEF, NETPLAYER_STATE_DEBRIEF_ACCEPT, NETPLAYER_STATE_DEBRIEF_REPLAY, 1)){
9491 // find all players who are not in the debrief state and hit them with the endgame packet
9492 for(idx=0; idx<MAX_PLAYERS; idx++){
9493 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)) ){
9494 send_endgame_packet(&Net_players[idx]);
9499 Multi_debrief_resend_time += 7.0f;
9502 // evaluate the status of all players in the game (0 == not ready, 1 == ready to continue, 2 == ready to replay)
9505 // check all players
9506 for(idx=0;idx<MAX_PLAYERS;idx++){
9507 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_HOST(Net_players[idx])){
9508 if(Net_players[idx].state != NETPLAYER_STATE_DEBRIEF_ACCEPT){
9515 // if we haven't already reported TvT results
9516 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)){
9517 multi_team_report();
9518 Multi_debrief_reported_tvt = 1;
9521 // if all other players are good to go, check the host
9523 // if he is ready to continue
9524 if(Netgame.host->state == NETPLAYER_STATE_DEBRIEF_ACCEPT){
9527 // if he wants to replay the mission
9528 else if(Netgame.host->state == NETPLAYER_STATE_DEBRIEF_REPLAY){
9531 // if he is not ready
9536 // if all players are _not_ good to go
9541 // if we're in the debriefing state in a campaign mode, process accordingly
9542 if(Netgame.campaign_mode == MP_CAMPAIGN){
9543 multi_campaign_do_debrief(player_status);
9545 // otherwise process as normal (looking for all players to be ready to go to the next mission
9547 if(player_status == 1){
9548 multi_flush_mission_stuff();
9550 // set the netgame state to be forming and continue
9551 Netgame.game_state = NETGAME_STATE_FORMING;
9552 send_netgame_update_packet();
9554 // move to the proper state
9555 if(Game_mode & GM_STANDALONE_SERVER){
9556 gameseq_post_event(GS_EVENT_STANDALONE_MAIN);
9558 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
9561 multi_reset_timestamps();
9562 } else if(player_status == 2){
9563 multi_flush_mission_stuff();
9565 // tell everyone to move into the pre-briefing sync state
9566 Netgame.game_state = NETGAME_STATE_MISSION_SYNC;
9567 send_netgame_update_packet();
9569 // move back to the mission sync screen for the same mission again
9570 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
9571 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
9573 multi_reset_timestamps();
9579 // -------------------------------------------------------------------------------------------------------------
9581 // MULTIPLAYER PASSWORD POPUP
9586 static char *Multi_pwd_bitmap_fname[GR_NUM_RESOLUTIONS] = {
9587 "Password", // GR_640
9588 "2_Password" // GR_1024
9591 static char *Multi_pwd_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
9592 "Password-M", // GR_640
9593 "2_Password-M" // GR_1024
9598 // constants for coordinate lookup
9599 #define MPWD_X_COORD 0
9600 #define MPWD_Y_COORD 1
9601 #define MPWD_W_COORD 2
9602 #define MPWD_H_COORD 3
9605 #define MULTI_PWD_NUM_BUTTONS 2
9606 #define MPWD_CANCEL 0
9607 #define MPWD_COMMIT 1
9609 // password area defs
9610 int Mpwd_coords[GR_NUM_RESOLUTIONS][4] = {
9623 UI_WINDOW Multi_pwd_window; // the window object for the join screen
9624 UI_INPUTBOX Multi_pwd_passwd; // for Netgame.passwd
9625 int Multi_pwd_bitmap; // the background bitmap
9626 int Multi_passwd_background = -1;
9627 int Multi_passwd_done = -1;
9628 int Multi_passwd_running = 0;
9631 ui_button_info Multi_pwd_buttons[GR_NUM_RESOLUTIONS][MULTI_PWD_NUM_BUTTONS] = {
9634 ui_button_info("PWB_00", 402, 134, -1, -1, 0),
9635 ui_button_info("PWB_01", 450, 134, -1, -1, 1),
9637 ui_button_info("PWB_00", 411, 151, 405, 141, 0),
9638 ui_button_info("PWB_01", 460, 151, 465, 141, 1),
9642 ui_button_info("2_PWB_00", 659, 242, 649, 225, 0),
9643 ui_button_info("2_PWB_01", 737, 242, 736, 225, 1),
9649 #define MULTI_PWD_NUM_TEXT 3
9651 UI_XSTR Multi_pwd_text[GR_NUM_RESOLUTIONS][MULTI_PWD_NUM_TEXT] = {
9653 { "Cancel", 387, 400, 141, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[0][MPWD_CANCEL].button},
9654 { "Commit", 1062, 455, 141, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[0][MPWD_COMMIT].button},
9655 { "Enter Password", 1332, 149, 92, UI_XSTR_COLOR_GREEN, -1, NULL},
9658 { "Cancel", 387, 649, 225, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[1][MPWD_CANCEL].button},
9659 { "Commit", 1062, 736, 225, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[1][MPWD_COMMIT].button},
9660 { "Enter Password", 1332, 239, 148, UI_XSTR_COLOR_GREEN, -1, NULL},
9665 // initialize all graphics, etc
9666 void multi_passwd_init()
9670 // store the background as it currently is
9671 Multi_passwd_background = gr_save_screen();
9673 // create the interface window
9674 Multi_pwd_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
9675 Multi_pwd_window.set_mask_bmap(Multi_pwd_bitmap_mask_fname[gr_screen.res]);
9677 // load the background bitmap
9678 Multi_pwd_bitmap = bm_load(Multi_pwd_bitmap_fname[gr_screen.res]);
9679 if(Multi_pwd_bitmap < 0){
9680 // we failed to load the bitmap - this is very bad
9684 // create the interface buttons
9685 for(idx=0; idx<MULTI_PWD_NUM_BUTTONS; idx++){
9686 // create the object
9687 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);
9689 // set the sound to play when highlighted
9690 Multi_pwd_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
9692 // set the ani for the button
9693 Multi_pwd_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pwd_buttons[gr_screen.res][idx].filename);
9696 Multi_pwd_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pwd_buttons[gr_screen.res][idx].hotspot);
9701 for(idx=0; idx<MULTI_PWD_NUM_TEXT; idx++){
9702 Multi_pwd_window.add_XSTR(&Multi_pwd_text[gr_screen.res][idx]);
9706 // create the password input box
9707 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);
9708 Multi_pwd_passwd.set_focus();
9710 // link the enter key to ACCEPT
9711 Multi_pwd_buttons[gr_screen.res][MPWD_COMMIT].button.set_hotkey(KEY_ENTER);
9713 Multi_passwd_done = -1;
9714 Multi_passwd_running = 1;
9717 // close down all graphics, etc
9718 void multi_passwd_close()
9720 // unload any bitmaps
9721 bm_release(Multi_pwd_bitmap);
9723 // destroy the UI_WINDOW
9724 Multi_pwd_window.destroy();
9726 // free up the saved background screen
9727 if(Multi_passwd_background >= 0){
9728 gr_free_screen(Multi_passwd_background);
9729 Multi_passwd_background = -1;
9732 Multi_passwd_running = 0;
9735 // process any button pressed
9736 void multi_passwd_process_buttons()
9738 // if the accept button was pressed
9739 if(Multi_pwd_buttons[gr_screen.res][MPWD_COMMIT].button.pressed()){
9740 gamesnd_play_iface(SND_USER_SELECT);
9741 Multi_passwd_done = 1;
9744 // if the cancel button was pressed
9745 if(Multi_pwd_buttons[gr_screen.res][MPWD_CANCEL].button.pressed()){
9746 gamesnd_play_iface(SND_USER_SELECT);
9747 Multi_passwd_done = 0;
9751 // run the passwd popup
9752 void multi_passwd_do(char *passwd)
9756 while(Multi_passwd_done == -1){
9757 // set frametime and run background stuff
9758 game_set_frametime(-1);
9759 game_do_state_common(gameseq_get_state());
9761 k = Multi_pwd_window.process();
9763 // process any keypresses
9766 // set this to indicate the user has cancelled for one reason or another
9767 Multi_passwd_done = 0;
9771 // if the input box text has changed
9772 if(Multi_pwd_passwd.changed()){
9774 Multi_pwd_passwd.get_text(passwd);
9777 // process any button pressed
9778 multi_passwd_process_buttons();
9780 // draw the background, etc
9783 if(Multi_passwd_background >= 0){
9784 gr_restore_screen(Multi_passwd_background);
9786 gr_set_bitmap(Multi_pwd_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
9788 Multi_pwd_window.draw();
9795 // bring up the password string popup, fill in passwd (return 1 if accept was pressed, 0 if cancel was pressed)
9796 int multi_passwd_popup(char *passwd)
9798 // if the popup is already running for some reason, don't do anything
9799 if(Multi_passwd_running){
9803 // initialize all graphics
9804 multi_passwd_init();
9807 multi_passwd_do(passwd);
9809 // shut everything down
9810 multi_passwd_close();
9812 return Multi_passwd_done;