2 * $Logfile: /Freespace2/code/Network/MultiUI.cpp $
7 * C file for all the UI controls of the mulitiplayer screens
10 * Revision 1.4 2002/06/01 07:12:33 relnev
11 * a few NDEBUG updates.
13 * removed a few warnings.
15 * Revision 1.3 2002/05/26 20:49:54 theoddone33
18 * Revision 1.2 2002/05/07 03:16:47 theoddone33
19 * The Great Newline Fix
21 * Revision 1.1.1.1 2002/05/03 03:28:10 root
25 * 94 6/16/00 3:16p Jefff
26 * sim of the year dvd version changes, a few german soty localization
29 * 93 10/14/99 2:51p Jefff
32 * 92 10/13/99 3:50p Jefff
33 * fixed unnumbered XSTRs
35 * 91 9/15/99 1:45a Dave
36 * Don't init joystick on standalone. Fixed campaign mode on standalone.
37 * Fixed no-score-report problem in TvT
39 * 90 9/14/99 12:51a Jefff
42 * 89 9/13/99 4:52p Dave
45 * 88 9/13/99 11:30a Dave
46 * Added checkboxes and functionality for disabling PXO banners as well as
47 * disabling d3d zbuffer biasing.
49 * 87 9/12/99 10:06p Jefff
50 * changed instances of "Squad War" to "SquadWar"
52 * 86 9/03/99 1:32a Dave
53 * CD checking by act. Added support to play 2 cutscenes in a row
54 * seamlessly. Fixed super low level cfile bug related to files in the
55 * root directory of a CD. Added cheat code to set campaign mission # in
58 * 85 9/01/99 10:49p Dave
59 * Added nice SquadWar checkbox to the client join wait screen.
61 * 84 8/30/99 2:49p Jefff
63 * 83 8/26/99 8:49p Jefff
64 * Updated medals screen and about everything that ever touches medals in
65 * one way or another. Sheesh.
67 * 82 8/25/99 4:38p Dave
68 * Updated PXO stuff. Make squad war report stuff much more nicely.
70 * 81 8/20/99 2:09p Dave
73 * 80 8/20/99 10:06a Jefff
74 * removed closed/rstricted buttons from multi start screen
76 * 79 8/18/99 11:30a Jefff
78 * 78 8/18/99 10:38a Jefff
80 * 77 8/16/99 4:06p Dave
81 * Big honking checkin.
83 * 76 8/16/99 1:08p Jefff
84 * added sounds to a few controls, made input boxes lose focus on ENTER
86 * 75 8/16/99 9:52a Jefff
87 * fixed bitmap loading on buttons in multi-sync screen
89 * 74 8/11/99 5:54p Dave
90 * Fixed collision problem. Fixed standalone ghost problem.
92 * 73 8/10/99 4:35p Jefff
95 * 72 8/06/99 12:29a Dave
98 * 71 8/05/99 3:13p Jasenw
99 * tweaked some text placement coords.
101 * 70 8/04/99 1:38p Jefff
102 * moved some text in multi join wait
104 * 69 8/03/99 12:45p Dave
107 * 68 7/25/99 5:17p Jefff
108 * campaign descriptions show up on multicreate screen
110 * 67 7/20/99 1:49p Dave
111 * Peter Drake build. Fixed some release build warnings.
113 * 66 7/19/99 2:13p Dave
114 * Added some new strings for Heiko.
116 * 65 7/15/99 9:20a Andsager
117 * FS2_DEMO initial checkin
119 * 64 7/08/99 10:53a Dave
120 * New multiplayer interpolation scheme. Not 100% done yet, but still
121 * better than the old way.
123 * 63 6/30/99 10:49a Jasenw
124 * Fixed coords for new launch countdown ani
126 * 62 6/29/99 7:39p Dave
127 * Lots of small bug fixes.
129 * 61 6/25/99 11:59a Dave
130 * Multi options screen.
132 * 60 6/09/99 2:17p Dave
133 * Fixed up pleasewait bitmap rendering.
135 * 59 6/05/99 3:42p Dave
136 * New multi sync screen.
138 * 58 6/01/99 6:07p Dave
139 * New loading/pause/please wait bar.
141 * 57 5/21/99 6:45p Dave
142 * Sped up ui loading a bit. Sped up localization disk access stuff. Multi
143 * start game screen, multi password, and multi pxo-help screen.
145 * 56 5/06/99 11:10a Dave
146 * Fixed coord on multi create screen.
148 * 55 5/04/99 6:38p Dave
149 * Finished multi join-wait screen.
151 * 54 5/04/99 5:20p Dave
152 * Fixed up multiplayer join screen and host options screen. Should both
155 * 53 5/03/99 11:04p Dave
156 * Most of the way done with the multi join screen.
158 * 52 5/03/99 8:32p Dave
159 * New version of multi host options screen.
161 * 51 4/29/99 2:15p Neilk
162 * slider2 code got modified; changed parameters for create
164 * 50 4/25/99 3:02p Dave
165 * Build defines for the E3 build.
167 * 49 4/21/99 6:15p Dave
168 * Did some serious housecleaning in the beam code. Made it ready to go
169 * for anti-fighter "pulse" weapons. Fixed collision pair creation. Added
170 * a handy macro for recalculating collision pairs for a given object.
172 * 48 4/16/99 5:27p Neilk
173 * added slider support and hir res for multi_create
175 * 47 4/14/99 6:37p Dave
176 * Fixed scroll button bug on host create screen.
178 * 46 4/14/99 5:28p Dave
181 * 45 4/12/99 10:07p Dave
182 * Made network startup more forgiving. Added checkmarks to dogfight
183 * screen for players who hit commit.
185 * 44 4/09/99 2:21p Dave
186 * Multiplayer beta stuff. CD checking.
188 * 43 4/08/99 1:28p Dave
189 * Small bug fixes for refresh button on the multi create screen.
191 * 42 4/08/99 11:55a Neilk
192 * Converted Multi_Create to new artwork (just lowres)
194 * 41 4/08/99 2:10a Dave
195 * Numerous bug fixes for the beta. Added builtin mission info for the
198 * 40 3/20/99 3:48p Andsager
199 * Do mission_loop stuff for PXO
201 * 39 3/10/99 6:50p Dave
202 * Changed the way we buffer packets for all clients. Optimized turret
203 * fired packets. Did some weapon firing optimizations.
205 * 38 3/09/99 6:24p Dave
206 * More work on object update revamping. Identified several sources of
207 * unnecessary bandwidth.
209 * 37 3/08/99 7:03p Dave
210 * First run of new object update system. Looks very promising.
212 * 36 2/25/99 4:19p Dave
213 * Added multiplayer_beta defines. Added cd_check define. Fixed a few
214 * release build warnings. Added more data to the squad war request and
217 * 35 2/24/99 3:26p Anoop
218 * Make sure the host is the only guy who bashes skill level for TvT.
220 * 34 2/24/99 2:25p Dave
221 * Fixed up chatbox bugs. Made squad war reporting better. Fixed a respawn
222 * bug for dogfight more.
224 * 33 2/23/99 2:29p Dave
225 * First run of oldschool dogfight mode.
227 * 32 2/17/99 2:11p Dave
228 * First full run of squad war. All freespace and tracker side stuff
231 * 31 2/12/99 6:16p Dave
232 * Pre-mission Squad War code is 95% done.
234 * 30 2/11/99 3:08p Dave
235 * PXO refresh button. Very preliminary squad war support.
237 * 29 2/08/99 5:07p Dave
238 * FS2 chat server support. FS2 specific validated missions.
240 * 28 2/04/99 6:29p Dave
241 * First full working rev of FS2 PXO support. Fixed Glide lighting
244 * 27 1/30/99 5:08p Dave
245 * More new hi-res stuff.Support for nice D3D textures.
247 * 26 1/29/99 2:08a Dave
248 * Fixed beam weapon collisions with players. Reduced size of scoring
249 * struct for multiplayer. Disabled PXO.
251 * 25 1/15/99 2:36p Neilk
252 * fixed multi_jw coordinates
254 * 24 1/13/99 7:19p Neilk
255 * Converted Mission Brief, Barracks, Synch to high res support
257 * 23 1/12/99 7:17p Neilk
259 * 22 1/12/99 5:45p Dave
260 * Moved weapon pipeline in multiplayer to almost exclusively client side.
261 * Very good results. Bandwidth goes down, playability goes up for crappy
262 * connections. Fixed object update problem for ship subsystems.
264 * 21 1/12/99 4:07a Dave
265 * Put in barracks code support for selecting squad logos. Properly
266 * distribute squad logos in a multiplayer game.
268 * 20 1/11/99 7:19p Neilk
269 * Converted multi_join interface to support multiple resolutions
271 * 19 12/18/98 1:13a Dave
272 * Rough 1024x768 support for Direct3D. Proper detection and usage through
275 * 18 12/17/98 4:50p Andsager
276 * Added debrief_assemble_optional_mission_popup_text() for single and
279 * 17 12/14/98 12:13p Dave
280 * Spiffed up xfer system a bit. Put in support for squad logo file xfer.
283 * 16 12/10/98 10:19a Andsager
284 * Fix mission loop assert
286 * 15 12/10/98 9:59a Andsager
287 * Fix some bugs with mission loops
289 * 14 12/09/98 1:56p Andsager
290 * Initial checkin of mission loop
292 * 13 12/03/98 5:22p Dave
293 * Ported over Freespace 1 multiplayer ships.tbl and weapons.tbl
296 * 12 11/30/98 1:07p Dave
297 * 16 bit conversion, first run.
299 * 11 11/20/98 11:16a Dave
300 * Fixed up IPX support a bit. Making sure that switching modes and
301 * loading/saving pilot files maintains proper state.
303 * 10 11/19/98 4:57p Dave
304 * Ignore PXO option if IPX is selected.
306 * 9 11/19/98 4:19p Dave
307 * Put IPX sockets back in psnet. Consolidated all multiplayer config
310 * 8 11/19/98 8:04a Dave
311 * Full support for D3-style reliable sockets. Revamped packet lag/loss
312 * system, made it receiver side and at the lowest possible level.
314 * 7 11/17/98 11:12a Dave
315 * Removed player identification by address. Now assign explicit id #'s.
317 * 6 10/19/98 11:15a Dave
318 * Changed requirements for stats storing in PXO mode.
320 * 5 10/16/98 9:40a Andsager
321 * Remove ".h" files from model.h
323 * 4 10/13/98 9:29a Dave
324 * Started neatening up freespace.h. Many variables renamed and
325 * reorganized. Added AlphaColors.[h,cpp]
327 * 3 10/07/98 6:27p Dave
328 * Globalized mission and campaign file extensions. Removed Silent Threat
329 * special code. Moved \cache \players and \multidata into the \data
332 * 2 10/07/98 10:53a Dave
335 * 1 10/07/98 10:50a Dave
337 * 333 10/02/98 3:22p Allender
338 * fix up the -connect option and fix the -port option
340 * 332 9/17/98 9:26p Dave
341 * Externalized new string.
343 * 331 9/17/98 3:08p Dave
344 * PXO to non-pxo game warning popup. Player icon stuff in create and join
345 * game screens. Upped server count refresh time in PXO to 35 secs (from
348 * 330 9/17/98 9:43a Allender
349 * removed an Assert that Dave called bogus.
351 * 329 9/16/98 6:54p Dave
352 * Upped max sexpression nodes to 1800 (from 1600). Changed FRED to sort
353 * the ship list box. Added code so that tracker stats are not stored with
356 * 328 9/15/98 7:24p Dave
357 * Minor UI changes. Localized bunch of new text.
359 * 327 9/15/98 4:03p Dave
360 * Changed readyroom and multi screens to display "st" icon for all
361 * missions with mission disk content (not necessarily just those that
362 * come with Silent Threat).
364 * 326 9/15/98 11:44a Dave
365 * Renamed builtin ships and wepaons appropriately in FRED. Put in scoring
366 * scale factors. Fixed standalone filtering of MD missions to non-MD
369 * 325 9/13/98 9:36p Dave
370 * Support for new info icons for multiplayer missions (from-volition,
371 * valid, mission disk, etc).
373 * 324 9/11/98 4:14p Dave
374 * Fixed file checksumming of < file_size. Put in more verbose kicking and
375 * PXO stats store reporting.
377 * 323 9/10/98 1:17p Dave
378 * Put in code to flag missions and campaigns as being MD or not in Fred
379 * and Freespace. Put in multiplayer support for filtering out MD
380 * missions. Put in multiplayer popups for warning of non-valid missions.
382 * 322 9/04/98 3:51p Dave
383 * Put in validated mission updating and application during stats
386 * 321 8/31/98 2:06p Dave
387 * Make cfile sort the ordering or vp files. Added support/checks for
388 * recognizing "mission disk" players.
390 * 320 8/21/98 1:15p Dave
391 * Put in log system hooks in useful places.
393 * 319 8/20/98 5:31p Dave
394 * Put in handy multiplayer logfile system. Now need to put in useful
395 * applications of it all over the code.
397 * 318 8/12/98 4:53p Dave
398 * Put in 32 bit checksumming for PXO missions. No validation on the
399 * actual tracker yet, though.
401 * 317 8/07/98 10:40a Allender
402 * new command line flags for starting netgames. Only starting currently
403 * works, and PXO isn't implemented yet
405 * 316 7/24/98 9:27a Dave
406 * Tidied up endgame sequencing by removing several old flags and
407 * standardizing _all_ endgame stuff with a single function call.
409 * 315 7/14/98 10:04a Allender
410 * fixed the countdown code to not be reliant on timer_get_fixed_seconds
412 * 314 7/10/98 5:04p Dave
413 * Fix connection speed bug on standalone server.
415 * 313 7/09/98 6:01p Dave
416 * Firsts full version of PXO updater. Put in stub for displaying
419 * 312 7/07/98 2:49p Dave
422 * 311 6/30/98 2:17p Dave
423 * Revised object update system. Removed updates for all weapons. Put
424 * button info back into control info packet.
426 * 310 6/13/98 9:32p Mike
427 * Kill last character in file which caused "Find in Files" to report the
428 * file as "not a text file."
435 #include <winsock.h> // for inet_addr()
437 #include <sys/types.h>
438 #include <sys/socket.h>
439 #include <netinet/in.h>
440 #include <arpa/inet.h>
445 #include "multiutil.h"
446 #include "multimsgs.h"
452 #include "gamesequence.h"
453 #include "freespace.h"
454 #include "contexthelp.h"
459 #include "missionshipchoice.h"
460 #include "multi_xfer.h"
462 #include "stand_gui.h"
463 #include "linklist.h"
464 #include "multiteamselect.h"
465 #include "missioncampaign.h"
472 #include "missiondebrief.h"
473 #include "multi_ingame.h"
474 #include "multi_kick.h"
475 #include "multi_data.h"
476 #include "multi_campaign.h"
477 #include "multi_team.h"
478 #include "multi_pinfo.h"
479 #include "multi_observer.h"
480 #include "multi_voice.h"
481 #include "multi_endgame.h"
482 #include "managepilot.h"
485 #include "objcollide.h"
487 #include "multi_pmsg.h"
488 #include "multi_obj.h"
489 #include "multi_log.h"
490 #include "alphacolors.h"
491 #include "animplay.h"
492 #include "multi_dogfight.h"
493 #include "missionpause.h"
495 // -------------------------------------------------------------------------------------------------------------
497 // MULTIPLAYER COMMON interface controls
500 // the common text info box stuff. This is lifted almost directly from Alans briefing code (minus the spiffy colored, scrolling
502 int Multi_common_text_coords[GR_NUM_RESOLUTIONS][4] = {
511 int Multi_common_text_max_display[GR_NUM_RESOLUTIONS] = {
516 #define MULTI_COMMON_TEXT_META_CHAR '$'
517 #define MULTI_COMMON_TEXT_MAX_LINE_LENGTH 100
518 #define MULTI_COMMON_TEXT_MAX_LINES 20
519 #define MULTI_COMMON_MAX_TEXT (MULTI_COMMON_TEXT_MAX_LINES * MULTI_COMMON_TEXT_MAX_LINE_LENGTH)
521 char Multi_common_all_text[MULTI_COMMON_MAX_TEXT];
522 char Multi_common_text[MULTI_COMMON_TEXT_MAX_LINES][MULTI_COMMON_TEXT_MAX_LINE_LENGTH];
524 int Multi_common_top_text_line = -1; // where to start displaying from
525 int Multi_common_num_text_lines = 0; // how many lines we have
527 void multi_common_scroll_text_up();
528 void multi_common_scroll_text_down();
529 void multi_common_move_to_bottom();
530 void multi_common_render_text();
531 void multi_common_split_text();
533 #define MAX_IP_STRING 255 // maximum length for ip string
535 void multi_common_scroll_text_up()
537 Multi_common_top_text_line--;
538 if ( Multi_common_top_text_line < 0 ) {
539 Multi_common_top_text_line = 0;
540 if ( !mouse_down(MOUSE_LEFT_BUTTON) )
541 gamesnd_play_iface(SND_GENERAL_FAIL);
544 gamesnd_play_iface(SND_SCROLL);
548 void multi_common_scroll_text_down()
550 Multi_common_top_text_line++;
551 if ( (Multi_common_num_text_lines - Multi_common_top_text_line) < Multi_common_text_max_display[gr_screen.res] ) {
552 Multi_common_top_text_line--;
553 if ( !mouse_down(MOUSE_LEFT_BUTTON) ){
554 gamesnd_play_iface(SND_GENERAL_FAIL);
557 gamesnd_play_iface(SND_SCROLL);
561 void multi_common_move_to_bottom()
563 // if there's nowhere to scroll down, do nothing
564 if(Multi_common_num_text_lines <= Multi_common_text_max_display[gr_screen.res]){
568 Multi_common_top_text_line = Multi_common_num_text_lines - Multi_common_text_max_display[gr_screen.res];
571 void multi_common_set_text(char *str,int auto_scroll)
574 // store the entire string as well
575 if(strlen(str) > MULTI_COMMON_MAX_TEXT){
578 strcpy(Multi_common_all_text,str);
581 // split the whole thing up
582 multi_common_split_text();
584 // scroll to the bottom if we're supposed to
586 multi_common_move_to_bottom();
590 void multi_common_add_text(char *str,int auto_scroll)
593 // store the entire string as well
594 if((strlen(str) + strlen(Multi_common_all_text)) > MULTI_COMMON_MAX_TEXT){
597 strcat(Multi_common_all_text,str);
600 // split the whole thing up
601 multi_common_split_text();
603 // scroll to the bottom if we're supposed to
605 multi_common_move_to_bottom();
609 void multi_common_split_text()
612 int n_chars[MAX_BRIEF_LINES];
613 char *p_str[MAX_BRIEF_LINES];
615 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);
616 Assert(n_lines != -1);
618 for ( i = 0; i < n_lines; i++ ) {
619 Assert(n_chars[i] < MULTI_COMMON_TEXT_MAX_LINE_LENGTH);
620 strncpy(Multi_common_text[i], p_str[i], n_chars[i]);
621 Multi_common_text[i][n_chars[i]] = 0;
622 drop_leading_white_space(Multi_common_text[i]);
625 Multi_common_top_text_line = 0;
626 Multi_common_num_text_lines = n_lines;
629 void multi_common_render_text()
631 int i, fh, line_count;
633 fh = gr_get_font_height();
636 gr_set_color_fast(&Color_text_normal);
637 for ( i = Multi_common_top_text_line; i < Multi_common_num_text_lines; i++ ) {
638 if ( line_count >= Multi_common_text_max_display[gr_screen.res] ){
641 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]);
645 if ( (Multi_common_num_text_lines - Multi_common_top_text_line) > Multi_common_text_max_display[gr_screen.res] ) {
646 gr_set_color_fast(&Color_bright_red);
647 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));
651 // common notification messaging stuff
652 #define MULTI_COMMON_NOTIFY_TIME 3500
653 int Multi_common_join_y[GR_NUM_RESOLUTIONS] = {
657 int Multi_common_create_y[GR_NUM_RESOLUTIONS] = {
662 int Multi_common_jw_y[GR_NUM_RESOLUTIONS] = {
667 int Multi_common_msg_y[GR_NUM_RESOLUTIONS] = {
672 char Multi_common_notify_text[200];
673 int Multi_common_notify_stamp;
675 void multi_common_notify_init()
677 strcpy(Multi_common_notify_text,"");
678 Multi_common_notify_stamp = -1;
681 // add a notification string, drawing appropriately depending on the state/screen we're in
682 void multi_common_add_notify(char *str)
685 strcpy(Multi_common_notify_text,str);
686 Multi_common_notify_stamp = timestamp(MULTI_COMMON_NOTIFY_TIME);
690 // process/display notification messages
691 void multi_common_notify_do()
693 if(Multi_common_notify_stamp != -1){
694 if(timestamp_elapsed(Multi_common_notify_stamp)){
695 Multi_common_notify_stamp = -1;
698 gr_get_string_size(&w,&h,Multi_common_notify_text);
699 gr_set_color_fast(&Color_white);
701 // determine where it should be placed based upon which screen we're on
703 switch(gameseq_get_state()){
704 case GS_STATE_MULTI_JOIN_GAME :
705 y = Multi_common_join_y[gr_screen.res];
707 case GS_STATE_MULTI_HOST_SETUP :
708 y = Multi_common_create_y[gr_screen.res];
710 case GS_STATE_MULTI_CLIENT_SETUP :
711 y = Multi_common_jw_y[gr_screen.res];
713 case GS_STATE_MULTI_START_GAME :
714 y = Multi_common_msg_y[gr_screen.res];
718 gr_string((gr_screen.max_w - w)/2, y, Multi_common_notify_text);
725 int Multi_common_icons[MULTI_NUM_COMMON_ICONS];
727 char *Multi_common_icon_names[MULTI_NUM_COMMON_ICONS] = {
728 "DotRed", // voice denied
729 "DotGreen", // voice recording
730 "OvalGreen", // team 0
731 "OvalGreen01", // team 0 select
733 "OvalRed01", // team 1 select
734 "mp_coop", // coop mission
735 "mp_teams", // TvT mission
736 "mp_furball", // furball mission
737 "icon-volition", // volition mission
738 "icon-valid", // mission is valid
742 // width and height of the icons
743 int Multi_common_icon_dims[MULTI_NUM_COMMON_ICONS][2] = {
744 {11, 11}, // voice denied
745 {11, 11}, // voice recording
747 {11, 11}, // team 0 select
749 {11, 11}, // team 1 select
752 {18, 11}, // mp furball
753 {9, 9}, // volition mission
754 {8, 8}, // mission is valid
758 void multi_load_common_icons()
763 for(idx=0; idx<MULTI_NUM_COMMON_ICONS; idx++){
764 Multi_common_icons[idx] = -1;
765 Multi_common_icons[idx] = bm_load(Multi_common_icon_names[idx]);
769 void multi_unload_common_icons()
774 for(idx=0; idx<MULTI_NUM_COMMON_ICONS; idx++){
775 if(Multi_common_icons[idx] != -1){
776 bm_unload(Multi_common_icons[idx]);
777 Multi_common_icons[idx] = -1;
782 // display any relevant voice status icons
783 void multi_common_voice_display_status()
785 switch(multi_voice_status()){
786 // i have been denied the voice token
787 case MULTI_VOICE_STATUS_DENIED:
788 if(Multi_common_icons[MICON_VOICE_DENIED] != -1){
789 gr_set_bitmap(Multi_common_icons[MICON_VOICE_DENIED]);
794 // i am currently recording
795 case MULTI_VOICE_STATUS_RECORDING:
796 if(Multi_common_icons[MICON_VOICE_RECORDING] != -1){
797 gr_set_bitmap(Multi_common_icons[MICON_VOICE_RECORDING]);
802 // i am currently playing back sound
803 case MULTI_VOICE_STATUS_PLAYING:
806 // the system is currently idle
807 case MULTI_VOICE_STATUS_IDLE:
813 // palette initialization stuff
814 #define MULTI_COMMON_PALETTE_FNAME "InterfacePalette"
817 int Multi_common_interface_palette = -1;
819 void multi_common_load_palette();
820 void multi_common_set_palette();
821 void multi_common_unload_palette();
823 // load in the palette if it doesn't already exist
824 void multi_common_load_palette()
826 if(Multi_common_interface_palette != -1){
830 Multi_common_interface_palette = bm_load(MULTI_COMMON_PALETTE_FNAME);
831 if(Multi_common_interface_palette == -1){
832 nprintf(("Network","Error loading multiplayer common palette!\n"));
836 // set the common palette to be the active one
837 void multi_common_set_palette()
839 // if the palette is not loaded yet, do so now
840 if(Multi_common_interface_palette == -1){
841 multi_common_load_palette();
844 if(Multi_common_interface_palette != -1){
845 #ifndef HARDWARE_ONLY
846 palette_use_bm_palette(Multi_common_interface_palette);
851 // unload the bitmap palette
852 void multi_common_unload_palette()
854 if(Multi_common_interface_palette != -1){
855 bm_unload(Multi_common_interface_palette);
856 Multi_common_interface_palette = -1;
860 void multi_common_verify_cd()
863 // otherwise, call the freespace function to determine if we have a cd
865 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) ){
874 // -------------------------------------------------------------------------------------------------------------
876 // MULTIPLAYER JOIN SCREEN
879 #define MULTI_JOIN_NUM_BUTTONS 11
883 #define MULTI_JOIN_PALETTE "InterfacePalette"
885 static char *Multi_join_bitmap_fname[GR_NUM_RESOLUTIONS] = {
886 "MultiJoin", // GR_640
887 "2_MultiJoin" // GR_1024
890 static char *Multi_join_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
891 "MultiJoin-M", // GR_640
892 "2_MultiJoin-M" // GR_1024
897 char *Mj_slider_name[GR_NUM_RESOLUTIONS] = {
901 int Mj_slider_coords[GR_NUM_RESOLUTIONS][4] = {
911 #define MJ_SCROLL_UP 0
912 #define MJ_SCROLL_DOWN 1
914 #define MJ_SCROLL_INFO_UP 3
915 #define MJ_SCROLL_INFO_DOWN 4
916 #define MJ_JOIN_OBSERVER 5
917 #define MJ_START_GAME 6
923 // uses MULTI_JOIN_REFRESH_TIME as its timestamp
924 int Multi_join_glr_stamp;
926 #define MULTI_JOIN_PING_TIME 15000 // how often we ping all the known servers
927 int Multi_join_ping_stamp;
928 UI_WINDOW Multi_join_window; // the window object for the join screen
929 UI_BUTTON Multi_join_select_button; // for selecting list items
930 UI_SLIDER2 Multi_join_slider; // handy dandy slider
931 int Multi_join_bitmap; // the background bitmap
933 ui_button_info Multi_join_buttons[GR_NUM_RESOLUTIONS][MULTI_JOIN_NUM_BUTTONS] = {
935 ui_button_info( "MJ_00", 1, 57, -1, -1, 0 ), // scroll up
936 ui_button_info( "MJ_02", 1, 297, -1, -1, 2 ), // scroll down
937 ui_button_info( "MJ_03", 10, 338, 65, 364, 3 ), // refresh
938 ui_button_info( "MJ_04", 1, 405, -1, -1, 4 ), // scroll info up
939 ui_button_info( "MJ_05", 1, 446, -1, -1, 5 ), // scroll info down
940 ui_button_info( "MJ_06", 489, 339, -1, -1, 6 ), // join as observer
941 ui_button_info( "MJ_07", 538, 339, -1, -1, 7 ), // create game
942 ui_button_info( "MJ_08", 583, 339, 588, 376, 8 ), // cancel
943 ui_button_info( "MJ_09", 534, 426, -1, -1, 9 ), // help
944 ui_button_info( "MJ_10", 534, 454, -1, -1, 10 ), // options
945 ui_button_info( "MJ_11", 571, 426, 589, 416, 11 ), // join
948 ui_button_info( "2_MJ_00", 2, 92, -1, -1, 0 ), // scroll up
949 ui_button_info( "2_MJ_02", 2, 475, -1, -1, 2 ), // scroll down
950 ui_button_info( "2_MJ_03", 16, 541, 104, 582, 3 ), // refresh
951 ui_button_info( "2_MJ_04", 2, 648, -1, -1, 4 ), // scroll info up
952 ui_button_info( "2_MJ_05", 2, 713, -1, -1, 5 ), // scroll info down
953 ui_button_info( "2_MJ_06", 783, 542, -1, -1, 6 ), // join as observer
954 ui_button_info( "2_MJ_07", 861, 542, -1, -1, 7 ), // create game
955 ui_button_info( "2_MJ_08", 933, 542, 588, 376, 8 ), // cancel
956 ui_button_info( "2_MJ_09", 854, 681, -1, -1, 9 ), // help
957 ui_button_info( "2_MJ_10", 854, 727, -1, -1, 10 ), // options
958 ui_button_info( "2_MJ_11", 914, 681, 937, 668, 11 ), // join
962 #define MULTI_JOIN_NUM_TEXT 13
964 UI_XSTR Multi_join_text[GR_NUM_RESOLUTIONS][MULTI_JOIN_NUM_TEXT] = {
966 {"Refresh", 1299, 65, 364, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_REFRESH].button},
967 {"Join as", 1300, 476, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_JOIN_OBSERVER].button},
968 {"Observer", 1301, 467, 385, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_JOIN_OBSERVER].button},
969 {"Create", 1408, 535, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_START_GAME].button},
970 {"Game", 1302, 541, 385, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_START_GAME].button},
971 {"Cancel", 387, 588, 376, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[0][MJ_CANCEL].button},
972 {"Help", 928, 479, 436, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_HELP].button},
973 {"Options", 1036, 479, 460, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_OPTIONS].button},
974 {"Join", 1303, 589, 416, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[0][MJ_ACCEPT].button},
975 {"Status", 1304, 37, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
976 {"Server", 1305, 116, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
977 {"Players", 1306, 471, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
978 {"Ping", 1307, 555, 37, UI_XSTR_COLOR_GREEN, -1, NULL}
981 {"Refresh", 1299, 104, 582, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_REFRESH].button},
982 {"Join as", 1300, 783, 602, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_JOIN_OBSERVER].button},
983 {"Observer", 1301, 774, 611, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_JOIN_OBSERVER].button},
984 {"Create", 1408, 868, 602, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_START_GAME].button},
985 {"Game", 1302, 872, 611, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_START_GAME].button},
986 {"Cancel", 387, 941, 602, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[1][MJ_CANCEL].button},
987 {"Help", 928, 782, 699, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_HELP].button},
988 {"Options", 1036, 782, 736, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_OPTIONS].button},
989 {"Join", 1303, 937, 668, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[1][MJ_ACCEPT].button},
990 {"Status", 1304, 60, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
991 {"Server", 1305, 186, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
992 {"Players", 1306, 753, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
993 {"Ping", 1307, 888, 60, UI_XSTR_COLOR_GREEN, -1, NULL}
997 // constants for coordinate look ups
1000 #define MJ_W_COORD 2
1001 #define MJ_H_COORD 3
1003 #define MULTI_JOIN_SENT_WAIT 10000 // wait this long since a join was sent to allow another
1004 int Multi_join_sent_stamp;
1006 // game information text areas
1007 int Mj_max_game_items[GR_NUM_RESOLUTIONS] = {
1012 int Mj_list_y[GR_NUM_RESOLUTIONS] = {
1017 int Mj_status_coords[GR_NUM_RESOLUTIONS][4] = {
1026 int Mj_game_icon_coords[GR_NUM_RESOLUTIONS][3] = {
1035 int Mj_speed_coords[GR_NUM_RESOLUTIONS][4] = {
1044 int Mj_game_name_coords[GR_NUM_RESOLUTIONS][4] = {
1053 int Mj_players_coords[GR_NUM_RESOLUTIONS][4] = {
1062 int Mj_ping_coords[GR_NUM_RESOLUTIONS][4] = {
1071 // game speed labels
1072 #define MJ_NUM_SPEED_LABELS 5
1073 char *Multi_join_speed_labels[MJ_NUM_SPEED_LABELS] = {
1080 color *Multi_join_speed_colors[MJ_NUM_SPEED_LABELS] = {
1083 &Color_bright_green,
1084 &Color_bright_green,
1088 int Mj_cd_coords[GR_NUM_RESOLUTIONS] = {
1093 #define IP_CONFIG_FNAME "tcp.cfg" // name of the file which contains known TCP addresses
1095 // extents of the entire boundable game info region
1096 // NOTE : these numbers are completely empirical
1097 #define MJ_PING_GREEN 160
1098 #define MJ_PING_YELLOW 300
1100 int Mj_list_area_coords[GR_NUM_RESOLUTIONS][4] = {
1109 // PXO channel filter
1110 #define MJ_PXO_FILTER_Y 0
1112 // special chars to indicate various status modes for servers
1113 #define MJ_CHAR_STANDALONE "*"
1114 #define MJ_CHAR_CAMPAIGN "c"
1117 // various interface indices
1118 int Multi_join_list_start; // where to start displaying from
1119 active_game *Multi_join_list_start_item; // a pointer to the corresponding active_game
1120 int Multi_join_list_selected; // which item we have selected
1121 active_game *Multi_join_selected_item; // a pointer to the corresponding active_game
1123 // use this macro to modify the list start
1124 #define MJ_LIST_START_INC() do { Multi_join_list_start++; } while(0);
1125 #define MJ_LIST_START_DEC() do { Multi_join_list_start--; } while(0);
1126 #define MJ_LIST_START_SET(vl) do { Multi_join_list_start = vl; } while(0);
1128 // if we should be sending a join request at the end of the frame
1129 int Multi_join_should_send = -1;
1131 // master tracker details
1132 int Multi_join_frame_count; // keep a count of frames displayed
1133 int Multi_join_mt_tried_verify; // already tried verifying the pilot with the tracker
1135 // data stuff for auto joining a game
1136 #define MULTI_AUTOJOIN_JOIN_STAMP 2000
1137 #define MULTI_AUTOJOIN_QUERY_STAMP 2000
1139 int Multi_did_autojoin;
1140 net_addr Multi_autojoin_addr;
1141 int Multi_autojoin_join_stamp;
1142 int Multi_autojoin_query_stamp;
1145 join_request Multi_join_request;
1147 // LOCAL function definitions
1148 void multi_join_check_buttons();
1149 void multi_join_button_pressed(int n);
1150 void multi_join_display_games();
1151 void multi_join_blit_game_status(active_game *game, int y);
1152 void multi_join_load_tcp_addrs();
1153 void multi_join_do_netstuff();
1154 void multi_join_ping_all();
1155 void multi_join_process_select();
1156 void multi_join_list_scroll_up();
1157 void multi_join_list_scroll_down();
1158 void multi_join_list_page_up();
1159 void multi_join_list_page_down();
1160 active_game *multi_join_get_game(int n);
1161 void multi_join_cull_timeouts();
1162 void multi_join_handle_item_cull(active_game *item, int item_index);
1163 void multi_join_send_join_request(int as_observer);
1164 void multi_join_create_game();
1165 void multi_join_blit_top_stuff();
1166 int multi_join_maybe_warn();
1167 int multi_join_warn_pxo();
1168 void multi_join_blit_protocol();
1172 active_game ag, *newitem;;
1175 dc_get_arg(ARG_INT);
1176 for(idx=0; idx<Dc_arg_int; idx++){
1177 // stuff some fake info
1178 memset(&ag, 0, sizeof(active_game));
1179 sprintf(ag.name, "Game %d", idx);
1180 ag.version = MULTI_FS_SERVER_VERSION;
1181 ag.comp_version = MULTI_FS_SERVER_VERSION;
1182 ag.server_addr.addr[0] = (char)idx;
1183 ag.flags = (AG_FLAG_COOP | AG_FLAG_FORMING | AG_FLAG_STANDALONE);
1186 newitem = multi_update_active_games(&ag);
1188 // timestamp it so we get random timeouts
1189 if(newitem != NULL){
1190 // newitem->heard_from_timer = timestamp((int)frand_range(500.0f, 10000.0f));
1195 void multi_join_notify_new_game()
1197 // reset the # of items
1198 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);
1199 Multi_join_slider.force_currentItem(Multi_join_list_start);
1202 int multi_join_autojoin_do()
1204 // if we have an active game on the list, then return a positive value so that we
1205 // can join the game
1206 if ( Active_game_head && (Active_game_count > 0) ) {
1207 Multi_join_selected_item = Active_game_head;
1211 // send out a server_query again
1212 if ( timestamp_elapsed(Multi_autojoin_query_stamp) ) {
1213 send_server_query(&Multi_autojoin_addr);
1214 Multi_autojoin_query_stamp = timestamp(MULTI_AUTOJOIN_QUERY_STAMP);
1220 void multi_join_game_init()
1224 // do the multiplayer init stuff - multi_level_init() now does all net_player zeroing.
1225 // setup various multiplayer things
1226 Assert( Game_mode & GM_MULTIPLAYER );
1227 Assert( Net_player != NULL );
1229 switch (Multi_options_g.protocol) {
1231 ADDRESS_LENGTH = IPX_ADDRESS_LENGTH;
1232 PORT_LENGTH = IPX_PORT_LENGTH;
1236 ADDRESS_LENGTH = IP_ADDRESS_LENGTH;
1237 PORT_LENGTH = IP_PORT_LENGTH;
1246 memset( &Netgame, 0, sizeof(Netgame) );
1249 Net_player->flags |= NETINFO_FLAG_DO_NETWORKING;
1250 Net_player->player = Player;
1251 memcpy(&Net_player->p_info.addr,&Psnet_my_addr,sizeof(net_addr));
1253 // check for the existence of a CD
1254 multi_common_verify_cd();
1256 // load my local netplayer options
1257 multi_options_local_load(&Net_player->p_info.options, Net_player);
1262 // common_set_interface_palette(MULTI_JOIN_PALETTE);
1264 // destroy any chatbox contents which previously existed (from another game)
1267 // create the interface window
1268 Multi_join_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
1269 Multi_join_window.set_mask_bmap(Multi_join_bitmap_mask_fname[gr_screen.res]);
1271 // load the background bitmap
1272 Multi_join_bitmap = bm_load(Multi_join_bitmap_fname[gr_screen.res]);
1273 if(Multi_join_bitmap < 0){
1274 // we failed to load the bitmap - this is very bad
1278 // intialize the endgame system
1279 multi_endgame_init();
1281 // initialize the common notification messaging
1282 multi_common_notify_init();
1284 // initialize the common text area
1285 multi_common_set_text("");
1287 // load and use the common interface palette
1288 multi_common_load_palette();
1289 multi_common_set_palette();
1291 // load the help overlay
1292 help_overlay_load(MULTI_JOIN_OVERLAY);
1293 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1295 // do TCP and VMT specific initialization
1296 if(Multi_options_g.protocol == NET_TCP){
1297 // if this is a TCP (non tracker) game, we'll load up our default address list right now
1298 multi_join_load_tcp_addrs();
1301 // initialize any and all timestamps
1302 Multi_join_glr_stamp = -1;
1303 Multi_join_ping_stamp = -1;
1304 Multi_join_sent_stamp = -1;
1306 // reset frame count
1307 Multi_join_frame_count = 0;
1309 // haven't tried to verify on the tracker yet.
1310 Multi_join_mt_tried_verify = 0;
1312 // clear our all game lists to save hassles
1313 multi_join_clear_game_list();
1315 // create the interface buttons
1316 for(idx=0; idx<MULTI_JOIN_NUM_BUTTONS; idx++){
1317 // create the object
1318 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);
1320 // set the sound to play when highlighted
1321 Multi_join_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
1323 // set the ani for the button
1324 Multi_join_buttons[gr_screen.res][idx].button.set_bmaps(Multi_join_buttons[gr_screen.res][idx].filename);
1327 Multi_join_buttons[gr_screen.res][idx].button.link_hotspot(Multi_join_buttons[gr_screen.res][idx].hotspot);
1331 for(idx=0; idx<MULTI_JOIN_NUM_TEXT; idx++){
1332 Multi_join_window.add_XSTR(&Multi_join_text[gr_screen.res][idx]);
1335 Multi_join_should_send = -1;
1337 // close any previously open chatbox
1340 // create the list item select button
1341 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);
1342 Multi_join_select_button.hide();
1345 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);
1347 // if starting a network game, then go to the create game screen
1348 if ( Cmdline_start_netgame ) {
1349 multi_join_create_game();
1350 } else if ( Cmdline_connect_addr != NULL ) {
1355 // joining a game. Send a join request to the given IP address, and wait for the return.
1356 memset( &Multi_autojoin_addr, 0, sizeof(net_addr) );
1357 Multi_autojoin_addr.type = NET_TCP;
1359 // create the address, looking out for port number at the end
1360 port_num = DEFAULT_GAME_PORT;
1361 p = strrchr(Cmdline_connect_addr, ':');
1365 port_num = (short)atoi(p);
1367 ip_addr = inet_addr(Cmdline_connect_addr);
1368 memcpy(Multi_autojoin_addr.addr, &ip_addr, 4);
1369 Multi_autojoin_addr.port = port_num;
1371 send_server_query(&Multi_autojoin_addr);
1372 Multi_autojoin_query_stamp = timestamp(MULTI_AUTOJOIN_QUERY_STAMP);
1373 Multi_did_autojoin = 0;
1377 void multi_join_clear_game_list()
1380 Multi_join_list_selected = -1;
1381 Multi_join_selected_item = NULL;
1382 MJ_LIST_START_SET(-1);
1383 Multi_join_list_start_item = NULL;
1385 // free up the active game list
1386 multi_free_active_games();
1388 // initialize the active game list
1389 Active_game_head = NULL;
1390 Active_game_count = 0;
1393 void multi_join_game_do_frame()
1395 // check the status of our reliable socket. If not valid, popup error and return to main menu
1396 // I put this code here to avoid nasty gameseq issues with states. Also, we will have nice
1397 // background for the popup
1398 if ( !psnet_rel_check() ) {
1399 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));
1400 gameseq_post_event(GS_EVENT_MAIN_MENU);
1404 // return here since we will be moving to the next stage anyway -- I don't want to see the backgrounds of
1405 // all the screens for < 1 second for every screen we automatically move to.
1406 if ( Cmdline_start_netgame ) {
1410 // when joining a network game, wait for the server query to come back, and then join the game
1411 if ( Cmdline_connect_addr != NULL ) {
1414 if ( !Multi_did_autojoin ) {
1415 rval = popup_till_condition(multi_join_autojoin_do, XSTR("&Cancel", 779), XSTR("Joining netgame", 1500) );
1417 // cancel was hit. Send the user back to the main hall
1418 gameseq_post_event(GS_EVENT_MAIN_MENU);
1419 Cmdline_connect_addr = NULL; // reset this value.
1422 // when we get here, we have the data -- join the game.
1423 multi_join_send_join_request(0);
1424 Multi_autojoin_join_stamp = timestamp(MULTI_AUTOJOIN_JOIN_STAMP);
1425 Multi_did_autojoin = 1;
1428 if ( timestamp_elapsed(Multi_autojoin_join_stamp) ) {
1429 multi_join_send_join_request(0);
1430 Multi_autojoin_join_stamp = timestamp(MULTI_AUTOJOIN_JOIN_STAMP);
1436 // reset the should send var
1437 Multi_join_should_send = -1;
1439 int k = Multi_join_window.process();
1441 // process any keypresses
1444 if(help_overlay_active(MULTI_JOIN_OVERLAY)){
1445 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1447 gameseq_post_event(GS_EVENT_MAIN_MENU);
1448 gamesnd_play_iface(SND_USER_SELECT);
1452 // page up the game list
1454 multi_join_list_page_up();
1455 Multi_join_slider.force_currentItem(Multi_join_list_start);
1459 multi_pinfo_popup(Net_player);
1462 // page down the game list
1464 multi_join_list_page_down();
1465 Multi_join_slider.force_currentItem(Multi_join_list_start);
1468 // send out a ping-all
1470 multi_join_ping_all();
1471 Multi_join_ping_stamp = timestamp(MULTI_JOIN_PING_TIME);
1474 // shortcut to start a game
1476 multi_join_create_game();
1479 // scroll the game list up
1481 multi_join_list_scroll_up();
1482 Multi_join_slider.force_currentItem(Multi_join_list_start);
1485 // scroll the game list down
1487 multi_join_list_scroll_down();
1488 Multi_join_slider.force_currentItem(Multi_join_list_start);
1492 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
1493 help_overlay_set_state(MULTI_JOIN_OVERLAY, 0);
1496 // do any network related stuff
1497 multi_join_do_netstuff();
1499 // process any button clicks
1500 multi_join_check_buttons();
1502 // process any list selection stuff
1503 multi_join_process_select();
1505 // draw the background, etc
1507 GR_MAYBE_CLEAR_RES(Multi_join_bitmap);
1508 if(Multi_join_bitmap != -1){
1509 gr_set_bitmap(Multi_join_bitmap);
1512 Multi_join_window.draw();
1514 // display the active games
1515 multi_join_display_games();
1517 // display any text in the info area
1518 multi_common_render_text();
1520 // display any pending notification messages
1521 multi_common_notify_do();
1523 // blit the CD icon and any PXO filter stuff
1524 multi_join_blit_top_stuff();
1526 // draw the help overlay
1527 help_overlay_maybe_blit(MULTI_JOIN_OVERLAY);
1532 // if we are supposed to be sending a join request
1533 if(Multi_join_should_send != -1){
1534 multi_join_send_join_request(Multi_join_should_send);
1536 Multi_join_should_send = -1;
1538 // increment the frame count
1539 Multi_join_frame_count++;
1542 void multi_join_game_close()
1544 // unload any bitmaps
1545 if(!bm_unload(Multi_join_bitmap)){
1546 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_join_bitmap_fname[gr_screen.res]));
1549 // unload the help overlay
1550 help_overlay_unload(MULTI_JOIN_OVERLAY);
1552 // free up the active game list
1553 multi_free_active_games();
1555 // destroy the UI_WINDOW
1556 Multi_join_window.destroy();
1559 void multi_join_check_buttons()
1562 for(idx=0;idx<MULTI_JOIN_NUM_BUTTONS;idx++){
1563 // we only really need to check for one button pressed at a time, so we can break after
1565 if(Multi_join_buttons[gr_screen.res][idx].button.pressed()){
1566 multi_join_button_pressed(idx);
1572 void multi_join_button_pressed(int n)
1576 // if we're player PXO, go back there
1577 gameseq_post_event(GS_EVENT_MAIN_MENU);
1578 gamesnd_play_iface(SND_USER_SELECT);
1581 if(Active_game_count <= 0){
1582 multi_common_add_notify(XSTR("No games found!",757));
1583 gamesnd_play_iface(SND_GENERAL_FAIL);
1584 } else if(Multi_join_list_selected == -1){
1585 multi_common_add_notify(XSTR("No game selected!",758));
1586 gamesnd_play_iface(SND_GENERAL_FAIL);
1587 } else if((Multi_join_sent_stamp != -1) && !timestamp_elapsed(Multi_join_sent_stamp)){
1588 multi_common_add_notify(XSTR("Still waiting on previous join request!",759));
1589 gamesnd_play_iface(SND_GENERAL_FAIL);
1591 // otherwise, if he's already played PXO games, warn him
1593 if(Player->flags & PLAYER_FLAGS_HAS_PLAYED_PXO){
1594 if(!multi_join_warn_pxo()){
1600 // send the join request here
1601 Assert(Multi_join_selected_item != NULL);
1603 // send a join request packet
1604 Multi_join_should_send = 0;
1606 gamesnd_play_iface(SND_COMMIT_PRESSED);
1612 if(!help_overlay_active(MULTI_JOIN_OVERLAY)){
1613 help_overlay_set_state(MULTI_JOIN_OVERLAY,1);
1615 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1619 // scroll the game list up
1621 multi_join_list_scroll_up();
1622 Multi_join_slider.force_currentItem(Multi_join_list_start);
1625 // scroll the game list down
1626 case MJ_SCROLL_DOWN:
1627 multi_join_list_scroll_down();
1628 Multi_join_slider.force_currentItem(Multi_join_list_start);
1631 // scroll the info text box up
1632 case MJ_SCROLL_INFO_UP:
1633 multi_common_scroll_text_up();
1636 // scroll the info text box down
1637 case MJ_SCROLL_INFO_DOWN:
1638 multi_common_scroll_text_down();
1641 // go to the options screen
1643 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
1646 // go to the start game screen
1648 multi_join_create_game();
1651 // refresh the game/server list
1653 gamesnd_play_iface(SND_USER_SELECT);
1654 broadcast_game_query();
1657 // join a game as an observer
1658 case MJ_JOIN_OBSERVER:
1659 if(Active_game_count <= 0){
1660 multi_common_add_notify(XSTR("No games found!",757));
1661 gamesnd_play_iface(SND_GENERAL_FAIL);
1662 } else if(Multi_join_list_selected == -1){
1663 multi_common_add_notify(XSTR("No game selected!",758));
1664 gamesnd_play_iface(SND_GENERAL_FAIL);
1665 } else if((Multi_join_sent_stamp != -1) && !timestamp_elapsed(Multi_join_sent_stamp)){
1666 multi_common_add_notify(XSTR("Still waiting on previous join request!",759));
1667 gamesnd_play_iface(SND_GENERAL_FAIL);
1669 // send the join request here
1670 Assert(Multi_join_selected_item != NULL);
1672 Multi_join_should_send = 1;
1674 gamesnd_play_iface(SND_COMMIT_PRESSED);
1679 multi_common_add_notify(XSTR("Not implemented yet!",760));
1680 gamesnd_play_iface(SND_GENERAL_FAIL);
1685 // display all relevant info for active games
1686 void multi_join_display_games()
1688 active_game *moveup = Multi_join_list_start_item;
1692 int y_start = Mj_list_y[gr_screen.res];
1697 // blit the game status (including text and type icon)
1698 multi_join_blit_game_status(moveup,y_start);
1700 // get the connection type
1701 con_type = (moveup->flags & AG_FLAG_CONNECTION_SPEED_MASK) >> AG_FLAG_CONNECTION_BIT;
1702 if((con_type > 4) || (con_type < 0)){
1706 // display the connection speed
1708 strcpy(str, Multi_join_speed_labels[con_type]);
1709 gr_set_color_fast(Multi_join_speed_colors[con_type]);
1710 gr_string(Mj_speed_coords[gr_screen.res][MJ_X_COORD], y_start, str);
1712 // we'll want to have different colors for highlighted items, etc.
1713 if(moveup == Multi_join_selected_item){
1714 gr_set_color_fast(&Color_text_selected);
1716 gr_set_color_fast(&Color_text_normal);
1719 // display the game name, adding appropriate status chars
1721 if(moveup->flags & AG_FLAG_STANDALONE){
1722 strcat(str,MJ_CHAR_STANDALONE);
1724 if(moveup->flags & AG_FLAG_CAMPAIGN){
1725 strcat(str,MJ_CHAR_CAMPAIGN);
1728 // tack on the actual server name
1730 strcat(str,moveup->name);
1731 if(strlen(moveup->mission_name) > 0){
1733 strcat(str,moveup->mission_name);
1736 // make sure the string fits in the display area and draw it
1737 gr_force_fit_string(str,200,Mj_game_name_coords[gr_screen.res][MJ_W_COORD]);
1738 gr_string(Mj_game_name_coords[gr_screen.res][MJ_X_COORD],y_start,str);
1740 // display the ping time
1741 if(moveup->ping.ping_avg > 0){
1742 if(moveup->ping.ping_avg > 1000){
1743 gr_set_color_fast(&Color_bright_red);
1744 strcpy(str,XSTR("> 1 sec",761));
1746 // set the appropriate ping time color indicator
1747 if(moveup->ping.ping_avg > MJ_PING_YELLOW){
1748 gr_set_color_fast(&Color_bright_red);
1749 } else if(moveup->ping.ping_avg > MJ_PING_YELLOW){
1750 gr_set_color_fast(&Color_bright_yellow);
1752 gr_set_color_fast(&Color_bright_green);
1755 sprintf(str,"%d",moveup->ping.ping_avg);
1756 strcat(str,XSTR(" ms",762)); // [[ Milliseconds ]]
1759 gr_string(Mj_ping_coords[gr_screen.res][MJ_X_COORD],y_start,str);
1762 // display the number of players (be sure to center it)
1763 if(moveup == Multi_join_selected_item){
1764 gr_set_color_fast(&Color_text_selected);
1766 gr_set_color_fast(&Color_text_normal);
1768 sprintf(str,"%d",moveup->num_players);
1769 gr_get_string_size(&w,&h,str);
1770 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);
1774 moveup = moveup->next;
1775 } while((moveup != Active_game_head) && (count < Mj_max_game_items[gr_screen.res]));
1777 // if there are no items on the list, display this info
1779 gr_set_color_fast(&Color_bright);
1780 gr_string(Mj_game_name_coords[gr_screen.res][MJ_X_COORD] - 30,y_start,XSTR("<No game servers found>",763));
1784 void multi_join_blit_game_status(active_game *game, int y)
1787 char status_text[25];
1789 // blit the proper icon
1791 switch( game->flags & AG_FLAG_TYPE_MASK ){
1794 if(Multi_common_icons[MICON_COOP] != -1){
1795 gr_set_bitmap(Multi_common_icons[MICON_COOP]);
1800 // team vs. team game
1802 if(Multi_common_icons[MICON_TVT] != -1){
1803 gr_set_bitmap(Multi_common_icons[MICON_TVT]);
1809 case AG_FLAG_DOGFIGHT:
1810 if(Multi_common_icons[MICON_DOGFIGHT] != -1){
1811 gr_set_bitmap(Multi_common_icons[MICON_DOGFIGHT]);
1816 // if we're supposed to draw a bitmap
1818 gr_bitmap(Mj_game_icon_coords[gr_screen.res][MJ_X_COORD],y-1);
1821 // blit the proper status text
1822 memset(status_text,0,25);
1824 switch( game->flags & AG_FLAG_STATE_MASK ){
1825 case AG_FLAG_FORMING:
1826 gr_set_color_fast(&Color_bright_green);
1827 strcpy(status_text,XSTR("Forming",764));
1829 case AG_FLAG_BRIEFING:
1830 gr_set_color_fast(&Color_bright_red);
1831 strcpy(status_text,XSTR("Briefing",765));
1833 case AG_FLAG_DEBRIEF:
1834 gr_set_color_fast(&Color_bright_red);
1835 strcpy(status_text,XSTR("Debrief",766));
1838 gr_set_color_fast(&Color_bright_red);
1839 strcpy(status_text,XSTR("Paused",767));
1841 case AG_FLAG_IN_MISSION:
1842 gr_set_color_fast(&Color_bright_red);
1843 strcpy(status_text,XSTR("Playing",768));
1846 gr_set_color_fast(&Color_bright);
1847 strcpy(status_text,XSTR("Unknown",769));
1850 gr_get_string_size(&str_w,NULL,status_text);
1851 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);
1854 // load in a list of active games from our tcp.cfg file
1855 void multi_join_load_tcp_addrs()
1857 char line[MAX_IP_STRING];
1862 // attempt to open the ip list file
1863 file = cfopen(IP_CONFIG_FNAME,"rt",CFILE_NORMAL,CF_TYPE_DATA);
1865 nprintf(("Network","Error loading tcp.cfg file!\n"));
1869 // free up any existing server list
1870 multi_free_server_list();
1872 // read in all the strings in the file
1873 while(!cfeof(file)){
1875 cfgets(line,MAX_IP_STRING,file);
1877 // strip off any newline character
1878 if(line[strlen(line) - 1] == '\n'){
1879 line[strlen(line) - 1] = '\0';
1882 // empty lines don't get processed
1883 if( (line[0] == '\0') || (line[0] == '\n') ){
1887 if ( !psnet_is_valid_ip_string(line) ) {
1888 nprintf(("Network","Invalid ip string (%s)\n",line));
1890 // copy the server ip address
1891 memset(&addr,0,sizeof(net_addr));
1892 addr.type = NET_TCP;
1893 psnet_string_to_addr(&addr,line);
1894 if ( addr.port == 0 ){
1895 addr.port = DEFAULT_GAME_PORT;
1898 // create a new server item on the list
1899 item = multi_new_server_item();
1901 memcpy(&item->server_addr,&addr,sizeof(net_addr));
1909 // do stuff like pinging servers, sending out requests, etc
1910 void multi_join_do_netstuff()
1912 // handle game query stuff
1913 if(Multi_join_glr_stamp == -1){
1914 broadcast_game_query();
1916 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
1917 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME_LOCAL);
1919 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME);
1922 // otherwise send out game query and restamp
1923 else if(timestamp_elapsed(Multi_join_glr_stamp)){
1924 broadcast_game_query();
1926 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
1927 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME_LOCAL);
1929 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME);
1933 // check to see if we've been accepted. If so, put up message saying so
1934 if ( Net_player->flags & (NETINFO_FLAG_ACCEPT_INGAME|NETINFO_FLAG_ACCEPT_CLIENT|NETINFO_FLAG_ACCEPT_HOST|NETINFO_FLAG_ACCEPT_OBSERVER) ) {
1935 multi_common_add_notify(XSTR("Accepted. Waiting for player data.",770));
1938 // check to see if any join packets we have sent have timed out
1939 if((Multi_join_sent_stamp != -1) && (timestamp_elapsed(Multi_join_sent_stamp))){
1940 Multi_join_sent_stamp = -1;
1941 multi_common_add_notify(XSTR("Join request timed out!",771));
1944 // check to see if we should be pinging everyone
1945 if((Multi_join_ping_stamp == -1) || (timestamp_elapsed(Multi_join_ping_stamp))){
1946 multi_join_ping_all();
1947 Multi_join_ping_stamp = timestamp(MULTI_JOIN_PING_TIME);
1951 multi_join_cull_timeouts();
1954 // evaluate a returned pong.
1955 void multi_join_eval_pong(net_addr *addr, fix pong_time)
1958 active_game *moveup = Active_game_head;
1963 if(psnet_same(&moveup->server_addr,addr)){
1965 multi_ping_eval_pong(&moveup->ping);
1969 moveup = moveup->next;
1971 } while(moveup != Active_game_head);
1974 // update the game's ping
1976 if(found && (moveup->ping_end > moveup->ping_start)){
1977 moveup->ping_time = f2fl(moveup->ping_end - moveup->ping_start);
1978 moveup->ping_start = -1;
1979 moveup->ping_end = -1;
1984 // ping all the server on the list
1985 void multi_join_ping_all()
1987 active_game *moveup = Active_game_head;
1992 moveup->ping_start = timer_get_fixed_seconds();
1993 moveup->ping_end = -1;
1994 send_ping(&moveup->server_addr);
1996 multi_ping_send(&moveup->server_addr,&moveup->ping);
1998 moveup = moveup->next;
1999 } while(moveup != Active_game_head);
2003 void multi_join_process_select()
2005 // if we don't have anything selected and there are items on the list - select the first one
2006 if((Multi_join_list_selected == -1) && (Active_game_count > 0)){
2007 Multi_join_list_selected = 0;
2008 Multi_join_selected_item = multi_join_get_game(0);
2009 MJ_LIST_START_SET(0);
2010 Multi_join_list_start_item = Multi_join_selected_item;
2012 // send a mission description request to this guy
2013 send_netgame_descript_packet(&Multi_join_selected_item->server_addr,0);
2014 multi_common_set_text("");
2016 // I sure hope this doesn't happen
2017 Assert(Multi_join_selected_item != NULL);
2020 // otherwise see if he's clicked on an item
2021 else if(Multi_join_select_button.pressed() && (Active_game_count > 0)){
2023 Multi_join_select_button.get_mouse_pos(NULL,&y);
2025 if(item + Multi_join_list_start < Active_game_count){
2026 gamesnd_play_iface(SND_IFACE_MOUSE_CLICK);
2028 Multi_join_list_selected = item + Multi_join_list_start;
2029 Multi_join_selected_item = multi_join_get_game(Multi_join_list_selected);
2031 // I sure hope this doesn't happen
2032 Assert(Multi_join_selected_item != NULL);
2034 // send a mission description request to this guy
2035 send_netgame_descript_packet(&Multi_join_selected_item->server_addr,0);
2036 multi_common_set_text("");
2040 // if he's double clicked, then select it and accept
2041 if(Multi_join_select_button.double_clicked()){
2043 Multi_join_select_button.get_mouse_pos(NULL,&y);
2045 if(item == Multi_join_list_selected){
2046 multi_join_button_pressed(MJ_ACCEPT);
2051 // return game n (0 based index)
2052 active_game *multi_join_get_game(int n)
2054 active_game *moveup = Active_game_head;
2061 moveup = moveup->next;
2062 while((moveup != Active_game_head) && (count != n)){
2063 moveup = moveup->next;
2066 if(moveup == Active_game_head){
2067 nprintf(("Network","Warning, couldn't find game item %d!\n",n));
2077 // scroll through the game list
2078 void multi_join_list_scroll_up()
2080 // if we're not at the beginning of the list, scroll up
2081 if(Multi_join_list_start_item != Active_game_head){
2082 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2084 MJ_LIST_START_DEC();
2086 gamesnd_play_iface(SND_SCROLL);
2088 gamesnd_play_iface(SND_GENERAL_FAIL);
2092 // scroll through the game list
2093 void multi_join_list_scroll_down()
2095 if((Active_game_count - Multi_join_list_start) > Mj_max_game_items[gr_screen.res]){
2096 Multi_join_list_start_item = Multi_join_list_start_item->next;
2098 MJ_LIST_START_INC();
2100 gamesnd_play_iface(SND_SCROLL);
2102 gamesnd_play_iface(SND_GENERAL_FAIL);
2106 void multi_join_list_page_up()
2108 // in this case, just set us to the beginning of the list
2109 if((Multi_join_list_start - Mj_max_game_items[gr_screen.res]) < 0){
2110 Multi_join_list_start_item = Active_game_head;
2112 MJ_LIST_START_SET(0);
2114 gamesnd_play_iface(SND_SCROLL);
2116 // otherwise page the whole thing up
2118 for(idx=0; idx<Mj_max_game_items[gr_screen.res]; idx++){
2119 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2121 MJ_LIST_START_DEC();
2123 gamesnd_play_iface(SND_SCROLL);
2127 void multi_join_list_page_down()
2131 // page the whole thing down
2132 while((count < Mj_max_game_items[gr_screen.res]) && ((Active_game_count - Multi_join_list_start) > Mj_max_game_items[gr_screen.res])){
2133 Multi_join_list_start_item = Multi_join_list_start_item->next;
2134 MJ_LIST_START_INC();
2139 gamesnd_play_iface(SND_SCROLL);
2142 void multi_join_cull_timeouts()
2144 active_game *backup;
2146 active_game *moveup = Active_game_head;
2148 // traverse through the entire list if any items exist
2152 if((moveup->heard_from_timer != -1) && (timestamp_elapsed(moveup->heard_from_timer))){
2153 Active_game_count--;
2155 // if this is the head of the list
2156 if(moveup == Active_game_head){
2157 // if this is the _only_ item on the list
2158 if(moveup->next == Active_game_head){
2159 // handle any gui details related to deleting this item
2160 multi_join_handle_item_cull(Active_game_head, count);
2162 free(Active_game_head);
2163 Active_game_head = NULL;
2166 // if there are other items on the list
2168 // handle any gui details related to deleting this item
2169 multi_join_handle_item_cull(moveup, count);
2171 Active_game_head = moveup->next;
2172 Active_game_head->prev = moveup->prev;
2173 Active_game_head->prev->next = Active_game_head;
2175 moveup = Active_game_head;
2178 // if its somewhere else on the list
2180 // handle any gui details related to deleting this item
2181 multi_join_handle_item_cull(moveup, count);
2183 // if its the last item on the list
2184 moveup->next->prev = moveup->prev;
2185 moveup->prev->next = moveup->next;
2187 // if it was the last element on the list, return
2188 if(moveup->next == Active_game_head){
2192 backup = moveup->next;
2198 moveup = moveup->next;
2201 } while(moveup != Active_game_head);
2205 // deep magic begins here.
2206 void multi_join_handle_item_cull(active_game *item, int item_index)
2208 // if this is the only item on the list, unset everything
2209 if(item->next == item){
2210 Multi_join_list_selected = -1;
2211 Multi_join_selected_item = NULL;
2213 Multi_join_slider.set_numberItems(0);
2214 MJ_LIST_START_SET(-1);
2215 Multi_join_list_start_item = NULL;
2221 // see if we should be adjusting our currently selected item
2222 if(item_index <= Multi_join_list_selected){
2223 // the selected item is the head of the list
2224 if(Multi_join_selected_item == Active_game_head){
2225 // move the pointer up since this item is about to be destroyed
2226 Multi_join_selected_item = Multi_join_selected_item->next;
2228 // if this is the item being deleted, select the previous one
2229 if(item == Multi_join_selected_item){
2231 Multi_join_selected_item = Multi_join_selected_item->prev;
2233 // decrement the selected index by 1
2234 Multi_join_list_selected--;
2236 // now we know its a previous item, so our pointer stays the same but our index goes down by one, since there will be
2237 // 1 less item on the list
2239 // decrement the selected index by 1
2240 Multi_join_list_selected--;
2245 // see if we should be adjusting out current start position
2246 if(item_index <= Multi_join_list_start){
2247 // the start position is the head of the list
2248 if(Multi_join_list_start_item == Active_game_head){
2249 // move the pointer up since this item is about to be destroyed
2250 Multi_join_list_start_item = Multi_join_list_start_item->next;
2252 // if this is the item being deleted, select the previous one
2253 if(item == Multi_join_list_start_item){
2254 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2256 // decrement the starting index by 1
2257 MJ_LIST_START_DEC();
2259 // but decrement the starting index by 1
2260 MJ_LIST_START_DEC();
2265 // maybe go back up a bit so that we always have a full page of items
2266 if(Active_game_count > Mj_max_game_items[gr_screen.res]){
2267 while((Active_game_count - Multi_join_list_start) < Mj_max_game_items[gr_screen.res]){
2268 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2269 MJ_LIST_START_DEC();
2273 // set slider location
2274 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);
2275 Multi_join_slider.force_currentItem(Multi_join_list_start);
2278 void multi_join_send_join_request(int as_observer)
2280 // don't do anything if we have no items selected
2281 if(Multi_join_selected_item == NULL){
2285 // 5/26/98 -- for team v team games, don't allow ingame joining :-(
2286 if ( (Multi_join_selected_item->flags & AG_FLAG_TEAMS) && (Multi_join_selected_item->flags & (AG_FLAG_PAUSE|AG_FLAG_IN_MISSION)) ) {
2287 popup(0, 1, POPUP_OK, XSTR("Joining ingame is currently not allowed for team vs. team games",772));
2291 memset(&Multi_join_request,0,sizeof(join_request));
2293 // if the netgame is in password mode, put up a request for the password
2294 if(Multi_join_selected_item->flags & AG_FLAG_PASSWD){
2295 if(!multi_passwd_popup(Multi_join_request.passwd)){
2299 nprintf(("Password : %s\n",Multi_join_request.passwd));
2302 // fill out the join request struct
2303 strcpy(Multi_join_request.callsign,Player->callsign);
2304 if(strlen(Player->image_filename) > 0){
2305 strcpy(Multi_join_request.image_filename, Player->image_filename);
2307 if(strlen(Player->squad_filename) > 0){
2308 strcpy(Multi_join_request.squad_filename, Player->squad_filename);
2311 // tracker id (if any)
2312 Multi_join_request.tracker_id = Multi_tracker_id;
2314 // player's rank (at least, what he wants you to _believe_)
2315 Multi_join_request.player_rank = (ubyte)Player->stats.rank;
2318 Multi_join_request.flags = 0;
2320 Multi_join_request.flags |= JOIN_FLAG_AS_OBSERVER;
2323 // if the player has hacked data
2324 if(game_hacked_data()){
2325 Multi_join_request.flags |= JOIN_FLAG_HAXOR;
2329 strncpy(Multi_join_request.pxo_squad_name, Multi_tracker_squad_name, LOGIN_LEN);
2331 // version of this server
2332 Multi_join_request.version = MULTI_FS_SERVER_VERSION;
2334 // server compatible version
2335 Multi_join_request.comp_version = MULTI_FS_SERVER_COMPATIBLE_VERSION;
2337 // his local player options
2338 memcpy(&Multi_join_request.player_options,&Player->m_local_options,sizeof(multi_local_options));
2340 // set the server address for the netgame
2341 memcpy(&Netgame.server_addr,&Multi_join_selected_item->server_addr,sizeof(net_addr));
2343 // send a join request to the guy
2344 send_join_packet(&Multi_join_selected_item->server_addr,&Multi_join_request);
2347 Multi_join_sent_stamp = timestamp(MULTI_JOIN_SENT_WAIT);
2350 multi_common_add_notify(XSTR("Sending join request...",773));
2353 void multi_join_create_game()
2355 // maybe warn the player about possible crappy server conditions
2356 if(!multi_join_maybe_warn()){
2360 // make sure to flag ourself as being the master
2361 Net_player->flags |= (NETINFO_FLAG_AM_MASTER | NETINFO_FLAG_GAME_HOST);
2362 Net_player->state = NETPLAYER_STATE_HOST_SETUP;
2364 // if we're in PXO mode, mark it down in our player struct
2365 if(MULTI_IS_TRACKER_GAME){
2366 Player->flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
2367 Player->save_flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
2369 // otherwise, if he's already played PXO games, warn him
2372 if(Player->flags & PLAYER_FLAGS_HAS_PLAYED_PXO){
2373 if(!multi_join_warn_pxo()){
2380 gameseq_post_event(GS_EVENT_MULTI_START_GAME);
2381 gamesnd_play_iface(SND_USER_SELECT);
2384 void multi_join_reset_join_stamp()
2386 // unset the timestamp here so the user can immediately send another join request
2387 Multi_join_sent_stamp = -1;
2388 multi_common_add_notify("");
2391 void multi_join_blit_top_stuff()
2393 // blit the cd icon if he has one
2394 if(Multi_has_cd && (Multi_common_icons[MICON_CD] != -1)){
2397 bm_get_info(Multi_common_icons[MICON_CD], &cd_w, NULL, NULL, NULL, NULL);
2399 gr_set_bitmap(Multi_common_icons[MICON_CD]);
2400 gr_bitmap((gr_screen.max_w / 2) - (cd_w / 2), Mj_cd_coords[gr_screen.res]);
2404 #define CW_CODE_CANCEL 0 // cancel the action
2405 #define CW_CODE_OK 1 // continue anyway
2406 #define CW_CODE_INFO 2 // gimme some more information
2408 #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)
2409 #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)
2411 #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)
2412 #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)
2414 int multi_join_warn_update_low(int code)
2418 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),LOW_WARN_TEXT);
2421 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),LOW_INFO_TEXT);
2424 return CW_CODE_CANCEL;
2427 int multi_join_warn_update_medium(int code)
2431 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),MED_WARN_TEXT);
2434 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),MED_INFO_TEXT);
2437 return CW_CODE_CANCEL;
2440 int multi_join_maybe_warn()
2444 // if the player is set for low updates
2445 if(Player->m_local_options.obj_update_level == OBJ_UPDATE_LOW){
2448 code = multi_join_warn_update_low(code);
2449 } while((code != CW_CODE_CANCEL) && (code != CW_CODE_OK));
2454 // if the player is set for medium updates
2455 else if(Player->m_local_options.obj_update_level == OBJ_UPDATE_MEDIUM){
2458 code = multi_join_warn_update_medium(code);
2459 } while((code != CW_CODE_CANCEL) && (code != CW_CODE_OK));
2467 int multi_join_warn_pxo()
2469 // 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;
2473 void multi_join_blit_protocol()
2475 gr_set_color_fast(&Color_bright);
2477 switch(Socket_type){
2480 gr_string(5, 2, "TCP");
2484 gr_string(5, 2, "IPX");
2490 // -------------------------------------------------------------------------------------------------
2492 // MULTIPLAYER START GAME screen
2497 #define MULTI_SG_PALETTE "InterfacePalette"
2499 static char *Multi_sg_bitmap_fname[GR_NUM_RESOLUTIONS] = {
2500 "MultiStartGame", // GR_640
2501 "2_MultiStartGame" // GR_1024
2504 static char *Multi_sg_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
2505 "MultiStartGame-M", // GR_640
2506 "2_MultiStartGame-M" // GR_1024
2511 int Multi_sg_rank_max_display[GR_NUM_RESOLUTIONS] = {
2516 // constants for coordinate look ups
2517 #define MSG_X_COORD 0
2518 #define MSG_Y_COORD 1
2519 #define MSG_W_COORD 2
2520 #define MSG_H_COORD 3
2524 // input password field
2525 int Msg_passwd_coords[GR_NUM_RESOLUTIONS][4] = {
2534 // input game title field
2535 int Msg_title_coords[GR_NUM_RESOLUTIONS][4] = {
2544 // rank selected field
2545 int Msg_rank_sel_coords[GR_NUM_RESOLUTIONS][4] = {
2555 int Msg_rank_list_coords[GR_NUM_RESOLUTIONS][4] = {
2566 #define MULTI_SG_NUM_BUTTONS 10
2567 #define MSG_OPEN_GAME 0
2568 //#define MSG_CLOSED_GAME 1
2569 //#define MSG_RESTRICTED_GAME 2
2570 #define MSG_PASSWD_GAME 1
2571 #define MSG_RANK_SET_GAME 2
2572 #define MSG_RANK_SCROLL_UP 3
2573 #define MSG_RANK_SCROLL_DOWN 4
2574 #define MSG_RANK_ABOVE 5
2575 #define MSG_RANK_BELOW 6
2577 #define MSG_OPTIONS 8
2578 #define MSG_ACCEPT 9
2580 UI_WINDOW Multi_sg_window; // the window object for the join screen
2581 UI_BUTTON Multi_sg_rank_button; // for selecting the rank marker
2582 UI_INPUTBOX Multi_sg_game_name; // for Netgame.name
2583 UI_INPUTBOX Multi_sg_game_passwd; // for Netgame.passwd
2584 int Multi_sg_bitmap; // the background bitmap
2586 ui_button_info Multi_sg_buttons[GR_NUM_RESOLUTIONS][MULTI_SG_NUM_BUTTONS] = {
2588 ui_button_info("MSG_00", 1, 184, 34, 191, 2), // open
2589 // ui_button_info("MSG_01", 1, 159, 34, 166, 1), // closed
2590 // ui_button_info("MSG_02", 1, 184, 34, 191, 2), // restricted
2591 ui_button_info("MSG_03", 1, 209, 34, 218, 3), // password
2592 ui_button_info("MSG_04", 1, 257, 34, 266, 4), // rank set
2593 ui_button_info("MSG_05", 1, 282, -1, -1, 5), // rank scroll up
2594 ui_button_info("MSG_06", 1, 307, -1, -1, 6), // rank scroll down
2595 ui_button_info("MSG_07", 177, 282, 210, 290, 7), // rank above
2596 ui_button_info("MSG_08", 177, 307, 210, 315, 8), // rank below
2597 ui_button_info("MSG_09", 536, 429, 500, 440, 9), // help
2598 ui_button_info("MSG_10", 536, 454, 479, 464, 10), // options
2599 ui_button_info("MSG_11", 576, 432, 571, 415, 11), // accept
2602 ui_button_info("2_MSG_00", 2, 295, 51, 307, 2), // open
2603 // ui_button_info("2_MSG_01", 2, 254, 51, 267, 1), // closed
2604 // ui_button_info("2_MSG_02", 2, 295, 51, 307, 2), // restricted
2605 ui_button_info("2_MSG_03", 2, 335, 51, 350, 3), // password
2606 ui_button_info("2_MSG_04", 2, 412, 51, 426, 4), // rank set
2607 ui_button_info("2_MSG_05", 2, 452, -1, -1, 5), // rank scroll up
2608 ui_button_info("2_MSG_06", 2, 492, -1, -1, 6), // rank scroll down
2609 ui_button_info("2_MSG_07", 284, 452, 335, 465, 7), // rank above
2610 ui_button_info("2_MSG_08", 284, 492, 335, 505, 8), // rank below
2611 ui_button_info("2_MSG_09", 858, 687, 817, 706, 9), // help
2612 ui_button_info("2_MSG_10", 858, 728, 797, 743, 10), // options
2613 ui_button_info("2_MSG_11", 921, 692, 921, 664, 11), // accept
2617 #define MULTI_SG_NUM_TEXT 11
2618 UI_XSTR Multi_sg_text[GR_NUM_RESOLUTIONS][MULTI_SG_NUM_TEXT] = {
2620 {"Open", 1322, 34, 191, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_OPEN_GAME].button},
2621 // {"Closed", 1323, 34, 166, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_CLOSED_GAME].button},
2622 // {"Restricted", 1324, 34, 191, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RESTRICTED_GAME].button},
2623 {"Password Protected", 1325, 34, 218, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_PASSWD_GAME].button},
2624 {"Allow Rank", 1326, 34, 266, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_SET_GAME].button},
2625 {"Above", 1327, 210, 290, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_ABOVE].button},
2626 {"Below", 1328, 210, 315, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_BELOW].button},
2627 {"Help", 928, 500, 440, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_HELP].button},
2628 {"Options", 1036, 479, 464, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_OPTIONS].button},
2629 {"Accept", 1035, 571, 415, UI_XSTR_COLOR_PINK, -1, &Multi_sg_buttons[0][MSG_ACCEPT].button},
2630 {"Start Game", 1329, 26, 10, UI_XSTR_COLOR_GREEN, -1, NULL},
2631 {"Title", 1330, 26, 31, UI_XSTR_COLOR_GREEN, -1, NULL},
2632 {"Game Type", 1331, 12, 165, UI_XSTR_COLOR_GREEN, -1, NULL},
2635 {"Open", 1322, 51, 307, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_OPEN_GAME].button},
2636 // {"Closed", 1323, 51, 267, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_CLOSED_GAME].button},
2637 // {"Restricted", 1324, 51, 307, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RESTRICTED_GAME].button},
2638 {"Password Protected", 1325, 51, 350, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_PASSWD_GAME].button},
2639 {"Allow Rank", 1326, 51, 426, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_SET_GAME].button},
2640 {"Above", 1327, 335, 465, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_ABOVE].button},
2641 {"Below", 1328, 335, 505, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_BELOW].button},
2642 {"Help", 928, 817, 706, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_HELP].button},
2643 {"Options", 1036, 797, 743, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_OPTIONS].button},
2644 {"Accept", 1035, 921, 664, UI_XSTR_COLOR_PINK, -1, &Multi_sg_buttons[1][MSG_ACCEPT].button},
2645 {"Start Game", 1329, 42, 22, UI_XSTR_COLOR_GREEN, -1, NULL},
2646 {"Title", 1330, 42, 50, UI_XSTR_COLOR_GREEN, -1, NULL},
2647 {"Game Type", 1331, 20, 264, UI_XSTR_COLOR_GREEN, -1, NULL},
2651 // starting index for displaying ranks
2652 int Multi_sg_rank_start;
2653 int Multi_sg_rank_select;
2655 // netgame pointer to indirect through
2656 netgame_info *Multi_sg_netgame;
2658 // hold temporary values in this structure when on a standalone server
2659 netgame_info Multi_sg_netgame_temp;
2661 // forward declarations
2662 void multi_sg_check_buttons();
2663 void multi_sg_button_pressed(int n);
2664 void multi_sg_init_gamenet();
2665 void multi_sg_draw_radio_buttons();
2666 void multi_sg_rank_scroll_up();
2667 void multi_sg_rank_scroll_down();
2668 void multi_sg_rank_display_stuff();
2669 void multi_sg_rank_process_select();
2670 void multi_sg_rank_build_name(char *in,char *out);
2671 void multi_sg_check_passwd();
2672 void multi_sg_check_name();
2673 void multi_sg_release_passwd();
2674 int multi_sg_rank_select_valid(int rank);
2675 void multi_sg_select_rank_default();
2677 // function which takes a rank name and returns the index. Useful for commandline options
2678 // for above and below rank. We return the index of the rank in the Ranks[] array. If
2679 // the rank isn't found, we return -1
2680 int multi_start_game_rank_from_name( char *rank ) {
2683 for ( i = 0; i <= MAX_FREESPACE2_RANK; i++ ) {
2684 if ( !stricmp(Ranks[i].name, rank) ) {
2692 void multi_start_game_init()
2696 // initialize the gamenet
2697 multi_sg_init_gamenet();
2699 // create the interface window
2700 Multi_sg_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
2701 Multi_sg_window.set_mask_bmap(Multi_sg_bitmap_mask_fname[gr_screen.res]);
2703 // load the background bitmap
2704 Multi_sg_bitmap = bm_load(Multi_sg_bitmap_fname[gr_screen.res]);
2705 if(Multi_sg_bitmap < 0){
2706 // we failed to load the bitmap - this is very bad
2710 // initialize the common notification messaging
2711 multi_common_notify_init();
2713 // initialize the common text area
2714 multi_common_set_text("");
2716 // use the common interface palette
2717 multi_common_set_palette();
2719 // create the interface buttons
2720 for(idx=0; idx<MULTI_SG_NUM_BUTTONS; idx++){
2721 // create the object
2722 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);
2724 // set the sound to play when highlighted
2725 Multi_sg_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
2727 // set the ani for the button
2728 Multi_sg_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sg_buttons[gr_screen.res][idx].filename);
2731 Multi_sg_buttons[gr_screen.res][idx].button.link_hotspot(Multi_sg_buttons[gr_screen.res][idx].hotspot);
2735 for(idx=0; idx<MULTI_SG_NUM_TEXT; idx++){
2736 Multi_sg_window.add_XSTR(&Multi_sg_text[gr_screen.res][idx]);
2739 // load the help overlay
2740 help_overlay_load(MULTI_START_OVERLAY);
2741 help_overlay_set_state(MULTI_START_OVERLAY,0);
2743 // intiialize the rank selection items
2744 multi_sg_select_rank_default();
2745 Multi_sg_rank_start = Multi_sg_rank_select;
2747 // create the rank select button
2748 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);
2749 Multi_sg_rank_button.hide();
2751 // create the netgame name input box
2752 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);
2754 // create the netgame password input box, and disable it by default
2755 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);
2756 Multi_sg_game_passwd.hide();
2757 Multi_sg_game_passwd.disable();
2759 // set the netgame text to this gadget and make it have focus
2760 Multi_sg_game_name.set_text(Multi_sg_netgame->name);
2761 Multi_sg_game_name.set_focus();
2763 // if starting a netgame, set the name of the game and any other options that are appropriate
2764 if ( Cmdline_start_netgame ) {
2765 if ( Cmdline_game_name != NULL ) {
2766 strcpy( Multi_sg_netgame->name, Cmdline_game_name );
2767 Multi_sg_game_name.set_text(Multi_sg_netgame->name);
2770 // deal with the different game types -- only one should even be active, so we will just go down
2771 // the line. Last one wins.
2772 if ( Cmdline_closed_game ) {
2773 Multi_sg_netgame->mode = NG_MODE_CLOSED;
2774 } else if ( Cmdline_restricted_game ) {
2775 Multi_sg_netgame->mode = NG_MODE_RESTRICTED;
2776 } else if ( Cmdline_game_password != NULL ) {
2777 Multi_sg_netgame->mode = NG_MODE_PASSWORD;
2778 strcpy(Multi_sg_netgame->passwd, Cmdline_game_password);
2779 Multi_sg_game_passwd.set_text(Multi_sg_netgame->passwd);
2782 // deal with rank above and rank below
2783 if ( (Cmdline_rank_above != NULL) || (Cmdline_rank_below != NULL) ) {
2787 if ( Cmdline_rank_above != NULL ) {
2788 rank_str = Cmdline_rank_above;
2790 rank_str = Cmdline_rank_below;
2793 // try and get the rank index from the name -- if found, then set the rank base
2794 // and the game type. apparently we only support either above or below, not both
2795 // together, so I make a random choice
2796 rank = multi_start_game_rank_from_name( rank_str );
2798 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
2800 // now an arbitrary decision
2801 if ( Cmdline_rank_above != NULL ) {
2802 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
2804 Multi_sg_netgame->mode = NG_MODE_RANK_BELOW;
2809 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
2813 void multi_start_game_do()
2815 // return here since we will be moving to the next stage anyway -- I don't want to see the backgrounds of
2816 // all the screens for < 1 second for every screen we automatically move to.
2817 if ( Cmdline_start_netgame ) {
2821 int k = Multi_sg_window.process();
2823 // process any keypresses
2826 if(help_overlay_active(MULTI_START_OVERLAY)){
2827 help_overlay_set_state(MULTI_START_OVERLAY,0);
2829 gamesnd_play_iface(SND_USER_SELECT);
2830 multi_quit_game(PROMPT_NONE);
2835 case KEY_LCTRL + KEY_ENTER :
2836 case KEY_RCTRL + KEY_ENTER :
2837 gamesnd_play_iface(SND_COMMIT_PRESSED);
2838 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
2842 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
2843 help_overlay_set_state(MULTI_START_OVERLAY, 0);
2846 // check to see if the user has selected a different rank
2847 multi_sg_rank_process_select();
2849 // check any button presses
2850 multi_sg_check_buttons();
2852 // check to see if any of the input boxes have changed, and update the appropriate Netgame fields if necessary
2853 multi_sg_check_passwd();
2854 multi_sg_check_name();
2856 // draw the background, etc
2858 GR_MAYBE_CLEAR_RES(Multi_sg_bitmap);
2859 if(Multi_sg_bitmap != -1){
2860 gr_set_bitmap(Multi_sg_bitmap);
2863 Multi_sg_window.draw();
2865 // display rank stuff
2866 multi_sg_rank_display_stuff();
2868 // display any pending notification messages
2869 multi_common_notify_do();
2871 // draw all radio button
2872 multi_sg_draw_radio_buttons();
2874 // draw the help overlay
2875 help_overlay_maybe_blit(MULTI_START_OVERLAY);
2881 void multi_start_game_close()
2883 // if i'm the host on a standalone server, send him my start game options (passwd, mode, etc)
2884 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
2885 multi_options_update_start_game(Multi_sg_netgame);
2888 // unload any bitmaps
2889 if(!bm_unload(Multi_sg_bitmap)){
2890 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_sg_bitmap_fname[gr_screen.res]));
2893 // unload the help overlay
2894 help_overlay_unload(MULTI_START_OVERLAY);
2896 // destroy the UI_WINDOW
2897 Multi_sg_window.destroy();
2900 void multi_sg_check_buttons()
2903 for(idx=0;idx<MULTI_SG_NUM_BUTTONS;idx++){
2904 // we only really need to check for one button pressed at a time, so we can break after
2906 if(Multi_sg_buttons[gr_screen.res][idx].button.pressed()){
2907 multi_sg_button_pressed(idx);
2913 void multi_sg_button_pressed(int n)
2916 // go to the options screen
2918 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
2923 if(!help_overlay_active(MULTI_START_OVERLAY)){
2924 help_overlay_set_state(MULTI_START_OVERLAY,1);
2926 help_overlay_set_state(MULTI_START_OVERLAY,0);
2930 // the open button was pressed
2932 // if the closed option is selected
2933 if(Multi_sg_netgame->mode != NG_MODE_OPEN){
2934 Multi_sg_netgame->mode = NG_MODE_OPEN;
2936 gamesnd_play_iface(SND_USER_SELECT);
2938 // release the password control if necessary
2939 multi_sg_release_passwd();
2941 // if its already selected
2943 gamesnd_play_iface(SND_GENERAL_FAIL);
2948 // the open button was pressed
2949 case MSG_CLOSED_GAME:
2950 // if the closed option is selected
2951 if(Multi_sg_netgame->mode != NG_MODE_CLOSED){
2952 Multi_sg_netgame->mode = NG_MODE_CLOSED;
2954 gamesnd_play_iface(SND_USER_SELECT);
2956 // release the password control if necessary
2957 multi_sg_release_passwd();
2959 // if its already selected
2961 gamesnd_play_iface(SND_GENERAL_FAIL);
2965 // toggle password protection
2966 case MSG_PASSWD_GAME:
2967 // if we selected it
2968 if(Multi_sg_netgame->mode != NG_MODE_PASSWORD){
2969 gamesnd_play_iface(SND_USER_SELECT);
2971 Multi_sg_game_passwd.enable();
2972 Multi_sg_game_passwd.unhide();
2973 Multi_sg_game_passwd.set_focus();
2975 Multi_sg_netgame->mode = NG_MODE_PASSWORD;
2977 // copy in the current network password
2978 Multi_sg_game_passwd.set_text(Multi_sg_netgame->passwd);
2980 gamesnd_play_iface(SND_GENERAL_FAIL);
2985 // toggle "restricted" on or off
2986 case MSG_RESTRICTED_GAME:
2987 if(Multi_sg_netgame->mode != NG_MODE_RESTRICTED){
2988 gamesnd_play_iface(SND_USER_SELECT);
2989 Multi_sg_netgame->mode = NG_MODE_RESTRICTED;
2991 // release the password control if necessary
2992 multi_sg_release_passwd();
2994 gamesnd_play_iface(SND_GENERAL_FAIL);
2998 // turn off all rank requirements
2999 case MSG_RANK_SET_GAME:
3000 // if either is set, then turn then both off
3001 if((Multi_sg_netgame->mode != NG_MODE_RANK_BELOW) && (Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE)){
3002 gamesnd_play_iface(SND_USER_SELECT);
3004 // set it to the default case if we're turning it off
3005 multi_sg_select_rank_default();
3006 Multi_sg_rank_start = Multi_sg_rank_select;
3008 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
3010 // release the password control if necessary
3011 multi_sg_release_passwd();
3013 gamesnd_play_iface(SND_GENERAL_FAIL);
3017 // rank above was pressed
3018 case MSG_RANK_ABOVE :
3019 if((Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE) || (Multi_sg_netgame->mode == NG_MODE_RANK_BELOW)){
3020 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
3022 // select the first item
3023 multi_sg_select_rank_default();
3024 Multi_sg_rank_start = Multi_sg_rank_select;
3027 gamesnd_play_iface(SND_USER_SELECT);
3029 gamesnd_play_iface(SND_GENERAL_FAIL);
3033 // rank below was pressed
3034 case MSG_RANK_BELOW :
3035 if((Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE) || (Multi_sg_netgame->mode == NG_MODE_RANK_BELOW)){
3036 Multi_sg_netgame->mode = NG_MODE_RANK_BELOW;
3038 // select the first item
3039 multi_sg_select_rank_default();
3040 Multi_sg_rank_start = Multi_sg_rank_select;
3043 gamesnd_play_iface(SND_USER_SELECT);
3045 gamesnd_play_iface(SND_GENERAL_FAIL);
3049 // scroll the rank list up
3050 case MSG_RANK_SCROLL_UP:
3051 multi_sg_rank_scroll_up();
3054 // scroll the rank list down
3055 case MSG_RANK_SCROLL_DOWN:
3056 multi_sg_rank_scroll_down();
3059 // move to the create game screen
3061 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
3062 gamesnd_play_iface(SND_COMMIT_PRESSED);
3066 gamesnd_play_iface(SND_GENERAL_FAIL);
3067 multi_common_add_notify(XSTR("Not implemented yet!",760));
3072 // NOTE : this is where all Netgame initialization should take place on the host
3073 void multi_sg_init_gamenet()
3075 char buf[128],out_name[128];
3077 net_player *server_save;
3079 // back this data up in case we are already connected to a standalone
3080 memcpy(&save,&Netgame.server_addr,sizeof(net_addr));
3081 server_save = Netgame.server;
3083 // remove campaign flags
3084 Game_mode &= ~(GM_CAMPAIGN_MODE);
3086 // clear out the Netgame structure and start filling in the values
3087 memset( &Netgame, 0, sizeof(Netgame) );
3088 memset( &Multi_sg_netgame_temp, 0, sizeof(netgame_info) );
3090 // if we're on the standalone, we're not the server, so we don't care about setting the netgame state
3091 if(Net_player->state != NETPLAYER_STATE_STD_HOST_SETUP){
3092 Netgame.game_state = NETGAME_STATE_HOST_SETUP;
3093 Multi_sg_netgame = &Netgame;
3096 ml_string(NOX("Starting netgame as Host/Server"));
3098 Multi_sg_netgame = &Multi_sg_netgame_temp;
3102 memcpy(&temp_addr.s_addr, &Netgame.server_addr, 4);
3103 char *server_addr = inet_ntoa(temp_addr);
3104 ml_printf(NOX("Starting netgame as Host on Standalone server : %s"), (server_addr == NULL) ? NOX("Unknown") : server_addr);
3107 Net_player->tracker_player_id = Multi_tracker_id;
3109 Multi_sg_netgame->security = (rand() % 32766) + 1; // get some random security number
3110 Multi_sg_netgame->mode = NG_MODE_OPEN;
3111 Multi_sg_netgame->rank_base = RANK_ENSIGN;
3112 if(Multi_sg_netgame->security < 16){
3113 Multi_sg_netgame->security += 16;
3116 // set the version_info field
3117 Multi_sg_netgame->version_info = NG_VERSION_ID;
3119 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
3120 Netgame.host = Net_player;
3123 // set the default netgame flags
3124 Multi_sg_netgame->flags = 0;
3126 // intialize endgame stuff
3127 multi_endgame_init();
3129 // load in my netgame options
3130 multi_options_netgame_load(&Netgame.options);
3132 // load my local netplayer options
3133 multi_options_local_load(&Net_player->p_info.options, Net_player);
3135 // setup the default game name, taking care of string length and player callsigns
3136 memset(out_name,0,128);
3138 pilot_format_callsign_personal(Player->callsign,out_name);
3139 sprintf(buf, XSTR("%s game",782), out_name); // [[ %s will be a pilot's name ]]
3140 if ( strlen(buf) > MAX_GAMENAME_LEN ){
3141 strcpy(buf, XSTR("Temporary name",783));
3143 strcpy(Multi_sg_netgame->name, buf);
3145 // set the default qos and duration
3146 multi_voice_maybe_update_vars(Netgame.options.voice_qos,Netgame.options.voice_record_time);
3148 // make sure to set the server correctly (me or the standalone)
3149 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3150 memcpy(&Netgame.server_addr, &Psnet_my_addr,sizeof(net_addr));
3151 Netgame.server = Net_player;
3152 Net_player->player_id = multi_get_new_id();
3154 // setup debug flags
3155 Netgame.debug_flags = 0;
3157 if(!Cmdline_server_firing){
3158 Netgame.debug_flags |= NETD_FLAG_CLIENT_FIRING;
3160 if(!Cmdline_client_dodamage){
3161 Netgame.debug_flags |= NETD_FLAG_CLIENT_NODAMAGE;
3165 memcpy(&Netgame.server_addr,&save,sizeof(net_addr));
3166 Netgame.server = server_save;
3169 // if I have a cd or not
3171 Net_player->flags |= NETINFO_FLAG_HAS_CD;
3174 // if I have hacked data
3175 if(game_hacked_data()){
3176 Net_player->flags |= NETINFO_FLAG_HAXOR;
3179 // assign my player struct and other data
3180 Net_player->flags |= (NETINFO_FLAG_CONNECTED | NETINFO_FLAG_DO_NETWORKING);
3181 Net_player->s_info.voice_token_timestamp = -1;
3183 // if we're supposed to flush our cache directory, do so now
3184 if(Net_player->p_info.options.flags & MLO_FLAG_FLUSH_CACHE){
3185 multi_flush_multidata_cache();
3188 ml_string(NOX("Flushing multi-data cache"));
3194 void multi_sg_draw_radio_buttons()
3196 // draw the appropriate radio button
3197 switch(Multi_sg_netgame->mode){
3199 Multi_sg_buttons[gr_screen.res][MSG_OPEN_GAME].button.draw_forced(2);
3202 case NG_MODE_CLOSED:
3203 Multi_sg_buttons[gr_screen.res][MSG_CLOSED_GAME].button.draw_forced(2);
3206 case NG_MODE_PASSWORD:
3207 Multi_sg_buttons[gr_screen.res][MSG_PASSWD_GAME].button.draw_forced(2);
3209 /* case NG_MODE_RESTRICTED:
3210 Multi_sg_buttons[gr_screen.res][MSG_RESTRICTED_GAME].button.draw_forced(2);
3213 case NG_MODE_RANK_ABOVE:
3214 Multi_sg_buttons[gr_screen.res][MSG_RANK_SET_GAME].button.draw_forced(2);
3215 Multi_sg_buttons[gr_screen.res][MSG_RANK_ABOVE].button.draw_forced(2);
3217 case NG_MODE_RANK_BELOW:
3218 Multi_sg_buttons[gr_screen.res][MSG_RANK_SET_GAME].button.draw_forced(2);
3219 Multi_sg_buttons[gr_screen.res][MSG_RANK_BELOW].button.draw_forced(2);
3224 void multi_sg_rank_scroll_up()
3226 // if he doesn't have either of the rank flags set, then ignore this
3227 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3231 if(Multi_sg_rank_start > 0){
3232 Multi_sg_rank_start--;
3233 gamesnd_play_iface(SND_SCROLL);
3235 gamesnd_play_iface(SND_GENERAL_FAIL);
3239 void multi_sg_rank_scroll_down()
3241 // if he doesn't have either of the rank flags set, then ignore this
3242 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3246 if((NUM_RANKS - Multi_sg_rank_start) > Multi_sg_rank_max_display[gr_screen.res]){
3247 Multi_sg_rank_start++;
3248 gamesnd_play_iface(SND_SCROLL);
3250 gamesnd_play_iface(SND_GENERAL_FAIL);
3254 void multi_sg_rank_display_stuff()
3259 // if he doesn't have either of the rank flags set, then ignore this
3260 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3264 // display the list of ranks
3265 y = Msg_rank_list_coords[gr_screen.res][MSG_Y_COORD];
3266 idx = Multi_sg_rank_start;
3268 while((count < NUM_RANKS) && (count < Multi_sg_rank_max_display[gr_screen.res]) && (idx < NUM_RANKS)){
3269 // if its the selected item, then color it differently
3270 if(idx == Multi_sg_rank_select){
3271 gr_set_color_fast(&Color_text_selected);
3273 gr_set_color_fast(&Color_text_normal);
3277 multi_sg_rank_build_name(Ranks[idx].name,rank_name);
3278 gr_string(Msg_rank_list_coords[gr_screen.res][MSG_X_COORD],y,rank_name);
3286 // display the selected rank
3288 gr_set_color_fast(&Color_bright);
3289 multi_sg_rank_build_name(Ranks[Multi_sg_netgame->rank_base].name,rank_name);
3290 gr_string(Msg_rank_sel_coords[gr_screen.res][MSG_X_COORD],Msg_rank_sel_coords[gr_screen.res][MSG_Y_COORD],rank_name);
3294 void multi_sg_rank_process_select()
3298 // if he doesn't have either of the rank flags set, then ignore this
3299 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3303 // see if he's clicked on an item on the rank list
3304 if(Multi_sg_rank_button.pressed()){
3306 Multi_sg_rank_button.get_mouse_pos(NULL,&y);
3309 if(item + Multi_sg_rank_start < NUM_RANKS){
3310 // evaluate whether this rank is valid for the guy to pick
3311 if(multi_sg_rank_select_valid(item + Multi_sg_rank_start)){
3312 gamesnd_play_iface(SND_USER_SELECT);
3314 Multi_sg_rank_select = item + Multi_sg_rank_start;
3316 // set the Netgame rank
3317 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
3319 gamesnd_play_iface(SND_GENERAL_FAIL);
3321 memset(string,0,255);
3322 sprintf(string,XSTR("Illegal value for a host of your rank (%s)\n",784),Ranks[Net_player->player->stats.rank].name);
3323 multi_common_add_notify(string);
3329 void multi_sg_rank_build_name(char *in,char *out)
3335 first = strtok(use," ");
3337 // just copy the string
3342 // if the first part of the string is lieutenant, then abbreivate it and tack on the rest of the string
3343 if (stricmp(first,XSTR("lieutenant",785)) == 0) {
3344 first = strtok(NULL, NOX("\n"));
3346 // if he's not just a plain lieutenant
3348 strcpy(out,XSTR("Lt. ",786)); // [[ lieutenant ]]
3351 // if he _is_ just a plain lieutenant
3360 void multi_sg_check_passwd()
3362 // check to see if the password input box has been pressed
3363 if(Multi_sg_game_passwd.changed()){
3364 Multi_sg_game_passwd.get_text(Multi_sg_netgame->passwd);
3368 void multi_sg_check_name()
3370 // check to see if the game name input box has been pressed
3371 if(Multi_sg_game_name.changed()){
3372 Multi_sg_game_name.get_text(Multi_sg_netgame->name);
3376 void multi_sg_release_passwd()
3378 // hide and disable the password input box
3379 Multi_sg_game_passwd.hide();
3380 Multi_sg_game_passwd.disable();
3382 // set the focus back to the name input box
3383 Multi_sg_game_name.set_focus();
3386 int multi_sg_rank_select_valid(int rank)
3389 if(Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE){
3390 if(Net_player->player->stats.rank >= rank){
3396 if(Net_player->player->stats.rank <= rank){
3404 void multi_sg_select_rank_default()
3406 // pick our rank for now
3407 Multi_sg_rank_select = Net_player->player->stats.rank;
3409 // set the Netgame rank
3410 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
3413 // -------------------------------------------------------------------------------------------------
3415 // MULTIPLAYER CREATE GAME screen
3420 char *Multi_create_bitmap_fname[GR_NUM_RESOLUTIONS] = {
3421 "MultiCreate", // GR_640
3422 "2_MultiCreate" // GR_1024
3425 char *Multi_create_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
3426 "MultiCreate-M", // GR_640
3427 "2_MultiCreate-M" // GR_1024
3430 char *Multi_create_loading_fname[GR_NUM_RESOLUTIONS] = {
3431 "PleaseWait", // GR_640
3432 "2_PleaseWait" // GR_1024
3436 #define MULTI_CREATE_NUM_BUTTONS 23
3439 #define MC_SHOW_ALL 0
3440 #define MC_SHOW_COOP 1
3441 #define MC_SHOW_TEAM 2
3442 #define MC_SHOW_DOGFIGHT 3
3443 #define MC_PXO_REFRESH 4
3444 #define MC_PILOT_INFO 5
3445 #define MC_SCROLL_LIST_UP 6
3446 #define MC_SCROLL_LIST_DOWN 7
3447 #define MC_SCROLL_PLAYERS_UP 8
3448 #define MC_SCROLL_PLAYERS_DOWN 9
3449 #define MC_MISSION_FILTER 10
3450 #define MC_CAMPAIGN_FILTER 11
3451 #define MC_CANCEL 12
3456 #define MC_SCROLL_INFO_UP 17
3457 #define MC_SCROLL_INFO_DOWN 18
3458 #define MC_HOST_OPTIONS 19
3460 #define MC_OPTIONS 21
3461 #define MC_ACCEPT 22
3464 UI_WINDOW Multi_create_window; // the window object for the create screen
3465 UI_BUTTON Multi_create_player_select_button; // for selecting players
3466 UI_BUTTON Multi_create_list_select_button; // for selecting missions/campaigns
3467 int Multi_create_bitmap; // the background bitmap
3468 UI_SLIDER2 Multi_create_slider; // for create list
3470 // constants for coordinate look ups
3471 #define MC_X_COORD 0
3472 #define MC_Y_COORD 1
3473 #define MC_W_COORD 2
3474 #define MC_H_COORD 3
3476 ui_button_info Multi_create_buttons[GR_NUM_RESOLUTIONS][MULTI_CREATE_NUM_BUTTONS] = {
3478 ui_button_info("MC_00", 32, 129, 36, 158, 0), // show all missions
3479 ui_button_info("MC_01", 76, 129, 71, 158, 1), // show coop missions
3480 ui_button_info("MC_02", 121, 129, 119, 158, 2), // show team missions
3481 ui_button_info("MC_03", 164, 129, 166, 158, 3), // show dogfight missions
3482 ui_button_info("MC_04", 399, 129, 229, 130, 4), // pxo mission refresh
3483 ui_button_info("MC_05", 567, 123, 467, 132, 5), // pilot info
3484 ui_button_info("MC_06", 1, 161, -1, -1, 6), // scroll mission info up
3485 ui_button_info("MC_08", 1, 304, -1, -1, 8), // scroll mission info down
3486 ui_button_info("MC_09", 613, 160, -1, -1, 9), // scroll players up
3487 ui_button_info("MC_10", 613, 202, -1, -1, 10), // scroll players down
3488 ui_button_info("MC_11", 22, 346, 27, 376, 11), // mission filter
3489 ui_button_info("MC_12", 104, 346, 110, 376, 12), // campaign filter
3490 ui_button_info("MC_13", 392, 341, 328, 364, 13), // cancel
3491 ui_button_info("MC_14", 472, 352, 482, 381, 14), // team 0
3492 ui_button_info("MC_15", 506, 352, 514, 381, 15), // team 1
3493 ui_button_info("MC_16", 539, 346, 539, 381, 16), // kick
3494 ui_button_info("MC_17", 589, 346, 582, 381, 17), // close
3495 ui_button_info("MC_18", 1, 406, -1, -1, 18), // scroll list up
3496 ui_button_info("MC_19", 1, 447, -1, -1, 19), // scroll list down
3497 ui_button_info("MC_20", 499, 434, 436, 423, 20), // host options
3498 ui_button_info("MC_21", 534, 426, -1, -1, 21), // help
3499 ui_button_info("MC_22", 534, 452, -1, -1, 22), // options
3500 ui_button_info("MC_23", 571, 426, 572, 413, 23), // commit
3503 ui_button_info("2_MC_00", 51, 207, 61, 253, 0), // show all missions
3504 ui_button_info("2_MC_01", 122, 207, 124, 253, 1), // show coop missions
3505 ui_button_info("2_MC_02", 193, 207, 194, 253, 2), // show team missions
3506 ui_button_info("2_MC_03", 263, 207, 261, 253, 3), // show dogfight missions
3507 ui_button_info("2_MC_04", 639, 207, 479, 218, 4), // pxo mission refresh
3508 ui_button_info("2_MC_05", 907, 197, 748, 216, 5), // pilot info
3509 ui_button_info("2_MC_06", 1, 258, -1, -1, 6), // scroll mission info up
3510 ui_button_info("2_MC_08", 1, 487, -1, -1, 8), // scroll mission info down
3511 ui_button_info("2_MC_09", 981, 256, -1, -1, 9), // scroll players up
3512 ui_button_info("2_MC_10", 981, 323, -1, -1, 10), // scroll players down
3513 ui_button_info("2_MC_11", 35, 554, 46, 601, 11), // mission filter
3514 ui_button_info("2_MC_12", 166, 554, 174, 601, 12), // campaign filter
3515 ui_button_info("2_MC_13", 628, 545, 559, 582, 13), // cancel
3516 ui_button_info("2_MC_14", 756, 564, 772, 610, 14), // team 0
3517 ui_button_info("2_MC_15", 810, 564, 826, 610, 15), // team 1
3518 ui_button_info("2_MC_16", 862, 554, 872, 610, 16), // kick
3519 ui_button_info("2_MC_17", 943, 554, 949, 610, 17), // close
3520 ui_button_info("2_MC_18", 1, 649, -1, -1, 18), // scroll list up
3521 ui_button_info("2_MC_19", 1, 716, -1, -1, 19), // scroll list down
3522 ui_button_info("2_MC_20", 798, 695, 726, 667, 20), // host options
3523 ui_button_info("2_MC_21", 854, 681, -1, -1, 21), // help
3524 ui_button_info("2_MC_22", 854, 724, -1, -1, 22), // options
3525 ui_button_info("2_MC_23", 914, 681, 932, 667, 23), // commit
3529 #define MULTI_CREATE_NUM_TEXT 15
3530 UI_XSTR Multi_create_text[GR_NUM_RESOLUTIONS][MULTI_CREATE_NUM_BUTTONS] = {
3532 {"All", 1256, 36, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_ALL].button},
3533 {"Coop", 1257, 71, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_COOP].button},
3534 {"Team", 1258, 119, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_TEAM].button},
3535 {"Dogfight", 1259, 166, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_DOGFIGHT].button},
3536 {"Refresh Missions", 1260, 229, 130, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_PXO_REFRESH].button},
3537 {"Pilot Info", 1261, 467, 132, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_PILOT_INFO].button},
3538 {"Missions", 1262, 27, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_MISSION_FILTER].button},
3539 {"Campaigns", 1263, 110, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_CAMPAIGN_FILTER].button},
3540 {"Cancel", 387, 328, 364, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_CANCEL].button},
3541 {"1", 1264, 482, 381, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_TEAM0].button},
3542 {"2", 1265, 514, 381, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_TEAM1].button},
3543 {"Kick", 1266, 539, 381, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_KICK].button},
3544 {"Close", 1508, 582, 381, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_CLOSE].button},
3545 {"Host Options", 1267, 436, 423, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_HOST_OPTIONS].button},
3546 {"Commit", 1062, 572, 413, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_ACCEPT].button}
3549 {"All", 1256, 61, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_ALL].button},
3550 {"Coop", 1257, 124, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_COOP].button},
3551 {"Team", 1258, 194, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_TEAM].button},
3552 {"Dogfight", 1259, 261, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_DOGFIGHT].button},
3553 {"Refresh Missions", 1260, 501, 218, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_PXO_REFRESH].button},
3554 {"Pilot Info", 1261, 814, 216, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_PILOT_INFO].button},
3555 {"Missions", 1262, 46, 601, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_MISSION_FILTER].button},
3556 {"Campaigns", 1263, 174, 601, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_CAMPAIGN_FILTER].button},
3557 {"Cancel", 387, 559, 582, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_CANCEL].button},
3558 {"1", 1264, 772, 610, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_TEAM0].button},
3559 {"2", 1265, 826, 610, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_TEAM1].button},
3560 {"Kick", 1266, 872, 610, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_KICK].button},
3561 {"Close", 1508, 949, 610, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_CLOSE].button},
3562 {"Host Options", 1267, 755, 683, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_HOST_OPTIONS].button},
3563 {"Commit", 1062, 932, 667, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_ACCEPT].button}
3567 // squad war checkbox
3568 UI_CHECKBOX Multi_create_sw_checkbox;
3569 char *Multi_create_sw_checkbox_fname[GR_NUM_RESOLUTIONS] = {
3573 int Multi_create_sw_checkbox_coords[GR_NUM_RESOLUTIONS][2] = {
3581 int Multi_create_sw_checkbox_text[GR_NUM_RESOLUTIONS][2] = {
3590 // game information text areas
3591 int Mc_list_coords[GR_NUM_RESOLUTIONS][4] = {
3600 int Mc_players_coords[GR_NUM_RESOLUTIONS][4] = {
3609 int Mc_info_coords[GR_NUM_RESOLUTIONS][4] = {
3618 // mission icon stuff
3619 int Mc_icon_type_coords[GR_NUM_RESOLUTIONS][2] = {
3621 38, -2 // y is an offset
3624 61, -2 // y is an offset
3628 int Mc_icon_volition_coords[GR_NUM_RESOLUTIONS][2] = {
3630 61, -1 // y is an offset
3633 98, 1 // y is an offset
3637 int Mc_icon_silent_coords[GR_NUM_RESOLUTIONS][2] = {
3639 72, 0 // y is an offset
3642 115, 0 // y is an offset
3646 int Mc_icon_valid_coords[GR_NUM_RESOLUTIONS][2] = {
3648 91, 0 // y is an offset
3651 146, 0 // y is an offset
3655 // mission/campaign list column areas
3656 int Mc_column1_w[GR_NUM_RESOLUTIONS] = {
3661 int Mc_column2_w[GR_NUM_RESOLUTIONS] = {
3666 int Mc_column3_w[GR_NUM_RESOLUTIONS] = {
3671 int Mc_mission_name_x[GR_NUM_RESOLUTIONS] = {
3676 int Mc_mission_count_x[GR_NUM_RESOLUTIONS] = {
3681 int Mc_mission_fname_x[GR_NUM_RESOLUTIONS] = {
3686 int Mc_create_game_text[GR_NUM_RESOLUTIONS][2] = {
3687 {13, 116}, // GR_640
3688 {21, 186} // GR_1024
3691 int Mc_players_text[GR_NUM_RESOLUTIONS][2] = {
3692 {467, 150}, // GR_640
3693 {747, 240} // GR_1024
3696 int Mc_team_text[GR_NUM_RESOLUTIONS][2] = {
3697 {484, 342}, // GR_640
3698 {774, 547} // GR_1024
3701 int Mc_slider_coords[GR_NUM_RESOLUTIONS][4] = {
3703 3, 197, 13, 105 // GR_640
3706 5, 316, 20, 168 // GR_1024
3710 char *Mc_slider_bitmap[GR_NUM_RESOLUTIONS] = {
3715 // player list control thingie defs
3716 #define MULTI_CREATE_PLIST_MAX_DISPLAY 20
3717 int Multi_create_plist_select_flag; // flag indicating if we have a play selected
3718 short Multi_create_plist_select_id; // the net address of the currently selected player (for lookup)
3720 // master tracker details
3721 int Multi_create_frame_count; // framecount
3722 int Multi_create_mt_tried_login; // attempted to login this server on the MT
3724 // mission filter settings
3725 int Multi_create_filter; // what mode we're in
3727 // game/campaign list control defs
3728 int Multi_create_list_max_display[GR_NUM_RESOLUTIONS] = {
3734 int Multi_create_list_count; // number of items in listbox
3735 int Multi_create_list_mode; // 0 == mission mode, 1 == campaign mode
3736 int Multi_create_list_start; // where to start displaying from
3737 int Multi_create_list_select; // which item is currently highlighted
3738 int Multi_create_files_loaded;
3740 char Multi_create_files_array[MULTI_CREATE_MAX_LIST_ITEMS][MAX_FILENAME_LEN];
3742 int Multi_create_mission_count; // how many we have
3743 int Multi_create_campaign_count;
3744 multi_create_info Multi_create_mission_list[MULTI_CREATE_MAX_LIST_ITEMS];
3745 multi_create_info Multi_create_campaign_list[MULTI_CREATE_MAX_LIST_ITEMS];
3747 // use a pointer for the file list. Will point to either the missions or the campaigns
3748 multi_create_info *Multi_create_file_list;
3750 // LOCAL function definitions
3751 void multi_create_check_buttons();
3752 void multi_create_button_pressed(int n);
3753 void multi_create_init_as_server();
3754 void multi_create_init_as_client();
3755 void multi_create_do_netstuff();
3756 void multi_create_plist_scroll_up();
3757 void multi_create_plist_scroll_down();
3758 void multi_create_plist_process();
3759 void multi_create_plist_blit_normal();
3760 void multi_create_plist_blit_team();
3761 void multi_create_list_scroll_up();
3762 void multi_create_list_scroll_down();
3763 void multi_create_list_do();
3764 void multi_create_list_select_item(int n);
3765 void multi_create_list_blit_icons(int list_index, int y_start);
3766 void multi_create_accept_hit();
3767 void multi_create_draw_filter_buttons();
3768 void multi_create_set_selected_team(int team);
3769 short multi_create_get_mouse_id();
3770 int multi_create_ok_to_commit();
3771 int multi_create_verify_cds();
3772 void multi_create_refresh_pxo();
3773 void multi_create_sw_clicked();
3775 // since we can selectively filter out mission/campaign types we always need to map a selected index (which is relative
3776 // to the displayed list), to an absolute index (which is relative to the total file list - some of which may filtered out)
3777 void multi_create_select_to_filename(int select_index,char *filename);
3778 int multi_create_select_to_index(int select_index);
3780 int Multi_create_should_show_popup = 0;
3783 // sorting function to sort mission lists.. Basic sorting on mission name
3784 int multi_create_sort_func(const void *a, const void *b)
3786 multi_create_info *m1, *m2;
3788 m1 = (multi_create_info *)a;
3789 m2 = (multi_create_info *)b;
3791 return ( strcmp(m1->name, m2->name) );
3794 void multi_create_setup_list_data(int mode)
3796 int idx,should_sort,switched_modes;
3798 // set the current mode
3801 if((Multi_create_list_mode != mode) && (mode != -1)){
3802 Multi_create_list_mode = mode;
3805 // set up the list pointers
3806 if ( Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ) {
3807 Multi_create_file_list = Multi_create_mission_list;
3808 } else if ( Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ) {
3809 Multi_create_file_list = Multi_create_campaign_list;
3815 // get the mission count based upon the filter selected
3816 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
3817 switch(Multi_create_filter){
3818 case MISSION_TYPE_MULTI:
3819 Multi_create_list_count = Multi_create_mission_count;
3822 Multi_create_list_count = 0;
3823 // find all missions which match
3824 for(idx=0;idx<Multi_create_mission_count;idx++){
3825 if(Multi_create_mission_list[idx].flags & Multi_create_filter){
3826 Multi_create_list_count++;
3830 // if we switched modes and we have more than 0 items, sort them
3831 if(switched_modes && (Multi_create_list_count > 0)){
3836 } else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
3837 switch(Multi_create_filter){
3838 case MISSION_TYPE_MULTI:
3839 Multi_create_list_count = Multi_create_campaign_count;
3842 Multi_create_list_count = 0;
3843 // find all missions which match
3844 for(idx=0;idx<Multi_create_campaign_count;idx++){
3845 if(Multi_create_campaign_list[idx].flags & Multi_create_filter){
3846 Multi_create_list_count++;
3850 // if we switched modes and we have more than 0 items, sort them
3851 if(switched_modes && (Multi_create_list_count > 0)){
3858 // reset the list start and selected indices
3859 Multi_create_list_start = 0;
3860 Multi_create_list_select = -1;
3861 multi_create_list_select_item(Multi_create_list_start);
3863 // sort the list of missions if necessary
3865 qsort(Multi_create_file_list, Multi_create_list_count, sizeof(multi_create_info), multi_create_sort_func);
3869 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);
3872 void multi_create_game_init()
3877 // now make sure to initialze various netgame stuff based upon whether we're on a standalone or not
3878 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3879 multi_create_init_as_server();
3881 multi_create_init_as_client();
3884 // initialize the player list data
3885 Multi_create_plist_select_flag = 0;
3886 Multi_create_plist_select_id = -1;
3888 // create the interface window
3889 Multi_create_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
3890 Multi_create_window.set_mask_bmap(Multi_create_bitmap_mask_fname[gr_screen.res]);
3892 // load the background bitmap
3893 Multi_create_bitmap = bm_load(Multi_create_bitmap_fname[gr_screen.res]);
3894 if(Multi_create_bitmap < 0){
3895 // we failed to load the bitmap - this is very bad
3899 // close any previous existing instances of the chatbox and create a new one
3903 // load the help overlay
3904 help_overlay_load(MULTI_CREATE_OVERLAY);
3905 help_overlay_set_state(MULTI_CREATE_OVERLAY, 0);
3907 // initialize the common notification messaging
3908 multi_common_notify_init();
3910 // use the common interface palette
3911 multi_common_set_palette();
3913 // create the interface buttons
3914 for(idx=0; idx<MULTI_CREATE_NUM_BUTTONS; idx++){
3915 b = &Multi_create_buttons[gr_screen.res][idx];
3917 // create the object
3918 b->button.create(&Multi_create_window, "", b->x, b->y, 1, 1, ((idx == MC_SCROLL_LIST_UP) || (idx == MC_SCROLL_LIST_DOWN)), 1);
3920 // set the sound to play when highlighted
3921 b->button.set_highlight_action(common_play_highlight_sound);
3923 // set the ani for the button
3924 b->button.set_bmaps(b->filename);
3927 b->button.link_hotspot(b->hotspot);
3929 // some special case stuff for the pxo refresh button
3930 if(idx == MC_PXO_REFRESH){
3931 // if not a PXO game, or if I'm not a server disable and hide the button
3932 if(!MULTI_IS_TRACKER_GAME || !MULTIPLAYER_MASTER){
3934 b->button.disable();
3940 for(idx=0; idx<MULTI_CREATE_NUM_TEXT; idx++){
3941 Multi_create_window.add_XSTR(&Multi_create_text[gr_screen.res][idx]);
3944 // if this is a PXO game, enable the squadwar checkbox
3945 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);
3946 Multi_create_sw_checkbox.set_bmaps(Multi_create_sw_checkbox_fname[gr_screen.res], 6, 0);
3947 if(!MULTI_IS_TRACKER_GAME){
3948 Multi_create_sw_checkbox.hide();
3949 Multi_create_sw_checkbox.disable();
3953 // disable squad war button in demo
3954 Multi_create_sw_checkbox.hide();
3955 Multi_create_sw_checkbox.disable();
3958 // initialize the mission type filtering mode
3959 Multi_create_filter = MISSION_TYPE_MULTI;
3961 // initialize the list mode, and load in a list
3962 memset(Multi_create_mission_list, 0, sizeof(multi_create_info) * MULTI_CREATE_MAX_LIST_ITEMS);
3963 memset(Multi_create_campaign_list, 0, sizeof(multi_create_info) * MULTI_CREATE_MAX_LIST_ITEMS);
3964 for(idx=0; idx<MULTI_CREATE_MAX_LIST_ITEMS; idx++){
3965 Multi_create_mission_list[idx].valid_status = MVALID_STATUS_UNKNOWN;
3966 Multi_create_campaign_list[idx].valid_status = MVALID_STATUS_UNKNOWN;
3968 Multi_create_list_mode = MULTI_CREATE_SHOW_MISSIONS;
3969 Multi_create_list_start = -1;
3970 Multi_create_list_select = -1;
3971 Multi_create_list_count = 0;
3973 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);
3975 // create the player list select button
3976 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);
3977 Multi_create_player_select_button.hide();
3979 // create the mission/campaign list select button
3980 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);
3981 Multi_create_list_select_button.hide();
3983 // set hotkeys for a couple of things.
3984 Multi_create_buttons[gr_screen.res][MC_ACCEPT].button.set_hotkey(KEY_CTRLED+KEY_ENTER);
3986 // init some master tracker stuff
3987 Multi_create_frame_count = 0;
3988 Multi_create_mt_tried_login = 0;
3990 // remove campaign flags
3991 Game_mode &= ~(GM_CAMPAIGN_MODE);
3993 // send any pilots as appropriate
3994 multi_data_send_my_junk();
3995 Multi_create_file_list = Multi_create_mission_list;
3997 Multi_create_campaign_count = 0;
3998 Multi_create_mission_count = 0;
3999 Multi_create_files_loaded = 0;
4002 void multi_create_game_do()
4005 char *loading_str = XSTR("Loading", 1336);
4008 // set this if we want to show the pilot info popup
4009 Multi_create_should_show_popup = 0;
4011 // first thing is to load the files
4012 if ( !Multi_create_files_loaded ) {
4013 // if I am a client, send a list request to the server for the missions
4014 if ( MULTIPLAYER_CLIENT ) {
4015 send_mission_list_request( MISSION_LIST_REQUEST );
4019 loading_bitmap = bm_load(Multi_create_loading_fname[gr_screen.res]);
4021 // draw the background, etc
4023 GR_MAYBE_CLEAR_RES(Multi_create_bitmap);
4024 if(Multi_create_bitmap != -1){
4025 gr_set_bitmap(Multi_create_bitmap);
4029 if ( loading_bitmap > -1 ){
4030 gr_set_bitmap(loading_bitmap);
4032 gr_bitmap( Please_wait_coords[gr_screen.res][MC_X_COORD], Please_wait_coords[gr_screen.res][MC_Y_COORD] );
4034 // draw "Loading" on it
4035 gr_set_color_fast(&Color_normal);
4037 gr_get_string_size(&str_w, &str_h, loading_str);
4038 gr_string((gr_screen.max_w - str_w) / 2, (gr_screen.max_h - str_h) / 2, loading_str);
4043 multi_create_list_load_missions();
4044 multi_create_list_load_campaigns();
4046 // if this is a tracker game, validate missions
4047 if(MULTI_IS_TRACKER_GAME){
4048 multi_update_valid_missions();
4051 // update the file list
4052 multi_create_setup_list_data(MULTI_CREATE_SHOW_MISSIONS);
4055 // don't bother setting netgame state if ont the server
4056 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4057 Netgame.game_state = NETGAME_STATE_FORMING;
4058 send_netgame_update_packet();
4061 // if we're on the standalone we have to tell him that we're now in the host setup screen
4062 Net_player->state = NETPLAYER_STATE_HOST_SETUP;
4063 send_netplayer_update_packet();
4065 Multi_create_files_loaded = 1;
4068 int k = chatbox_process();
4069 k = Multi_create_window.process(k,0);
4072 // same as the cancel button
4074 if(help_overlay_active(MULTI_CREATE_OVERLAY)){
4075 help_overlay_set_state(MULTI_CREATE_OVERLAY,0);
4077 gamesnd_play_iface(SND_USER_SELECT);
4078 multi_quit_game(PROMPT_HOST);
4083 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
4084 help_overlay_set_state(MULTI_CREATE_OVERLAY, 0);
4087 // process any button clicks
4088 multi_create_check_buttons();
4090 // do any network related stuff
4091 multi_create_do_netstuff();
4093 // draw the background, etc
4095 GR_MAYBE_CLEAR_RES(Multi_create_bitmap);
4096 if(Multi_create_bitmap != -1){
4097 gr_set_bitmap(Multi_create_bitmap);
4101 // if we're not in team vs. team mode, don't draw the team buttons
4102 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
4103 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.hide();
4104 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.hide();
4105 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.disable();
4106 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.disable();
4108 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.enable();
4109 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.enable();
4110 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.unhide();
4111 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.unhide();
4114 // draw the window itself
4115 Multi_create_window.draw();
4117 gr_set_color_fast(&Color_normal);
4119 // draw Create Game text
4120 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));
4122 // draw players text
4123 gr_string(Mc_players_text[gr_screen.res][MC_X_COORD], Mc_players_text[gr_screen.res][MC_Y_COORD], XSTR("Players", 1269));
4125 // draw players text
4126 gr_string(Mc_team_text[gr_screen.res][MC_X_COORD], Mc_team_text[gr_screen.res][MC_Y_COORD], XSTR("Team", 1258));
4128 // process and display the player list
4129 // NOTE : this must be done before the buttons are checked to insure that a player hasn't dropped
4130 multi_create_plist_process();
4131 if(Netgame.type_flags & NG_TYPE_TEAM){
4132 multi_create_plist_blit_team();
4134 multi_create_plist_blit_normal();
4137 // process and display the game/campaign list
4138 multi_create_list_do();
4140 // draw the correct mission filter button
4141 multi_create_draw_filter_buttons();
4143 // display any text in the info area
4144 multi_common_render_text();
4146 // display any pending notification messages
4147 multi_common_notify_do();
4149 // force the correct mission/campaign button to light up
4150 if( Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ){
4151 Multi_create_buttons[gr_screen.res][MC_MISSION_FILTER].button.draw_forced(2);
4153 Multi_create_buttons[gr_screen.res][MC_CAMPAIGN_FILTER].button.draw_forced(2);
4156 // force draw the closed button if it is toggled on
4157 if(Netgame.flags & NG_FLAG_TEMP_CLOSED){
4158 Multi_create_buttons[gr_screen.res][MC_CLOSE].button.draw_forced(2);
4161 // process and show the chatbox thingie
4165 Multi_create_window.draw_tooltip();
4167 // display the voice status indicator
4168 multi_common_voice_display_status();
4170 // blit the help overlay if necessary
4171 help_overlay_maybe_blit(MULTI_CREATE_OVERLAY);
4174 if(MULTI_IS_TRACKER_GAME){
4175 if(Netgame.type_flags & NG_TYPE_SW){
4176 gr_set_color_fast(&Color_bright);
4178 gr_set_color_fast(&Color_normal);
4180 gr_string(Multi_create_sw_checkbox_text[gr_screen.res][0], Multi_create_sw_checkbox_text[gr_screen.res][1], "SquadWar");
4186 // if we're supposed to show the pilot info popup, do it now
4187 if(Multi_create_should_show_popup){
4188 // get the player index and address of the player item the mouse is currently over
4189 if(Multi_create_plist_select_flag){
4190 player_index = find_player_id(Multi_create_plist_select_id);
4191 if(player_index != -1){
4192 multi_pinfo_popup(&Net_players[player_index]);
4197 // increment the frame count
4198 Multi_create_frame_count++;
4201 void multi_create_game_close()
4203 // unload any bitmaps
4204 if(!bm_unload(Multi_create_bitmap)){
4205 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_create_bitmap_fname[gr_screen.res]));
4208 // unload the help overlay
4209 help_overlay_unload(MULTI_CREATE_OVERLAY);
4211 // destroy the chatbox
4214 // destroy the UI_WINDOW
4215 Multi_create_window.destroy();
4218 void multi_create_check_buttons()
4221 for(idx=0;idx<MULTI_CREATE_NUM_BUTTONS;idx++){
4222 // we only really need to check for one button pressed at a time, so we can break after
4224 if(Multi_create_buttons[gr_screen.res][idx].button.pressed()){
4225 multi_create_button_pressed(idx);
4230 // if the squad war checkbox was clicked
4231 if(Multi_create_sw_checkbox.changed()){
4232 multi_create_sw_clicked();
4236 void multi_create_button_pressed(int n)
4242 gamesnd_play_iface(SND_USER_SELECT);
4243 multi_quit_game(PROMPT_HOST);
4246 // if valid commit conditions have not been met
4247 if(!multi_create_ok_to_commit()){
4252 multi_create_accept_hit();
4257 if(!help_overlay_active(MULTI_CREATE_OVERLAY)){
4258 help_overlay_set_state(MULTI_CREATE_OVERLAY,1);
4260 help_overlay_set_state(MULTI_CREATE_OVERLAY,0);
4264 // scroll the info text box up
4265 case MC_SCROLL_INFO_UP:
4266 multi_common_scroll_text_up();
4269 // scroll the info text box down
4270 case MC_SCROLL_INFO_DOWN:
4271 multi_common_scroll_text_down();
4274 // scroll the player list up
4275 case MC_SCROLL_PLAYERS_UP:
4276 multi_create_plist_scroll_up();
4279 // scroll the player list down
4280 case MC_SCROLL_PLAYERS_DOWN:
4281 multi_create_plist_scroll_down();
4284 // scroll the game/campaign list up
4285 case MC_SCROLL_LIST_UP:
4286 multi_create_list_scroll_up();
4287 Multi_create_slider.forceUp(); // move slider up
4290 // scroll the game/campaign list down
4291 case MC_SCROLL_LIST_DOWN:
4292 multi_create_list_scroll_down();
4293 Multi_create_slider.forceDown(); // move slider down
4296 // go to the options screen
4298 gamesnd_play_iface(SND_USER_SELECT);
4299 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
4302 // show all missions
4304 if(Multi_create_filter != MISSION_TYPE_MULTI){
4305 gamesnd_play_iface(SND_USER_SELECT);
4306 Multi_create_filter = MISSION_TYPE_MULTI;
4307 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4309 gamesnd_play_iface(SND_GENERAL_FAIL);
4313 // show cooperative missions
4315 if(Multi_create_filter != MISSION_TYPE_MULTI_COOP){
4316 gamesnd_play_iface(SND_USER_SELECT);
4317 Multi_create_filter = MISSION_TYPE_MULTI_COOP;
4318 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4320 gamesnd_play_iface(SND_GENERAL_FAIL);
4324 // show team vs. team missions
4326 if(Multi_create_filter != MISSION_TYPE_MULTI_TEAMS){
4327 gamesnd_play_iface(SND_USER_SELECT);
4328 Multi_create_filter = MISSION_TYPE_MULTI_TEAMS;
4329 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4331 gamesnd_play_iface(SND_GENERAL_FAIL);
4335 // show dogfight missions
4336 case MC_SHOW_DOGFIGHT:
4337 if (Multi_create_filter != MISSION_TYPE_MULTI_DOGFIGHT){
4338 gamesnd_play_iface(SND_USER_SELECT);
4339 Multi_create_filter = MISSION_TYPE_MULTI_DOGFIGHT;
4340 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4342 gamesnd_play_iface(SND_GENERAL_FAIL);
4346 // toggle temporary netgame closed on/off
4348 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4349 Netgame.flags ^= NG_FLAG_TEMP_CLOSED;
4351 Netgame.options.flags |= MLO_FLAG_TEMP_CLOSED;
4352 multi_options_update_netgame();
4354 gamesnd_play_iface(SND_USER_SELECT);
4357 // kick the currently selected player (if possible)
4359 // lookup the player at the specified index
4360 if(Multi_create_plist_select_flag){
4361 idx = find_player_id(Multi_create_plist_select_id);
4362 // kick him - but don't ban him
4364 multi_kick_player(idx,0);
4369 // switch to individual mission mode and load in a list
4370 case MC_MISSION_FILTER:
4371 if(Multi_create_list_mode != MULTI_CREATE_SHOW_MISSIONS){
4372 Netgame.campaign_mode = MP_SINGLE;
4374 gamesnd_play_iface(SND_USER_SELECT);
4376 // update the file list
4377 multi_create_setup_list_data(MULTI_CREATE_SHOW_MISSIONS);
4379 gamesnd_play_iface(SND_GENERAL_FAIL);
4383 // switch to campaign mode and load in a list
4384 case MC_CAMPAIGN_FILTER:
4385 // switch off squad war
4386 Multi_create_sw_checkbox.set_state(0);
4387 Netgame.type_flags = NG_TYPE_COOP;
4389 if(Multi_create_list_mode != MULTI_CREATE_SHOW_CAMPAIGNS){
4390 Netgame.campaign_mode = MP_CAMPAIGN;
4392 gamesnd_play_iface(SND_USER_SELECT);
4394 // update the file list
4395 multi_create_setup_list_data(MULTI_CREATE_SHOW_CAMPAIGNS);
4397 gamesnd_play_iface(SND_GENERAL_FAIL);
4401 // attempt to set the selected player's team
4403 multi_create_set_selected_team(0);
4404 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4405 multi_team_send_update();
4409 // attempt to set the selected player's team
4411 multi_create_set_selected_team(1);
4412 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4413 multi_team_send_update();
4417 // popup the pilot info dialog for the currently selected pilot (will occur at the end of the frame)
4419 Multi_create_should_show_popup = 1;
4422 // go to the host options screen
4423 case MC_HOST_OPTIONS:
4424 gamesnd_play_iface(SND_USER_SELECT);
4425 gameseq_post_event(GS_EVENT_MULTI_HOST_OPTIONS);
4428 // refresh PXO file list
4429 case MC_PXO_REFRESH:
4430 if(!MULTI_IS_TRACKER_GAME){
4433 multi_create_refresh_pxo();
4437 gamesnd_play_iface(SND_GENERAL_FAIL);
4438 multi_common_add_notify(XSTR("Not implemented yet!",760));
4443 // do stuff like pinging servers, sending out requests, etc
4444 void multi_create_do_netstuff()
4448 // if not on a standalone
4449 void multi_create_init_as_server()
4451 // set me up as the host and master
4452 Net_player->flags |= (NETINFO_FLAG_AM_MASTER | NETINFO_FLAG_GAME_HOST);
4455 // if on a standalone
4456 void multi_create_init_as_client()
4458 Net_player->flags |= NETINFO_FLAG_GAME_HOST;
4461 // scroll up through the player list
4462 void multi_create_plist_scroll_up()
4464 gamesnd_play_iface(SND_GENERAL_FAIL);
4467 // scroll down through the player list
4468 void multi_create_plist_scroll_down()
4470 gamesnd_play_iface(SND_GENERAL_FAIL);
4473 void multi_create_plist_process()
4475 int test_count,idx,player_index;
4477 // first determine if there are 0 players in the game. This should never happen since the host is _always_ in the game
4479 for(idx=0;idx<MAX_PLAYERS;idx++){
4480 // count anyone except the standalone server (if applicable)
4481 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
4485 if(test_count <= 0){
4489 // if we had a selected item but that player has left, select myself instead
4490 if(Multi_create_plist_select_flag){
4491 player_index = find_player_id(Multi_create_plist_select_id);
4492 if(player_index == -1){
4493 Multi_create_plist_select_id = Net_player->player_id;
4496 Multi_create_plist_select_flag = 1;
4497 Multi_create_plist_select_id = Net_player->player_id;
4500 // if the player has clicked somewhere in the player list area
4501 if(Multi_create_player_select_button.pressed()){
4504 // get the player index and address of the player item the mouse is currently over
4505 player_id = multi_create_get_mouse_id();
4506 player_index = find_player_id(player_id);
4507 if(player_index != -1){
4508 Multi_create_plist_select_flag = 1;
4509 Multi_create_plist_select_id = player_id;
4514 void multi_create_plist_blit_normal()
4517 char str[CALLSIGN_LEN+5];
4518 int y_start = Mc_players_coords[gr_screen.res][MC_Y_COORD];
4521 // display all the players
4522 for(idx=0;idx<MAX_PLAYERS;idx++){
4523 // count anyone except the standalone server (if applicable)
4524 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
4528 // highlight him if he's the host
4529 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4530 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4531 gr_set_color_fast(&Color_text_active_hi);
4533 gr_set_color_fast(&Color_bright);
4536 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4537 gr_set_color_fast(&Color_text_active);
4539 gr_set_color_fast(&Color_text_normal);
4543 // optionally draw his CD status
4544 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4545 gr_set_bitmap(Multi_common_icons[MICON_CD]);
4546 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4548 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4551 // make sure the string will fit, then display it
4552 strcpy(str,Net_players[idx].player->callsign);
4553 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4554 strcat(str,XSTR("(O)",787)); // [[ Observer ]]
4556 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4557 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4564 void multi_create_plist_blit_team()
4567 char str[CALLSIGN_LEN+1];
4568 int y_start = Mc_players_coords[gr_screen.res][MC_Y_COORD];
4571 // display all the red players first
4572 for(idx=0;idx<MAX_PLAYERS;idx++){
4573 // count anyone except the standalone server (if applicable)
4574 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
4575 // reset total offset
4578 // highlight him if he's the host
4579 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4580 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4581 gr_set_color_fast(&Color_text_active_hi);
4583 // be sure to blit the correct team button
4584 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.draw_forced(2);
4586 gr_set_color_fast(&Color_bright);
4589 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4590 gr_set_color_fast(&Color_text_active);
4592 // be sure to blit the correct team button
4593 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.draw_forced(2);
4595 gr_set_color_fast(&Color_text_normal);
4599 // optionally draw his CD status
4600 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4601 gr_set_bitmap(Multi_common_icons[MICON_CD]);
4602 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4604 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4607 // blit the red team indicator
4608 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
4609 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
4610 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT]);
4611 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4613 total_offset += Multi_common_icon_dims[MICON_TEAM0_SELECT][0] + 1;
4616 if(Multi_common_icons[MICON_TEAM0] != -1){
4617 gr_set_bitmap(Multi_common_icons[MICON_TEAM0]);
4618 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4620 total_offset += Multi_common_icon_dims[MICON_TEAM0][0] + 1;
4624 // make sure the string will fit
4625 strcpy(str,Net_players[idx].player->callsign);
4626 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4627 strcat(str,XSTR("(O)",787));
4629 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4631 // display him in the correct half of the list depending on his team
4632 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4637 // display all the green players next
4638 for(idx=0;idx<MAX_PLAYERS;idx++){
4639 // count anyone except the standalone server (if applicable)
4640 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
4641 // reset total offset
4644 // highlight him if he's the host
4645 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4646 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4647 gr_set_color_fast(&Color_text_active_hi);
4649 // be sure to blit the correct team button
4650 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.draw_forced(2);
4652 gr_set_color_fast(&Color_bright);
4655 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4656 gr_set_color_fast(&Color_text_active);
4658 // be sure to blit the correct team button
4659 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.draw_forced(2);
4661 gr_set_color_fast(&Color_text_normal);
4665 // optionally draw his CD status
4666 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4667 gr_set_bitmap(Multi_common_icons[MICON_CD]);
4668 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4670 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4673 // blit the red team indicator
4674 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
4675 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
4676 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT]);
4677 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4679 total_offset += Multi_common_icon_dims[MICON_TEAM1_SELECT][0] + 1;
4682 if(Multi_common_icons[MICON_TEAM1] != -1){
4683 gr_set_bitmap(Multi_common_icons[MICON_TEAM1]);
4684 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4686 total_offset += Multi_common_icon_dims[MICON_TEAM1][0] + 1;
4690 // make sure the string will fit
4691 strcpy(str,Net_players[idx].player->callsign);
4692 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4693 strcat(str,XSTR("(O)",787));
4695 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4697 // display him in the correct half of the list depending on his team
4698 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4704 void multi_create_list_scroll_up()
4706 if(Multi_create_list_start > 0){
4707 Multi_create_list_start--;
4709 gamesnd_play_iface(SND_SCROLL);
4711 gamesnd_play_iface(SND_GENERAL_FAIL);
4715 void multi_create_list_scroll_down()
4717 if((Multi_create_list_count - Multi_create_list_start) > Multi_create_list_max_display[gr_screen.res]){
4718 Multi_create_list_start++;
4720 gamesnd_play_iface(SND_SCROLL);
4722 gamesnd_play_iface(SND_GENERAL_FAIL);
4726 // gets a list of multiplayer misisons
4727 void multi_create_list_load_missions()
4729 char *fname, mission_name[NAME_LENGTH+1];
4730 char wild_card[256];
4733 memset(wild_card, 0, 256);
4734 strcpy(wild_card, NOX("*"));
4735 strcat(wild_card, FS_MISSION_FILE_EXT);
4736 file_count = cf_get_file_list_preallocated(MULTI_CREATE_MAX_LIST_ITEMS, Multi_create_files_array, NULL, CF_TYPE_MISSIONS, wild_card);
4737 Multi_create_mission_count = 0;
4739 // maybe create a standalone dialog
4740 if(Game_mode & GM_STANDALONE_SERVER){
4741 std_create_gen_dialog("Loading missions");
4742 std_gen_set_text("Mission:", 1);
4745 for(idx = 0; idx < file_count; idx++){
4746 int flags,max_players;
4750 fname = Multi_create_files_array[idx];
4752 // tack on any necessary file extension
4753 filename = cf_add_ext( fname, FS_MISSION_FILE_EXT );
4755 // for multiplayer beta builds, only accept builtin missions
4756 #if defined(MULTIPLAYER_BETA_BUILD) || defined(FS2_DEMO)
4757 if(game_find_builtin_mission(filename) == NULL){
4760 #elif defined(PD_BUILD)
4761 if((game_find_builtin_mission(filename) == NULL) && !strstr(filename, "peterdrake")){
4766 if(Game_mode & GM_STANDALONE_SERVER){
4767 std_gen_set_text(filename, 2);
4770 flags = mission_parse_is_multi(filename, mission_name);
4772 // if the mission is a multiplayer mission, then add it to the mission list
4774 max_players = mission_parse_get_multi_mission_info( filename );
4775 m_respawn = The_mission.num_respawns;
4777 if ( Multi_create_mission_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
4778 multi_create_info *mcip;
4780 mcip = &Multi_create_mission_list[Multi_create_mission_count];
4781 strcpy(mcip->filename, filename );
4782 strcpy(mcip->name, mission_name );
4783 mcip->flags = flags;
4784 mcip->respawn = m_respawn;
4785 mcip->max_players = (ubyte)max_players;
4787 // get any additional information for possibly builtin missions
4788 fs_builtin_mission *fb = game_find_builtin_mission(filename);
4792 Multi_create_mission_count++;
4797 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);
4799 // maybe create a standalone dialog
4800 if(Game_mode & GM_STANDALONE_SERVER){
4801 std_destroy_gen_dialog();
4805 void multi_create_list_load_campaigns()
4808 int idx, file_count;
4809 int campaign_type,max_players;
4811 char wild_card[256];
4813 // maybe create a standalone dialog
4814 if(Game_mode & GM_STANDALONE_SERVER){
4815 std_create_gen_dialog("Loading campaigns");
4816 std_gen_set_text("Campaign:", 1);
4819 Multi_create_campaign_count = 0;
4820 memset(wild_card, 0, 256);
4821 strcpy(wild_card, NOX("*"));
4822 strcat(wild_card, FS_CAMPAIGN_FILE_EXT);
4823 file_count = cf_get_file_list_preallocated(MULTI_CREATE_MAX_LIST_ITEMS, Multi_create_files_array, NULL, CF_TYPE_MISSIONS, wild_card);
4824 for(idx = 0; idx < file_count; idx++){
4826 char *filename, name[NAME_LENGTH];
4828 fname = Multi_create_files_array[idx];
4830 // tack on any necessary file extension
4831 filename = cf_add_ext( fname, FS_CAMPAIGN_FILE_EXT );
4833 // for multiplayer beta builds, only accept builtin missions
4834 #if defined(MULTIPLAYER_BETA_BUILD) || defined(FS2_DEMO)
4835 if(game_find_builtin_mission(filename) == NULL){
4838 #elif defined(PD_BUILD)
4839 if((game_find_builtin_mission(filename) == NULL) && !strstr(filename, "peterdrake")){
4844 if(Game_mode & GM_STANDALONE_SERVER){
4845 std_gen_set_text(filename, 2);
4848 // if the campaign is a multiplayer campaign, then add the data to the campaign list items
4849 flags = mission_campaign_parse_is_multi( filename, name );
4850 if( flags != CAMPAIGN_TYPE_SINGLE && mission_campaign_get_info(filename,title,&campaign_type,&max_players)) {
4851 if ( Multi_create_campaign_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
4852 multi_create_info *mcip;
4854 mcip = &Multi_create_campaign_list[Multi_create_campaign_count];
4855 strcpy(mcip->filename, filename );
4856 strcpy(mcip->name, name );
4858 // setup various flags
4859 if ( flags == CAMPAIGN_TYPE_MULTI_COOP ){
4860 mcip->flags = MISSION_TYPE_MULTI_COOP | MISSION_TYPE_MULTI;
4861 } else if ( flags == CAMPAIGN_TYPE_MULTI_TEAMS ) {
4862 mcip->flags = MISSION_TYPE_MULTI_TEAMS | MISSION_TYPE_MULTI;
4864 Int3(); // bogus campaign multi type -- find allender
4867 // 0 respawns for campaign files (should be contained within the mission files themselves)
4870 // 0 max players for campaign files
4871 mcip->max_players = (unsigned char)max_players;
4873 // get any additional information for possibly builtin missions
4874 fs_builtin_mission *fb = game_find_builtin_mission(filename);
4878 Multi_create_campaign_count++;
4883 // maybe create a standalone dialog
4884 if(Game_mode & GM_STANDALONE_SERVER){
4885 std_destroy_gen_dialog();
4889 void multi_create_list_do()
4892 int start_index,stop_index;
4893 char selected_name[255];
4895 // bail early if there aren't any selectable items
4896 if(Multi_create_list_count == 0){
4900 // first check to see if the user has clicked on an item
4901 if(Multi_create_list_select_button.pressed()){
4903 Multi_create_list_select_button.get_mouse_pos(NULL,&y);
4906 // make sure we are selectedin valid indices
4907 if((item < Multi_create_list_max_display[gr_screen.res]) && (item >= 0)){
4908 item += Multi_create_list_start;
4910 if(item < Multi_create_list_count){
4911 multi_create_list_select_item(item);
4912 gamesnd_play_iface(SND_IFACE_MOUSE_CLICK);
4917 // bail early if we don't have a start position
4918 if(Multi_create_list_start == -1){
4922 // display the list of individual campaigns/missions
4924 int y_start = Mc_list_coords[gr_screen.res][MC_Y_COORD];
4926 start_index = multi_create_select_to_index(Multi_create_list_start);
4927 stop_index = Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ? Multi_create_mission_count : Multi_create_campaign_count;
4928 for(idx=start_index; idx<stop_index; idx++){
4929 // see if we should drop out
4930 if(count == Multi_create_list_max_display[gr_screen.res]){
4934 // see if we should filter out this mission
4935 if ( !(Multi_create_file_list[idx].flags & Multi_create_filter) ){
4939 // highlight the selected item
4940 multi_create_select_to_filename(Multi_create_list_select,selected_name);
4941 if(!strcmp(selected_name,Multi_create_file_list[idx].filename)){
4942 gr_set_color_fast(&Color_text_selected);
4944 gr_set_color_fast(&Color_text_normal);
4947 // draw the type icon
4948 multi_create_list_blit_icons(idx, y_start);
4950 // force fit the mission name string
4951 strcpy(selected_name,Multi_create_file_list[idx].name);
4952 gr_force_fit_string(selected_name,255,Mc_column1_w[gr_screen.res]);
4953 gr_string(Mc_mission_name_x[gr_screen.res],y_start,selected_name);
4955 // draw the max players if in mission mode
4956 sprintf(selected_name,"%d",(int)Multi_create_file_list[idx].max_players);
4957 gr_string(Mc_mission_count_x[gr_screen.res],y_start,selected_name);
4959 // force fit the mission filename string
4960 strcpy(selected_name,Multi_create_file_list[idx].filename);
4961 gr_force_fit_string(selected_name,255,Mc_column3_w[gr_screen.res]);
4962 gr_string(Mc_mission_fname_x[gr_screen.res],y_start,selected_name);
4969 // takes care of stuff like changing indices around and setting up the netgame structure
4970 void multi_create_list_select_item(int n)
4972 int abs_index,campaign_type,max_players;
4973 char title[NAME_LENGTH+1];
4974 netgame_info ng_temp;
4977 char *campaign_desc;
4979 // if not on the standalone server
4980 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4983 // on the standalone
4985 memset(&ng_temp,0,sizeof(netgame_info));
4989 if ( n != Multi_create_list_select ) {
4990 // check to see if this is a valid index, and bail if it is not
4991 abs_index = multi_create_select_to_index(n);
4992 if(abs_index == -1){
4996 Multi_create_list_select = n;
4998 // set the mission name
4999 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5000 multi_create_select_to_filename(n,ng->mission_name);
5002 multi_create_select_to_filename(n,ng->campaign_name);
5005 // make sure the netgame type is properly set
5006 int old_type = Netgame.type_flags;
5007 abs_index = multi_create_select_to_index(n);
5008 if(abs_index != -1){
5009 if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_TEAMS){
5010 // if we're in squad war mode, leave it as squad war
5011 if(old_type & NG_TYPE_SW){
5012 ng->type_flags = NG_TYPE_SW;
5014 ng->type_flags = NG_TYPE_TVT;
5016 } else if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_COOP){
5017 ng->type_flags = NG_TYPE_COOP;
5018 } else if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_DOGFIGHT){
5019 ng->type_flags = NG_TYPE_DOGFIGHT;
5023 // if we're no longer in a TvT game, just uncheck the squadwar checkbox
5024 if(!(ng->type_flags & NG_TYPE_TEAM)){
5025 Multi_create_sw_checkbox.set_state(0);
5028 // if we switched from something else to team vs. team mode, do some special processing
5029 if((ng->type_flags & NG_TYPE_TEAM) && (ng->type_flags != old_type) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
5033 switch(Multi_create_list_mode){
5034 case MULTI_CREATE_SHOW_MISSIONS:
5035 // don't forget to update the info box window thingie
5036 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5037 ship_init(); // mwa -- 10/15/97. Call this function to reset number of ships in mission
5038 ng->max_players = mission_parse_get_multi_mission_info( ng->mission_name );
5040 Assert(ng->max_players > 0);
5041 strcpy(ng->title,The_mission.name);
5043 // set the information area text
5044 multi_common_set_text(The_mission.mission_desc);
5046 // if we're on the standalone, send a request for the description
5048 send_netgame_descript_packet(&Netgame.server_addr,0);
5049 multi_common_set_text("");
5052 // set the respawns as appropriate
5053 if(Netgame.options.respawn <= Multi_create_file_list[abs_index].respawn){
5054 ng->respawn = Netgame.options.respawn;
5055 nprintf(("Network","Using netgame options for respawn count (%d %d)\n",Netgame.options.respawn,Multi_create_file_list[abs_index].respawn));
5057 ng->respawn = Multi_create_file_list[abs_index].respawn;
5058 nprintf(("Network","Using mission settings for respawn count (%d %d)\n",Netgame.options.respawn,Multi_create_file_list[abs_index].respawn));
5061 case MULTI_CREATE_SHOW_CAMPAIGNS:
5062 // if not on the standalone server
5063 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5064 // get the campaign info
5065 memset(title,0,NAME_LENGTH+1);
5066 if(!mission_campaign_get_info(ng->campaign_name,title,&campaign_type,&max_players, &campaign_desc)) {
5067 memset(ng->campaign_name,0,NAME_LENGTH+1);
5068 ng->max_players = 0;
5070 // if we successfully got the # of players
5072 memset(ng->title,0,NAME_LENGTH+1);
5073 strcpy(ng->title,title);
5074 ng->max_players = max_players;
5077 nprintf(("Network","MC MAX PLAYERS : %d\n",ng->max_players));
5079 // set the information area text
5080 // multi_common_set_text(ng->title);
5081 multi_common_set_text(campaign_desc);
5083 // if on the standalone server, send a request for the description
5085 // no descriptions currently kept for campaigns
5088 // netgame respawns are always 0 for campaigns (until the first mission is loaded)
5093 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5095 send_netgame_update_packet();
5097 // update all machines about stuff like respawns, etc.
5098 multi_options_update_netgame();
5100 multi_options_update_mission(ng, Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ? 1 : 0);
5105 void multi_create_list_blit_icons(int list_index, int y_start)
5107 multi_create_info *mcip;
5108 fs_builtin_mission *fb;
5111 // get a pointer to the list item
5112 max_index = Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ? Multi_create_mission_count - 1 : Multi_create_campaign_count - 1;
5113 if((list_index < 0) || (list_index > max_index)){
5116 mcip = &Multi_create_file_list[list_index];
5118 // blit the multiplayer type icons
5119 if(mcip->flags & MISSION_TYPE_MULTI_COOP){
5120 if(Multi_common_icons[MICON_COOP] >= 0){
5121 gr_set_bitmap(Multi_common_icons[MICON_COOP]);
5122 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5124 } else if(mcip->flags & MISSION_TYPE_MULTI_TEAMS){
5125 if(Multi_common_icons[MICON_TVT] >= 0){
5126 gr_set_bitmap(Multi_common_icons[MICON_TVT]);
5127 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5129 } else if(mcip->flags & MISSION_TYPE_MULTI_DOGFIGHT){
5130 if(Multi_common_icons[MICON_DOGFIGHT] >= 0){
5131 gr_set_bitmap(Multi_common_icons[MICON_DOGFIGHT]);
5132 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5136 // if its a valid mission, blit the valid mission icon
5137 if(MULTI_IS_TRACKER_GAME && (mcip->valid_status == MVALID_STATUS_VALID)){
5138 if(Multi_common_icons[MICON_VALID] >= 0){
5139 gr_set_bitmap(Multi_common_icons[MICON_VALID]);
5140 gr_bitmap(Mc_icon_valid_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_valid_coords[gr_screen.res][MC_Y_COORD]);
5144 // now see if its a builtin mission
5145 fb = game_find_builtin_mission(mcip->filename);
5146 // if the mission is from volition, blit the volition icon
5147 if((fb != NULL) && (fb->flags & FSB_FROM_VOLITION)){
5148 if(Multi_common_icons[MICON_VOLITION] >= 0){
5149 gr_set_bitmap(Multi_common_icons[MICON_VOLITION]);
5150 gr_bitmap(Mc_icon_volition_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_volition_coords[gr_screen.res][MC_Y_COORD]);
5155 void multi_create_accept_hit()
5157 char selected_name[255];
5158 int start_campaign = 0;
5160 // make sure all players have finished joining
5161 if(!multi_netplayer_state_check(NETPLAYER_STATE_JOINED,1)){
5162 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("Please wait until all clients have finished joining",788));
5163 gamesnd_play_iface(SND_GENERAL_FAIL);
5166 gamesnd_play_iface(SND_COMMIT_PRESSED);
5169 // do single mission stuff
5170 switch(Multi_create_list_mode){
5171 case MULTI_CREATE_SHOW_MISSIONS:
5172 if(Multi_create_list_select != -1){
5173 // set the netgame mode
5174 Netgame.campaign_mode = MP_SINGLE;
5176 // setup various filenames and mission names
5177 multi_create_select_to_filename(Multi_create_list_select,selected_name);
5178 strncpy( Game_current_mission_filename, selected_name, MAX_FILENAME_LEN );
5179 strncpy(Netgame.mission_name,selected_name,MAX_FILENAME_LEN);
5182 ml_printf(NOX("Starting single mission %s, with %d players"), Game_current_mission_filename, multi_num_players());
5184 multi_common_add_notify(XSTR("No mission selected!",789));
5189 case MULTI_CREATE_SHOW_CAMPAIGNS:
5190 // do campaign related stuff
5191 if(Multi_create_list_select != -1){
5192 // set the netgame mode
5193 Netgame.campaign_mode = MP_CAMPAIGN;
5195 // start a campaign instead of a single mission
5196 multi_create_select_to_filename(Multi_create_list_select,selected_name);
5197 multi_campaign_start(selected_name);
5201 ml_printf(NOX("Starting campaign %s, with %d players"), selected_name, multi_num_players());
5203 multi_common_add_notify(XSTR("No campaign selected!",790));
5209 // if this is a team vs team situation, lock the players send a final team update
5210 if((Netgame.type_flags & NG_TYPE_TEAM) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
5211 multi_team_host_lock_all();
5212 multi_team_send_update();
5215 // if not on the standalone, move to the mission sync state which will take care of everything
5216 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5217 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
5218 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
5220 // otherwise tell the standalone to do so
5222 // when the standalone receives this, he'll do the mission syncing himself
5223 send_mission_sync_packet(MULTI_SYNC_PRE_BRIEFING,start_campaign);
5227 void multi_create_draw_filter_buttons()
5229 // highlight the correct filter button
5230 if ( Multi_create_filter == MISSION_TYPE_MULTI ){
5231 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL].button.draw_forced(2);
5232 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_COOP ) {
5233 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 1].button.draw_forced(2);
5234 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_TEAMS ) {
5235 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 2].button.draw_forced(2);
5236 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_DOGFIGHT ){
5237 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 3].button.draw_forced(2);
5243 void multi_create_set_selected_team(int team)
5247 // if we don't currently have a player selected, don't do anything
5248 if(!Multi_create_plist_select_flag){
5249 gamesnd_play_iface(SND_GENERAL_FAIL);
5252 gamesnd_play_iface(SND_USER_SELECT);
5254 // otherwise attempt to set the team for this guy
5255 player_index = find_player_id(Multi_create_plist_select_id);
5256 if(player_index != -1){
5257 multi_team_set_team(&Net_players[player_index],team);
5261 void multi_create_handle_join(net_player *pl)
5263 // for now just play a bloop sound
5264 gamesnd_play_iface(SND_ICON_DROP_ON_WING);
5267 // fill in net address of player the mouse is over, return player index (or -1 if none)
5268 short multi_create_get_mouse_id()
5270 // determine where he clicked (y pixel value)
5272 Multi_create_player_select_button.get_mouse_pos(NULL,&y);
5274 // select things a little differently if we're in team vs. team or non-team vs. team mode
5276 if(Netgame.type_flags & NG_TYPE_TEAM){
5277 int player_index = -1;
5279 // look through all of team red first
5280 for(idx=0;idx<MAX_PLAYERS;idx++){
5281 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
5284 // if this is the _nth_ guy
5292 // if we still haven't found him yet, look through the green team
5293 if(player_index == -1){
5294 for(idx=0;idx<MAX_PLAYERS;idx++){
5295 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
5297 // if this is the _nth_ guy
5306 if(player_index != -1){
5307 return Net_players[player_index].player_id;
5310 // select the nth active player if possible, disregarding the standalone server
5311 for(idx=0;idx<MAX_PLAYERS;idx++){
5312 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
5315 // if this is the _nth_ guy
5317 return Net_players[idx].player_id;
5326 void multi_create_select_to_filename(int select_index,char *filename)
5330 // look through the mission list
5331 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5332 for(idx=0;idx<Multi_create_mission_count;idx++){
5333 if(Multi_create_file_list[idx].flags & Multi_create_filter){
5337 // if we found the item
5338 if(select_index < 0){
5339 strcpy(filename,Multi_create_file_list[idx].filename);
5344 // look through the campaign list
5345 else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
5346 for(idx=0;idx<Multi_create_campaign_count;idx++){
5349 // if we found the item
5350 if(select_index < 0){
5351 strcpy(filename,Multi_create_file_list[idx].filename);
5357 strcpy(filename,"");
5360 int multi_create_select_to_index(int select_index)
5363 int lookup_index = 0;
5365 // look through the mission list
5366 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5367 for(idx=0;idx<Multi_create_mission_count;idx++){
5368 if(Multi_create_file_list[idx].flags & Multi_create_filter){
5372 // if we found the item
5373 if(select_index < lookup_index){
5378 // look through the campaign list
5379 else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
5380 for(idx=0;idx<Multi_create_campaign_count;idx++){
5383 // if we found the item
5384 if(select_index < 0){
5393 int multi_create_ok_to_commit()
5395 int player_count, observer_count, idx;
5396 int notify_of_hacked_ships_tbl = 0;
5397 int notify_of_hacked_weapons_tbl = 0;
5398 char err_string[255];
5402 // make sure we have a valid mission selected
5403 if(Multi_create_list_select < 0){
5407 // if this is not a valid mission, let the player know
5408 abs_index = multi_create_select_to_index(Multi_create_list_select);
5413 // if we're playing with a hacked ships.tbl (on PXO)
5414 notify_of_hacked_ships_tbl = 0;
5415 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5416 if(!Game_ships_tbl_valid){
5417 notify_of_hacked_ships_tbl = 1;
5420 if(Netgame.flags & NG_FLAG_HACKED_SHIPS_TBL){
5421 notify_of_hacked_ships_tbl = 1;
5424 if(!MULTI_IS_TRACKER_GAME){
5425 notify_of_hacked_ships_tbl = 0;
5427 if(notify_of_hacked_ships_tbl){
5428 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){
5433 // if we're playing with a hacked weapons.tbl (on PXO)
5434 notify_of_hacked_weapons_tbl = 0;
5435 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5436 if(!Game_weapons_tbl_valid){
5437 notify_of_hacked_weapons_tbl = 1;
5440 if(Netgame.flags & NG_FLAG_HACKED_WEAPONS_TBL){
5441 notify_of_hacked_weapons_tbl = 1;
5444 if(!MULTI_IS_TRACKER_GAME){
5445 notify_of_hacked_weapons_tbl = 0;
5447 if(notify_of_hacked_weapons_tbl){
5448 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){
5453 // if any of the players have hacked data
5455 for(idx=0; idx<MAX_PLAYERS; idx++){
5456 // look for hacked players
5457 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAXOR)){
5461 // message everyone - haha
5462 if(Net_players[idx].player != NULL){
5463 sprintf(err_string, "%s %s", Net_players[idx].player->callsign, XSTR("has hacked tables/data", 1271));
5465 sprintf(err_string, "somebody %s", XSTR("has hacked tables/data", 1271));
5467 send_game_chat_packet(Net_player, err_string, MULTI_MSG_ALL, NULL, NULL, 1);
5470 // if we found a hacked set of data
5473 if(MULTI_IS_TRACKER_GAME){
5474 // don't allow squad war matches to continue
5475 if(Netgame.type_flags & NG_TYPE_SW){
5477 // if this is squad war, don't allow it to continue
5478 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));
5483 // otherwise, warn the players that stats will not saved
5485 // if this is squad war, don't allow it to continue
5486 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){
5491 // non-pxo, just give a notice
5493 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){
5499 // check to see that we don't have too many observers
5500 observer_count = multi_num_observers();
5501 if(observer_count > Netgame.options.max_observers){
5502 // print up the error string
5503 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);
5505 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, err_string);
5509 // check to see that we have a valid # of players for the the # of ships in the game
5510 player_count = multi_num_players();
5511 if(player_count > Netgame.max_players){
5512 // print up the error string
5513 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);
5515 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, err_string);
5519 // check to see if teams are assigned properly in a team vs. team situation
5520 if(Netgame.type_flags & NG_TYPE_TEAM){
5521 if(!multi_team_ok_to_commit()){
5522 gamesnd_play_iface(SND_GENERAL_FAIL);
5523 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("Teams and/or team captains are not assigned properly", 793));
5529 if(!multi_create_verify_cds()){
5530 gamesnd_play_iface(SND_GENERAL_FAIL);
5532 #ifdef MULTIPLAYER_BETA_BUILD
5533 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, "You need 1 CD for every player!");
5535 #ifdef DVD_MESSAGE_HACK
5536 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You need 1 DVD for every 4 players!", 794));
5538 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You need 1 CD for every 4 players!", 794));
5544 // if we're playing on the tracker
5545 if(MULTI_IS_TRACKER_GAME){
5546 #ifdef PXO_CHECK_VALID_MISSIONS
5547 if((Multi_create_file_list == Multi_create_mission_list) && (Multi_create_file_list[abs_index].valid_status != MVALID_STATUS_VALID)){
5548 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){
5555 if(!(Netgame.type_flags & NG_TYPE_SW)){
5556 // if he is playing by himself, tell him stats will not be accepted
5557 if(multi_num_players() == 1){
5558 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){
5571 int multi_create_verify_cds()
5573 int player_count = multi_num_players();
5577 // count how many cds we have
5579 for(idx=0;idx<MAX_PLAYERS;idx++){
5580 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAS_CD)){
5585 // for the beta, everyone must have a CD
5586 #ifdef MULTIPLAYER_BETA_BUILD
5587 if(multi_cd_count < player_count){
5591 // determine if we have enough
5592 float ratio = (float)player_count / (float)multi_cd_count;
5593 // greater than a 4 to 1 ratio
5599 // we meet the conditions
5603 // returns an index into Multi_create_mission_list
5604 int multi_create_lookup_mission(char *fname)
5608 for(idx=0; idx<Multi_create_mission_count; idx++){
5609 if(!stricmp(fname, Multi_create_mission_list[idx].filename)){
5614 // couldn't find the mission
5618 // returns an index into Multi_create_campaign_list
5619 int multi_create_lookup_campaign(char *fname)
5623 for(idx=0; idx<Multi_create_campaign_count; idx++){
5624 if(!stricmp(fname, Multi_create_campaign_list[idx].filename)){
5629 // couldn't find the campaign
5633 void multi_create_refresh_pxo()
5635 // delete mvalid.cfg if it exists
5636 cf_delete(MULTI_VALID_MISSION_FILE, CF_TYPE_DATA);
5638 // refresh missions from the tracker
5639 multi_update_valid_missions();
5642 void multi_create_sw_clicked()
5644 netgame_info ng_temp;
5647 int file_index = multi_create_select_to_index(Multi_create_list_select);
5649 // either a temporary netgame or the real one
5650 if(MULTIPLAYER_MASTER){
5657 // maybe switch squad war off
5658 if(!Multi_create_sw_checkbox.checked()){
5659 // if the mission selected is a coop mission, go back to coop mode
5660 Assert(file_index != -1);
5661 if(file_index == -1){
5662 ng->type_flags = NG_TYPE_COOP;
5664 if(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_TEAMS){
5665 ng->type_flags = NG_TYPE_TVT;
5666 } else if(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_DOGFIGHT){
5667 ng->type_flags = NG_TYPE_DOGFIGHT;
5669 ng->type_flags = NG_TYPE_COOP;
5672 // switch squad war on
5674 Assert(file_index != -1);
5675 if((file_index == -1) || !(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_TEAMS)){
5676 Multi_create_sw_checkbox.set_state(0);
5678 // at this point we know its safe to switch squad war on
5679 ng->type_flags = NG_TYPE_SW;
5684 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5686 send_netgame_update_packet();
5688 // update all machines about stuff like respawns, etc.
5689 multi_options_update_netgame();
5691 // on the standalone
5693 // standalone will take care of polling the usertracker
5694 multi_options_update_mission(ng, Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ? 1 : 0);
5699 // -------------------------------------------------------------------------------------------------------------
5701 // MULTIPLAYER HOST OPTIONS SCREEN
5704 #define MULTI_HO_NUM_BUTTONS 12
5705 #define MULTI_HO_NUM_RADIO_BUTTONS 10
5709 #define MULTI_HO_PALETTE "InterfacePalette"
5711 static char *Multi_ho_bitmap_fname[GR_NUM_RESOLUTIONS] = {
5712 "MultiHost", // GR_640
5713 "2_MultiHost" // GR_1024
5716 static char *Multi_ho_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
5717 "MultiHost-M", // GR_640
5718 "2_MultiHost-M" // GR_1024
5722 UI_WINDOW Multi_ho_window; // the window object for the join screen
5723 UI_INPUTBOX Multi_ho_respawns; // the # of respawns allowed in the game
5724 UI_INPUTBOX Multi_ho_time_limit; // mission time limit
5725 UI_INPUTBOX Multi_ho_voice_wait; // wait time between tokens
5726 UI_INPUTBOX Multi_ho_kill_limit; // kill limit in a furball mission
5727 UI_INPUTBOX Multi_ho_obs; // # of observers we'll allow
5728 int Multi_ho_bitmap; // the background bitmap
5730 // constants for coordinate lookup
5731 #define MULTI_HO_X_COORD 0
5732 #define MULTI_HO_Y_COORD 1
5733 #define MULTI_HO_W_COORD 2
5734 #define MULTI_HO_H_COORD 3
5735 #define MULTI_HO_TEXT_X_COORD 4
5736 #define MULTI_HO_TEXT_Y_COORD 5
5739 #define MULTI_HO_MSG_RANK 0 // highest ranking players can do messaging
5740 #define MULTI_HO_MSG_LEADER 1 // wing/team leaders can do messaging
5741 #define MULTI_HO_MSG_ANY 2 // any player can do messaging
5742 #define MULTI_HO_MSG_HOST 3 // only the host can do messaging
5743 #define MULTI_HO_END_RANK 4 // highest rank can and host can end mission
5744 #define MULTI_HO_END_LEADER 5 // wing/team leaders and host can end the mission
5745 #define MULTI_HO_END_ANY 6 // any player can end the mission
5746 #define MULTI_HO_END_HOST 7 // only host can end the mission
5747 #define MULTI_HO_VOICE_ON 8 // voice toggled on
5748 #define MULTI_HO_VOICE_OFF 9 // voice toggled off
5749 #define MULTI_HO_HOST_MODIFIES 10 // only the host or team captains can modify ships/weapons in briefing
5750 #define MULTI_HO_ACCEPT 11 // accept button
5752 ui_button_info Multi_ho_buttons[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_BUTTONS] = {
5754 // who is allowed to message
5755 ui_button_info("MH_00", 3, 160, 46, 166, 0), // highest rank
5756 ui_button_info("MH_01", 3, 179, 46, 185, 1), // team/wing leader
5757 ui_button_info("MH_02", 3, 196, 46, 203, 2), // any
5758 ui_button_info("MH_03", 3, 214, 46, 220, 3), // host
5760 // who is allowed to end the mission
5761 ui_button_info("MH_04", 3, 257, 46, 265, 4), // highest rank
5762 ui_button_info("MH_05", 3, 276, 46, 283, 5), // team/wing leader
5763 ui_button_info("MH_06", 3, 294, 46, 300, 6), // any
5764 ui_button_info("MH_07", 3, 311, 46, 317, 7), // host
5766 // voice on/off button
5767 ui_button_info("MH_09", 542, 158, 545, 185, 9),
5768 ui_button_info("MH_10", 598, 158, 604, 185, 10),
5770 // host modifies ships
5771 ui_button_info("MH_13", 542, 377, 437, 363, 13),
5774 ui_button_info("MH_14", 572, 428, 580, 414, 14),
5777 // who is allowed to message
5778 ui_button_info("2_MH_00", 5, 256, 73, 269, 0), // highest rank
5779 ui_button_info("2_MH_01", 5, 286, 73, 297, 1), // team/wing leader
5780 ui_button_info("2_MH_02", 5, 314, 73, 325, 2), // any
5781 ui_button_info("2_MH_03", 5, 341, 73, 352, 3), // host
5783 // who is allowed to end the mission
5784 ui_button_info("2_MH_04", 5, 412, 73, 425, 4), // highest rank
5785 ui_button_info("2_MH_05", 5, 442, 73, 452, 5), // team/wing leader
5786 ui_button_info("2_MH_06", 5, 470, 73, 480, 6), // any
5787 ui_button_info("2_MH_07", 5, 497, 73, 508, 7), // host
5789 // voice on/off button
5790 ui_button_info("2_MH_09", 867, 253, 872, 296, 9),
5791 ui_button_info("2_MH_10", 957, 253, 966, 296, 10),
5793 // host modifies ships
5794 ui_button_info("2_MH_13", 867, 603, 784, 581, 13),
5797 ui_button_info("2_MH_14", 916, 685, 925, 665, 14),
5800 UI_XSTR Multi_ho_text[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_BUTTONS] = {
5802 {"Highest rank", 1280, 46, 166, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_RANK].button},
5803 {"Team / wing-leader", 1281, 46, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_LEADER].button},
5804 {"Any", 1282, 46, 203, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_ANY].button},
5805 {"Host", 1283, 46, 220, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_HOST].button},
5806 {"Highest rank", 1280, 46, 265, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_RANK].button},
5807 {"Team / wing-leader", 1281, 46, 283, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_LEADER].button},
5808 {"Any", 1282, 46, 300, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_ANY].button},
5809 {"Host", 1283, 46, 317, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_HOST].button},
5810 {"On", 1285, 545, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_VOICE_ON].button},
5811 {"Off", 1286, 604, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_VOICE_OFF].button},
5812 {"Host modifies ships", 1287, 437, 363, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_HOST_MODIFIES].button},
5813 {"Exit", 1417, 572, 418, UI_XSTR_COLOR_PINK, -1, &Multi_ho_buttons[0][MULTI_HO_ACCEPT].button},
5816 {"Highest rank", 1280, 62, 269, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_RANK].button},
5817 {"Team / wing-leader", 1281, 62, 297, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_LEADER].button},
5818 {"Any", 1282, 62, 325, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_ANY].button},
5819 {"Host", 1283, 62, 352, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_HOST].button},
5820 {"Highest rank", 1280, 62, 425, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_RANK].button},
5821 {"Team / wing-leader", 1281, 62, 452, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_LEADER].button},
5822 {"Any", 1282, 62, 480, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_ANY].button},
5823 {"Host", 1283, 62, 508, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_HOST].button},
5824 {"On", 1285, 877, 294, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_VOICE_ON].button},
5825 {"Off", 1286, 967, 293, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_VOICE_OFF].button},
5826 {"Host modifies ships", 1287, 869, 589, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_HOST_MODIFIES].button},
5827 {"Exit", 1417, 953, 672, UI_XSTR_COLOR_PINK, -1, &Multi_ho_buttons[1][MULTI_HO_ACCEPT].button},
5831 // radio button controls
5832 #define MULTI_HO_NUM_RADIO_GROUPS 3
5833 #define MULTI_HO_MSG_GROUP 0 // group dealing with squadmate messaging
5834 #define MULTI_HO_END_GROUP 1 // group dealing with ending the mission
5835 #define MULTI_HO_VOICE_GROUP 2 // group dealing with voice stuff
5836 int Multi_ho_radio_groups[MULTI_HO_NUM_RADIO_GROUPS] = { // currently selected button in the radio button group
5839 int Multi_ho_radio_info[MULTI_HO_NUM_RADIO_BUTTONS][3] = { // info related to each of the radio buttons themselves
5840 // { group #, value, button id# }
5841 {0, 0, 0}, // highest ranking players can do messaging
5842 {0, 1, 1}, // wing/team leaders can do messaging
5843 {0, 2, 2}, // any player can do messaging
5844 {0, 3, 3}, // only host can do messaging
5845 {1, 0, 4}, // highest rank and host can end the mission
5846 {1, 1, 5}, // team/wing leader can end the mission
5847 {1, 2, 6}, // any player can end the mission
5848 {1, 3, 7}, // only the host can end the mission
5849 {2, 0, 8}, // voice toggled on
5850 {2, 1, 9} // voice toggled off
5854 #define MULTI_HO_NUM_SLIDERS 3
5855 #define MULTI_HO_SLIDER_VOICE_QOS 0 // voice quality of sound
5856 #define MULTI_HO_SLIDER_VOICE_DUR 1 // max duration of voice recording
5857 #define MULTI_HO_SLIDER_SKILL 2 // skill level
5864 UI_DOT_SLIDER_NEW slider; // because we have a class inside this struct, we need the constructor below..
5866 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){}
5868 ho_sliders Multi_ho_sliders[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_SLIDERS] = {
5870 ho_sliders("MH_11", 428, 214, 437, 199, 11, 19, 10), // voice qos
5871 ho_sliders("MH_12", 428, 261, 437, 246, 12, 19, 10), // voice duration
5872 ho_sliders("MH_08", 237, 454, 230, 411, 8, 36, 5), // skill level
5875 ho_sliders("2_MH_11", 684, 343, 690, 323, 11, 32, 10), // voice qos
5876 ho_sliders("2_MH_12", 685, 418, 837, 468, 12, 32, 10), // voice duration
5877 ho_sliders("2_MH_08", 379, 727, 369, 663, 8, 60, 5), // skill level
5881 int Multi_ho_mission_respawn;
5883 int Multi_ho_host_modifies;
5885 // whether or not any of the inputboxes on this screen had focus last frame
5886 int Multi_ho_lastframe_input = 0;
5888 // game information text areas
5891 #define MULTI_HO_NUM_TITLES 14
5892 UI_XSTR Multi_ho_titles[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_TITLES] = {
5894 { "AI Orders", 1289, 32, 144, UI_XSTR_COLOR_GREEN, -1, NULL },
5895 { "End Mission", 1290, 32, 242, UI_XSTR_COLOR_GREEN, -1, NULL },
5896 { "Time Limit", 1291, 32, 347, UI_XSTR_COLOR_GREEN, -1, NULL },
5897 { "Min", 1292, 74, 362, UI_XSTR_COLOR_GREEN, -1, NULL },
5898 { "Respawn Limit", 1288, 32, 378, UI_XSTR_COLOR_GREEN, -1, NULL },
5899 { "Kill Limit", 1293, 32, 409, UI_XSTR_COLOR_GREEN, -1, NULL },
5900 { "Observers", 1294, 32, 441, UI_XSTR_COLOR_GREEN, -1, NULL },
5901 { "Skill Level", 1284, 230, 411, UI_XSTR_COLOR_GREEN, -1, NULL },
5902 { "Voice Transmission", 1295, 437, 144, UI_XSTR_COLOR_GREEN, -1, NULL },
5903 { "Voice Quality", 1296, 437, 199, UI_XSTR_COLOR_GREEN, -1, NULL },
5904 { "Message Duration", 1297, 437, 246, UI_XSTR_COLOR_GREEN, -1, NULL },
5905 { "sec", 1522, 523, 292, UI_XSTR_COLOR_GREEN, -1, NULL },
5906 { "sec", 1523, 523, 332, UI_XSTR_COLOR_GREEN, -1, NULL },
5907 { "Voice Wait", 1298, 437, 313, UI_XSTR_COLOR_GREEN, -1, NULL },
5910 { "AI Orders", 1289, 48, 238, UI_XSTR_COLOR_GREEN, -1, NULL },
5911 { "End Mission", 1290, 48, 394, UI_XSTR_COLOR_GREEN, -1, NULL },
5912 { "Time Limit", 1291, 50, 568, UI_XSTR_COLOR_GREEN, -1, NULL },
5913 { "Min", 1292, 119, 581, UI_XSTR_COLOR_GREEN, -1, NULL },
5914 { "Respawn Limit", 1288, 50, 618, UI_XSTR_COLOR_GREEN, -1, NULL },
5915 { "Kill Limit", 1293, 50, 668, UI_XSTR_COLOR_GREEN, -1, NULL },
5916 { "Observers", 1294, 50, 718, UI_XSTR_COLOR_GREEN, -1, NULL },
5917 { "Skill Level", 1284, 398, 670, UI_XSTR_COLOR_GREEN, -1, NULL },
5918 { "Voice Transmission", 1295, 869, 239, UI_XSTR_COLOR_GREEN, -1, NULL },
5919 { "Voice Quality", 1296, 690, 331, UI_XSTR_COLOR_GREEN, -1, NULL },
5920 { "Message Duration", 1297, 690, 405, UI_XSTR_COLOR_GREEN, -1, NULL },
5921 { "sec", 1522, 837, 467, UI_XSTR_COLOR_GREEN, -1, NULL },
5922 { "sec", 1523, 837, 534, UI_XSTR_COLOR_GREEN, -1, NULL },
5923 { "Voice Wait", 1298, 742, 510, UI_XSTR_COLOR_GREEN, -1, NULL },
5927 // mission time limit input box
5928 int Ho_time_coords[GR_NUM_RESOLUTIONS][4] = {
5937 // furball kill limit input box
5938 int Ho_kill_coords[GR_NUM_RESOLUTIONS][4] = {
5947 // voice recording duration text display area
5948 int Ho_vd_coords[GR_NUM_RESOLUTIONS][4] = {
5957 // voice token wait input box
5958 int Ho_vw_coords[GR_NUM_RESOLUTIONS][6] = {
5967 // observer count input box
5968 int Ho_obs_coords[GR_NUM_RESOLUTIONS][4] = {
5977 // skill text description area
5978 int Ho_st_coords[GR_NUM_RESOLUTIONS][4] = {
5987 // respawn input box
5988 int Ho_rsp_coords[GR_NUM_RESOLUTIONS][6] = {
5997 // respawn max text area
5998 int Ho_max_rsp_coords[GR_NUM_RESOLUTIONS][2] = {
6007 // maximum values for various input boxes (to notify user of overruns)
6008 #define MULTI_HO_MAX_TIME_LIMIT 500
6009 #define MULTI_HO_MAX_TOKEN_WAIT 5
6010 #define MULTI_HO_MAX_KILL_LIMIT 9999
6011 #define MULTI_HO_MAX_OBS 4
6013 // LOCAL function definitions
6014 void multi_ho_check_buttons();
6015 void multi_ho_button_pressed(int n);
6016 void multi_ho_draw_radio_groups();
6017 void multi_ho_accept_hit();
6018 void multi_ho_get_options();
6019 void multi_ho_apply_options();
6020 void multi_ho_display_record_time();
6021 int multi_ho_check_values();
6022 void multi_ho_check_focus();
6023 void multi_ho_blit_max_respawns();
6024 void multi_ho_display_skill_level();
6026 void multi_host_options_init()
6030 // create the interface window
6031 Multi_ho_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
6032 Multi_ho_window.set_mask_bmap(Multi_ho_bitmap_mask_fname[gr_screen.res]);
6034 // load the background bitmap
6035 Multi_ho_bitmap = bm_load(Multi_ho_bitmap_fname[gr_screen.res]);
6036 if(Multi_ho_bitmap < 0){
6037 // we failed to load the bitmap - this is very bad
6041 // initialize the common notification messaging
6042 multi_common_notify_init();
6044 // use the common interface palette
6045 multi_common_set_palette();
6047 // create the interface buttons
6048 for(idx=0;idx<MULTI_HO_NUM_BUTTONS;idx++){
6049 // create the object
6050 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);
6052 // set the sound to play when highlighted
6053 Multi_ho_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
6055 // set the ani for the button
6056 Multi_ho_buttons[gr_screen.res][idx].button.set_bmaps(Multi_ho_buttons[gr_screen.res][idx].filename);
6058 // set the hotspot, ignoring the skill level button
6059 Multi_ho_buttons[gr_screen.res][idx].button.link_hotspot(Multi_ho_buttons[gr_screen.res][idx].hotspot);
6062 Multi_ho_window.add_XSTR(&Multi_ho_text[gr_screen.res][idx]);
6066 for(idx=0; idx<MULTI_HO_NUM_TITLES; idx++){
6067 Multi_ho_window.add_XSTR(&Multi_ho_titles[gr_screen.res][idx]);
6070 // create the interface sliders
6071 for(idx=0; idx<MULTI_HO_NUM_SLIDERS; idx++){
6072 // create the object
6073 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);
6076 // create the respawn count input box
6077 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);
6078 // if we're in campaign mode, disable it
6079 if(Netgame.campaign_mode == MP_CAMPAIGN){
6080 Multi_ho_respawns.set_text(XSTR("NA",795)); // [[ Not applicable ]]
6081 Multi_ho_respawns.disable();
6083 Multi_ho_respawns.set_valid_chars("0123456789");
6086 // create the time limit input box
6087 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);
6088 Multi_ho_time_limit.set_valid_chars("-0123456789");
6090 // create the voice token wait input box
6091 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);
6092 Multi_ho_voice_wait.set_valid_chars("01243456789");
6094 // create the furball kill limit input box
6095 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);
6096 Multi_ho_kill_limit.set_valid_chars("0123456789");
6098 // create the observer limit input box
6099 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);
6100 Multi_ho_obs.set_valid_chars("01234");
6102 // load in the current netgame defaults
6103 multi_ho_get_options();
6105 // whether or not any of the inputboxes on this screen had focus last frame
6106 Multi_ho_lastframe_input = 0;
6108 // get the # of respawns for the currently selected mission (if any)
6109 if(Multi_create_list_select != -1){
6110 int abs_index = multi_create_select_to_index(Multi_create_list_select);
6112 // if he has a valid mission selected
6114 Multi_ho_mission_respawn = (int)Multi_create_file_list[abs_index].respawn;
6116 Multi_ho_mission_respawn = -1;
6119 Multi_ho_mission_respawn = -1;
6123 void multi_ho_update_sliders()
6125 // game skill slider
6126 if (Game_skill_level != Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos) {
6127 if ( !(Netgame.type_flags & NG_TYPE_TEAM) ){
6128 Game_skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6129 gamesnd_play_iface(SND_USER_SELECT);
6131 Game_skill_level = NUM_SKILL_LEVELS / 2;
6135 // get the voice qos options
6136 if (Netgame.options.voice_qos != (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1)) {
6137 Netgame.options.voice_qos = (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1);
6138 gamesnd_play_iface(SND_USER_SELECT);
6141 // get the voice duration options
6142 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)) {
6143 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);
6144 gamesnd_play_iface(SND_USER_SELECT);
6149 void multi_host_options_do()
6154 k = Multi_ho_window.process();
6157 // process any keypresses
6160 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
6163 case KEY_CTRLED + KEY_ENTER :
6164 gamesnd_play_iface(SND_COMMIT_PRESSED);
6165 multi_ho_accept_hit();
6169 // process any button clicks
6170 multi_ho_check_buttons();
6172 // update the sliders
6173 multi_ho_update_sliders();
6175 // make sure that the chatbox inputbox and any inputbox on this screen are mutually exclusive in terms of focus
6176 multi_ho_check_focus();
6178 // draw the background, etc
6180 GR_MAYBE_CLEAR_RES(Multi_ho_bitmap);
6181 if(Multi_ho_bitmap != -1){
6182 gr_set_bitmap(Multi_ho_bitmap);
6185 Multi_ho_window.draw();
6187 // draw all the radio buttons properly
6188 multi_ho_draw_radio_groups();
6190 // display any pending notification messages
6191 multi_common_notify_do();
6193 // display the voice record time settings
6194 multi_ho_display_record_time();
6196 // maybe display the max # of respawns next to the respawn input box
6197 multi_ho_blit_max_respawns();
6199 // blit the proper skill level
6200 multi_ho_display_skill_level();
6202 // blit the "host modifies button"
6203 if(Multi_ho_host_modifies){
6204 Multi_ho_buttons[gr_screen.res][MULTI_HO_HOST_MODIFIES].button.draw_forced(2);
6207 // process and show the chatbox thingie
6211 Multi_ho_window.draw_tooltip();
6213 // display the voice status indicator
6214 multi_common_voice_display_status();
6220 void multi_host_options_close()
6222 // unload any bitmaps
6223 if(!bm_unload(Multi_ho_bitmap)){
6224 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_ho_bitmap_fname[gr_screen.res]));
6227 // destroy the UI_WINDOW
6228 Multi_ho_window.destroy();
6231 void multi_ho_check_buttons()
6234 for(idx=0;idx<MULTI_HO_NUM_BUTTONS;idx++){
6235 // we only really need to check for one button pressed at a time, so we can break after
6237 if(Multi_ho_buttons[gr_screen.res][idx].button.pressed()){
6238 multi_ho_button_pressed(idx);
6244 void multi_ho_button_pressed(int n)
6246 int radio_index,idx;
6247 int x_pixel,y_pixel;
6249 // get the pixel position of the click
6250 Multi_ho_buttons[gr_screen.res][n].button.get_mouse_pos(&x_pixel,&y_pixel);
6253 // clicked on the accept button
6254 case MULTI_HO_ACCEPT:
6255 gamesnd_play_iface(SND_COMMIT_PRESSED);
6256 multi_ho_accept_hit();
6259 // clicked on the host/captains only modify button
6260 case MULTI_HO_HOST_MODIFIES:
6261 // toggle it on or off
6262 Multi_ho_host_modifies = !Multi_ho_host_modifies;
6263 gamesnd_play_iface(SND_USER_SELECT);
6267 // look through the radio buttons and see which one this corresponds to
6269 for(idx=0;idx<MULTI_HO_NUM_RADIO_BUTTONS;idx++){
6270 if(Multi_ho_radio_info[idx][2] == n){
6275 Assert(radio_index != -1);
6277 // check to see if a radio button was pressed
6278 if(radio_index < MULTI_HO_NUM_RADIO_BUTTONS){
6279 // see if this value is already picked for this radio group
6280 if(Multi_ho_radio_groups[Multi_ho_radio_info[radio_index][0]] != Multi_ho_radio_info[radio_index][1]){
6281 gamesnd_play_iface(SND_USER_SELECT);
6282 Multi_ho_radio_groups[Multi_ho_radio_info[radio_index][0]] = Multi_ho_radio_info[radio_index][1];
6284 gamesnd_play_iface(SND_GENERAL_FAIL);
6289 void multi_ho_draw_radio_groups()
6293 // go through each item and draw it if it is the selected button in its respective group
6294 for(idx=0;idx<MULTI_HO_NUM_RADIO_BUTTONS;idx++){
6295 /// if this button is the currently selected one in its group
6296 if(Multi_ho_radio_info[idx][1] == Multi_ho_radio_groups[Multi_ho_radio_info[idx][0]]){
6297 Multi_ho_buttons[gr_screen.res][Multi_ho_radio_info[idx][2]].button.draw_forced(2);
6302 void multi_ho_accept_hit()
6306 // check the values in the input boxes
6307 if(!multi_ho_check_values()){
6311 // zero out the netgame flags
6314 // set default options
6315 Netgame.options.flags = (MSO_FLAG_INGAME_XFER | MSO_FLAG_ACCEPT_PIX);
6317 // set the squadmate messaging flags
6318 switch(Multi_ho_radio_groups[MULTI_HO_MSG_GROUP]){
6320 Netgame.options.squad_set = MSO_SQUAD_RANK;
6323 Netgame.options.squad_set = MSO_SQUAD_LEADER;
6326 Netgame.options.squad_set = MSO_SQUAD_ANY;
6329 Netgame.options.squad_set = MSO_SQUAD_HOST;
6335 // set the end mission flags
6336 switch(Multi_ho_radio_groups[MULTI_HO_END_GROUP]){
6338 Netgame.options.endgame_set = MSO_END_RANK;
6341 Netgame.options.endgame_set = MSO_END_LEADER;
6344 Netgame.options.endgame_set = MSO_END_ANY;
6347 Netgame.options.endgame_set = MSO_END_HOST;
6353 // set the voice toggle
6354 switch(Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP]){
6356 Netgame.options.flags &= ~(MSO_FLAG_NO_VOICE);
6359 Netgame.options.flags |= MSO_FLAG_NO_VOICE;
6365 // get the voice qos options
6366 Netgame.options.voice_qos = (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1);
6368 // get the voice duration options
6369 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);
6371 // set the skill level. If in team vs. team mode, preserve the old setting before saving
6372 // the pilot file. I'll bet that this doesn't work though because the pilot file gets
6373 // written in a bunch of locations....sigh.
6374 if ( !(Netgame.type_flags & NG_TYPE_TEAM) ){
6375 Game_skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6377 Game_skill_level = NUM_SKILL_LEVELS / 2;
6380 // set the netgame respawn count
6381 // maybe warn the user that respawns will not be used for a campaign mission
6382 if(Netgame.campaign_mode == MP_SINGLE){
6383 Multi_ho_respawns.get_text(resp_str);
6384 uint temp_respawn = (uint)atoi(resp_str);
6385 // if he currently has no mission selected, let the user set any # of respawns
6386 if((int)temp_respawn > Multi_ho_mission_respawn){
6387 if(Multi_ho_mission_respawn == -1){
6388 Netgame.respawn = temp_respawn;
6389 Netgame.options.respawn = temp_respawn;
6391 // this should have been taken care of by the interface code
6396 Netgame.options.respawn = temp_respawn;
6397 Netgame.respawn = temp_respawn;
6401 // get the mission time limit
6402 Multi_ho_time_limit.get_text(resp_str);
6403 int temp_time = atoi(resp_str);
6405 Netgame.options.mission_time_limit = fl2f(-1.0f);
6406 } else if(temp_time > MULTI_HO_MAX_TIME_LIMIT){
6409 Netgame.options.mission_time_limit = fl2f(60.0f * (float)temp_time);
6412 // get observer count options
6413 Multi_ho_obs.get_text(resp_str);
6414 int temp_obs = atoi(resp_str);
6415 if(temp_obs > MULTI_HO_MAX_OBS){
6418 Netgame.options.max_observers = (ubyte)temp_obs;
6420 // get the furball kill limit
6421 Multi_ho_kill_limit.get_text(resp_str);
6422 int temp_kills = atoi(resp_str);
6423 if(temp_kills > MULTI_HO_MAX_KILL_LIMIT){
6426 Netgame.options.kill_limit = temp_kills;
6428 // get the token wait limit
6429 Multi_ho_voice_wait.get_text(resp_str);
6430 int temp_wait = atoi(resp_str);
6431 if(temp_wait > MULTI_HO_MAX_TOKEN_WAIT){
6434 Netgame.options.voice_token_wait = (temp_wait * 1000);
6436 // set the netgame option
6437 Netgame.options.skill_level = (ubyte)Game_skill_level;
6439 // get whether we're in host/captains only modify mode
6440 Netgame.options.flags &= ~(MSO_FLAG_SS_LEADERS);
6441 if(Multi_ho_host_modifies){
6442 Netgame.options.flags |= MSO_FLAG_SS_LEADERS;
6445 // store these values locally
6446 memcpy(&Player->m_local_options,&Net_player->p_info.options,sizeof(multi_local_options));
6447 memcpy(&Player->m_server_options,&Netgame.options,sizeof(multi_server_options));
6448 write_pilot_file(Player);
6450 // apply any changes in settings (notify everyone of voice qos changes, etc)
6451 multi_ho_apply_options();
6453 // move back to the create game screen
6454 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
6457 void multi_ho_get_options()
6461 // set the squadmate messaging buttons
6462 switch(Netgame.options.squad_set){
6463 case MSO_SQUAD_RANK :
6464 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 0;
6466 case MSO_SQUAD_LEADER:
6467 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 1;
6470 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 2;
6472 case MSO_SQUAD_HOST:
6473 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 3;
6479 // set the mission end buttons
6480 switch(Netgame.options.endgame_set){
6482 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 0;
6484 case MSO_END_LEADER:
6485 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 1;
6488 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 2;
6491 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 3;
6497 // set the voice toggle buttons
6498 if(Netgame.options.flags & MSO_FLAG_NO_VOICE){
6499 Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP] = 1;
6501 Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP] = 0;
6504 // get the voice qos options
6505 Assert((Netgame.options.voice_qos >= 1) && (Netgame.options.voice_qos <= 10));
6506 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos = (Netgame.options.voice_qos - 1);
6508 // get the voice duration options
6509 Assert((Netgame.options.voice_record_time > 0) && (Netgame.options.voice_record_time <= MULTI_VOICE_MAX_TIME));
6510 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos = ((int)((float)Netgame.options.voice_record_time / 500.0f)) - 1;
6512 // get the current skill level
6513 Assert((Game_skill_level >= 0) && (Game_skill_level < NUM_SKILL_LEVELS));
6514 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos = Game_skill_level;
6516 // get the # of observers
6517 memset(resp_str,0,10);
6518 sprintf(resp_str,"%d",Netgame.options.max_observers);
6519 Multi_ho_obs.set_text(resp_str);
6521 // set the respawn count
6522 if(Netgame.campaign_mode == MP_SINGLE){
6523 memset(resp_str,0,10);
6524 sprintf(resp_str,"%d",Netgame.respawn);
6525 Multi_ho_respawns.set_text(resp_str);
6528 // set the mission time limit
6529 memset(resp_str,0,10);
6530 float tl = f2fl(Netgame.options.mission_time_limit);
6531 sprintf(resp_str,"%d",(int)(tl / 60.0f));
6532 Multi_ho_time_limit.set_text(resp_str);
6534 // set the furball kill limit
6535 memset(resp_str,0,10);
6536 sprintf(resp_str,"%d",Netgame.options.kill_limit);
6537 Multi_ho_kill_limit.set_text(resp_str);
6539 // set the token wait time
6540 memset(resp_str,0,10);
6541 sprintf(resp_str,"%d",Netgame.options.voice_token_wait / 1000);
6542 Multi_ho_voice_wait.set_text(resp_str);
6544 // get whether we're in host/captains only modify mode
6545 if(Netgame.options.flags & MSO_FLAG_SS_LEADERS){
6546 Multi_ho_host_modifies = 1;
6548 Multi_ho_host_modifies = 0;
6552 void multi_ho_apply_options()
6554 // if the voice qos or duration has changed, apply the change
6555 multi_voice_maybe_update_vars(Netgame.options.voice_qos,Netgame.options.voice_record_time);
6557 // send an options update
6558 multi_options_update_netgame();
6561 // display the voice record time settings
6562 void multi_ho_display_record_time()
6565 int full_seconds, half_seconds;
6568 memset(time_str,0,30);
6571 full_seconds = (((Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 500) / 1000);
6573 // get the half-seconds
6574 half_seconds = ((((Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 500) % 1000) / 500) * 5;
6576 // format the string
6577 sprintf(time_str,"%d.%d",full_seconds,half_seconds);
6578 gr_set_color_fast(&Color_bright);
6579 gr_string(Ho_vd_coords[gr_screen.res][MULTI_HO_X_COORD],Ho_vd_coords[gr_screen.res][MULTI_HO_Y_COORD],time_str);
6582 int multi_ho_check_values()
6586 memset(val_txt,0,255);
6588 // check against respawn settings
6589 if(Multi_ho_mission_respawn != -1){
6590 Multi_ho_respawns.get_text(val_txt);
6591 // if the value is invalid, let the user know
6592 if(atoi(val_txt) > Multi_ho_mission_respawn){
6593 memset(val_txt,0,255);
6594 sprintf(val_txt,XSTR("Warning\nRespawn count in greater than mission specified max (%d)",796),Multi_ho_mission_respawn);
6595 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6600 // check against mission time limit max
6601 Multi_ho_time_limit.get_text(val_txt);
6602 // if the value is invalid, force it to be valid
6603 if(atoi(val_txt) > MULTI_HO_MAX_TIME_LIMIT){
6604 memset(val_txt,0,255);
6605 sprintf(val_txt,XSTR("Warning\nMission time limit is greater than max allowed (%d)",797),MULTI_HO_MAX_TIME_LIMIT);
6606 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6610 // check against max observer limit
6611 Multi_ho_obs.get_text(val_txt);
6612 // if the value is invalid, force it to be valid
6613 if(atoi(val_txt) > MULTI_HO_MAX_OBS){
6614 memset(val_txt,0,255);
6615 sprintf(val_txt,XSTR("Warning\nObserver count is greater than max allowed (%d)",798),MULTI_HO_MAX_OBS);
6616 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6620 // check against furball kill limit
6621 Multi_ho_kill_limit.get_text(val_txt);
6622 // if the value is invalid, force it to be valid
6623 if(atoi(val_txt) > MULTI_HO_MAX_KILL_LIMIT){
6624 memset(val_txt,0,255);
6625 sprintf(val_txt,XSTR("Warning\nMission kill limit is greater than max allowed (%d)",799),MULTI_HO_MAX_KILL_LIMIT);
6626 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6630 // check against the token wait limit
6631 Multi_ho_voice_wait.get_text(val_txt);
6632 if(atoi(val_txt) > MULTI_HO_MAX_TOKEN_WAIT){
6633 memset(val_txt,0,255);
6634 sprintf(val_txt,XSTR("Warning\nvoice wait time is greater than max allowed (%d)",800),MULTI_HO_MAX_TOKEN_WAIT);
6635 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6639 // all values are valid
6643 void multi_ho_check_focus()
6645 // if an inputbox has been pressed (hit enter), lose its focus
6646 if (Multi_ho_respawns.pressed() || Multi_ho_time_limit.pressed() || Multi_ho_voice_wait.pressed() || Multi_ho_kill_limit.pressed() || Multi_ho_obs.pressed()) {
6647 Multi_ho_respawns.clear_focus();
6648 Multi_ho_time_limit.clear_focus();
6649 Multi_ho_voice_wait.clear_focus();
6650 Multi_ho_kill_limit.clear_focus();
6651 Multi_ho_obs.clear_focus();
6652 gamesnd_play_iface(SND_COMMIT_PRESSED);
6653 chatbox_set_focus();
6654 Multi_ho_lastframe_input = 0;
6656 } else if(!Multi_ho_lastframe_input) {
6657 // if we didn't have focus last frame
6658 if(Multi_ho_respawns.has_focus() || Multi_ho_time_limit.has_focus() || Multi_ho_kill_limit.has_focus() || Multi_ho_voice_wait.has_focus() ){
6659 chatbox_lose_focus();
6661 Multi_ho_lastframe_input = 1;
6664 // if we _did_ have focus last frame
6666 // if we no longer have focus on any of the input boxes, set the focus on the chatbox
6667 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()){
6668 chatbox_set_focus();
6670 // if the chatbox now has focus, clear all focus from our inputboxes
6671 else if (chatbox_has_focus()) {
6672 Multi_ho_respawns.clear_focus();
6673 Multi_ho_time_limit.clear_focus();
6674 Multi_ho_kill_limit.clear_focus();
6675 Multi_ho_voice_wait.clear_focus();
6677 Multi_ho_lastframe_input = 0;
6682 void multi_ho_blit_max_respawns()
6686 // if we're in campaign mode, do nothing
6687 if(Netgame.campaign_mode == MP_CAMPAIGN){
6691 // otherwise blit the max as specified by the current mission file
6692 sprintf(string,"(%d)",Multi_ho_mission_respawn);
6693 gr_set_color_fast(&Color_normal);
6694 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);
6697 void multi_ho_display_skill_level()
6699 int skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6702 Assert((skill_level >= 0) && (skill_level < NUM_SKILL_LEVELS));
6703 if((skill_level < 0) || (skill_level >= NUM_SKILL_LEVELS)){
6707 gr_set_color_fast(&Color_bright);
6708 gr_string(Ho_st_coords[gr_screen.res][0], Ho_st_coords[gr_screen.res][1], Skill_level_names(skill_level, 1));
6711 // -------------------------------------------------------------------------------------------------------------
6713 // MULTIPLAYER JOIN SCREEN
6716 #define MULTI_JW_NUM_BUTTONS 8
6720 #define MULTI_JW_PALETTE "InterfacePalette"
6722 static char *Multi_jw_bitmap_fname[GR_NUM_RESOLUTIONS] = {
6723 "MultiJoinWait", // GR_640
6724 "2_MultiJoinWait" // GR_1024
6727 static char *Multi_jw_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
6728 "MultiJoinWait-M", // GR_640
6729 "2_MultiJoinWait-M" // GR_1024
6735 #define MJW_SCROLL_PLAYERS_UP 0
6736 #define MJW_SCROLL_PLAYERS_DOWN 1
6739 #define MJW_PILOT_INFO 4
6740 #define MJW_SCROLL_INFO_UP 5
6741 #define MJW_SCROLL_INFO_DOWN 6
6742 #define MJW_CANCEL 7
6744 UI_WINDOW Multi_jw_window; // the window object for the join screen
6745 int Multi_jw_bitmap; // the background bitmap
6747 // constants for coordinate lookup
6748 #define MJW_X_COORD 0
6749 #define MJW_Y_COORD 1
6750 #define MJW_W_COORD 2
6751 #define MJW_H_COORD 3
6753 ui_button_info Multi_jw_buttons[GR_NUM_RESOLUTIONS][MULTI_JW_NUM_BUTTONS] = {
6755 ui_button_info("MJW_00", 1, 24, -1, -1, 0),
6756 ui_button_info("MJW_01", 1, 66, -1, -1, 1),
6757 ui_button_info("MJW_02", 30, 244, 20, 272, 2),
6758 ui_button_info("MJW_03", 84, 244, 73, 272, 3),
6759 ui_button_info("MJW_04", 139, 242, 134, 272, 4),
6760 ui_button_info("MJW_05", 1, 406, -1, -1, 5),
6761 ui_button_info("MJW_06", 1, 447, -1, -1, 6),
6762 ui_button_info("MJW_07", 577, 428, 570, 414, 7),
6765 ui_button_info("2_MJW_00", 2, 38, -1, -1, 0),
6766 ui_button_info("2_MJW_01", 2, 106, -1, -1, 1),
6767 ui_button_info("2_MJW_02", 48, 390, 47, 435, 2),
6768 ui_button_info("2_MJW_03", 134, 390, 133, 435, 3),
6769 ui_button_info("2_MJW_04", 223, 388, 225, 435, 4),
6770 ui_button_info("2_MJW_05", 2, 649, -1, -1, 5),
6771 ui_button_info("2_MJW_06", 2, 715, -1, -1, 6),
6772 ui_button_info("2_MJW_07", 923, 685, 931, 667, 7),
6776 #define MULTI_JW_NUM_TEXT 7
6778 UI_XSTR Multi_jw_text[GR_NUM_RESOLUTIONS][MULTI_JW_NUM_TEXT] = {
6780 { "Team 1", 1308, 20, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_TEAM0].button },
6781 { "Team 2", 1309, 73, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_TEAM1].button },
6782 { "Pilot", 1310, 134, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_PILOT_INFO].button },
6783 { "Info", 1311, 134, 283, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_PILOT_INFO].button },
6784 { "Cancel", 387, 570, 414, UI_XSTR_COLOR_PINK, -1, &Multi_jw_buttons[0][MJW_CANCEL].button },
6785 { "Players", 1269, 38, 8, UI_XSTR_COLOR_GREEN, -1, NULL },
6786 { "Choose Team", 1312, 27, 231, UI_XSTR_COLOR_GREEN, -1, NULL },
6789 { "Team 1", 1308, 47, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_TEAM0].button },
6790 { "Team 2", 1309, 133, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_TEAM1].button },
6791 { "Pilot", 1310, 225, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_PILOT_INFO].button },
6792 { "Info", 1311, 225, 446, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_PILOT_INFO].button },
6793 { "Cancel", 387, 931, 667, UI_XSTR_COLOR_PINK, -1, &Multi_jw_buttons[1][MJW_CANCEL].button },
6794 { "Players", 1269, 165, 12, UI_XSTR_COLOR_GREEN, -1, NULL },
6795 { "Choose Team", 1312, 45, 373, UI_XSTR_COLOR_GREEN, -1, NULL },
6799 int Mjw_players_coords[GR_NUM_RESOLUTIONS][4] = {
6808 int Mjw_mission_name_coords[GR_NUM_RESOLUTIONS][2] = {
6817 // squad war checkbox
6818 UI_CHECKBOX Multi_jw_sw_checkbox;
6819 char *Multi_jw_sw_checkbox_fname[GR_NUM_RESOLUTIONS] = {
6823 int Multi_jw_sw_checkbox_coords[GR_NUM_RESOLUTIONS][2] = {
6831 int Multi_jw_sw_checkbox_text[GR_NUM_RESOLUTIONS][2] = {
6841 // player list control thingie defs
6842 #define MULTI_JW_PLIST_MAX_DISPLAY 19
6843 int Multi_jw_plist_select_flag; // indicates whether we currently have a selected player
6844 short Multi_jw_plist_select_id; // id of the current selected player
6845 UI_BUTTON Multi_jw_plist_select_button; // for selecting a player
6847 int Multi_jw_should_show_popup = 0;
6849 // LOCAL function definitions
6850 void multi_jw_check_buttons();
6851 void multi_jw_button_pressed(int n);
6852 void multi_jw_do_netstuff();
6853 void multi_jw_scroll_players_up();
6854 void multi_jw_scroll_players_down();
6855 void multi_jw_plist_process();
6856 void multi_jw_plist_blit_normal();
6857 void multi_jw_plist_blit_team();
6858 short multi_jw_get_mouse_id();
6860 void multi_game_client_setup_init()
6864 // create the interface window
6865 Multi_jw_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
6866 Multi_jw_window.set_mask_bmap(Multi_jw_bitmap_mask_fname[gr_screen.res]);
6868 // load the background bitmap
6869 Multi_jw_bitmap = bm_load(Multi_jw_bitmap_fname[gr_screen.res]);
6870 if(Multi_jw_bitmap < 0){
6871 // we failed to load the bitmap - this is very bad
6875 // initialize the player list data
6876 Multi_jw_plist_select_flag = 0;
6877 Multi_jw_plist_select_id = -1;
6879 // kill any old instances of the chatbox and create a new one
6881 chatbox_create(CHATBOX_FLAG_BIG | CHATBOX_FLAG_DRAW_BOX | CHATBOX_FLAG_BUTTONS);
6883 // initialize the common notification messaging
6884 multi_common_notify_init();
6886 // initialize the common mission info display area.
6887 multi_common_set_text("");
6889 // use the common interface palette
6890 multi_common_set_palette();
6892 // create the interface buttons
6893 for(idx=0; idx<MULTI_JW_NUM_BUTTONS; idx++){
6894 // create the object
6895 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);
6897 // set the sound to play when highlighted
6898 Multi_jw_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
6900 // set the ani for the button
6901 Multi_jw_buttons[gr_screen.res][idx].button.set_bmaps(Multi_jw_buttons[gr_screen.res][idx].filename);
6904 Multi_jw_buttons[gr_screen.res][idx].button.link_hotspot(Multi_jw_buttons[gr_screen.res][idx].hotspot);
6907 // if this is a PXO game, enable the squadwar checkbox
6908 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);
6909 Multi_jw_sw_checkbox.set_bmaps(Multi_jw_sw_checkbox_fname[gr_screen.res], 6, 0);
6910 Multi_jw_sw_checkbox.disable();
6911 if(!MULTI_IS_TRACKER_GAME){
6912 Multi_jw_sw_checkbox.hide();
6916 for(idx=0; idx<MULTI_JW_NUM_TEXT; idx++){
6917 Multi_jw_window.add_XSTR(&Multi_jw_text[gr_screen.res][idx]);
6920 // create the player select list button and hide it
6921 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);
6922 Multi_jw_plist_select_button.hide();
6925 Multi_jw_buttons[gr_screen.res][MJW_CANCEL].button.set_hotkey(KEY_ESC);
6927 // remove campaign flags
6928 Game_mode &= ~(GM_CAMPAIGN_MODE);
6930 // tell the server we have finished joining
6931 Net_player->state = NETPLAYER_STATE_JOINED;
6932 send_netplayer_update_packet();
6935 ml_printf(NOX("Joined netgame %s"), Netgame.name);
6937 // send any appropriate files
6938 multi_data_send_my_junk();
6941 void multi_game_client_setup_do_frame()
6944 int k = chatbox_process();
6945 char mission_text[255];
6946 k = Multi_jw_window.process(k,0);
6948 Multi_jw_should_show_popup = 0;
6950 // process any button clicks
6951 multi_jw_check_buttons();
6953 // do any network related stuff
6954 multi_jw_do_netstuff();
6956 // draw the background, etc
6958 GR_MAYBE_CLEAR_RES(Multi_jw_bitmap);
6959 if(Multi_jw_bitmap != -1){
6960 gr_set_bitmap(Multi_jw_bitmap);
6964 // if we're not in team vs. team mode, don't draw the team buttons
6965 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
6966 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.hide();
6967 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.hide();
6968 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.disable();
6969 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.disable();
6971 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.enable();
6972 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.enable();
6973 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.unhide();
6974 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.unhide();
6977 if(MULTI_IS_TRACKER_GAME){
6978 // maybe check the squadwar button
6979 if(Netgame.type_flags & NG_TYPE_SW){
6980 Multi_jw_sw_checkbox.set_state(1);
6981 gr_set_color_fast(&Color_bright);
6983 Multi_jw_sw_checkbox.set_state(0);
6984 gr_set_color_fast(&Color_normal);
6987 gr_string(Multi_jw_sw_checkbox_text[gr_screen.res][0], Multi_jw_sw_checkbox_text[gr_screen.res][1], "SquadWar");
6990 // draw the UI window
6991 Multi_jw_window.draw();
6993 // process and display the player list
6994 // NOTE : this must be done before the buttons are checked to insure that a player hasn't dropped
6995 multi_jw_plist_process();
6996 if(Netgame.type_flags & NG_TYPE_TEAM){
6997 multi_jw_plist_blit_team();
6999 multi_jw_plist_blit_normal();
7002 // display any text in the info area
7003 multi_common_render_text();
7005 // display any pending notification messages
7006 multi_common_notify_do();
7008 // blit the mission filename if possible
7009 if(Netgame.campaign_mode){
7010 if(strlen(Netgame.campaign_name) > 0){
7011 strcpy(mission_text,Netgame.campaign_name);
7013 if(strlen(Netgame.title) > 0){
7014 strcat(mission_text,", ");
7015 strcat(mission_text,Netgame.title);
7018 gr_set_color_fast(&Color_bright_white);
7019 gr_string(Mjw_mission_name_coords[gr_screen.res][MJW_X_COORD],Mjw_mission_name_coords[gr_screen.res][MJW_Y_COORD],mission_text);
7022 if(strlen(Netgame.mission_name) > 0){
7023 strcpy(mission_text,Netgame.mission_name);
7025 if(strlen(Netgame.title) > 0){
7026 strcat(mission_text,", ");
7027 strcat(mission_text,Netgame.title);
7030 gr_set_color_fast(&Color_bright_white);
7031 gr_string(Mjw_mission_name_coords[gr_screen.res][MJW_X_COORD],Mjw_mission_name_coords[gr_screen.res][MJW_Y_COORD],mission_text);
7035 // process and show the chatbox thingie
7039 Multi_jw_window.draw_tooltip();
7041 // display the voice status indicator
7042 multi_common_voice_display_status();
7047 // if we're supposed to be displaying a pilot info popup
7048 if(Multi_jw_should_show_popup){
7049 player_index = find_player_id(Multi_jw_plist_select_id);
7050 if(player_index != -1){
7051 multi_pinfo_popup(&Net_players[player_index]);
7056 void multi_game_client_setup_close()
7058 // unload any bitmaps
7059 if(!bm_unload(Multi_jw_bitmap)){
7060 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_jw_bitmap_fname[gr_screen.res]));
7063 // destroy the chatbox
7066 // destroy the UI_WINDOW
7067 Multi_jw_window.destroy();
7070 if(Netgame.game_state == NETGAME_STATE_MISSION_SYNC){
7071 gamesnd_play_iface(SND_COMMIT_PRESSED);
7076 void multi_jw_check_buttons()
7079 for(idx=0;idx<MULTI_JW_NUM_BUTTONS;idx++){
7080 // we only really need to check for one button pressed at a time, so we can break after
7082 if(Multi_jw_buttons[gr_screen.res][idx].button.pressed()){
7083 multi_jw_button_pressed(idx);
7089 void multi_jw_button_pressed(int n)
7093 gamesnd_play_iface(SND_USER_SELECT);
7094 multi_quit_game(PROMPT_CLIENT);
7096 case MJW_SCROLL_PLAYERS_UP:
7097 multi_jw_scroll_players_up();
7099 case MJW_SCROLL_PLAYERS_DOWN:
7100 multi_jw_scroll_players_down();
7102 case MJW_SCROLL_INFO_UP:
7103 multi_common_scroll_text_up();
7105 case MJW_SCROLL_INFO_DOWN:
7106 multi_common_scroll_text_down();
7109 // request to set myself to team 0
7111 gamesnd_play_iface(SND_USER_SELECT);
7112 multi_team_set_team(Net_player,0);
7115 // request to set myself to team 1
7117 gamesnd_play_iface(SND_USER_SELECT);
7118 multi_team_set_team(Net_player,1);
7122 case MJW_PILOT_INFO:
7123 Multi_jw_should_show_popup = 1;
7127 multi_common_add_notify(XSTR("Not implemented yet!",760));
7132 // do stuff like pinging servers, sending out requests, etc
7133 void multi_jw_do_netstuff()
7137 void multi_jw_scroll_players_up()
7139 gamesnd_play_iface(SND_GENERAL_FAIL);
7142 // scroll down through the player list
7143 void multi_jw_scroll_players_down()
7145 gamesnd_play_iface(SND_GENERAL_FAIL);
7148 void multi_jw_plist_process()
7150 int test_count,player_index,idx;
7152 // first determine if there are 0 players in the game. This should never happen since the host is _always_ in the game
7154 for(idx=0;idx<MAX_PLAYERS;idx++){
7155 // count anyone except the standalone server (if applicable)
7156 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7160 if(test_count <= 0){
7164 // if we had a selected item but that player has left, select myself instead
7165 if(Multi_jw_plist_select_flag){
7166 player_index = find_player_id(Multi_jw_plist_select_id);
7167 if(player_index == -1){
7168 Multi_jw_plist_select_id = Net_player->player_id;
7171 Multi_jw_plist_select_flag = 1;
7172 Multi_jw_plist_select_id = Net_player->player_id;
7175 // if the player has clicked somewhere in the player list area
7176 if(Multi_jw_plist_select_button.pressed()){
7180 player_id = multi_jw_get_mouse_id();
7181 player_index = find_player_id(player_id);
7182 if(player_index != -1){
7183 Multi_jw_plist_select_id = player_id;
7184 Multi_jw_plist_select_flag = 1;
7189 void multi_jw_plist_blit_normal()
7192 char str[CALLSIGN_LEN+1];
7193 int y_start = Mjw_players_coords[gr_screen.res][MJW_Y_COORD];
7196 // display all the players
7197 for(idx=0;idx<MAX_PLAYERS;idx++){
7198 // reset total offset
7201 // count anyone except the standalone server (if applicable)
7202 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7203 // highlight him if he's the host
7204 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7205 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7206 gr_set_color_fast(&Color_text_active_hi);
7208 gr_set_color_fast(&Color_bright);
7211 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7212 gr_set_color_fast(&Color_text_active);
7214 gr_set_color_fast(&Color_text_normal);
7218 // optionally draw his CD status
7219 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7220 gr_set_bitmap(Multi_common_icons[MICON_CD]);
7221 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7223 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7226 // make sure the string will fit, then display it
7227 strcpy(str,Net_players[idx].player->callsign);
7228 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
7231 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7232 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7239 void multi_jw_plist_blit_team()
7242 char str[CALLSIGN_LEN+1];
7243 int y_start = Mjw_players_coords[gr_screen.res][MJW_Y_COORD];
7246 // always blit the proper team button based on _my_ team status
7247 if(Net_player->p_info.team == 0){
7248 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.draw_forced(2);
7250 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.draw_forced(2);
7253 // display all the red players first
7254 for(idx=0;idx<MAX_PLAYERS;idx++){
7255 // reset total offset
7258 // count anyone except the standalone server (if applicable)
7259 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
7260 // highlight him if he's the host
7261 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7262 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7263 gr_set_color_fast(&Color_text_active_hi);
7265 gr_set_color_fast(&Color_bright);
7268 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7269 gr_set_color_fast(&Color_text_active);
7271 gr_set_color_fast(&Color_text_normal);
7275 // optionally draw his CD status
7276 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7277 gr_set_bitmap(Multi_common_icons[MICON_CD]);
7278 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7280 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7283 // blit the red team indicator
7284 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
7285 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
7286 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT]);
7287 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7289 total_offset += Multi_common_icon_dims[MICON_TEAM0_SELECT][0] + 1;
7292 if(Multi_common_icons[MICON_TEAM0] != -1){
7293 gr_set_bitmap(Multi_common_icons[MICON_TEAM0]);
7294 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7296 total_offset += Multi_common_icon_dims[MICON_TEAM0][0] + 1;
7300 // make sure the string will fit
7301 strcpy(str,Net_players[idx].player->callsign);
7302 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7304 // display him in the correct half of the list depending on his team
7305 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7310 // display all the green players next
7311 for(idx=0;idx<MAX_PLAYERS;idx++){
7312 // reset total offset
7315 // count anyone except the standalone server (if applicable)
7316 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
7317 // highlight him if he's the host
7318 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7319 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7320 gr_set_color_fast(&Color_text_active_hi);
7322 gr_set_color_fast(&Color_bright);
7325 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7326 gr_set_color_fast(&Color_text_active);
7328 gr_set_color_fast(&Color_text_normal);
7332 // optionally draw his CD status
7333 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7334 gr_set_bitmap(Multi_common_icons[MICON_CD]);
7335 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7337 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7340 // blit the red team indicator
7341 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
7342 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
7343 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT]);
7344 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7346 total_offset += Multi_common_icon_dims[MICON_TEAM1_SELECT][0] + 1;
7349 if(Multi_common_icons[MICON_TEAM1] != -1){
7350 gr_set_bitmap(Multi_common_icons[MICON_TEAM1]);
7351 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7353 total_offset += Multi_common_icon_dims[MICON_TEAM1][0] + 1;
7357 // make sure the string will fit
7358 strcpy(str,Net_players[idx].player->callsign);
7359 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
7362 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7364 // display him in the correct half of the list depending on his team
7365 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7371 void multi_jw_handle_join(net_player *pl)
7373 // for now just play a bloop sound
7374 gamesnd_play_iface(SND_ICON_DROP_ON_WING);
7377 short multi_jw_get_mouse_id()
7379 // determine where he clicked (y pixel value)
7381 Multi_jw_plist_select_button.get_mouse_pos(NULL,&y);
7383 // select things a little differently if we're in team vs. team or non-team vs. team mode
7385 if(Netgame.type_flags & NG_TYPE_TEAM){
7386 int player_index = -1;
7388 // look through all of team red first
7389 for(idx=0;idx<MAX_PLAYERS;idx++){
7390 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
7393 // if this is the _nth_ guy
7401 // if we still haven't found him yet, look through the green team
7402 if(player_index == -1){
7403 for(idx=0;idx<MAX_PLAYERS;idx++){
7404 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
7406 // if this is the _nth_ guy
7414 if(player_index != -1){
7415 return Net_players[idx].player_id;
7418 // select the nth active player if possible, disregarding the standalone server
7419 for(idx=0;idx<MAX_PLAYERS;idx++){
7420 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7423 // if this is the _nth_ guy
7425 return Net_players[idx].player_id;
7436 // -------------------------------------------------------------------------------------------------------------
7438 // MULTIPLAYER WAIT/SYNCH SCREEN
7441 #define MULTI_SYNC_HOST_COUNT 4 // host uses 4 buttons (and sometimes 5)
7442 #define MULTI_SYNC_CLIENT_COUNT 3 // client only uses 3 buttons
7444 char *Multi_sync_bitmap_fname[GR_NUM_RESOLUTIONS] = {
7445 "MultiSynch", // GR_640
7446 "2_MultiSynch" // GR_1024
7449 char *Multi_sync_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
7450 "MultiSynch-M", // GR_640
7451 "2_MultiSynch-M" // GR_1024
7456 // constants for coordinate lookup
7457 #define MS_X_COORD 0
7458 #define MS_Y_COORD 1
7459 #define MS_W_COORD 2
7460 #define MS_H_COORD 3
7462 UI_WINDOW Multi_sync_window; // the window object for the join screen
7463 int Multi_sync_button_count; // the # of buttons to use for this instance of mission sync
7464 int Multi_sync_bitmap; // the background bitmap
7467 #define MULTI_SYNC_NUM_BUTTONS 5
7468 #define MS_SCROLL_INFO_UP 0
7469 #define MS_SCROLL_INFO_DOWN 1
7473 ui_button_info Multi_sync_buttons[GR_NUM_RESOLUTIONS][MULTI_SYNC_NUM_BUTTONS] = {
7475 ui_button_info("MS_00", 1, 404, -1, -1, 0),
7476 ui_button_info("MS_01", 1, 446, -1, -1, 1),
7477 ui_button_info("MS_03", 518, 426, 519, 416, 3),
7478 ui_button_info("MS_02", 469, 426, 479, 416, 2),
7479 ui_button_info("MS_04", 571, 420, 577, 416, 4),
7482 ui_button_info("2_MS_00", 2, 647, -1, -1, 0),
7483 ui_button_info("2_MS_01", 2, 713, -1, -1, 1),
7484 ui_button_info("2_MS_03", 829, 682, 831, 667, 3),
7485 ui_button_info("2_MS_02", 751, 682, 766, 667, 2),
7486 ui_button_info("2_MS_04", 914, 672, 924, 667, 4),
7491 #define MULTI_SYNC_NUM_TEXT 5
7493 #define MST_LAUNCH 2
7494 UI_XSTR Multi_sync_text[GR_NUM_RESOLUTIONS][MULTI_SYNC_NUM_TEXT] = {
7496 { "Kick", 1266, 479, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_KICK].button },
7497 { "Cancel", 387, 519, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_CANCEL].button },
7498 { "Launch", 801, 577, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_LAUNCH].button },
7499 { "Players", 1269, 23, 133, UI_XSTR_COLOR_GREEN, -1, NULL },
7500 { "Status", 1304, 228, 133, UI_XSTR_COLOR_GREEN, -1, NULL }
7503 { "Kick", 1266, 766, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_KICK].button },
7504 { "Cancel", 387, 831, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_CANCEL].button },
7505 { "Launch", 801, 924, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_LAUNCH].button },
7506 { "Players", 1269, 38, 214, UI_XSTR_COLOR_GREEN, -1, NULL },
7507 { "Status", 1304, 366, 214, UI_XSTR_COLOR_GREEN, -1, NULL }
7512 int Ms_status_coords[GR_NUM_RESOLUTIONS][4] = {
7521 // player status coords
7522 int Ms_status2_coords[GR_NUM_RESOLUTIONS][4] = {
7531 int Ms_cd_icon_offset[GR_NUM_RESOLUTIONS] = {
7536 int Ms_team_icon_offset[GR_NUM_RESOLUTIONS] = {
7541 // player currently selected, index into Net_players[]
7542 int Multi_sync_player_select = -1;
7544 // player list control thingie defs
7545 #define MULTI_SYNC_PLIST_MAX_DISPLAY 15
7546 int Multi_sync_plist_start; // where to start displaying from
7547 int Multi_sync_plist_count; // how many we have
7549 // list select button
7550 UI_BUTTON Multi_sync_plist_button;
7552 int Multi_sync_mode = -1;
7554 #define MULTI_SYNC_COUNTDOWN_TIME 5 // in seconds
7555 float Multi_sync_countdown_timer;
7556 int Multi_sync_countdown = -1;
7558 int Multi_launch_button_created;
7561 // countdown animation timer
7562 char* Multi_sync_countdown_fname[GR_NUM_RESOLUTIONS] = {
7564 "2_Count" // GR_1024
7567 int Multi_sync_countdown_coords[GR_NUM_RESOLUTIONS][2] = {
7578 anim *Multi_sync_countdown_anim = NULL;
7579 anim_instance *Multi_sync_countdown_instance = NULL;
7582 // PREBRIEFING STUFF
7583 // syncing flags used by the server
7584 int Mission_sync_flags = 0;
7585 #define MS_FLAG_SENT_FILESIG (1<<0) // sent filesig requests
7586 #define MS_FLAG_SENT_LOAD (1<<1) // sent load packets
7587 #define MS_FLAG_PUSHED_BRIEFING (1<<2) // pushed everyone else into the briefing
7588 #define MS_FLAG_POST_DATA (1<<3) // sent the post data block
7589 #define MS_FLAG_WSS_SLOTS (1<<4) // all players have received wss slot data
7590 #define MS_FLAG_PSETTINGS (1<<5) // send the player settings packet
7591 #define MS_FLAG_MT_STATS_START (1<<6) // server has started getting player stats from the tracker
7592 #define MS_FLAG_MT_STATS_DONE (1<<7) // server has finished getting player stats from the tracker (success or fail)
7593 #define MS_FLAG_TS_SLOTS (1<<8) // team/ship slots have been sent
7594 #define MS_FLAG_DATA_DONE (1<<9) // done transferring all necessary data
7595 #define MS_FLAG_CAMP_DONE (1<<10) // send campaign pool/goal/event stuff
7597 // POSTBRIEFING STUFF
7598 int Multi_state_timestamp;
7599 int Multi_sync_launch_pressed;
7601 // LOCAL function definitions
7602 void multi_sync_check_buttons();
7603 void multi_sync_button_pressed(int n);
7604 void multi_sync_scroll_info_up();
7605 void multi_sync_scroll_info_down();
7606 void multi_sync_display_name(char *name,int index,int np_index); // display info on the left hand portion of the status window thingie
7607 void multi_sync_display_status(char *status,int index); // display info on the right hand portion of the status window thingie
7608 void multi_sync_force_start_pre();
7609 void multi_sync_force_start_post();
7610 void multi_sync_launch();
7611 void multi_sync_create_launch_button();
7612 void multi_sync_blit_screen_all();
7613 void multi_sync_handle_plist();
7615 void multi_sync_common_init();
7616 void multi_sync_common_do();
7617 void multi_sync_common_close();
7619 void multi_sync_pre_init();
7620 void multi_sync_pre_do();
7621 void multi_sync_pre_close();
7623 void multi_sync_post_init();
7624 void multi_sync_post_do();
7625 void multi_sync_post_close();
7630 // perform the correct init functions
7631 void multi_sync_init()
7633 Multi_sync_countdown = -1;
7637 // reset all timestamp
7638 multi_reset_timestamps();
7640 extern int Player_multi_died_check;
7641 Player_multi_died_check = -1;
7643 if(!(Game_mode & GM_STANDALONE_SERVER)){
7644 multi_sync_common_init();
7647 switch(Multi_sync_mode){
7648 case MULTI_SYNC_PRE_BRIEFING:
7649 multi_sync_pre_init();
7651 case MULTI_SYNC_POST_BRIEFING:
7652 multi_sync_post_init();
7654 case MULTI_SYNC_INGAME:
7655 multi_ingame_sync_init();
7660 // perform the correct do frame functions
7661 void multi_sync_do()
7663 if(!(Game_mode & GM_STANDALONE_SERVER)){
7664 multi_sync_common_do();
7667 // if the netgame is ending, don't do any sync processing
7668 if(multi_endgame_ending()){
7672 // process appropriateliy
7673 switch(Multi_sync_mode){
7674 case MULTI_SYNC_PRE_BRIEFING:
7675 multi_sync_pre_do();
7677 case MULTI_SYNC_POST_BRIEFING:
7678 multi_sync_post_do();
7680 case MULTI_SYNC_INGAME:
7681 multi_ingame_sync_do();
7684 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
7685 if(Multi_sync_bitmap != -1){
7686 gr_set_bitmap(Multi_sync_bitmap);
7689 Multi_sync_window.draw();
7691 multi_sync_blit_screen_all();
7698 // perform the correct close functions
7699 void multi_sync_close()
7701 switch(Multi_sync_mode){
7702 case MULTI_SYNC_PRE_BRIEFING:
7703 multi_sync_pre_close();
7705 case MULTI_SYNC_POST_BRIEFING:
7706 multi_sync_post_close();
7708 case MULTI_SYNC_INGAME:
7709 multi_ingame_sync_close();
7713 if(!(Game_mode & GM_STANDALONE_SERVER)){
7714 multi_sync_common_close();
7718 char *multi_sync_tooltip_handler(char *str)
7720 if (!stricmp(str, NOX("@launch"))) {
7721 if (Multi_launch_button_created){
7722 return XSTR("Launch",801);
7729 void multi_sync_common_init()
7733 // create the interface window
7734 Multi_sync_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
7735 Multi_sync_window.set_mask_bmap(Multi_sync_bitmap_mask_fname[gr_screen.res]);
7736 Multi_sync_window.tooltip_handler = multi_sync_tooltip_handler;
7738 // load the background bitmap
7739 Multi_sync_bitmap = bm_load(Multi_sync_bitmap_fname[gr_screen.res]);
7740 if (Multi_sync_bitmap < 0) {
7741 // we failed to load the bitmap - this is very bad
7745 // initialize the player list data
7746 Multi_sync_plist_start = 0;
7747 Multi_sync_plist_count = 1; // we can pretty safely assume that there's one player in the game - me.
7749 Multi_launch_button_created = 0;
7751 // create the chatbox thingie (shouldn't be necesary to do this, but we'll put it in for good measure)
7754 // force the chatbox to be small
7755 chatbox_force_small();
7757 // initialize the common notification messaging
7758 multi_common_notify_init();
7760 // initialize the common mission info display area.
7761 multi_common_set_text("");
7763 // use the common interface palette
7764 multi_common_set_palette();
7766 // don't select any player yet.
7767 Multi_sync_player_select = -1;
7769 // determine how many of the 5 buttons to create
7770 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
7771 Multi_sync_button_count = MULTI_SYNC_HOST_COUNT;
7773 Multi_sync_button_count = MULTI_SYNC_CLIENT_COUNT;
7775 // create the interface buttons
7776 for(idx=0; idx<Multi_sync_button_count; idx++){
7777 // create the object
7778 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);
7780 // set the sound to play when highlighted
7781 Multi_sync_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
7783 // set the ani for the button
7784 // this wierdness is necessary because cancel and kick buttons aren't drawn on the background bitmap,
7785 // so we have to load in frame 0, too (the file should exist)
7786 if ((idx == MS_CANCEL) || (idx == MS_KICK) || (idx == MS_LAUNCH)) {
7787 Multi_sync_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sync_buttons[gr_screen.res][idx].filename, 3, 0);
7789 Multi_sync_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sync_buttons[gr_screen.res][idx].filename);
7793 Multi_sync_buttons[gr_screen.res][idx].button.link_hotspot(Multi_sync_buttons[gr_screen.res][idx].hotspot);
7797 for(idx=0; idx<MULTI_SYNC_NUM_TEXT; idx++) {
7798 // don't create the "launch" button text just yet
7799 if(idx == MST_LAUNCH) {
7802 // multiplayer clients should ignore the kick button
7803 if(!MULTIPLAYER_MASTER && !MULTIPLAYER_HOST && (idx == MST_KICK)) {
7807 Multi_sync_window.add_XSTR(&Multi_sync_text[gr_screen.res][idx]);
7810 // create the player list select button and hide it
7811 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);
7812 Multi_sync_plist_button.hide();
7814 // set up hotkeys for certain common functions
7815 Multi_sync_buttons[gr_screen.res][MS_CANCEL].button.set_hotkey(KEY_ESC);
7818 void multi_sync_common_do()
7820 int k = chatbox_process();
7821 k = Multi_sync_window.process(k);
7823 // process the player list
7824 multi_sync_handle_plist();
7826 // process any button clicks
7827 multi_sync_check_buttons();
7829 // process any keypresses
7833 gamesnd_play_iface(SND_USER_SELECT);
7834 multi_quit_game(PROMPT_ALL);
7839 void multi_sync_common_close()
7841 // unload any bitmaps
7842 if(!bm_unload(Multi_sync_bitmap)){
7843 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_sync_bitmap_fname[gr_screen.res]));
7846 extern int Player_multi_died_check;
7847 Player_multi_died_check = -1;
7849 // destroy the UI_WINDOW
7850 Multi_sync_window.destroy();
7853 void multi_sync_blit_screen_all()
7860 // display any text in the info area
7861 multi_common_render_text();
7863 // display any pending notification messages
7864 multi_common_notify_do();
7866 // display any info about visible players
7868 for(idx=0;idx<MAX_PLAYERS;idx++){
7869 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7870 // display his name and status
7871 multi_sync_display_name(Net_players[idx].player->callsign,count,idx);
7873 // get the player state
7874 state = Net_players[idx].state;
7876 // if we're ingame joining, show all other players except myself as "playing"
7877 if((Net_player != NULL) && (&Net_players[idx] != Net_player) && ((Multi_sync_mode == MULTI_SYNC_INGAME) || (Net_player->flags & NETINFO_FLAG_INGAME_JOIN)) ){
7878 state = NETPLAYER_STATE_IN_MISSION;
7882 case NETPLAYER_STATE_MISSION_LOADING:
7883 multi_sync_display_status(XSTR("Mission Loading",802),count);
7885 case NETPLAYER_STATE_INGAME_SHIP_SELECT: // I don't think its possible to see this state, but...
7886 multi_sync_display_status(XSTR("Ingame Ship Select",803),count);
7888 case NETPLAYER_STATE_DEBRIEF:
7889 multi_sync_display_status(XSTR("Debriefing",804),count);
7891 case NETPLAYER_STATE_MISSION_SYNC:
7892 multi_sync_display_status(XSTR("Mission Sync",805),count);
7894 case NETPLAYER_STATE_JOINING:
7895 multi_sync_display_status(XSTR("Joining",806),count);
7897 case NETPLAYER_STATE_JOINED:
7898 multi_sync_display_status(XSTR("Joined",807),count);
7900 case NETPLAYER_STATE_SLOT_ACK :
7901 multi_sync_display_status(XSTR("Slot Ack",808),count);
7903 case NETPLAYER_STATE_BRIEFING:
7904 multi_sync_display_status(XSTR("Briefing",765),count);
7906 case NETPLAYER_STATE_SHIP_SELECT:
7907 multi_sync_display_status(XSTR("Ship Select",809),count);
7909 case NETPLAYER_STATE_WEAPON_SELECT:
7910 multi_sync_display_status(XSTR("Weapon Select",810),count);
7912 case NETPLAYER_STATE_WAITING:
7913 multi_sync_display_status(XSTR("Waiting",811),count);
7915 case NETPLAYER_STATE_IN_MISSION:
7916 multi_sync_display_status(XSTR("In Mission",812),count);
7918 case NETPLAYER_STATE_MISSION_LOADED:
7919 multi_sync_display_status(XSTR("Mission Loaded",813),count);
7921 case NETPLAYER_STATE_DATA_LOAD:
7922 multi_sync_display_status(XSTR("Data loading",814),count);
7924 case NETPLAYER_STATE_SETTINGS_ACK:
7925 multi_sync_display_status(XSTR("Ready To Enter Mission",815),count);
7927 case NETPLAYER_STATE_INGAME_SHIPS:
7928 multi_sync_display_status(XSTR("Ingame Ships Packet Ack",816),count);
7930 case NETPLAYER_STATE_INGAME_WINGS:
7931 multi_sync_display_status(XSTR("Ingame Wings Packet Ack",817),count);
7933 case NETPLAYER_STATE_INGAME_RPTS:
7934 multi_sync_display_status(XSTR("Ingame Respawn Points Ack",818),count);
7936 case NETPLAYER_STATE_SLOTS_ACK:
7937 multi_sync_display_status(XSTR("Ingame Weapon Slots Ack",819),count);
7939 case NETPLAYER_STATE_POST_DATA_ACK:
7940 multi_sync_display_status(XSTR("Post Briefing Data Block Ack",820),count);
7942 case NETPLAYER_STATE_FLAG_ACK :
7943 multi_sync_display_status(XSTR("Flags Ack",821),count);
7945 case NETPLAYER_STATE_MT_STATS :
7946 multi_sync_display_status(XSTR("Parallax Online Stats Updating",822),count);
7948 case NETPLAYER_STATE_WSS_ACK :
7949 multi_sync_display_status(XSTR("Weapon Slots Ack",823),count);
7951 case NETPLAYER_STATE_HOST_SETUP :
7952 multi_sync_display_status(XSTR("Host setup",824),count);
7954 case NETPLAYER_STATE_DEBRIEF_ACCEPT:
7955 multi_sync_display_status(XSTR("Debrief accept",825),count);
7957 case NETPLAYER_STATE_DEBRIEF_REPLAY:
7958 multi_sync_display_status(XSTR("Debrief replay",826),count);
7960 case NETPLAYER_STATE_CPOOL_ACK:
7961 multi_sync_display_status(XSTR("Campaign ship/weapon ack",827),count);
7963 case NETPLAYER_STATE_MISSION_XFER :
7965 // server should display the pct completion of all clients
7966 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
7967 if(Net_players[idx].s_info.xfer_handle != -1){
7968 pct_complete = multi_xfer_pct_complete(Net_players[idx].s_info.xfer_handle);
7970 // if we've got a valid xfer handle
7971 if((pct_complete >= 0.0) && (pct_complete <= 1.0)){
7972 sprintf(txt,XSTR("Mission file xfer %d%%",828),(int)(pct_complete * 100.0f));
7976 strcpy(txt,XSTR("Mission file xfer",829));
7979 strcpy(txt,XSTR("Mission file xfer",829));
7982 // clients should display only for themselves (which is the only thing they know)
7984 // if we've got a valid file xfer handle
7985 if((&Net_players[idx] == Net_player) && (Net_player->s_info.xfer_handle != -1)){
7986 pct_complete = multi_xfer_pct_complete(Net_player->s_info.xfer_handle);
7988 // if we've got a valid xfer handle
7989 if((pct_complete >= 0.0) && (pct_complete <= 1.0)){
7990 sprintf(txt,XSTR("Mission file xfer %d%%",828),(int)(pct_complete * 100.0f));
7994 strcpy(txt,XSTR("Mission file xfer",829));
7999 strcpy(txt,XSTR("Mission file xfer",829));
8004 multi_sync_display_status(txt,count);
8007 nprintf(("Network","Unhandled player state : %d !\n",Net_players[idx].state));
8014 // display the mission start countdown timer (if any)
8015 anim_render_all(GS_STATE_MULTI_MISSION_SYNC,flFrametime);
8017 // process and show the chatbox thingie
8021 Multi_sync_window.draw_tooltip();
8023 // display the voice status indicator
8024 multi_common_voice_display_status();
8027 void multi_sync_check_buttons()
8030 for(idx=0;idx<Multi_sync_button_count;idx++){
8031 // we only really need to check for one button pressed at a time, so we can break after
8033 if(Multi_sync_buttons[gr_screen.res][idx].button.pressed()){
8034 multi_sync_button_pressed(idx);
8040 void multi_sync_button_pressed(int n)
8045 gamesnd_play_iface(SND_USER_SELECT);
8046 multi_quit_game(PROMPT_ALL);
8049 // scroll the info box up
8050 case MS_SCROLL_INFO_UP:
8051 multi_common_scroll_text_up();
8054 // scroll the info box down
8055 case MS_SCROLL_INFO_DOWN:
8056 multi_common_scroll_text_down();
8061 // if we have a currently selected player, kick him
8062 if(Multi_sync_player_select >= 0){
8063 multi_kick_player(Multi_sync_player_select);
8067 // start the final launch countdown (post-sync only)
8069 multi_sync_start_countdown();
8072 // doesn't do anything
8078 void multi_sync_pre_init()
8082 Netgame.game_state = NETGAME_STATE_MISSION_SYNC;
8084 // if we're in teamplay mode, always force skill level to be medium
8085 if((Netgame.type_flags & NG_TYPE_TEAM) && (Net_player->flags & NETINFO_FLAG_GAME_HOST)){
8086 Netgame.options.skill_level = NUM_SKILL_LEVELS / 2;
8087 Game_skill_level = NUM_SKILL_LEVELS / 2;
8088 multi_options_update_netgame();
8091 // notify everyone of when we get here
8092 if(!(Game_mode & GM_STANDALONE_SERVER)){
8093 Net_player->state = NETPLAYER_STATE_MISSION_SYNC;
8094 send_netplayer_update_packet();
8097 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8099 ml_string(NOX("Server performing pre-briefing data sync"));
8101 if(!(Game_mode & GM_STANDALONE_SERVER)){
8102 multi_common_set_text(XSTR("Server performing sync\n",830),1);
8105 // maybe initialize tvt and squad war stuff
8106 if(Netgame.type_flags & NG_TYPE_TEAM){
8107 multi_team_level_init();
8110 // force everyone into this state
8111 send_netgame_update_packet();
8113 if(!(Game_mode & GM_STANDALONE_SERVER)){
8114 multi_common_add_text(XSTR("Send update packet\n",831),1);
8117 // setup some of my own data
8118 Net_player->flags |= NETINFO_FLAG_MISSION_OK;
8120 // do any output stuff
8121 if(Game_mode & GM_STANDALONE_SERVER){
8122 std_debug_set_standalone_state_string("Mission Sync");
8125 // do this here to insure we have the most up to date file checksum info
8126 multi_get_mission_checksum(Game_current_mission_filename);
8127 // parse_get_file_signature(Game_current_mission_filename);
8129 if(!(Game_mode & GM_STANDALONE_SERVER)){
8130 multi_common_add_text(XSTR("Got file signatures\n",832),1);
8133 if(!(Game_mode & GM_STANDALONE_SERVER)){
8134 multi_common_add_text(XSTR("Sending update state packet\n",833),1);
8138 // if we're not in team vs. team mode - set all player teams to be 0, and unset all captaincy bits
8139 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
8140 for(idx=0;idx<MAX_PLAYERS;idx++){
8141 Net_players[idx].p_info.team = 0;
8142 Net_players[idx].flags &= ~(NETINFO_FLAG_TEAM_CAPTAIN);
8146 // we aren't necessarily xferring the mission file yet
8147 Assert(Net_player->s_info.xfer_handle == -1);
8149 // always call this for good measure
8150 multi_campaign_flush_data();
8152 Mission_sync_flags = 0;
8153 Multi_mission_loaded = 0;
8156 void multi_sync_pre_do()
8160 // If I'm the server, wait for everyone to arrive in this state, then begin transferring data, etc.
8161 // all servers (standalone or no, go through this)
8162 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8163 // wait for everyone to arrive, then request filesig from all of them
8164 if(multi_netplayer_state_check(NETPLAYER_STATE_MISSION_SYNC) && !(Mission_sync_flags & MS_FLAG_SENT_FILESIG) && !multi_endgame_ending()){
8165 send_file_sig_request(Netgame.mission_name);
8166 Mission_sync_flags |= MS_FLAG_SENT_FILESIG;
8168 if(!(Game_mode & GM_STANDALONE_SERVER)){
8169 multi_common_add_text(XSTR("Sent filesig request\n",834),1);
8173 // if we're waiting for players to receive files, then check on their status
8174 if((Mission_sync_flags & MS_FLAG_SENT_FILESIG) && !multi_netplayer_flag_check(NETINFO_FLAG_MISSION_OK) && !multi_endgame_ending()){
8175 for(idx=0;idx<MAX_PLAYERS;idx++){
8176 // if this player is in the process of xferring a file
8177 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].s_info.xfer_handle != -1)){
8178 switch(multi_xfer_get_status(Net_players[idx].s_info.xfer_handle)){
8179 // if it has successfully completed, set his ok flag
8180 case MULTI_XFER_SUCCESS :
8182 Net_players[idx].flags |= NETINFO_FLAG_MISSION_OK;
8184 // release the xfer instance handle
8185 multi_xfer_release_handle(Net_players[idx].s_info.xfer_handle);
8186 Net_players[idx].s_info.xfer_handle = -1;
8188 // if it has failed or timed-out, kick the player
8189 case MULTI_XFER_TIMEDOUT:
8190 case MULTI_XFER_FAIL:
8191 // release the xfer handle
8192 multi_xfer_release_handle(Net_players[idx].s_info.xfer_handle);
8193 Net_players[idx].s_info.xfer_handle = -1;
8196 multi_kick_player(idx, 0, KICK_REASON_BAD_XFER);
8203 // NOTE : this is now obsolete
8204 // once everyone is verified, do any data transfer necessary
8205 if(multi_netplayer_flag_check(NETINFO_FLAG_MISSION_OK) && !(Mission_sync_flags & MS_FLAG_DATA_DONE) && !multi_endgame_ending()){
8206 // do nothing for now
8207 Mission_sync_flags |= MS_FLAG_DATA_DONE;
8209 // send campaign pool data
8210 multi_campaign_send_pool_status();
8213 // wait for everyone to ack on campaign pool data (even in non-campaign situations)
8214 if((Mission_sync_flags & MS_FLAG_DATA_DONE) && !(Mission_sync_flags & MS_FLAG_CAMP_DONE) && !multi_endgame_ending()){
8215 // check to see if everyone has acked the campaign pool data
8216 if(multi_netplayer_state_check(NETPLAYER_STATE_CPOOL_ACK)){
8217 Mission_sync_flags |= MS_FLAG_CAMP_DONE;
8221 // once everyone is verified, tell them to load the mission
8222 // also make sure to load the mission myself _AFTER_ telling everyone to do so. This makes the whole process
8223 // move along faster
8224 if((Mission_sync_flags & MS_FLAG_CAMP_DONE) && !(Mission_sync_flags & MS_FLAG_SENT_LOAD) && !multi_endgame_ending()){
8225 send_netplayer_load_packet(NULL);
8226 Mission_sync_flags |= MS_FLAG_SENT_LOAD;
8228 if(!(Game_mode & GM_STANDALONE_SERVER)){
8229 multi_common_add_text(XSTR("Sent load packet\n",835),1);
8232 // load the mission myself, as soon as possible
8233 if(!Multi_mission_loaded){
8234 nprintf(("Network","Server loading mission..."));
8236 // update everyone about my status
8237 Net_player->state = NETPLAYER_STATE_MISSION_LOADING;
8238 send_netplayer_update_packet();
8240 game_start_mission();
8242 nprintf(("Network","Done\n"));
8243 Multi_mission_loaded = 1;
8245 // update everyone about my status
8246 Net_player->state = NETPLAYER_STATE_MISSION_LOADED;
8247 send_netplayer_update_packet();
8249 if(!(Game_mode & GM_STANDALONE_SERVER)){
8250 multi_common_add_text(XSTR("Loaded mission locally\n",836),1);
8255 // if everyone has loaded the mission, randomly assign players to ships
8256 if(multi_netplayer_state_check(NETPLAYER_STATE_MISSION_LOADED) && !(Mission_sync_flags & MS_FLAG_TS_SLOTS) && !multi_endgame_ending()){
8257 // call the team select function to assign players to their ships, wings, etc
8258 multi_ts_assign_players_all();
8259 send_netplayer_slot_packet();
8262 Mission_sync_flags |= MS_FLAG_TS_SLOTS;
8265 // if everyone has loaded the mission, move to the team select stage
8266 if(Sync_test && multi_netplayer_state_check(NETPLAYER_STATE_SLOT_ACK) && !(Mission_sync_flags & MS_FLAG_PUSHED_BRIEFING) && !multi_endgame_ending()){
8267 Netgame.game_state = NETGAME_STATE_BRIEFING;
8268 send_netgame_update_packet(); // this will push everyone into the next state
8270 // the standalone moves to his own wait state, whereas in the normal game mode, the server/host moves in to the
8271 // team select state
8272 if(Game_mode & GM_STANDALONE_SERVER){
8273 gameseq_post_event(GS_EVENT_MULTI_STD_WAIT);
8275 gameseq_post_event(GS_EVENT_START_GAME);
8278 Mission_sync_flags |= MS_FLAG_PUSHED_BRIEFING;
8280 if(!(Game_mode & GM_STANDALONE_SERVER)){
8281 multi_common_add_text(XSTR("Moving to team select\n",837),1);
8285 // clients should detect here if they are doing a file xfer and do error processing
8286 if((Net_player->state == NETPLAYER_STATE_MISSION_XFER) && (Net_player->s_info.xfer_handle != -1) && !multi_endgame_ending()){
8287 switch(multi_xfer_get_status(Net_player->s_info.xfer_handle)){
8288 // if it has successfully completed, set his ok flag
8289 case MULTI_XFER_SUCCESS :
8290 // release my xfer handle
8291 multi_xfer_release_handle(Net_player->s_info.xfer_handle);
8292 Net_player->s_info.xfer_handle = -1;
8295 // if it has failed or timed-out, kick the player
8296 case MULTI_XFER_TIMEDOUT:
8297 case MULTI_XFER_FAIL:
8298 // release my xfer handle
8299 multi_xfer_release_handle(Net_player->s_info.xfer_handle);
8300 Net_player->s_info.xfer_handle = -1;
8302 // leave the game qith an error code
8303 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_XFER_FAIL);
8310 if(!(Game_mode & GM_STANDALONE_SERVER)){
8312 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8313 if(Multi_sync_bitmap != -1){
8314 gr_set_bitmap(Multi_sync_bitmap);
8317 Multi_sync_window.draw();
8319 multi_sync_blit_screen_all();
8325 void multi_sync_pre_close()
8327 // at this point, we should shut down any file xfers...
8328 if(Net_player->s_info.xfer_handle != -1){
8329 nprintf(("Network","WARNING - killing file xfer while leaving mission sync state!!!\n"));
8331 multi_xfer_abort(Net_player->s_info.xfer_handle);
8332 Net_player->s_info.xfer_handle;
8336 void multi_sync_post_init()
8338 multi_reset_timestamps();
8340 Multi_state_timestamp = timestamp(0);
8343 ml_string(NOX("Performing post-briefing data sync"));
8345 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8346 multi_common_add_text(XSTR("Server performing sync\n",830),1);
8348 multi_common_add_text(XSTR("Client performing sync\n",838),1);
8351 // everyone should re-initialize these
8352 init_multiplayer_stats();
8354 // reset all sequencing info
8355 multi_oo_reset_sequencing();
8357 // if I am not the master of the game, then send the firing information for my ship
8359 if ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) ){
8360 send_firing_info_packet();
8363 // if I'm not a standalone server, load up the countdown stuff
8364 if(!(Game_mode & GM_STANDALONE_SERVER)){
8365 Multi_sync_countdown_anim = NULL;
8366 Multi_sync_countdown_instance = NULL;
8367 Multi_sync_countdown_anim = anim_load(Multi_sync_countdown_fname[gr_screen.res]);
8368 if(Multi_sync_countdown_anim == NULL){
8369 nprintf(("General","WARNING!, Could not load countdown animation %s!\n",Multi_sync_countdown_fname[gr_screen.res]));
8373 // create objects for all permanent observers
8374 multi_obs_level_init();
8376 // clear the game start countdown timer
8377 Multi_sync_countdown_timer = -1.0f;
8378 Multi_sync_countdown = -1;
8380 // if this is a team vs. team mission, mark all ship teams appropriately
8381 if(Netgame.type_flags & NG_TYPE_TEAM){
8382 multi_team_mark_all_ships();
8385 Mission_sync_flags = 0;
8386 Multi_sync_launch_pressed = 0;
8389 #define MULTI_POST_TIMESTAMP 7000
8391 extern int create_wings();
8393 void multi_sync_post_do()
8397 // only if the host is also the master should he be doing this (non-standalone situation)
8398 if ( Net_player->flags & NETINFO_FLAG_AM_MASTER ) {
8400 // once everyone gets to this screen, send them the ship classes of all ships.
8401 if(multi_netplayer_state_check(NETPLAYER_STATE_WAITING) && !(Mission_sync_flags & MS_FLAG_POST_DATA)) {
8402 // only the host should ever do this
8403 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8404 // at this point we want to delete all necessary ships, change all necessary ship classes, and set all weapons up
8405 multi_ts_create_wings();
8407 // update player ets settings
8408 for(idx=0;idx<MAX_PLAYERS;idx++){
8409 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].player->objnum != -1)){
8410 multi_server_update_player_weapons(&Net_players[idx],&Ships[Objects[Net_players[idx].player->objnum].instance]);
8415 // note that this is done a little differently for standalones and nonstandalones
8416 send_post_sync_data_packet();
8418 multi_common_add_text(XSTR("Sending post briefing block information\n",839),1);
8420 Mission_sync_flags |= MS_FLAG_POST_DATA;
8423 // send weapon slots data
8424 if(multi_netplayer_state_check(NETPLAYER_STATE_POST_DATA_ACK) && !(Mission_sync_flags & MS_FLAG_WSS_SLOTS)) {
8425 // note that this is done a little differently for standalones and nonstandalones
8426 if(Netgame.type_flags & NG_TYPE_TEAM){
8427 send_wss_slots_data_packet(0,0);
8428 send_wss_slots_data_packet(1,1);
8430 send_wss_slots_data_packet(0,1);
8433 multi_common_add_text(XSTR("Sending weapon slots information\n",840),1);
8435 Mission_sync_flags |= MS_FLAG_WSS_SLOTS;
8438 // once weapon information is received, send player settings info
8439 if ( multi_netplayer_state_check(NETPLAYER_STATE_WSS_ACK) && !(Mission_sync_flags & MS_FLAG_PSETTINGS)) {
8440 send_player_settings_packet();
8442 // server (specifically, the standalone), should set this here
8443 Net_player->state = NETPLAYER_STATE_SETTINGS_ACK;
8444 send_netplayer_update_packet();
8446 multi_common_add_text(XSTR("Sending player settings packets\n",841),1);
8448 Mission_sync_flags |= MS_FLAG_PSETTINGS;
8451 // check to see if the countdown timer has started and act appropriately
8452 if( Multi_sync_countdown_timer > -1.0f ) {
8454 // increment by frametime.
8455 Multi_sync_countdown_timer += flFrametime;
8457 // if the animation is not playing, start it
8458 if(!(Game_mode & GM_STANDALONE_SERVER) && (Multi_sync_countdown_instance == NULL) && (Multi_sync_countdown_anim != NULL)){
8459 anim_play_struct aps;
8461 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]);
8462 aps.screen_id = GS_STATE_MULTI_MISSION_SYNC;
8463 aps.framerate_independent = 1;
8465 Multi_sync_countdown_instance = anim_play(&aps);
8468 // if the next second has expired
8469 if( Multi_sync_countdown_timer >= 1.0f ) {
8471 Multi_sync_countdown--;
8472 Multi_sync_countdown_timer = 0.0f;
8474 // if the countdown has reached 0, launch the mission
8475 if(Multi_sync_countdown == 0){
8476 Multi_sync_countdown_timer = -1.0f;
8478 Multi_sync_launch_pressed = 0;
8479 multi_sync_launch();
8481 // otherwise send a countdown packet
8483 send_countdown_packet(Multi_sync_countdown);
8488 // jump into the mission myself
8489 if((Multi_sync_countdown == 0) && multi_netplayer_state_check(NETPLAYER_STATE_IN_MISSION)){
8490 if(!((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !Multi_sync_launch_pressed)){
8491 Net_player->state = NETPLAYER_STATE_IN_MISSION;
8492 Netgame.game_state = NETGAME_STATE_IN_MISSION;
8493 gameseq_post_event(GS_EVENT_ENTER_GAME);
8495 multi_common_add_text(XSTR("Moving into game\n",842),1);
8499 // maybe start the animation countdown
8500 if((Multi_sync_countdown >= 0) && (Multi_sync_countdown_instance == NULL) && (Multi_sync_countdown_anim != NULL)){
8501 anim_play_struct aps;
8503 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]);
8504 aps.screen_id = GS_STATE_MULTI_MISSION_SYNC;
8505 aps.framerate_independent = 1;
8507 Multi_sync_countdown_instance = anim_play(&aps);
8511 // host - specific stuff
8512 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8513 // create the launch button so the host can click
8514 if( Sync_test && multi_netplayer_state_check(NETPLAYER_STATE_SETTINGS_ACK) ){
8515 multi_sync_create_launch_button();
8520 if(!(Game_mode & GM_STANDALONE_SERVER)){
8522 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8523 if(Multi_sync_bitmap != -1){
8524 gr_set_bitmap(Multi_sync_bitmap);
8527 Multi_sync_window.draw();
8529 multi_sync_blit_screen_all();
8535 void multi_sync_post_close()
8539 // if I'm not a standalone server, unload up the countdown stuff
8540 if(!(Game_mode & GM_STANDALONE_SERVER)){
8541 // release all rendering animation instances (should only be 1)
8542 anim_release_all_instances(GS_STATE_MULTI_MISSION_SYNC);
8543 Multi_sync_countdown_instance = NULL;
8545 // free up the countdown animation
8546 if(Multi_sync_countdown_anim != NULL){
8547 anim_free(Multi_sync_countdown_anim);
8548 Multi_sync_countdown_anim = NULL;
8552 // all players should reset sequencing
8553 for(idx=0;idx<MAX_PLAYERS;idx++){
8554 if(Net_player->flags & NETINFO_FLAG_CONNECTED){
8555 Net_players[idx].client_cinfo_seq = 0;
8556 Net_players[idx].client_server_seq = 0;
8560 // multiplayer dogfight
8561 multi_df_level_pre_enter();
8563 // clients should clear obj_pair array and add pair for themselves
8565 if ( MULTIPLAYER_CLIENT ) {
8567 obj_add_pairs( OBJ_INDEX(Player_obj) );
8572 void multi_sync_display_name(char *name,int index,int np_index)
8574 char fit[CALLSIGN_LEN];
8576 // make sure the string actually fits
8579 // if we're in team vs. team mode
8580 if(Netgame.type_flags & NG_TYPE_TEAM){
8581 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]);
8583 // if this is the currently selected player, draw him highlighted
8584 if(np_index == Multi_sync_player_select){
8585 gr_set_color_fast(&Color_text_selected);
8587 gr_set_color_fast(&Color_text_normal);
8591 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);
8593 // blit his team icon
8595 if(Net_players[np_index].p_info.team == 0){
8596 // blit the team captain icon
8597 if(Net_players[np_index].flags & NETINFO_FLAG_TEAM_CAPTAIN){
8598 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
8599 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT]);
8600 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);
8603 // normal team member icon
8605 if(Multi_common_icons[MICON_TEAM0] != -1){
8606 gr_set_bitmap(Multi_common_icons[MICON_TEAM0]);
8607 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);
8612 else if(Net_players[np_index].p_info.team == 1){
8613 // blit the team captain icon
8614 if(Net_players[np_index].flags & NETINFO_FLAG_TEAM_CAPTAIN){
8615 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
8616 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT]);
8617 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);
8620 // normal team member icon
8622 if(Multi_common_icons[MICON_TEAM1] != -1){
8623 gr_set_bitmap(Multi_common_icons[MICON_TEAM1]);
8624 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);
8629 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]);
8631 // if this is the currently selected player, draw him highlighted
8632 if(np_index == Multi_sync_player_select){
8633 gr_set_color_fast(&Color_text_selected);
8635 gr_set_color_fast(&Color_text_normal);
8639 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);
8642 // maybe blit his CD status icon
8643 if((Net_players[np_index].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
8644 gr_set_bitmap(Multi_common_icons[MICON_CD]);
8645 gr_bitmap(Ms_status_coords[gr_screen.res][MS_X_COORD], Ms_status_coords[gr_screen.res][MS_Y_COORD] + (index * 10));
8649 void multi_sync_display_status(char *status,int index)
8653 // make sure the string actually fits
8654 strcpy(fit, status);
8655 gr_force_fit_string(fit, 250, Ms_status2_coords[gr_screen.res][MS_W_COORD] - 20);
8656 gr_set_color_fast(&Color_bright);
8657 gr_string(Ms_status2_coords[gr_screen.res][MS_X_COORD], Ms_status2_coords[gr_screen.res][MS_Y_COORD] + (index * 10), fit);
8660 void multi_sync_force_start_pre()
8663 int want_state = NETPLAYER_STATE_SLOT_ACK; // kick any players who are still in this state
8665 // go through the player list and boot anyone who isn't in the right state
8666 for(idx=0;idx<MAX_PLAYERS;idx++){
8667 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx]) && (Net_players[idx].state == want_state)){
8668 multi_kick_player(idx,0);
8673 void multi_sync_force_start_post()
8677 int num_kill_states;
8679 // determine the state we want all players in so that we can find those who are not in the state
8680 kill_state[0] = NETPLAYER_STATE_BRIEFING;
8681 kill_state[1] = NETPLAYER_STATE_SHIP_SELECT;
8682 kill_state[2] = NETPLAYER_STATE_WEAPON_SELECT;
8683 num_kill_states = 3;
8685 // go through the player list and boot anyone who isn't in the right state
8686 for(idx=0;idx<MAX_PLAYERS;idx++){
8687 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx])){
8688 // check against all kill state
8689 for(idx2 = 0;idx2<num_kill_states;idx2++){
8690 if(Net_players[idx].state == kill_state[idx2]){
8691 multi_kick_player(idx,0);
8699 void multi_sync_start_countdown()
8701 // don't allow repeat button presses
8702 if(Multi_sync_launch_pressed){
8706 Multi_sync_launch_pressed = 1;
8708 // if I'm the server, begin the countdown
8709 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8710 gamesnd_play_iface(SND_COMMIT_PRESSED);
8711 Multi_sync_countdown_timer = 0.0f;
8712 Multi_sync_countdown = MULTI_SYNC_COUNTDOWN_TIME;
8714 // send an initial countdown value
8715 send_countdown_packet(Multi_sync_countdown);
8717 // otherwise send the "start countdown" packet to the standalone
8719 Assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
8720 send_countdown_packet(-1);
8724 void multi_sync_launch()
8726 // don't allow repeat button presses
8727 if(Multi_sync_launch_pressed){
8731 Multi_sync_launch_pressed = 1;
8734 ml_printf(NOX("Entering mission %s"), Game_current_mission_filename);
8736 // tell everyone to jump into the mission
8737 send_jump_into_mission_packet();
8738 Multi_state_timestamp = timestamp(MULTI_POST_TIMESTAMP);
8740 // set the # of players at the start of the mission
8741 Multi_num_players_at_start = multi_num_players();
8742 nprintf(("Network","# of players at start of mission : %d\n", Multi_num_players_at_start));
8744 // initialize datarate limiting for all clients
8745 multi_oo_rate_init_all();
8747 multi_common_add_text(XSTR("Sending mission start packet\n",843),1);
8750 void multi_sync_create_launch_button()
8752 if (!Multi_launch_button_created) {
8753 // create the object
8754 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);
8756 // set the sound to play when highlighted
8757 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_highlight_action(common_play_highlight_sound);
8759 // set the ani for the button
8760 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_bmaps(Multi_sync_buttons[gr_screen.res][MS_LAUNCH].filename, 3, 0);
8763 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.link_hotspot(Multi_sync_buttons[gr_screen.res][MS_LAUNCH].hotspot);
8766 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_hotkey(KEY_CTRLED+KEY_ENTER);
8768 // create the text for the button
8769 Multi_sync_window.add_XSTR(&Multi_sync_text[gr_screen.res][MST_LAUNCH]);
8771 // increment the button count so we start checking this one
8772 Multi_sync_button_count++;
8774 Multi_launch_button_created = 1;
8778 void multi_sync_handle_plist()
8784 // if we don't have a currently selected player, select one
8785 if((Multi_sync_player_select < 0) || !MULTI_CONNECTED(Net_players[Multi_sync_player_select])){
8786 for(idx=0;idx<MAX_PLAYERS;idx++){
8787 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
8788 Multi_sync_player_select = idx;
8794 // check for button list presses
8795 if(Multi_sync_plist_button.pressed()){
8796 // get the y mouse coords
8797 Multi_sync_plist_button.get_mouse_pos(NULL,&my);
8799 // get the index of the item selected
8800 select_index = my / 10;
8802 // if the index is greater than the current # connections, do nothing
8803 if(select_index > (multi_num_connections() - 1)){
8807 // translate into an absolute Net_players[] index (get the Nth net player)
8808 Multi_sync_player_select = -1;
8809 for(idx=0;idx<MAX_PLAYERS;idx++){
8810 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
8814 // if we've found the item we're looking for
8815 if(select_index < 0){
8816 Multi_sync_player_select = idx;
8821 // if for some bizarre reason, this is an invalid player, unselect him and wait for the next interation
8823 if((Multi_sync_player_select >= 0) && (!MULTI_CONNECTED(Net_players[Multi_sync_player_select]) || MULTI_STANDALONE(Net_players[Multi_sync_player_select])) ){
8824 Multi_sync_player_select = -1;
8830 // -------------------------------------------------------------------------------------------------------------
8832 // MULTIPLAYER DEBRIEF SCREEN
8835 // other relevant data
8836 int Multi_debrief_accept_hit;
8837 int Multi_debrief_replay_hit;
8839 // set if the server has left the game
8840 int Multi_debrief_server_left = 0;
8842 // if we've reported on TvT status all players are in the debrief
8843 int Multi_debrief_reported_tvt = 0;
8845 // whether stats are being accepted
8846 // -1 == no decision yet
8849 int Multi_debrief_stats_accept_code = -1;
8851 int Multi_debrief_server_framecount = 0;
8853 float Multi_debrief_time = 0.0f;
8854 float Multi_debrief_resend_time = 10.0f;
8856 void multi_debrief_init()
8860 Multi_debrief_time = 0.0f;
8861 Multi_debrief_resend_time = 10.0f;
8863 // do this to notify the standalone or the normal server that we're in the debrief state and ready to receive packets
8864 if (!(Net_player->flags & NETINFO_FLAG_AM_MASTER)) {
8865 Net_player->state = NETPLAYER_STATE_DEBRIEF;
8866 send_netplayer_update_packet();
8869 // unflag some stuff
8870 for(idx=0;idx<MAX_PLAYERS;idx++){
8871 if(MULTI_CONNECTED(Net_players[idx])){
8872 Net_players[idx].flags &= ~(NETINFO_FLAG_RESPAWNING | NETINFO_FLAG_LIMBO | NETINFO_FLAG_WARPING_OUT);
8876 // if text input mode is active, clear it
8877 multi_msg_text_flush();
8879 // the server has not left yet
8880 Multi_debrief_server_left = 0;
8882 // have not hit accept or replay yet
8883 Multi_debrief_accept_hit = 0;
8884 Multi_debrief_replay_hit = 0;
8886 // stats have not been accepted yet
8887 Multi_debrief_stats_accept_code = -1;
8889 // mark stats as not being store yet
8890 Netgame.flags &= ~(NG_FLAG_STORED_MT_STATS);
8892 // no report on TvT yet
8893 Multi_debrief_reported_tvt = 0;
8895 Multi_debrief_server_framecount = 0;
8898 void multi_debrief_do_frame()
8900 Multi_debrief_time += flFrametime;
8902 // set the netgame state to be debriefing when appropriate
8903 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)){
8904 Netgame.game_state = NETGAME_STATE_DEBRIEF;
8905 send_netgame_update_packet();
8908 // evaluate all server stuff
8909 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8910 multi_debrief_server_process();
8914 void multi_debrief_close()
8916 if ( MULTIPLAYER_CLIENT && (Netgame.game_state == NETGAME_STATE_MISSION_SYNC) ){
8917 gamesnd_play_iface( SND_COMMIT_PRESSED );
8921 // handle optional mission loop
8922 void multi_maybe_set_mission_loop()
8924 int cur = Campaign.current_mission;
8925 if (Campaign.missions[cur].has_mission_loop) {
8926 Assert(Campaign.loop_mission != CAMPAIGN_LOOP_MISSION_UNINITIALIZED);
8928 bool require_repeat_mission = (Campaign.current_mission == Campaign.next_mission);
8930 // check for (1) mission loop available, (2) dont have to repeat last mission
8931 if ( (Campaign.missions[cur].has_mission_loop && (Campaign.loop_mission != -1)) && !require_repeat_mission ) {
8934 debrief_assemble_optional_mission_popup_text(buffer, Campaign.missions[cur].mission_loop_desc);
8936 int choice = popup(0 , 2, POPUP_NO, POPUP_YES, buffer);
8938 Campaign.loop_enabled = 1;
8939 Campaign.next_mission = Campaign.loop_mission;
8944 // handle all cases for when the accept key is hit in a multiplayer debriefing
8945 void multi_debrief_accept_hit()
8947 // if we already accepted, do nothing
8948 // but he may need to hit accept again after the server has left the game, so allow this
8949 if(Multi_debrief_accept_hit){
8953 // mark this so that we don't hit it again
8954 Multi_debrief_accept_hit = 1;
8956 gamesnd_play_iface(SND_COMMIT_PRESSED);
8958 // if the server has left the game, always just end the game.
8959 if(Multi_debrief_server_left){
8960 if(!multi_quit_game(PROMPT_ALL)){
8961 Multi_debrief_server_left = 1;
8962 Multi_debrief_accept_hit = 0;
8964 Multi_debrief_server_left = 0;
8967 // query the host and see if he wants to accept stats
8968 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8969 // if we're on a tracker game, he gets no choice for storing stats
8970 if(MULTI_IS_TRACKER_GAME){
8971 multi_maybe_set_mission_loop();
8973 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));
8975 // evaluate the result
8980 Multi_debrief_accept_hit = 0;
8983 // set the accept code to be "not accepting"
8985 multi_debrief_stats_toss();
8986 multi_maybe_set_mission_loop();
8989 // accept the stats and continue
8991 multi_debrief_stats_accept();
8992 multi_maybe_set_mission_loop();
8998 // set my netplayer state to be "debrief_accept", and be done with it
8999 Net_player->state = NETPLAYER_STATE_DEBRIEF_ACCEPT;
9000 send_netplayer_update_packet();
9004 // handle all cases for when the escape key is hit in a multiplayer debriefing
9005 void multi_debrief_esc_hit()
9009 // if the server has left
9010 if(Multi_debrief_server_left){
9011 multi_quit_game(PROMPT_ALL);
9016 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
9017 // if the stats have already been accepted
9018 if((Multi_debrief_stats_accept_code != -1) || (MULTI_IS_TRACKER_GAME)){
9019 multi_quit_game(PROMPT_HOST);
9021 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));
9023 // evaluate the result
9030 // set the accept code to be "not accepting"
9032 multi_debrief_stats_toss();
9033 multi_quit_game(PROMPT_NONE);
9036 // accept the stats and continue
9038 multi_debrief_stats_accept();
9039 multi_quit_game(PROMPT_NONE);
9044 // if the stats haven't been accepted yet, or this is a tracker game
9045 if((Multi_debrief_stats_accept_code == -1) && !(MULTI_IS_TRACKER_GAME)){
9046 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));
9048 // evaluate the result
9050 multi_quit_game(PROMPT_NONE);
9053 // otherwise go through the normal endgame channels
9055 multi_quit_game(PROMPT_ALL);
9060 void multi_debrief_replay_hit()
9062 // only the host should ever get here
9063 Assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
9065 // if the button was already pressed, do nothing
9066 if(Multi_debrief_accept_hit){
9070 // same as hittin the except button except no stats are kept
9071 Multi_debrief_accept_hit = 1;
9073 // mark myself as being in the replay state so we know what to do next
9074 Net_player->state = NETPLAYER_STATE_DEBRIEF_REPLAY;
9075 send_netplayer_update_packet();
9078 // call this when the server has left and we would otherwise be saying "contact lost with server
9079 void multi_debrief_server_left()
9082 Multi_debrief_server_left = 1;
9084 // undo any "accept" hit so that clients can hit accept again to leave
9085 Multi_debrief_accept_hit = 0;
9088 void multi_debrief_stats_accept()
9090 // don't do anything if we've already accepted
9091 if(Multi_debrief_stats_accept_code != -1){
9095 Multi_debrief_stats_accept_code = 1;
9097 // if we're the host, and we're on a standalone, tell the standalone to begin the stats storing process
9098 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) || (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
9099 // send a packet to the players telling them to store their stats
9100 send_store_stats_packet(1);
9103 // add a chat line saying "stats have been accepted"
9104 multi_display_chat_msg(XSTR("<stats have been accepted>",850),0,0);
9107 ml_string(NOX("Stats stored"));
9110 void multi_debrief_stats_toss()
9112 // don't do anything if we've already accepted
9113 if(Multi_debrief_stats_accept_code != -1){
9117 Multi_debrief_stats_accept_code = 0;
9119 // if we're the host, and we're on a standalone, tell everyone to "toss" stats
9120 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) || (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
9121 // send a packet to the players telling them to store their stats
9122 send_store_stats_packet(0);
9125 // add a chat line saying "stats have been accepted"
9126 multi_display_chat_msg(XSTR("<stats have been tossed>",851),0,0);
9129 ml_string(NOX("Stats tossed"));
9132 int multi_debrief_stats_accept_code()
9134 return Multi_debrief_stats_accept_code;
9137 void multi_debrief_server_process()
9140 int player_status,other_status;
9142 Multi_debrief_server_framecount++;
9144 // if we're > 10 seconds into the debrief and not everyone is here, try warping everyone out again
9145 if((Multi_debrief_time >= Multi_debrief_resend_time) && !multi_netplayer_state_check3(NETPLAYER_STATE_DEBRIEF, NETPLAYER_STATE_DEBRIEF_ACCEPT, NETPLAYER_STATE_DEBRIEF_REPLAY, 1)){
9146 // find all players who are not in the debrief state and hit them with the endgame packet
9147 for(idx=0; idx<MAX_PLAYERS; idx++){
9148 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)) ){
9149 send_endgame_packet(&Net_players[idx]);
9154 Multi_debrief_resend_time += 7.0f;
9157 // evaluate the status of all players in the game (0 == not ready, 1 == ready to continue, 2 == ready to replay)
9160 // check all players
9161 for(idx=0;idx<MAX_PLAYERS;idx++){
9162 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_HOST(Net_players[idx])){
9163 if(Net_players[idx].state != NETPLAYER_STATE_DEBRIEF_ACCEPT){
9170 // if we haven't already reported TvT results
9171 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)){
9172 multi_team_report();
9173 Multi_debrief_reported_tvt = 1;
9176 // if all other players are good to go, check the host
9178 // if he is ready to continue
9179 if(Netgame.host->state == NETPLAYER_STATE_DEBRIEF_ACCEPT){
9182 // if he wants to replay the mission
9183 else if(Netgame.host->state == NETPLAYER_STATE_DEBRIEF_REPLAY){
9186 // if he is not ready
9191 // if all players are _not_ good to go
9196 // if we're in the debriefing state in a campaign mode, process accordingly
9197 if(Netgame.campaign_mode == MP_CAMPAIGN){
9198 multi_campaign_do_debrief(player_status);
9200 // otherwise process as normal (looking for all players to be ready to go to the next mission
9202 if(player_status == 1){
9203 multi_flush_mission_stuff();
9205 // set the netgame state to be forming and continue
9206 Netgame.game_state = NETGAME_STATE_FORMING;
9207 send_netgame_update_packet();
9209 // move to the proper state
9210 if(Game_mode & GM_STANDALONE_SERVER){
9211 gameseq_post_event(GS_EVENT_STANDALONE_MAIN);
9213 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
9216 multi_reset_timestamps();
9217 } else if(player_status == 2){
9218 multi_flush_mission_stuff();
9220 // tell everyone to move into the pre-briefing sync state
9221 Netgame.game_state = NETGAME_STATE_MISSION_SYNC;
9222 send_netgame_update_packet();
9224 // move back to the mission sync screen for the same mission again
9225 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
9226 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
9228 multi_reset_timestamps();
9234 // -------------------------------------------------------------------------------------------------------------
9236 // MULTIPLAYER PASSWORD POPUP
9241 static char *Multi_pwd_bitmap_fname[GR_NUM_RESOLUTIONS] = {
9242 "Password", // GR_640
9243 "2_Password" // GR_1024
9246 static char *Multi_pwd_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
9247 "Password-M", // GR_640
9248 "2_Password-M" // GR_1024
9253 // constants for coordinate lookup
9254 #define MPWD_X_COORD 0
9255 #define MPWD_Y_COORD 1
9256 #define MPWD_W_COORD 2
9257 #define MPWD_H_COORD 3
9260 #define MULTI_PWD_NUM_BUTTONS 2
9261 #define MPWD_CANCEL 0
9262 #define MPWD_COMMIT 1
9264 // password area defs
9265 int Mpwd_coords[GR_NUM_RESOLUTIONS][4] = {
9274 UI_WINDOW Multi_pwd_window; // the window object for the join screen
9275 UI_INPUTBOX Multi_pwd_passwd; // for Netgame.passwd
9276 int Multi_pwd_bitmap; // the background bitmap
9277 int Multi_passwd_background = -1;
9278 int Multi_passwd_done = -1;
9279 int Multi_passwd_running = 0;
9282 ui_button_info Multi_pwd_buttons[GR_NUM_RESOLUTIONS][MULTI_PWD_NUM_BUTTONS] = {
9284 ui_button_info("PWB_00", 411, 151, 405, 141, 0),
9285 ui_button_info("PWB_01", 460, 151, 465, 141, 1),
9288 ui_button_info("2_PWB_00", 659, 242, 649, 225, 0),
9289 ui_button_info("2_PWB_01", 737, 242, 736, 225, 1),
9294 #define MULTI_PWD_NUM_TEXT 3
9295 UI_XSTR Multi_pwd_text[GR_NUM_RESOLUTIONS][MULTI_PWD_NUM_TEXT] = {
9297 { "Cancel", 387, 400, 141, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[0][MPWD_CANCEL].button},
9298 { "Commit", 1062, 455, 141, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[0][MPWD_COMMIT].button},
9299 { "Enter Password", 1332, 149, 92, UI_XSTR_COLOR_GREEN, -1, NULL},
9302 { "Cancel", 387, 649, 225, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[1][MPWD_CANCEL].button},
9303 { "Commit", 1062, 736, 225, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[1][MPWD_COMMIT].button},
9304 { "Enter Password", 1332, 239, 148, UI_XSTR_COLOR_GREEN, -1, NULL},
9308 // initialize all graphics, etc
9309 void multi_passwd_init()
9313 // store the background as it currently is
9314 Multi_passwd_background = gr_save_screen();
9316 // create the interface window
9317 Multi_pwd_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
9318 Multi_pwd_window.set_mask_bmap(Multi_pwd_bitmap_mask_fname[gr_screen.res]);
9320 // load the background bitmap
9321 Multi_pwd_bitmap = bm_load(Multi_pwd_bitmap_fname[gr_screen.res]);
9322 if(Multi_pwd_bitmap < 0){
9323 // we failed to load the bitmap - this is very bad
9327 // create the interface buttons
9328 for(idx=0; idx<MULTI_PWD_NUM_BUTTONS; idx++){
9329 // create the object
9330 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);
9332 // set the sound to play when highlighted
9333 Multi_pwd_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
9335 // set the ani for the button
9336 Multi_pwd_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pwd_buttons[gr_screen.res][idx].filename);
9339 Multi_pwd_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pwd_buttons[gr_screen.res][idx].hotspot);
9343 for(idx=0; idx<MULTI_PWD_NUM_TEXT; idx++){
9344 Multi_pwd_window.add_XSTR(&Multi_pwd_text[gr_screen.res][idx]);
9347 // create the password input box
9348 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);
9349 Multi_pwd_passwd.set_focus();
9351 // link the enter key to ACCEPT
9352 Multi_pwd_buttons[gr_screen.res][MPWD_COMMIT].button.set_hotkey(KEY_ENTER);
9354 Multi_passwd_done = -1;
9355 Multi_passwd_running = 1;
9358 // close down all graphics, etc
9359 void multi_passwd_close()
9361 // unload any bitmaps
9362 bm_release(Multi_pwd_bitmap);
9364 // destroy the UI_WINDOW
9365 Multi_pwd_window.destroy();
9367 // free up the saved background screen
9368 if(Multi_passwd_background >= 0){
9369 gr_free_screen(Multi_passwd_background);
9370 Multi_passwd_background = -1;
9373 Multi_passwd_running = 0;
9376 // process any button pressed
9377 void multi_passwd_process_buttons()
9379 // if the accept button was pressed
9380 if(Multi_pwd_buttons[gr_screen.res][MPWD_COMMIT].button.pressed()){
9381 gamesnd_play_iface(SND_USER_SELECT);
9382 Multi_passwd_done = 1;
9385 // if the cancel button was pressed
9386 if(Multi_pwd_buttons[gr_screen.res][MPWD_CANCEL].button.pressed()){
9387 gamesnd_play_iface(SND_USER_SELECT);
9388 Multi_passwd_done = 0;
9392 // run the passwd popup
9393 void multi_passwd_do(char *passwd)
9397 while(Multi_passwd_done == -1){
9398 // set frametime and run background stuff
9399 game_set_frametime(-1);
9400 game_do_state_common(gameseq_get_state());
9402 k = Multi_pwd_window.process();
9404 // process any keypresses
9407 // set this to indicate the user has cancelled for one reason or another
9408 Multi_passwd_done = 0;
9412 // if the input box text has changed
9413 if(Multi_pwd_passwd.changed()){
9415 Multi_pwd_passwd.get_text(passwd);
9418 // process any button pressed
9419 multi_passwd_process_buttons();
9421 // draw the background, etc
9424 if(Multi_passwd_background >= 0){
9425 gr_restore_screen(Multi_passwd_background);
9427 gr_set_bitmap(Multi_pwd_bitmap);
9429 Multi_pwd_window.draw();
9436 // bring up the password string popup, fill in passwd (return 1 if accept was pressed, 0 if cancel was pressed)
9437 int multi_passwd_popup(char *passwd)
9439 // if the popup is already running for some reason, don't do anything
9440 if(Multi_passwd_running){
9444 // initialize all graphics
9445 multi_passwd_init();
9448 multi_passwd_do(passwd);
9450 // shut everything down
9451 multi_passwd_close();
9453 return Multi_passwd_done;