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.8 2003/05/25 02:30:43 taylor
21 * Revision 1.7 2002/06/09 04:41:24 relnev
22 * added copyright header
24 * Revision 1.6 2002/06/02 06:02:59 relnev
27 * Revision 1.5 2002/06/02 00:31:35 relnev
28 * implemented osregistry
30 * Revision 1.4 2002/06/01 07:12:33 relnev
31 * a few NDEBUG updates.
33 * removed a few warnings.
35 * Revision 1.3 2002/05/26 20:49:54 theoddone33
38 * Revision 1.2 2002/05/07 03:16:47 theoddone33
39 * The Great Newline Fix
41 * Revision 1.1.1.1 2002/05/03 03:28:10 root
45 * 94 6/16/00 3:16p Jefff
46 * sim of the year dvd version changes, a few german soty localization
49 * 93 10/14/99 2:51p Jefff
52 * 92 10/13/99 3:50p Jefff
53 * fixed unnumbered XSTRs
55 * 91 9/15/99 1:45a Dave
56 * Don't init joystick on standalone. Fixed campaign mode on standalone.
57 * Fixed no-score-report problem in TvT
59 * 90 9/14/99 12:51a Jefff
62 * 89 9/13/99 4:52p Dave
65 * 88 9/13/99 11:30a Dave
66 * Added checkboxes and functionality for disabling PXO banners as well as
67 * disabling d3d zbuffer biasing.
69 * 87 9/12/99 10:06p Jefff
70 * changed instances of "Squad War" to "SquadWar"
72 * 86 9/03/99 1:32a Dave
73 * CD checking by act. Added support to play 2 cutscenes in a row
74 * seamlessly. Fixed super low level cfile bug related to files in the
75 * root directory of a CD. Added cheat code to set campaign mission # in
78 * 85 9/01/99 10:49p Dave
79 * Added nice SquadWar checkbox to the client join wait screen.
81 * 84 8/30/99 2:49p Jefff
83 * 83 8/26/99 8:49p Jefff
84 * Updated medals screen and about everything that ever touches medals in
85 * one way or another. Sheesh.
87 * 82 8/25/99 4:38p Dave
88 * Updated PXO stuff. Make squad war report stuff much more nicely.
90 * 81 8/20/99 2:09p Dave
93 * 80 8/20/99 10:06a Jefff
94 * removed closed/rstricted buttons from multi start screen
96 * 79 8/18/99 11:30a Jefff
98 * 78 8/18/99 10:38a Jefff
100 * 77 8/16/99 4:06p Dave
101 * Big honking checkin.
103 * 76 8/16/99 1:08p Jefff
104 * added sounds to a few controls, made input boxes lose focus on ENTER
106 * 75 8/16/99 9:52a Jefff
107 * fixed bitmap loading on buttons in multi-sync screen
109 * 74 8/11/99 5:54p Dave
110 * Fixed collision problem. Fixed standalone ghost problem.
112 * 73 8/10/99 4:35p Jefff
113 * fixed hi-res coords
115 * 72 8/06/99 12:29a Dave
116 * Multiple bug fixes.
118 * 71 8/05/99 3:13p Jasenw
119 * tweaked some text placement coords.
121 * 70 8/04/99 1:38p Jefff
122 * moved some text in multi join wait
124 * 69 8/03/99 12:45p Dave
127 * 68 7/25/99 5:17p Jefff
128 * campaign descriptions show up on multicreate screen
130 * 67 7/20/99 1:49p Dave
131 * Peter Drake build. Fixed some release build warnings.
133 * 66 7/19/99 2:13p Dave
134 * Added some new strings for Heiko.
136 * 65 7/15/99 9:20a Andsager
137 * FS2_DEMO initial checkin
139 * 64 7/08/99 10:53a Dave
140 * New multiplayer interpolation scheme. Not 100% done yet, but still
141 * better than the old way.
143 * 63 6/30/99 10:49a Jasenw
144 * Fixed coords for new launch countdown ani
146 * 62 6/29/99 7:39p Dave
147 * Lots of small bug fixes.
149 * 61 6/25/99 11:59a Dave
150 * Multi options screen.
152 * 60 6/09/99 2:17p Dave
153 * Fixed up pleasewait bitmap rendering.
155 * 59 6/05/99 3:42p Dave
156 * New multi sync screen.
158 * 58 6/01/99 6:07p Dave
159 * New loading/pause/please wait bar.
161 * 57 5/21/99 6:45p Dave
162 * Sped up ui loading a bit. Sped up localization disk access stuff. Multi
163 * start game screen, multi password, and multi pxo-help screen.
165 * 56 5/06/99 11:10a Dave
166 * Fixed coord on multi create screen.
168 * 55 5/04/99 6:38p Dave
169 * Finished multi join-wait screen.
171 * 54 5/04/99 5:20p Dave
172 * Fixed up multiplayer join screen and host options screen. Should both
175 * 53 5/03/99 11:04p Dave
176 * Most of the way done with the multi join screen.
178 * 52 5/03/99 8:32p Dave
179 * New version of multi host options screen.
181 * 51 4/29/99 2:15p Neilk
182 * slider2 code got modified; changed parameters for create
184 * 50 4/25/99 3:02p Dave
185 * Build defines for the E3 build.
187 * 49 4/21/99 6:15p Dave
188 * Did some serious housecleaning in the beam code. Made it ready to go
189 * for anti-fighter "pulse" weapons. Fixed collision pair creation. Added
190 * a handy macro for recalculating collision pairs for a given object.
192 * 48 4/16/99 5:27p Neilk
193 * added slider support and hir res for multi_create
195 * 47 4/14/99 6:37p Dave
196 * Fixed scroll button bug on host create screen.
198 * 46 4/14/99 5:28p Dave
201 * 45 4/12/99 10:07p Dave
202 * Made network startup more forgiving. Added checkmarks to dogfight
203 * screen for players who hit commit.
205 * 44 4/09/99 2:21p Dave
206 * Multiplayer beta stuff. CD checking.
208 * 43 4/08/99 1:28p Dave
209 * Small bug fixes for refresh button on the multi create screen.
211 * 42 4/08/99 11:55a Neilk
212 * Converted Multi_Create to new artwork (just lowres)
214 * 41 4/08/99 2:10a Dave
215 * Numerous bug fixes for the beta. Added builtin mission info for the
218 * 40 3/20/99 3:48p Andsager
219 * Do mission_loop stuff for PXO
221 * 39 3/10/99 6:50p Dave
222 * Changed the way we buffer packets for all clients. Optimized turret
223 * fired packets. Did some weapon firing optimizations.
225 * 38 3/09/99 6:24p Dave
226 * More work on object update revamping. Identified several sources of
227 * unnecessary bandwidth.
229 * 37 3/08/99 7:03p Dave
230 * First run of new object update system. Looks very promising.
232 * 36 2/25/99 4:19p Dave
233 * Added multiplayer_beta defines. Added cd_check define. Fixed a few
234 * release build warnings. Added more data to the squad war request and
237 * 35 2/24/99 3:26p Anoop
238 * Make sure the host is the only guy who bashes skill level for TvT.
240 * 34 2/24/99 2:25p Dave
241 * Fixed up chatbox bugs. Made squad war reporting better. Fixed a respawn
242 * bug for dogfight more.
244 * 33 2/23/99 2:29p Dave
245 * First run of oldschool dogfight mode.
247 * 32 2/17/99 2:11p Dave
248 * First full run of squad war. All freespace and tracker side stuff
251 * 31 2/12/99 6:16p Dave
252 * Pre-mission Squad War code is 95% done.
254 * 30 2/11/99 3:08p Dave
255 * PXO refresh button. Very preliminary squad war support.
257 * 29 2/08/99 5:07p Dave
258 * FS2 chat server support. FS2 specific validated missions.
260 * 28 2/04/99 6:29p Dave
261 * First full working rev of FS2 PXO support. Fixed Glide lighting
264 * 27 1/30/99 5:08p Dave
265 * More new hi-res stuff.Support for nice D3D textures.
267 * 26 1/29/99 2:08a Dave
268 * Fixed beam weapon collisions with players. Reduced size of scoring
269 * struct for multiplayer. Disabled PXO.
271 * 25 1/15/99 2:36p Neilk
272 * fixed multi_jw coordinates
274 * 24 1/13/99 7:19p Neilk
275 * Converted Mission Brief, Barracks, Synch to high res support
277 * 23 1/12/99 7:17p Neilk
279 * 22 1/12/99 5:45p Dave
280 * Moved weapon pipeline in multiplayer to almost exclusively client side.
281 * Very good results. Bandwidth goes down, playability goes up for crappy
282 * connections. Fixed object update problem for ship subsystems.
284 * 21 1/12/99 4:07a Dave
285 * Put in barracks code support for selecting squad logos. Properly
286 * distribute squad logos in a multiplayer game.
288 * 20 1/11/99 7:19p Neilk
289 * Converted multi_join interface to support multiple resolutions
291 * 19 12/18/98 1:13a Dave
292 * Rough 1024x768 support for Direct3D. Proper detection and usage through
295 * 18 12/17/98 4:50p Andsager
296 * Added debrief_assemble_optional_mission_popup_text() for single and
299 * 17 12/14/98 12:13p Dave
300 * Spiffed up xfer system a bit. Put in support for squad logo file xfer.
303 * 16 12/10/98 10:19a Andsager
304 * Fix mission loop assert
306 * 15 12/10/98 9:59a Andsager
307 * Fix some bugs with mission loops
309 * 14 12/09/98 1:56p Andsager
310 * Initial checkin of mission loop
312 * 13 12/03/98 5:22p Dave
313 * Ported over Freespace 1 multiplayer ships.tbl and weapons.tbl
316 * 12 11/30/98 1:07p Dave
317 * 16 bit conversion, first run.
319 * 11 11/20/98 11:16a Dave
320 * Fixed up IPX support a bit. Making sure that switching modes and
321 * loading/saving pilot files maintains proper state.
323 * 10 11/19/98 4:57p Dave
324 * Ignore PXO option if IPX is selected.
326 * 9 11/19/98 4:19p Dave
327 * Put IPX sockets back in psnet. Consolidated all multiplayer config
330 * 8 11/19/98 8:04a Dave
331 * Full support for D3-style reliable sockets. Revamped packet lag/loss
332 * system, made it receiver side and at the lowest possible level.
334 * 7 11/17/98 11:12a Dave
335 * Removed player identification by address. Now assign explicit id #'s.
337 * 6 10/19/98 11:15a Dave
338 * Changed requirements for stats storing in PXO mode.
340 * 5 10/16/98 9:40a Andsager
341 * Remove ".h" files from model.h
343 * 4 10/13/98 9:29a Dave
344 * Started neatening up freespace.h. Many variables renamed and
345 * reorganized. Added AlphaColors.[h,cpp]
347 * 3 10/07/98 6:27p Dave
348 * Globalized mission and campaign file extensions. Removed Silent Threat
349 * special code. Moved \cache \players and \multidata into the \data
352 * 2 10/07/98 10:53a Dave
355 * 1 10/07/98 10:50a Dave
357 * 333 10/02/98 3:22p Allender
358 * fix up the -connect option and fix the -port option
360 * 332 9/17/98 9:26p Dave
361 * Externalized new string.
363 * 331 9/17/98 3:08p Dave
364 * PXO to non-pxo game warning popup. Player icon stuff in create and join
365 * game screens. Upped server count refresh time in PXO to 35 secs (from
368 * 330 9/17/98 9:43a Allender
369 * removed an Assert that Dave called bogus.
371 * 329 9/16/98 6:54p Dave
372 * Upped max sexpression nodes to 1800 (from 1600). Changed FRED to sort
373 * the ship list box. Added code so that tracker stats are not stored with
376 * 328 9/15/98 7:24p Dave
377 * Minor UI changes. Localized bunch of new text.
379 * 327 9/15/98 4:03p Dave
380 * Changed readyroom and multi screens to display "st" icon for all
381 * missions with mission disk content (not necessarily just those that
382 * come with Silent Threat).
384 * 326 9/15/98 11:44a Dave
385 * Renamed builtin ships and wepaons appropriately in FRED. Put in scoring
386 * scale factors. Fixed standalone filtering of MD missions to non-MD
389 * 325 9/13/98 9:36p Dave
390 * Support for new info icons for multiplayer missions (from-volition,
391 * valid, mission disk, etc).
393 * 324 9/11/98 4:14p Dave
394 * Fixed file checksumming of < file_size. Put in more verbose kicking and
395 * PXO stats store reporting.
397 * 323 9/10/98 1:17p Dave
398 * Put in code to flag missions and campaigns as being MD or not in Fred
399 * and Freespace. Put in multiplayer support for filtering out MD
400 * missions. Put in multiplayer popups for warning of non-valid missions.
402 * 322 9/04/98 3:51p Dave
403 * Put in validated mission updating and application during stats
406 * 321 8/31/98 2:06p Dave
407 * Make cfile sort the ordering or vp files. Added support/checks for
408 * recognizing "mission disk" players.
410 * 320 8/21/98 1:15p Dave
411 * Put in log system hooks in useful places.
413 * 319 8/20/98 5:31p Dave
414 * Put in handy multiplayer logfile system. Now need to put in useful
415 * applications of it all over the code.
417 * 318 8/12/98 4:53p Dave
418 * Put in 32 bit checksumming for PXO missions. No validation on the
419 * actual tracker yet, though.
421 * 317 8/07/98 10:40a Allender
422 * new command line flags for starting netgames. Only starting currently
423 * works, and PXO isn't implemented yet
425 * 316 7/24/98 9:27a Dave
426 * Tidied up endgame sequencing by removing several old flags and
427 * standardizing _all_ endgame stuff with a single function call.
429 * 315 7/14/98 10:04a Allender
430 * fixed the countdown code to not be reliant on timer_get_fixed_seconds
432 * 314 7/10/98 5:04p Dave
433 * Fix connection speed bug on standalone server.
435 * 313 7/09/98 6:01p Dave
436 * Firsts full version of PXO updater. Put in stub for displaying
439 * 312 7/07/98 2:49p Dave
442 * 311 6/30/98 2:17p Dave
443 * Revised object update system. Removed updates for all weapons. Put
444 * button info back into control info packet.
446 * 310 6/13/98 9:32p Mike
447 * Kill last character in file which caused "Find in Files" to report the
448 * file as "not a text file."
455 #include <winsock.h> // for inet_addr()
457 #include <sys/types.h>
458 #include <sys/socket.h>
459 #include <netinet/in.h>
460 #include <arpa/inet.h>
465 #include "multiutil.h"
466 #include "multimsgs.h"
472 #include "gamesequence.h"
473 #include "freespace.h"
474 #include "contexthelp.h"
479 #include "missionshipchoice.h"
480 #include "multi_xfer.h"
482 #include "stand_gui.h"
483 #include "linklist.h"
484 #include "multiteamselect.h"
485 #include "missioncampaign.h"
492 #include "missiondebrief.h"
493 #include "multi_ingame.h"
494 #include "multi_kick.h"
495 #include "multi_data.h"
496 #include "multi_campaign.h"
497 #include "multi_team.h"
498 #include "multi_pinfo.h"
499 #include "multi_observer.h"
500 #include "multi_voice.h"
501 #include "multi_endgame.h"
502 #include "managepilot.h"
505 #include "objcollide.h"
507 #include "multi_pmsg.h"
508 #include "multi_obj.h"
509 #include "multi_log.h"
510 #include "alphacolors.h"
511 #include "animplay.h"
512 #include "multi_dogfight.h"
513 #include "missionpause.h"
515 // -------------------------------------------------------------------------------------------------------------
517 // MULTIPLAYER COMMON interface controls
520 // the common text info box stuff. This is lifted almost directly from Alans briefing code (minus the spiffy colored, scrolling
522 int Multi_common_text_coords[GR_NUM_RESOLUTIONS][4] = {
535 int Multi_common_text_max_display[GR_NUM_RESOLUTIONS] = {
544 #define MULTI_COMMON_TEXT_META_CHAR '$'
545 #define MULTI_COMMON_TEXT_MAX_LINE_LENGTH 100
546 #define MULTI_COMMON_TEXT_MAX_LINES 20
547 #define MULTI_COMMON_MAX_TEXT (MULTI_COMMON_TEXT_MAX_LINES * MULTI_COMMON_TEXT_MAX_LINE_LENGTH)
549 char Multi_common_all_text[MULTI_COMMON_MAX_TEXT];
550 char Multi_common_text[MULTI_COMMON_TEXT_MAX_LINES][MULTI_COMMON_TEXT_MAX_LINE_LENGTH];
552 int Multi_common_top_text_line = -1; // where to start displaying from
553 int Multi_common_num_text_lines = 0; // how many lines we have
555 void multi_common_scroll_text_up();
556 void multi_common_scroll_text_down();
557 void multi_common_move_to_bottom();
558 void multi_common_render_text();
559 void multi_common_split_text();
561 #define MAX_IP_STRING 255 // maximum length for ip string
563 void multi_common_scroll_text_up()
565 Multi_common_top_text_line--;
566 if ( Multi_common_top_text_line < 0 ) {
567 Multi_common_top_text_line = 0;
568 if ( !mouse_down(MOUSE_LEFT_BUTTON) )
569 gamesnd_play_iface(SND_GENERAL_FAIL);
572 gamesnd_play_iface(SND_SCROLL);
576 void multi_common_scroll_text_down()
578 Multi_common_top_text_line++;
579 if ( (Multi_common_num_text_lines - Multi_common_top_text_line) < Multi_common_text_max_display[gr_screen.res] ) {
580 Multi_common_top_text_line--;
581 if ( !mouse_down(MOUSE_LEFT_BUTTON) ){
582 gamesnd_play_iface(SND_GENERAL_FAIL);
585 gamesnd_play_iface(SND_SCROLL);
589 void multi_common_move_to_bottom()
591 // if there's nowhere to scroll down, do nothing
592 if(Multi_common_num_text_lines <= Multi_common_text_max_display[gr_screen.res]){
596 Multi_common_top_text_line = Multi_common_num_text_lines - Multi_common_text_max_display[gr_screen.res];
599 void multi_common_set_text(char *str,int auto_scroll)
602 // store the entire string as well
603 if(strlen(str) > MULTI_COMMON_MAX_TEXT){
606 strcpy(Multi_common_all_text,str);
609 // split the whole thing up
610 multi_common_split_text();
612 // scroll to the bottom if we're supposed to
614 multi_common_move_to_bottom();
618 void multi_common_add_text(char *str,int auto_scroll)
621 // store the entire string as well
622 if((strlen(str) + strlen(Multi_common_all_text)) > MULTI_COMMON_MAX_TEXT){
625 strcat(Multi_common_all_text,str);
628 // split the whole thing up
629 multi_common_split_text();
631 // scroll to the bottom if we're supposed to
633 multi_common_move_to_bottom();
637 void multi_common_split_text()
640 int n_chars[MAX_BRIEF_LINES];
641 char *p_str[MAX_BRIEF_LINES];
643 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);
644 Assert(n_lines != -1);
646 for ( i = 0; i < n_lines; i++ ) {
647 Assert(n_chars[i] < MULTI_COMMON_TEXT_MAX_LINE_LENGTH);
648 strncpy(Multi_common_text[i], p_str[i], n_chars[i]);
649 Multi_common_text[i][n_chars[i]] = 0;
650 drop_leading_white_space(Multi_common_text[i]);
653 Multi_common_top_text_line = 0;
654 Multi_common_num_text_lines = n_lines;
657 void multi_common_render_text()
659 int i, fh, line_count;
661 fh = gr_get_font_height();
664 gr_set_color_fast(&Color_text_normal);
665 for ( i = Multi_common_top_text_line; i < Multi_common_num_text_lines; i++ ) {
666 if ( line_count >= Multi_common_text_max_display[gr_screen.res] ){
669 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]);
673 if ( (Multi_common_num_text_lines - Multi_common_top_text_line) > Multi_common_text_max_display[gr_screen.res] ) {
674 gr_set_color_fast(&Color_bright_red);
675 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));
679 // common notification messaging stuff
680 #define MULTI_COMMON_NOTIFY_TIME 3500
681 int Multi_common_join_y[GR_NUM_RESOLUTIONS] = {
685 int Multi_common_create_y[GR_NUM_RESOLUTIONS] = {
690 int Multi_common_jw_y[GR_NUM_RESOLUTIONS] = {
695 int Multi_common_msg_y[GR_NUM_RESOLUTIONS] = {
700 char Multi_common_notify_text[200];
701 int Multi_common_notify_stamp;
703 void multi_common_notify_init()
705 strcpy(Multi_common_notify_text,"");
706 Multi_common_notify_stamp = -1;
709 // add a notification string, drawing appropriately depending on the state/screen we're in
710 void multi_common_add_notify(char *str)
713 strcpy(Multi_common_notify_text,str);
714 Multi_common_notify_stamp = timestamp(MULTI_COMMON_NOTIFY_TIME);
718 // process/display notification messages
719 void multi_common_notify_do()
721 if(Multi_common_notify_stamp != -1){
722 if(timestamp_elapsed(Multi_common_notify_stamp)){
723 Multi_common_notify_stamp = -1;
726 gr_get_string_size(&w,&h,Multi_common_notify_text);
727 gr_set_color_fast(&Color_white);
729 // determine where it should be placed based upon which screen we're on
731 switch(gameseq_get_state()){
732 case GS_STATE_MULTI_JOIN_GAME :
733 y = Multi_common_join_y[gr_screen.res];
735 case GS_STATE_MULTI_HOST_SETUP :
736 y = Multi_common_create_y[gr_screen.res];
738 case GS_STATE_MULTI_CLIENT_SETUP :
739 y = Multi_common_jw_y[gr_screen.res];
741 case GS_STATE_MULTI_START_GAME :
742 y = Multi_common_msg_y[gr_screen.res];
746 gr_string((gr_screen.max_w - w)/2, y, Multi_common_notify_text);
753 int Multi_common_icons[MULTI_NUM_COMMON_ICONS];
755 char *Multi_common_icon_names[MULTI_NUM_COMMON_ICONS] = {
756 "DotRed", // voice denied
757 "DotGreen", // voice recording
758 "OvalGreen", // team 0
759 "OvalGreen01", // team 0 select
761 "OvalRed01", // team 1 select
762 "mp_coop", // coop mission
763 "mp_teams", // TvT mission
764 "mp_furball", // furball mission
765 "icon-volition", // volition mission
766 "icon-valid", // mission is valid
771 "icon-silent" // SilentThreat
775 // width and height of the icons
776 int Multi_common_icon_dims[MULTI_NUM_COMMON_ICONS][2] = {
777 {11, 11}, // voice denied
778 {11, 11}, // voice recording
780 {11, 11}, // team 0 select
782 {11, 11}, // team 1 select
785 {18, 11}, // mp furball
786 {9, 9}, // volition mission
787 {8, 8}, // mission is valid
792 {16, 7} // silent threat
796 void multi_load_common_icons()
801 for(idx=0; idx<MULTI_NUM_COMMON_ICONS; idx++){
802 Multi_common_icons[idx] = -1;
803 Multi_common_icons[idx] = bm_load(Multi_common_icon_names[idx]);
807 void multi_unload_common_icons()
812 for(idx=0; idx<MULTI_NUM_COMMON_ICONS; idx++){
813 if(Multi_common_icons[idx] != -1){
814 bm_unload(Multi_common_icons[idx]);
815 Multi_common_icons[idx] = -1;
820 // display any relevant voice status icons
821 void multi_common_voice_display_status()
823 switch(multi_voice_status()){
824 // i have been denied the voice token
825 case MULTI_VOICE_STATUS_DENIED:
826 if(Multi_common_icons[MICON_VOICE_DENIED] != -1){
827 gr_set_bitmap(Multi_common_icons[MICON_VOICE_DENIED]);
832 // i am currently recording
833 case MULTI_VOICE_STATUS_RECORDING:
834 if(Multi_common_icons[MICON_VOICE_RECORDING] != -1){
835 gr_set_bitmap(Multi_common_icons[MICON_VOICE_RECORDING]);
840 // i am currently playing back sound
841 case MULTI_VOICE_STATUS_PLAYING:
844 // the system is currently idle
845 case MULTI_VOICE_STATUS_IDLE:
851 // palette initialization stuff
852 #define MULTI_COMMON_PALETTE_FNAME "InterfacePalette"
855 int Multi_common_interface_palette = -1;
857 void multi_common_load_palette();
858 void multi_common_set_palette();
859 void multi_common_unload_palette();
861 // load in the palette if it doesn't already exist
862 void multi_common_load_palette()
864 if(Multi_common_interface_palette != -1){
868 Multi_common_interface_palette = bm_load(MULTI_COMMON_PALETTE_FNAME);
869 if(Multi_common_interface_palette == -1){
870 nprintf(("Network","Error loading multiplayer common palette!\n"));
874 // set the common palette to be the active one
875 void multi_common_set_palette()
877 // if the palette is not loaded yet, do so now
878 if(Multi_common_interface_palette == -1){
879 multi_common_load_palette();
882 if(Multi_common_interface_palette != -1){
883 #ifndef HARDWARE_ONLY
884 palette_use_bm_palette(Multi_common_interface_palette);
889 // unload the bitmap palette
890 void multi_common_unload_palette()
892 if(Multi_common_interface_palette != -1){
893 bm_unload(Multi_common_interface_palette);
894 Multi_common_interface_palette = -1;
898 void multi_common_verify_cd()
901 // otherwise, call the freespace function to determine if we have a cd
904 if((find_freespace_cd(FS_CDROM_VOLUME_1) >= 0) || (find_freespace_cd(FS_CDROM_VOLUME_2) >= 0) ){
906 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) ){
916 // -------------------------------------------------------------------------------------------------------------
918 // MULTIPLAYER JOIN SCREEN
921 #define MULTI_JOIN_NUM_BUTTONS 11
925 #define MULTI_JOIN_PALETTE "InterfacePalette"
927 static char *Multi_join_bitmap_fname[GR_NUM_RESOLUTIONS] = {
928 "MultiJoin", // GR_640
929 "2_MultiJoin" // GR_1024
932 static char *Multi_join_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
933 "MultiJoin-M", // GR_640
934 "2_MultiJoin-M" // GR_1024
940 char *Mj_slider_name[GR_NUM_RESOLUTIONS] = {
944 int Mj_slider_coords[GR_NUM_RESOLUTIONS][4] = {
955 #define MJ_SCROLL_UP 0
956 #define MJ_SCROLL_DOWN 1
958 #define MJ_SCROLL_INFO_UP 3
959 #define MJ_SCROLL_INFO_DOWN 4
960 #define MJ_JOIN_OBSERVER 5
961 #define MJ_START_GAME 6
967 // uses MULTI_JOIN_REFRESH_TIME as its timestamp
968 int Multi_join_glr_stamp;
970 #define MULTI_JOIN_PING_TIME 15000 // how often we ping all the known servers
971 int Multi_join_ping_stamp;
972 UI_WINDOW Multi_join_window; // the window object for the join screen
973 UI_BUTTON Multi_join_select_button; // for selecting list items
975 UI_SLIDER2 Multi_join_slider; // handy dandy slider
977 int Multi_join_bitmap; // the background bitmap
979 ui_button_info Multi_join_buttons[GR_NUM_RESOLUTIONS][MULTI_JOIN_NUM_BUTTONS] = {
982 ui_button_info("MJ_00", 0, 85, -1, -1, 0),
983 ui_button_info("MJ_01", 0, 125, -1, -1, 1),
984 ui_button_info("MJ_03", 20, 324, -1, -1, 3),
985 ui_button_info("MJ_04", 0, 399, -1, -1, 4),
986 ui_button_info("MJ_05", 0, 436, -1, -1, 5),
987 ui_button_info("MJ_15", 450, 323, -1, -1, 15),
988 ui_button_info("MJ_10", 519, 323, -1, -1, 10),
989 ui_button_info("MJ_06", 574, 323, -1, -1, 6),
990 ui_button_info("MJ_08", 470, 427, -1, -1, 8),
991 ui_button_info("MJ_09", 448, 454, -1, -1, 9),
992 ui_button_info("MJ_07", 563, 411, -1, -1, 7),
994 ui_button_info( "MJ_00", 1, 57, -1, -1, 0 ), // scroll up
995 ui_button_info( "MJ_02", 1, 297, -1, -1, 2 ), // scroll down
996 ui_button_info( "MJ_03", 10, 338, 65, 364, 3 ), // refresh
997 ui_button_info( "MJ_04", 1, 405, -1, -1, 4 ), // scroll info up
998 ui_button_info( "MJ_05", 1, 446, -1, -1, 5 ), // scroll info down
999 ui_button_info( "MJ_06", 489, 339, -1, -1, 6 ), // join as observer
1000 ui_button_info( "MJ_07", 538, 339, -1, -1, 7 ), // create game
1001 ui_button_info( "MJ_08", 583, 339, 588, 376, 8 ), // cancel
1002 ui_button_info( "MJ_09", 534, 426, -1, -1, 9 ), // help
1003 ui_button_info( "MJ_10", 534, 454, -1, -1, 10 ), // options
1004 ui_button_info( "MJ_11", 571, 426, 589, 416, 11 ), // join
1008 ui_button_info( "2_MJ_00", 2, 92, -1, -1, 0 ), // scroll up
1009 ui_button_info( "2_MJ_02", 2, 475, -1, -1, 2 ), // scroll down
1010 ui_button_info( "2_MJ_03", 16, 541, 104, 582, 3 ), // refresh
1011 ui_button_info( "2_MJ_04", 2, 648, -1, -1, 4 ), // scroll info up
1012 ui_button_info( "2_MJ_05", 2, 713, -1, -1, 5 ), // scroll info down
1013 ui_button_info( "2_MJ_06", 783, 542, -1, -1, 6 ), // join as observer
1014 ui_button_info( "2_MJ_07", 861, 542, -1, -1, 7 ), // create game
1015 ui_button_info( "2_MJ_08", 933, 542, 588, 376, 8 ), // cancel
1016 ui_button_info( "2_MJ_09", 854, 681, -1, -1, 9 ), // help
1017 ui_button_info( "2_MJ_10", 854, 727, -1, -1, 10 ), // options
1018 ui_button_info( "2_MJ_11", 914, 681, 937, 668, 11 ), // join
1023 #define MULTI_JOIN_NUM_TEXT 0
1025 #define MULTI_JOIN_NUM_TEXT 13
1028 UI_XSTR Multi_join_text[GR_NUM_RESOLUTIONS][MULTI_JOIN_NUM_TEXT] = {
1030 // not needed for FS1
1032 {"Refresh", 1299, 65, 364, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_REFRESH].button},
1033 {"Join as", 1300, 476, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_JOIN_OBSERVER].button},
1034 {"Observer", 1301, 467, 385, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_JOIN_OBSERVER].button},
1035 {"Create", 1408, 535, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_START_GAME].button},
1036 {"Game", 1302, 541, 385, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_START_GAME].button},
1037 {"Cancel", 387, 588, 376, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[0][MJ_CANCEL].button},
1038 {"Help", 928, 479, 436, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_HELP].button},
1039 {"Options", 1036, 479, 460, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_OPTIONS].button},
1040 {"Join", 1303, 589, 416, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[0][MJ_ACCEPT].button},
1041 {"Status", 1304, 37, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
1042 {"Server", 1305, 116, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
1043 {"Players", 1306, 471, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
1044 {"Ping", 1307, 555, 37, UI_XSTR_COLOR_GREEN, -1, NULL}
1048 // not needed for FS1
1050 {"Refresh", 1299, 104, 582, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_REFRESH].button},
1051 {"Join as", 1300, 783, 602, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_JOIN_OBSERVER].button},
1052 {"Observer", 1301, 774, 611, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_JOIN_OBSERVER].button},
1053 {"Create", 1408, 868, 602, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_START_GAME].button},
1054 {"Game", 1302, 872, 611, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_START_GAME].button},
1055 {"Cancel", 387, 941, 602, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[1][MJ_CANCEL].button},
1056 {"Help", 928, 782, 699, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_HELP].button},
1057 {"Options", 1036, 782, 736, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_OPTIONS].button},
1058 {"Join", 1303, 937, 668, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[1][MJ_ACCEPT].button},
1059 {"Status", 1304, 60, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
1060 {"Server", 1305, 186, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
1061 {"Players", 1306, 753, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
1062 {"Ping", 1307, 888, 60, UI_XSTR_COLOR_GREEN, -1, NULL}
1067 // constants for coordinate look ups
1068 #define MJ_X_COORD 0
1069 #define MJ_Y_COORD 1
1070 #define MJ_W_COORD 2
1071 #define MJ_H_COORD 3
1073 #define MULTI_JOIN_SENT_WAIT 10000 // wait this long since a join was sent to allow another
1074 int Multi_join_sent_stamp;
1076 // game information text areas
1077 int Mj_max_game_items[GR_NUM_RESOLUTIONS] = {
1082 int Mj_list_y[GR_NUM_RESOLUTIONS] = {
1087 int Mj_status_coords[GR_NUM_RESOLUTIONS][4] = {
1096 int Mj_game_icon_coords[GR_NUM_RESOLUTIONS][3] = {
1105 int Mj_speed_coords[GR_NUM_RESOLUTIONS][4] = {
1114 int Mj_game_name_coords[GR_NUM_RESOLUTIONS][4] = {
1123 int Mj_players_coords[GR_NUM_RESOLUTIONS][4] = {
1132 int Mj_ping_coords[GR_NUM_RESOLUTIONS][4] = {
1141 // game speed labels
1142 #define MJ_NUM_SPEED_LABELS 5
1143 char *Multi_join_speed_labels[MJ_NUM_SPEED_LABELS] = {
1150 color *Multi_join_speed_colors[MJ_NUM_SPEED_LABELS] = {
1153 &Color_bright_green,
1154 &Color_bright_green,
1158 int Mj_cd_coords[GR_NUM_RESOLUTIONS] = {
1163 // extents of the entire boundable game info region
1164 // NOTE : these numbers are completely empirical
1165 #define MJ_PING_GREEN 160
1166 #define MJ_PING_YELLOW 300
1168 int Mj_list_area_coords[GR_NUM_RESOLUTIONS][4] = {
1177 // PXO channel filter
1178 #define MJ_PXO_FILTER_Y 0
1180 // special chars to indicate various status modes for servers
1181 #define MJ_CHAR_STANDALONE "*"
1182 #define MJ_CHAR_CAMPAIGN "c"
1185 // various interface indices
1186 int Multi_join_list_start; // where to start displaying from
1187 active_game *Multi_join_list_start_item; // a pointer to the corresponding active_game
1188 int Multi_join_list_selected; // which item we have selected
1189 active_game *Multi_join_selected_item; // a pointer to the corresponding active_game
1191 // use this macro to modify the list start
1192 #define MJ_LIST_START_INC() do { Multi_join_list_start++; } while(0);
1193 #define MJ_LIST_START_DEC() do { Multi_join_list_start--; } while(0);
1194 #define MJ_LIST_START_SET(vl) do { Multi_join_list_start = vl; } while(0);
1196 // if we should be sending a join request at the end of the frame
1197 int Multi_join_should_send = -1;
1199 // master tracker details
1200 int Multi_join_frame_count; // keep a count of frames displayed
1201 int Multi_join_mt_tried_verify; // already tried verifying the pilot with the tracker
1203 // data stuff for auto joining a game
1204 #define MULTI_AUTOJOIN_JOIN_STAMP 2000
1205 #define MULTI_AUTOJOIN_QUERY_STAMP 2000
1207 int Multi_did_autojoin;
1208 net_addr Multi_autojoin_addr;
1209 int Multi_autojoin_join_stamp;
1210 int Multi_autojoin_query_stamp;
1213 join_request Multi_join_request;
1215 // LOCAL function definitions
1216 void multi_join_check_buttons();
1217 void multi_join_button_pressed(int n);
1218 void multi_join_display_games();
1219 void multi_join_blit_game_status(active_game *game, int y);
1220 void multi_join_load_tcp_addrs();
1221 void multi_join_do_netstuff();
1222 void multi_join_ping_all();
1223 void multi_join_process_select();
1224 void multi_join_list_scroll_up();
1225 void multi_join_list_scroll_down();
1226 void multi_join_list_page_up();
1227 void multi_join_list_page_down();
1228 active_game *multi_join_get_game(int n);
1229 void multi_join_cull_timeouts();
1230 void multi_join_handle_item_cull(active_game *item, int item_index);
1231 void multi_join_send_join_request(int as_observer);
1232 void multi_join_create_game();
1233 void multi_join_blit_top_stuff();
1234 int multi_join_maybe_warn();
1235 int multi_join_warn_pxo();
1236 void multi_join_blit_protocol();
1240 active_game ag, *newitem;;
1243 dc_get_arg(ARG_INT);
1244 for(idx=0; idx<Dc_arg_int; idx++){
1245 // stuff some fake info
1246 memset(&ag, 0, sizeof(active_game));
1247 sprintf(ag.name, "Game %d", idx);
1248 ag.version = MULTI_FS_SERVER_VERSION;
1249 ag.comp_version = MULTI_FS_SERVER_VERSION;
1250 ag.server_addr.addr[0] = (char)idx;
1251 ag.flags = (AG_FLAG_COOP | AG_FLAG_FORMING | AG_FLAG_STANDALONE);
1254 newitem = multi_update_active_games(&ag);
1256 // timestamp it so we get random timeouts
1257 if(newitem != NULL){
1258 // newitem->heard_from_timer = timestamp((int)frand_range(500.0f, 10000.0f));
1263 void multi_join_notify_new_game()
1266 // reset the # of items
1267 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);
1268 Multi_join_slider.force_currentItem(Multi_join_list_start);
1272 int multi_join_autojoin_do()
1274 // if we have an active game on the list, then return a positive value so that we
1275 // can join the game
1276 if ( Active_game_head && (Active_game_count > 0) ) {
1277 Multi_join_selected_item = Active_game_head;
1281 // send out a server_query again
1282 if ( timestamp_elapsed(Multi_autojoin_query_stamp) ) {
1283 send_server_query(&Multi_autojoin_addr);
1284 Multi_autojoin_query_stamp = timestamp(MULTI_AUTOJOIN_QUERY_STAMP);
1290 void multi_join_game_init()
1294 // do the multiplayer init stuff - multi_level_init() now does all net_player zeroing.
1295 // setup various multiplayer things
1296 Assert( Game_mode & GM_MULTIPLAYER );
1297 Assert( Net_player != NULL );
1299 switch (Multi_options_g.protocol) {
1301 ADDRESS_LENGTH = IPX_ADDRESS_LENGTH;
1302 PORT_LENGTH = IPX_PORT_LENGTH;
1306 ADDRESS_LENGTH = IP_ADDRESS_LENGTH;
1307 PORT_LENGTH = IP_PORT_LENGTH;
1316 memset( &Netgame, 0, sizeof(Netgame) );
1319 Net_player->flags |= NETINFO_FLAG_DO_NETWORKING;
1320 Net_player->player = Player;
1321 memcpy(&Net_player->p_info.addr,&Psnet_my_addr,sizeof(net_addr));
1323 // check for the existence of a CD
1324 multi_common_verify_cd();
1326 // load my local netplayer options
1327 multi_options_local_load(&Net_player->p_info.options, Net_player);
1333 common_set_interface_palette(MULTI_JOIN_PALETTE);
1336 // destroy any chatbox contents which previously existed (from another game)
1339 // create the interface window
1340 Multi_join_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
1341 Multi_join_window.set_mask_bmap(Multi_join_bitmap_mask_fname[gr_screen.res]);
1343 // load the background bitmap
1344 Multi_join_bitmap = bm_load(Multi_join_bitmap_fname[gr_screen.res]);
1345 if(Multi_join_bitmap < 0){
1346 // we failed to load the bitmap - this is very bad
1350 // intialize the endgame system
1351 multi_endgame_init();
1353 // initialize the common notification messaging
1354 multi_common_notify_init();
1356 // initialize the common text area
1357 multi_common_set_text("");
1359 // load and use the common interface palette
1360 multi_common_load_palette();
1361 multi_common_set_palette();
1363 // load the help overlay
1364 help_overlay_load(MULTI_JOIN_OVERLAY);
1365 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1367 // do TCP and VMT specific initialization
1368 if(Multi_options_g.protocol == NET_TCP){
1369 // if this is a TCP (non tracker) game, we'll load up our default address list right now
1370 multi_join_load_tcp_addrs();
1373 // initialize any and all timestamps
1374 Multi_join_glr_stamp = -1;
1375 Multi_join_ping_stamp = -1;
1376 Multi_join_sent_stamp = -1;
1378 // reset frame count
1379 Multi_join_frame_count = 0;
1381 // haven't tried to verify on the tracker yet.
1382 Multi_join_mt_tried_verify = 0;
1384 // clear our all game lists to save hassles
1385 multi_join_clear_game_list();
1387 // create the interface buttons
1388 for(idx=0; idx<MULTI_JOIN_NUM_BUTTONS; idx++){
1389 // create the object
1390 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);
1392 // set the sound to play when highlighted
1393 Multi_join_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
1395 // set the ani for the button
1396 Multi_join_buttons[gr_screen.res][idx].button.set_bmaps(Multi_join_buttons[gr_screen.res][idx].filename);
1399 Multi_join_buttons[gr_screen.res][idx].button.link_hotspot(Multi_join_buttons[gr_screen.res][idx].hotspot);
1403 for(idx=0; idx<MULTI_JOIN_NUM_TEXT; idx++){
1404 Multi_join_window.add_XSTR(&Multi_join_text[gr_screen.res][idx]);
1407 Multi_join_should_send = -1;
1409 // close any previously open chatbox
1412 // create the list item select button
1413 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);
1414 Multi_join_select_button.hide();
1418 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);
1421 // if starting a network game, then go to the create game screen
1422 if ( Cmdline_start_netgame ) {
1423 multi_join_create_game();
1424 } else if ( Cmdline_connect_addr != NULL ) {
1429 // joining a game. Send a join request to the given IP address, and wait for the return.
1430 memset( &Multi_autojoin_addr, 0, sizeof(net_addr) );
1431 Multi_autojoin_addr.type = NET_TCP;
1433 // create the address, looking out for port number at the end
1434 port_num = DEFAULT_GAME_PORT;
1435 p = strrchr(Cmdline_connect_addr, ':');
1439 port_num = (short)atoi(p);
1441 ip_addr = inet_addr(Cmdline_connect_addr);
1442 memcpy(Multi_autojoin_addr.addr, &ip_addr, 4);
1443 Multi_autojoin_addr.port = port_num;
1445 send_server_query(&Multi_autojoin_addr);
1446 Multi_autojoin_query_stamp = timestamp(MULTI_AUTOJOIN_QUERY_STAMP);
1447 Multi_did_autojoin = 0;
1451 void multi_join_clear_game_list()
1454 Multi_join_list_selected = -1;
1455 Multi_join_selected_item = NULL;
1456 MJ_LIST_START_SET(-1);
1457 Multi_join_list_start_item = NULL;
1459 // free up the active game list
1460 multi_free_active_games();
1462 // initialize the active game list
1463 Active_game_head = NULL;
1464 Active_game_count = 0;
1467 void multi_join_game_do_frame()
1469 // check the status of our reliable socket. If not valid, popup error and return to main menu
1470 // I put this code here to avoid nasty gameseq issues with states. Also, we will have nice
1471 // background for the popup
1472 if ( !psnet_rel_check() ) {
1473 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));
1474 gameseq_post_event(GS_EVENT_MAIN_MENU);
1478 // return here since we will be moving to the next stage anyway -- I don't want to see the backgrounds of
1479 // all the screens for < 1 second for every screen we automatically move to.
1480 if ( Cmdline_start_netgame ) {
1484 // when joining a network game, wait for the server query to come back, and then join the game
1485 if ( Cmdline_connect_addr != NULL ) {
1488 if ( !Multi_did_autojoin ) {
1489 rval = popup_till_condition(multi_join_autojoin_do, XSTR("&Cancel", 779), XSTR("Joining netgame", 1500) );
1491 // cancel was hit. Send the user back to the main hall
1492 gameseq_post_event(GS_EVENT_MAIN_MENU);
1493 Cmdline_connect_addr = NULL; // reset this value.
1496 // when we get here, we have the data -- join the game.
1497 multi_join_send_join_request(0);
1498 Multi_autojoin_join_stamp = timestamp(MULTI_AUTOJOIN_JOIN_STAMP);
1499 Multi_did_autojoin = 1;
1502 if ( timestamp_elapsed(Multi_autojoin_join_stamp) ) {
1503 multi_join_send_join_request(0);
1504 Multi_autojoin_join_stamp = timestamp(MULTI_AUTOJOIN_JOIN_STAMP);
1510 // reset the should send var
1511 Multi_join_should_send = -1;
1513 int k = Multi_join_window.process();
1515 // process any keypresses
1518 if(help_overlay_active(MULTI_JOIN_OVERLAY)){
1519 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1521 gameseq_post_event(GS_EVENT_MAIN_MENU);
1522 gamesnd_play_iface(SND_USER_SELECT);
1526 // page up the game list
1528 multi_join_list_page_up();
1530 Multi_join_slider.force_currentItem(Multi_join_list_start);
1535 multi_pinfo_popup(Net_player);
1538 // page down the game list
1540 multi_join_list_page_down();
1542 Multi_join_slider.force_currentItem(Multi_join_list_start);
1546 // send out a ping-all
1548 multi_join_ping_all();
1549 Multi_join_ping_stamp = timestamp(MULTI_JOIN_PING_TIME);
1552 // shortcut to start a game
1554 multi_join_create_game();
1557 // scroll the game list up
1559 multi_join_list_scroll_up();
1561 Multi_join_slider.force_currentItem(Multi_join_list_start);
1565 // scroll the game list down
1567 multi_join_list_scroll_down();
1569 Multi_join_slider.force_currentItem(Multi_join_list_start);
1574 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
1575 help_overlay_set_state(MULTI_JOIN_OVERLAY, 0);
1578 // do any network related stuff
1579 multi_join_do_netstuff();
1581 // process any button clicks
1582 multi_join_check_buttons();
1584 // process any list selection stuff
1585 multi_join_process_select();
1587 // draw the background, etc
1589 GR_MAYBE_CLEAR_RES(Multi_join_bitmap);
1590 if(Multi_join_bitmap != -1){
1591 gr_set_bitmap(Multi_join_bitmap);
1594 Multi_join_window.draw();
1596 // display the active games
1597 multi_join_display_games();
1599 // display any text in the info area
1600 multi_common_render_text();
1602 // display any pending notification messages
1603 multi_common_notify_do();
1605 // blit the CD icon and any PXO filter stuff
1606 multi_join_blit_top_stuff();
1608 // draw the help overlay
1609 help_overlay_maybe_blit(MULTI_JOIN_OVERLAY);
1614 // if we are supposed to be sending a join request
1615 if(Multi_join_should_send != -1){
1616 multi_join_send_join_request(Multi_join_should_send);
1618 Multi_join_should_send = -1;
1620 // increment the frame count
1621 Multi_join_frame_count++;
1624 void multi_join_game_close()
1626 // unload any bitmaps
1627 if(!bm_unload(Multi_join_bitmap)){
1628 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_join_bitmap_fname[gr_screen.res]));
1631 // unload the help overlay
1632 help_overlay_unload(MULTI_JOIN_OVERLAY);
1634 // free up the active game list
1635 multi_free_active_games();
1637 // destroy the UI_WINDOW
1638 Multi_join_window.destroy();
1641 common_free_interface_palette();
1645 void multi_join_check_buttons()
1648 for(idx=0;idx<MULTI_JOIN_NUM_BUTTONS;idx++){
1649 // we only really need to check for one button pressed at a time, so we can break after
1651 if(Multi_join_buttons[gr_screen.res][idx].button.pressed()){
1652 multi_join_button_pressed(idx);
1658 void multi_join_button_pressed(int n)
1662 // if we're player PXO, go back there
1663 gameseq_post_event(GS_EVENT_MAIN_MENU);
1664 gamesnd_play_iface(SND_USER_SELECT);
1667 if(Active_game_count <= 0){
1668 multi_common_add_notify(XSTR("No games found!",757));
1669 gamesnd_play_iface(SND_GENERAL_FAIL);
1670 } else if(Multi_join_list_selected == -1){
1671 multi_common_add_notify(XSTR("No game selected!",758));
1672 gamesnd_play_iface(SND_GENERAL_FAIL);
1673 } else if((Multi_join_sent_stamp != -1) && !timestamp_elapsed(Multi_join_sent_stamp)){
1674 multi_common_add_notify(XSTR("Still waiting on previous join request!",759));
1675 gamesnd_play_iface(SND_GENERAL_FAIL);
1677 // otherwise, if he's already played PXO games, warn him
1679 if(Player->flags & PLAYER_FLAGS_HAS_PLAYED_PXO){
1680 if(!multi_join_warn_pxo()){
1686 // send the join request here
1687 Assert(Multi_join_selected_item != NULL);
1689 // send a join request packet
1690 Multi_join_should_send = 0;
1692 gamesnd_play_iface(SND_COMMIT_PRESSED);
1698 if(!help_overlay_active(MULTI_JOIN_OVERLAY)){
1699 help_overlay_set_state(MULTI_JOIN_OVERLAY,1);
1701 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1705 // scroll the game list up
1707 multi_join_list_scroll_up();
1709 Multi_join_slider.force_currentItem(Multi_join_list_start);
1713 // scroll the game list down
1714 case MJ_SCROLL_DOWN:
1715 multi_join_list_scroll_down();
1717 Multi_join_slider.force_currentItem(Multi_join_list_start);
1721 // scroll the info text box up
1722 case MJ_SCROLL_INFO_UP:
1723 multi_common_scroll_text_up();
1726 // scroll the info text box down
1727 case MJ_SCROLL_INFO_DOWN:
1728 multi_common_scroll_text_down();
1731 // go to the options screen
1733 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
1736 // go to the start game screen
1738 multi_join_create_game();
1741 // refresh the game/server list
1743 gamesnd_play_iface(SND_USER_SELECT);
1744 broadcast_game_query();
1747 // join a game as an observer
1748 case MJ_JOIN_OBSERVER:
1749 if(Active_game_count <= 0){
1750 multi_common_add_notify(XSTR("No games found!",757));
1751 gamesnd_play_iface(SND_GENERAL_FAIL);
1752 } else if(Multi_join_list_selected == -1){
1753 multi_common_add_notify(XSTR("No game selected!",758));
1754 gamesnd_play_iface(SND_GENERAL_FAIL);
1755 } else if((Multi_join_sent_stamp != -1) && !timestamp_elapsed(Multi_join_sent_stamp)){
1756 multi_common_add_notify(XSTR("Still waiting on previous join request!",759));
1757 gamesnd_play_iface(SND_GENERAL_FAIL);
1759 // send the join request here
1760 Assert(Multi_join_selected_item != NULL);
1762 Multi_join_should_send = 1;
1764 gamesnd_play_iface(SND_COMMIT_PRESSED);
1769 multi_common_add_notify(XSTR("Not implemented yet!",760));
1770 gamesnd_play_iface(SND_GENERAL_FAIL);
1775 // display all relevant info for active games
1776 void multi_join_display_games()
1778 active_game *moveup = Multi_join_list_start_item;
1782 int y_start = Mj_list_y[gr_screen.res];
1787 // blit the game status (including text and type icon)
1788 multi_join_blit_game_status(moveup,y_start);
1790 // get the connection type
1791 con_type = (moveup->flags & AG_FLAG_CONNECTION_SPEED_MASK) >> AG_FLAG_CONNECTION_BIT;
1792 if((con_type > 4) || (con_type < 0)){
1796 // display the connection speed
1798 strcpy(str, Multi_join_speed_labels[con_type]);
1799 gr_set_color_fast(Multi_join_speed_colors[con_type]);
1800 gr_string(Mj_speed_coords[gr_screen.res][MJ_X_COORD], y_start, str);
1802 // we'll want to have different colors for highlighted items, etc.
1803 if(moveup == Multi_join_selected_item){
1804 gr_set_color_fast(&Color_text_selected);
1806 gr_set_color_fast(&Color_text_normal);
1809 // display the game name, adding appropriate status chars
1811 if(moveup->flags & AG_FLAG_STANDALONE){
1812 strcat(str,MJ_CHAR_STANDALONE);
1814 if(moveup->flags & AG_FLAG_CAMPAIGN){
1815 strcat(str,MJ_CHAR_CAMPAIGN);
1818 // tack on the actual server name
1820 strcat(str,moveup->name);
1821 if(strlen(moveup->mission_name) > 0){
1823 strcat(str,moveup->mission_name);
1826 // make sure the string fits in the display area and draw it
1827 gr_force_fit_string(str,200,Mj_game_name_coords[gr_screen.res][MJ_W_COORD]);
1828 gr_string(Mj_game_name_coords[gr_screen.res][MJ_X_COORD],y_start,str);
1830 // display the ping time
1831 if(moveup->ping.ping_avg > 0){
1832 if(moveup->ping.ping_avg > 1000){
1833 gr_set_color_fast(&Color_bright_red);
1834 strcpy(str,XSTR("> 1 sec",761));
1836 // set the appropriate ping time color indicator
1837 if(moveup->ping.ping_avg > MJ_PING_YELLOW){
1838 gr_set_color_fast(&Color_bright_red);
1839 } else if(moveup->ping.ping_avg > MJ_PING_YELLOW){
1840 gr_set_color_fast(&Color_bright_yellow);
1842 gr_set_color_fast(&Color_bright_green);
1845 sprintf(str,"%d",moveup->ping.ping_avg);
1846 strcat(str,XSTR(" ms",762)); // [[ Milliseconds ]]
1849 gr_string(Mj_ping_coords[gr_screen.res][MJ_X_COORD],y_start,str);
1852 // display the number of players (be sure to center it)
1853 if(moveup == Multi_join_selected_item){
1854 gr_set_color_fast(&Color_text_selected);
1856 gr_set_color_fast(&Color_text_normal);
1858 sprintf(str,"%d",moveup->num_players);
1859 gr_get_string_size(&w,&h,str);
1860 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);
1864 moveup = moveup->next;
1865 } while((moveup != Active_game_head) && (count < Mj_max_game_items[gr_screen.res]));
1867 // if there are no items on the list, display this info
1869 gr_set_color_fast(&Color_bright);
1870 gr_string(Mj_game_name_coords[gr_screen.res][MJ_X_COORD] - 30,y_start,XSTR("<No game servers found>",763));
1874 void multi_join_blit_game_status(active_game *game, int y)
1877 char status_text[25];
1879 // blit the proper icon
1881 switch( game->flags & AG_FLAG_TYPE_MASK ){
1884 if(Multi_common_icons[MICON_COOP] != -1){
1885 gr_set_bitmap(Multi_common_icons[MICON_COOP]);
1890 // team vs. team game
1892 if(Multi_common_icons[MICON_TVT] != -1){
1893 gr_set_bitmap(Multi_common_icons[MICON_TVT]);
1900 case AG_FLAG_DOGFIGHT:
1901 if(Multi_common_icons[MICON_DOGFIGHT] != -1){
1902 gr_set_bitmap(Multi_common_icons[MICON_DOGFIGHT]);
1908 // if we're supposed to draw a bitmap
1910 gr_bitmap(Mj_game_icon_coords[gr_screen.res][MJ_X_COORD],y-1);
1913 // blit the proper status text
1914 memset(status_text,0,25);
1916 switch( game->flags & AG_FLAG_STATE_MASK ){
1917 case AG_FLAG_FORMING:
1918 gr_set_color_fast(&Color_bright_green);
1919 strcpy(status_text,XSTR("Forming",764));
1921 case AG_FLAG_BRIEFING:
1922 gr_set_color_fast(&Color_bright_red);
1923 strcpy(status_text,XSTR("Briefing",765));
1925 case AG_FLAG_DEBRIEF:
1926 gr_set_color_fast(&Color_bright_red);
1927 strcpy(status_text,XSTR("Debrief",766));
1930 gr_set_color_fast(&Color_bright_red);
1931 strcpy(status_text,XSTR("Paused",767));
1933 case AG_FLAG_IN_MISSION:
1934 gr_set_color_fast(&Color_bright_red);
1935 strcpy(status_text,XSTR("Playing",768));
1938 gr_set_color_fast(&Color_bright);
1939 strcpy(status_text,XSTR("Unknown",769));
1942 gr_get_string_size(&str_w,NULL,status_text);
1943 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);
1946 // load in a list of active games from our tcp.cfg file
1947 void multi_join_load_tcp_addrs()
1949 char line[MAX_IP_STRING];
1954 // attempt to open the ip list file
1955 file = cfopen(IP_CONFIG_FNAME,"rt",CFILE_NORMAL,CF_TYPE_DATA);
1957 nprintf(("Network","Error loading tcp.cfg file!\n"));
1961 // free up any existing server list
1962 multi_free_server_list();
1964 // read in all the strings in the file
1965 while(!cfeof(file)){
1967 cfgets(line,MAX_IP_STRING,file);
1969 // strip off any newline character
1970 if(line[strlen(line) - 1] == '\n'){
1971 line[strlen(line) - 1] = '\0';
1974 // empty lines don't get processed
1975 if( (line[0] == '\0') || (line[0] == '\n') ){
1979 if ( !psnet_is_valid_ip_string(line) ) {
1980 nprintf(("Network","Invalid ip string (%s)\n",line));
1982 // copy the server ip address
1983 memset(&addr,0,sizeof(net_addr));
1984 addr.type = NET_TCP;
1985 psnet_string_to_addr(&addr,line);
1986 if ( addr.port == 0 ){
1987 addr.port = DEFAULT_GAME_PORT;
1990 // create a new server item on the list
1991 item = multi_new_server_item();
1993 memcpy(&item->server_addr,&addr,sizeof(net_addr));
2001 // do stuff like pinging servers, sending out requests, etc
2002 void multi_join_do_netstuff()
2004 // handle game query stuff
2005 if(Multi_join_glr_stamp == -1){
2006 broadcast_game_query();
2008 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
2009 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME_LOCAL);
2011 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME);
2014 // otherwise send out game query and restamp
2015 else if(timestamp_elapsed(Multi_join_glr_stamp)){
2016 broadcast_game_query();
2018 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
2019 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME_LOCAL);
2021 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME);
2025 // check to see if we've been accepted. If so, put up message saying so
2026 if ( Net_player->flags & (NETINFO_FLAG_ACCEPT_INGAME|NETINFO_FLAG_ACCEPT_CLIENT|NETINFO_FLAG_ACCEPT_HOST|NETINFO_FLAG_ACCEPT_OBSERVER) ) {
2027 multi_common_add_notify(XSTR("Accepted. Waiting for player data.",770));
2030 // check to see if any join packets we have sent have timed out
2031 if((Multi_join_sent_stamp != -1) && (timestamp_elapsed(Multi_join_sent_stamp))){
2032 Multi_join_sent_stamp = -1;
2033 multi_common_add_notify(XSTR("Join request timed out!",771));
2036 // check to see if we should be pinging everyone
2037 if((Multi_join_ping_stamp == -1) || (timestamp_elapsed(Multi_join_ping_stamp))){
2038 multi_join_ping_all();
2039 Multi_join_ping_stamp = timestamp(MULTI_JOIN_PING_TIME);
2043 multi_join_cull_timeouts();
2046 // evaluate a returned pong.
2047 void multi_join_eval_pong(net_addr *addr, fix pong_time)
2050 active_game *moveup = Active_game_head;
2055 if(psnet_same(&moveup->server_addr,addr)){
2057 multi_ping_eval_pong(&moveup->ping);
2061 moveup = moveup->next;
2063 } while(moveup != Active_game_head);
2066 // update the game's ping
2068 if(found && (moveup->ping_end > moveup->ping_start)){
2069 moveup->ping_time = f2fl(moveup->ping_end - moveup->ping_start);
2070 moveup->ping_start = -1;
2071 moveup->ping_end = -1;
2076 // ping all the server on the list
2077 void multi_join_ping_all()
2079 active_game *moveup = Active_game_head;
2084 moveup->ping_start = timer_get_fixed_seconds();
2085 moveup->ping_end = -1;
2086 send_ping(&moveup->server_addr);
2088 multi_ping_send(&moveup->server_addr,&moveup->ping);
2090 moveup = moveup->next;
2091 } while(moveup != Active_game_head);
2095 void multi_join_process_select()
2097 // if we don't have anything selected and there are items on the list - select the first one
2098 if((Multi_join_list_selected == -1) && (Active_game_count > 0)){
2099 Multi_join_list_selected = 0;
2100 Multi_join_selected_item = multi_join_get_game(0);
2101 MJ_LIST_START_SET(0);
2102 Multi_join_list_start_item = Multi_join_selected_item;
2104 // send a mission description request to this guy
2105 send_netgame_descript_packet(&Multi_join_selected_item->server_addr,0);
2106 multi_common_set_text("");
2108 // I sure hope this doesn't happen
2109 Assert(Multi_join_selected_item != NULL);
2112 // otherwise see if he's clicked on an item
2113 else if(Multi_join_select_button.pressed() && (Active_game_count > 0)){
2115 Multi_join_select_button.get_mouse_pos(NULL,&y);
2117 if(item + Multi_join_list_start < Active_game_count){
2118 gamesnd_play_iface(SND_IFACE_MOUSE_CLICK);
2120 Multi_join_list_selected = item + Multi_join_list_start;
2121 Multi_join_selected_item = multi_join_get_game(Multi_join_list_selected);
2123 // I sure hope this doesn't happen
2124 Assert(Multi_join_selected_item != NULL);
2126 // send a mission description request to this guy
2127 send_netgame_descript_packet(&Multi_join_selected_item->server_addr,0);
2128 multi_common_set_text("");
2132 // if he's double clicked, then select it and accept
2133 if(Multi_join_select_button.double_clicked()){
2135 Multi_join_select_button.get_mouse_pos(NULL,&y);
2137 if(item == Multi_join_list_selected){
2138 multi_join_button_pressed(MJ_ACCEPT);
2143 // return game n (0 based index)
2144 active_game *multi_join_get_game(int n)
2146 active_game *moveup = Active_game_head;
2153 moveup = moveup->next;
2154 while((moveup != Active_game_head) && (count != n)){
2155 moveup = moveup->next;
2158 if(moveup == Active_game_head){
2159 nprintf(("Network","Warning, couldn't find game item %d!\n",n));
2169 // scroll through the game list
2170 void multi_join_list_scroll_up()
2172 // if we're not at the beginning of the list, scroll up
2173 if(Multi_join_list_start_item != Active_game_head){
2174 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2176 MJ_LIST_START_DEC();
2178 gamesnd_play_iface(SND_SCROLL);
2180 gamesnd_play_iface(SND_GENERAL_FAIL);
2184 // scroll through the game list
2185 void multi_join_list_scroll_down()
2187 if((Active_game_count - Multi_join_list_start) > Mj_max_game_items[gr_screen.res]){
2188 Multi_join_list_start_item = Multi_join_list_start_item->next;
2190 MJ_LIST_START_INC();
2192 gamesnd_play_iface(SND_SCROLL);
2194 gamesnd_play_iface(SND_GENERAL_FAIL);
2198 void multi_join_list_page_up()
2200 // in this case, just set us to the beginning of the list
2201 if((Multi_join_list_start - Mj_max_game_items[gr_screen.res]) < 0){
2202 Multi_join_list_start_item = Active_game_head;
2204 MJ_LIST_START_SET(0);
2206 gamesnd_play_iface(SND_SCROLL);
2208 // otherwise page the whole thing up
2210 for(idx=0; idx<Mj_max_game_items[gr_screen.res]; idx++){
2211 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2213 MJ_LIST_START_DEC();
2215 gamesnd_play_iface(SND_SCROLL);
2219 void multi_join_list_page_down()
2223 // page the whole thing down
2224 while((count < Mj_max_game_items[gr_screen.res]) && ((Active_game_count - Multi_join_list_start) > Mj_max_game_items[gr_screen.res])){
2225 Multi_join_list_start_item = Multi_join_list_start_item->next;
2226 MJ_LIST_START_INC();
2231 gamesnd_play_iface(SND_SCROLL);
2234 void multi_join_cull_timeouts()
2236 active_game *backup;
2238 active_game *moveup = Active_game_head;
2240 // traverse through the entire list if any items exist
2244 if((moveup->heard_from_timer != -1) && (timestamp_elapsed(moveup->heard_from_timer))){
2245 Active_game_count--;
2247 // if this is the head of the list
2248 if(moveup == Active_game_head){
2249 // if this is the _only_ item on the list
2250 if(moveup->next == Active_game_head){
2251 // handle any gui details related to deleting this item
2252 multi_join_handle_item_cull(Active_game_head, count);
2254 free(Active_game_head);
2255 Active_game_head = NULL;
2258 // if there are other items on the list
2260 // handle any gui details related to deleting this item
2261 multi_join_handle_item_cull(moveup, count);
2263 Active_game_head = moveup->next;
2264 Active_game_head->prev = moveup->prev;
2265 Active_game_head->prev->next = Active_game_head;
2267 moveup = Active_game_head;
2270 // if its somewhere else on the list
2272 // handle any gui details related to deleting this item
2273 multi_join_handle_item_cull(moveup, count);
2275 // if its the last item on the list
2276 moveup->next->prev = moveup->prev;
2277 moveup->prev->next = moveup->next;
2279 // if it was the last element on the list, return
2280 if(moveup->next == Active_game_head){
2284 backup = moveup->next;
2290 moveup = moveup->next;
2293 } while(moveup != Active_game_head);
2297 // deep magic begins here.
2298 void multi_join_handle_item_cull(active_game *item, int item_index)
2300 // if this is the only item on the list, unset everything
2301 if(item->next == item){
2302 Multi_join_list_selected = -1;
2303 Multi_join_selected_item = NULL;
2306 Multi_join_slider.set_numberItems(0);
2308 MJ_LIST_START_SET(-1);
2309 Multi_join_list_start_item = NULL;
2315 // see if we should be adjusting our currently selected item
2316 if(item_index <= Multi_join_list_selected){
2317 // the selected item is the head of the list
2318 if(Multi_join_selected_item == Active_game_head){
2319 // move the pointer up since this item is about to be destroyed
2320 Multi_join_selected_item = Multi_join_selected_item->next;
2322 // if this is the item being deleted, select the previous one
2323 if(item == Multi_join_selected_item){
2325 Multi_join_selected_item = Multi_join_selected_item->prev;
2327 // decrement the selected index by 1
2328 Multi_join_list_selected--;
2330 // now we know its a previous item, so our pointer stays the same but our index goes down by one, since there will be
2331 // 1 less item on the list
2333 // decrement the selected index by 1
2334 Multi_join_list_selected--;
2339 // see if we should be adjusting out current start position
2340 if(item_index <= Multi_join_list_start){
2341 // the start position is the head of the list
2342 if(Multi_join_list_start_item == Active_game_head){
2343 // move the pointer up since this item is about to be destroyed
2344 Multi_join_list_start_item = Multi_join_list_start_item->next;
2346 // if this is the item being deleted, select the previous one
2347 if(item == Multi_join_list_start_item){
2348 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2350 // decrement the starting index by 1
2351 MJ_LIST_START_DEC();
2353 // but decrement the starting index by 1
2354 MJ_LIST_START_DEC();
2359 // maybe go back up a bit so that we always have a full page of items
2360 if(Active_game_count > Mj_max_game_items[gr_screen.res]){
2361 while((Active_game_count - Multi_join_list_start) < Mj_max_game_items[gr_screen.res]){
2362 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2363 MJ_LIST_START_DEC();
2367 // set slider location
2369 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);
2370 Multi_join_slider.force_currentItem(Multi_join_list_start);
2374 void multi_join_send_join_request(int as_observer)
2376 // don't do anything if we have no items selected
2377 if(Multi_join_selected_item == NULL){
2381 // 5/26/98 -- for team v team games, don't allow ingame joining :-(
2382 if ( (Multi_join_selected_item->flags & AG_FLAG_TEAMS) && (Multi_join_selected_item->flags & (AG_FLAG_PAUSE|AG_FLAG_IN_MISSION)) ) {
2383 popup(0, 1, POPUP_OK, XSTR("Joining ingame is currently not allowed for team vs. team games",772));
2387 memset(&Multi_join_request,0,sizeof(join_request));
2389 // if the netgame is in password mode, put up a request for the password
2390 if(Multi_join_selected_item->flags & AG_FLAG_PASSWD){
2391 if(!multi_passwd_popup(Multi_join_request.passwd)){
2395 nprintf(("Password : %s\n",Multi_join_request.passwd));
2398 // fill out the join request struct
2399 strcpy(Multi_join_request.callsign,Player->callsign);
2400 if(strlen(Player->image_filename) > 0){
2401 strcpy(Multi_join_request.image_filename, Player->image_filename);
2404 if(strlen(Player->squad_filename) > 0){
2405 strcpy(Multi_join_request.squad_filename, Player->squad_filename);
2409 // tracker id (if any)
2410 Multi_join_request.tracker_id = Multi_tracker_id;
2412 // player's rank (at least, what he wants you to _believe_)
2413 Multi_join_request.player_rank = (ubyte)Player->stats.rank;
2416 Multi_join_request.flags = 0;
2418 Multi_join_request.flags |= JOIN_FLAG_AS_OBSERVER;
2421 // if the player has hacked data
2422 if(game_hacked_data()){
2423 Multi_join_request.flags |= JOIN_FLAG_HAXOR;
2428 strncpy(Multi_join_request.pxo_squad_name, Multi_tracker_squad_name, LOGIN_LEN);
2431 // version of this server
2432 Multi_join_request.version = MULTI_FS_SERVER_VERSION;
2434 // server compatible version
2435 Multi_join_request.comp_version = MULTI_FS_SERVER_COMPATIBLE_VERSION;
2437 // his local player options
2438 memcpy(&Multi_join_request.player_options,&Player->m_local_options,sizeof(multi_local_options));
2440 // set the server address for the netgame
2441 memcpy(&Netgame.server_addr,&Multi_join_selected_item->server_addr,sizeof(net_addr));
2443 // send a join request to the guy
2444 send_join_packet(&Multi_join_selected_item->server_addr,&Multi_join_request);
2447 Multi_join_sent_stamp = timestamp(MULTI_JOIN_SENT_WAIT);
2450 multi_common_add_notify(XSTR("Sending join request...",773));
2453 void multi_join_create_game()
2455 // maybe warn the player about possible crappy server conditions
2456 if(!multi_join_maybe_warn()){
2460 // make sure to flag ourself as being the master
2461 Net_player->flags |= (NETINFO_FLAG_AM_MASTER | NETINFO_FLAG_GAME_HOST);
2462 Net_player->state = NETPLAYER_STATE_HOST_SETUP;
2464 // if we're in PXO mode, mark it down in our player struct
2465 if(MULTI_IS_TRACKER_GAME){
2466 Player->flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
2467 Player->save_flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
2469 // otherwise, if he's already played PXO games, warn him
2472 if(Player->flags & PLAYER_FLAGS_HAS_PLAYED_PXO){
2473 if(!multi_join_warn_pxo()){
2480 gameseq_post_event(GS_EVENT_MULTI_START_GAME);
2481 gamesnd_play_iface(SND_USER_SELECT);
2484 void multi_join_reset_join_stamp()
2486 // unset the timestamp here so the user can immediately send another join request
2487 Multi_join_sent_stamp = -1;
2488 multi_common_add_notify("");
2491 void multi_join_blit_top_stuff()
2493 // blit the cd icon if he has one
2494 if(Multi_has_cd && (Multi_common_icons[MICON_CD] != -1)){
2497 bm_get_info(Multi_common_icons[MICON_CD], &cd_w, NULL, NULL, NULL, NULL);
2499 gr_set_bitmap(Multi_common_icons[MICON_CD]);
2500 gr_bitmap((gr_screen.max_w / 2) - (cd_w / 2), Mj_cd_coords[gr_screen.res]);
2504 #define CW_CODE_CANCEL 0 // cancel the action
2505 #define CW_CODE_OK 1 // continue anyway
2506 #define CW_CODE_INFO 2 // gimme some more information
2508 #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)
2509 #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)
2511 #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)
2512 #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)
2514 int multi_join_warn_update_low(int code)
2518 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),LOW_WARN_TEXT);
2521 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),LOW_INFO_TEXT);
2524 return CW_CODE_CANCEL;
2527 int multi_join_warn_update_medium(int code)
2531 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),MED_WARN_TEXT);
2534 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),MED_INFO_TEXT);
2537 return CW_CODE_CANCEL;
2540 int multi_join_maybe_warn()
2544 // if the player is set for low updates
2545 if(Player->m_local_options.obj_update_level == OBJ_UPDATE_LOW){
2548 code = multi_join_warn_update_low(code);
2549 } while((code != CW_CODE_CANCEL) && (code != CW_CODE_OK));
2554 // if the player is set for medium updates
2555 else if(Player->m_local_options.obj_update_level == OBJ_UPDATE_MEDIUM){
2558 code = multi_join_warn_update_medium(code);
2559 } while((code != CW_CODE_CANCEL) && (code != CW_CODE_OK));
2567 int multi_join_warn_pxo()
2569 // 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;
2573 void multi_join_blit_protocol()
2575 gr_set_color_fast(&Color_bright);
2577 switch(Socket_type){
2580 gr_string(5, 2, "TCP");
2584 gr_string(5, 2, "IPX");
2590 // -------------------------------------------------------------------------------------------------
2592 // MULTIPLAYER START GAME screen
2597 #define MULTI_SG_PALETTE "InterfacePalette"
2599 static char *Multi_sg_bitmap_fname[GR_NUM_RESOLUTIONS] = {
2600 "MultiStartGame", // GR_640
2601 "2_MultiStartGame" // GR_1024
2604 static char *Multi_sg_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
2605 "MultiStartGame-M", // GR_640
2606 "2_MultiStartGame-M" // GR_1024
2611 int Multi_sg_rank_max_display[GR_NUM_RESOLUTIONS] = {
2616 // constants for coordinate look ups
2617 #define MSG_X_COORD 0
2618 #define MSG_Y_COORD 1
2619 #define MSG_W_COORD 2
2620 #define MSG_H_COORD 3
2624 // input password field
2625 int Msg_passwd_coords[GR_NUM_RESOLUTIONS][4] = {
2638 // input game title field
2639 int Msg_title_coords[GR_NUM_RESOLUTIONS][4] = {
2652 // rank selected field
2653 int Msg_rank_sel_coords[GR_NUM_RESOLUTIONS][4] = {
2667 int Msg_rank_list_coords[GR_NUM_RESOLUTIONS][4] = {
2683 #define MULTI_SG_NUM_BUTTONS 12
2684 #define MSG_OPEN_GAME 0
2685 #define MSG_CLOSED_GAME 1
2686 #define MSG_PASSWD_GAME 2
2687 #define MSG_RESTRICTED_GAME 3
2688 #define MSG_RANK_SET_GAME 4
2689 #define MSG_RANK_SCROLL_UP 5
2690 #define MSG_RANK_SCROLL_DOWN 6
2691 #define MSG_RANK_ABOVE 7
2692 #define MSG_RANK_BELOW 8
2694 #define MSG_OPTIONS 10
2695 #define MSG_ACCEPT 11
2697 #define MULTI_SG_NUM_BUTTONS 10
2698 #define MSG_OPEN_GAME 0
2699 //#define MSG_CLOSED_GAME 1
2700 //#define MSG_RESTRICTED_GAME 2
2701 #define MSG_PASSWD_GAME 1
2702 #define MSG_RANK_SET_GAME 2
2703 #define MSG_RANK_SCROLL_UP 3
2704 #define MSG_RANK_SCROLL_DOWN 4
2705 #define MSG_RANK_ABOVE 5
2706 #define MSG_RANK_BELOW 6
2708 #define MSG_OPTIONS 8
2709 #define MSG_ACCEPT 9
2712 UI_WINDOW Multi_sg_window; // the window object for the join screen
2713 UI_BUTTON Multi_sg_rank_button; // for selecting the rank marker
2714 UI_INPUTBOX Multi_sg_game_name; // for Netgame.name
2715 UI_INPUTBOX Multi_sg_game_passwd; // for Netgame.passwd
2716 int Multi_sg_bitmap; // the background bitmap
2718 ui_button_info Multi_sg_buttons[GR_NUM_RESOLUTIONS][MULTI_SG_NUM_BUTTONS] = {
2721 ui_button_info("MSG_00", 75, 111, -1, -1, 0),
2722 ui_button_info("MSG_01", 75, 139, -1, -1, 1),
2723 ui_button_info("MSG_02", 75, 164, -1, -1, 2),
2724 ui_button_info("MSG_03", 75, 199, -1, -1, 3),
2725 ui_button_info("MSG_04", 75, 243, -1, -1, 4),
2726 ui_button_info("MSG_05", 376, 231, -1, -1, 5),
2727 ui_button_info("MSG_06", 376, 258, -1, -1, 6),
2728 ui_button_info("MSG_07", 376, 291, -1, -1, 7),
2729 ui_button_info("MSG_08", 376, 320, -1, -1, 8),
2730 ui_button_info("MSG_09", 469, 427, -1, -1, 9),
2731 ui_button_info("MSG_10", 447, 452, -1, -1, 10),
2732 ui_button_info("MSG_11", 561, 411, -1, -1, 11),
2734 ui_button_info("MSG_00", 1, 184, 34, 191, 2), // open
2735 // ui_button_info("MSG_01", 1, 159, 34, 166, 1), // closed
2736 // ui_button_info("MSG_02", 1, 184, 34, 191, 2), // restricted
2737 ui_button_info("MSG_03", 1, 209, 34, 218, 3), // password
2738 ui_button_info("MSG_04", 1, 257, 34, 266, 4), // rank set
2739 ui_button_info("MSG_05", 1, 282, -1, -1, 5), // rank scroll up
2740 ui_button_info("MSG_06", 1, 307, -1, -1, 6), // rank scroll down
2741 ui_button_info("MSG_07", 177, 282, 210, 290, 7), // rank above
2742 ui_button_info("MSG_08", 177, 307, 210, 315, 8), // rank below
2743 ui_button_info("MSG_09", 536, 429, 500, 440, 9), // help
2744 ui_button_info("MSG_10", 536, 454, 479, 464, 10), // options
2745 ui_button_info("MSG_11", 576, 432, 571, 415, 11), // accept
2749 ui_button_info("2_MSG_00", 2, 295, 51, 307, 2), // open
2750 // ui_button_info("2_MSG_01", 2, 254, 51, 267, 1), // closed
2751 // ui_button_info("2_MSG_02", 2, 295, 51, 307, 2), // restricted
2752 ui_button_info("2_MSG_03", 2, 335, 51, 350, 3), // password
2753 ui_button_info("2_MSG_04", 2, 412, 51, 426, 4), // rank set
2754 ui_button_info("2_MSG_05", 2, 452, -1, -1, 5), // rank scroll up
2755 ui_button_info("2_MSG_06", 2, 492, -1, -1, 6), // rank scroll down
2756 ui_button_info("2_MSG_07", 284, 452, 335, 465, 7), // rank above
2757 ui_button_info("2_MSG_08", 284, 492, 335, 505, 8), // rank below
2758 ui_button_info("2_MSG_09", 858, 687, 817, 706, 9), // help
2759 ui_button_info("2_MSG_10", 858, 728, 797, 743, 10), // options
2760 ui_button_info("2_MSG_11", 921, 692, 921, 664, 11), // accept
2761 #ifdef MAKE_FS1 // filler for extra FS1 buttons
2762 ui_button_info("none", -1, -1, -1, -1, -1),
2763 ui_button_info("none", -1, -1, -1, -1, -1),
2769 #define MULTI_SG_NUM_TEXT 0
2771 #define MULTI_SG_NUM_TEXT 11
2773 UI_XSTR Multi_sg_text[GR_NUM_RESOLUTIONS][MULTI_SG_NUM_TEXT] = {
2775 // not needed for FS1
2777 {"Open", 1322, 34, 191, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_OPEN_GAME].button},
2778 // {"Closed", 1323, 34, 166, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_CLOSED_GAME].button},
2779 // {"Restricted", 1324, 34, 191, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RESTRICTED_GAME].button},
2780 {"Password Protected", 1325, 34, 218, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_PASSWD_GAME].button},
2781 {"Allow Rank", 1326, 34, 266, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_SET_GAME].button},
2782 {"Above", 1327, 210, 290, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_ABOVE].button},
2783 {"Below", 1328, 210, 315, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_BELOW].button},
2784 {"Help", 928, 500, 440, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_HELP].button},
2785 {"Options", 1036, 479, 464, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_OPTIONS].button},
2786 {"Accept", 1035, 571, 415, UI_XSTR_COLOR_PINK, -1, &Multi_sg_buttons[0][MSG_ACCEPT].button},
2787 {"Start Game", 1329, 26, 10, UI_XSTR_COLOR_GREEN, -1, NULL},
2788 {"Title", 1330, 26, 31, UI_XSTR_COLOR_GREEN, -1, NULL},
2789 {"Game Type", 1331, 12, 165, UI_XSTR_COLOR_GREEN, -1, NULL},
2793 // not needed for FS1
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);
2900 for(idx=0; idx<MULTI_SG_NUM_TEXT; idx++){
2901 Multi_sg_window.add_XSTR(&Multi_sg_text[gr_screen.res][idx]);
2904 // load the help overlay
2905 help_overlay_load(MULTI_START_OVERLAY);
2906 help_overlay_set_state(MULTI_START_OVERLAY,0);
2908 // intiialize the rank selection items
2909 multi_sg_select_rank_default();
2910 Multi_sg_rank_start = Multi_sg_rank_select;
2912 // create the rank select button
2913 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);
2914 Multi_sg_rank_button.hide();
2916 // create the netgame name input box
2917 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);
2919 // create the netgame password input box, and disable it by default
2920 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);
2921 Multi_sg_game_passwd.hide();
2922 Multi_sg_game_passwd.disable();
2924 // set the netgame text to this gadget and make it have focus
2925 Multi_sg_game_name.set_text(Multi_sg_netgame->name);
2926 Multi_sg_game_name.set_focus();
2928 // if starting a netgame, set the name of the game and any other options that are appropriate
2929 if ( Cmdline_start_netgame ) {
2930 if ( Cmdline_game_name != NULL ) {
2931 strcpy( Multi_sg_netgame->name, Cmdline_game_name );
2932 Multi_sg_game_name.set_text(Multi_sg_netgame->name);
2935 // deal with the different game types -- only one should even be active, so we will just go down
2936 // the line. Last one wins.
2937 if ( Cmdline_closed_game ) {
2938 Multi_sg_netgame->mode = NG_MODE_CLOSED;
2939 } else if ( Cmdline_restricted_game ) {
2940 Multi_sg_netgame->mode = NG_MODE_RESTRICTED;
2941 } else if ( Cmdline_game_password != NULL ) {
2942 Multi_sg_netgame->mode = NG_MODE_PASSWORD;
2943 strcpy(Multi_sg_netgame->passwd, Cmdline_game_password);
2944 Multi_sg_game_passwd.set_text(Multi_sg_netgame->passwd);
2947 // deal with rank above and rank below
2948 if ( (Cmdline_rank_above != NULL) || (Cmdline_rank_below != NULL) ) {
2952 if ( Cmdline_rank_above != NULL ) {
2953 rank_str = Cmdline_rank_above;
2955 rank_str = Cmdline_rank_below;
2958 // try and get the rank index from the name -- if found, then set the rank base
2959 // and the game type. apparently we only support either above or below, not both
2960 // together, so I make a random choice
2961 rank = multi_start_game_rank_from_name( rank_str );
2963 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
2965 // now an arbitrary decision
2966 if ( Cmdline_rank_above != NULL ) {
2967 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
2969 Multi_sg_netgame->mode = NG_MODE_RANK_BELOW;
2974 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
2978 void multi_start_game_do()
2980 // return here since we will be moving to the next stage anyway -- I don't want to see the backgrounds of
2981 // all the screens for < 1 second for every screen we automatically move to.
2982 if ( Cmdline_start_netgame ) {
2986 int k = Multi_sg_window.process();
2988 // process any keypresses
2991 if(help_overlay_active(MULTI_START_OVERLAY)){
2992 help_overlay_set_state(MULTI_START_OVERLAY,0);
2994 gamesnd_play_iface(SND_USER_SELECT);
2995 multi_quit_game(PROMPT_NONE);
3000 case KEY_LCTRL + KEY_ENTER :
3001 case KEY_RCTRL + KEY_ENTER :
3002 gamesnd_play_iface(SND_COMMIT_PRESSED);
3003 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
3007 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
3008 help_overlay_set_state(MULTI_START_OVERLAY, 0);
3011 // check to see if the user has selected a different rank
3012 multi_sg_rank_process_select();
3014 // check any button presses
3015 multi_sg_check_buttons();
3017 // check to see if any of the input boxes have changed, and update the appropriate Netgame fields if necessary
3018 multi_sg_check_passwd();
3019 multi_sg_check_name();
3021 // draw the background, etc
3023 GR_MAYBE_CLEAR_RES(Multi_sg_bitmap);
3024 if(Multi_sg_bitmap != -1){
3025 gr_set_bitmap(Multi_sg_bitmap);
3028 Multi_sg_window.draw();
3030 // display rank stuff
3031 multi_sg_rank_display_stuff();
3033 // display any pending notification messages
3034 multi_common_notify_do();
3036 // draw all radio button
3037 multi_sg_draw_radio_buttons();
3039 // draw the help overlay
3040 help_overlay_maybe_blit(MULTI_START_OVERLAY);
3046 void multi_start_game_close()
3048 // if i'm the host on a standalone server, send him my start game options (passwd, mode, etc)
3049 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
3050 multi_options_update_start_game(Multi_sg_netgame);
3053 // unload any bitmaps
3054 if(!bm_unload(Multi_sg_bitmap)){
3055 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_sg_bitmap_fname[gr_screen.res]));
3058 // unload the help overlay
3059 help_overlay_unload(MULTI_START_OVERLAY);
3061 // destroy the UI_WINDOW
3062 Multi_sg_window.destroy();
3065 void multi_sg_check_buttons()
3068 for(idx=0;idx<MULTI_SG_NUM_BUTTONS;idx++){
3069 // we only really need to check for one button pressed at a time, so we can break after
3071 if(Multi_sg_buttons[gr_screen.res][idx].button.pressed()){
3072 multi_sg_button_pressed(idx);
3078 void multi_sg_button_pressed(int n)
3081 // go to the options screen
3083 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
3088 if(!help_overlay_active(MULTI_START_OVERLAY)){
3089 help_overlay_set_state(MULTI_START_OVERLAY,1);
3091 help_overlay_set_state(MULTI_START_OVERLAY,0);
3095 // the open button was pressed
3097 // if the closed option is selected
3098 if(Multi_sg_netgame->mode != NG_MODE_OPEN){
3099 Multi_sg_netgame->mode = NG_MODE_OPEN;
3101 gamesnd_play_iface(SND_USER_SELECT);
3103 // release the password control if necessary
3104 multi_sg_release_passwd();
3106 // if its already selected
3108 gamesnd_play_iface(SND_GENERAL_FAIL);
3113 // the open button was pressed
3114 case MSG_CLOSED_GAME:
3115 // if the closed option is selected
3116 if(Multi_sg_netgame->mode != NG_MODE_CLOSED){
3117 Multi_sg_netgame->mode = NG_MODE_CLOSED;
3119 gamesnd_play_iface(SND_USER_SELECT);
3121 // release the password control if necessary
3122 multi_sg_release_passwd();
3124 // if its already selected
3126 gamesnd_play_iface(SND_GENERAL_FAIL);
3131 // toggle password protection
3132 case MSG_PASSWD_GAME:
3133 // if we selected it
3134 if(Multi_sg_netgame->mode != NG_MODE_PASSWORD){
3135 gamesnd_play_iface(SND_USER_SELECT);
3137 Multi_sg_game_passwd.enable();
3138 Multi_sg_game_passwd.unhide();
3139 Multi_sg_game_passwd.set_focus();
3141 Multi_sg_netgame->mode = NG_MODE_PASSWORD;
3143 // copy in the current network password
3144 Multi_sg_game_passwd.set_text(Multi_sg_netgame->passwd);
3146 gamesnd_play_iface(SND_GENERAL_FAIL);
3151 // toggle "restricted" on or off
3152 case MSG_RESTRICTED_GAME:
3153 if(Multi_sg_netgame->mode != NG_MODE_RESTRICTED){
3154 gamesnd_play_iface(SND_USER_SELECT);
3155 Multi_sg_netgame->mode = NG_MODE_RESTRICTED;
3157 // release the password control if necessary
3158 multi_sg_release_passwd();
3160 gamesnd_play_iface(SND_GENERAL_FAIL);
3165 // turn off all rank requirements
3166 case MSG_RANK_SET_GAME:
3167 // if either is set, then turn then both off
3168 if((Multi_sg_netgame->mode != NG_MODE_RANK_BELOW) && (Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE)){
3169 gamesnd_play_iface(SND_USER_SELECT);
3171 // set it to the default case if we're turning it off
3172 multi_sg_select_rank_default();
3173 Multi_sg_rank_start = Multi_sg_rank_select;
3175 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
3177 // release the password control if necessary
3178 multi_sg_release_passwd();
3180 gamesnd_play_iface(SND_GENERAL_FAIL);
3184 // rank above was pressed
3185 case MSG_RANK_ABOVE :
3186 if((Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE) || (Multi_sg_netgame->mode == NG_MODE_RANK_BELOW)){
3187 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
3189 // select the first item
3190 multi_sg_select_rank_default();
3191 Multi_sg_rank_start = Multi_sg_rank_select;
3194 gamesnd_play_iface(SND_USER_SELECT);
3196 gamesnd_play_iface(SND_GENERAL_FAIL);
3200 // rank below was pressed
3201 case MSG_RANK_BELOW :
3202 if((Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE) || (Multi_sg_netgame->mode == NG_MODE_RANK_BELOW)){
3203 Multi_sg_netgame->mode = NG_MODE_RANK_BELOW;
3205 // select the first item
3206 multi_sg_select_rank_default();
3207 Multi_sg_rank_start = Multi_sg_rank_select;
3210 gamesnd_play_iface(SND_USER_SELECT);
3212 gamesnd_play_iface(SND_GENERAL_FAIL);
3216 // scroll the rank list up
3217 case MSG_RANK_SCROLL_UP:
3218 multi_sg_rank_scroll_up();
3221 // scroll the rank list down
3222 case MSG_RANK_SCROLL_DOWN:
3223 multi_sg_rank_scroll_down();
3226 // move to the create game screen
3228 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
3229 gamesnd_play_iface(SND_COMMIT_PRESSED);
3233 gamesnd_play_iface(SND_GENERAL_FAIL);
3234 multi_common_add_notify(XSTR("Not implemented yet!",760));
3239 // NOTE : this is where all Netgame initialization should take place on the host
3240 void multi_sg_init_gamenet()
3242 char buf[128],out_name[128];
3244 net_player *server_save;
3246 // back this data up in case we are already connected to a standalone
3247 memcpy(&save,&Netgame.server_addr,sizeof(net_addr));
3248 server_save = Netgame.server;
3250 // remove campaign flags
3251 Game_mode &= ~(GM_CAMPAIGN_MODE);
3253 // clear out the Netgame structure and start filling in the values
3254 memset( &Netgame, 0, sizeof(Netgame) );
3255 memset( &Multi_sg_netgame_temp, 0, sizeof(netgame_info) );
3257 // if we're on the standalone, we're not the server, so we don't care about setting the netgame state
3258 if(Net_player->state != NETPLAYER_STATE_STD_HOST_SETUP){
3259 Netgame.game_state = NETGAME_STATE_HOST_SETUP;
3260 Multi_sg_netgame = &Netgame;
3263 ml_string(NOX("Starting netgame as Host/Server"));
3265 Multi_sg_netgame = &Multi_sg_netgame_temp;
3269 memcpy(&temp_addr.s_addr, &Netgame.server_addr, 4);
3270 char *server_addr = inet_ntoa(temp_addr);
3271 ml_printf(NOX("Starting netgame as Host on Standalone server : %s"), (server_addr == NULL) ? NOX("Unknown") : server_addr);
3274 Net_player->tracker_player_id = Multi_tracker_id;
3276 Multi_sg_netgame->security = (rand() % 32766) + 1; // get some random security number
3277 Multi_sg_netgame->mode = NG_MODE_OPEN;
3278 Multi_sg_netgame->rank_base = RANK_ENSIGN;
3279 if(Multi_sg_netgame->security < 16){
3280 Multi_sg_netgame->security += 16;
3283 // set the version_info field
3284 Multi_sg_netgame->version_info = NG_VERSION_ID;
3286 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
3287 Netgame.host = Net_player;
3290 // set the default netgame flags
3291 Multi_sg_netgame->flags = 0;
3293 // intialize endgame stuff
3294 multi_endgame_init();
3296 // load in my netgame options
3297 multi_options_netgame_load(&Netgame.options);
3299 // load my local netplayer options
3300 multi_options_local_load(&Net_player->p_info.options, Net_player);
3302 // setup the default game name, taking care of string length and player callsigns
3303 memset(out_name,0,128);
3305 pilot_format_callsign_personal(Player->callsign,out_name);
3306 sprintf(buf, XSTR("%s game",782), out_name); // [[ %s will be a pilot's name ]]
3307 if ( strlen(buf) > MAX_GAMENAME_LEN ){
3308 strcpy(buf, XSTR("Temporary name",783));
3310 strcpy(Multi_sg_netgame->name, buf);
3312 // set the default qos and duration
3313 multi_voice_maybe_update_vars(Netgame.options.voice_qos,Netgame.options.voice_record_time);
3315 // make sure to set the server correctly (me or the standalone)
3316 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3317 memcpy(&Netgame.server_addr, &Psnet_my_addr,sizeof(net_addr));
3318 Netgame.server = Net_player;
3319 Net_player->player_id = multi_get_new_id();
3321 // setup debug flags
3322 Netgame.debug_flags = 0;
3324 if(!Cmdline_server_firing){
3325 Netgame.debug_flags |= NETD_FLAG_CLIENT_FIRING;
3327 if(!Cmdline_client_dodamage){
3328 Netgame.debug_flags |= NETD_FLAG_CLIENT_NODAMAGE;
3332 memcpy(&Netgame.server_addr,&save,sizeof(net_addr));
3333 Netgame.server = server_save;
3336 // if I have a cd or not
3338 Net_player->flags |= NETINFO_FLAG_HAS_CD;
3341 // if I have hacked data
3342 if(game_hacked_data()){
3343 Net_player->flags |= NETINFO_FLAG_HAXOR;
3346 // assign my player struct and other data
3347 Net_player->flags |= (NETINFO_FLAG_CONNECTED | NETINFO_FLAG_DO_NETWORKING);
3348 Net_player->s_info.voice_token_timestamp = -1;
3350 // if we're supposed to flush our cache directory, do so now
3351 if(Net_player->p_info.options.flags & MLO_FLAG_FLUSH_CACHE){
3352 multi_flush_multidata_cache();
3355 ml_string(NOX("Flushing multi-data cache"));
3361 void multi_sg_draw_radio_buttons()
3363 // draw the appropriate radio button
3364 switch(Multi_sg_netgame->mode){
3366 Multi_sg_buttons[gr_screen.res][MSG_OPEN_GAME].button.draw_forced(2);
3370 case NG_MODE_CLOSED:
3371 Multi_sg_buttons[gr_screen.res][MSG_CLOSED_GAME].button.draw_forced(2);
3375 case NG_MODE_PASSWORD:
3376 Multi_sg_buttons[gr_screen.res][MSG_PASSWD_GAME].button.draw_forced(2);
3380 case NG_MODE_RESTRICTED:
3381 Multi_sg_buttons[gr_screen.res][MSG_RESTRICTED_GAME].button.draw_forced(2);
3385 case NG_MODE_RANK_ABOVE:
3386 Multi_sg_buttons[gr_screen.res][MSG_RANK_SET_GAME].button.draw_forced(2);
3387 Multi_sg_buttons[gr_screen.res][MSG_RANK_ABOVE].button.draw_forced(2);
3389 case NG_MODE_RANK_BELOW:
3390 Multi_sg_buttons[gr_screen.res][MSG_RANK_SET_GAME].button.draw_forced(2);
3391 Multi_sg_buttons[gr_screen.res][MSG_RANK_BELOW].button.draw_forced(2);
3396 void multi_sg_rank_scroll_up()
3398 // if he doesn't have either of the rank flags set, then ignore this
3399 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3403 if(Multi_sg_rank_start > 0){
3404 Multi_sg_rank_start--;
3405 gamesnd_play_iface(SND_SCROLL);
3407 gamesnd_play_iface(SND_GENERAL_FAIL);
3411 void multi_sg_rank_scroll_down()
3413 // if he doesn't have either of the rank flags set, then ignore this
3414 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3418 if((NUM_RANKS - Multi_sg_rank_start) > Multi_sg_rank_max_display[gr_screen.res]){
3419 Multi_sg_rank_start++;
3420 gamesnd_play_iface(SND_SCROLL);
3422 gamesnd_play_iface(SND_GENERAL_FAIL);
3426 void multi_sg_rank_display_stuff()
3431 // if he doesn't have either of the rank flags set, then ignore this
3432 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3436 // display the list of ranks
3437 y = Msg_rank_list_coords[gr_screen.res][MSG_Y_COORD];
3438 idx = Multi_sg_rank_start;
3440 while((count < NUM_RANKS) && (count < Multi_sg_rank_max_display[gr_screen.res]) && (idx < NUM_RANKS)){
3441 // if its the selected item, then color it differently
3442 if(idx == Multi_sg_rank_select){
3443 gr_set_color_fast(&Color_text_selected);
3445 gr_set_color_fast(&Color_text_normal);
3449 multi_sg_rank_build_name(Ranks[idx].name,rank_name);
3450 gr_string(Msg_rank_list_coords[gr_screen.res][MSG_X_COORD],y,rank_name);
3458 // display the selected rank
3460 gr_set_color_fast(&Color_bright);
3461 multi_sg_rank_build_name(Ranks[Multi_sg_netgame->rank_base].name,rank_name);
3462 gr_string(Msg_rank_sel_coords[gr_screen.res][MSG_X_COORD],Msg_rank_sel_coords[gr_screen.res][MSG_Y_COORD],rank_name);
3466 void multi_sg_rank_process_select()
3470 // if he doesn't have either of the rank flags set, then ignore this
3471 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3475 // see if he's clicked on an item on the rank list
3476 if(Multi_sg_rank_button.pressed()){
3478 Multi_sg_rank_button.get_mouse_pos(NULL,&y);
3481 if(item + Multi_sg_rank_start < NUM_RANKS){
3482 // evaluate whether this rank is valid for the guy to pick
3483 if(multi_sg_rank_select_valid(item + Multi_sg_rank_start)){
3484 gamesnd_play_iface(SND_USER_SELECT);
3486 Multi_sg_rank_select = item + Multi_sg_rank_start;
3488 // set the Netgame rank
3489 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
3491 gamesnd_play_iface(SND_GENERAL_FAIL);
3493 memset(string,0,255);
3494 sprintf(string,XSTR("Illegal value for a host of your rank (%s)\n",784),Ranks[Net_player->player->stats.rank].name);
3495 multi_common_add_notify(string);
3501 void multi_sg_rank_build_name(char *in,char *out)
3507 first = strtok(use," ");
3509 // just copy the string
3514 // if the first part of the string is lieutenant, then abbreivate it and tack on the rest of the string
3515 if (stricmp(first,XSTR("lieutenant",785)) == 0) {
3516 first = strtok(NULL, NOX("\n"));
3518 // if he's not just a plain lieutenant
3520 strcpy(out,XSTR("Lt. ",786)); // [[ lieutenant ]]
3523 // if he _is_ just a plain lieutenant
3532 void multi_sg_check_passwd()
3534 // check to see if the password input box has been pressed
3535 if(Multi_sg_game_passwd.changed()){
3536 Multi_sg_game_passwd.get_text(Multi_sg_netgame->passwd);
3540 void multi_sg_check_name()
3542 // check to see if the game name input box has been pressed
3543 if(Multi_sg_game_name.changed()){
3544 Multi_sg_game_name.get_text(Multi_sg_netgame->name);
3548 void multi_sg_release_passwd()
3550 // hide and disable the password input box
3551 Multi_sg_game_passwd.hide();
3552 Multi_sg_game_passwd.disable();
3554 // set the focus back to the name input box
3555 Multi_sg_game_name.set_focus();
3558 int multi_sg_rank_select_valid(int rank)
3561 if(Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE){
3562 if(Net_player->player->stats.rank >= rank){
3568 if(Net_player->player->stats.rank <= rank){
3576 void multi_sg_select_rank_default()
3578 // pick our rank for now
3579 Multi_sg_rank_select = Net_player->player->stats.rank;
3581 // set the Netgame rank
3582 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
3585 // -------------------------------------------------------------------------------------------------
3587 // MULTIPLAYER CREATE GAME screen
3592 char *Multi_create_bitmap_fname[GR_NUM_RESOLUTIONS] = {
3593 "MultiCreate", // GR_640
3594 "2_MultiCreate" // GR_1024
3597 char *Multi_create_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
3598 "MultiCreate-M", // GR_640
3599 "2_MultiCreate-M" // GR_1024
3602 char *Multi_create_loading_fname[GR_NUM_RESOLUTIONS] = {
3603 "PleaseWait", // GR_640
3604 "2_PleaseWait" // GR_1024
3608 #define MULTI_CREATE_NUM_BUTTONS 23
3611 #define MC_SHOW_ALL 0
3612 #define MC_SHOW_COOP 1
3613 #define MC_SHOW_TEAM 2
3614 #define MC_SHOW_DOGFIGHT 3
3615 #define MC_PXO_REFRESH 4
3616 #define MC_PILOT_INFO 5
3617 #define MC_SCROLL_LIST_UP 6
3618 #define MC_SCROLL_LIST_DOWN 7
3619 #define MC_SCROLL_PLAYERS_UP 8
3620 #define MC_SCROLL_PLAYERS_DOWN 9
3621 #define MC_MISSION_FILTER 10
3622 #define MC_CAMPAIGN_FILTER 11
3623 #define MC_CANCEL 12
3628 #define MC_SCROLL_INFO_UP 17
3629 #define MC_SCROLL_INFO_DOWN 18
3630 #define MC_HOST_OPTIONS 19
3632 #define MC_OPTIONS 21
3633 #define MC_ACCEPT 22
3636 UI_WINDOW Multi_create_window; // the window object for the create screen
3637 UI_BUTTON Multi_create_player_select_button; // for selecting players
3638 UI_BUTTON Multi_create_list_select_button; // for selecting missions/campaigns
3639 int Multi_create_bitmap; // the background bitmap
3640 UI_SLIDER2 Multi_create_slider; // for create list
3642 // constants for coordinate look ups
3643 #define MC_X_COORD 0
3644 #define MC_Y_COORD 1
3645 #define MC_W_COORD 2
3646 #define MC_H_COORD 3
3648 ui_button_info Multi_create_buttons[GR_NUM_RESOLUTIONS][MULTI_CREATE_NUM_BUTTONS] = {
3651 ui_button_info("MC_18", 34, 131, -1, -1, 18), // all
3652 ui_button_info("MC_19", 72, 131, -1, -1, 19), // coop
3653 ui_button_info("MC_20", 120, 131, -1, -1, 20), // team
3654 // ui_button_info("MC_21", 166, 131, -1, -1, 21), // dogfight
3655 ui_button_info("none", -1, -1, -1, -1, -1), // dogfight (not used)
3656 ui_button_info("none", -1, -1, -1, -1, -1), // pxo?
3657 ui_button_info("MC_26", 540, 114, -1, -1, 26), // pilot info
3658 ui_button_info("MC_03", 0, 187, -1, -1, 2), // scroll list up
3659 ui_button_info("MC_02", 0, 227, -1, -1, 3), // scroll list down
3660 ui_button_info("MC_04", 611, 182, -1, -1, 4), // scroll players up
3661 ui_button_info("MC_05", 611, 221, -1, -1, 5), // scroll players down
3662 ui_button_info("MC_06", 18, 322, -1, -1, 6), // mission filter
3663 ui_button_info("MC_07", 18, 344, -1, -1, 7), // campaign filter
3664 ui_button_info("MC_10", 317, 339, -1, -1, 10), // cancel
3665 ui_button_info("MC_14", 464, 350, -1, -1, 14), // team 1
3666 ui_button_info("MC_15", 498, 350, -1, -1, 15), // team 2
3667 ui_button_info("MC_16", 527, 346, -1, -1, 16), // kick
3668 ui_button_info("MC_17", 572, 346, -1, -1, 17), // close
3669 ui_button_info("MC_08", 0, 398, -1, -1, 8), // scroll mission info up
3670 ui_button_info("MC_09", 0, 435, -1, -1, 9), // scroll mission info down
3671 ui_button_info("MC_27", 447, 402, -1, -1, 27), // host options
3672 ui_button_info("MC_11", 510, 428, -1, -1, 11), // help
3673 ui_button_info("MC_12", 510, 453, -1, -1, 12), // options
3674 ui_button_info("Mc_13", 562, 412, -1, -1, 13), // commit
3676 ui_button_info("MC_00", 32, 129, 36, 158, 0), // show all missions
3677 ui_button_info("MC_01", 76, 129, 71, 158, 1), // show coop missions
3678 ui_button_info("MC_02", 121, 129, 119, 158, 2), // show team missions
3679 ui_button_info("MC_03", 164, 129, 166, 158, 3), // show dogfight missions
3680 ui_button_info("MC_04", 399, 129, 229, 130, 4), // pxo mission refresh
3681 ui_button_info("MC_05", 567, 123, 467, 132, 5), // pilot info
3682 ui_button_info("MC_06", 1, 161, -1, -1, 6), // scroll mission info up
3683 ui_button_info("MC_08", 1, 304, -1, -1, 8), // scroll mission info down
3684 ui_button_info("MC_09", 613, 160, -1, -1, 9), // scroll players up
3685 ui_button_info("MC_10", 613, 202, -1, -1, 10), // scroll players down
3686 ui_button_info("MC_11", 22, 346, 27, 376, 11), // mission filter
3687 ui_button_info("MC_12", 104, 346, 110, 376, 12), // campaign filter
3688 ui_button_info("MC_13", 392, 341, 328, 364, 13), // cancel
3689 ui_button_info("MC_14", 472, 352, 482, 381, 14), // team 0
3690 ui_button_info("MC_15", 506, 352, 514, 381, 15), // team 1
3691 ui_button_info("MC_16", 539, 346, 539, 381, 16), // kick
3692 ui_button_info("MC_17", 589, 346, 582, 381, 17), // close
3693 ui_button_info("MC_18", 1, 406, -1, -1, 18), // scroll list up
3694 ui_button_info("MC_19", 1, 447, -1, -1, 19), // scroll list down
3695 ui_button_info("MC_20", 499, 434, 436, 423, 20), // host options
3696 ui_button_info("MC_21", 534, 426, -1, -1, 21), // help
3697 ui_button_info("MC_22", 534, 452, -1, -1, 22), // options
3698 ui_button_info("MC_23", 571, 426, 572, 413, 23), // commit
3702 ui_button_info("2_MC_00", 51, 207, 61, 253, 0), // show all missions
3703 ui_button_info("2_MC_01", 122, 207, 124, 253, 1), // show coop missions
3704 ui_button_info("2_MC_02", 193, 207, 194, 253, 2), // show team missions
3705 ui_button_info("2_MC_03", 263, 207, 261, 253, 3), // show dogfight missions
3706 ui_button_info("2_MC_04", 639, 207, 479, 218, 4), // pxo mission refresh
3707 ui_button_info("2_MC_05", 907, 197, 748, 216, 5), // pilot info
3708 ui_button_info("2_MC_06", 1, 258, -1, -1, 6), // scroll mission info up
3709 ui_button_info("2_MC_08", 1, 487, -1, -1, 8), // scroll mission info down
3710 ui_button_info("2_MC_09", 981, 256, -1, -1, 9), // scroll players up
3711 ui_button_info("2_MC_10", 981, 323, -1, -1, 10), // scroll players down
3712 ui_button_info("2_MC_11", 35, 554, 46, 601, 11), // mission filter
3713 ui_button_info("2_MC_12", 166, 554, 174, 601, 12), // campaign filter
3714 ui_button_info("2_MC_13", 628, 545, 559, 582, 13), // cancel
3715 ui_button_info("2_MC_14", 756, 564, 772, 610, 14), // team 0
3716 ui_button_info("2_MC_15", 810, 564, 826, 610, 15), // team 1
3717 ui_button_info("2_MC_16", 862, 554, 872, 610, 16), // kick
3718 ui_button_info("2_MC_17", 943, 554, 949, 610, 17), // close
3719 ui_button_info("2_MC_18", 1, 649, -1, -1, 18), // scroll list up
3720 ui_button_info("2_MC_19", 1, 716, -1, -1, 19), // scroll list down
3721 ui_button_info("2_MC_20", 798, 695, 726, 667, 20), // host options
3722 ui_button_info("2_MC_21", 854, 681, -1, -1, 21), // help
3723 ui_button_info("2_MC_22", 854, 724, -1, -1, 22), // options
3724 ui_button_info("2_MC_23", 914, 681, 932, 667, 23), // commit
3729 #define MULTI_CREATE_NUM_TEXT 0
3731 #define MULTI_CREATE_NUM_TEXT 15
3733 UI_XSTR Multi_create_text[GR_NUM_RESOLUTIONS][MULTI_CREATE_NUM_BUTTONS] = {
3735 // not needed for FS1
3737 {"All", 1256, 36, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_ALL].button},
3738 {"Coop", 1257, 71, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_COOP].button},
3739 {"Team", 1258, 119, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_TEAM].button},
3740 {"Dogfight", 1259, 166, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_DOGFIGHT].button},
3741 {"Refresh Missions", 1260, 229, 130, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_PXO_REFRESH].button},
3742 {"Pilot Info", 1261, 467, 132, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_PILOT_INFO].button},
3743 {"Missions", 1262, 27, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_MISSION_FILTER].button},
3744 {"Campaigns", 1263, 110, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_CAMPAIGN_FILTER].button},
3745 {"Cancel", 387, 328, 364, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_CANCEL].button},
3746 {"1", 1264, 482, 381, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_TEAM0].button},
3747 {"2", 1265, 514, 381, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_TEAM1].button},
3748 {"Kick", 1266, 539, 381, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_KICK].button},
3749 {"Close", 1508, 582, 381, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_CLOSE].button},
3750 {"Host Options", 1267, 436, 423, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_HOST_OPTIONS].button},
3751 {"Commit", 1062, 572, 413, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_ACCEPT].button}
3755 // not needed for FS1
3757 {"All", 1256, 61, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_ALL].button},
3758 {"Coop", 1257, 124, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_COOP].button},
3759 {"Team", 1258, 194, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_TEAM].button},
3760 {"Dogfight", 1259, 261, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_DOGFIGHT].button},
3761 {"Refresh Missions", 1260, 501, 218, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_PXO_REFRESH].button},
3762 {"Pilot Info", 1261, 814, 216, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_PILOT_INFO].button},
3763 {"Missions", 1262, 46, 601, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_MISSION_FILTER].button},
3764 {"Campaigns", 1263, 174, 601, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_CAMPAIGN_FILTER].button},
3765 {"Cancel", 387, 559, 582, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_CANCEL].button},
3766 {"1", 1264, 772, 610, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_TEAM0].button},
3767 {"2", 1265, 826, 610, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_TEAM1].button},
3768 {"Kick", 1266, 872, 610, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_KICK].button},
3769 {"Close", 1508, 949, 610, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_CLOSE].button},
3770 {"Host Options", 1267, 755, 683, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_HOST_OPTIONS].button},
3771 {"Commit", 1062, 932, 667, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_ACCEPT].button}
3776 // squad war checkbox
3777 UI_CHECKBOX Multi_create_sw_checkbox;
3778 char *Multi_create_sw_checkbox_fname[GR_NUM_RESOLUTIONS] = {
3782 int Multi_create_sw_checkbox_coords[GR_NUM_RESOLUTIONS][2] = {
3790 int Multi_create_sw_checkbox_text[GR_NUM_RESOLUTIONS][2] = {
3799 // game information text areas
3800 int Mc_list_coords[GR_NUM_RESOLUTIONS][4] = {
3813 int Mc_players_coords[GR_NUM_RESOLUTIONS][4] = {
3826 int Mc_info_coords[GR_NUM_RESOLUTIONS][4] = {
3839 // mission icon stuff
3840 int Mc_icon_type_coords[GR_NUM_RESOLUTIONS][2] = {
3842 38, -2 // y is an offset
3845 61, -2 // y is an offset
3849 int Mc_icon_volition_coords[GR_NUM_RESOLUTIONS][2] = {
3851 61, -1 // y is an offset
3854 98, 1 // y is an offset
3858 int Mc_icon_silent_coords[GR_NUM_RESOLUTIONS][2] = {
3860 72, 0 // y is an offset
3863 115, 0 // y is an offset
3867 int Mc_icon_valid_coords[GR_NUM_RESOLUTIONS][2] = {
3869 91, 0 // y is an offset
3872 146, 0 // y is an offset
3876 // mission/campaign list column areas
3877 int Mc_column1_w[GR_NUM_RESOLUTIONS] = {
3882 int Mc_column2_w[GR_NUM_RESOLUTIONS] = {
3887 int Mc_column3_w[GR_NUM_RESOLUTIONS] = {
3892 int Mc_mission_name_x[GR_NUM_RESOLUTIONS] = {
3897 int Mc_mission_count_x[GR_NUM_RESOLUTIONS] = {
3902 int Mc_mission_fname_x[GR_NUM_RESOLUTIONS] = {
3907 int Mc_create_game_text[GR_NUM_RESOLUTIONS][2] = {
3908 {13, 116}, // GR_640
3909 {21, 186} // GR_1024
3912 int Mc_players_text[GR_NUM_RESOLUTIONS][2] = {
3913 {467, 150}, // GR_640
3914 {747, 240} // GR_1024
3917 int Mc_team_text[GR_NUM_RESOLUTIONS][2] = {
3918 {484, 342}, // GR_640
3919 {774, 547} // GR_1024
3922 int Mc_slider_coords[GR_NUM_RESOLUTIONS][4] = {
3924 3, 197, 13, 105 // GR_640
3927 5, 316, 20, 168 // GR_1024
3931 char *Mc_slider_bitmap[GR_NUM_RESOLUTIONS] = {
3936 // player list control thingie defs
3937 #define MULTI_CREATE_PLIST_MAX_DISPLAY 20
3938 int Multi_create_plist_select_flag; // flag indicating if we have a play selected
3939 short Multi_create_plist_select_id; // the net address of the currently selected player (for lookup)
3941 // master tracker details
3942 int Multi_create_frame_count; // framecount
3943 int Multi_create_mt_tried_login; // attempted to login this server on the MT
3945 // mission filter settings
3946 int Multi_create_filter; // what mode we're in
3948 // game/campaign list control defs
3949 int Multi_create_list_max_display[GR_NUM_RESOLUTIONS] = {
3955 int Multi_create_list_count; // number of items in listbox
3956 int Multi_create_list_mode; // 0 == mission mode, 1 == campaign mode
3957 int Multi_create_list_start; // where to start displaying from
3958 int Multi_create_list_select; // which item is currently highlighted
3959 int Multi_create_files_loaded;
3961 char Multi_create_files_array[MULTI_CREATE_MAX_LIST_ITEMS][MAX_FILENAME_LEN];
3963 int Multi_create_mission_count; // how many we have
3964 int Multi_create_campaign_count;
3965 multi_create_info Multi_create_mission_list[MULTI_CREATE_MAX_LIST_ITEMS];
3966 multi_create_info Multi_create_campaign_list[MULTI_CREATE_MAX_LIST_ITEMS];
3968 // use a pointer for the file list. Will point to either the missions or the campaigns
3969 multi_create_info *Multi_create_file_list;
3971 // LOCAL function definitions
3972 void multi_create_check_buttons();
3973 void multi_create_button_pressed(int n);
3974 void multi_create_init_as_server();
3975 void multi_create_init_as_client();
3976 void multi_create_do_netstuff();
3977 void multi_create_plist_scroll_up();
3978 void multi_create_plist_scroll_down();
3979 void multi_create_plist_process();
3980 void multi_create_plist_blit_normal();
3981 void multi_create_plist_blit_team();
3982 void multi_create_list_scroll_up();
3983 void multi_create_list_scroll_down();
3984 void multi_create_list_do();
3985 void multi_create_list_select_item(int n);
3986 void multi_create_list_blit_icons(int list_index, int y_start);
3987 void multi_create_accept_hit();
3988 void multi_create_draw_filter_buttons();
3989 void multi_create_set_selected_team(int team);
3990 short multi_create_get_mouse_id();
3991 int multi_create_ok_to_commit();
3992 int multi_create_verify_cds();
3993 void multi_create_refresh_pxo();
3994 void multi_create_sw_clicked();
3996 // since we can selectively filter out mission/campaign types we always need to map a selected index (which is relative
3997 // to the displayed list), to an absolute index (which is relative to the total file list - some of which may filtered out)
3998 void multi_create_select_to_filename(int select_index,char *filename);
3999 int multi_create_select_to_index(int select_index);
4001 int Multi_create_should_show_popup = 0;
4004 // sorting function to sort mission lists.. Basic sorting on mission name
4005 int multi_create_sort_func(const void *a, const void *b)
4007 multi_create_info *m1, *m2;
4009 m1 = (multi_create_info *)a;
4010 m2 = (multi_create_info *)b;
4012 return ( strcmp(m1->name, m2->name) );
4015 void multi_create_setup_list_data(int mode)
4017 int idx,should_sort,switched_modes;
4019 // set the current mode
4022 if((Multi_create_list_mode != mode) && (mode != -1)){
4023 Multi_create_list_mode = mode;
4026 // set up the list pointers
4027 if ( Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ) {
4028 Multi_create_file_list = Multi_create_mission_list;
4029 } else if ( Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ) {
4030 Multi_create_file_list = Multi_create_campaign_list;
4036 // get the mission count based upon the filter selected
4037 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
4038 switch(Multi_create_filter){
4039 case MISSION_TYPE_MULTI:
4040 Multi_create_list_count = Multi_create_mission_count;
4043 Multi_create_list_count = 0;
4044 // find all missions which match
4045 for(idx=0;idx<Multi_create_mission_count;idx++){
4046 if(Multi_create_mission_list[idx].flags & Multi_create_filter){
4047 Multi_create_list_count++;
4051 // if we switched modes and we have more than 0 items, sort them
4052 if(switched_modes && (Multi_create_list_count > 0)){
4057 } else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
4058 switch(Multi_create_filter){
4059 case MISSION_TYPE_MULTI:
4060 Multi_create_list_count = Multi_create_campaign_count;
4063 Multi_create_list_count = 0;
4064 // find all missions which match
4065 for(idx=0;idx<Multi_create_campaign_count;idx++){
4066 if(Multi_create_campaign_list[idx].flags & Multi_create_filter){
4067 Multi_create_list_count++;
4071 // if we switched modes and we have more than 0 items, sort them
4072 if(switched_modes && (Multi_create_list_count > 0)){
4079 // reset the list start and selected indices
4080 Multi_create_list_start = 0;
4081 Multi_create_list_select = -1;
4082 multi_create_list_select_item(Multi_create_list_start);
4084 // sort the list of missions if necessary
4086 qsort(Multi_create_file_list, Multi_create_list_count, sizeof(multi_create_info), multi_create_sort_func);
4091 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);
4095 void multi_create_game_init()
4100 // now make sure to initialze various netgame stuff based upon whether we're on a standalone or not
4101 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4102 multi_create_init_as_server();
4104 multi_create_init_as_client();
4107 // initialize the player list data
4108 Multi_create_plist_select_flag = 0;
4109 Multi_create_plist_select_id = -1;
4111 // create the interface window
4112 Multi_create_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
4113 Multi_create_window.set_mask_bmap(Multi_create_bitmap_mask_fname[gr_screen.res]);
4115 // load the background bitmap
4116 Multi_create_bitmap = bm_load(Multi_create_bitmap_fname[gr_screen.res]);
4117 if(Multi_create_bitmap < 0){
4118 // we failed to load the bitmap - this is very bad
4122 // close any previous existing instances of the chatbox and create a new one
4126 // load the help overlay
4127 help_overlay_load(MULTI_CREATE_OVERLAY);
4128 help_overlay_set_state(MULTI_CREATE_OVERLAY, 0);
4130 // initialize the common notification messaging
4131 multi_common_notify_init();
4133 // use the common interface palette
4134 multi_common_set_palette();
4136 // create the interface buttons
4137 for(idx=0; idx<MULTI_CREATE_NUM_BUTTONS; idx++){
4138 b = &Multi_create_buttons[gr_screen.res][idx];
4140 // create the object
4141 b->button.create(&Multi_create_window, "", b->x, b->y, 1, 1, ((idx == MC_SCROLL_LIST_UP) || (idx == MC_SCROLL_LIST_DOWN)), 1);
4143 // set the sound to play when highlighted
4144 b->button.set_highlight_action(common_play_highlight_sound);
4146 // set the ani for the button
4147 b->button.set_bmaps(b->filename);
4150 b->button.link_hotspot(b->hotspot);
4152 // some special case stuff for the pxo refresh button
4153 if(idx == MC_PXO_REFRESH){
4154 // if not a PXO game, or if I'm not a server disable and hide the button
4155 if(!MULTI_IS_TRACKER_GAME || !MULTIPLAYER_MASTER){
4157 b->button.disable();
4163 for(idx=0; idx<MULTI_CREATE_NUM_TEXT; idx++){
4164 Multi_create_window.add_XSTR(&Multi_create_text[gr_screen.res][idx]);
4167 // if this is a PXO game, enable the squadwar checkbox
4168 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);
4169 Multi_create_sw_checkbox.set_bmaps(Multi_create_sw_checkbox_fname[gr_screen.res], 6, 0);
4170 if(!MULTI_IS_TRACKER_GAME){
4171 Multi_create_sw_checkbox.hide();
4172 Multi_create_sw_checkbox.disable();
4176 // disable squad war button in demo
4177 Multi_create_sw_checkbox.hide();
4178 Multi_create_sw_checkbox.disable();
4181 // initialize the mission type filtering mode
4182 Multi_create_filter = MISSION_TYPE_MULTI;
4184 // initialize the list mode, and load in a list
4185 memset(Multi_create_mission_list, 0, sizeof(multi_create_info) * MULTI_CREATE_MAX_LIST_ITEMS);
4186 memset(Multi_create_campaign_list, 0, sizeof(multi_create_info) * MULTI_CREATE_MAX_LIST_ITEMS);
4187 for(idx=0; idx<MULTI_CREATE_MAX_LIST_ITEMS; idx++){
4188 Multi_create_mission_list[idx].valid_status = MVALID_STATUS_UNKNOWN;
4189 Multi_create_campaign_list[idx].valid_status = MVALID_STATUS_UNKNOWN;
4191 Multi_create_list_mode = MULTI_CREATE_SHOW_MISSIONS;
4192 Multi_create_list_start = -1;
4193 Multi_create_list_select = -1;
4194 Multi_create_list_count = 0;
4197 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);
4200 // create the player list select button
4201 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);
4202 Multi_create_player_select_button.hide();
4204 // create the mission/campaign list select button
4205 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);
4206 Multi_create_list_select_button.hide();
4208 // set hotkeys for a couple of things.
4209 Multi_create_buttons[gr_screen.res][MC_ACCEPT].button.set_hotkey(KEY_CTRLED+KEY_ENTER);
4211 // init some master tracker stuff
4212 Multi_create_frame_count = 0;
4213 Multi_create_mt_tried_login = 0;
4215 // remove campaign flags
4216 Game_mode &= ~(GM_CAMPAIGN_MODE);
4218 // send any pilots as appropriate
4219 multi_data_send_my_junk();
4220 Multi_create_file_list = Multi_create_mission_list;
4222 Multi_create_campaign_count = 0;
4223 Multi_create_mission_count = 0;
4224 Multi_create_files_loaded = 0;
4227 void multi_create_game_do()
4231 char *loading_str = XSTR("Loading", 1336);
4235 // set this if we want to show the pilot info popup
4236 Multi_create_should_show_popup = 0;
4238 // first thing is to load the files
4239 if ( !Multi_create_files_loaded ) {
4240 // if I am a client, send a list request to the server for the missions
4241 if ( MULTIPLAYER_CLIENT ) {
4242 send_mission_list_request( MISSION_LIST_REQUEST );
4246 loading_bitmap = bm_load(Multi_create_loading_fname[gr_screen.res]);
4248 // draw the background, etc
4250 GR_MAYBE_CLEAR_RES(Multi_create_bitmap);
4251 if(Multi_create_bitmap != -1){
4252 gr_set_bitmap(Multi_create_bitmap);
4256 if ( loading_bitmap > -1 ){
4257 gr_set_bitmap(loading_bitmap);
4259 gr_bitmap( Please_wait_coords[gr_screen.res][MC_X_COORD], Please_wait_coords[gr_screen.res][MC_Y_COORD] );
4261 // draw "Loading" on it
4263 gr_set_color_fast(&Color_normal);
4265 gr_get_string_size(&str_w, &str_h, loading_str);
4266 gr_string((gr_screen.max_w - str_w) / 2, (gr_screen.max_h - str_h) / 2, loading_str);
4272 multi_create_list_load_missions();
4273 multi_create_list_load_campaigns();
4275 // if this is a tracker game, validate missions
4276 if(MULTI_IS_TRACKER_GAME){
4277 multi_update_valid_missions();
4280 // update the file list
4281 multi_create_setup_list_data(MULTI_CREATE_SHOW_MISSIONS);
4284 // don't bother setting netgame state if ont the server
4285 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4286 Netgame.game_state = NETGAME_STATE_FORMING;
4287 send_netgame_update_packet();
4290 // if we're on the standalone we have to tell him that we're now in the host setup screen
4291 Net_player->state = NETPLAYER_STATE_HOST_SETUP;
4292 send_netplayer_update_packet();
4294 Multi_create_files_loaded = 1;
4297 int k = chatbox_process();
4298 k = Multi_create_window.process(k,0);
4301 // same as the cancel button
4303 if(help_overlay_active(MULTI_CREATE_OVERLAY)){
4304 help_overlay_set_state(MULTI_CREATE_OVERLAY,0);
4306 gamesnd_play_iface(SND_USER_SELECT);
4307 multi_quit_game(PROMPT_HOST);
4312 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
4313 help_overlay_set_state(MULTI_CREATE_OVERLAY, 0);
4316 // process any button clicks
4317 multi_create_check_buttons();
4319 // do any network related stuff
4320 multi_create_do_netstuff();
4322 // draw the background, etc
4324 GR_MAYBE_CLEAR_RES(Multi_create_bitmap);
4325 if(Multi_create_bitmap != -1){
4326 gr_set_bitmap(Multi_create_bitmap);
4330 // if we're not in team vs. team mode, don't draw the team buttons
4331 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
4332 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.hide();
4333 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.hide();
4334 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.disable();
4335 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.disable();
4337 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.enable();
4338 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.enable();
4339 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.unhide();
4340 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.unhide();
4343 // draw the window itself
4344 Multi_create_window.draw();
4346 gr_set_color_fast(&Color_normal);
4349 // draw Create Game text
4350 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));
4352 // draw players text
4353 gr_string(Mc_players_text[gr_screen.res][MC_X_COORD], Mc_players_text[gr_screen.res][MC_Y_COORD], XSTR("Players", 1269));
4355 // draw players text
4356 gr_string(Mc_team_text[gr_screen.res][MC_X_COORD], Mc_team_text[gr_screen.res][MC_Y_COORD], XSTR("Team", 1258));
4359 // process and display the player list
4360 // NOTE : this must be done before the buttons are checked to insure that a player hasn't dropped
4361 multi_create_plist_process();
4362 if(Netgame.type_flags & NG_TYPE_TEAM){
4363 multi_create_plist_blit_team();
4365 multi_create_plist_blit_normal();
4368 // process and display the game/campaign list
4369 multi_create_list_do();
4371 // draw the correct mission filter button
4372 multi_create_draw_filter_buttons();
4374 // display any text in the info area
4375 multi_common_render_text();
4377 // display any pending notification messages
4378 multi_common_notify_do();
4380 // force the correct mission/campaign button to light up
4381 if( Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ){
4382 Multi_create_buttons[gr_screen.res][MC_MISSION_FILTER].button.draw_forced(2);
4384 Multi_create_buttons[gr_screen.res][MC_CAMPAIGN_FILTER].button.draw_forced(2);
4387 // force draw the closed button if it is toggled on
4388 if(Netgame.flags & NG_FLAG_TEMP_CLOSED){
4389 Multi_create_buttons[gr_screen.res][MC_CLOSE].button.draw_forced(2);
4392 // process and show the chatbox thingie
4396 Multi_create_window.draw_tooltip();
4398 // display the voice status indicator
4399 multi_common_voice_display_status();
4401 // blit the help overlay if necessary
4402 help_overlay_maybe_blit(MULTI_CREATE_OVERLAY);
4405 if(MULTI_IS_TRACKER_GAME){
4406 if(Netgame.type_flags & NG_TYPE_SW){
4407 gr_set_color_fast(&Color_bright);
4409 gr_set_color_fast(&Color_normal);
4411 gr_string(Multi_create_sw_checkbox_text[gr_screen.res][0], Multi_create_sw_checkbox_text[gr_screen.res][1], "SquadWar");
4417 // if we're supposed to show the pilot info popup, do it now
4418 if(Multi_create_should_show_popup){
4419 // get the player index and address of the player item the mouse is currently over
4420 if(Multi_create_plist_select_flag){
4421 player_index = find_player_id(Multi_create_plist_select_id);
4422 if(player_index != -1){
4423 multi_pinfo_popup(&Net_players[player_index]);
4428 // increment the frame count
4429 Multi_create_frame_count++;
4432 void multi_create_game_close()
4434 // unload any bitmaps
4435 if(!bm_unload(Multi_create_bitmap)){
4436 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_create_bitmap_fname[gr_screen.res]));
4439 // unload the help overlay
4440 help_overlay_unload(MULTI_CREATE_OVERLAY);
4442 // destroy the chatbox
4445 // destroy the UI_WINDOW
4446 Multi_create_window.destroy();
4449 void multi_create_check_buttons()
4452 for(idx=0;idx<MULTI_CREATE_NUM_BUTTONS;idx++){
4453 // we only really need to check for one button pressed at a time, so we can break after
4455 if(Multi_create_buttons[gr_screen.res][idx].button.pressed()){
4456 multi_create_button_pressed(idx);
4461 // if the squad war checkbox was clicked
4462 if(Multi_create_sw_checkbox.changed()){
4463 multi_create_sw_clicked();
4467 void multi_create_button_pressed(int n)
4473 gamesnd_play_iface(SND_USER_SELECT);
4474 multi_quit_game(PROMPT_HOST);
4477 // if valid commit conditions have not been met
4478 if(!multi_create_ok_to_commit()){
4483 multi_create_accept_hit();
4488 if(!help_overlay_active(MULTI_CREATE_OVERLAY)){
4489 help_overlay_set_state(MULTI_CREATE_OVERLAY,1);
4491 help_overlay_set_state(MULTI_CREATE_OVERLAY,0);
4495 // scroll the info text box up
4496 case MC_SCROLL_INFO_UP:
4497 multi_common_scroll_text_up();
4500 // scroll the info text box down
4501 case MC_SCROLL_INFO_DOWN:
4502 multi_common_scroll_text_down();
4505 // scroll the player list up
4506 case MC_SCROLL_PLAYERS_UP:
4507 multi_create_plist_scroll_up();
4510 // scroll the player list down
4511 case MC_SCROLL_PLAYERS_DOWN:
4512 multi_create_plist_scroll_down();
4515 // scroll the game/campaign list up
4516 case MC_SCROLL_LIST_UP:
4517 multi_create_list_scroll_up();
4519 Multi_create_slider.forceUp(); // move slider up
4523 // scroll the game/campaign list down
4524 case MC_SCROLL_LIST_DOWN:
4525 multi_create_list_scroll_down();
4527 Multi_create_slider.forceDown(); // move slider down
4531 // go to the options screen
4533 gamesnd_play_iface(SND_USER_SELECT);
4534 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
4537 // show all missions
4539 if(Multi_create_filter != MISSION_TYPE_MULTI){
4540 gamesnd_play_iface(SND_USER_SELECT);
4541 Multi_create_filter = MISSION_TYPE_MULTI;
4542 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4544 gamesnd_play_iface(SND_GENERAL_FAIL);
4548 // show cooperative missions
4550 if(Multi_create_filter != MISSION_TYPE_MULTI_COOP){
4551 gamesnd_play_iface(SND_USER_SELECT);
4552 Multi_create_filter = MISSION_TYPE_MULTI_COOP;
4553 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4555 gamesnd_play_iface(SND_GENERAL_FAIL);
4559 // show team vs. team missions
4561 if(Multi_create_filter != MISSION_TYPE_MULTI_TEAMS){
4562 gamesnd_play_iface(SND_USER_SELECT);
4563 Multi_create_filter = MISSION_TYPE_MULTI_TEAMS;
4564 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4566 gamesnd_play_iface(SND_GENERAL_FAIL);
4570 // show dogfight missions
4572 case MC_SHOW_DOGFIGHT:
4573 if (Multi_create_filter != MISSION_TYPE_MULTI_DOGFIGHT){
4574 gamesnd_play_iface(SND_USER_SELECT);
4575 Multi_create_filter = MISSION_TYPE_MULTI_DOGFIGHT;
4576 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4578 gamesnd_play_iface(SND_GENERAL_FAIL);
4583 // toggle temporary netgame closed on/off
4585 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4586 Netgame.flags ^= NG_FLAG_TEMP_CLOSED;
4588 Netgame.options.flags |= MLO_FLAG_TEMP_CLOSED;
4589 multi_options_update_netgame();
4591 gamesnd_play_iface(SND_USER_SELECT);
4594 // kick the currently selected player (if possible)
4596 // lookup the player at the specified index
4597 if(Multi_create_plist_select_flag){
4598 idx = find_player_id(Multi_create_plist_select_id);
4599 // kick him - but don't ban him
4601 multi_kick_player(idx,0);
4606 // switch to individual mission mode and load in a list
4607 case MC_MISSION_FILTER:
4608 if(Multi_create_list_mode != MULTI_CREATE_SHOW_MISSIONS){
4609 Netgame.campaign_mode = MP_SINGLE;
4611 gamesnd_play_iface(SND_USER_SELECT);
4613 // update the file list
4614 multi_create_setup_list_data(MULTI_CREATE_SHOW_MISSIONS);
4616 gamesnd_play_iface(SND_GENERAL_FAIL);
4620 // switch to campaign mode and load in a list
4621 case MC_CAMPAIGN_FILTER:
4622 // switch off squad war
4623 Multi_create_sw_checkbox.set_state(0);
4624 Netgame.type_flags = NG_TYPE_COOP;
4626 if(Multi_create_list_mode != MULTI_CREATE_SHOW_CAMPAIGNS){
4627 Netgame.campaign_mode = MP_CAMPAIGN;
4629 gamesnd_play_iface(SND_USER_SELECT);
4631 // update the file list
4632 multi_create_setup_list_data(MULTI_CREATE_SHOW_CAMPAIGNS);
4634 gamesnd_play_iface(SND_GENERAL_FAIL);
4638 // attempt to set the selected player's team
4640 multi_create_set_selected_team(0);
4641 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4642 multi_team_send_update();
4646 // attempt to set the selected player's team
4648 multi_create_set_selected_team(1);
4649 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4650 multi_team_send_update();
4654 // popup the pilot info dialog for the currently selected pilot (will occur at the end of the frame)
4656 Multi_create_should_show_popup = 1;
4659 // go to the host options screen
4660 case MC_HOST_OPTIONS:
4661 gamesnd_play_iface(SND_USER_SELECT);
4662 gameseq_post_event(GS_EVENT_MULTI_HOST_OPTIONS);
4665 // refresh PXO file list
4666 case MC_PXO_REFRESH:
4667 if(!MULTI_IS_TRACKER_GAME){
4670 multi_create_refresh_pxo();
4674 gamesnd_play_iface(SND_GENERAL_FAIL);
4675 multi_common_add_notify(XSTR("Not implemented yet!",760));
4680 // do stuff like pinging servers, sending out requests, etc
4681 void multi_create_do_netstuff()
4685 // if not on a standalone
4686 void multi_create_init_as_server()
4688 // set me up as the host and master
4689 Net_player->flags |= (NETINFO_FLAG_AM_MASTER | NETINFO_FLAG_GAME_HOST);
4692 // if on a standalone
4693 void multi_create_init_as_client()
4695 Net_player->flags |= NETINFO_FLAG_GAME_HOST;
4698 // scroll up through the player list
4699 void multi_create_plist_scroll_up()
4701 gamesnd_play_iface(SND_GENERAL_FAIL);
4704 // scroll down through the player list
4705 void multi_create_plist_scroll_down()
4707 gamesnd_play_iface(SND_GENERAL_FAIL);
4710 void multi_create_plist_process()
4712 int test_count,idx,player_index;
4714 // first determine if there are 0 players in the game. This should never happen since the host is _always_ in the game
4716 for(idx=0;idx<MAX_PLAYERS;idx++){
4717 // count anyone except the standalone server (if applicable)
4718 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
4722 if(test_count <= 0){
4726 // if we had a selected item but that player has left, select myself instead
4727 if(Multi_create_plist_select_flag){
4728 player_index = find_player_id(Multi_create_plist_select_id);
4729 if(player_index == -1){
4730 Multi_create_plist_select_id = Net_player->player_id;
4733 Multi_create_plist_select_flag = 1;
4734 Multi_create_plist_select_id = Net_player->player_id;
4737 // if the player has clicked somewhere in the player list area
4738 if(Multi_create_player_select_button.pressed()){
4741 // get the player index and address of the player item the mouse is currently over
4742 player_id = multi_create_get_mouse_id();
4743 player_index = find_player_id(player_id);
4744 if(player_index != -1){
4745 Multi_create_plist_select_flag = 1;
4746 Multi_create_plist_select_id = player_id;
4751 void multi_create_plist_blit_normal()
4754 char str[CALLSIGN_LEN+5];
4755 int y_start = Mc_players_coords[gr_screen.res][MC_Y_COORD];
4758 // display all the players
4759 for(idx=0;idx<MAX_PLAYERS;idx++){
4760 // count anyone except the standalone server (if applicable)
4761 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
4765 // highlight him if he's the host
4766 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4767 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4768 gr_set_color_fast(&Color_text_active_hi);
4770 gr_set_color_fast(&Color_bright);
4773 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4774 gr_set_color_fast(&Color_text_active);
4776 gr_set_color_fast(&Color_text_normal);
4780 // optionally draw his CD status
4781 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4782 gr_set_bitmap(Multi_common_icons[MICON_CD]);
4783 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4785 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4788 // make sure the string will fit, then display it
4789 strcpy(str,Net_players[idx].player->callsign);
4790 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4791 strcat(str,XSTR("(O)",787)); // [[ Observer ]]
4793 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4794 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4801 void multi_create_plist_blit_team()
4804 char str[CALLSIGN_LEN+1];
4805 int y_start = Mc_players_coords[gr_screen.res][MC_Y_COORD];
4808 // display all the red players first
4809 for(idx=0;idx<MAX_PLAYERS;idx++){
4810 // count anyone except the standalone server (if applicable)
4811 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
4812 // reset total offset
4815 // highlight him if he's the host
4816 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4817 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4818 gr_set_color_fast(&Color_text_active_hi);
4820 // be sure to blit the correct team button
4821 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.draw_forced(2);
4823 gr_set_color_fast(&Color_bright);
4826 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4827 gr_set_color_fast(&Color_text_active);
4829 // be sure to blit the correct team button
4830 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.draw_forced(2);
4832 gr_set_color_fast(&Color_text_normal);
4836 // optionally draw his CD status
4837 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4838 gr_set_bitmap(Multi_common_icons[MICON_CD]);
4839 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4841 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4844 // blit the red team indicator
4845 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
4846 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
4847 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT]);
4848 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4850 total_offset += Multi_common_icon_dims[MICON_TEAM0_SELECT][0] + 1;
4853 if(Multi_common_icons[MICON_TEAM0] != -1){
4854 gr_set_bitmap(Multi_common_icons[MICON_TEAM0]);
4855 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4857 total_offset += Multi_common_icon_dims[MICON_TEAM0][0] + 1;
4861 // make sure the string will fit
4862 strcpy(str,Net_players[idx].player->callsign);
4863 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4864 strcat(str,XSTR("(O)",787));
4866 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4868 // display him in the correct half of the list depending on his team
4869 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4874 // display all the green players next
4875 for(idx=0;idx<MAX_PLAYERS;idx++){
4876 // count anyone except the standalone server (if applicable)
4877 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
4878 // reset total offset
4881 // highlight him if he's the host
4882 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4883 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4884 gr_set_color_fast(&Color_text_active_hi);
4886 // be sure to blit the correct team button
4887 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.draw_forced(2);
4889 gr_set_color_fast(&Color_bright);
4892 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4893 gr_set_color_fast(&Color_text_active);
4895 // be sure to blit the correct team button
4896 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.draw_forced(2);
4898 gr_set_color_fast(&Color_text_normal);
4902 // optionally draw his CD status
4903 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4904 gr_set_bitmap(Multi_common_icons[MICON_CD]);
4905 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4907 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4910 // blit the red team indicator
4911 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
4912 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
4913 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT]);
4914 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4916 total_offset += Multi_common_icon_dims[MICON_TEAM1_SELECT][0] + 1;
4919 if(Multi_common_icons[MICON_TEAM1] != -1){
4920 gr_set_bitmap(Multi_common_icons[MICON_TEAM1]);
4921 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4923 total_offset += Multi_common_icon_dims[MICON_TEAM1][0] + 1;
4927 // make sure the string will fit
4928 strcpy(str,Net_players[idx].player->callsign);
4929 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4930 strcat(str,XSTR("(O)",787));
4932 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4934 // display him in the correct half of the list depending on his team
4935 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4941 void multi_create_list_scroll_up()
4943 if(Multi_create_list_start > 0){
4944 Multi_create_list_start--;
4946 gamesnd_play_iface(SND_SCROLL);
4948 gamesnd_play_iface(SND_GENERAL_FAIL);
4952 void multi_create_list_scroll_down()
4954 if((Multi_create_list_count - Multi_create_list_start) > Multi_create_list_max_display[gr_screen.res]){
4955 Multi_create_list_start++;
4957 gamesnd_play_iface(SND_SCROLL);
4959 gamesnd_play_iface(SND_GENERAL_FAIL);
4963 // gets a list of multiplayer misisons
4964 void multi_create_list_load_missions()
4966 char *fname, mission_name[NAME_LENGTH+1];
4967 char wild_card[256];
4970 memset(wild_card, 0, 256);
4971 strcpy(wild_card, NOX("*"));
4972 strcat(wild_card, FS_MISSION_FILE_EXT);
4973 file_count = cf_get_file_list_preallocated(MULTI_CREATE_MAX_LIST_ITEMS, Multi_create_files_array, NULL, CF_TYPE_MISSIONS, wild_card);
4974 Multi_create_mission_count = 0;
4976 // maybe create a standalone dialog
4977 if(Game_mode & GM_STANDALONE_SERVER){
4978 std_create_gen_dialog("Loading missions");
4979 std_gen_set_text("Mission:", 1);
4982 for(idx = 0; idx < file_count; idx++){
4983 int flags,max_players;
4987 fname = Multi_create_files_array[idx];
4989 // tack on any necessary file extension
4990 filename = cf_add_ext( fname, FS_MISSION_FILE_EXT );
4992 // for multiplayer beta builds, only accept builtin missions
4993 #if defined(MULTIPLAYER_BETA_BUILD) || defined(FS2_DEMO)
4994 if(game_find_builtin_mission(filename) == NULL){
4997 #elif defined(PD_BUILD)
4998 if((game_find_builtin_mission(filename) == NULL) && !strstr(filename, "peterdrake")){
5003 if(Game_mode & GM_STANDALONE_SERVER){
5004 std_gen_set_text(filename, 2);
5007 flags = mission_parse_is_multi(filename, mission_name);
5009 // if the mission is a multiplayer mission, then add it to the mission list
5011 max_players = mission_parse_get_multi_mission_info( filename );
5012 m_respawn = The_mission.num_respawns;
5014 if ( Multi_create_mission_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
5015 multi_create_info *mcip;
5017 mcip = &Multi_create_mission_list[Multi_create_mission_count];
5018 strcpy(mcip->filename, filename );
5019 strcpy(mcip->name, mission_name );
5020 mcip->flags = flags;
5021 mcip->respawn = m_respawn;
5022 mcip->max_players = (ubyte)max_players;
5024 // get any additional information for possibly builtin missions
5025 fs_builtin_mission *fb = game_find_builtin_mission(filename);
5029 Multi_create_mission_count++;
5035 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);
5038 // maybe create a standalone dialog
5039 if(Game_mode & GM_STANDALONE_SERVER){
5040 std_destroy_gen_dialog();
5044 void multi_create_list_load_campaigns()
5047 int idx, file_count;
5048 int campaign_type,max_players;
5050 char wild_card[256];
5052 // maybe create a standalone dialog
5053 if(Game_mode & GM_STANDALONE_SERVER){
5054 std_create_gen_dialog("Loading campaigns");
5055 std_gen_set_text("Campaign:", 1);
5058 Multi_create_campaign_count = 0;
5059 memset(wild_card, 0, 256);
5060 strcpy(wild_card, NOX("*"));
5061 strcat(wild_card, FS_CAMPAIGN_FILE_EXT);
5062 file_count = cf_get_file_list_preallocated(MULTI_CREATE_MAX_LIST_ITEMS, Multi_create_files_array, NULL, CF_TYPE_MISSIONS, wild_card);
5063 for(idx = 0; idx < file_count; idx++){
5065 char *filename, name[NAME_LENGTH];
5067 fname = Multi_create_files_array[idx];
5069 // tack on any necessary file extension
5070 filename = cf_add_ext( fname, FS_CAMPAIGN_FILE_EXT );
5072 // for multiplayer beta builds, only accept builtin missions
5073 #if defined(MULTIPLAYER_BETA_BUILD) || defined(FS2_DEMO)
5074 if(game_find_builtin_mission(filename) == NULL){
5077 #elif defined(PD_BUILD)
5078 if((game_find_builtin_mission(filename) == NULL) && !strstr(filename, "peterdrake")){
5083 if(Game_mode & GM_STANDALONE_SERVER){
5084 std_gen_set_text(filename, 2);
5087 // if the campaign is a multiplayer campaign, then add the data to the campaign list items
5088 flags = mission_campaign_parse_is_multi( filename, name );
5089 if( flags != CAMPAIGN_TYPE_SINGLE && mission_campaign_get_info(filename,title,&campaign_type,&max_players)) {
5090 if ( Multi_create_campaign_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
5091 multi_create_info *mcip;
5093 mcip = &Multi_create_campaign_list[Multi_create_campaign_count];
5094 strcpy(mcip->filename, filename );
5095 strcpy(mcip->name, name );
5097 // setup various flags
5098 if ( flags == CAMPAIGN_TYPE_MULTI_COOP ){
5099 mcip->flags = MISSION_TYPE_MULTI_COOP | MISSION_TYPE_MULTI;
5100 } else if ( flags == CAMPAIGN_TYPE_MULTI_TEAMS ) {
5101 mcip->flags = MISSION_TYPE_MULTI_TEAMS | MISSION_TYPE_MULTI;
5103 Int3(); // bogus campaign multi type -- find allender
5106 // 0 respawns for campaign files (should be contained within the mission files themselves)
5109 // 0 max players for campaign files
5110 mcip->max_players = (unsigned char)max_players;
5112 // get any additional information for possibly builtin missions
5113 fs_builtin_mission *fb = game_find_builtin_mission(filename);
5117 Multi_create_campaign_count++;
5122 // maybe create a standalone dialog
5123 if(Game_mode & GM_STANDALONE_SERVER){
5124 std_destroy_gen_dialog();
5128 void multi_create_list_do()
5131 int start_index,stop_index;
5132 char selected_name[255];
5134 // bail early if there aren't any selectable items
5135 if(Multi_create_list_count == 0){
5139 // first check to see if the user has clicked on an item
5140 if(Multi_create_list_select_button.pressed()){
5142 Multi_create_list_select_button.get_mouse_pos(NULL,&y);
5145 // make sure we are selectedin valid indices
5146 if((item < Multi_create_list_max_display[gr_screen.res]) && (item >= 0)){
5147 item += Multi_create_list_start;
5149 if(item < Multi_create_list_count){
5150 multi_create_list_select_item(item);
5151 gamesnd_play_iface(SND_IFACE_MOUSE_CLICK);
5156 // bail early if we don't have a start position
5157 if(Multi_create_list_start == -1){
5161 // display the list of individual campaigns/missions
5163 int y_start = Mc_list_coords[gr_screen.res][MC_Y_COORD];
5165 start_index = multi_create_select_to_index(Multi_create_list_start);
5166 stop_index = Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ? Multi_create_mission_count : Multi_create_campaign_count;
5167 for(idx=start_index; idx<stop_index; idx++){
5168 // see if we should drop out
5169 if(count == Multi_create_list_max_display[gr_screen.res]){
5173 // see if we should filter out this mission
5174 if ( !(Multi_create_file_list[idx].flags & Multi_create_filter) ){
5178 // highlight the selected item
5179 multi_create_select_to_filename(Multi_create_list_select,selected_name);
5180 if(!strcmp(selected_name,Multi_create_file_list[idx].filename)){
5181 gr_set_color_fast(&Color_text_selected);
5183 gr_set_color_fast(&Color_text_normal);
5186 // draw the type icon
5187 multi_create_list_blit_icons(idx, y_start);
5189 // force fit the mission name string
5190 strcpy(selected_name,Multi_create_file_list[idx].name);
5191 gr_force_fit_string(selected_name,255,Mc_column1_w[gr_screen.res]);
5192 gr_string(Mc_mission_name_x[gr_screen.res],y_start,selected_name);
5194 // draw the max players if in mission mode
5195 sprintf(selected_name,"%d",(int)Multi_create_file_list[idx].max_players);
5196 gr_string(Mc_mission_count_x[gr_screen.res],y_start,selected_name);
5198 // force fit the mission filename string
5199 strcpy(selected_name,Multi_create_file_list[idx].filename);
5200 gr_force_fit_string(selected_name,255,Mc_column3_w[gr_screen.res]);
5201 gr_string(Mc_mission_fname_x[gr_screen.res],y_start,selected_name);
5208 // takes care of stuff like changing indices around and setting up the netgame structure
5209 void multi_create_list_select_item(int n)
5211 int abs_index,campaign_type,max_players;
5212 char title[NAME_LENGTH+1];
5213 netgame_info ng_temp;
5216 char *campaign_desc;
5218 // if not on the standalone server
5219 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5222 // on the standalone
5224 memset(&ng_temp,0,sizeof(netgame_info));
5228 if ( n != Multi_create_list_select ) {
5229 // check to see if this is a valid index, and bail if it is not
5230 abs_index = multi_create_select_to_index(n);
5231 if(abs_index == -1){
5235 Multi_create_list_select = n;
5237 // set the mission name
5238 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5239 multi_create_select_to_filename(n,ng->mission_name);
5241 multi_create_select_to_filename(n,ng->campaign_name);
5244 // make sure the netgame type is properly set
5245 int old_type = Netgame.type_flags;
5246 abs_index = multi_create_select_to_index(n);
5247 if(abs_index != -1){
5248 if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_TEAMS){
5249 // if we're in squad war mode, leave it as squad war
5250 if(old_type & NG_TYPE_SW){
5251 ng->type_flags = NG_TYPE_SW;
5253 ng->type_flags = NG_TYPE_TVT;
5255 } else if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_COOP){
5256 ng->type_flags = NG_TYPE_COOP;
5257 } else if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_DOGFIGHT){
5258 ng->type_flags = NG_TYPE_DOGFIGHT;
5262 // if we're no longer in a TvT game, just uncheck the squadwar checkbox
5263 if(!(ng->type_flags & NG_TYPE_TEAM)){
5264 Multi_create_sw_checkbox.set_state(0);
5267 // if we switched from something else to team vs. team mode, do some special processing
5268 if((ng->type_flags & NG_TYPE_TEAM) && (ng->type_flags != old_type) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
5272 switch(Multi_create_list_mode){
5273 case MULTI_CREATE_SHOW_MISSIONS:
5274 // don't forget to update the info box window thingie
5275 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5276 ship_init(); // mwa -- 10/15/97. Call this function to reset number of ships in mission
5277 ng->max_players = mission_parse_get_multi_mission_info( ng->mission_name );
5279 Assert(ng->max_players > 0);
5280 strcpy(ng->title,The_mission.name);
5282 // set the information area text
5283 multi_common_set_text(The_mission.mission_desc);
5285 // if we're on the standalone, send a request for the description
5287 send_netgame_descript_packet(&Netgame.server_addr,0);
5288 multi_common_set_text("");
5291 // set the respawns as appropriate
5292 if(Netgame.options.respawn <= Multi_create_file_list[abs_index].respawn){
5293 ng->respawn = Netgame.options.respawn;
5294 nprintf(("Network","Using netgame options for respawn count (%d %d)\n",Netgame.options.respawn,Multi_create_file_list[abs_index].respawn));
5296 ng->respawn = Multi_create_file_list[abs_index].respawn;
5297 nprintf(("Network","Using mission settings for respawn count (%d %d)\n",Netgame.options.respawn,Multi_create_file_list[abs_index].respawn));
5300 case MULTI_CREATE_SHOW_CAMPAIGNS:
5301 // if not on the standalone server
5302 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5303 // get the campaign info
5304 memset(title,0,NAME_LENGTH+1);
5305 if(!mission_campaign_get_info(ng->campaign_name,title,&campaign_type,&max_players, &campaign_desc)) {
5306 memset(ng->campaign_name,0,NAME_LENGTH+1);
5307 ng->max_players = 0;
5309 // if we successfully got the # of players
5311 memset(ng->title,0,NAME_LENGTH+1);
5312 strcpy(ng->title,title);
5313 ng->max_players = max_players;
5316 nprintf(("Network","MC MAX PLAYERS : %d\n",ng->max_players));
5318 // set the information area text
5319 // multi_common_set_text(ng->title);
5320 multi_common_set_text(campaign_desc);
5322 // if on the standalone server, send a request for the description
5324 // no descriptions currently kept for campaigns
5327 // netgame respawns are always 0 for campaigns (until the first mission is loaded)
5332 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5334 send_netgame_update_packet();
5336 // update all machines about stuff like respawns, etc.
5337 multi_options_update_netgame();
5339 multi_options_update_mission(ng, Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ? 1 : 0);
5344 void multi_create_list_blit_icons(int list_index, int y_start)
5346 multi_create_info *mcip;
5347 fs_builtin_mission *fb;
5350 // get a pointer to the list item
5351 max_index = Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ? Multi_create_mission_count - 1 : Multi_create_campaign_count - 1;
5352 if((list_index < 0) || (list_index > max_index)){
5355 mcip = &Multi_create_file_list[list_index];
5357 // blit the multiplayer type icons
5358 if(mcip->flags & MISSION_TYPE_MULTI_COOP){
5359 if(Multi_common_icons[MICON_COOP] >= 0){
5360 gr_set_bitmap(Multi_common_icons[MICON_COOP]);
5361 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5363 } else if(mcip->flags & MISSION_TYPE_MULTI_TEAMS){
5364 if(Multi_common_icons[MICON_TVT] >= 0){
5365 gr_set_bitmap(Multi_common_icons[MICON_TVT]);
5366 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5368 } else if(mcip->flags & MISSION_TYPE_MULTI_DOGFIGHT){
5369 if(Multi_common_icons[MICON_DOGFIGHT] >= 0){
5370 gr_set_bitmap(Multi_common_icons[MICON_DOGFIGHT]);
5371 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5375 // if its a valid mission, blit the valid mission icon
5376 if(MULTI_IS_TRACKER_GAME && (mcip->valid_status == MVALID_STATUS_VALID)){
5377 if(Multi_common_icons[MICON_VALID] >= 0){
5378 gr_set_bitmap(Multi_common_icons[MICON_VALID]);
5379 gr_bitmap(Mc_icon_valid_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_valid_coords[gr_screen.res][MC_Y_COORD]);
5383 // now see if its a builtin mission
5384 fb = game_find_builtin_mission(mcip->filename);
5385 // if the mission is from volition, blit the volition icon
5386 if((fb != NULL) && (fb->flags & FSB_FROM_VOLITION)){
5387 if(Multi_common_icons[MICON_VOLITION] >= 0){
5388 gr_set_bitmap(Multi_common_icons[MICON_VOLITION]);
5389 gr_bitmap(Mc_icon_volition_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_volition_coords[gr_screen.res][MC_Y_COORD]);
5394 // check for mdisk mission
5395 fb = game_find_builtin_mission(mcip->filename);
5396 if((fb != NULL) && (fb->flags & FSB_FROM_MDISK)){
5397 if(Multi_common_icons[MICON_MDISK] >= 0){
5398 gr_set_bitmap(Multi_common_icons[MICON_MDISK]);
5399 gr_bitmap(Mc_icon_silent_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_silent_coords[gr_screen.res][MC_Y_COORD]);
5405 void multi_create_accept_hit()
5407 char selected_name[255];
5408 int start_campaign = 0;
5410 // make sure all players have finished joining
5411 if(!multi_netplayer_state_check(NETPLAYER_STATE_JOINED,1)){
5412 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("Please wait until all clients have finished joining",788));
5413 gamesnd_play_iface(SND_GENERAL_FAIL);
5416 gamesnd_play_iface(SND_COMMIT_PRESSED);
5419 // do single mission stuff
5420 switch(Multi_create_list_mode){
5421 case MULTI_CREATE_SHOW_MISSIONS:
5422 if(Multi_create_list_select != -1){
5423 // set the netgame mode
5424 Netgame.campaign_mode = MP_SINGLE;
5426 // setup various filenames and mission names
5427 multi_create_select_to_filename(Multi_create_list_select,selected_name);
5428 strncpy( Game_current_mission_filename, selected_name, MAX_FILENAME_LEN );
5429 strncpy(Netgame.mission_name,selected_name,MAX_FILENAME_LEN);
5432 ml_printf(NOX("Starting single mission %s, with %d players"), Game_current_mission_filename, multi_num_players());
5434 multi_common_add_notify(XSTR("No mission selected!",789));
5439 case MULTI_CREATE_SHOW_CAMPAIGNS:
5440 // do campaign related stuff
5441 if(Multi_create_list_select != -1){
5442 // set the netgame mode
5443 Netgame.campaign_mode = MP_CAMPAIGN;
5445 // start a campaign instead of a single mission
5446 multi_create_select_to_filename(Multi_create_list_select,selected_name);
5447 multi_campaign_start(selected_name);
5451 ml_printf(NOX("Starting campaign %s, with %d players"), selected_name, multi_num_players());
5453 multi_common_add_notify(XSTR("No campaign selected!",790));
5459 // if this is a team vs team situation, lock the players send a final team update
5460 if((Netgame.type_flags & NG_TYPE_TEAM) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
5461 multi_team_host_lock_all();
5462 multi_team_send_update();
5465 // if not on the standalone, move to the mission sync state which will take care of everything
5466 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5467 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
5468 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
5470 // otherwise tell the standalone to do so
5472 // when the standalone receives this, he'll do the mission syncing himself
5473 send_mission_sync_packet(MULTI_SYNC_PRE_BRIEFING,start_campaign);
5477 void multi_create_draw_filter_buttons()
5479 // highlight the correct filter button
5480 if ( Multi_create_filter == MISSION_TYPE_MULTI ){
5481 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL].button.draw_forced(2);
5482 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_COOP ) {
5483 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 1].button.draw_forced(2);
5484 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_TEAMS ) {
5485 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 2].button.draw_forced(2);
5486 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_DOGFIGHT ){
5487 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 3].button.draw_forced(2);
5493 void multi_create_set_selected_team(int team)
5497 // if we don't currently have a player selected, don't do anything
5498 if(!Multi_create_plist_select_flag){
5499 gamesnd_play_iface(SND_GENERAL_FAIL);
5502 gamesnd_play_iface(SND_USER_SELECT);
5504 // otherwise attempt to set the team for this guy
5505 player_index = find_player_id(Multi_create_plist_select_id);
5506 if(player_index != -1){
5507 multi_team_set_team(&Net_players[player_index],team);
5511 void multi_create_handle_join(net_player *pl)
5513 // for now just play a bloop sound
5514 gamesnd_play_iface(SND_ICON_DROP_ON_WING);
5517 // fill in net address of player the mouse is over, return player index (or -1 if none)
5518 short multi_create_get_mouse_id()
5520 // determine where he clicked (y pixel value)
5522 Multi_create_player_select_button.get_mouse_pos(NULL,&y);
5524 // select things a little differently if we're in team vs. team or non-team vs. team mode
5526 if(Netgame.type_flags & NG_TYPE_TEAM){
5527 int player_index = -1;
5529 // look through all of team red first
5530 for(idx=0;idx<MAX_PLAYERS;idx++){
5531 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
5534 // if this is the _nth_ guy
5542 // if we still haven't found him yet, look through the green team
5543 if(player_index == -1){
5544 for(idx=0;idx<MAX_PLAYERS;idx++){
5545 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
5547 // if this is the _nth_ guy
5556 if(player_index != -1){
5557 return Net_players[player_index].player_id;
5560 // select the nth active player if possible, disregarding the standalone server
5561 for(idx=0;idx<MAX_PLAYERS;idx++){
5562 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
5565 // if this is the _nth_ guy
5567 return Net_players[idx].player_id;
5576 void multi_create_select_to_filename(int select_index,char *filename)
5580 // look through the mission list
5581 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5582 for(idx=0;idx<Multi_create_mission_count;idx++){
5583 if(Multi_create_file_list[idx].flags & Multi_create_filter){
5587 // if we found the item
5588 if(select_index < 0){
5589 strcpy(filename,Multi_create_file_list[idx].filename);
5594 // look through the campaign list
5595 else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
5596 for(idx=0;idx<Multi_create_campaign_count;idx++){
5599 // if we found the item
5600 if(select_index < 0){
5601 strcpy(filename,Multi_create_file_list[idx].filename);
5607 strcpy(filename,"");
5610 int multi_create_select_to_index(int select_index)
5613 int lookup_index = 0;
5615 // look through the mission list
5616 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5617 for(idx=0;idx<Multi_create_mission_count;idx++){
5618 if(Multi_create_file_list[idx].flags & Multi_create_filter){
5622 // if we found the item
5623 if(select_index < lookup_index){
5628 // look through the campaign list
5629 else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
5630 for(idx=0;idx<Multi_create_campaign_count;idx++){
5633 // if we found the item
5634 if(select_index < 0){
5643 int multi_create_ok_to_commit()
5645 int player_count, observer_count, idx;
5646 int notify_of_hacked_ships_tbl = 0;
5647 int notify_of_hacked_weapons_tbl = 0;
5648 char err_string[255];
5652 // make sure we have a valid mission selected
5653 if(Multi_create_list_select < 0){
5657 // if this is not a valid mission, let the player know
5658 abs_index = multi_create_select_to_index(Multi_create_list_select);
5663 // if we're playing with a hacked ships.tbl (on PXO)
5664 notify_of_hacked_ships_tbl = 0;
5665 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5666 if(!Game_ships_tbl_valid){
5667 notify_of_hacked_ships_tbl = 1;
5670 if(Netgame.flags & NG_FLAG_HACKED_SHIPS_TBL){
5671 notify_of_hacked_ships_tbl = 1;
5674 if(!MULTI_IS_TRACKER_GAME){
5675 notify_of_hacked_ships_tbl = 0;
5677 if(notify_of_hacked_ships_tbl){
5678 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){
5683 // if we're playing with a hacked weapons.tbl (on PXO)
5684 notify_of_hacked_weapons_tbl = 0;
5685 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5686 if(!Game_weapons_tbl_valid){
5687 notify_of_hacked_weapons_tbl = 1;
5690 if(Netgame.flags & NG_FLAG_HACKED_WEAPONS_TBL){
5691 notify_of_hacked_weapons_tbl = 1;
5694 if(!MULTI_IS_TRACKER_GAME){
5695 notify_of_hacked_weapons_tbl = 0;
5697 if(notify_of_hacked_weapons_tbl){
5698 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){
5703 // if any of the players have hacked data
5705 for(idx=0; idx<MAX_PLAYERS; idx++){
5706 // look for hacked players
5707 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAXOR)){
5711 // message everyone - haha
5712 if(Net_players[idx].player != NULL){
5713 sprintf(err_string, "%s %s", Net_players[idx].player->callsign, XSTR("has hacked tables/data", 1271));
5715 sprintf(err_string, "somebody %s", XSTR("has hacked tables/data", 1271));
5717 send_game_chat_packet(Net_player, err_string, MULTI_MSG_ALL, NULL, NULL, 1);
5720 // if we found a hacked set of data
5723 if(MULTI_IS_TRACKER_GAME){
5724 // don't allow squad war matches to continue
5725 if(Netgame.type_flags & NG_TYPE_SW){
5727 // if this is squad war, don't allow it to continue
5728 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));
5733 // otherwise, warn the players that stats will not saved
5735 // if this is squad war, don't allow it to continue
5736 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){
5741 // non-pxo, just give a notice
5743 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){
5749 // check to see that we don't have too many observers
5750 observer_count = multi_num_observers();
5751 if(observer_count > Netgame.options.max_observers){
5752 // print up the error string
5753 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);
5755 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, err_string);
5759 // check to see that we have a valid # of players for the the # of ships in the game
5760 player_count = multi_num_players();
5761 if(player_count > Netgame.max_players){
5762 // print up the error string
5763 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);
5765 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, err_string);
5769 // check to see if teams are assigned properly in a team vs. team situation
5770 if(Netgame.type_flags & NG_TYPE_TEAM){
5771 if(!multi_team_ok_to_commit()){
5772 gamesnd_play_iface(SND_GENERAL_FAIL);
5773 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("Teams and/or team captains are not assigned properly", 793));
5779 if(!multi_create_verify_cds()){
5780 gamesnd_play_iface(SND_GENERAL_FAIL);
5782 #ifdef MULTIPLAYER_BETA_BUILD
5783 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, "You need 1 CD for every player!");
5785 #ifdef DVD_MESSAGE_HACK
5786 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You need 1 DVD for every 4 players!", 794));
5788 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You need 1 CD for every 4 players!", 794));
5794 // if we're playing on the tracker
5795 if(MULTI_IS_TRACKER_GAME){
5796 #ifdef PXO_CHECK_VALID_MISSIONS
5797 if((Multi_create_file_list == Multi_create_mission_list) && (Multi_create_file_list[abs_index].valid_status != MVALID_STATUS_VALID)){
5798 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){
5805 if(!(Netgame.type_flags & NG_TYPE_SW)){
5806 // if he is playing by himself, tell him stats will not be accepted
5807 if(multi_num_players() == 1){
5808 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){
5821 int multi_create_verify_cds()
5823 int player_count = multi_num_players();
5827 // count how many cds we have
5829 for(idx=0;idx<MAX_PLAYERS;idx++){
5830 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAS_CD)){
5835 // for the beta, everyone must have a CD
5836 #ifdef MULTIPLAYER_BETA_BUILD
5837 if(multi_cd_count < player_count){
5841 // determine if we have enough
5842 float ratio = (float)player_count / (float)multi_cd_count;
5843 // greater than a 4 to 1 ratio
5849 // we meet the conditions
5853 // returns an index into Multi_create_mission_list
5854 int multi_create_lookup_mission(char *fname)
5858 for(idx=0; idx<Multi_create_mission_count; idx++){
5859 if(!stricmp(fname, Multi_create_mission_list[idx].filename)){
5864 // couldn't find the mission
5868 // returns an index into Multi_create_campaign_list
5869 int multi_create_lookup_campaign(char *fname)
5873 for(idx=0; idx<Multi_create_campaign_count; idx++){
5874 if(!stricmp(fname, Multi_create_campaign_list[idx].filename)){
5879 // couldn't find the campaign
5883 void multi_create_refresh_pxo()
5885 // delete mvalid.cfg if it exists
5886 cf_delete(MULTI_VALID_MISSION_FILE, CF_TYPE_DATA);
5888 // refresh missions from the tracker
5889 multi_update_valid_missions();
5892 void multi_create_sw_clicked()
5894 netgame_info ng_temp;
5897 int file_index = multi_create_select_to_index(Multi_create_list_select);
5899 // either a temporary netgame or the real one
5900 if(MULTIPLAYER_MASTER){
5907 // maybe switch squad war off
5908 if(!Multi_create_sw_checkbox.checked()){
5909 // if the mission selected is a coop mission, go back to coop mode
5910 Assert(file_index != -1);
5911 if(file_index == -1){
5912 ng->type_flags = NG_TYPE_COOP;
5914 if(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_TEAMS){
5915 ng->type_flags = NG_TYPE_TVT;
5916 } else if(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_DOGFIGHT){
5917 ng->type_flags = NG_TYPE_DOGFIGHT;
5919 ng->type_flags = NG_TYPE_COOP;
5922 // switch squad war on
5924 Assert(file_index != -1);
5925 if((file_index == -1) || !(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_TEAMS)){
5926 Multi_create_sw_checkbox.set_state(0);
5928 // at this point we know its safe to switch squad war on
5929 ng->type_flags = NG_TYPE_SW;
5934 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5936 send_netgame_update_packet();
5938 // update all machines about stuff like respawns, etc.
5939 multi_options_update_netgame();
5941 // on the standalone
5943 // standalone will take care of polling the usertracker
5944 multi_options_update_mission(ng, Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ? 1 : 0);
5949 // -------------------------------------------------------------------------------------------------------------
5951 // MULTIPLAYER HOST OPTIONS SCREEN
5954 #define MULTI_HO_NUM_BUTTONS 12
5955 #define MULTI_HO_NUM_RADIO_BUTTONS 10
5959 #define MULTI_HO_PALETTE "InterfacePalette"
5961 static char *Multi_ho_bitmap_fname[GR_NUM_RESOLUTIONS] = {
5962 "MultiHost", // GR_640
5963 "2_MultiHost" // GR_1024
5966 static char *Multi_ho_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
5967 "MultiHost-M", // GR_640
5968 "2_MultiHost-M" // GR_1024
5972 UI_WINDOW Multi_ho_window; // the window object for the join screen
5973 UI_INPUTBOX Multi_ho_respawns; // the # of respawns allowed in the game
5974 UI_INPUTBOX Multi_ho_time_limit; // mission time limit
5975 UI_INPUTBOX Multi_ho_voice_wait; // wait time between tokens
5976 UI_INPUTBOX Multi_ho_kill_limit; // kill limit in a furball mission
5977 UI_INPUTBOX Multi_ho_obs; // # of observers we'll allow
5978 int Multi_ho_bitmap; // the background bitmap
5980 // constants for coordinate lookup
5981 #define MULTI_HO_X_COORD 0
5982 #define MULTI_HO_Y_COORD 1
5983 #define MULTI_HO_W_COORD 2
5984 #define MULTI_HO_H_COORD 3
5985 #define MULTI_HO_TEXT_X_COORD 4
5986 #define MULTI_HO_TEXT_Y_COORD 5
5989 #define MULTI_HO_MSG_RANK 0 // highest ranking players can do messaging
5990 #define MULTI_HO_MSG_LEADER 1 // wing/team leaders can do messaging
5991 #define MULTI_HO_MSG_ANY 2 // any player can do messaging
5992 #define MULTI_HO_MSG_HOST 3 // only the host can do messaging
5993 #define MULTI_HO_END_RANK 4 // highest rank can and host can end mission
5994 #define MULTI_HO_END_LEADER 5 // wing/team leaders and host can end the mission
5995 #define MULTI_HO_END_ANY 6 // any player can end the mission
5996 #define MULTI_HO_END_HOST 7 // only host can end the mission
5997 #define MULTI_HO_VOICE_ON 8 // voice toggled on
5998 #define MULTI_HO_VOICE_OFF 9 // voice toggled off
5999 #define MULTI_HO_HOST_MODIFIES 10 // only the host or team captains can modify ships/weapons in briefing
6000 #define MULTI_HO_ACCEPT 11 // accept button
6002 ui_button_info Multi_ho_buttons[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_BUTTONS] = {
6005 // who is allowed to message
6006 ui_button_info("MH_00", 13, 157, -1, -1, 0), // highest rank
6007 ui_button_info("MH_01", 13, 179, -1, -1, 1), // team/wing leader
6008 ui_button_info("MH_02", 13, 200, -1, -1, 2), // any
6009 ui_button_info("MH_03", 13, 223, -1, -1, 3), // host
6011 // who is allowed to end the mission
6012 ui_button_info("MH_09", 13, 273, -1, -1, 9), // highest rank
6013 ui_button_info("MH_10", 13, 295, -1, -1, 10), // team/wing leader
6014 ui_button_info("MH_11", 13, 317, -1, -1, 11), // any
6015 ui_button_info("MH_12", 13, 339, -1, -1, 12), // host
6017 // voice on/off button
6018 ui_button_info("MH_14", 396, 156, -1, -1, 14),
6019 ui_button_info("MH_15", 453, 156, -1, -1, 15),
6021 // host modifies ships
6022 ui_button_info("MH_19", 215, 410, -1, -1, 19),
6025 ui_button_info("MH_18", 560, 411, -1, -1, 18),
6027 // who is allowed to message
6028 ui_button_info("MH_00", 3, 160, 46, 166, 0), // highest rank
6029 ui_button_info("MH_01", 3, 179, 46, 185, 1), // team/wing leader
6030 ui_button_info("MH_02", 3, 196, 46, 203, 2), // any
6031 ui_button_info("MH_03", 3, 214, 46, 220, 3), // host
6033 // who is allowed to end the mission
6034 ui_button_info("MH_04", 3, 257, 46, 265, 4), // highest rank
6035 ui_button_info("MH_05", 3, 276, 46, 283, 5), // team/wing leader
6036 ui_button_info("MH_06", 3, 294, 46, 300, 6), // any
6037 ui_button_info("MH_07", 3, 311, 46, 317, 7), // host
6039 // voice on/off button
6040 ui_button_info("MH_09", 542, 158, 545, 185, 9),
6041 ui_button_info("MH_10", 598, 158, 604, 185, 10),
6043 // host modifies ships
6044 ui_button_info("MH_13", 542, 377, 437, 363, 13),
6047 ui_button_info("MH_14", 572, 428, 580, 414, 14),
6051 // who is allowed to message
6052 ui_button_info("2_MH_00", 5, 256, 73, 269, 0), // highest rank
6053 ui_button_info("2_MH_01", 5, 286, 73, 297, 1), // team/wing leader
6054 ui_button_info("2_MH_02", 5, 314, 73, 325, 2), // any
6055 ui_button_info("2_MH_03", 5, 341, 73, 352, 3), // host
6057 // who is allowed to end the mission
6058 ui_button_info("2_MH_04", 5, 412, 73, 425, 4), // highest rank
6059 ui_button_info("2_MH_05", 5, 442, 73, 452, 5), // team/wing leader
6060 ui_button_info("2_MH_06", 5, 470, 73, 480, 6), // any
6061 ui_button_info("2_MH_07", 5, 497, 73, 508, 7), // host
6063 // voice on/off button
6064 ui_button_info("2_MH_09", 867, 253, 872, 296, 9),
6065 ui_button_info("2_MH_10", 957, 253, 966, 296, 10),
6067 // host modifies ships
6068 ui_button_info("2_MH_13", 867, 603, 784, 581, 13),
6071 ui_button_info("2_MH_14", 916, 685, 925, 665, 14),
6074 UI_XSTR Multi_ho_text[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_BUTTONS] = {
6076 // not needed for FS1
6078 {"Highest rank", 1280, 46, 166, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_RANK].button},
6079 {"Team / wing-leader", 1281, 46, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_LEADER].button},
6080 {"Any", 1282, 46, 203, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_ANY].button},
6081 {"Host", 1283, 46, 220, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_HOST].button},
6082 {"Highest rank", 1280, 46, 265, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_RANK].button},
6083 {"Team / wing-leader", 1281, 46, 283, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_LEADER].button},
6084 {"Any", 1282, 46, 300, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_ANY].button},
6085 {"Host", 1283, 46, 317, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_HOST].button},
6086 {"On", 1285, 545, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_VOICE_ON].button},
6087 {"Off", 1286, 604, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_VOICE_OFF].button},
6088 {"Host modifies ships", 1287, 437, 363, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_HOST_MODIFIES].button},
6089 {"Exit", 1417, 572, 418, UI_XSTR_COLOR_PINK, -1, &Multi_ho_buttons[0][MULTI_HO_ACCEPT].button},
6093 // not needed for FS1
6095 {"Highest rank", 1280, 62, 269, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_RANK].button},
6096 {"Team / wing-leader", 1281, 62, 297, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_LEADER].button},
6097 {"Any", 1282, 62, 325, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_ANY].button},
6098 {"Host", 1283, 62, 352, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_HOST].button},
6099 {"Highest rank", 1280, 62, 425, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_RANK].button},
6100 {"Team / wing-leader", 1281, 62, 452, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_LEADER].button},
6101 {"Any", 1282, 62, 480, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_ANY].button},
6102 {"Host", 1283, 62, 508, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_HOST].button},
6103 {"On", 1285, 877, 294, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_VOICE_ON].button},
6104 {"Off", 1286, 967, 293, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_VOICE_OFF].button},
6105 {"Host modifies ships", 1287, 869, 589, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_HOST_MODIFIES].button},
6106 {"Exit", 1417, 953, 672, UI_XSTR_COLOR_PINK, -1, &Multi_ho_buttons[1][MULTI_HO_ACCEPT].button},
6111 // radio button controls
6112 #define MULTI_HO_NUM_RADIO_GROUPS 3
6113 #define MULTI_HO_MSG_GROUP 0 // group dealing with squadmate messaging
6114 #define MULTI_HO_END_GROUP 1 // group dealing with ending the mission
6115 #define MULTI_HO_VOICE_GROUP 2 // group dealing with voice stuff
6116 int Multi_ho_radio_groups[MULTI_HO_NUM_RADIO_GROUPS] = { // currently selected button in the radio button group
6119 int Multi_ho_radio_info[MULTI_HO_NUM_RADIO_BUTTONS][3] = { // info related to each of the radio buttons themselves
6120 // { group #, value, button id# }
6121 {0, 0, 0}, // highest ranking players can do messaging
6122 {0, 1, 1}, // wing/team leaders can do messaging
6123 {0, 2, 2}, // any player can do messaging
6124 {0, 3, 3}, // only host can do messaging
6125 {1, 0, 4}, // highest rank and host can end the mission
6126 {1, 1, 5}, // team/wing leader can end the mission
6127 {1, 2, 6}, // any player can end the mission
6128 {1, 3, 7}, // only the host can end the mission
6129 {2, 0, 8}, // voice toggled on
6130 {2, 1, 9} // voice toggled off
6134 #define MULTI_HO_NUM_SLIDERS 3
6135 #define MULTI_HO_SLIDER_VOICE_QOS 0 // voice quality of sound
6136 #define MULTI_HO_SLIDER_VOICE_DUR 1 // max duration of voice recording
6137 #define MULTI_HO_SLIDER_SKILL 2 // skill level
6144 UI_DOT_SLIDER_NEW slider; // because we have a class inside this struct, we need the constructor below..
6146 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){}
6148 ho_sliders Multi_ho_sliders[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_SLIDERS] = {
6151 ho_sliders("MH_16", 396, 210, -1, -1, 16, 19, 10), // voice qos
6152 ho_sliders("MH_17", 396, 263, -1, -1, 17, 19, 10), // voice duration
6153 ho_sliders("MH_13", 10, 387, -1, -1, 13, 36, 5), // skill level
6155 ho_sliders("MH_11", 428, 214, 437, 199, 11, 19, 10), // voice qos
6156 ho_sliders("MH_12", 428, 261, 437, 246, 12, 19, 10), // voice duration
6157 ho_sliders("MH_08", 237, 454, 230, 411, 8, 36, 5), // skill level
6161 ho_sliders("2_MH_11", 684, 343, 690, 323, 11, 32, 10), // voice qos
6162 ho_sliders("2_MH_12", 685, 418, 837, 468, 12, 32, 10), // voice duration
6163 ho_sliders("2_MH_08", 379, 727, 369, 663, 8, 60, 5), // skill level
6167 int Multi_ho_mission_respawn;
6169 int Multi_ho_host_modifies;
6171 // whether or not any of the inputboxes on this screen had focus last frame
6172 int Multi_ho_lastframe_input = 0;
6174 // game information text areas
6178 #define MULTI_HO_NUM_TITLES 0
6180 #define MULTI_HO_NUM_TITLES 14
6182 UI_XSTR Multi_ho_titles[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_TITLES] = {
6184 // not needed for FS1
6186 { "AI Orders", 1289, 32, 144, UI_XSTR_COLOR_GREEN, -1, NULL },
6187 { "End Mission", 1290, 32, 242, UI_XSTR_COLOR_GREEN, -1, NULL },
6188 { "Time Limit", 1291, 32, 347, UI_XSTR_COLOR_GREEN, -1, NULL },
6189 { "Min", 1292, 74, 362, UI_XSTR_COLOR_GREEN, -1, NULL },
6190 { "Respawn Limit", 1288, 32, 378, UI_XSTR_COLOR_GREEN, -1, NULL },
6191 { "Kill Limit", 1293, 32, 409, UI_XSTR_COLOR_GREEN, -1, NULL },
6192 { "Observers", 1294, 32, 441, UI_XSTR_COLOR_GREEN, -1, NULL },
6193 { "Skill Level", 1284, 230, 411, UI_XSTR_COLOR_GREEN, -1, NULL },
6194 { "Voice Transmission", 1295, 437, 144, UI_XSTR_COLOR_GREEN, -1, NULL },
6195 { "Voice Quality", 1296, 437, 199, UI_XSTR_COLOR_GREEN, -1, NULL },
6196 { "Message Duration", 1297, 437, 246, UI_XSTR_COLOR_GREEN, -1, NULL },
6197 { "sec", 1522, 523, 292, UI_XSTR_COLOR_GREEN, -1, NULL },
6198 { "sec", 1523, 523, 332, UI_XSTR_COLOR_GREEN, -1, NULL },
6199 { "Voice Wait", 1298, 437, 313, UI_XSTR_COLOR_GREEN, -1, NULL },
6203 // not needed for FS1
6205 { "AI Orders", 1289, 48, 238, UI_XSTR_COLOR_GREEN, -1, NULL },
6206 { "End Mission", 1290, 48, 394, UI_XSTR_COLOR_GREEN, -1, NULL },
6207 { "Time Limit", 1291, 50, 568, UI_XSTR_COLOR_GREEN, -1, NULL },
6208 { "Min", 1292, 119, 581, UI_XSTR_COLOR_GREEN, -1, NULL },
6209 { "Respawn Limit", 1288, 50, 618, UI_XSTR_COLOR_GREEN, -1, NULL },
6210 { "Kill Limit", 1293, 50, 668, UI_XSTR_COLOR_GREEN, -1, NULL },
6211 { "Observers", 1294, 50, 718, UI_XSTR_COLOR_GREEN, -1, NULL },
6212 { "Skill Level", 1284, 398, 670, UI_XSTR_COLOR_GREEN, -1, NULL },
6213 { "Voice Transmission", 1295, 869, 239, UI_XSTR_COLOR_GREEN, -1, NULL },
6214 { "Voice Quality", 1296, 690, 331, UI_XSTR_COLOR_GREEN, -1, NULL },
6215 { "Message Duration", 1297, 690, 405, UI_XSTR_COLOR_GREEN, -1, NULL },
6216 { "sec", 1522, 837, 467, UI_XSTR_COLOR_GREEN, -1, NULL },
6217 { "sec", 1523, 837, 534, UI_XSTR_COLOR_GREEN, -1, NULL },
6218 { "Voice Wait", 1298, 742, 510, UI_XSTR_COLOR_GREEN, -1, NULL },
6223 // mission time limit input box
6224 int Ho_time_coords[GR_NUM_RESOLUTIONS][4] = {
6237 // furball kill limit input box
6238 int Ho_kill_coords[GR_NUM_RESOLUTIONS][4] = {
6251 // voice recording duration text display area
6252 int Ho_vd_coords[GR_NUM_RESOLUTIONS][4] = {
6265 // voice token wait input box
6266 int Ho_vw_coords[GR_NUM_RESOLUTIONS][6] = {
6279 // observer count input box
6280 int Ho_obs_coords[GR_NUM_RESOLUTIONS][4] = {
6293 // skill text description area
6294 int Ho_st_coords[GR_NUM_RESOLUTIONS][4] = {
6307 // respawn input box
6308 int Ho_rsp_coords[GR_NUM_RESOLUTIONS][6] = {
6321 // respawn max text area
6322 int Ho_max_rsp_coords[GR_NUM_RESOLUTIONS][2] = {
6335 // maximum values for various input boxes (to notify user of overruns)
6336 #define MULTI_HO_MAX_TIME_LIMIT 500
6337 #define MULTI_HO_MAX_TOKEN_WAIT 5
6338 #define MULTI_HO_MAX_KILL_LIMIT 9999
6339 #define MULTI_HO_MAX_OBS 4
6341 // LOCAL function definitions
6342 void multi_ho_check_buttons();
6343 void multi_ho_button_pressed(int n);
6344 void multi_ho_draw_radio_groups();
6345 void multi_ho_accept_hit();
6346 void multi_ho_get_options();
6347 void multi_ho_apply_options();
6348 void multi_ho_display_record_time();
6349 int multi_ho_check_values();
6350 void multi_ho_check_focus();
6351 void multi_ho_blit_max_respawns();
6352 void multi_ho_display_skill_level();
6354 void multi_host_options_init()
6358 // create the interface window
6359 Multi_ho_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
6360 Multi_ho_window.set_mask_bmap(Multi_ho_bitmap_mask_fname[gr_screen.res]);
6362 // load the background bitmap
6363 Multi_ho_bitmap = bm_load(Multi_ho_bitmap_fname[gr_screen.res]);
6364 if(Multi_ho_bitmap < 0){
6365 // we failed to load the bitmap - this is very bad
6369 // initialize the common notification messaging
6370 multi_common_notify_init();
6372 // use the common interface palette
6373 multi_common_set_palette();
6375 // create the interface buttons
6376 for(idx=0;idx<MULTI_HO_NUM_BUTTONS;idx++){
6377 // create the object
6378 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);
6380 // set the sound to play when highlighted
6381 Multi_ho_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
6383 // set the ani for the button
6384 Multi_ho_buttons[gr_screen.res][idx].button.set_bmaps(Multi_ho_buttons[gr_screen.res][idx].filename);
6386 // set the hotspot, ignoring the skill level button
6387 Multi_ho_buttons[gr_screen.res][idx].button.link_hotspot(Multi_ho_buttons[gr_screen.res][idx].hotspot);
6391 Multi_ho_window.add_XSTR(&Multi_ho_text[gr_screen.res][idx]);
6396 for(idx=0; idx<MULTI_HO_NUM_TITLES; idx++){
6397 Multi_ho_window.add_XSTR(&Multi_ho_titles[gr_screen.res][idx]);
6400 // create the interface sliders
6401 for(idx=0; idx<MULTI_HO_NUM_SLIDERS; idx++){
6402 // create the object
6403 Multi_ho_sliders[gr_screen.res][idx].slider.create(&Multi_ho_window, Multi_ho_sliders[gr_screen.res][idx].x, Multi_ho_sliders[gr_screen.res][idx].y, Multi_ho_sliders[gr_screen.res][idx].dots, Multi_ho_sliders[gr_screen.res][idx].filename, Multi_ho_sliders[gr_screen.res][idx].hotspot, NULL, -1, -1, -1, NULL, -1, -1, -1, Multi_ho_sliders[gr_screen.res][idx].dot_w);
6406 // create the respawn count input box
6407 Multi_ho_respawns.create(&Multi_ho_window,Ho_rsp_coords[gr_screen.res][MULTI_HO_X_COORD],Ho_rsp_coords[gr_screen.res][MULTI_HO_Y_COORD],Ho_rsp_coords[gr_screen.res][MULTI_HO_W_COORD],6,"",UI_INPUTBOX_FLAG_ESC_FOC | UI_INPUTBOX_FLAG_INVIS | UI_INPUTBOX_FLAG_NO_LETTERS,-1,&Color_bright);
6408 // if we're in campaign mode, disable it
6409 if(Netgame.campaign_mode == MP_CAMPAIGN){
6410 Multi_ho_respawns.set_text(XSTR("NA",795)); // [[ Not applicable ]]
6411 Multi_ho_respawns.disable();
6413 Multi_ho_respawns.set_valid_chars("0123456789");
6416 // create the time limit input box
6417 Multi_ho_time_limit.create(&Multi_ho_window, Ho_time_coords[gr_screen.res][MULTI_HO_X_COORD], Ho_time_coords[gr_screen.res][MULTI_HO_Y_COORD], Ho_time_coords[gr_screen.res][MULTI_HO_W_COORD], 3, "", UI_INPUTBOX_FLAG_ESC_FOC | UI_INPUTBOX_FLAG_INVIS | UI_INPUTBOX_FLAG_NO_LETTERS, -1, &Color_bright);
6418 Multi_ho_time_limit.set_valid_chars("-0123456789");
6420 // create the voice token wait input box
6421 Multi_ho_voice_wait.create(&Multi_ho_window, Ho_vw_coords[gr_screen.res][MULTI_HO_X_COORD], Ho_vw_coords[gr_screen.res][MULTI_HO_Y_COORD], Ho_vw_coords[gr_screen.res][MULTI_HO_W_COORD], 1, "", UI_INPUTBOX_FLAG_ESC_FOC | UI_INPUTBOX_FLAG_INVIS | UI_INPUTBOX_FLAG_NO_LETTERS, -1, &Color_bright);
6422 Multi_ho_voice_wait.set_valid_chars("01243456789");
6424 // create the furball kill limit input box
6425 Multi_ho_kill_limit.create(&Multi_ho_window, Ho_kill_coords[gr_screen.res][MULTI_HO_X_COORD], Ho_kill_coords[gr_screen.res][MULTI_HO_Y_COORD], Ho_kill_coords[gr_screen.res][MULTI_HO_W_COORD], 4, "", UI_INPUTBOX_FLAG_ESC_FOC | UI_INPUTBOX_FLAG_INVIS | UI_INPUTBOX_FLAG_NO_LETTERS, -1, &Color_bright);
6426 Multi_ho_kill_limit.set_valid_chars("0123456789");
6428 // create the observer limit input box
6429 Multi_ho_obs.create(&Multi_ho_window, Ho_obs_coords[gr_screen.res][MULTI_HO_X_COORD], Ho_obs_coords[gr_screen.res][MULTI_HO_Y_COORD], Ho_obs_coords[gr_screen.res][MULTI_HO_W_COORD], 1, "", UI_INPUTBOX_FLAG_ESC_FOC | UI_INPUTBOX_FLAG_INVIS | UI_INPUTBOX_FLAG_NO_LETTERS, -1, &Color_bright);
6430 Multi_ho_obs.set_valid_chars("01234");
6432 // load in the current netgame defaults
6433 multi_ho_get_options();
6435 // whether or not any of the inputboxes on this screen had focus last frame
6436 Multi_ho_lastframe_input = 0;
6438 // get the # of respawns for the currently selected mission (if any)
6439 if(Multi_create_list_select != -1){
6440 int abs_index = multi_create_select_to_index(Multi_create_list_select);
6442 // if he has a valid mission selected
6444 Multi_ho_mission_respawn = (int)Multi_create_file_list[abs_index].respawn;
6446 Multi_ho_mission_respawn = -1;
6449 Multi_ho_mission_respawn = -1;
6453 void multi_ho_update_sliders()
6455 // game skill slider
6456 if (Game_skill_level != Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos) {
6457 if ( !(Netgame.type_flags & NG_TYPE_TEAM) ){
6458 Game_skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6459 gamesnd_play_iface(SND_USER_SELECT);
6461 Game_skill_level = NUM_SKILL_LEVELS / 2;
6465 // get the voice qos options
6466 if (Netgame.options.voice_qos != (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1)) {
6467 Netgame.options.voice_qos = (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1);
6468 gamesnd_play_iface(SND_USER_SELECT);
6471 // get the voice duration options
6472 if (Netgame.options.voice_record_time != (int)(0.5f * (float)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 1000.0f)) {
6473 Netgame.options.voice_record_time = (int)(0.5f * (float)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 1000.0f);
6474 gamesnd_play_iface(SND_USER_SELECT);
6479 void multi_host_options_do()
6484 k = Multi_ho_window.process();
6487 // process any keypresses
6490 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
6493 case KEY_CTRLED + KEY_ENTER :
6494 gamesnd_play_iface(SND_COMMIT_PRESSED);
6495 multi_ho_accept_hit();
6499 // process any button clicks
6500 multi_ho_check_buttons();
6502 // update the sliders
6503 multi_ho_update_sliders();
6505 // make sure that the chatbox inputbox and any inputbox on this screen are mutually exclusive in terms of focus
6506 multi_ho_check_focus();
6508 // draw the background, etc
6510 GR_MAYBE_CLEAR_RES(Multi_ho_bitmap);
6511 if(Multi_ho_bitmap != -1){
6512 gr_set_bitmap(Multi_ho_bitmap);
6515 Multi_ho_window.draw();
6517 // draw all the radio buttons properly
6518 multi_ho_draw_radio_groups();
6520 // display any pending notification messages
6521 multi_common_notify_do();
6523 // display the voice record time settings
6524 multi_ho_display_record_time();
6526 // maybe display the max # of respawns next to the respawn input box
6527 multi_ho_blit_max_respawns();
6529 // blit the proper skill level
6530 multi_ho_display_skill_level();
6532 // blit the "host modifies button"
6533 if(Multi_ho_host_modifies){
6534 Multi_ho_buttons[gr_screen.res][MULTI_HO_HOST_MODIFIES].button.draw_forced(2);
6537 // process and show the chatbox thingie
6541 Multi_ho_window.draw_tooltip();
6543 // display the voice status indicator
6544 multi_common_voice_display_status();
6550 void multi_host_options_close()
6552 // unload any bitmaps
6553 if(!bm_unload(Multi_ho_bitmap)){
6554 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_ho_bitmap_fname[gr_screen.res]));
6557 // destroy the UI_WINDOW
6558 Multi_ho_window.destroy();
6561 void multi_ho_check_buttons()
6564 for(idx=0;idx<MULTI_HO_NUM_BUTTONS;idx++){
6565 // we only really need to check for one button pressed at a time, so we can break after
6567 if(Multi_ho_buttons[gr_screen.res][idx].button.pressed()){
6568 multi_ho_button_pressed(idx);
6574 void multi_ho_button_pressed(int n)
6576 int radio_index,idx;
6577 int x_pixel,y_pixel;
6579 // get the pixel position of the click
6580 Multi_ho_buttons[gr_screen.res][n].button.get_mouse_pos(&x_pixel,&y_pixel);
6583 // clicked on the accept button
6584 case MULTI_HO_ACCEPT:
6585 gamesnd_play_iface(SND_COMMIT_PRESSED);
6586 multi_ho_accept_hit();
6589 // clicked on the host/captains only modify button
6590 case MULTI_HO_HOST_MODIFIES:
6591 // toggle it on or off
6592 Multi_ho_host_modifies = !Multi_ho_host_modifies;
6593 gamesnd_play_iface(SND_USER_SELECT);
6597 // look through the radio buttons and see which one this corresponds to
6599 for(idx=0;idx<MULTI_HO_NUM_RADIO_BUTTONS;idx++){
6600 if(Multi_ho_radio_info[idx][2] == n){
6605 Assert(radio_index != -1);
6607 // check to see if a radio button was pressed
6608 if(radio_index < MULTI_HO_NUM_RADIO_BUTTONS){
6609 // see if this value is already picked for this radio group
6610 if(Multi_ho_radio_groups[Multi_ho_radio_info[radio_index][0]] != Multi_ho_radio_info[radio_index][1]){
6611 gamesnd_play_iface(SND_USER_SELECT);
6612 Multi_ho_radio_groups[Multi_ho_radio_info[radio_index][0]] = Multi_ho_radio_info[radio_index][1];
6614 gamesnd_play_iface(SND_GENERAL_FAIL);
6619 void multi_ho_draw_radio_groups()
6623 // go through each item and draw it if it is the selected button in its respective group
6624 for(idx=0;idx<MULTI_HO_NUM_RADIO_BUTTONS;idx++){
6625 /// if this button is the currently selected one in its group
6626 if(Multi_ho_radio_info[idx][1] == Multi_ho_radio_groups[Multi_ho_radio_info[idx][0]]){
6627 Multi_ho_buttons[gr_screen.res][Multi_ho_radio_info[idx][2]].button.draw_forced(2);
6632 void multi_ho_accept_hit()
6636 // check the values in the input boxes
6637 if(!multi_ho_check_values()){
6641 // zero out the netgame flags
6644 // set default options
6645 Netgame.options.flags = (MSO_FLAG_INGAME_XFER | MSO_FLAG_ACCEPT_PIX);
6647 // set the squadmate messaging flags
6648 switch(Multi_ho_radio_groups[MULTI_HO_MSG_GROUP]){
6650 Netgame.options.squad_set = MSO_SQUAD_RANK;
6653 Netgame.options.squad_set = MSO_SQUAD_LEADER;
6656 Netgame.options.squad_set = MSO_SQUAD_ANY;
6659 Netgame.options.squad_set = MSO_SQUAD_HOST;
6665 // set the end mission flags
6666 switch(Multi_ho_radio_groups[MULTI_HO_END_GROUP]){
6668 Netgame.options.endgame_set = MSO_END_RANK;
6671 Netgame.options.endgame_set = MSO_END_LEADER;
6674 Netgame.options.endgame_set = MSO_END_ANY;
6677 Netgame.options.endgame_set = MSO_END_HOST;
6683 // set the voice toggle
6684 switch(Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP]){
6686 Netgame.options.flags &= ~(MSO_FLAG_NO_VOICE);
6689 Netgame.options.flags |= MSO_FLAG_NO_VOICE;
6695 // get the voice qos options
6696 Netgame.options.voice_qos = (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1);
6698 // get the voice duration options
6699 Netgame.options.voice_record_time = (int)(0.5f * (float)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 1000.0f);
6701 // set the skill level. If in team vs. team mode, preserve the old setting before saving
6702 // the pilot file. I'll bet that this doesn't work though because the pilot file gets
6703 // written in a bunch of locations....sigh.
6704 if ( !(Netgame.type_flags & NG_TYPE_TEAM) ){
6705 Game_skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6707 Game_skill_level = NUM_SKILL_LEVELS / 2;
6710 // set the netgame respawn count
6711 // maybe warn the user that respawns will not be used for a campaign mission
6712 if(Netgame.campaign_mode == MP_SINGLE){
6713 Multi_ho_respawns.get_text(resp_str);
6714 uint temp_respawn = (uint)atoi(resp_str);
6715 // if he currently has no mission selected, let the user set any # of respawns
6716 if((int)temp_respawn > Multi_ho_mission_respawn){
6717 if(Multi_ho_mission_respawn == -1){
6718 Netgame.respawn = temp_respawn;
6719 Netgame.options.respawn = temp_respawn;
6721 // this should have been taken care of by the interface code
6726 Netgame.options.respawn = temp_respawn;
6727 Netgame.respawn = temp_respawn;
6731 // get the mission time limit
6732 Multi_ho_time_limit.get_text(resp_str);
6733 int temp_time = atoi(resp_str);
6735 Netgame.options.mission_time_limit = fl2f(-1.0f);
6736 } else if(temp_time > MULTI_HO_MAX_TIME_LIMIT){
6739 Netgame.options.mission_time_limit = fl2f(60.0f * (float)temp_time);
6742 // get observer count options
6743 Multi_ho_obs.get_text(resp_str);
6744 int temp_obs = atoi(resp_str);
6745 if(temp_obs > MULTI_HO_MAX_OBS){
6748 Netgame.options.max_observers = (ubyte)temp_obs;
6750 // get the furball kill limit
6751 Multi_ho_kill_limit.get_text(resp_str);
6752 int temp_kills = atoi(resp_str);
6753 if(temp_kills > MULTI_HO_MAX_KILL_LIMIT){
6756 Netgame.options.kill_limit = temp_kills;
6758 // get the token wait limit
6759 Multi_ho_voice_wait.get_text(resp_str);
6760 int temp_wait = atoi(resp_str);
6761 if(temp_wait > MULTI_HO_MAX_TOKEN_WAIT){
6764 Netgame.options.voice_token_wait = (temp_wait * 1000);
6766 // set the netgame option
6767 Netgame.options.skill_level = (ubyte)Game_skill_level;
6769 // get whether we're in host/captains only modify mode
6770 Netgame.options.flags &= ~(MSO_FLAG_SS_LEADERS);
6771 if(Multi_ho_host_modifies){
6772 Netgame.options.flags |= MSO_FLAG_SS_LEADERS;
6775 // store these values locally
6776 memcpy(&Player->m_local_options,&Net_player->p_info.options,sizeof(multi_local_options));
6777 memcpy(&Player->m_server_options,&Netgame.options,sizeof(multi_server_options));
6778 write_pilot_file(Player);
6780 // apply any changes in settings (notify everyone of voice qos changes, etc)
6781 multi_ho_apply_options();
6783 // move back to the create game screen
6784 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
6787 void multi_ho_get_options()
6791 // set the squadmate messaging buttons
6792 switch(Netgame.options.squad_set){
6793 case MSO_SQUAD_RANK :
6794 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 0;
6796 case MSO_SQUAD_LEADER:
6797 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 1;
6800 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 2;
6802 case MSO_SQUAD_HOST:
6803 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 3;
6809 // set the mission end buttons
6810 switch(Netgame.options.endgame_set){
6812 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 0;
6814 case MSO_END_LEADER:
6815 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 1;
6818 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 2;
6821 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 3;
6827 // set the voice toggle buttons
6828 if(Netgame.options.flags & MSO_FLAG_NO_VOICE){
6829 Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP] = 1;
6831 Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP] = 0;
6834 // get the voice qos options
6835 Assert((Netgame.options.voice_qos >= 1) && (Netgame.options.voice_qos <= 10));
6836 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos = (Netgame.options.voice_qos - 1);
6838 // get the voice duration options
6839 Assert((Netgame.options.voice_record_time > 0) && (Netgame.options.voice_record_time <= MULTI_VOICE_MAX_TIME));
6840 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos = ((int)((float)Netgame.options.voice_record_time / 500.0f)) - 1;
6842 // get the current skill level
6843 Assert((Game_skill_level >= 0) && (Game_skill_level < NUM_SKILL_LEVELS));
6844 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos = Game_skill_level;
6846 // get the # of observers
6847 memset(resp_str,0,10);
6848 sprintf(resp_str,"%d",Netgame.options.max_observers);
6849 Multi_ho_obs.set_text(resp_str);
6851 // set the respawn count
6852 if(Netgame.campaign_mode == MP_SINGLE){
6853 memset(resp_str,0,10);
6854 sprintf(resp_str,"%d",Netgame.respawn);
6855 Multi_ho_respawns.set_text(resp_str);
6858 // set the mission time limit
6859 memset(resp_str,0,10);
6860 float tl = f2fl(Netgame.options.mission_time_limit);
6861 sprintf(resp_str,"%d",(int)(tl / 60.0f));
6862 Multi_ho_time_limit.set_text(resp_str);
6864 // set the furball kill limit
6865 memset(resp_str,0,10);
6866 sprintf(resp_str,"%d",Netgame.options.kill_limit);
6867 Multi_ho_kill_limit.set_text(resp_str);
6869 // set the token wait time
6870 memset(resp_str,0,10);
6871 sprintf(resp_str,"%d",Netgame.options.voice_token_wait / 1000);
6872 Multi_ho_voice_wait.set_text(resp_str);
6874 // get whether we're in host/captains only modify mode
6875 if(Netgame.options.flags & MSO_FLAG_SS_LEADERS){
6876 Multi_ho_host_modifies = 1;
6878 Multi_ho_host_modifies = 0;
6882 void multi_ho_apply_options()
6884 // if the voice qos or duration has changed, apply the change
6885 multi_voice_maybe_update_vars(Netgame.options.voice_qos,Netgame.options.voice_record_time);
6887 // send an options update
6888 multi_options_update_netgame();
6891 // display the voice record time settings
6892 void multi_ho_display_record_time()
6895 int full_seconds, half_seconds;
6898 memset(time_str,0,30);
6901 full_seconds = (((Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 500) / 1000);
6903 // get the half-seconds
6904 half_seconds = ((((Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 500) % 1000) / 500) * 5;
6906 // format the string
6907 sprintf(time_str,"%d.%d",full_seconds,half_seconds);
6908 gr_set_color_fast(&Color_bright);
6909 gr_string(Ho_vd_coords[gr_screen.res][MULTI_HO_X_COORD],Ho_vd_coords[gr_screen.res][MULTI_HO_Y_COORD],time_str);
6912 int multi_ho_check_values()
6916 memset(val_txt,0,255);
6918 // check against respawn settings
6919 if(Multi_ho_mission_respawn != -1){
6920 Multi_ho_respawns.get_text(val_txt);
6921 // if the value is invalid, let the user know
6922 if(atoi(val_txt) > Multi_ho_mission_respawn){
6923 memset(val_txt,0,255);
6924 sprintf(val_txt,XSTR("Warning\nRespawn count in greater than mission specified max (%d)",796),Multi_ho_mission_respawn);
6925 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6930 // check against mission time limit max
6931 Multi_ho_time_limit.get_text(val_txt);
6932 // if the value is invalid, force it to be valid
6933 if(atoi(val_txt) > MULTI_HO_MAX_TIME_LIMIT){
6934 memset(val_txt,0,255);
6935 sprintf(val_txt,XSTR("Warning\nMission time limit is greater than max allowed (%d)",797),MULTI_HO_MAX_TIME_LIMIT);
6936 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6940 // check against max observer limit
6941 Multi_ho_obs.get_text(val_txt);
6942 // if the value is invalid, force it to be valid
6943 if(atoi(val_txt) > MULTI_HO_MAX_OBS){
6944 memset(val_txt,0,255);
6945 sprintf(val_txt,XSTR("Warning\nObserver count is greater than max allowed (%d)",798),MULTI_HO_MAX_OBS);
6946 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6950 // check against furball kill limit
6951 Multi_ho_kill_limit.get_text(val_txt);
6952 // if the value is invalid, force it to be valid
6953 if(atoi(val_txt) > MULTI_HO_MAX_KILL_LIMIT){
6954 memset(val_txt,0,255);
6955 sprintf(val_txt,XSTR("Warning\nMission kill limit is greater than max allowed (%d)",799),MULTI_HO_MAX_KILL_LIMIT);
6956 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6960 // check against the token wait limit
6961 Multi_ho_voice_wait.get_text(val_txt);
6962 if(atoi(val_txt) > MULTI_HO_MAX_TOKEN_WAIT){
6963 memset(val_txt,0,255);
6964 sprintf(val_txt,XSTR("Warning\nvoice wait time is greater than max allowed (%d)",800),MULTI_HO_MAX_TOKEN_WAIT);
6965 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6969 // all values are valid
6973 void multi_ho_check_focus()
6975 // if an inputbox has been pressed (hit enter), lose its focus
6976 if (Multi_ho_respawns.pressed() || Multi_ho_time_limit.pressed() || Multi_ho_voice_wait.pressed() || Multi_ho_kill_limit.pressed() || Multi_ho_obs.pressed()) {
6977 Multi_ho_respawns.clear_focus();
6978 Multi_ho_time_limit.clear_focus();
6979 Multi_ho_voice_wait.clear_focus();
6980 Multi_ho_kill_limit.clear_focus();
6981 Multi_ho_obs.clear_focus();
6982 gamesnd_play_iface(SND_COMMIT_PRESSED);
6983 chatbox_set_focus();
6984 Multi_ho_lastframe_input = 0;
6986 } else if(!Multi_ho_lastframe_input) {
6987 // if we didn't have focus last frame
6988 if(Multi_ho_respawns.has_focus() || Multi_ho_time_limit.has_focus() || Multi_ho_kill_limit.has_focus() || Multi_ho_voice_wait.has_focus() ){
6989 chatbox_lose_focus();
6991 Multi_ho_lastframe_input = 1;
6994 // if we _did_ have focus last frame
6996 // if we no longer have focus on any of the input boxes, set the focus on the chatbox
6997 if(!Multi_ho_respawns.has_focus() && !Multi_ho_time_limit.has_focus() && !Multi_ho_kill_limit.has_focus() && !Multi_ho_voice_wait.has_focus() && !chatbox_has_focus()){
6998 chatbox_set_focus();
7000 // if the chatbox now has focus, clear all focus from our inputboxes
7001 else if (chatbox_has_focus()) {
7002 Multi_ho_respawns.clear_focus();
7003 Multi_ho_time_limit.clear_focus();
7004 Multi_ho_kill_limit.clear_focus();
7005 Multi_ho_voice_wait.clear_focus();
7007 Multi_ho_lastframe_input = 0;
7012 void multi_ho_blit_max_respawns()
7016 // if we're in campaign mode, do nothing
7017 if(Netgame.campaign_mode == MP_CAMPAIGN){
7021 // otherwise blit the max as specified by the current mission file
7022 sprintf(string,"(%d)",Multi_ho_mission_respawn);
7023 gr_set_color_fast(&Color_normal);
7024 gr_string(Ho_max_rsp_coords[gr_screen.res][MULTI_HO_X_COORD], Ho_max_rsp_coords[gr_screen.res][MULTI_HO_Y_COORD], string);
7027 void multi_ho_display_skill_level()
7029 int skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
7032 Assert((skill_level >= 0) && (skill_level < NUM_SKILL_LEVELS));
7033 if((skill_level < 0) || (skill_level >= NUM_SKILL_LEVELS)){
7037 gr_set_color_fast(&Color_bright);
7038 gr_string(Ho_st_coords[gr_screen.res][0], Ho_st_coords[gr_screen.res][1], Skill_level_names(skill_level, 1));
7041 // -------------------------------------------------------------------------------------------------------------
7043 // MULTIPLAYER JOIN SCREEN
7046 #define MULTI_JW_NUM_BUTTONS 8
7050 #define MULTI_JW_PALETTE "InterfacePalette"
7052 static char *Multi_jw_bitmap_fname[GR_NUM_RESOLUTIONS] = {
7053 "MultiJoinWait", // GR_640
7054 "2_MultiJoinWait" // GR_1024
7057 static char *Multi_jw_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
7058 "MultiJoinWait-M", // GR_640
7059 "2_MultiJoinWait-M" // GR_1024
7065 #define MJW_SCROLL_PLAYERS_UP 0
7066 #define MJW_SCROLL_PLAYERS_DOWN 1
7069 #define MJW_PILOT_INFO 4
7070 #define MJW_SCROLL_INFO_UP 5
7071 #define MJW_SCROLL_INFO_DOWN 6
7072 #define MJW_CANCEL 7
7074 UI_WINDOW Multi_jw_window; // the window object for the join screen
7075 int Multi_jw_bitmap; // the background bitmap
7077 // constants for coordinate lookup
7078 #define MJW_X_COORD 0
7079 #define MJW_Y_COORD 1
7080 #define MJW_W_COORD 2
7081 #define MJW_H_COORD 3
7083 ui_button_info Multi_jw_buttons[GR_NUM_RESOLUTIONS][MULTI_JW_NUM_BUTTONS] = {
7085 ui_button_info("MJW_00", 1, 24, -1, -1, 0),
7086 ui_button_info("MJW_01", 1, 66, -1, -1, 1),
7087 ui_button_info("MJW_02", 30, 244, 20, 272, 2),
7088 ui_button_info("MJW_03", 84, 244, 73, 272, 3),
7089 ui_button_info("MJW_04", 139, 242, 134, 272, 4),
7090 ui_button_info("MJW_05", 1, 406, -1, -1, 5),
7091 ui_button_info("MJW_06", 1, 447, -1, -1, 6),
7092 ui_button_info("MJW_07", 577, 428, 570, 414, 7),
7095 ui_button_info("2_MJW_00", 2, 38, -1, -1, 0),
7096 ui_button_info("2_MJW_01", 2, 106, -1, -1, 1),
7097 ui_button_info("2_MJW_02", 48, 390, 47, 435, 2),
7098 ui_button_info("2_MJW_03", 134, 390, 133, 435, 3),
7099 ui_button_info("2_MJW_04", 223, 388, 225, 435, 4),
7100 ui_button_info("2_MJW_05", 2, 649, -1, -1, 5),
7101 ui_button_info("2_MJW_06", 2, 715, -1, -1, 6),
7102 ui_button_info("2_MJW_07", 923, 685, 931, 667, 7),
7107 #define MULTI_JW_NUM_TEXT 0
7109 #define MULTI_JW_NUM_TEXT 7
7112 UI_XSTR Multi_jw_text[GR_NUM_RESOLUTIONS][MULTI_JW_NUM_TEXT] = {
7114 // not needed for FS1
7116 { "Team 1", 1308, 20, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_TEAM0].button },
7117 { "Team 2", 1309, 73, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_TEAM1].button },
7118 { "Pilot", 1310, 134, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_PILOT_INFO].button },
7119 { "Info", 1311, 134, 283, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_PILOT_INFO].button },
7120 { "Cancel", 387, 570, 414, UI_XSTR_COLOR_PINK, -1, &Multi_jw_buttons[0][MJW_CANCEL].button },
7121 { "Players", 1269, 38, 8, UI_XSTR_COLOR_GREEN, -1, NULL },
7122 { "Choose Team", 1312, 27, 231, UI_XSTR_COLOR_GREEN, -1, NULL },
7126 // not needed for FS1
7128 { "Team 1", 1308, 47, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_TEAM0].button },
7129 { "Team 2", 1309, 133, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_TEAM1].button },
7130 { "Pilot", 1310, 225, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_PILOT_INFO].button },
7131 { "Info", 1311, 225, 446, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_PILOT_INFO].button },
7132 { "Cancel", 387, 931, 667, UI_XSTR_COLOR_PINK, -1, &Multi_jw_buttons[1][MJW_CANCEL].button },
7133 { "Players", 1269, 165, 12, UI_XSTR_COLOR_GREEN, -1, NULL },
7134 { "Choose Team", 1312, 45, 373, UI_XSTR_COLOR_GREEN, -1, NULL },
7139 int Mjw_players_coords[GR_NUM_RESOLUTIONS][4] = {
7148 int Mjw_mission_name_coords[GR_NUM_RESOLUTIONS][2] = {
7157 // squad war checkbox
7158 UI_CHECKBOX Multi_jw_sw_checkbox;
7159 char *Multi_jw_sw_checkbox_fname[GR_NUM_RESOLUTIONS] = {
7163 int Multi_jw_sw_checkbox_coords[GR_NUM_RESOLUTIONS][2] = {
7171 int Multi_jw_sw_checkbox_text[GR_NUM_RESOLUTIONS][2] = {
7181 // player list control thingie defs
7182 #define MULTI_JW_PLIST_MAX_DISPLAY 19
7183 int Multi_jw_plist_select_flag; // indicates whether we currently have a selected player
7184 short Multi_jw_plist_select_id; // id of the current selected player
7185 UI_BUTTON Multi_jw_plist_select_button; // for selecting a player
7187 int Multi_jw_should_show_popup = 0;
7189 // LOCAL function definitions
7190 void multi_jw_check_buttons();
7191 void multi_jw_button_pressed(int n);
7192 void multi_jw_do_netstuff();
7193 void multi_jw_scroll_players_up();
7194 void multi_jw_scroll_players_down();
7195 void multi_jw_plist_process();
7196 void multi_jw_plist_blit_normal();
7197 void multi_jw_plist_blit_team();
7198 short multi_jw_get_mouse_id();
7200 void multi_game_client_setup_init()
7204 // create the interface window
7205 Multi_jw_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
7206 Multi_jw_window.set_mask_bmap(Multi_jw_bitmap_mask_fname[gr_screen.res]);
7208 // load the background bitmap
7209 Multi_jw_bitmap = bm_load(Multi_jw_bitmap_fname[gr_screen.res]);
7210 if(Multi_jw_bitmap < 0){
7211 // we failed to load the bitmap - this is very bad
7215 // initialize the player list data
7216 Multi_jw_plist_select_flag = 0;
7217 Multi_jw_plist_select_id = -1;
7219 // kill any old instances of the chatbox and create a new one
7221 chatbox_create(CHATBOX_FLAG_BIG | CHATBOX_FLAG_DRAW_BOX | CHATBOX_FLAG_BUTTONS);
7223 // initialize the common notification messaging
7224 multi_common_notify_init();
7226 // initialize the common mission info display area.
7227 multi_common_set_text("");
7229 // use the common interface palette
7230 multi_common_set_palette();
7232 // create the interface buttons
7233 for(idx=0; idx<MULTI_JW_NUM_BUTTONS; idx++){
7234 // create the object
7235 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);
7237 // set the sound to play when highlighted
7238 Multi_jw_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
7240 // set the ani for the button
7241 Multi_jw_buttons[gr_screen.res][idx].button.set_bmaps(Multi_jw_buttons[gr_screen.res][idx].filename);
7244 Multi_jw_buttons[gr_screen.res][idx].button.link_hotspot(Multi_jw_buttons[gr_screen.res][idx].hotspot);
7247 // if this is a PXO game, enable the squadwar checkbox
7248 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);
7249 Multi_jw_sw_checkbox.set_bmaps(Multi_jw_sw_checkbox_fname[gr_screen.res], 6, 0);
7250 Multi_jw_sw_checkbox.disable();
7251 if(!MULTI_IS_TRACKER_GAME){
7252 Multi_jw_sw_checkbox.hide();
7256 for(idx=0; idx<MULTI_JW_NUM_TEXT; idx++){
7257 Multi_jw_window.add_XSTR(&Multi_jw_text[gr_screen.res][idx]);
7260 // create the player select list button and hide it
7261 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);
7262 Multi_jw_plist_select_button.hide();
7265 Multi_jw_buttons[gr_screen.res][MJW_CANCEL].button.set_hotkey(KEY_ESC);
7267 // remove campaign flags
7268 Game_mode &= ~(GM_CAMPAIGN_MODE);
7270 // tell the server we have finished joining
7271 Net_player->state = NETPLAYER_STATE_JOINED;
7272 send_netplayer_update_packet();
7275 ml_printf(NOX("Joined netgame %s"), Netgame.name);
7277 // send any appropriate files
7278 multi_data_send_my_junk();
7281 void multi_game_client_setup_do_frame()
7284 int k = chatbox_process();
7285 char mission_text[255];
7286 k = Multi_jw_window.process(k,0);
7288 Multi_jw_should_show_popup = 0;
7290 // process any button clicks
7291 multi_jw_check_buttons();
7293 // do any network related stuff
7294 multi_jw_do_netstuff();
7296 // draw the background, etc
7298 GR_MAYBE_CLEAR_RES(Multi_jw_bitmap);
7299 if(Multi_jw_bitmap != -1){
7300 gr_set_bitmap(Multi_jw_bitmap);
7304 // if we're not in team vs. team mode, don't draw the team buttons
7305 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
7306 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.hide();
7307 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.hide();
7308 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.disable();
7309 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.disable();
7311 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.enable();
7312 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.enable();
7313 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.unhide();
7314 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.unhide();
7317 if(MULTI_IS_TRACKER_GAME){
7318 // maybe check the squadwar button
7319 if(Netgame.type_flags & NG_TYPE_SW){
7320 Multi_jw_sw_checkbox.set_state(1);
7321 gr_set_color_fast(&Color_bright);
7323 Multi_jw_sw_checkbox.set_state(0);
7324 gr_set_color_fast(&Color_normal);
7327 gr_string(Multi_jw_sw_checkbox_text[gr_screen.res][0], Multi_jw_sw_checkbox_text[gr_screen.res][1], "SquadWar");
7330 // draw the UI window
7331 Multi_jw_window.draw();
7333 // process and display the player list
7334 // NOTE : this must be done before the buttons are checked to insure that a player hasn't dropped
7335 multi_jw_plist_process();
7336 if(Netgame.type_flags & NG_TYPE_TEAM){
7337 multi_jw_plist_blit_team();
7339 multi_jw_plist_blit_normal();
7342 // display any text in the info area
7343 multi_common_render_text();
7345 // display any pending notification messages
7346 multi_common_notify_do();
7348 // blit the mission filename if possible
7349 if(Netgame.campaign_mode){
7350 if(strlen(Netgame.campaign_name) > 0){
7351 strcpy(mission_text,Netgame.campaign_name);
7353 if(strlen(Netgame.title) > 0){
7354 strcat(mission_text,", ");
7355 strcat(mission_text,Netgame.title);
7358 gr_set_color_fast(&Color_bright_white);
7359 gr_string(Mjw_mission_name_coords[gr_screen.res][MJW_X_COORD],Mjw_mission_name_coords[gr_screen.res][MJW_Y_COORD],mission_text);
7362 if(strlen(Netgame.mission_name) > 0){
7363 strcpy(mission_text,Netgame.mission_name);
7365 if(strlen(Netgame.title) > 0){
7366 strcat(mission_text,", ");
7367 strcat(mission_text,Netgame.title);
7370 gr_set_color_fast(&Color_bright_white);
7371 gr_string(Mjw_mission_name_coords[gr_screen.res][MJW_X_COORD],Mjw_mission_name_coords[gr_screen.res][MJW_Y_COORD],mission_text);
7375 // process and show the chatbox thingie
7379 Multi_jw_window.draw_tooltip();
7381 // display the voice status indicator
7382 multi_common_voice_display_status();
7387 // if we're supposed to be displaying a pilot info popup
7388 if(Multi_jw_should_show_popup){
7389 player_index = find_player_id(Multi_jw_plist_select_id);
7390 if(player_index != -1){
7391 multi_pinfo_popup(&Net_players[player_index]);
7396 void multi_game_client_setup_close()
7398 // unload any bitmaps
7399 if(!bm_unload(Multi_jw_bitmap)){
7400 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_jw_bitmap_fname[gr_screen.res]));
7403 // destroy the chatbox
7406 // destroy the UI_WINDOW
7407 Multi_jw_window.destroy();
7410 if(Netgame.game_state == NETGAME_STATE_MISSION_SYNC){
7411 gamesnd_play_iface(SND_COMMIT_PRESSED);
7416 void multi_jw_check_buttons()
7419 for(idx=0;idx<MULTI_JW_NUM_BUTTONS;idx++){
7420 // we only really need to check for one button pressed at a time, so we can break after
7422 if(Multi_jw_buttons[gr_screen.res][idx].button.pressed()){
7423 multi_jw_button_pressed(idx);
7429 void multi_jw_button_pressed(int n)
7433 gamesnd_play_iface(SND_USER_SELECT);
7434 multi_quit_game(PROMPT_CLIENT);
7436 case MJW_SCROLL_PLAYERS_UP:
7437 multi_jw_scroll_players_up();
7439 case MJW_SCROLL_PLAYERS_DOWN:
7440 multi_jw_scroll_players_down();
7442 case MJW_SCROLL_INFO_UP:
7443 multi_common_scroll_text_up();
7445 case MJW_SCROLL_INFO_DOWN:
7446 multi_common_scroll_text_down();
7449 // request to set myself to team 0
7451 gamesnd_play_iface(SND_USER_SELECT);
7452 multi_team_set_team(Net_player,0);
7455 // request to set myself to team 1
7457 gamesnd_play_iface(SND_USER_SELECT);
7458 multi_team_set_team(Net_player,1);
7462 case MJW_PILOT_INFO:
7463 Multi_jw_should_show_popup = 1;
7467 multi_common_add_notify(XSTR("Not implemented yet!",760));
7472 // do stuff like pinging servers, sending out requests, etc
7473 void multi_jw_do_netstuff()
7477 void multi_jw_scroll_players_up()
7479 gamesnd_play_iface(SND_GENERAL_FAIL);
7482 // scroll down through the player list
7483 void multi_jw_scroll_players_down()
7485 gamesnd_play_iface(SND_GENERAL_FAIL);
7488 void multi_jw_plist_process()
7490 int test_count,player_index,idx;
7492 // first determine if there are 0 players in the game. This should never happen since the host is _always_ in the game
7494 for(idx=0;idx<MAX_PLAYERS;idx++){
7495 // count anyone except the standalone server (if applicable)
7496 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7500 if(test_count <= 0){
7504 // if we had a selected item but that player has left, select myself instead
7505 if(Multi_jw_plist_select_flag){
7506 player_index = find_player_id(Multi_jw_plist_select_id);
7507 if(player_index == -1){
7508 Multi_jw_plist_select_id = Net_player->player_id;
7511 Multi_jw_plist_select_flag = 1;
7512 Multi_jw_plist_select_id = Net_player->player_id;
7515 // if the player has clicked somewhere in the player list area
7516 if(Multi_jw_plist_select_button.pressed()){
7520 player_id = multi_jw_get_mouse_id();
7521 player_index = find_player_id(player_id);
7522 if(player_index != -1){
7523 Multi_jw_plist_select_id = player_id;
7524 Multi_jw_plist_select_flag = 1;
7529 void multi_jw_plist_blit_normal()
7532 char str[CALLSIGN_LEN+1];
7533 int y_start = Mjw_players_coords[gr_screen.res][MJW_Y_COORD];
7536 // display all the players
7537 for(idx=0;idx<MAX_PLAYERS;idx++){
7538 // reset total offset
7541 // count anyone except the standalone server (if applicable)
7542 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7543 // highlight him if he's the host
7544 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7545 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7546 gr_set_color_fast(&Color_text_active_hi);
7548 gr_set_color_fast(&Color_bright);
7551 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7552 gr_set_color_fast(&Color_text_active);
7554 gr_set_color_fast(&Color_text_normal);
7558 // optionally draw his CD status
7559 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7560 gr_set_bitmap(Multi_common_icons[MICON_CD]);
7561 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7563 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7566 // make sure the string will fit, then display it
7567 strcpy(str,Net_players[idx].player->callsign);
7568 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
7571 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7572 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7579 void multi_jw_plist_blit_team()
7582 char str[CALLSIGN_LEN+1];
7583 int y_start = Mjw_players_coords[gr_screen.res][MJW_Y_COORD];
7586 // always blit the proper team button based on _my_ team status
7587 if(Net_player->p_info.team == 0){
7588 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.draw_forced(2);
7590 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.draw_forced(2);
7593 // display all the red players first
7594 for(idx=0;idx<MAX_PLAYERS;idx++){
7595 // reset total offset
7598 // count anyone except the standalone server (if applicable)
7599 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
7600 // highlight him if he's the host
7601 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7602 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7603 gr_set_color_fast(&Color_text_active_hi);
7605 gr_set_color_fast(&Color_bright);
7608 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7609 gr_set_color_fast(&Color_text_active);
7611 gr_set_color_fast(&Color_text_normal);
7615 // optionally draw his CD status
7616 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7617 gr_set_bitmap(Multi_common_icons[MICON_CD]);
7618 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7620 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7623 // blit the red team indicator
7624 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
7625 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
7626 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT]);
7627 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7629 total_offset += Multi_common_icon_dims[MICON_TEAM0_SELECT][0] + 1;
7632 if(Multi_common_icons[MICON_TEAM0] != -1){
7633 gr_set_bitmap(Multi_common_icons[MICON_TEAM0]);
7634 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7636 total_offset += Multi_common_icon_dims[MICON_TEAM0][0] + 1;
7640 // make sure the string will fit
7641 strcpy(str,Net_players[idx].player->callsign);
7642 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7644 // display him in the correct half of the list depending on his team
7645 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7650 // display all the green players next
7651 for(idx=0;idx<MAX_PLAYERS;idx++){
7652 // reset total offset
7655 // count anyone except the standalone server (if applicable)
7656 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
7657 // highlight him if he's the host
7658 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7659 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7660 gr_set_color_fast(&Color_text_active_hi);
7662 gr_set_color_fast(&Color_bright);
7665 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7666 gr_set_color_fast(&Color_text_active);
7668 gr_set_color_fast(&Color_text_normal);
7672 // optionally draw his CD status
7673 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7674 gr_set_bitmap(Multi_common_icons[MICON_CD]);
7675 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7677 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7680 // blit the red team indicator
7681 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
7682 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
7683 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT]);
7684 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7686 total_offset += Multi_common_icon_dims[MICON_TEAM1_SELECT][0] + 1;
7689 if(Multi_common_icons[MICON_TEAM1] != -1){
7690 gr_set_bitmap(Multi_common_icons[MICON_TEAM1]);
7691 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7693 total_offset += Multi_common_icon_dims[MICON_TEAM1][0] + 1;
7697 // make sure the string will fit
7698 strcpy(str,Net_players[idx].player->callsign);
7699 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
7702 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7704 // display him in the correct half of the list depending on his team
7705 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7711 void multi_jw_handle_join(net_player *pl)
7713 // for now just play a bloop sound
7714 gamesnd_play_iface(SND_ICON_DROP_ON_WING);
7717 short multi_jw_get_mouse_id()
7719 // determine where he clicked (y pixel value)
7721 Multi_jw_plist_select_button.get_mouse_pos(NULL,&y);
7723 // select things a little differently if we're in team vs. team or non-team vs. team mode
7725 if(Netgame.type_flags & NG_TYPE_TEAM){
7726 int player_index = -1;
7728 // look through all of team red first
7729 for(idx=0;idx<MAX_PLAYERS;idx++){
7730 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
7733 // if this is the _nth_ guy
7741 // if we still haven't found him yet, look through the green team
7742 if(player_index == -1){
7743 for(idx=0;idx<MAX_PLAYERS;idx++){
7744 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
7746 // if this is the _nth_ guy
7754 if(player_index != -1){
7755 return Net_players[idx].player_id;
7758 // select the nth active player if possible, disregarding the standalone server
7759 for(idx=0;idx<MAX_PLAYERS;idx++){
7760 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7763 // if this is the _nth_ guy
7765 return Net_players[idx].player_id;
7776 // -------------------------------------------------------------------------------------------------------------
7778 // MULTIPLAYER WAIT/SYNCH SCREEN
7781 #define MULTI_SYNC_HOST_COUNT 4 // host uses 4 buttons (and sometimes 5)
7782 #define MULTI_SYNC_CLIENT_COUNT 3 // client only uses 3 buttons
7784 char *Multi_sync_bitmap_fname[GR_NUM_RESOLUTIONS] = {
7785 "MultiSynch", // GR_640
7786 "2_MultiSynch" // GR_1024
7789 char *Multi_sync_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
7790 "MultiSynch-M", // GR_640
7791 "2_MultiSynch-M" // GR_1024
7796 // constants for coordinate lookup
7797 #define MS_X_COORD 0
7798 #define MS_Y_COORD 1
7799 #define MS_W_COORD 2
7800 #define MS_H_COORD 3
7802 UI_WINDOW Multi_sync_window; // the window object for the join screen
7803 int Multi_sync_button_count; // the # of buttons to use for this instance of mission sync
7804 int Multi_sync_bitmap; // the background bitmap
7807 #define MULTI_SYNC_NUM_BUTTONS 5
7808 #define MS_SCROLL_INFO_UP 0
7809 #define MS_SCROLL_INFO_DOWN 1
7813 ui_button_info Multi_sync_buttons[GR_NUM_RESOLUTIONS][MULTI_SYNC_NUM_BUTTONS] = {
7816 ui_button_info("MS_00", 0, 396, -1, -1, 0),
7817 ui_button_info("MS_01", 0, 435, -1, -1, 1),
7818 ui_button_info("MS_02", 496, 411, -1, -1, 2),
7819 ui_button_info("MS_04", 436, 403, -1, -1, 4),
7820 ui_button_info("MS_03", 556, 398, -1, -1, 3),
7822 ui_button_info("MS_00", 1, 404, -1, -1, 0),
7823 ui_button_info("MS_01", 1, 446, -1, -1, 1),
7824 ui_button_info("MS_03", 518, 426, 519, 416, 3),
7825 ui_button_info("MS_02", 469, 426, 479, 416, 2),
7826 ui_button_info("MS_04", 571, 420, 577, 416, 4),
7830 ui_button_info("2_MS_00", 2, 647, -1, -1, 0),
7831 ui_button_info("2_MS_01", 2, 713, -1, -1, 1),
7832 ui_button_info("2_MS_03", 829, 682, 831, 667, 3),
7833 ui_button_info("2_MS_02", 751, 682, 766, 667, 2),
7834 ui_button_info("2_MS_04", 914, 672, 924, 667, 4),
7840 #define MULTI_SYNC_NUM_TEXT 0
7842 #define MULTI_SYNC_NUM_TEXT 5
7845 #define MST_LAUNCH 2
7846 UI_XSTR Multi_sync_text[GR_NUM_RESOLUTIONS][MULTI_SYNC_NUM_TEXT] = {
7848 // not needed for FS1
7850 { "Kick", 1266, 479, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_KICK].button },
7851 { "Cancel", 387, 519, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_CANCEL].button },
7852 { "Launch", 801, 577, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_LAUNCH].button },
7853 { "Players", 1269, 23, 133, UI_XSTR_COLOR_GREEN, -1, NULL },
7854 { "Status", 1304, 228, 133, UI_XSTR_COLOR_GREEN, -1, NULL }
7858 // not needed for FS1
7860 { "Kick", 1266, 766, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_KICK].button },
7861 { "Cancel", 387, 831, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_CANCEL].button },
7862 { "Launch", 801, 924, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_LAUNCH].button },
7863 { "Players", 1269, 38, 214, UI_XSTR_COLOR_GREEN, -1, NULL },
7864 { "Status", 1304, 366, 214, UI_XSTR_COLOR_GREEN, -1, NULL }
7870 int Ms_status_coords[GR_NUM_RESOLUTIONS][4] = {
7879 // player status coords
7880 int Ms_status2_coords[GR_NUM_RESOLUTIONS][4] = {
7889 int Ms_cd_icon_offset[GR_NUM_RESOLUTIONS] = {
7894 int Ms_team_icon_offset[GR_NUM_RESOLUTIONS] = {
7899 // player currently selected, index into Net_players[]
7900 int Multi_sync_player_select = -1;
7902 // player list control thingie defs
7903 #define MULTI_SYNC_PLIST_MAX_DISPLAY 15
7904 int Multi_sync_plist_start; // where to start displaying from
7905 int Multi_sync_plist_count; // how many we have
7907 // list select button
7908 UI_BUTTON Multi_sync_plist_button;
7910 int Multi_sync_mode = -1;
7912 #define MULTI_SYNC_COUNTDOWN_TIME 5 // in seconds
7913 float Multi_sync_countdown_timer;
7914 int Multi_sync_countdown = -1;
7916 int Multi_launch_button_created;
7919 // countdown animation timer
7920 char* Multi_sync_countdown_fname[GR_NUM_RESOLUTIONS] = {
7922 "2_Count" // GR_1024
7925 int Multi_sync_countdown_coords[GR_NUM_RESOLUTIONS][2] = {
7936 anim *Multi_sync_countdown_anim = NULL;
7937 anim_instance *Multi_sync_countdown_instance = NULL;
7940 // PREBRIEFING STUFF
7941 // syncing flags used by the server
7942 int Mission_sync_flags = 0;
7943 #define MS_FLAG_SENT_FILESIG (1<<0) // sent filesig requests
7944 #define MS_FLAG_SENT_LOAD (1<<1) // sent load packets
7945 #define MS_FLAG_PUSHED_BRIEFING (1<<2) // pushed everyone else into the briefing
7946 #define MS_FLAG_POST_DATA (1<<3) // sent the post data block
7947 #define MS_FLAG_WSS_SLOTS (1<<4) // all players have received wss slot data
7948 #define MS_FLAG_PSETTINGS (1<<5) // send the player settings packet
7949 #define MS_FLAG_MT_STATS_START (1<<6) // server has started getting player stats from the tracker
7950 #define MS_FLAG_MT_STATS_DONE (1<<7) // server has finished getting player stats from the tracker (success or fail)
7951 #define MS_FLAG_TS_SLOTS (1<<8) // team/ship slots have been sent
7952 #define MS_FLAG_DATA_DONE (1<<9) // done transferring all necessary data
7953 #define MS_FLAG_CAMP_DONE (1<<10) // send campaign pool/goal/event stuff
7955 // POSTBRIEFING STUFF
7956 int Multi_state_timestamp;
7957 int Multi_sync_launch_pressed;
7959 // LOCAL function definitions
7960 void multi_sync_check_buttons();
7961 void multi_sync_button_pressed(int n);
7962 void multi_sync_scroll_info_up();
7963 void multi_sync_scroll_info_down();
7964 void multi_sync_display_name(char *name,int index,int np_index); // display info on the left hand portion of the status window thingie
7965 void multi_sync_display_status(char *status,int index); // display info on the right hand portion of the status window thingie
7966 void multi_sync_force_start_pre();
7967 void multi_sync_force_start_post();
7968 void multi_sync_launch();
7969 void multi_sync_create_launch_button();
7970 void multi_sync_blit_screen_all();
7971 void multi_sync_handle_plist();
7973 void multi_sync_common_init();
7974 void multi_sync_common_do();
7975 void multi_sync_common_close();
7977 void multi_sync_pre_init();
7978 void multi_sync_pre_do();
7979 void multi_sync_pre_close();
7981 void multi_sync_post_init();
7982 void multi_sync_post_do();
7983 void multi_sync_post_close();
7988 // perform the correct init functions
7989 void multi_sync_init()
7991 Multi_sync_countdown = -1;
7995 // reset all timestamp
7996 multi_reset_timestamps();
7998 extern int Player_multi_died_check;
7999 Player_multi_died_check = -1;
8001 if(!(Game_mode & GM_STANDALONE_SERVER)){
8002 multi_sync_common_init();
8005 switch(Multi_sync_mode){
8006 case MULTI_SYNC_PRE_BRIEFING:
8007 multi_sync_pre_init();
8009 case MULTI_SYNC_POST_BRIEFING:
8010 multi_sync_post_init();
8012 case MULTI_SYNC_INGAME:
8013 multi_ingame_sync_init();
8018 // perform the correct do frame functions
8019 void multi_sync_do()
8021 if(!(Game_mode & GM_STANDALONE_SERVER)){
8022 multi_sync_common_do();
8025 // if the netgame is ending, don't do any sync processing
8026 if(multi_endgame_ending()){
8030 // process appropriateliy
8031 switch(Multi_sync_mode){
8032 case MULTI_SYNC_PRE_BRIEFING:
8033 multi_sync_pre_do();
8035 case MULTI_SYNC_POST_BRIEFING:
8036 multi_sync_post_do();
8038 case MULTI_SYNC_INGAME:
8039 multi_ingame_sync_do();
8042 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8043 if(Multi_sync_bitmap != -1){
8044 gr_set_bitmap(Multi_sync_bitmap);
8047 Multi_sync_window.draw();
8049 multi_sync_blit_screen_all();
8056 // perform the correct close functions
8057 void multi_sync_close()
8059 switch(Multi_sync_mode){
8060 case MULTI_SYNC_PRE_BRIEFING:
8061 multi_sync_pre_close();
8063 case MULTI_SYNC_POST_BRIEFING:
8064 multi_sync_post_close();
8066 case MULTI_SYNC_INGAME:
8067 multi_ingame_sync_close();
8071 if(!(Game_mode & GM_STANDALONE_SERVER)){
8072 multi_sync_common_close();
8076 char *multi_sync_tooltip_handler(char *str)
8078 if (!stricmp(str, NOX("@launch"))) {
8079 if (Multi_launch_button_created){
8080 return XSTR("Launch",801);
8087 void multi_sync_common_init()
8091 // create the interface window
8092 Multi_sync_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
8093 Multi_sync_window.set_mask_bmap(Multi_sync_bitmap_mask_fname[gr_screen.res]);
8094 Multi_sync_window.tooltip_handler = multi_sync_tooltip_handler;
8096 // load the background bitmap
8097 Multi_sync_bitmap = bm_load(Multi_sync_bitmap_fname[gr_screen.res]);
8098 if (Multi_sync_bitmap < 0) {
8099 // we failed to load the bitmap - this is very bad
8103 // initialize the player list data
8104 Multi_sync_plist_start = 0;
8105 Multi_sync_plist_count = 1; // we can pretty safely assume that there's one player in the game - me.
8107 Multi_launch_button_created = 0;
8109 // create the chatbox thingie (shouldn't be necesary to do this, but we'll put it in for good measure)
8112 // force the chatbox to be small
8113 chatbox_force_small();
8115 // initialize the common notification messaging
8116 multi_common_notify_init();
8118 // initialize the common mission info display area.
8119 multi_common_set_text("");
8121 // use the common interface palette
8122 multi_common_set_palette();
8124 // don't select any player yet.
8125 Multi_sync_player_select = -1;
8127 // determine how many of the 5 buttons to create
8128 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8129 Multi_sync_button_count = MULTI_SYNC_HOST_COUNT;
8131 Multi_sync_button_count = MULTI_SYNC_CLIENT_COUNT;
8133 // create the interface buttons
8134 for(idx=0; idx<Multi_sync_button_count; idx++){
8135 // create the object
8136 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);
8138 // set the sound to play when highlighted
8139 Multi_sync_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
8141 // set the ani for the button
8142 // this wierdness is necessary because cancel and kick buttons aren't drawn on the background bitmap,
8143 // so we have to load in frame 0, too (the file should exist)
8144 if ((idx == MS_CANCEL) || (idx == MS_KICK) || (idx == MS_LAUNCH)) {
8145 Multi_sync_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sync_buttons[gr_screen.res][idx].filename, 3, 0);
8147 Multi_sync_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sync_buttons[gr_screen.res][idx].filename);
8151 Multi_sync_buttons[gr_screen.res][idx].button.link_hotspot(Multi_sync_buttons[gr_screen.res][idx].hotspot);
8155 for(idx=0; idx<MULTI_SYNC_NUM_TEXT; idx++) {
8156 // don't create the "launch" button text just yet
8157 if(idx == MST_LAUNCH) {
8160 // multiplayer clients should ignore the kick button
8161 if(!MULTIPLAYER_MASTER && !MULTIPLAYER_HOST && (idx == MST_KICK)) {
8165 Multi_sync_window.add_XSTR(&Multi_sync_text[gr_screen.res][idx]);
8168 // create the player list select button and hide it
8169 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);
8170 Multi_sync_plist_button.hide();
8172 // set up hotkeys for certain common functions
8173 Multi_sync_buttons[gr_screen.res][MS_CANCEL].button.set_hotkey(KEY_ESC);
8176 void multi_sync_common_do()
8178 int k = chatbox_process();
8179 k = Multi_sync_window.process(k);
8181 // process the player list
8182 multi_sync_handle_plist();
8184 // process any button clicks
8185 multi_sync_check_buttons();
8187 // process any keypresses
8191 gamesnd_play_iface(SND_USER_SELECT);
8192 multi_quit_game(PROMPT_ALL);
8197 void multi_sync_common_close()
8199 // unload any bitmaps
8200 if(!bm_unload(Multi_sync_bitmap)){
8201 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_sync_bitmap_fname[gr_screen.res]));
8204 extern int Player_multi_died_check;
8205 Player_multi_died_check = -1;
8207 // destroy the UI_WINDOW
8208 Multi_sync_window.destroy();
8211 void multi_sync_blit_screen_all()
8218 // display any text in the info area
8219 multi_common_render_text();
8221 // display any pending notification messages
8222 multi_common_notify_do();
8224 // display any info about visible players
8226 for(idx=0;idx<MAX_PLAYERS;idx++){
8227 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
8228 // display his name and status
8229 multi_sync_display_name(Net_players[idx].player->callsign,count,idx);
8231 // get the player state
8232 state = Net_players[idx].state;
8234 // if we're ingame joining, show all other players except myself as "playing"
8235 if((Net_player != NULL) && (&Net_players[idx] != Net_player) && ((Multi_sync_mode == MULTI_SYNC_INGAME) || (Net_player->flags & NETINFO_FLAG_INGAME_JOIN)) ){
8236 state = NETPLAYER_STATE_IN_MISSION;
8240 case NETPLAYER_STATE_MISSION_LOADING:
8241 multi_sync_display_status(XSTR("Mission Loading",802),count);
8243 case NETPLAYER_STATE_INGAME_SHIP_SELECT: // I don't think its possible to see this state, but...
8244 multi_sync_display_status(XSTR("Ingame Ship Select",803),count);
8246 case NETPLAYER_STATE_DEBRIEF:
8247 multi_sync_display_status(XSTR("Debriefing",804),count);
8249 case NETPLAYER_STATE_MISSION_SYNC:
8250 multi_sync_display_status(XSTR("Mission Sync",805),count);
8252 case NETPLAYER_STATE_JOINING:
8253 multi_sync_display_status(XSTR("Joining",806),count);
8255 case NETPLAYER_STATE_JOINED:
8256 multi_sync_display_status(XSTR("Joined",807),count);
8258 case NETPLAYER_STATE_SLOT_ACK :
8259 multi_sync_display_status(XSTR("Slot Ack",808),count);
8261 case NETPLAYER_STATE_BRIEFING:
8262 multi_sync_display_status(XSTR("Briefing",765),count);
8264 case NETPLAYER_STATE_SHIP_SELECT:
8265 multi_sync_display_status(XSTR("Ship Select",809),count);
8267 case NETPLAYER_STATE_WEAPON_SELECT:
8268 multi_sync_display_status(XSTR("Weapon Select",810),count);
8270 case NETPLAYER_STATE_WAITING:
8271 multi_sync_display_status(XSTR("Waiting",811),count);
8273 case NETPLAYER_STATE_IN_MISSION:
8274 multi_sync_display_status(XSTR("In Mission",812),count);
8276 case NETPLAYER_STATE_MISSION_LOADED:
8277 multi_sync_display_status(XSTR("Mission Loaded",813),count);
8279 case NETPLAYER_STATE_DATA_LOAD:
8280 multi_sync_display_status(XSTR("Data loading",814),count);
8282 case NETPLAYER_STATE_SETTINGS_ACK:
8283 multi_sync_display_status(XSTR("Ready To Enter Mission",815),count);
8285 case NETPLAYER_STATE_INGAME_SHIPS:
8286 multi_sync_display_status(XSTR("Ingame Ships Packet Ack",816),count);
8288 case NETPLAYER_STATE_INGAME_WINGS:
8289 multi_sync_display_status(XSTR("Ingame Wings Packet Ack",817),count);
8291 case NETPLAYER_STATE_INGAME_RPTS:
8292 multi_sync_display_status(XSTR("Ingame Respawn Points Ack",818),count);
8294 case NETPLAYER_STATE_SLOTS_ACK:
8295 multi_sync_display_status(XSTR("Ingame Weapon Slots Ack",819),count);
8297 case NETPLAYER_STATE_POST_DATA_ACK:
8298 multi_sync_display_status(XSTR("Post Briefing Data Block Ack",820),count);
8300 case NETPLAYER_STATE_FLAG_ACK :
8301 multi_sync_display_status(XSTR("Flags Ack",821),count);
8303 case NETPLAYER_STATE_MT_STATS :
8304 multi_sync_display_status(XSTR("Parallax Online Stats Updating",822),count);
8306 case NETPLAYER_STATE_WSS_ACK :
8307 multi_sync_display_status(XSTR("Weapon Slots Ack",823),count);
8309 case NETPLAYER_STATE_HOST_SETUP :
8310 multi_sync_display_status(XSTR("Host setup",824),count);
8312 case NETPLAYER_STATE_DEBRIEF_ACCEPT:
8313 multi_sync_display_status(XSTR("Debrief accept",825),count);
8315 case NETPLAYER_STATE_DEBRIEF_REPLAY:
8316 multi_sync_display_status(XSTR("Debrief replay",826),count);
8318 case NETPLAYER_STATE_CPOOL_ACK:
8319 multi_sync_display_status(XSTR("Campaign ship/weapon ack",827),count);
8321 case NETPLAYER_STATE_MISSION_XFER :
8323 // server should display the pct completion of all clients
8324 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8325 if(Net_players[idx].s_info.xfer_handle != -1){
8326 pct_complete = multi_xfer_pct_complete(Net_players[idx].s_info.xfer_handle);
8328 // if we've got a valid xfer handle
8329 if((pct_complete >= 0.0) && (pct_complete <= 1.0)){
8330 sprintf(txt,XSTR("Mission file xfer %d%%",828),(int)(pct_complete * 100.0f));
8334 strcpy(txt,XSTR("Mission file xfer",829));
8337 strcpy(txt,XSTR("Mission file xfer",829));
8340 // clients should display only for themselves (which is the only thing they know)
8342 // if we've got a valid file xfer handle
8343 if((&Net_players[idx] == Net_player) && (Net_player->s_info.xfer_handle != -1)){
8344 pct_complete = multi_xfer_pct_complete(Net_player->s_info.xfer_handle);
8346 // if we've got a valid xfer handle
8347 if((pct_complete >= 0.0) && (pct_complete <= 1.0)){
8348 sprintf(txt,XSTR("Mission file xfer %d%%",828),(int)(pct_complete * 100.0f));
8352 strcpy(txt,XSTR("Mission file xfer",829));
8357 strcpy(txt,XSTR("Mission file xfer",829));
8362 multi_sync_display_status(txt,count);
8365 nprintf(("Network","Unhandled player state : %d !\n",Net_players[idx].state));
8372 // display the mission start countdown timer (if any)
8373 anim_render_all(GS_STATE_MULTI_MISSION_SYNC,flFrametime);
8375 // process and show the chatbox thingie
8379 Multi_sync_window.draw_tooltip();
8381 // display the voice status indicator
8382 multi_common_voice_display_status();
8385 void multi_sync_check_buttons()
8388 for(idx=0;idx<Multi_sync_button_count;idx++){
8389 // we only really need to check for one button pressed at a time, so we can break after
8391 if(Multi_sync_buttons[gr_screen.res][idx].button.pressed()){
8392 multi_sync_button_pressed(idx);
8398 void multi_sync_button_pressed(int n)
8403 gamesnd_play_iface(SND_USER_SELECT);
8404 multi_quit_game(PROMPT_ALL);
8407 // scroll the info box up
8408 case MS_SCROLL_INFO_UP:
8409 multi_common_scroll_text_up();
8412 // scroll the info box down
8413 case MS_SCROLL_INFO_DOWN:
8414 multi_common_scroll_text_down();
8419 // if we have a currently selected player, kick him
8420 if(Multi_sync_player_select >= 0){
8421 multi_kick_player(Multi_sync_player_select);
8425 // start the final launch countdown (post-sync only)
8427 multi_sync_start_countdown();
8430 // doesn't do anything
8436 void multi_sync_pre_init()
8440 Netgame.game_state = NETGAME_STATE_MISSION_SYNC;
8442 // if we're in teamplay mode, always force skill level to be medium
8443 if((Netgame.type_flags & NG_TYPE_TEAM) && (Net_player->flags & NETINFO_FLAG_GAME_HOST)){
8444 Netgame.options.skill_level = NUM_SKILL_LEVELS / 2;
8445 Game_skill_level = NUM_SKILL_LEVELS / 2;
8446 multi_options_update_netgame();
8449 // notify everyone of when we get here
8450 if(!(Game_mode & GM_STANDALONE_SERVER)){
8451 Net_player->state = NETPLAYER_STATE_MISSION_SYNC;
8452 send_netplayer_update_packet();
8455 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8457 ml_string(NOX("Server performing pre-briefing data sync"));
8459 if(!(Game_mode & GM_STANDALONE_SERVER)){
8460 multi_common_set_text(XSTR("Server performing sync\n",830),1);
8463 // maybe initialize tvt and squad war stuff
8464 if(Netgame.type_flags & NG_TYPE_TEAM){
8465 multi_team_level_init();
8468 // force everyone into this state
8469 send_netgame_update_packet();
8471 if(!(Game_mode & GM_STANDALONE_SERVER)){
8472 multi_common_add_text(XSTR("Send update packet\n",831),1);
8475 // setup some of my own data
8476 Net_player->flags |= NETINFO_FLAG_MISSION_OK;
8478 // do any output stuff
8479 if(Game_mode & GM_STANDALONE_SERVER){
8480 std_debug_set_standalone_state_string("Mission Sync");
8483 // do this here to insure we have the most up to date file checksum info
8484 multi_get_mission_checksum(Game_current_mission_filename);
8485 // parse_get_file_signature(Game_current_mission_filename);
8487 if(!(Game_mode & GM_STANDALONE_SERVER)){
8488 multi_common_add_text(XSTR("Got file signatures\n",832),1);
8491 if(!(Game_mode & GM_STANDALONE_SERVER)){
8492 multi_common_add_text(XSTR("Sending update state packet\n",833),1);
8496 // if we're not in team vs. team mode - set all player teams to be 0, and unset all captaincy bits
8497 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
8498 for(idx=0;idx<MAX_PLAYERS;idx++){
8499 Net_players[idx].p_info.team = 0;
8500 Net_players[idx].flags &= ~(NETINFO_FLAG_TEAM_CAPTAIN);
8504 // we aren't necessarily xferring the mission file yet
8505 Assert(Net_player->s_info.xfer_handle == -1);
8507 // always call this for good measure
8508 multi_campaign_flush_data();
8510 Mission_sync_flags = 0;
8511 Multi_mission_loaded = 0;
8514 void multi_sync_pre_do()
8518 // If I'm the server, wait for everyone to arrive in this state, then begin transferring data, etc.
8519 // all servers (standalone or no, go through this)
8520 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8521 // wait for everyone to arrive, then request filesig from all of them
8522 if(multi_netplayer_state_check(NETPLAYER_STATE_MISSION_SYNC) && !(Mission_sync_flags & MS_FLAG_SENT_FILESIG) && !multi_endgame_ending()){
8523 send_file_sig_request(Netgame.mission_name);
8524 Mission_sync_flags |= MS_FLAG_SENT_FILESIG;
8526 if(!(Game_mode & GM_STANDALONE_SERVER)){
8527 multi_common_add_text(XSTR("Sent filesig request\n",834),1);
8531 // if we're waiting for players to receive files, then check on their status
8532 if((Mission_sync_flags & MS_FLAG_SENT_FILESIG) && !multi_netplayer_flag_check(NETINFO_FLAG_MISSION_OK) && !multi_endgame_ending()){
8533 for(idx=0;idx<MAX_PLAYERS;idx++){
8534 // if this player is in the process of xferring a file
8535 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].s_info.xfer_handle != -1)){
8536 switch(multi_xfer_get_status(Net_players[idx].s_info.xfer_handle)){
8537 // if it has successfully completed, set his ok flag
8538 case MULTI_XFER_SUCCESS :
8540 Net_players[idx].flags |= NETINFO_FLAG_MISSION_OK;
8542 // release the xfer instance handle
8543 multi_xfer_release_handle(Net_players[idx].s_info.xfer_handle);
8544 Net_players[idx].s_info.xfer_handle = -1;
8546 // if it has failed or timed-out, kick the player
8547 case MULTI_XFER_TIMEDOUT:
8548 case MULTI_XFER_FAIL:
8549 // release the xfer handle
8550 multi_xfer_release_handle(Net_players[idx].s_info.xfer_handle);
8551 Net_players[idx].s_info.xfer_handle = -1;
8554 multi_kick_player(idx, 0, KICK_REASON_BAD_XFER);
8561 // NOTE : this is now obsolete
8562 // once everyone is verified, do any data transfer necessary
8563 if(multi_netplayer_flag_check(NETINFO_FLAG_MISSION_OK) && !(Mission_sync_flags & MS_FLAG_DATA_DONE) && !multi_endgame_ending()){
8564 // do nothing for now
8565 Mission_sync_flags |= MS_FLAG_DATA_DONE;
8567 // send campaign pool data
8568 multi_campaign_send_pool_status();
8571 // wait for everyone to ack on campaign pool data (even in non-campaign situations)
8572 if((Mission_sync_flags & MS_FLAG_DATA_DONE) && !(Mission_sync_flags & MS_FLAG_CAMP_DONE) && !multi_endgame_ending()){
8573 // check to see if everyone has acked the campaign pool data
8574 if(multi_netplayer_state_check(NETPLAYER_STATE_CPOOL_ACK)){
8575 Mission_sync_flags |= MS_FLAG_CAMP_DONE;
8579 // once everyone is verified, tell them to load the mission
8580 // also make sure to load the mission myself _AFTER_ telling everyone to do so. This makes the whole process
8581 // move along faster
8582 if((Mission_sync_flags & MS_FLAG_CAMP_DONE) && !(Mission_sync_flags & MS_FLAG_SENT_LOAD) && !multi_endgame_ending()){
8583 send_netplayer_load_packet(NULL);
8584 Mission_sync_flags |= MS_FLAG_SENT_LOAD;
8586 if(!(Game_mode & GM_STANDALONE_SERVER)){
8587 multi_common_add_text(XSTR("Sent load packet\n",835),1);
8590 // load the mission myself, as soon as possible
8591 if(!Multi_mission_loaded){
8592 nprintf(("Network","Server loading mission..."));
8594 // update everyone about my status
8595 Net_player->state = NETPLAYER_STATE_MISSION_LOADING;
8596 send_netplayer_update_packet();
8598 game_start_mission();
8600 nprintf(("Network","Done\n"));
8601 Multi_mission_loaded = 1;
8603 // update everyone about my status
8604 Net_player->state = NETPLAYER_STATE_MISSION_LOADED;
8605 send_netplayer_update_packet();
8607 if(!(Game_mode & GM_STANDALONE_SERVER)){
8608 multi_common_add_text(XSTR("Loaded mission locally\n",836),1);
8613 // if everyone has loaded the mission, randomly assign players to ships
8614 if(multi_netplayer_state_check(NETPLAYER_STATE_MISSION_LOADED) && !(Mission_sync_flags & MS_FLAG_TS_SLOTS) && !multi_endgame_ending()){
8615 // call the team select function to assign players to their ships, wings, etc
8616 multi_ts_assign_players_all();
8617 send_netplayer_slot_packet();
8620 Mission_sync_flags |= MS_FLAG_TS_SLOTS;
8623 // if everyone has loaded the mission, move to the team select stage
8624 if(Sync_test && multi_netplayer_state_check(NETPLAYER_STATE_SLOT_ACK) && !(Mission_sync_flags & MS_FLAG_PUSHED_BRIEFING) && !multi_endgame_ending()){
8625 Netgame.game_state = NETGAME_STATE_BRIEFING;
8626 send_netgame_update_packet(); // this will push everyone into the next state
8628 // the standalone moves to his own wait state, whereas in the normal game mode, the server/host moves in to the
8629 // team select state
8630 if(Game_mode & GM_STANDALONE_SERVER){
8631 gameseq_post_event(GS_EVENT_MULTI_STD_WAIT);
8633 gameseq_post_event(GS_EVENT_START_GAME);
8636 Mission_sync_flags |= MS_FLAG_PUSHED_BRIEFING;
8638 if(!(Game_mode & GM_STANDALONE_SERVER)){
8639 multi_common_add_text(XSTR("Moving to team select\n",837),1);
8643 // clients should detect here if they are doing a file xfer and do error processing
8644 if((Net_player->state == NETPLAYER_STATE_MISSION_XFER) && (Net_player->s_info.xfer_handle != -1) && !multi_endgame_ending()){
8645 switch(multi_xfer_get_status(Net_player->s_info.xfer_handle)){
8646 // if it has successfully completed, set his ok flag
8647 case MULTI_XFER_SUCCESS :
8648 // release my xfer handle
8649 multi_xfer_release_handle(Net_player->s_info.xfer_handle);
8650 Net_player->s_info.xfer_handle = -1;
8653 // if it has failed or timed-out, kick the player
8654 case MULTI_XFER_TIMEDOUT:
8655 case MULTI_XFER_FAIL:
8656 // release my xfer handle
8657 multi_xfer_release_handle(Net_player->s_info.xfer_handle);
8658 Net_player->s_info.xfer_handle = -1;
8660 // leave the game qith an error code
8661 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_XFER_FAIL);
8668 if(!(Game_mode & GM_STANDALONE_SERVER)){
8670 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8671 if(Multi_sync_bitmap != -1){
8672 gr_set_bitmap(Multi_sync_bitmap);
8675 Multi_sync_window.draw();
8677 multi_sync_blit_screen_all();
8683 void multi_sync_pre_close()
8685 // at this point, we should shut down any file xfers...
8686 if(Net_player->s_info.xfer_handle != -1){
8687 nprintf(("Network","WARNING - killing file xfer while leaving mission sync state!!!\n"));
8689 multi_xfer_abort(Net_player->s_info.xfer_handle);
8690 Net_player->s_info.xfer_handle;
8694 void multi_sync_post_init()
8696 multi_reset_timestamps();
8698 Multi_state_timestamp = timestamp(0);
8701 ml_string(NOX("Performing post-briefing data sync"));
8703 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8704 multi_common_add_text(XSTR("Server performing sync\n",830),1);
8706 multi_common_add_text(XSTR("Client performing sync\n",838),1);
8709 // everyone should re-initialize these
8710 init_multiplayer_stats();
8712 // reset all sequencing info
8713 multi_oo_reset_sequencing();
8715 // if I am not the master of the game, then send the firing information for my ship
8717 if ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) ){
8718 send_firing_info_packet();
8721 // if I'm not a standalone server, load up the countdown stuff
8722 if(!(Game_mode & GM_STANDALONE_SERVER)){
8723 Multi_sync_countdown_anim = NULL;
8724 Multi_sync_countdown_instance = NULL;
8725 Multi_sync_countdown_anim = anim_load(Multi_sync_countdown_fname[gr_screen.res]);
8726 if(Multi_sync_countdown_anim == NULL){
8727 nprintf(("General","WARNING!, Could not load countdown animation %s!\n",Multi_sync_countdown_fname[gr_screen.res]));
8731 // create objects for all permanent observers
8732 multi_obs_level_init();
8734 // clear the game start countdown timer
8735 Multi_sync_countdown_timer = -1.0f;
8736 Multi_sync_countdown = -1;
8738 // if this is a team vs. team mission, mark all ship teams appropriately
8739 if(Netgame.type_flags & NG_TYPE_TEAM){
8740 multi_team_mark_all_ships();
8743 Mission_sync_flags = 0;
8744 Multi_sync_launch_pressed = 0;
8747 #define MULTI_POST_TIMESTAMP 7000
8749 extern int create_wings();
8751 void multi_sync_post_do()
8755 // only if the host is also the master should he be doing this (non-standalone situation)
8756 if ( Net_player->flags & NETINFO_FLAG_AM_MASTER ) {
8758 // once everyone gets to this screen, send them the ship classes of all ships.
8759 if(multi_netplayer_state_check(NETPLAYER_STATE_WAITING) && !(Mission_sync_flags & MS_FLAG_POST_DATA)) {
8760 // only the host should ever do this
8761 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8762 // at this point we want to delete all necessary ships, change all necessary ship classes, and set all weapons up
8763 multi_ts_create_wings();
8765 // update player ets settings
8766 for(idx=0;idx<MAX_PLAYERS;idx++){
8767 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].player->objnum != -1)){
8768 multi_server_update_player_weapons(&Net_players[idx],&Ships[Objects[Net_players[idx].player->objnum].instance]);
8773 // note that this is done a little differently for standalones and nonstandalones
8774 send_post_sync_data_packet();
8776 multi_common_add_text(XSTR("Sending post briefing block information\n",839),1);
8778 Mission_sync_flags |= MS_FLAG_POST_DATA;
8781 // send weapon slots data
8782 if(multi_netplayer_state_check(NETPLAYER_STATE_POST_DATA_ACK) && !(Mission_sync_flags & MS_FLAG_WSS_SLOTS)) {
8783 // note that this is done a little differently for standalones and nonstandalones
8784 if(Netgame.type_flags & NG_TYPE_TEAM){
8785 send_wss_slots_data_packet(0,0);
8786 send_wss_slots_data_packet(1,1);
8788 send_wss_slots_data_packet(0,1);
8791 multi_common_add_text(XSTR("Sending weapon slots information\n",840),1);
8793 Mission_sync_flags |= MS_FLAG_WSS_SLOTS;
8796 // once weapon information is received, send player settings info
8797 if ( multi_netplayer_state_check(NETPLAYER_STATE_WSS_ACK) && !(Mission_sync_flags & MS_FLAG_PSETTINGS)) {
8798 send_player_settings_packet();
8800 // server (specifically, the standalone), should set this here
8801 Net_player->state = NETPLAYER_STATE_SETTINGS_ACK;
8802 send_netplayer_update_packet();
8804 multi_common_add_text(XSTR("Sending player settings packets\n",841),1);
8806 Mission_sync_flags |= MS_FLAG_PSETTINGS;
8809 // check to see if the countdown timer has started and act appropriately
8810 if( Multi_sync_countdown_timer > -1.0f ) {
8812 // increment by frametime.
8813 Multi_sync_countdown_timer += flFrametime;
8815 // if the animation is not playing, start it
8816 if(!(Game_mode & GM_STANDALONE_SERVER) && (Multi_sync_countdown_instance == NULL) && (Multi_sync_countdown_anim != NULL)){
8817 anim_play_struct aps;
8819 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]);
8820 aps.screen_id = GS_STATE_MULTI_MISSION_SYNC;
8821 aps.framerate_independent = 1;
8823 Multi_sync_countdown_instance = anim_play(&aps);
8826 // if the next second has expired
8827 if( Multi_sync_countdown_timer >= 1.0f ) {
8829 Multi_sync_countdown--;
8830 Multi_sync_countdown_timer = 0.0f;
8832 // if the countdown has reached 0, launch the mission
8833 if(Multi_sync_countdown == 0){
8834 Multi_sync_countdown_timer = -1.0f;
8836 Multi_sync_launch_pressed = 0;
8837 multi_sync_launch();
8839 // otherwise send a countdown packet
8841 send_countdown_packet(Multi_sync_countdown);
8846 // jump into the mission myself
8847 if((Multi_sync_countdown == 0) && multi_netplayer_state_check(NETPLAYER_STATE_IN_MISSION)){
8848 if(!((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !Multi_sync_launch_pressed)){
8849 Net_player->state = NETPLAYER_STATE_IN_MISSION;
8850 Netgame.game_state = NETGAME_STATE_IN_MISSION;
8851 gameseq_post_event(GS_EVENT_ENTER_GAME);
8853 multi_common_add_text(XSTR("Moving into game\n",842),1);
8857 // maybe start the animation countdown
8858 if((Multi_sync_countdown >= 0) && (Multi_sync_countdown_instance == NULL) && (Multi_sync_countdown_anim != NULL)){
8859 anim_play_struct aps;
8861 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]);
8862 aps.screen_id = GS_STATE_MULTI_MISSION_SYNC;
8863 aps.framerate_independent = 1;
8865 Multi_sync_countdown_instance = anim_play(&aps);
8869 // host - specific stuff
8870 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8871 // create the launch button so the host can click
8872 if( Sync_test && multi_netplayer_state_check(NETPLAYER_STATE_SETTINGS_ACK) ){
8873 multi_sync_create_launch_button();
8878 if(!(Game_mode & GM_STANDALONE_SERVER)){
8880 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8881 if(Multi_sync_bitmap != -1){
8882 gr_set_bitmap(Multi_sync_bitmap);
8885 Multi_sync_window.draw();
8887 multi_sync_blit_screen_all();
8893 void multi_sync_post_close()
8897 // if I'm not a standalone server, unload up the countdown stuff
8898 if(!(Game_mode & GM_STANDALONE_SERVER)){
8899 // release all rendering animation instances (should only be 1)
8900 anim_release_all_instances(GS_STATE_MULTI_MISSION_SYNC);
8901 Multi_sync_countdown_instance = NULL;
8903 // free up the countdown animation
8904 if(Multi_sync_countdown_anim != NULL){
8905 anim_free(Multi_sync_countdown_anim);
8906 Multi_sync_countdown_anim = NULL;
8910 // all players should reset sequencing
8911 for(idx=0;idx<MAX_PLAYERS;idx++){
8912 if(Net_player->flags & NETINFO_FLAG_CONNECTED){
8913 Net_players[idx].client_cinfo_seq = 0;
8914 Net_players[idx].client_server_seq = 0;
8918 // multiplayer dogfight
8919 multi_df_level_pre_enter();
8921 // clients should clear obj_pair array and add pair for themselves
8923 if ( MULTIPLAYER_CLIENT ) {
8925 obj_add_pairs( OBJ_INDEX(Player_obj) );
8930 void multi_sync_display_name(char *name,int index,int np_index)
8932 char fit[CALLSIGN_LEN];
8934 // make sure the string actually fits
8937 // if we're in team vs. team mode
8938 if(Netgame.type_flags & NG_TYPE_TEAM){
8939 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]);
8941 // if this is the currently selected player, draw him highlighted
8942 if(np_index == Multi_sync_player_select){
8943 gr_set_color_fast(&Color_text_selected);
8945 gr_set_color_fast(&Color_text_normal);
8949 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);
8951 // blit his team icon
8953 if(Net_players[np_index].p_info.team == 0){
8954 // blit the team captain icon
8955 if(Net_players[np_index].flags & NETINFO_FLAG_TEAM_CAPTAIN){
8956 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
8957 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT]);
8958 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);
8961 // normal team member icon
8963 if(Multi_common_icons[MICON_TEAM0] != -1){
8964 gr_set_bitmap(Multi_common_icons[MICON_TEAM0]);
8965 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);
8970 else if(Net_players[np_index].p_info.team == 1){
8971 // blit the team captain icon
8972 if(Net_players[np_index].flags & NETINFO_FLAG_TEAM_CAPTAIN){
8973 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
8974 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT]);
8975 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);
8978 // normal team member icon
8980 if(Multi_common_icons[MICON_TEAM1] != -1){
8981 gr_set_bitmap(Multi_common_icons[MICON_TEAM1]);
8982 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);
8987 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]);
8989 // if this is the currently selected player, draw him highlighted
8990 if(np_index == Multi_sync_player_select){
8991 gr_set_color_fast(&Color_text_selected);
8993 gr_set_color_fast(&Color_text_normal);
8997 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);
9000 // maybe blit his CD status icon
9001 if((Net_players[np_index].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
9002 gr_set_bitmap(Multi_common_icons[MICON_CD]);
9003 gr_bitmap(Ms_status_coords[gr_screen.res][MS_X_COORD], Ms_status_coords[gr_screen.res][MS_Y_COORD] + (index * 10));
9007 void multi_sync_display_status(char *status,int index)
9011 // make sure the string actually fits
9012 strcpy(fit, status);
9013 gr_force_fit_string(fit, 250, Ms_status2_coords[gr_screen.res][MS_W_COORD] - 20);
9014 gr_set_color_fast(&Color_bright);
9015 gr_string(Ms_status2_coords[gr_screen.res][MS_X_COORD], Ms_status2_coords[gr_screen.res][MS_Y_COORD] + (index * 10), fit);
9018 void multi_sync_force_start_pre()
9021 int want_state = NETPLAYER_STATE_SLOT_ACK; // kick any players who are still in this state
9023 // go through the player list and boot anyone who isn't in the right state
9024 for(idx=0;idx<MAX_PLAYERS;idx++){
9025 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx]) && (Net_players[idx].state == want_state)){
9026 multi_kick_player(idx,0);
9031 void multi_sync_force_start_post()
9035 int num_kill_states;
9037 // determine the state we want all players in so that we can find those who are not in the state
9038 kill_state[0] = NETPLAYER_STATE_BRIEFING;
9039 kill_state[1] = NETPLAYER_STATE_SHIP_SELECT;
9040 kill_state[2] = NETPLAYER_STATE_WEAPON_SELECT;
9041 num_kill_states = 3;
9043 // go through the player list and boot anyone who isn't in the right state
9044 for(idx=0;idx<MAX_PLAYERS;idx++){
9045 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx])){
9046 // check against all kill state
9047 for(idx2 = 0;idx2<num_kill_states;idx2++){
9048 if(Net_players[idx].state == kill_state[idx2]){
9049 multi_kick_player(idx,0);
9057 void multi_sync_start_countdown()
9059 // don't allow repeat button presses
9060 if(Multi_sync_launch_pressed){
9064 Multi_sync_launch_pressed = 1;
9066 // if I'm the server, begin the countdown
9067 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
9068 gamesnd_play_iface(SND_COMMIT_PRESSED);
9069 Multi_sync_countdown_timer = 0.0f;
9070 Multi_sync_countdown = MULTI_SYNC_COUNTDOWN_TIME;
9072 // send an initial countdown value
9073 send_countdown_packet(Multi_sync_countdown);
9075 // otherwise send the "start countdown" packet to the standalone
9077 Assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
9078 send_countdown_packet(-1);
9082 void multi_sync_launch()
9084 // don't allow repeat button presses
9085 if(Multi_sync_launch_pressed){
9089 Multi_sync_launch_pressed = 1;
9092 ml_printf(NOX("Entering mission %s"), Game_current_mission_filename);
9094 // tell everyone to jump into the mission
9095 send_jump_into_mission_packet();
9096 Multi_state_timestamp = timestamp(MULTI_POST_TIMESTAMP);
9098 // set the # of players at the start of the mission
9099 Multi_num_players_at_start = multi_num_players();
9100 nprintf(("Network","# of players at start of mission : %d\n", Multi_num_players_at_start));
9102 // initialize datarate limiting for all clients
9103 multi_oo_rate_init_all();
9105 multi_common_add_text(XSTR("Sending mission start packet\n",843),1);
9108 void multi_sync_create_launch_button()
9110 if (!Multi_launch_button_created) {
9111 // create the object
9112 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);
9114 // set the sound to play when highlighted
9115 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_highlight_action(common_play_highlight_sound);
9117 // set the ani for the button
9118 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_bmaps(Multi_sync_buttons[gr_screen.res][MS_LAUNCH].filename, 3, 0);
9121 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.link_hotspot(Multi_sync_buttons[gr_screen.res][MS_LAUNCH].hotspot);
9124 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_hotkey(KEY_CTRLED+KEY_ENTER);
9127 // create the text for the button
9128 Multi_sync_window.add_XSTR(&Multi_sync_text[gr_screen.res][MST_LAUNCH]);
9131 // increment the button count so we start checking this one
9132 Multi_sync_button_count++;
9134 Multi_launch_button_created = 1;
9138 void multi_sync_handle_plist()
9144 // if we don't have a currently selected player, select one
9145 if((Multi_sync_player_select < 0) || !MULTI_CONNECTED(Net_players[Multi_sync_player_select])){
9146 for(idx=0;idx<MAX_PLAYERS;idx++){
9147 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
9148 Multi_sync_player_select = idx;
9154 // check for button list presses
9155 if(Multi_sync_plist_button.pressed()){
9156 // get the y mouse coords
9157 Multi_sync_plist_button.get_mouse_pos(NULL,&my);
9159 // get the index of the item selected
9160 select_index = my / 10;
9162 // if the index is greater than the current # connections, do nothing
9163 if(select_index > (multi_num_connections() - 1)){
9167 // translate into an absolute Net_players[] index (get the Nth net player)
9168 Multi_sync_player_select = -1;
9169 for(idx=0;idx<MAX_PLAYERS;idx++){
9170 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
9174 // if we've found the item we're looking for
9175 if(select_index < 0){
9176 Multi_sync_player_select = idx;
9181 // if for some bizarre reason, this is an invalid player, unselect him and wait for the next interation
9183 if((Multi_sync_player_select >= 0) && (!MULTI_CONNECTED(Net_players[Multi_sync_player_select]) || MULTI_STANDALONE(Net_players[Multi_sync_player_select])) ){
9184 Multi_sync_player_select = -1;
9190 // -------------------------------------------------------------------------------------------------------------
9192 // MULTIPLAYER DEBRIEF SCREEN
9195 // other relevant data
9196 int Multi_debrief_accept_hit;
9197 int Multi_debrief_replay_hit;
9199 // set if the server has left the game
9200 int Multi_debrief_server_left = 0;
9202 // if we've reported on TvT status all players are in the debrief
9203 int Multi_debrief_reported_tvt = 0;
9205 // whether stats are being accepted
9206 // -1 == no decision yet
9209 int Multi_debrief_stats_accept_code = -1;
9211 int Multi_debrief_server_framecount = 0;
9213 float Multi_debrief_time = 0.0f;
9214 float Multi_debrief_resend_time = 10.0f;
9216 void multi_debrief_init()
9220 Multi_debrief_time = 0.0f;
9221 Multi_debrief_resend_time = 10.0f;
9223 // do this to notify the standalone or the normal server that we're in the debrief state and ready to receive packets
9224 if (!(Net_player->flags & NETINFO_FLAG_AM_MASTER)) {
9225 Net_player->state = NETPLAYER_STATE_DEBRIEF;
9226 send_netplayer_update_packet();
9229 // unflag some stuff
9230 for(idx=0;idx<MAX_PLAYERS;idx++){
9231 if(MULTI_CONNECTED(Net_players[idx])){
9232 Net_players[idx].flags &= ~(NETINFO_FLAG_RESPAWNING | NETINFO_FLAG_LIMBO | NETINFO_FLAG_WARPING_OUT);
9236 // if text input mode is active, clear it
9237 multi_msg_text_flush();
9239 // the server has not left yet
9240 Multi_debrief_server_left = 0;
9242 // have not hit accept or replay yet
9243 Multi_debrief_accept_hit = 0;
9244 Multi_debrief_replay_hit = 0;
9246 // stats have not been accepted yet
9247 Multi_debrief_stats_accept_code = -1;
9249 // mark stats as not being store yet
9250 Netgame.flags &= ~(NG_FLAG_STORED_MT_STATS);
9252 // no report on TvT yet
9253 Multi_debrief_reported_tvt = 0;
9255 Multi_debrief_server_framecount = 0;
9258 void multi_debrief_do_frame()
9260 Multi_debrief_time += flFrametime;
9262 // set the netgame state to be debriefing when appropriate
9263 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)){
9264 Netgame.game_state = NETGAME_STATE_DEBRIEF;
9265 send_netgame_update_packet();
9268 // evaluate all server stuff
9269 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
9270 multi_debrief_server_process();
9274 void multi_debrief_close()
9276 if ( MULTIPLAYER_CLIENT && (Netgame.game_state == NETGAME_STATE_MISSION_SYNC) ){
9277 gamesnd_play_iface( SND_COMMIT_PRESSED );
9281 // handle optional mission loop
9282 void multi_maybe_set_mission_loop()
9284 int cur = Campaign.current_mission;
9285 if (Campaign.missions[cur].has_mission_loop) {
9286 Assert(Campaign.loop_mission != CAMPAIGN_LOOP_MISSION_UNINITIALIZED);
9288 bool require_repeat_mission = (Campaign.current_mission == Campaign.next_mission);
9290 // check for (1) mission loop available, (2) dont have to repeat last mission
9291 if ( (Campaign.missions[cur].has_mission_loop && (Campaign.loop_mission != -1)) && !require_repeat_mission ) {
9294 debrief_assemble_optional_mission_popup_text(buffer, Campaign.missions[cur].mission_loop_desc);
9296 int choice = popup(0 , 2, POPUP_NO, POPUP_YES, buffer);
9298 Campaign.loop_enabled = 1;
9299 Campaign.next_mission = Campaign.loop_mission;
9304 // handle all cases for when the accept key is hit in a multiplayer debriefing
9305 void multi_debrief_accept_hit()
9307 // if we already accepted, do nothing
9308 // but he may need to hit accept again after the server has left the game, so allow this
9309 if(Multi_debrief_accept_hit){
9313 // mark this so that we don't hit it again
9314 Multi_debrief_accept_hit = 1;
9316 gamesnd_play_iface(SND_COMMIT_PRESSED);
9318 // if the server has left the game, always just end the game.
9319 if(Multi_debrief_server_left){
9320 if(!multi_quit_game(PROMPT_ALL)){
9321 Multi_debrief_server_left = 1;
9322 Multi_debrief_accept_hit = 0;
9324 Multi_debrief_server_left = 0;
9327 // query the host and see if he wants to accept stats
9328 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
9329 // if we're on a tracker game, he gets no choice for storing stats
9330 if(MULTI_IS_TRACKER_GAME){
9331 multi_maybe_set_mission_loop();
9333 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));
9335 // evaluate the result
9340 Multi_debrief_accept_hit = 0;
9343 // set the accept code to be "not accepting"
9345 multi_debrief_stats_toss();
9346 multi_maybe_set_mission_loop();
9349 // accept the stats and continue
9351 multi_debrief_stats_accept();
9352 multi_maybe_set_mission_loop();
9358 // set my netplayer state to be "debrief_accept", and be done with it
9359 Net_player->state = NETPLAYER_STATE_DEBRIEF_ACCEPT;
9360 send_netplayer_update_packet();
9364 // handle all cases for when the escape key is hit in a multiplayer debriefing
9365 void multi_debrief_esc_hit()
9369 // if the server has left
9370 if(Multi_debrief_server_left){
9371 multi_quit_game(PROMPT_ALL);
9376 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
9377 // if the stats have already been accepted
9378 if((Multi_debrief_stats_accept_code != -1) || (MULTI_IS_TRACKER_GAME)){
9379 multi_quit_game(PROMPT_HOST);
9381 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));
9383 // evaluate the result
9390 // set the accept code to be "not accepting"
9392 multi_debrief_stats_toss();
9393 multi_quit_game(PROMPT_NONE);
9396 // accept the stats and continue
9398 multi_debrief_stats_accept();
9399 multi_quit_game(PROMPT_NONE);
9404 // if the stats haven't been accepted yet, or this is a tracker game
9405 if((Multi_debrief_stats_accept_code == -1) && !(MULTI_IS_TRACKER_GAME)){
9406 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));
9408 // evaluate the result
9410 multi_quit_game(PROMPT_NONE);
9413 // otherwise go through the normal endgame channels
9415 multi_quit_game(PROMPT_ALL);
9420 void multi_debrief_replay_hit()
9422 // only the host should ever get here
9423 Assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
9425 // if the button was already pressed, do nothing
9426 if(Multi_debrief_accept_hit){
9430 // same as hittin the except button except no stats are kept
9431 Multi_debrief_accept_hit = 1;
9433 // mark myself as being in the replay state so we know what to do next
9434 Net_player->state = NETPLAYER_STATE_DEBRIEF_REPLAY;
9435 send_netplayer_update_packet();
9438 // call this when the server has left and we would otherwise be saying "contact lost with server
9439 void multi_debrief_server_left()
9442 Multi_debrief_server_left = 1;
9444 // undo any "accept" hit so that clients can hit accept again to leave
9445 Multi_debrief_accept_hit = 0;
9448 void multi_debrief_stats_accept()
9450 // don't do anything if we've already accepted
9451 if(Multi_debrief_stats_accept_code != -1){
9455 Multi_debrief_stats_accept_code = 1;
9457 // if we're the host, and we're on a standalone, tell the standalone to begin the stats storing process
9458 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) || (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
9459 // send a packet to the players telling them to store their stats
9460 send_store_stats_packet(1);
9463 // add a chat line saying "stats have been accepted"
9464 multi_display_chat_msg(XSTR("<stats have been accepted>",850),0,0);
9467 ml_string(NOX("Stats stored"));
9470 void multi_debrief_stats_toss()
9472 // don't do anything if we've already accepted
9473 if(Multi_debrief_stats_accept_code != -1){
9477 Multi_debrief_stats_accept_code = 0;
9479 // if we're the host, and we're on a standalone, tell everyone to "toss" stats
9480 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) || (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
9481 // send a packet to the players telling them to store their stats
9482 send_store_stats_packet(0);
9485 // add a chat line saying "stats have been accepted"
9486 multi_display_chat_msg(XSTR("<stats have been tossed>",851),0,0);
9489 ml_string(NOX("Stats tossed"));
9492 int multi_debrief_stats_accept_code()
9494 return Multi_debrief_stats_accept_code;
9497 void multi_debrief_server_process()
9500 int player_status,other_status;
9502 Multi_debrief_server_framecount++;
9504 // if we're > 10 seconds into the debrief and not everyone is here, try warping everyone out again
9505 if((Multi_debrief_time >= Multi_debrief_resend_time) && !multi_netplayer_state_check3(NETPLAYER_STATE_DEBRIEF, NETPLAYER_STATE_DEBRIEF_ACCEPT, NETPLAYER_STATE_DEBRIEF_REPLAY, 1)){
9506 // find all players who are not in the debrief state and hit them with the endgame packet
9507 for(idx=0; idx<MAX_PLAYERS; idx++){
9508 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)) ){
9509 send_endgame_packet(&Net_players[idx]);
9514 Multi_debrief_resend_time += 7.0f;
9517 // evaluate the status of all players in the game (0 == not ready, 1 == ready to continue, 2 == ready to replay)
9520 // check all players
9521 for(idx=0;idx<MAX_PLAYERS;idx++){
9522 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_HOST(Net_players[idx])){
9523 if(Net_players[idx].state != NETPLAYER_STATE_DEBRIEF_ACCEPT){
9530 // if we haven't already reported TvT results
9531 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)){
9532 multi_team_report();
9533 Multi_debrief_reported_tvt = 1;
9536 // if all other players are good to go, check the host
9538 // if he is ready to continue
9539 if(Netgame.host->state == NETPLAYER_STATE_DEBRIEF_ACCEPT){
9542 // if he wants to replay the mission
9543 else if(Netgame.host->state == NETPLAYER_STATE_DEBRIEF_REPLAY){
9546 // if he is not ready
9551 // if all players are _not_ good to go
9556 // if we're in the debriefing state in a campaign mode, process accordingly
9557 if(Netgame.campaign_mode == MP_CAMPAIGN){
9558 multi_campaign_do_debrief(player_status);
9560 // otherwise process as normal (looking for all players to be ready to go to the next mission
9562 if(player_status == 1){
9563 multi_flush_mission_stuff();
9565 // set the netgame state to be forming and continue
9566 Netgame.game_state = NETGAME_STATE_FORMING;
9567 send_netgame_update_packet();
9569 // move to the proper state
9570 if(Game_mode & GM_STANDALONE_SERVER){
9571 gameseq_post_event(GS_EVENT_STANDALONE_MAIN);
9573 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
9576 multi_reset_timestamps();
9577 } else if(player_status == 2){
9578 multi_flush_mission_stuff();
9580 // tell everyone to move into the pre-briefing sync state
9581 Netgame.game_state = NETGAME_STATE_MISSION_SYNC;
9582 send_netgame_update_packet();
9584 // move back to the mission sync screen for the same mission again
9585 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
9586 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
9588 multi_reset_timestamps();
9594 // -------------------------------------------------------------------------------------------------------------
9596 // MULTIPLAYER PASSWORD POPUP
9601 static char *Multi_pwd_bitmap_fname[GR_NUM_RESOLUTIONS] = {
9602 "Password", // GR_640
9603 "2_Password" // GR_1024
9606 static char *Multi_pwd_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
9607 "Password-M", // GR_640
9608 "2_Password-M" // GR_1024
9613 // constants for coordinate lookup
9614 #define MPWD_X_COORD 0
9615 #define MPWD_Y_COORD 1
9616 #define MPWD_W_COORD 2
9617 #define MPWD_H_COORD 3
9620 #define MULTI_PWD_NUM_BUTTONS 2
9621 #define MPWD_CANCEL 0
9622 #define MPWD_COMMIT 1
9624 // password area defs
9625 int Mpwd_coords[GR_NUM_RESOLUTIONS][4] = {
9638 UI_WINDOW Multi_pwd_window; // the window object for the join screen
9639 UI_INPUTBOX Multi_pwd_passwd; // for Netgame.passwd
9640 int Multi_pwd_bitmap; // the background bitmap
9641 int Multi_passwd_background = -1;
9642 int Multi_passwd_done = -1;
9643 int Multi_passwd_running = 0;
9646 ui_button_info Multi_pwd_buttons[GR_NUM_RESOLUTIONS][MULTI_PWD_NUM_BUTTONS] = {
9649 ui_button_info("PWB_00", 402, 134, -1, -1, 0),
9650 ui_button_info("PWB_01", 450, 134, -1, -1, 1),
9652 ui_button_info("PWB_00", 411, 151, 405, 141, 0),
9653 ui_button_info("PWB_01", 460, 151, 465, 141, 1),
9657 ui_button_info("2_PWB_00", 659, 242, 649, 225, 0),
9658 ui_button_info("2_PWB_01", 737, 242, 736, 225, 1),
9664 #define MULTI_PWD_NUM_TEXT 0
9666 #define MULTI_PWD_NUM_TEXT 3
9668 UI_XSTR Multi_pwd_text[GR_NUM_RESOLUTIONS][MULTI_PWD_NUM_TEXT] = {
9670 // not needed for FS1
9672 { "Cancel", 387, 400, 141, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[0][MPWD_CANCEL].button},
9673 { "Commit", 1062, 455, 141, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[0][MPWD_COMMIT].button},
9674 { "Enter Password", 1332, 149, 92, UI_XSTR_COLOR_GREEN, -1, NULL},
9678 // not needed for FS1
9680 { "Cancel", 387, 649, 225, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[1][MPWD_CANCEL].button},
9681 { "Commit", 1062, 736, 225, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[1][MPWD_COMMIT].button},
9682 { "Enter Password", 1332, 239, 148, UI_XSTR_COLOR_GREEN, -1, NULL},
9687 // initialize all graphics, etc
9688 void multi_passwd_init()
9692 // store the background as it currently is
9693 Multi_passwd_background = gr_save_screen();
9695 // create the interface window
9696 Multi_pwd_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
9697 Multi_pwd_window.set_mask_bmap(Multi_pwd_bitmap_mask_fname[gr_screen.res]);
9699 // load the background bitmap
9700 Multi_pwd_bitmap = bm_load(Multi_pwd_bitmap_fname[gr_screen.res]);
9701 if(Multi_pwd_bitmap < 0){
9702 // we failed to load the bitmap - this is very bad
9706 // create the interface buttons
9707 for(idx=0; idx<MULTI_PWD_NUM_BUTTONS; idx++){
9708 // create the object
9709 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);
9711 // set the sound to play when highlighted
9712 Multi_pwd_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
9714 // set the ani for the button
9715 Multi_pwd_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pwd_buttons[gr_screen.res][idx].filename);
9718 Multi_pwd_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pwd_buttons[gr_screen.res][idx].hotspot);
9722 for(idx=0; idx<MULTI_PWD_NUM_TEXT; idx++){
9723 Multi_pwd_window.add_XSTR(&Multi_pwd_text[gr_screen.res][idx]);
9726 // create the password input box
9727 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);
9728 Multi_pwd_passwd.set_focus();
9730 // link the enter key to ACCEPT
9731 Multi_pwd_buttons[gr_screen.res][MPWD_COMMIT].button.set_hotkey(KEY_ENTER);
9733 Multi_passwd_done = -1;
9734 Multi_passwd_running = 1;
9737 // close down all graphics, etc
9738 void multi_passwd_close()
9740 // unload any bitmaps
9741 bm_release(Multi_pwd_bitmap);
9743 // destroy the UI_WINDOW
9744 Multi_pwd_window.destroy();
9746 // free up the saved background screen
9747 if(Multi_passwd_background >= 0){
9748 gr_free_screen(Multi_passwd_background);
9749 Multi_passwd_background = -1;
9752 Multi_passwd_running = 0;
9755 // process any button pressed
9756 void multi_passwd_process_buttons()
9758 // if the accept button was pressed
9759 if(Multi_pwd_buttons[gr_screen.res][MPWD_COMMIT].button.pressed()){
9760 gamesnd_play_iface(SND_USER_SELECT);
9761 Multi_passwd_done = 1;
9764 // if the cancel button was pressed
9765 if(Multi_pwd_buttons[gr_screen.res][MPWD_CANCEL].button.pressed()){
9766 gamesnd_play_iface(SND_USER_SELECT);
9767 Multi_passwd_done = 0;
9771 // run the passwd popup
9772 void multi_passwd_do(char *passwd)
9776 while(Multi_passwd_done == -1){
9777 // set frametime and run background stuff
9778 game_set_frametime(-1);
9779 game_do_state_common(gameseq_get_state());
9781 k = Multi_pwd_window.process();
9783 // process any keypresses
9786 // set this to indicate the user has cancelled for one reason or another
9787 Multi_passwd_done = 0;
9791 // if the input box text has changed
9792 if(Multi_pwd_passwd.changed()){
9794 Multi_pwd_passwd.get_text(passwd);
9797 // process any button pressed
9798 multi_passwd_process_buttons();
9800 // draw the background, etc
9803 if(Multi_passwd_background >= 0){
9804 gr_restore_screen(Multi_passwd_background);
9806 gr_set_bitmap(Multi_pwd_bitmap);
9808 Multi_pwd_window.draw();
9815 // bring up the password string popup, fill in passwd (return 1 if accept was pressed, 0 if cancel was pressed)
9816 int multi_passwd_popup(char *passwd)
9818 // if the popup is already running for some reason, don't do anything
9819 if(Multi_passwd_running){
9823 // initialize all graphics
9824 multi_passwd_init();
9827 multi_passwd_do(passwd);
9829 // shut everything down
9830 multi_passwd_close();
9832 return Multi_passwd_done;