2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on
10 * $Logfile: /Freespace2/code/Network/MultiUI.cpp $
15 * C file for all the UI controls of the mulitiplayer screens
18 * Revision 1.12 2005/10/02 09:27:49 taylor
19 * fix interface problems with MultiJoinWait screen in FS1
21 * Revision 1.11 2005/03/29 02:18:47 taylor
22 * Various 64-bit platform fixes
23 * Fix compiler errors with MAKE_FS1 and fix gr_set_bitmap() too
24 * Make sure that turrets can fire at asteroids for FS1 (needed for a couple missions)
25 * Streaming audio support (big thanks to Pierre Willenbrock!!)
26 * Removed dependance on strings.tbl for FS1 since we don't actually need it now
28 * Revision 1.10 2004/09/20 01:31:44 theoddone33
31 * Revision 1.9 2004/07/04 11:39:06 taylor
32 * fix missing debrief text, crash on exit, path separator's, warning fixes, no GR_SOFT
34 * Revision 1.8 2003/05/25 02:30:43 taylor
37 * Revision 1.7 2002/06/09 04:41:24 relnev
38 * added copyright header
40 * Revision 1.6 2002/06/02 06:02:59 relnev
43 * Revision 1.5 2002/06/02 00:31:35 relnev
44 * implemented osregistry
46 * Revision 1.4 2002/06/01 07:12:33 relnev
47 * a few NDEBUG updates.
49 * removed a few warnings.
51 * Revision 1.3 2002/05/26 20:49:54 theoddone33
54 * Revision 1.2 2002/05/07 03:16:47 theoddone33
55 * The Great Newline Fix
57 * Revision 1.1.1.1 2002/05/03 03:28:10 root
61 * 94 6/16/00 3:16p Jefff
62 * sim of the year dvd version changes, a few german soty localization
65 * 93 10/14/99 2:51p Jefff
68 * 92 10/13/99 3:50p Jefff
69 * fixed unnumbered XSTRs
71 * 91 9/15/99 1:45a Dave
72 * Don't init joystick on standalone. Fixed campaign mode on standalone.
73 * Fixed no-score-report problem in TvT
75 * 90 9/14/99 12:51a Jefff
78 * 89 9/13/99 4:52p Dave
81 * 88 9/13/99 11:30a Dave
82 * Added checkboxes and functionality for disabling PXO banners as well as
83 * disabling d3d zbuffer biasing.
85 * 87 9/12/99 10:06p Jefff
86 * changed instances of "Squad War" to "SquadWar"
88 * 86 9/03/99 1:32a Dave
89 * CD checking by act. Added support to play 2 cutscenes in a row
90 * seamlessly. Fixed super low level cfile bug related to files in the
91 * root directory of a CD. Added cheat code to set campaign mission # in
94 * 85 9/01/99 10:49p Dave
95 * Added nice SquadWar checkbox to the client join wait screen.
97 * 84 8/30/99 2:49p Jefff
99 * 83 8/26/99 8:49p Jefff
100 * Updated medals screen and about everything that ever touches medals in
101 * one way or another. Sheesh.
103 * 82 8/25/99 4:38p Dave
104 * Updated PXO stuff. Make squad war report stuff much more nicely.
106 * 81 8/20/99 2:09p Dave
107 * PXO banner cycling.
109 * 80 8/20/99 10:06a Jefff
110 * removed closed/rstricted buttons from multi start screen
112 * 79 8/18/99 11:30a Jefff
114 * 78 8/18/99 10:38a Jefff
116 * 77 8/16/99 4:06p Dave
117 * Big honking checkin.
119 * 76 8/16/99 1:08p Jefff
120 * added sounds to a few controls, made input boxes lose focus on ENTER
122 * 75 8/16/99 9:52a Jefff
123 * fixed bitmap loading on buttons in multi-sync screen
125 * 74 8/11/99 5:54p Dave
126 * Fixed collision problem. Fixed standalone ghost problem.
128 * 73 8/10/99 4:35p Jefff
129 * fixed hi-res coords
131 * 72 8/06/99 12:29a Dave
132 * Multiple bug fixes.
134 * 71 8/05/99 3:13p Jasenw
135 * tweaked some text placement coords.
137 * 70 8/04/99 1:38p Jefff
138 * moved some text in multi join wait
140 * 69 8/03/99 12:45p Dave
143 * 68 7/25/99 5:17p Jefff
144 * campaign descriptions show up on multicreate screen
146 * 67 7/20/99 1:49p Dave
147 * Peter Drake build. Fixed some release build warnings.
149 * 66 7/19/99 2:13p Dave
150 * Added some new strings for Heiko.
152 * 65 7/15/99 9:20a Andsager
153 * FS2_DEMO initial checkin
155 * 64 7/08/99 10:53a Dave
156 * New multiplayer interpolation scheme. Not 100% done yet, but still
157 * better than the old way.
159 * 63 6/30/99 10:49a Jasenw
160 * Fixed coords for new launch countdown ani
162 * 62 6/29/99 7:39p Dave
163 * Lots of small bug fixes.
165 * 61 6/25/99 11:59a Dave
166 * Multi options screen.
168 * 60 6/09/99 2:17p Dave
169 * Fixed up pleasewait bitmap rendering.
171 * 59 6/05/99 3:42p Dave
172 * New multi sync screen.
174 * 58 6/01/99 6:07p Dave
175 * New loading/pause/please wait bar.
177 * 57 5/21/99 6:45p Dave
178 * Sped up ui loading a bit. Sped up localization disk access stuff. Multi
179 * start game screen, multi password, and multi pxo-help screen.
181 * 56 5/06/99 11:10a Dave
182 * Fixed coord on multi create screen.
184 * 55 5/04/99 6:38p Dave
185 * Finished multi join-wait screen.
187 * 54 5/04/99 5:20p Dave
188 * Fixed up multiplayer join screen and host options screen. Should both
191 * 53 5/03/99 11:04p Dave
192 * Most of the way done with the multi join screen.
194 * 52 5/03/99 8:32p Dave
195 * New version of multi host options screen.
197 * 51 4/29/99 2:15p Neilk
198 * slider2 code got modified; changed parameters for create
200 * 50 4/25/99 3:02p Dave
201 * Build defines for the E3 build.
203 * 49 4/21/99 6:15p Dave
204 * Did some serious housecleaning in the beam code. Made it ready to go
205 * for anti-fighter "pulse" weapons. Fixed collision pair creation. Added
206 * a handy macro for recalculating collision pairs for a given object.
208 * 48 4/16/99 5:27p Neilk
209 * added slider support and hir res for multi_create
211 * 47 4/14/99 6:37p Dave
212 * Fixed scroll button bug on host create screen.
214 * 46 4/14/99 5:28p Dave
217 * 45 4/12/99 10:07p Dave
218 * Made network startup more forgiving. Added checkmarks to dogfight
219 * screen for players who hit commit.
221 * 44 4/09/99 2:21p Dave
222 * Multiplayer beta stuff. CD checking.
224 * 43 4/08/99 1:28p Dave
225 * Small bug fixes for refresh button on the multi create screen.
227 * 42 4/08/99 11:55a Neilk
228 * Converted Multi_Create to new artwork (just lowres)
230 * 41 4/08/99 2:10a Dave
231 * Numerous bug fixes for the beta. Added builtin mission info for the
234 * 40 3/20/99 3:48p Andsager
235 * Do mission_loop stuff for PXO
237 * 39 3/10/99 6:50p Dave
238 * Changed the way we buffer packets for all clients. Optimized turret
239 * fired packets. Did some weapon firing optimizations.
241 * 38 3/09/99 6:24p Dave
242 * More work on object update revamping. Identified several sources of
243 * unnecessary bandwidth.
245 * 37 3/08/99 7:03p Dave
246 * First run of new object update system. Looks very promising.
248 * 36 2/25/99 4:19p Dave
249 * Added multiplayer_beta defines. Added cd_check define. Fixed a few
250 * release build warnings. Added more data to the squad war request and
253 * 35 2/24/99 3:26p Anoop
254 * Make sure the host is the only guy who bashes skill level for TvT.
256 * 34 2/24/99 2:25p Dave
257 * Fixed up chatbox bugs. Made squad war reporting better. Fixed a respawn
258 * bug for dogfight more.
260 * 33 2/23/99 2:29p Dave
261 * First run of oldschool dogfight mode.
263 * 32 2/17/99 2:11p Dave
264 * First full run of squad war. All freespace and tracker side stuff
267 * 31 2/12/99 6:16p Dave
268 * Pre-mission Squad War code is 95% done.
270 * 30 2/11/99 3:08p Dave
271 * PXO refresh button. Very preliminary squad war support.
273 * 29 2/08/99 5:07p Dave
274 * FS2 chat server support. FS2 specific validated missions.
276 * 28 2/04/99 6:29p Dave
277 * First full working rev of FS2 PXO support. Fixed Glide lighting
280 * 27 1/30/99 5:08p Dave
281 * More new hi-res stuff.Support for nice D3D textures.
283 * 26 1/29/99 2:08a Dave
284 * Fixed beam weapon collisions with players. Reduced size of scoring
285 * struct for multiplayer. Disabled PXO.
287 * 25 1/15/99 2:36p Neilk
288 * fixed multi_jw coordinates
290 * 24 1/13/99 7:19p Neilk
291 * Converted Mission Brief, Barracks, Synch to high res support
293 * 23 1/12/99 7:17p Neilk
295 * 22 1/12/99 5:45p Dave
296 * Moved weapon pipeline in multiplayer to almost exclusively client side.
297 * Very good results. Bandwidth goes down, playability goes up for crappy
298 * connections. Fixed object update problem for ship subsystems.
300 * 21 1/12/99 4:07a Dave
301 * Put in barracks code support for selecting squad logos. Properly
302 * distribute squad logos in a multiplayer game.
304 * 20 1/11/99 7:19p Neilk
305 * Converted multi_join interface to support multiple resolutions
307 * 19 12/18/98 1:13a Dave
308 * Rough 1024x768 support for Direct3D. Proper detection and usage through
311 * 18 12/17/98 4:50p Andsager
312 * Added debrief_assemble_optional_mission_popup_text() for single and
315 * 17 12/14/98 12:13p Dave
316 * Spiffed up xfer system a bit. Put in support for squad logo file xfer.
319 * 16 12/10/98 10:19a Andsager
320 * Fix mission loop assert
322 * 15 12/10/98 9:59a Andsager
323 * Fix some bugs with mission loops
325 * 14 12/09/98 1:56p Andsager
326 * Initial checkin of mission loop
328 * 13 12/03/98 5:22p Dave
329 * Ported over Freespace 1 multiplayer ships.tbl and weapons.tbl
332 * 12 11/30/98 1:07p Dave
333 * 16 bit conversion, first run.
335 * 11 11/20/98 11:16a Dave
336 * Fixed up IPX support a bit. Making sure that switching modes and
337 * loading/saving pilot files maintains proper state.
339 * 10 11/19/98 4:57p Dave
340 * Ignore PXO option if IPX is selected.
342 * 9 11/19/98 4:19p Dave
343 * Put IPX sockets back in psnet. Consolidated all multiplayer config
346 * 8 11/19/98 8:04a Dave
347 * Full support for D3-style reliable sockets. Revamped packet lag/loss
348 * system, made it receiver side and at the lowest possible level.
350 * 7 11/17/98 11:12a Dave
351 * Removed player identification by address. Now assign explicit id #'s.
353 * 6 10/19/98 11:15a Dave
354 * Changed requirements for stats storing in PXO mode.
356 * 5 10/16/98 9:40a Andsager
357 * Remove ".h" files from model.h
359 * 4 10/13/98 9:29a Dave
360 * Started neatening up freespace.h. Many variables renamed and
361 * reorganized. Added AlphaColors.[h,cpp]
363 * 3 10/07/98 6:27p Dave
364 * Globalized mission and campaign file extensions. Removed Silent Threat
365 * special code. Moved \cache \players and \multidata into the \data
368 * 2 10/07/98 10:53a Dave
371 * 1 10/07/98 10:50a Dave
373 * 333 10/02/98 3:22p Allender
374 * fix up the -connect option and fix the -port option
376 * 332 9/17/98 9:26p Dave
377 * Externalized new string.
379 * 331 9/17/98 3:08p Dave
380 * PXO to non-pxo game warning popup. Player icon stuff in create and join
381 * game screens. Upped server count refresh time in PXO to 35 secs (from
384 * 330 9/17/98 9:43a Allender
385 * removed an SDL_assert that Dave called bogus.
387 * 329 9/16/98 6:54p Dave
388 * Upped max sexpression nodes to 1800 (from 1600). Changed FRED to sort
389 * the ship list box. Added code so that tracker stats are not stored with
392 * 328 9/15/98 7:24p Dave
393 * Minor UI changes. Localized bunch of new text.
395 * 327 9/15/98 4:03p Dave
396 * Changed readyroom and multi screens to display "st" icon for all
397 * missions with mission disk content (not necessarily just those that
398 * come with Silent Threat).
400 * 326 9/15/98 11:44a Dave
401 * Renamed builtin ships and wepaons appropriately in FRED. Put in scoring
402 * scale factors. Fixed standalone filtering of MD missions to non-MD
405 * 325 9/13/98 9:36p Dave
406 * Support for new info icons for multiplayer missions (from-volition,
407 * valid, mission disk, etc).
409 * 324 9/11/98 4:14p Dave
410 * Fixed file checksumming of < file_size. Put in more verbose kicking and
411 * PXO stats store reporting.
413 * 323 9/10/98 1:17p Dave
414 * Put in code to flag missions and campaigns as being MD or not in Fred
415 * and Freespace. Put in multiplayer support for filtering out MD
416 * missions. Put in multiplayer popups for warning of non-valid missions.
418 * 322 9/04/98 3:51p Dave
419 * Put in validated mission updating and application during stats
422 * 321 8/31/98 2:06p Dave
423 * Make cfile sort the ordering or vp files. Added support/checks for
424 * recognizing "mission disk" players.
426 * 320 8/21/98 1:15p Dave
427 * Put in log system hooks in useful places.
429 * 319 8/20/98 5:31p Dave
430 * Put in handy multiplayer logfile system. Now need to put in useful
431 * applications of it all over the code.
433 * 318 8/12/98 4:53p Dave
434 * Put in 32 bit checksumming for PXO missions. No validation on the
435 * actual tracker yet, though.
437 * 317 8/07/98 10:40a Allender
438 * new command line flags for starting netgames. Only starting currently
439 * works, and PXO isn't implemented yet
441 * 316 7/24/98 9:27a Dave
442 * Tidied up endgame sequencing by removing several old flags and
443 * standardizing _all_ endgame stuff with a single function call.
445 * 315 7/14/98 10:04a Allender
446 * fixed the countdown code to not be reliant on timer_get_fixed_seconds
448 * 314 7/10/98 5:04p Dave
449 * Fix connection speed bug on standalone server.
451 * 313 7/09/98 6:01p Dave
452 * Firsts full version of PXO updater. Put in stub for displaying
455 * 312 7/07/98 2:49p Dave
458 * 311 6/30/98 2:17p Dave
459 * Revised object update system. Removed updates for all weapons. Put
460 * button info back into control info packet.
462 * 310 6/13/98 9:32p Mike
463 * Kill last character in file which caused "Find in Files" to report the
464 * file as "not a text file."
470 #include <winsock2.h> // for inet_addr()
472 #include <sys/types.h>
473 #include <sys/socket.h>
474 #include <netinet/in.h>
475 #include <arpa/inet.h>
480 #include "multiutil.h"
481 #include "multimsgs.h"
487 #include "gamesequence.h"
488 #include "freespace.h"
489 #include "contexthelp.h"
494 #include "missionshipchoice.h"
495 #include "multi_xfer.h"
497 #include "stand_server.h"
498 #include "linklist.h"
499 #include "multiteamselect.h"
500 #include "missioncampaign.h"
507 #include "missiondebrief.h"
508 #include "multi_ingame.h"
509 #include "multi_kick.h"
510 #include "multi_data.h"
511 #include "multi_campaign.h"
512 #include "multi_team.h"
513 #include "multi_pinfo.h"
514 #include "multi_observer.h"
515 #include "multi_voice.h"
516 #include "multi_endgame.h"
517 #include "managepilot.h"
520 #include "objcollide.h"
522 #include "multi_pmsg.h"
523 #include "multi_obj.h"
524 #include "multi_log.h"
525 #include "alphacolors.h"
526 #include "animplay.h"
527 #include "multi_dogfight.h"
528 #include "missionpause.h"
529 #include "multi_fstracker.h"
530 #include "multi_sw.h"
532 // -------------------------------------------------------------------------------------------------------------
534 // MULTIPLAYER COMMON interface controls
537 // the common text info box stuff. This is lifted almost directly from Alans briefing code (minus the spiffy colored, scrolling
539 int Multi_common_text_coords[GR_NUM_RESOLUTIONS][4] = {
552 int Multi_common_text_max_display[GR_NUM_RESOLUTIONS] = {
561 #define MULTI_COMMON_TEXT_META_CHAR '$'
562 #define MULTI_COMMON_TEXT_MAX_LINE_LENGTH 100
563 #define MULTI_COMMON_TEXT_MAX_LINES 20
564 #define MULTI_COMMON_MAX_TEXT (MULTI_COMMON_TEXT_MAX_LINES * MULTI_COMMON_TEXT_MAX_LINE_LENGTH)
566 char Multi_common_all_text[MULTI_COMMON_MAX_TEXT];
567 char Multi_common_text[MULTI_COMMON_TEXT_MAX_LINES][MULTI_COMMON_TEXT_MAX_LINE_LENGTH];
569 int Multi_common_top_text_line = -1; // where to start displaying from
570 int Multi_common_num_text_lines = 0; // how many lines we have
572 void multi_common_scroll_text_up();
573 void multi_common_scroll_text_down();
574 void multi_common_move_to_bottom();
575 void multi_common_render_text();
576 void multi_common_split_text();
578 #define MAX_IP_STRING 255 // maximum length for ip string
580 void multi_common_scroll_text_up()
582 Multi_common_top_text_line--;
583 if ( Multi_common_top_text_line < 0 ) {
584 Multi_common_top_text_line = 0;
585 if ( !mouse_down(MOUSE_LEFT_BUTTON) )
586 gamesnd_play_iface(SND_GENERAL_FAIL);
589 gamesnd_play_iface(SND_SCROLL);
593 void multi_common_scroll_text_down()
595 Multi_common_top_text_line++;
596 if ( (Multi_common_num_text_lines - Multi_common_top_text_line) < Multi_common_text_max_display[gr_screen.res] ) {
597 Multi_common_top_text_line--;
598 if ( !mouse_down(MOUSE_LEFT_BUTTON) ){
599 gamesnd_play_iface(SND_GENERAL_FAIL);
602 gamesnd_play_iface(SND_SCROLL);
606 void multi_common_move_to_bottom()
608 // if there's nowhere to scroll down, do nothing
609 if(Multi_common_num_text_lines <= Multi_common_text_max_display[gr_screen.res]){
613 Multi_common_top_text_line = Multi_common_num_text_lines - Multi_common_text_max_display[gr_screen.res];
616 void multi_common_set_text(const char *str, int auto_scroll)
619 // store the entire string as well
620 if(strlen(str) > MULTI_COMMON_MAX_TEXT){
623 SDL_strlcpy(Multi_common_all_text, str, SDL_arraysize(Multi_common_all_text));
626 // split the whole thing up
627 multi_common_split_text();
629 // scroll to the bottom if we're supposed to
631 multi_common_move_to_bottom();
635 void multi_common_add_text(const char *str, int auto_scroll)
638 // store the entire string as well
639 if((strlen(str) + strlen(Multi_common_all_text)) > MULTI_COMMON_MAX_TEXT){
642 SDL_strlcat(Multi_common_all_text, str, SDL_arraysize(Multi_common_all_text));
645 // split the whole thing up
646 multi_common_split_text();
648 // scroll to the bottom if we're supposed to
650 multi_common_move_to_bottom();
654 void multi_common_split_text()
657 int n_chars[MAX_BRIEF_LINES];
658 char *p_str[MAX_BRIEF_LINES];
660 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);
661 SDL_assert(n_lines != -1);
663 for ( i = 0; i < n_lines; i++ ) {
664 SDL_assert(n_chars[i] < MULTI_COMMON_TEXT_MAX_LINE_LENGTH);
665 int len = SDL_min(n_chars[i] + 1, MULTI_COMMON_TEXT_MAX_LINE_LENGTH);
666 SDL_strlcpy(Multi_common_text[i], p_str[i], len);
667 Multi_common_text[i][n_chars[i]] = 0;
668 drop_leading_white_space(Multi_common_text[i]);
671 Multi_common_top_text_line = 0;
672 Multi_common_num_text_lines = n_lines;
675 void multi_common_render_text()
677 int i, fh, line_count;
679 fh = gr_get_font_height();
682 gr_set_color_fast(&Color_text_normal);
683 for ( i = Multi_common_top_text_line; i < Multi_common_num_text_lines; i++ ) {
684 if ( line_count >= Multi_common_text_max_display[gr_screen.res] ){
687 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]);
691 if ( (Multi_common_num_text_lines - Multi_common_top_text_line) > Multi_common_text_max_display[gr_screen.res] ) {
692 gr_set_color_fast(&Color_bright_red);
693 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));
697 // common notification messaging stuff
698 #define MULTI_COMMON_NOTIFY_TIME 3500
699 int Multi_common_join_y[GR_NUM_RESOLUTIONS] = {
703 int Multi_common_create_y[GR_NUM_RESOLUTIONS] = {
708 int Multi_common_jw_y[GR_NUM_RESOLUTIONS] = {
713 int Multi_common_msg_y[GR_NUM_RESOLUTIONS] = {
718 char Multi_common_notify_text[200];
719 int Multi_common_notify_stamp;
721 void multi_common_notify_init()
723 SDL_strlcpy(Multi_common_notify_text, "", SDL_arraysize(Multi_common_notify_text));
724 Multi_common_notify_stamp = -1;
727 // add a notification string, drawing appropriately depending on the state/screen we're in
728 void multi_common_add_notify(const char *str)
731 SDL_strlcpy(Multi_common_notify_text, str, SDL_arraysize(Multi_common_notify_text));
732 Multi_common_notify_stamp = timestamp(MULTI_COMMON_NOTIFY_TIME);
736 // process/display notification messages
737 void multi_common_notify_do()
739 if(Multi_common_notify_stamp != -1){
740 if(timestamp_elapsed(Multi_common_notify_stamp)){
741 Multi_common_notify_stamp = -1;
744 gr_get_string_size(&w,&h,Multi_common_notify_text);
745 gr_set_color_fast(&Color_white);
747 // determine where it should be placed based upon which screen we're on
749 switch(gameseq_get_state()){
750 case GS_STATE_MULTI_JOIN_GAME :
751 y = Multi_common_join_y[gr_screen.res];
753 case GS_STATE_MULTI_HOST_SETUP :
754 y = Multi_common_create_y[gr_screen.res];
756 case GS_STATE_MULTI_CLIENT_SETUP :
757 y = Multi_common_jw_y[gr_screen.res];
759 case GS_STATE_MULTI_START_GAME :
760 y = Multi_common_msg_y[gr_screen.res];
764 gr_string((gr_screen.max_w - w)/2, y, Multi_common_notify_text);
771 int Multi_common_icons[MULTI_NUM_COMMON_ICONS];
773 const char *Multi_common_icon_names[MULTI_NUM_COMMON_ICONS] = {
774 "DotRed", // voice denied
775 "DotGreen", // voice recording
776 "OvalGreen", // team 0
777 "OvalGreen01", // team 0 select
779 "OvalRed01", // team 1 select
780 "mp_coop", // coop mission
781 "mp_teams", // TvT mission
782 "mp_furball", // furball mission
783 "icon-volition", // volition mission
784 "icon-valid", // mission is valid
789 "icon-silent" // SilentThreat
793 // width and height of the icons
794 int Multi_common_icon_dims[MULTI_NUM_COMMON_ICONS][2] = {
795 {11, 11}, // voice denied
796 {11, 11}, // voice recording
798 {11, 11}, // team 0 select
800 {11, 11}, // team 1 select
803 {18, 11}, // mp furball
804 {9, 9}, // volition mission
805 {8, 8}, // mission is valid
810 {16, 7} // silent threat
814 void multi_load_common_icons()
819 for(idx=0; idx<MULTI_NUM_COMMON_ICONS; idx++){
820 Multi_common_icons[idx] = -1;
821 Multi_common_icons[idx] = bm_load(Multi_common_icon_names[idx]);
825 void multi_unload_common_icons()
830 for(idx=0; idx<MULTI_NUM_COMMON_ICONS; idx++){
831 if(Multi_common_icons[idx] != -1){
832 bm_unload(Multi_common_icons[idx]);
833 Multi_common_icons[idx] = -1;
838 // display any relevant voice status icons
839 void multi_common_voice_display_status()
841 switch(multi_voice_status()){
842 // i have been denied the voice token
843 case MULTI_VOICE_STATUS_DENIED:
844 if(Multi_common_icons[MICON_VOICE_DENIED] != -1){
845 gr_set_bitmap(Multi_common_icons[MICON_VOICE_DENIED], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
850 // i am currently recording
851 case MULTI_VOICE_STATUS_RECORDING:
852 if(Multi_common_icons[MICON_VOICE_RECORDING] != -1){
853 gr_set_bitmap(Multi_common_icons[MICON_VOICE_RECORDING], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
858 // i am currently playing back sound
859 case MULTI_VOICE_STATUS_PLAYING:
862 // the system is currently idle
863 case MULTI_VOICE_STATUS_IDLE:
869 // palette initialization stuff
870 #define MULTI_COMMON_PALETTE_FNAME "InterfacePalette"
873 int Multi_common_interface_palette = -1;
875 void multi_common_load_palette();
876 void multi_common_set_palette();
877 void multi_common_unload_palette();
879 // load in the palette if it doesn't already exist
880 void multi_common_load_palette()
882 if(Multi_common_interface_palette != -1){
886 Multi_common_interface_palette = bm_load(MULTI_COMMON_PALETTE_FNAME);
887 if(Multi_common_interface_palette == -1){
888 nprintf(("Network","Error loading multiplayer common palette!\n"));
892 // set the common palette to be the active one
893 void multi_common_set_palette()
895 // if the palette is not loaded yet, do so now
896 if(Multi_common_interface_palette == -1){
897 multi_common_load_palette();
901 // unload the bitmap palette
902 void multi_common_unload_palette()
904 if(Multi_common_interface_palette != -1){
905 bm_unload(Multi_common_interface_palette);
906 Multi_common_interface_palette = -1;
910 void multi_common_verify_cd()
916 // -------------------------------------------------------------------------------------------------------------
918 // MULTIPLAYER JOIN SCREEN
921 #define MULTI_JOIN_NUM_BUTTONS 11
925 #define MULTI_JOIN_PALETTE "InterfacePalette"
927 static const char *Multi_join_bitmap_fname[GR_NUM_RESOLUTIONS] = {
928 "MultiJoin", // GR_640
929 "2_MultiJoin" // GR_1024
932 static const char *Multi_join_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
933 "MultiJoin-M", // GR_640
934 "2_MultiJoin-M" // GR_1024
940 const char *Mj_slider_name[GR_NUM_RESOLUTIONS] = {
944 int Mj_slider_coords[GR_NUM_RESOLUTIONS][4] = {
955 #define MJ_SCROLL_UP 0
956 #define MJ_SCROLL_DOWN 1
958 #define MJ_SCROLL_INFO_UP 3
959 #define MJ_SCROLL_INFO_DOWN 4
960 #define MJ_JOIN_OBSERVER 5
961 #define MJ_START_GAME 6
967 // uses MULTI_JOIN_REFRESH_TIME as its timestamp
968 int Multi_join_glr_stamp;
970 #define MULTI_JOIN_PING_TIME 15000 // how often we ping all the known servers
971 int Multi_join_ping_stamp;
972 UI_WINDOW Multi_join_window; // the window object for the join screen
973 UI_BUTTON Multi_join_select_button; // for selecting list items
975 UI_SLIDER2 Multi_join_slider; // handy dandy slider
977 int Multi_join_bitmap; // the background bitmap
979 ui_button_info Multi_join_buttons[GR_NUM_RESOLUTIONS][MULTI_JOIN_NUM_BUTTONS] = {
982 ui_button_info("MJ_00", 0, 85, -1, -1, 0),
983 ui_button_info("MJ_01", 0, 125, -1, -1, 1),
984 ui_button_info("MJ_03", 20, 324, -1, -1, 3),
985 ui_button_info("MJ_04", 0, 399, -1, -1, 4),
986 ui_button_info("MJ_05", 0, 436, -1, -1, 5),
987 ui_button_info("MJ_15", 450, 323, -1, -1, 15),
988 ui_button_info("MJ_10", 519, 323, -1, -1, 10),
989 ui_button_info("MJ_06", 574, 323, -1, -1, 6),
990 ui_button_info("MJ_08", 470, 427, -1, -1, 8),
991 ui_button_info("MJ_09", 448, 454, -1, -1, 9),
992 ui_button_info("MJ_07", 563, 411, -1, -1, 7),
994 ui_button_info( "MJ_00", 1, 57, -1, -1, 0 ), // scroll up
995 ui_button_info( "MJ_02", 1, 297, -1, -1, 2 ), // scroll down
996 ui_button_info( "MJ_03", 10, 338, 65, 364, 3 ), // refresh
997 ui_button_info( "MJ_04", 1, 405, -1, -1, 4 ), // scroll info up
998 ui_button_info( "MJ_05", 1, 446, -1, -1, 5 ), // scroll info down
999 ui_button_info( "MJ_06", 489, 339, -1, -1, 6 ), // join as observer
1000 ui_button_info( "MJ_07", 538, 339, -1, -1, 7 ), // create game
1001 ui_button_info( "MJ_08", 583, 339, 588, 376, 8 ), // cancel
1002 ui_button_info( "MJ_09", 534, 426, -1, -1, 9 ), // help
1003 ui_button_info( "MJ_10", 534, 454, -1, -1, 10 ), // options
1004 ui_button_info( "MJ_11", 571, 426, 589, 416, 11 ), // join
1008 ui_button_info( "2_MJ_00", 2, 92, -1, -1, 0 ), // scroll up
1009 ui_button_info( "2_MJ_02", 2, 475, -1, -1, 2 ), // scroll down
1010 ui_button_info( "2_MJ_03", 16, 541, 104, 582, 3 ), // refresh
1011 ui_button_info( "2_MJ_04", 2, 648, -1, -1, 4 ), // scroll info up
1012 ui_button_info( "2_MJ_05", 2, 713, -1, -1, 5 ), // scroll info down
1013 ui_button_info( "2_MJ_06", 783, 542, -1, -1, 6 ), // join as observer
1014 ui_button_info( "2_MJ_07", 861, 542, -1, -1, 7 ), // create game
1015 ui_button_info( "2_MJ_08", 933, 542, 588, 376, 8 ), // cancel
1016 ui_button_info( "2_MJ_09", 854, 681, -1, -1, 9 ), // help
1017 ui_button_info( "2_MJ_10", 854, 727, -1, -1, 10 ), // options
1018 ui_button_info( "2_MJ_11", 914, 681, 937, 668, 11 ), // join
1023 #define MULTI_JOIN_NUM_TEXT 13
1025 UI_XSTR Multi_join_text[GR_NUM_RESOLUTIONS][MULTI_JOIN_NUM_TEXT] = {
1027 {"Refresh", 1299, 65, 364, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_REFRESH].button},
1028 {"Join as", 1300, 476, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_JOIN_OBSERVER].button},
1029 {"Observer", 1301, 467, 385, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_JOIN_OBSERVER].button},
1030 {"Create", 1408, 535, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_START_GAME].button},
1031 {"Game", 1302, 541, 385, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_START_GAME].button},
1032 {"Cancel", 387, 588, 376, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[0][MJ_CANCEL].button},
1033 {"Help", 928, 479, 436, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_HELP].button},
1034 {"Options", 1036, 479, 460, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_OPTIONS].button},
1035 {"Join", 1303, 589, 416, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[0][MJ_ACCEPT].button},
1036 {"Status", 1304, 37, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
1037 {"Server", 1305, 116, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
1038 {"Players", 1306, 471, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
1039 {"Ping", 1307, 555, 37, UI_XSTR_COLOR_GREEN, -1, NULL}
1042 {"Refresh", 1299, 104, 582, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_REFRESH].button},
1043 {"Join as", 1300, 783, 602, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_JOIN_OBSERVER].button},
1044 {"Observer", 1301, 774, 611, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_JOIN_OBSERVER].button},
1045 {"Create", 1408, 868, 602, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_START_GAME].button},
1046 {"Game", 1302, 872, 611, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_START_GAME].button},
1047 {"Cancel", 387, 941, 602, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[1][MJ_CANCEL].button},
1048 {"Help", 928, 782, 699, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_HELP].button},
1049 {"Options", 1036, 782, 736, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_OPTIONS].button},
1050 {"Join", 1303, 937, 668, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[1][MJ_ACCEPT].button},
1051 {"Status", 1304, 60, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
1052 {"Server", 1305, 186, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
1053 {"Players", 1306, 753, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
1054 {"Ping", 1307, 888, 60, UI_XSTR_COLOR_GREEN, -1, NULL}
1059 // constants for coordinate look ups
1060 #define MJ_X_COORD 0
1061 #define MJ_Y_COORD 1
1062 #define MJ_W_COORD 2
1063 #define MJ_H_COORD 3
1065 #define MULTI_JOIN_SENT_WAIT 10000 // wait this long since a join was sent to allow another
1066 int Multi_join_sent_stamp;
1068 // game information text areas
1069 int Mj_max_game_items[GR_NUM_RESOLUTIONS] = {
1074 int Mj_list_y[GR_NUM_RESOLUTIONS] = {
1079 int Mj_status_coords[GR_NUM_RESOLUTIONS][4] = {
1088 int Mj_game_icon_coords[GR_NUM_RESOLUTIONS][3] = {
1097 int Mj_speed_coords[GR_NUM_RESOLUTIONS][4] = {
1106 int Mj_game_name_coords[GR_NUM_RESOLUTIONS][4] = {
1115 int Mj_players_coords[GR_NUM_RESOLUTIONS][4] = {
1124 int Mj_ping_coords[GR_NUM_RESOLUTIONS][4] = {
1133 // game speed labels
1134 #define MJ_NUM_SPEED_LABELS 5
1135 const char *Multi_join_speed_labels[MJ_NUM_SPEED_LABELS] = {
1142 color *Multi_join_speed_colors[MJ_NUM_SPEED_LABELS] = {
1145 &Color_bright_green,
1146 &Color_bright_green,
1150 int Mj_cd_coords[GR_NUM_RESOLUTIONS] = {
1155 // extents of the entire boundable game info region
1156 // NOTE : these numbers are completely empirical
1157 #define MJ_PING_GREEN 160
1158 #define MJ_PING_YELLOW 300
1160 int Mj_list_area_coords[GR_NUM_RESOLUTIONS][4] = {
1169 // PXO channel filter
1170 #define MJ_PXO_FILTER_Y 0
1172 // special chars to indicate various status modes for servers
1173 #define MJ_CHAR_STANDALONE "*"
1174 #define MJ_CHAR_CAMPAIGN "c"
1177 // various interface indices
1178 int Multi_join_list_start; // where to start displaying from
1179 active_game *Multi_join_list_start_item; // a pointer to the corresponding active_game
1180 int Multi_join_list_selected; // which item we have selected
1181 active_game *Multi_join_selected_item; // a pointer to the corresponding active_game
1183 // use this macro to modify the list start
1184 #define MJ_LIST_START_INC() do { Multi_join_list_start++; } while(0);
1185 #define MJ_LIST_START_DEC() do { Multi_join_list_start--; } while(0);
1186 #define MJ_LIST_START_SET(vl) do { Multi_join_list_start = vl; } while(0);
1188 // if we should be sending a join request at the end of the frame
1189 int Multi_join_should_send = -1;
1191 // master tracker details
1192 int Multi_join_frame_count; // keep a count of frames displayed
1193 int Multi_join_mt_tried_verify; // already tried verifying the pilot with the tracker
1195 // data stuff for auto joining a game
1196 #define MULTI_AUTOJOIN_JOIN_STAMP 2000
1197 #define MULTI_AUTOJOIN_QUERY_STAMP 2000
1199 int Multi_did_autojoin;
1200 net_addr Multi_autojoin_addr;
1201 int Multi_autojoin_join_stamp;
1202 int Multi_autojoin_query_stamp;
1205 join_request Multi_join_request;
1207 // LOCAL function definitions
1208 void multi_join_check_buttons();
1209 void multi_join_button_pressed(int n);
1210 void multi_join_display_games();
1211 void multi_join_blit_game_status(active_game *game, int y);
1212 void multi_join_load_tcp_addrs();
1213 void multi_join_do_netstuff();
1214 void multi_join_ping_all();
1215 void multi_join_process_select();
1216 void multi_join_list_scroll_up();
1217 void multi_join_list_scroll_down();
1218 void multi_join_list_page_up();
1219 void multi_join_list_page_down();
1220 active_game *multi_join_get_game(int n);
1221 void multi_join_cull_timeouts();
1222 void multi_join_handle_item_cull(active_game *item, int item_index);
1223 void multi_join_send_join_request(int as_observer);
1224 void multi_join_create_game();
1225 void multi_join_blit_top_stuff();
1226 int multi_join_maybe_warn();
1227 int multi_join_warn_pxo();
1228 void multi_join_blit_protocol();
1232 active_game ag, *newitem;;
1235 dc_get_arg(ARG_INT);
1236 for(idx=0; idx<Dc_arg_int; idx++){
1237 // stuff some fake info
1238 memset(&ag, 0, sizeof(active_game));
1239 SDL_snprintf(ag.name, SDL_arraysize(ag.name), "Game %d", idx);
1240 ag.version = MULTI_FS_SERVER_VERSION;
1241 ag.comp_version = MULTI_FS_SERVER_VERSION;
1242 ag.server_addr.addr[0] = (char)idx;
1243 ag.flags = (AG_FLAG_COOP | AG_FLAG_FORMING | AG_FLAG_STANDALONE);
1246 newitem = multi_update_active_games(&ag);
1248 // timestamp it so we get random timeouts
1249 if(newitem != NULL){
1250 // newitem->heard_from_timer = timestamp((int)frand_range(500.0f, 10000.0f));
1255 void multi_join_notify_new_game()
1258 // reset the # of items
1259 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);
1260 Multi_join_slider.force_currentItem(Multi_join_list_start);
1264 int multi_join_autojoin_do()
1266 // if we have an active game on the list, then return a positive value so that we
1267 // can join the game
1268 if ( Active_game_head && (Active_game_count > 0) ) {
1269 Multi_join_selected_item = Active_game_head;
1273 // send out a server_query again
1274 if ( timestamp_elapsed(Multi_autojoin_query_stamp) ) {
1275 send_server_query(&Multi_autojoin_addr);
1276 Multi_autojoin_query_stamp = timestamp(MULTI_AUTOJOIN_QUERY_STAMP);
1282 void multi_join_game_init()
1286 // do the multiplayer init stuff - multi_level_init() now does all net_player zeroing.
1287 // setup various multiplayer things
1288 SDL_assert( Game_mode & GM_MULTIPLAYER );
1289 SDL_assert( Net_player != NULL );
1291 memset( &Netgame, 0, sizeof(Netgame) );
1294 Net_player->flags |= NETINFO_FLAG_DO_NETWORKING;
1295 Net_player->player = Player;
1296 memcpy(&Net_player->p_info.addr,&Psnet_my_addr,sizeof(net_addr));
1298 // check for the existence of a CD
1299 multi_common_verify_cd();
1301 // load my local netplayer options
1302 multi_options_local_load(&Net_player->p_info.options, Net_player);
1308 common_set_interface_palette(MULTI_JOIN_PALETTE);
1311 // destroy any chatbox contents which previously existed (from another game)
1314 // create the interface window
1315 Multi_join_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
1316 Multi_join_window.set_mask_bmap(Multi_join_bitmap_mask_fname[gr_screen.res]);
1318 // load the background bitmap
1319 Multi_join_bitmap = bm_load(Multi_join_bitmap_fname[gr_screen.res]);
1320 if(Multi_join_bitmap < 0){
1321 // we failed to load the bitmap - this is very bad
1325 // intialize the endgame system
1326 multi_endgame_init();
1328 // initialize the common notification messaging
1329 multi_common_notify_init();
1331 // initialize the common text area
1332 multi_common_set_text("");
1334 // load and use the common interface palette
1335 multi_common_load_palette();
1336 multi_common_set_palette();
1338 // load the help overlay
1339 help_overlay_load(MULTI_JOIN_OVERLAY);
1340 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1342 // do TCP and VMT specific initialization
1343 if ( !Multi_options_g.pxo ) {
1344 // if this is a TCP (non tracker) game, we'll load up our default address list right now
1345 multi_join_load_tcp_addrs();
1348 // initialize any and all timestamps
1349 Multi_join_glr_stamp = -1;
1350 Multi_join_ping_stamp = -1;
1351 Multi_join_sent_stamp = -1;
1353 // reset frame count
1354 Multi_join_frame_count = 0;
1356 // haven't tried to verify on the tracker yet.
1357 Multi_join_mt_tried_verify = 0;
1359 // clear our all game lists to save hassles
1360 multi_join_clear_game_list();
1362 // create the interface buttons
1363 for(idx=0; idx<MULTI_JOIN_NUM_BUTTONS; idx++){
1364 // create the object
1365 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);
1367 // set the sound to play when highlighted
1368 Multi_join_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
1370 // set the ani for the button
1371 Multi_join_buttons[gr_screen.res][idx].button.set_bmaps(Multi_join_buttons[gr_screen.res][idx].filename);
1374 Multi_join_buttons[gr_screen.res][idx].button.link_hotspot(Multi_join_buttons[gr_screen.res][idx].hotspot);
1379 for(idx=0; idx<MULTI_JOIN_NUM_TEXT; idx++){
1380 Multi_join_window.add_XSTR(&Multi_join_text[gr_screen.res][idx]);
1384 Multi_join_should_send = -1;
1386 // close any previously open chatbox
1389 // create the list item select button
1390 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);
1391 Multi_join_select_button.hide();
1395 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);
1398 // if starting a network game, then go to the create game screen
1399 if ( Cmdline_start_netgame ) {
1400 multi_join_create_game();
1401 } else if ( Cmdline_connect_addr != NULL ) {
1406 // joining a game. Send a join request to the given IP address, and wait for the return.
1407 memset( &Multi_autojoin_addr, 0, sizeof(net_addr) );
1408 Multi_autojoin_addr.type = NET_TCP;
1410 // create the address, looking out for port number at the end
1411 port_num = DEFAULT_GAME_PORT;
1412 p = strrchr(Cmdline_connect_addr, ':');
1416 port_num = (short)atoi(p);
1418 ip_addr = inet_addr(Cmdline_connect_addr);
1419 memcpy(Multi_autojoin_addr.addr, &ip_addr, IP_ADDRESS_LENGTH);
1420 Multi_autojoin_addr.port = port_num;
1422 send_server_query(&Multi_autojoin_addr);
1423 Multi_autojoin_query_stamp = timestamp(MULTI_AUTOJOIN_QUERY_STAMP);
1424 Multi_did_autojoin = 0;
1428 void multi_join_clear_game_list()
1431 Multi_join_list_selected = -1;
1432 Multi_join_selected_item = NULL;
1433 MJ_LIST_START_SET(-1);
1434 Multi_join_list_start_item = NULL;
1436 // free up the active game list
1437 multi_free_active_games();
1439 // initialize the active game list
1440 Active_game_head = NULL;
1441 Active_game_count = 0;
1444 void multi_join_game_do_frame()
1446 // check the status of our reliable socket. If not valid, popup error and return to main menu
1447 // I put this code here to avoid nasty gameseq issues with states. Also, we will have nice
1448 // background for the popup
1449 if ( !psnet_rel_check() ) {
1450 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));
1451 gameseq_post_event(GS_EVENT_MAIN_MENU);
1455 // return here since we will be moving to the next stage anyway -- I don't want to see the backgrounds of
1456 // all the screens for < 1 second for every screen we automatically move to.
1457 if ( Cmdline_start_netgame ) {
1461 // when joining a network game, wait for the server query to come back, and then join the game
1462 if ( Cmdline_connect_addr != NULL ) {
1465 if ( !Multi_did_autojoin ) {
1466 rval = popup_till_condition(multi_join_autojoin_do, XSTR("&Cancel", 779), XSTR("Joining netgame", 1500) );
1468 // cancel was hit. Send the user back to the main hall
1469 gameseq_post_event(GS_EVENT_MAIN_MENU);
1470 Cmdline_connect_addr = NULL; // reset this value.
1473 // when we get here, we have the data -- join the game.
1474 multi_join_send_join_request(0);
1475 Multi_autojoin_join_stamp = timestamp(MULTI_AUTOJOIN_JOIN_STAMP);
1476 Multi_did_autojoin = 1;
1479 if ( timestamp_elapsed(Multi_autojoin_join_stamp) ) {
1480 multi_join_send_join_request(0);
1481 Multi_autojoin_join_stamp = timestamp(MULTI_AUTOJOIN_JOIN_STAMP);
1487 // reset the should send var
1488 Multi_join_should_send = -1;
1490 int k = Multi_join_window.process();
1492 // process any keypresses
1495 if(help_overlay_active(MULTI_JOIN_OVERLAY)){
1496 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1498 if (Multi_options_g.pxo == 1) {
1499 gameseq_post_event(GS_EVENT_PXO);
1501 gameseq_post_event(GS_EVENT_MAIN_MENU);
1504 gamesnd_play_iface(SND_USER_SELECT);
1508 // page up the game list
1510 multi_join_list_page_up();
1512 Multi_join_slider.force_currentItem(Multi_join_list_start);
1517 multi_pinfo_popup(Net_player);
1520 // page down the game list
1522 multi_join_list_page_down();
1524 Multi_join_slider.force_currentItem(Multi_join_list_start);
1528 // send out a ping-all
1530 multi_join_ping_all();
1531 Multi_join_ping_stamp = timestamp(MULTI_JOIN_PING_TIME);
1534 // shortcut to start a game
1536 multi_join_create_game();
1539 // scroll the game list up
1541 multi_join_list_scroll_up();
1543 Multi_join_slider.force_currentItem(Multi_join_list_start);
1547 // scroll the game list down
1549 multi_join_list_scroll_down();
1551 Multi_join_slider.force_currentItem(Multi_join_list_start);
1556 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
1557 help_overlay_set_state(MULTI_JOIN_OVERLAY, 0);
1560 // do any network related stuff
1561 multi_join_do_netstuff();
1563 // process any button clicks
1564 multi_join_check_buttons();
1566 // process any list selection stuff
1567 multi_join_process_select();
1569 // draw the background, etc
1571 GR_MAYBE_CLEAR_RES(Multi_join_bitmap);
1572 if(Multi_join_bitmap != -1){
1573 gr_set_bitmap(Multi_join_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1576 Multi_join_window.draw();
1578 // display the active games
1579 multi_join_display_games();
1581 // display any text in the info area
1582 multi_common_render_text();
1584 // display any pending notification messages
1585 multi_common_notify_do();
1587 // blit the CD icon and any PXO filter stuff
1588 multi_join_blit_top_stuff();
1590 // draw the help overlay
1591 help_overlay_maybe_blit(MULTI_JOIN_OVERLAY);
1596 // if we are supposed to be sending a join request
1597 if(Multi_join_should_send != -1){
1598 multi_join_send_join_request(Multi_join_should_send);
1600 Multi_join_should_send = -1;
1602 // increment the frame count
1603 Multi_join_frame_count++;
1606 void multi_join_game_close()
1608 // unload any bitmaps
1609 if(!bm_unload(Multi_join_bitmap)){
1610 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_join_bitmap_fname[gr_screen.res]));
1613 // unload the help overlay
1614 help_overlay_unload(MULTI_JOIN_OVERLAY);
1616 // free up the active game list
1617 multi_free_active_games();
1619 // destroy the UI_WINDOW
1620 Multi_join_window.destroy();
1623 common_free_interface_palette();
1627 void multi_join_check_buttons()
1630 for(idx=0;idx<MULTI_JOIN_NUM_BUTTONS;idx++){
1631 // we only really need to check for one button pressed at a time, so we can break after
1633 if(Multi_join_buttons[gr_screen.res][idx].button.pressed()){
1634 multi_join_button_pressed(idx);
1640 void multi_join_button_pressed(int n)
1644 // if we're player PXO, go back there
1645 if (Multi_options_g.pxo == 1) {
1646 gameseq_post_event(GS_EVENT_PXO);
1648 gameseq_post_event(GS_EVENT_MAIN_MENU);
1650 gamesnd_play_iface(SND_USER_SELECT);
1653 if(Active_game_count <= 0){
1654 multi_common_add_notify(XSTR("No games found!",757));
1655 gamesnd_play_iface(SND_GENERAL_FAIL);
1656 } else if(Multi_join_list_selected == -1){
1657 multi_common_add_notify(XSTR("No game selected!",758));
1658 gamesnd_play_iface(SND_GENERAL_FAIL);
1659 } else if((Multi_join_sent_stamp != -1) && !timestamp_elapsed(Multi_join_sent_stamp)){
1660 multi_common_add_notify(XSTR("Still waiting on previous join request!",759));
1661 gamesnd_play_iface(SND_GENERAL_FAIL);
1663 // otherwise, if he's already played PXO games, warn him
1665 if(Player->flags & PLAYER_FLAGS_HAS_PLAYED_PXO){
1666 if(!multi_join_warn_pxo()){
1672 // send the join request here
1673 SDL_assert(Multi_join_selected_item != NULL);
1675 // send a join request packet
1676 Multi_join_should_send = 0;
1678 gamesnd_play_iface(SND_COMMIT_PRESSED);
1684 if(!help_overlay_active(MULTI_JOIN_OVERLAY)){
1685 help_overlay_set_state(MULTI_JOIN_OVERLAY,1);
1687 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1691 // scroll the game list up
1693 multi_join_list_scroll_up();
1695 Multi_join_slider.force_currentItem(Multi_join_list_start);
1699 // scroll the game list down
1700 case MJ_SCROLL_DOWN:
1701 multi_join_list_scroll_down();
1703 Multi_join_slider.force_currentItem(Multi_join_list_start);
1707 // scroll the info text box up
1708 case MJ_SCROLL_INFO_UP:
1709 multi_common_scroll_text_up();
1712 // scroll the info text box down
1713 case MJ_SCROLL_INFO_DOWN:
1714 multi_common_scroll_text_down();
1717 // go to the options screen
1719 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
1722 // go to the start game screen
1724 multi_join_create_game();
1727 // refresh the game/server list
1729 gamesnd_play_iface(SND_USER_SELECT);
1730 broadcast_game_query();
1733 // join a game as an observer
1734 case MJ_JOIN_OBSERVER:
1735 if(Active_game_count <= 0){
1736 multi_common_add_notify(XSTR("No games found!",757));
1737 gamesnd_play_iface(SND_GENERAL_FAIL);
1738 } else if(Multi_join_list_selected == -1){
1739 multi_common_add_notify(XSTR("No game selected!",758));
1740 gamesnd_play_iface(SND_GENERAL_FAIL);
1741 } else if((Multi_join_sent_stamp != -1) && !timestamp_elapsed(Multi_join_sent_stamp)){
1742 multi_common_add_notify(XSTR("Still waiting on previous join request!",759));
1743 gamesnd_play_iface(SND_GENERAL_FAIL);
1745 // send the join request here
1746 SDL_assert(Multi_join_selected_item != NULL);
1748 Multi_join_should_send = 1;
1750 gamesnd_play_iface(SND_COMMIT_PRESSED);
1755 multi_common_add_notify(XSTR("Not implemented yet!",760));
1756 gamesnd_play_iface(SND_GENERAL_FAIL);
1761 // display all relevant info for active games
1762 void multi_join_display_games()
1764 active_game *moveup = Multi_join_list_start_item;
1768 int y_start = Mj_list_y[gr_screen.res];
1773 // blit the game status (including text and type icon)
1774 multi_join_blit_game_status(moveup,y_start);
1776 // get the connection type
1777 con_type = (moveup->flags & AG_FLAG_CONNECTION_SPEED_MASK) >> AG_FLAG_CONNECTION_BIT;
1778 if((con_type > 4) || (con_type < 0)){
1782 // display the connection speed
1784 SDL_strlcpy(str, Multi_join_speed_labels[con_type], SDL_arraysize(str));
1785 gr_set_color_fast(Multi_join_speed_colors[con_type]);
1786 gr_string(Mj_speed_coords[gr_screen.res][MJ_X_COORD], y_start, str);
1788 // we'll want to have different colors for highlighted items, etc.
1789 if(moveup == Multi_join_selected_item){
1790 gr_set_color_fast(&Color_text_selected);
1792 gr_set_color_fast(&Color_text_normal);
1795 // display the game name, adding appropriate status chars
1797 if(moveup->flags & AG_FLAG_STANDALONE){
1798 SDL_strlcat(str, MJ_CHAR_STANDALONE, SDL_arraysize(str));
1800 if(moveup->flags & AG_FLAG_CAMPAIGN){
1801 SDL_strlcat(str, MJ_CHAR_CAMPAIGN, SDL_arraysize(str));
1804 // tack on the actual server name
1805 SDL_strlcat(str, " ", SDL_arraysize(str));
1806 SDL_strlcat(str, moveup->name, SDL_arraysize(str));
1807 if(strlen(moveup->mission_name) > 0){
1808 SDL_strlcat(str, " / ", SDL_arraysize(str));
1809 SDL_strlcat(str, moveup->mission_name, SDL_arraysize(str));
1812 // make sure the string fits in the display area and draw it
1813 gr_force_fit_string(str,200,Mj_game_name_coords[gr_screen.res][MJ_W_COORD]);
1814 gr_string(Mj_game_name_coords[gr_screen.res][MJ_X_COORD],y_start,str);
1816 // display the ping time
1817 if(moveup->ping.ping_avg > 0){
1818 if(moveup->ping.ping_avg > 1000){
1819 gr_set_color_fast(&Color_bright_red);
1820 SDL_strlcpy(str, XSTR("> 1 sec",761), SDL_arraysize(str));
1822 // set the appropriate ping time color indicator
1823 if(moveup->ping.ping_avg > MJ_PING_YELLOW){
1824 gr_set_color_fast(&Color_bright_red);
1825 } else if(moveup->ping.ping_avg > MJ_PING_YELLOW){
1826 gr_set_color_fast(&Color_bright_yellow);
1828 gr_set_color_fast(&Color_bright_green);
1831 SDL_snprintf(str, SDL_arraysize(str), "%d%s", moveup->ping.ping_avg, XSTR(" ms",762));
1834 gr_string(Mj_ping_coords[gr_screen.res][MJ_X_COORD],y_start,str);
1837 // display the number of players (be sure to center it)
1838 if(moveup == Multi_join_selected_item){
1839 gr_set_color_fast(&Color_text_selected);
1841 gr_set_color_fast(&Color_text_normal);
1843 SDL_snprintf(str, SDL_arraysize(str), "%d", moveup->num_players);
1844 gr_get_string_size(&w,&h,str);
1845 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);
1849 moveup = moveup->next;
1850 } while((moveup != Active_game_head) && (count < Mj_max_game_items[gr_screen.res]));
1852 // if there are no items on the list, display this info
1854 gr_set_color_fast(&Color_bright);
1855 gr_string(Mj_game_name_coords[gr_screen.res][MJ_X_COORD] - 30,y_start,XSTR("<No game servers found>",763));
1859 void multi_join_blit_game_status(active_game *game, int y)
1862 char status_text[25];
1864 // blit the proper icon
1866 switch( game->flags & AG_FLAG_TYPE_MASK ){
1869 if(Multi_common_icons[MICON_COOP] != -1){
1870 gr_set_bitmap(Multi_common_icons[MICON_COOP], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1875 // team vs. team game
1877 if(Multi_common_icons[MICON_TVT] != -1){
1878 gr_set_bitmap(Multi_common_icons[MICON_TVT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1885 case AG_FLAG_DOGFIGHT:
1886 if(Multi_common_icons[MICON_DOGFIGHT] != -1){
1887 gr_set_bitmap(Multi_common_icons[MICON_DOGFIGHT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1893 // if we're supposed to draw a bitmap
1895 gr_bitmap(Mj_game_icon_coords[gr_screen.res][MJ_X_COORD],y-1);
1898 // blit the proper status text
1899 memset(status_text,0,25);
1901 switch( game->flags & AG_FLAG_STATE_MASK ){
1902 case AG_FLAG_FORMING:
1903 gr_set_color_fast(&Color_bright_green);
1904 SDL_strlcpy(status_text, XSTR("Forming", 764), SDL_arraysize(status_text));
1906 case AG_FLAG_BRIEFING:
1907 gr_set_color_fast(&Color_bright_red);
1908 SDL_strlcpy(status_text, XSTR("Briefing", 765), SDL_arraysize(status_text));
1910 case AG_FLAG_DEBRIEF:
1911 gr_set_color_fast(&Color_bright_red);
1912 SDL_strlcpy(status_text, XSTR("Debrief", 766), SDL_arraysize(status_text));
1915 gr_set_color_fast(&Color_bright_red);
1916 SDL_strlcpy(status_text, XSTR("Paused", 767), SDL_arraysize(status_text));
1918 case AG_FLAG_IN_MISSION:
1919 gr_set_color_fast(&Color_bright_red);
1920 SDL_strlcpy(status_text, XSTR("Playing", 768), SDL_arraysize(status_text));
1923 gr_set_color_fast(&Color_bright);
1924 SDL_strlcpy(status_text, XSTR("Unknown", 769), SDL_arraysize(status_text));
1927 gr_get_string_size(&str_w,NULL,status_text);
1928 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);
1931 // load in a list of active games from our tcp.cfg file
1932 void multi_join_load_tcp_addrs()
1934 char line[MAX_IP_STRING];
1939 // attempt to open the ip list file
1940 file = cfopen(IP_CONFIG_FNAME,"rt",CFILE_NORMAL,CF_TYPE_DATA);
1942 nprintf(("Network","Error loading tcp.cfg file!\n"));
1946 // free up any existing server list
1947 multi_free_server_list();
1949 // read in all the strings in the file
1950 while(!cfeof(file)){
1952 cfgets(line,MAX_IP_STRING,file);
1954 // strip off any newline character
1955 if(line[strlen(line) - 1] == '\n'){
1956 line[strlen(line) - 1] = '\0';
1959 // empty lines don't get processed
1960 if( (line[0] == '\0') || (line[0] == '\n') ){
1964 if ( !psnet_is_valid_ip_string(line) ) {
1965 nprintf(("Network","Invalid ip string (%s)\n",line));
1967 // copy the server ip address
1968 memset(&addr,0,sizeof(net_addr));
1969 addr.type = NET_TCP;
1970 psnet_string_to_addr(&addr, line, SDL_arraysize(line));
1971 if ( addr.port == 0 ){
1972 addr.port = DEFAULT_GAME_PORT;
1975 // create a new server item on the list
1976 item = multi_new_server_item();
1978 memcpy(&item->server_addr,&addr,sizeof(net_addr));
1986 // do stuff like pinging servers, sending out requests, etc
1987 void multi_join_do_netstuff()
1989 // handle game query stuff
1990 if(Multi_join_glr_stamp == -1){
1991 broadcast_game_query();
1993 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
1994 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME_LOCAL);
1996 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME);
1999 // otherwise send out game query and restamp
2000 else if(timestamp_elapsed(Multi_join_glr_stamp)){
2001 broadcast_game_query();
2003 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
2004 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME_LOCAL);
2006 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME);
2010 // check to see if we've been accepted. If so, put up message saying so
2011 if ( Net_player->flags & (NETINFO_FLAG_ACCEPT_INGAME|NETINFO_FLAG_ACCEPT_CLIENT|NETINFO_FLAG_ACCEPT_HOST|NETINFO_FLAG_ACCEPT_OBSERVER) ) {
2012 multi_common_add_notify(XSTR("Accepted. Waiting for player data.",770));
2015 // check to see if any join packets we have sent have timed out
2016 if((Multi_join_sent_stamp != -1) && (timestamp_elapsed(Multi_join_sent_stamp))){
2017 Multi_join_sent_stamp = -1;
2018 multi_common_add_notify(XSTR("Join request timed out!",771));
2021 // check to see if we should be pinging everyone
2022 if((Multi_join_ping_stamp == -1) || (timestamp_elapsed(Multi_join_ping_stamp))){
2023 multi_join_ping_all();
2024 Multi_join_ping_stamp = timestamp(MULTI_JOIN_PING_TIME);
2028 multi_join_cull_timeouts();
2031 // evaluate a returned pong.
2032 void multi_join_eval_pong(net_addr *addr, fix pong_time)
2035 active_game *moveup = Active_game_head;
2040 if(psnet_same(&moveup->server_addr,addr)){
2042 multi_ping_eval_pong(&moveup->ping);
2046 moveup = moveup->next;
2048 } while(moveup != Active_game_head);
2051 // update the game's ping
2053 if(found && (moveup->ping_end > moveup->ping_start)){
2054 moveup->ping_time = f2fl(moveup->ping_end - moveup->ping_start);
2055 moveup->ping_start = -1;
2056 moveup->ping_end = -1;
2061 // ping all the server on the list
2062 void multi_join_ping_all()
2064 active_game *moveup = Active_game_head;
2069 moveup->ping_start = timer_get_fixed_seconds();
2070 moveup->ping_end = -1;
2071 send_ping(&moveup->server_addr);
2073 multi_ping_send(&moveup->server_addr,&moveup->ping);
2075 moveup = moveup->next;
2076 } while(moveup != Active_game_head);
2080 void multi_join_process_select()
2082 // if we don't have anything selected and there are items on the list - select the first one
2083 if((Multi_join_list_selected == -1) && (Active_game_count > 0)){
2084 Multi_join_list_selected = 0;
2085 Multi_join_selected_item = multi_join_get_game(0);
2086 MJ_LIST_START_SET(0);
2087 Multi_join_list_start_item = Multi_join_selected_item;
2089 // send a mission description request to this guy
2090 send_netgame_descript_packet(&Multi_join_selected_item->server_addr,0);
2091 multi_common_set_text("");
2093 // I sure hope this doesn't happen
2094 SDL_assert(Multi_join_selected_item != NULL);
2097 // otherwise see if he's clicked on an item
2098 else if(Multi_join_select_button.pressed() && (Active_game_count > 0)){
2100 Multi_join_select_button.get_mouse_pos(NULL,&y);
2102 if(item + Multi_join_list_start < Active_game_count){
2103 gamesnd_play_iface(SND_IFACE_MOUSE_CLICK);
2105 Multi_join_list_selected = item + Multi_join_list_start;
2106 Multi_join_selected_item = multi_join_get_game(Multi_join_list_selected);
2108 // I sure hope this doesn't happen
2109 SDL_assert(Multi_join_selected_item != NULL);
2111 // send a mission description request to this guy
2112 send_netgame_descript_packet(&Multi_join_selected_item->server_addr,0);
2113 multi_common_set_text("");
2117 // if he's double clicked, then select it and accept
2118 if(Multi_join_select_button.double_clicked()){
2120 Multi_join_select_button.get_mouse_pos(NULL,&y);
2122 if(item == Multi_join_list_selected){
2123 multi_join_button_pressed(MJ_ACCEPT);
2128 // return game n (0 based index)
2129 active_game *multi_join_get_game(int n)
2131 active_game *moveup = Active_game_head;
2138 moveup = moveup->next;
2139 while((moveup != Active_game_head) && (count != n)){
2140 moveup = moveup->next;
2143 if(moveup == Active_game_head){
2144 nprintf(("Network","Warning, couldn't find game item %d!\n",n));
2154 // scroll through the game list
2155 void multi_join_list_scroll_up()
2157 // if we're not at the beginning of the list, scroll up
2158 if(Multi_join_list_start_item != Active_game_head){
2159 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2161 MJ_LIST_START_DEC();
2163 gamesnd_play_iface(SND_SCROLL);
2165 gamesnd_play_iface(SND_GENERAL_FAIL);
2169 // scroll through the game list
2170 void multi_join_list_scroll_down()
2172 if((Active_game_count - Multi_join_list_start) > Mj_max_game_items[gr_screen.res]){
2173 Multi_join_list_start_item = Multi_join_list_start_item->next;
2175 MJ_LIST_START_INC();
2177 gamesnd_play_iface(SND_SCROLL);
2179 gamesnd_play_iface(SND_GENERAL_FAIL);
2183 void multi_join_list_page_up()
2185 // in this case, just set us to the beginning of the list
2186 if((Multi_join_list_start - Mj_max_game_items[gr_screen.res]) < 0){
2187 Multi_join_list_start_item = Active_game_head;
2189 MJ_LIST_START_SET(0);
2191 gamesnd_play_iface(SND_SCROLL);
2193 // otherwise page the whole thing up
2195 for(idx=0; idx<Mj_max_game_items[gr_screen.res]; idx++){
2196 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2198 MJ_LIST_START_DEC();
2200 gamesnd_play_iface(SND_SCROLL);
2204 void multi_join_list_page_down()
2208 // page the whole thing down
2209 while((count < Mj_max_game_items[gr_screen.res]) && ((Active_game_count - Multi_join_list_start) > Mj_max_game_items[gr_screen.res])){
2210 Multi_join_list_start_item = Multi_join_list_start_item->next;
2211 MJ_LIST_START_INC();
2216 gamesnd_play_iface(SND_SCROLL);
2219 void multi_join_cull_timeouts()
2221 active_game *backup;
2223 active_game *moveup = Active_game_head;
2225 // traverse through the entire list if any items exist
2229 if((moveup->heard_from_timer != -1) && (timestamp_elapsed(moveup->heard_from_timer))){
2230 Active_game_count--;
2232 // if this is the head of the list
2233 if(moveup == Active_game_head){
2234 // if this is the _only_ item on the list
2235 if(moveup->next == Active_game_head){
2236 // handle any gui details related to deleting this item
2237 multi_join_handle_item_cull(Active_game_head, count);
2239 free(Active_game_head);
2240 Active_game_head = NULL;
2243 // if there are other items on the list
2245 // handle any gui details related to deleting this item
2246 multi_join_handle_item_cull(moveup, count);
2248 Active_game_head = moveup->next;
2249 Active_game_head->prev = moveup->prev;
2250 Active_game_head->prev->next = Active_game_head;
2252 moveup = Active_game_head;
2255 // if its somewhere else on the list
2257 // handle any gui details related to deleting this item
2258 multi_join_handle_item_cull(moveup, count);
2260 // if its the last item on the list
2261 moveup->next->prev = moveup->prev;
2262 moveup->prev->next = moveup->next;
2264 // if it was the last element on the list, return
2265 if(moveup->next == Active_game_head){
2269 backup = moveup->next;
2275 moveup = moveup->next;
2278 } while(moveup != Active_game_head);
2282 // deep magic begins here.
2283 void multi_join_handle_item_cull(active_game *item, int item_index)
2285 // if this is the only item on the list, unset everything
2286 if(item->next == item){
2287 Multi_join_list_selected = -1;
2288 Multi_join_selected_item = NULL;
2291 Multi_join_slider.set_numberItems(0);
2293 MJ_LIST_START_SET(-1);
2294 Multi_join_list_start_item = NULL;
2300 // see if we should be adjusting our currently selected item
2301 if(item_index <= Multi_join_list_selected){
2302 // the selected item is the head of the list
2303 if(Multi_join_selected_item == Active_game_head){
2304 // move the pointer up since this item is about to be destroyed
2305 Multi_join_selected_item = Multi_join_selected_item->next;
2307 // if this is the item being deleted, select the previous one
2308 if(item == Multi_join_selected_item){
2310 Multi_join_selected_item = Multi_join_selected_item->prev;
2312 // decrement the selected index by 1
2313 Multi_join_list_selected--;
2315 // now we know its a previous item, so our pointer stays the same but our index goes down by one, since there will be
2316 // 1 less item on the list
2318 // decrement the selected index by 1
2319 Multi_join_list_selected--;
2324 // see if we should be adjusting out current start position
2325 if(item_index <= Multi_join_list_start){
2326 // the start position is the head of the list
2327 if(Multi_join_list_start_item == Active_game_head){
2328 // move the pointer up since this item is about to be destroyed
2329 Multi_join_list_start_item = Multi_join_list_start_item->next;
2331 // if this is the item being deleted, select the previous one
2332 if(item == Multi_join_list_start_item){
2333 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2335 // decrement the starting index by 1
2336 MJ_LIST_START_DEC();
2338 // but decrement the starting index by 1
2339 MJ_LIST_START_DEC();
2344 // maybe go back up a bit so that we always have a full page of items
2345 if(Active_game_count > Mj_max_game_items[gr_screen.res]){
2346 while((Active_game_count - Multi_join_list_start) < Mj_max_game_items[gr_screen.res]){
2347 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2348 MJ_LIST_START_DEC();
2352 // set slider location
2354 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);
2355 Multi_join_slider.force_currentItem(Multi_join_list_start);
2359 void multi_join_send_join_request(int as_observer)
2361 // don't do anything if we have no items selected
2362 if(Multi_join_selected_item == NULL){
2366 // 5/26/98 -- for team v team games, don't allow ingame joining :-(
2367 if ( (Multi_join_selected_item->flags & AG_FLAG_TEAMS) && (Multi_join_selected_item->flags & (AG_FLAG_PAUSE|AG_FLAG_IN_MISSION)) ) {
2368 popup(0, 1, POPUP_OK, XSTR("Joining ingame is currently not allowed for team vs. team games",772));
2372 memset(&Multi_join_request,0,sizeof(join_request));
2374 // if the netgame is in password mode, put up a request for the password
2375 if(Multi_join_selected_item->flags & AG_FLAG_PASSWD){
2376 if(!multi_passwd_popup(Multi_join_request.passwd, SDL_arraysize(Multi_join_request.passwd))){
2380 mprintf(("Password : %s\n",Multi_join_request.passwd));
2383 // fill out the join request struct
2384 SDL_strlcpy(Multi_join_request.callsign, Player->callsign, SDL_arraysize(Multi_join_request.callsign));
2385 if(strlen(Player->image_filename) > 0){
2386 SDL_strlcpy(Multi_join_request.image_filename, Player->image_filename, SDL_arraysize(Multi_join_request.image_filename));
2389 if(strlen(Player->squad_filename) > 0){
2390 SDL_strlcpy(Multi_join_request.squad_filename, Player->squad_filename, SDL_arraysize(Multi_join_request.squad_filename));
2394 // tracker id (if any)
2395 Multi_join_request.tracker_id = Multi_tracker_id;
2397 // player's rank (at least, what he wants you to _believe_)
2398 Multi_join_request.player_rank = (ubyte)Player->stats.rank;
2401 Multi_join_request.flags = 0;
2403 Multi_join_request.flags |= JOIN_FLAG_AS_OBSERVER;
2406 // if the player has hacked data
2407 if(game_hacked_data()){
2408 Multi_join_request.flags |= JOIN_FLAG_HAXOR;
2413 SDL_strlcpy(Multi_join_request.pxo_squad_name, Multi_tracker_squad_name, LOGIN_LEN);
2416 // version of this server
2417 Multi_join_request.version = MULTI_FS_SERVER_VERSION;
2419 // server compatible version
2420 Multi_join_request.comp_version = MULTI_FS_SERVER_COMPATIBLE_VERSION;
2422 // his local player options
2423 memcpy(&Multi_join_request.player_options,&Player->m_local_options,sizeof(multi_local_options));
2425 // set the server address for the netgame
2426 memcpy(&Netgame.server_addr,&Multi_join_selected_item->server_addr,sizeof(net_addr));
2428 // send a join request to the guy
2429 send_join_packet(&Multi_join_selected_item->server_addr,&Multi_join_request);
2432 Multi_join_sent_stamp = timestamp(MULTI_JOIN_SENT_WAIT);
2435 multi_common_add_notify(XSTR("Sending join request...",773));
2438 void multi_join_create_game()
2440 // maybe warn the player about possible crappy server conditions
2441 if(!multi_join_maybe_warn()){
2445 // make sure to flag ourself as being the master
2446 Net_player->flags |= (NETINFO_FLAG_AM_MASTER | NETINFO_FLAG_GAME_HOST);
2447 Net_player->state = NETPLAYER_STATE_HOST_SETUP;
2449 // if we're in PXO mode, mark it down in our player struct
2450 if(MULTI_IS_TRACKER_GAME){
2451 Player->flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
2452 Player->save_flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
2454 // otherwise, if he's already played PXO games, warn him
2457 if(Player->flags & PLAYER_FLAGS_HAS_PLAYED_PXO){
2458 if(!multi_join_warn_pxo()){
2465 gameseq_post_event(GS_EVENT_MULTI_START_GAME);
2466 gamesnd_play_iface(SND_USER_SELECT);
2469 void multi_join_reset_join_stamp()
2471 // unset the timestamp here so the user can immediately send another join request
2472 Multi_join_sent_stamp = -1;
2473 multi_common_add_notify("");
2476 void multi_join_blit_top_stuff()
2478 // blit the cd icon if he has one
2479 if(Multi_has_cd && (Multi_common_icons[MICON_CD] != -1)){
2482 bm_get_info(Multi_common_icons[MICON_CD], &cd_w, NULL, NULL, NULL, NULL);
2484 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
2485 gr_bitmap((gr_screen.max_w / 2) - (cd_w / 2), Mj_cd_coords[gr_screen.res]);
2489 #define CW_CODE_CANCEL 0 // cancel the action
2490 #define CW_CODE_OK 1 // continue anyway
2491 #define CW_CODE_INFO 2 // gimme some more information
2493 #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)
2494 #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)
2496 #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)
2497 #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)
2499 int multi_join_warn_update_low(int code)
2503 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),LOW_WARN_TEXT);
2506 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),LOW_INFO_TEXT);
2509 return CW_CODE_CANCEL;
2512 int multi_join_warn_update_medium(int code)
2516 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),MED_WARN_TEXT);
2519 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),MED_INFO_TEXT);
2522 return CW_CODE_CANCEL;
2525 int multi_join_maybe_warn()
2529 // if the player is set for low updates
2530 if(Player->m_local_options.obj_update_level == OBJ_UPDATE_LOW){
2533 code = multi_join_warn_update_low(code);
2534 } while((code != CW_CODE_CANCEL) && (code != CW_CODE_OK));
2539 // if the player is set for medium updates
2540 else if(Player->m_local_options.obj_update_level == OBJ_UPDATE_MEDIUM){
2543 code = multi_join_warn_update_medium(code);
2544 } while((code != CW_CODE_CANCEL) && (code != CW_CODE_OK));
2552 int multi_join_warn_pxo()
2554 // 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;
2558 void multi_join_blit_protocol()
2560 gr_set_color_fast(&Color_bright);
2562 switch(Socket_type){
2565 gr_string(5, 2, "TCP");
2574 // -------------------------------------------------------------------------------------------------
2576 // MULTIPLAYER START GAME screen
2581 #define MULTI_SG_PALETTE "InterfacePalette"
2583 static const char *Multi_sg_bitmap_fname[GR_NUM_RESOLUTIONS] = {
2584 "MultiStartGame", // GR_640
2585 "2_MultiStartGame" // GR_1024
2588 static const char *Multi_sg_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
2589 "MultiStartGame-M", // GR_640
2590 "2_MultiStartGame-M" // GR_1024
2595 int Multi_sg_rank_max_display[GR_NUM_RESOLUTIONS] = {
2600 // constants for coordinate look ups
2601 #define MSG_X_COORD 0
2602 #define MSG_Y_COORD 1
2603 #define MSG_W_COORD 2
2604 #define MSG_H_COORD 3
2608 // input password field
2609 int Msg_passwd_coords[GR_NUM_RESOLUTIONS][4] = {
2622 // input game title field
2623 int Msg_title_coords[GR_NUM_RESOLUTIONS][4] = {
2636 // rank selected field
2637 int Msg_rank_sel_coords[GR_NUM_RESOLUTIONS][4] = {
2651 int Msg_rank_list_coords[GR_NUM_RESOLUTIONS][4] = {
2667 #define MULTI_SG_NUM_BUTTONS 12
2668 #define MSG_OPEN_GAME 0
2669 #define MSG_CLOSED_GAME 1
2670 #define MSG_PASSWD_GAME 2
2671 #define MSG_RESTRICTED_GAME 3
2672 #define MSG_RANK_SET_GAME 4
2673 #define MSG_RANK_SCROLL_UP 5
2674 #define MSG_RANK_SCROLL_DOWN 6
2675 #define MSG_RANK_ABOVE 7
2676 #define MSG_RANK_BELOW 8
2678 #define MSG_OPTIONS 10
2679 #define MSG_ACCEPT 11
2681 #define MULTI_SG_NUM_BUTTONS 10
2682 #define MSG_OPEN_GAME 0
2683 //#define MSG_CLOSED_GAME 1
2684 //#define MSG_RESTRICTED_GAME 2
2685 #define MSG_PASSWD_GAME 1
2686 #define MSG_RANK_SET_GAME 2
2687 #define MSG_RANK_SCROLL_UP 3
2688 #define MSG_RANK_SCROLL_DOWN 4
2689 #define MSG_RANK_ABOVE 5
2690 #define MSG_RANK_BELOW 6
2692 #define MSG_OPTIONS 8
2693 #define MSG_ACCEPT 9
2696 UI_WINDOW Multi_sg_window; // the window object for the join screen
2697 UI_BUTTON Multi_sg_rank_button; // for selecting the rank marker
2698 UI_INPUTBOX Multi_sg_game_name; // for Netgame.name
2699 UI_INPUTBOX Multi_sg_game_passwd; // for Netgame.passwd
2700 int Multi_sg_bitmap; // the background bitmap
2702 ui_button_info Multi_sg_buttons[GR_NUM_RESOLUTIONS][MULTI_SG_NUM_BUTTONS] = {
2705 ui_button_info("MSG_00", 75, 111, -1, -1, 0),
2706 ui_button_info("MSG_01", 75, 139, -1, -1, 1),
2707 ui_button_info("MSG_02", 75, 164, -1, -1, 2),
2708 ui_button_info("MSG_03", 75, 199, -1, -1, 3),
2709 ui_button_info("MSG_04", 75, 243, -1, -1, 4),
2710 ui_button_info("MSG_05", 376, 231, -1, -1, 5),
2711 ui_button_info("MSG_06", 376, 258, -1, -1, 6),
2712 ui_button_info("MSG_07", 376, 291, -1, -1, 7),
2713 ui_button_info("MSG_08", 376, 320, -1, -1, 8),
2714 ui_button_info("MSG_09", 469, 427, -1, -1, 9),
2715 ui_button_info("MSG_10", 447, 452, -1, -1, 10),
2716 ui_button_info("MSG_11", 561, 411, -1, -1, 11),
2718 ui_button_info("MSG_00", 1, 184, 34, 191, 2), // open
2719 // ui_button_info("MSG_01", 1, 159, 34, 166, 1), // closed
2720 // ui_button_info("MSG_02", 1, 184, 34, 191, 2), // restricted
2721 ui_button_info("MSG_03", 1, 209, 34, 218, 3), // password
2722 ui_button_info("MSG_04", 1, 257, 34, 266, 4), // rank set
2723 ui_button_info("MSG_05", 1, 282, -1, -1, 5), // rank scroll up
2724 ui_button_info("MSG_06", 1, 307, -1, -1, 6), // rank scroll down
2725 ui_button_info("MSG_07", 177, 282, 210, 290, 7), // rank above
2726 ui_button_info("MSG_08", 177, 307, 210, 315, 8), // rank below
2727 ui_button_info("MSG_09", 536, 429, 500, 440, 9), // help
2728 ui_button_info("MSG_10", 536, 454, 479, 464, 10), // options
2729 ui_button_info("MSG_11", 576, 432, 571, 415, 11), // accept
2733 ui_button_info("2_MSG_00", 2, 295, 51, 307, 2), // open
2734 // ui_button_info("2_MSG_01", 2, 254, 51, 267, 1), // closed
2735 // ui_button_info("2_MSG_02", 2, 295, 51, 307, 2), // restricted
2736 ui_button_info("2_MSG_03", 2, 335, 51, 350, 3), // password
2737 ui_button_info("2_MSG_04", 2, 412, 51, 426, 4), // rank set
2738 ui_button_info("2_MSG_05", 2, 452, -1, -1, 5), // rank scroll up
2739 ui_button_info("2_MSG_06", 2, 492, -1, -1, 6), // rank scroll down
2740 ui_button_info("2_MSG_07", 284, 452, 335, 465, 7), // rank above
2741 ui_button_info("2_MSG_08", 284, 492, 335, 505, 8), // rank below
2742 ui_button_info("2_MSG_09", 858, 687, 817, 706, 9), // help
2743 ui_button_info("2_MSG_10", 858, 728, 797, 743, 10), // options
2744 ui_button_info("2_MSG_11", 921, 692, 921, 664, 11), // accept
2745 #ifdef MAKE_FS1 // filler for extra FS1 buttons
2746 ui_button_info("none", -1, -1, -1, -1, -1),
2747 ui_button_info("none", -1, -1, -1, -1, -1),
2753 #define MULTI_SG_NUM_TEXT 11
2755 UI_XSTR Multi_sg_text[GR_NUM_RESOLUTIONS][MULTI_SG_NUM_TEXT] = {
2757 {"Open", 1322, 34, 191, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_OPEN_GAME].button},
2758 // {"Closed", 1323, 34, 166, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_CLOSED_GAME].button},
2759 // {"Restricted", 1324, 34, 191, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RESTRICTED_GAME].button},
2760 {"Password Protected", 1325, 34, 218, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_PASSWD_GAME].button},
2761 {"Allow Rank", 1326, 34, 266, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_SET_GAME].button},
2762 {"Above", 1327, 210, 290, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_ABOVE].button},
2763 {"Below", 1328, 210, 315, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_BELOW].button},
2764 {"Help", 928, 500, 440, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_HELP].button},
2765 {"Options", 1036, 479, 464, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_OPTIONS].button},
2766 {"Accept", 1035, 571, 415, UI_XSTR_COLOR_PINK, -1, &Multi_sg_buttons[0][MSG_ACCEPT].button},
2767 {"Start Game", 1329, 26, 10, UI_XSTR_COLOR_GREEN, -1, NULL},
2768 {"Title", 1330, 26, 31, UI_XSTR_COLOR_GREEN, -1, NULL},
2769 {"Game Type", 1331, 12, 165, UI_XSTR_COLOR_GREEN, -1, NULL},
2772 {"Open", 1322, 51, 307, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_OPEN_GAME].button},
2773 // {"Closed", 1323, 51, 267, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_CLOSED_GAME].button},
2774 // {"Restricted", 1324, 51, 307, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RESTRICTED_GAME].button},
2775 {"Password Protected", 1325, 51, 350, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_PASSWD_GAME].button},
2776 {"Allow Rank", 1326, 51, 426, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_SET_GAME].button},
2777 {"Above", 1327, 335, 465, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_ABOVE].button},
2778 {"Below", 1328, 335, 505, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_BELOW].button},
2779 {"Help", 928, 817, 706, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_HELP].button},
2780 {"Options", 1036, 797, 743, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_OPTIONS].button},
2781 {"Accept", 1035, 921, 664, UI_XSTR_COLOR_PINK, -1, &Multi_sg_buttons[1][MSG_ACCEPT].button},
2782 {"Start Game", 1329, 42, 22, UI_XSTR_COLOR_GREEN, -1, NULL},
2783 {"Title", 1330, 42, 50, UI_XSTR_COLOR_GREEN, -1, NULL},
2784 {"Game Type", 1331, 20, 264, UI_XSTR_COLOR_GREEN, -1, NULL},
2789 // starting index for displaying ranks
2790 int Multi_sg_rank_start;
2791 int Multi_sg_rank_select;
2793 // netgame pointer to indirect through
2794 netgame_info *Multi_sg_netgame;
2796 // hold temporary values in this structure when on a standalone server
2797 netgame_info Multi_sg_netgame_temp;
2799 // forward declarations
2800 void multi_sg_check_buttons();
2801 void multi_sg_button_pressed(int n);
2802 void multi_sg_init_gamenet();
2803 void multi_sg_draw_radio_buttons();
2804 void multi_sg_rank_scroll_up();
2805 void multi_sg_rank_scroll_down();
2806 void multi_sg_rank_display_stuff();
2807 void multi_sg_rank_process_select();
2808 void multi_sg_rank_build_name(char *in, char *out, const int max_outlen);
2809 void multi_sg_check_passwd();
2810 void multi_sg_check_name();
2811 void multi_sg_release_passwd();
2812 int multi_sg_rank_select_valid(int rank);
2813 void multi_sg_select_rank_default();
2815 // function which takes a rank name and returns the index. Useful for commandline options
2816 // for above and below rank. We return the index of the rank in the Ranks[] array. If
2817 // the rank isn't found, we return -1
2818 int multi_start_game_rank_from_name( char *rank ) {
2822 for ( i = 0; i <= MAX_FREESPACE1_RANK; i++ ) {
2824 for ( i = 0; i <= MAX_FREESPACE2_RANK; i++ ) {
2826 if ( !SDL_strcasecmp(Ranks[i].name, rank) ) {
2834 void multi_start_game_init()
2838 // initialize the gamenet
2839 multi_sg_init_gamenet();
2841 // create the interface window
2842 Multi_sg_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
2843 Multi_sg_window.set_mask_bmap(Multi_sg_bitmap_mask_fname[gr_screen.res]);
2845 // load the background bitmap
2846 Multi_sg_bitmap = bm_load(Multi_sg_bitmap_fname[gr_screen.res]);
2847 if(Multi_sg_bitmap < 0){
2848 // we failed to load the bitmap - this is very bad
2852 // initialize the common notification messaging
2853 multi_common_notify_init();
2855 // initialize the common text area
2856 multi_common_set_text("");
2858 // use the common interface palette
2859 multi_common_set_palette();
2861 // create the interface buttons
2862 for(idx=0; idx<MULTI_SG_NUM_BUTTONS; idx++){
2863 // create the object
2864 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);
2866 // set the sound to play when highlighted
2867 Multi_sg_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
2869 // set the ani for the button
2870 Multi_sg_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sg_buttons[gr_screen.res][idx].filename);
2873 Multi_sg_buttons[gr_screen.res][idx].button.link_hotspot(Multi_sg_buttons[gr_screen.res][idx].hotspot);
2878 for(idx=0; idx<MULTI_SG_NUM_TEXT; idx++){
2879 Multi_sg_window.add_XSTR(&Multi_sg_text[gr_screen.res][idx]);
2883 // load the help overlay
2884 help_overlay_load(MULTI_START_OVERLAY);
2885 help_overlay_set_state(MULTI_START_OVERLAY,0);
2887 // intiialize the rank selection items
2888 multi_sg_select_rank_default();
2889 Multi_sg_rank_start = Multi_sg_rank_select;
2891 // create the rank select button
2892 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);
2893 Multi_sg_rank_button.hide();
2895 // create the netgame name input box
2896 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);
2898 // create the netgame password input box, and disable it by default
2899 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);
2900 Multi_sg_game_passwd.hide();
2901 Multi_sg_game_passwd.disable();
2903 // set the netgame text to this gadget and make it have focus
2904 Multi_sg_game_name.set_text(Multi_sg_netgame->name);
2905 Multi_sg_game_name.set_focus();
2907 // if starting a netgame, set the name of the game and any other options that are appropriate
2908 if ( Cmdline_start_netgame ) {
2909 if ( Cmdline_game_name != NULL ) {
2910 SDL_strlcpy( Multi_sg_netgame->name, Cmdline_game_name, SDL_arraysize(Multi_sg_netgame->name) );
2911 Multi_sg_game_name.set_text(Multi_sg_netgame->name);
2914 // deal with the different game types -- only one should even be active, so we will just go down
2915 // the line. Last one wins.
2916 if ( Cmdline_closed_game ) {
2917 Multi_sg_netgame->mode = NG_MODE_CLOSED;
2918 } else if ( Cmdline_restricted_game ) {
2919 Multi_sg_netgame->mode = NG_MODE_RESTRICTED;
2920 } else if ( Cmdline_game_password != NULL ) {
2921 Multi_sg_netgame->mode = NG_MODE_PASSWORD;
2922 SDL_strlcpy(Multi_sg_netgame->passwd, Cmdline_game_password, SDL_arraysize(Multi_sg_netgame->passwd));
2923 Multi_sg_game_passwd.set_text(Multi_sg_netgame->passwd);
2926 // deal with rank above and rank below
2927 if ( (Cmdline_rank_above != NULL) || (Cmdline_rank_below != NULL) ) {
2931 if ( Cmdline_rank_above != NULL ) {
2932 rank_str = Cmdline_rank_above;
2934 rank_str = Cmdline_rank_below;
2937 // try and get the rank index from the name -- if found, then set the rank base
2938 // and the game type. apparently we only support either above or below, not both
2939 // together, so I make a random choice
2940 rank = multi_start_game_rank_from_name( rank_str );
2942 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
2944 // now an arbitrary decision
2945 if ( Cmdline_rank_above != NULL ) {
2946 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
2948 Multi_sg_netgame->mode = NG_MODE_RANK_BELOW;
2953 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
2956 if ( multi_fs_tracker_inited() ) {
2957 multi_fs_tracker_login_freespace();
2961 void multi_start_game_do()
2963 // return here since we will be moving to the next stage anyway -- I don't want to see the backgrounds of
2964 // all the screens for < 1 second for every screen we automatically move to.
2965 if ( Cmdline_start_netgame ) {
2969 int k = Multi_sg_window.process();
2971 // process any keypresses
2974 if(help_overlay_active(MULTI_START_OVERLAY)){
2975 help_overlay_set_state(MULTI_START_OVERLAY,0);
2977 gamesnd_play_iface(SND_USER_SELECT);
2978 multi_quit_game(PROMPT_NONE);
2983 case SDLK_LCTRL + SDLK_RETURN :
2984 case SDLK_RCTRL + SDLK_RETURN :
2985 gamesnd_play_iface(SND_COMMIT_PRESSED);
2986 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
2990 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
2991 help_overlay_set_state(MULTI_START_OVERLAY, 0);
2994 // check to see if the user has selected a different rank
2995 multi_sg_rank_process_select();
2997 // check any button presses
2998 multi_sg_check_buttons();
3000 // check to see if any of the input boxes have changed, and update the appropriate Netgame fields if necessary
3001 multi_sg_check_passwd();
3002 multi_sg_check_name();
3004 // draw the background, etc
3006 GR_MAYBE_CLEAR_RES(Multi_sg_bitmap);
3007 if(Multi_sg_bitmap != -1){
3008 gr_set_bitmap(Multi_sg_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
3011 Multi_sg_window.draw();
3013 // display rank stuff
3014 multi_sg_rank_display_stuff();
3016 // display any pending notification messages
3017 multi_common_notify_do();
3019 // draw all radio button
3020 multi_sg_draw_radio_buttons();
3022 // draw the help overlay
3023 help_overlay_maybe_blit(MULTI_START_OVERLAY);
3029 void multi_start_game_close()
3031 // if i'm the host on a standalone server, send him my start game options (passwd, mode, etc)
3032 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
3033 multi_options_update_start_game(Multi_sg_netgame);
3036 // unload any bitmaps
3037 if(!bm_unload(Multi_sg_bitmap)){
3038 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_sg_bitmap_fname[gr_screen.res]));
3041 // unload the help overlay
3042 help_overlay_unload(MULTI_START_OVERLAY);
3044 // destroy the UI_WINDOW
3045 Multi_sg_window.destroy();
3048 void multi_sg_check_buttons()
3051 for(idx=0;idx<MULTI_SG_NUM_BUTTONS;idx++){
3052 // we only really need to check for one button pressed at a time, so we can break after
3054 if(Multi_sg_buttons[gr_screen.res][idx].button.pressed()){
3055 multi_sg_button_pressed(idx);
3061 void multi_sg_button_pressed(int n)
3064 // go to the options screen
3066 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
3071 if(!help_overlay_active(MULTI_START_OVERLAY)){
3072 help_overlay_set_state(MULTI_START_OVERLAY,1);
3074 help_overlay_set_state(MULTI_START_OVERLAY,0);
3078 // the open button was pressed
3080 // if the closed option is selected
3081 if(Multi_sg_netgame->mode != NG_MODE_OPEN){
3082 Multi_sg_netgame->mode = NG_MODE_OPEN;
3084 gamesnd_play_iface(SND_USER_SELECT);
3086 // release the password control if necessary
3087 multi_sg_release_passwd();
3089 // if its already selected
3091 gamesnd_play_iface(SND_GENERAL_FAIL);
3096 // the open button was pressed
3097 case MSG_CLOSED_GAME:
3098 // if the closed option is selected
3099 if(Multi_sg_netgame->mode != NG_MODE_CLOSED){
3100 Multi_sg_netgame->mode = NG_MODE_CLOSED;
3102 gamesnd_play_iface(SND_USER_SELECT);
3104 // release the password control if necessary
3105 multi_sg_release_passwd();
3107 // if its already selected
3109 gamesnd_play_iface(SND_GENERAL_FAIL);
3114 // toggle password protection
3115 case MSG_PASSWD_GAME:
3116 // if we selected it
3117 if(Multi_sg_netgame->mode != NG_MODE_PASSWORD){
3118 gamesnd_play_iface(SND_USER_SELECT);
3120 Multi_sg_game_passwd.enable();
3121 Multi_sg_game_passwd.unhide();
3122 Multi_sg_game_passwd.set_focus();
3124 Multi_sg_netgame->mode = NG_MODE_PASSWORD;
3126 // copy in the current network password
3127 Multi_sg_game_passwd.set_text(Multi_sg_netgame->passwd);
3129 gamesnd_play_iface(SND_GENERAL_FAIL);
3134 // toggle "restricted" on or off
3135 case MSG_RESTRICTED_GAME:
3136 if(Multi_sg_netgame->mode != NG_MODE_RESTRICTED){
3137 gamesnd_play_iface(SND_USER_SELECT);
3138 Multi_sg_netgame->mode = NG_MODE_RESTRICTED;
3140 // release the password control if necessary
3141 multi_sg_release_passwd();
3143 gamesnd_play_iface(SND_GENERAL_FAIL);
3148 // turn off all rank requirements
3149 case MSG_RANK_SET_GAME:
3150 // if either is set, then turn then both off
3151 if((Multi_sg_netgame->mode != NG_MODE_RANK_BELOW) && (Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE)){
3152 gamesnd_play_iface(SND_USER_SELECT);
3154 // set it to the default case if we're turning it off
3155 multi_sg_select_rank_default();
3156 Multi_sg_rank_start = Multi_sg_rank_select;
3158 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
3160 // release the password control if necessary
3161 multi_sg_release_passwd();
3163 gamesnd_play_iface(SND_GENERAL_FAIL);
3167 // rank above was pressed
3168 case MSG_RANK_ABOVE :
3169 if((Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE) || (Multi_sg_netgame->mode == NG_MODE_RANK_BELOW)){
3170 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
3172 // select the first item
3173 multi_sg_select_rank_default();
3174 Multi_sg_rank_start = Multi_sg_rank_select;
3177 gamesnd_play_iface(SND_USER_SELECT);
3179 gamesnd_play_iface(SND_GENERAL_FAIL);
3183 // rank below was pressed
3184 case MSG_RANK_BELOW :
3185 if((Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE) || (Multi_sg_netgame->mode == NG_MODE_RANK_BELOW)){
3186 Multi_sg_netgame->mode = NG_MODE_RANK_BELOW;
3188 // select the first item
3189 multi_sg_select_rank_default();
3190 Multi_sg_rank_start = Multi_sg_rank_select;
3193 gamesnd_play_iface(SND_USER_SELECT);
3195 gamesnd_play_iface(SND_GENERAL_FAIL);
3199 // scroll the rank list up
3200 case MSG_RANK_SCROLL_UP:
3201 multi_sg_rank_scroll_up();
3204 // scroll the rank list down
3205 case MSG_RANK_SCROLL_DOWN:
3206 multi_sg_rank_scroll_down();
3209 // move to the create game screen
3211 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
3212 gamesnd_play_iface(SND_COMMIT_PRESSED);
3216 gamesnd_play_iface(SND_GENERAL_FAIL);
3217 multi_common_add_notify(XSTR("Not implemented yet!",760));
3222 // NOTE : this is where all Netgame initialization should take place on the host
3223 void multi_sg_init_gamenet()
3225 char buf[128],out_name[128];
3227 net_player *server_save;
3229 // back this data up in case we are already connected to a standalone
3230 memcpy(&save,&Netgame.server_addr,sizeof(net_addr));
3231 server_save = Netgame.server;
3233 // remove campaign flags
3234 Game_mode &= ~(GM_CAMPAIGN_MODE);
3236 // clear out the Netgame structure and start filling in the values
3237 memset( &Netgame, 0, sizeof(Netgame) );
3238 memset( &Multi_sg_netgame_temp, 0, sizeof(netgame_info) );
3240 // if we're on the standalone, we're not the server, so we don't care about setting the netgame state
3241 if(Net_player->state != NETPLAYER_STATE_STD_HOST_SETUP){
3242 Netgame.game_state = NETGAME_STATE_HOST_SETUP;
3243 Multi_sg_netgame = &Netgame;
3246 ml_string(NOX("Starting netgame as Host/Server"));
3248 Multi_sg_netgame = &Multi_sg_netgame_temp;
3252 memcpy(&temp_addr.s_addr, &Netgame.server_addr, 4);
3253 char *server_addr = inet_ntoa(temp_addr);
3254 ml_printf(NOX("Starting netgame as Host on Standalone server : %s"), (server_addr == NULL) ? NOX("Unknown") : server_addr);
3257 Net_player->tracker_player_id = Multi_tracker_id;
3259 Multi_sg_netgame->security = (rand() % 32766) + 1; // get some random security number
3260 Multi_sg_netgame->mode = NG_MODE_OPEN;
3261 Multi_sg_netgame->rank_base = RANK_ENSIGN;
3262 if(Multi_sg_netgame->security < 16){
3263 Multi_sg_netgame->security += 16;
3266 // set the version_info field
3267 Multi_sg_netgame->version_info = NG_VERSION_ID;
3269 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
3270 Netgame.host = Net_player;
3273 // set the default netgame flags
3274 Multi_sg_netgame->flags = 0;
3276 // intialize endgame stuff
3277 multi_endgame_init();
3279 // load in my netgame options
3280 multi_options_netgame_load(&Netgame.options);
3282 // load my local netplayer options
3283 multi_options_local_load(&Net_player->p_info.options, Net_player);
3285 // setup the default game name, taking care of string length and player callsigns
3286 memset(out_name,0,128);
3288 pilot_format_callsign_personal(Player->callsign, out_name, SDL_arraysize(out_name));
3289 SDL_snprintf(buf, SDL_arraysize(buf), XSTR("%s game",782), out_name); // [[ %s will be a pilot's name ]]
3290 if ( strlen(buf) > MAX_GAMENAME_LEN ){
3291 SDL_strlcpy(buf, XSTR("Temporary name",783), SDL_arraysize(buf));
3293 SDL_strlcpy(Multi_sg_netgame->name, buf, SDL_arraysize(Multi_sg_netgame->name));
3295 // set the default qos and duration
3296 multi_voice_maybe_update_vars(Netgame.options.voice_qos,Netgame.options.voice_record_time);
3298 // make sure to set the server correctly (me or the standalone)
3299 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3300 memcpy(&Netgame.server_addr, &Psnet_my_addr,sizeof(net_addr));
3301 Netgame.server = Net_player;
3302 Net_player->player_id = multi_get_new_id();
3304 // setup debug flags
3305 Netgame.debug_flags = 0;
3307 if(!Cmdline_server_firing){
3308 Netgame.debug_flags |= NETD_FLAG_CLIENT_FIRING;
3310 if(!Cmdline_client_dodamage){
3311 Netgame.debug_flags |= NETD_FLAG_CLIENT_NODAMAGE;
3315 memcpy(&Netgame.server_addr,&save,sizeof(net_addr));
3316 Netgame.server = server_save;
3319 // if I have a cd or not
3321 Net_player->flags |= NETINFO_FLAG_HAS_CD;
3324 // if I have hacked data
3325 if(game_hacked_data()){
3326 Net_player->flags |= NETINFO_FLAG_HAXOR;
3329 // assign my player struct and other data
3330 Net_player->flags |= (NETINFO_FLAG_CONNECTED | NETINFO_FLAG_DO_NETWORKING);
3331 Net_player->s_info.voice_token_timestamp = -1;
3333 // if we're supposed to flush our cache directory, do so now
3334 if(Net_player->p_info.options.flags & MLO_FLAG_FLUSH_CACHE){
3335 multi_flush_multidata_cache();
3338 ml_string(NOX("Flushing multi-data cache"));
3344 void multi_sg_draw_radio_buttons()
3346 // draw the appropriate radio button
3347 switch(Multi_sg_netgame->mode){
3349 Multi_sg_buttons[gr_screen.res][MSG_OPEN_GAME].button.draw_forced(2);
3353 case NG_MODE_CLOSED:
3354 Multi_sg_buttons[gr_screen.res][MSG_CLOSED_GAME].button.draw_forced(2);
3358 case NG_MODE_PASSWORD:
3359 Multi_sg_buttons[gr_screen.res][MSG_PASSWD_GAME].button.draw_forced(2);
3363 case NG_MODE_RESTRICTED:
3364 Multi_sg_buttons[gr_screen.res][MSG_RESTRICTED_GAME].button.draw_forced(2);
3368 case NG_MODE_RANK_ABOVE:
3369 Multi_sg_buttons[gr_screen.res][MSG_RANK_SET_GAME].button.draw_forced(2);
3370 Multi_sg_buttons[gr_screen.res][MSG_RANK_ABOVE].button.draw_forced(2);
3372 case NG_MODE_RANK_BELOW:
3373 Multi_sg_buttons[gr_screen.res][MSG_RANK_SET_GAME].button.draw_forced(2);
3374 Multi_sg_buttons[gr_screen.res][MSG_RANK_BELOW].button.draw_forced(2);
3379 void multi_sg_rank_scroll_up()
3381 // if he doesn't have either of the rank flags set, then ignore this
3382 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3386 if(Multi_sg_rank_start > 0){
3387 Multi_sg_rank_start--;
3388 gamesnd_play_iface(SND_SCROLL);
3390 gamesnd_play_iface(SND_GENERAL_FAIL);
3394 void multi_sg_rank_scroll_down()
3396 // if he doesn't have either of the rank flags set, then ignore this
3397 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3401 if((NUM_RANKS - Multi_sg_rank_start) > Multi_sg_rank_max_display[gr_screen.res]){
3402 Multi_sg_rank_start++;
3403 gamesnd_play_iface(SND_SCROLL);
3405 gamesnd_play_iface(SND_GENERAL_FAIL);
3409 void multi_sg_rank_display_stuff()
3414 // if he doesn't have either of the rank flags set, then ignore this
3415 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3419 // display the list of ranks
3420 y = Msg_rank_list_coords[gr_screen.res][MSG_Y_COORD];
3421 idx = Multi_sg_rank_start;
3423 while((count < NUM_RANKS) && (count < Multi_sg_rank_max_display[gr_screen.res]) && (idx < NUM_RANKS)){
3424 // if its the selected item, then color it differently
3425 if(idx == Multi_sg_rank_select){
3426 gr_set_color_fast(&Color_text_selected);
3428 gr_set_color_fast(&Color_text_normal);
3432 multi_sg_rank_build_name(Ranks[idx].name, rank_name, sizeof(rank_name));
3433 gr_string(Msg_rank_list_coords[gr_screen.res][MSG_X_COORD],y,rank_name);
3441 // display the selected rank
3443 gr_set_color_fast(&Color_bright);
3444 multi_sg_rank_build_name(Ranks[Multi_sg_netgame->rank_base].name, rank_name, SDL_arraysize(rank_name));
3445 gr_string(Msg_rank_sel_coords[gr_screen.res][MSG_X_COORD],Msg_rank_sel_coords[gr_screen.res][MSG_Y_COORD],rank_name);
3449 void multi_sg_rank_process_select()
3453 // if he doesn't have either of the rank flags set, then ignore this
3454 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3458 // see if he's clicked on an item on the rank list
3459 if(Multi_sg_rank_button.pressed()){
3461 Multi_sg_rank_button.get_mouse_pos(NULL,&y);
3464 if(item + Multi_sg_rank_start < NUM_RANKS){
3465 // evaluate whether this rank is valid for the guy to pick
3466 if(multi_sg_rank_select_valid(item + Multi_sg_rank_start)){
3467 gamesnd_play_iface(SND_USER_SELECT);
3469 Multi_sg_rank_select = item + Multi_sg_rank_start;
3471 // set the Netgame rank
3472 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
3474 gamesnd_play_iface(SND_GENERAL_FAIL);
3476 memset(string,0,255);
3477 SDL_snprintf(string,SDL_arraysize(string),XSTR("Illegal value for a host of your rank (%s)\n",784),Ranks[Net_player->player->stats.rank].name);
3478 multi_common_add_notify(string);
3484 void multi_sg_rank_build_name(char *in, char *out, const int max_outlen)
3489 SDL_strlcpy(use, in, SDL_arraysize(use));
3490 first = strtok(use," ");
3492 // just copy the string
3494 SDL_strlcpy(out, in, max_outlen);
3498 // if the first part of the string is lieutenant, then abbreivate it and tack on the rest of the string
3499 if (SDL_strcasecmp(first,XSTR("lieutenant",785)) == 0) {
3500 first = strtok(NULL, NOX("\n"));
3502 // if he's not just a plain lieutenant
3504 SDL_snprintf(out, max_outlen, "%s%s", XSTR("Lt. ",786), first); // [[ lieutenant ]]
3506 // if he _is_ just a plain lieutenant
3508 SDL_strlcpy(out, in, max_outlen);
3511 SDL_strlcpy(out, in, max_outlen);
3515 void multi_sg_check_passwd()
3517 // check to see if the password input box has been pressed
3518 if(Multi_sg_game_passwd.changed()){
3519 Multi_sg_game_passwd.get_text(Multi_sg_netgame->passwd);
3523 void multi_sg_check_name()
3525 // check to see if the game name input box has been pressed
3526 if(Multi_sg_game_name.changed()){
3527 Multi_sg_game_name.get_text(Multi_sg_netgame->name);
3531 void multi_sg_release_passwd()
3533 // hide and disable the password input box
3534 Multi_sg_game_passwd.hide();
3535 Multi_sg_game_passwd.disable();
3537 // set the focus back to the name input box
3538 Multi_sg_game_name.set_focus();
3541 int multi_sg_rank_select_valid(int rank)
3544 if(Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE){
3545 if(Net_player->player->stats.rank >= rank){
3551 if(Net_player->player->stats.rank <= rank){
3559 void multi_sg_select_rank_default()
3561 // pick our rank for now
3562 Multi_sg_rank_select = Net_player->player->stats.rank;
3564 // set the Netgame rank
3565 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
3568 // -------------------------------------------------------------------------------------------------
3570 // MULTIPLAYER CREATE GAME screen
3575 const char *Multi_create_bitmap_fname[GR_NUM_RESOLUTIONS] = {
3576 "MultiCreate", // GR_640
3577 "2_MultiCreate" // GR_1024
3580 const char *Multi_create_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
3581 "MultiCreate-M", // GR_640
3582 "2_MultiCreate-M" // GR_1024
3585 const char *Multi_create_loading_fname[GR_NUM_RESOLUTIONS] = {
3586 "PleaseWait", // GR_640
3587 "2_PleaseWait" // GR_1024
3591 #define MULTI_CREATE_NUM_BUTTONS 23
3594 #define MC_SHOW_ALL 0
3595 #define MC_SHOW_COOP 1
3596 #define MC_SHOW_TEAM 2
3597 #define MC_SHOW_DOGFIGHT 3
3598 #define MC_PXO_REFRESH 4
3599 #define MC_PILOT_INFO 5
3600 #define MC_SCROLL_LIST_UP 6
3601 #define MC_SCROLL_LIST_DOWN 7
3602 #define MC_SCROLL_PLAYERS_UP 8
3603 #define MC_SCROLL_PLAYERS_DOWN 9
3604 #define MC_MISSION_FILTER 10
3605 #define MC_CAMPAIGN_FILTER 11
3606 #define MC_CANCEL 12
3611 #define MC_SCROLL_INFO_UP 17
3612 #define MC_SCROLL_INFO_DOWN 18
3613 #define MC_HOST_OPTIONS 19
3615 #define MC_OPTIONS 21
3616 #define MC_ACCEPT 22
3619 UI_WINDOW Multi_create_window; // the window object for the create screen
3620 UI_BUTTON Multi_create_player_select_button; // for selecting players
3621 UI_BUTTON Multi_create_list_select_button; // for selecting missions/campaigns
3622 int Multi_create_bitmap; // the background bitmap
3623 UI_SLIDER2 Multi_create_slider; // for create list
3625 // constants for coordinate look ups
3626 #define MC_X_COORD 0
3627 #define MC_Y_COORD 1
3628 #define MC_W_COORD 2
3629 #define MC_H_COORD 3
3631 ui_button_info Multi_create_buttons[GR_NUM_RESOLUTIONS][MULTI_CREATE_NUM_BUTTONS] = {
3634 ui_button_info("MC_18", 34, 131, -1, -1, 18), // all
3635 ui_button_info("MC_19", 72, 131, -1, -1, 19), // coop
3636 ui_button_info("MC_20", 120, 131, -1, -1, 20), // team
3637 // ui_button_info("MC_21", 166, 131, -1, -1, 21), // dogfight
3638 ui_button_info("none", -1, -1, -1, -1, -1), // dogfight (not used)
3639 ui_button_info("none", -1, -1, -1, -1, -1), // pxo?
3640 ui_button_info("MC_26", 540, 114, -1, -1, 26), // pilot info
3641 ui_button_info("MC_03", 0, 187, -1, -1, 2), // scroll list up
3642 ui_button_info("MC_02", 0, 227, -1, -1, 3), // scroll list down
3643 ui_button_info("MC_04", 611, 182, -1, -1, 4), // scroll players up
3644 ui_button_info("MC_05", 611, 221, -1, -1, 5), // scroll players down
3645 ui_button_info("MC_06", 18, 322, -1, -1, 6), // mission filter
3646 ui_button_info("MC_07", 18, 344, -1, -1, 7), // campaign filter
3647 ui_button_info("MC_10", 317, 339, -1, -1, 10), // cancel
3648 ui_button_info("MC_14", 464, 350, -1, -1, 14), // team 1
3649 ui_button_info("MC_15", 498, 350, -1, -1, 15), // team 2
3650 ui_button_info("MC_16", 527, 346, -1, -1, 16), // kick
3651 ui_button_info("MC_17", 572, 346, -1, -1, 17), // close
3652 ui_button_info("MC_08", 0, 398, -1, -1, 8), // scroll mission info up
3653 ui_button_info("MC_09", 0, 435, -1, -1, 9), // scroll mission info down
3654 ui_button_info("MC_27", 447, 402, -1, -1, 27), // host options
3655 ui_button_info("MC_11", 510, 428, -1, -1, 11), // help
3656 ui_button_info("MC_12", 510, 453, -1, -1, 12), // options
3657 ui_button_info("Mc_13", 562, 412, -1, -1, 13), // commit
3659 ui_button_info("MC_00", 32, 129, 36, 158, 0), // show all missions
3660 ui_button_info("MC_01", 76, 129, 71, 158, 1), // show coop missions
3661 ui_button_info("MC_02", 121, 129, 119, 158, 2), // show team missions
3662 ui_button_info("MC_03", 164, 129, 166, 158, 3), // show dogfight missions
3663 ui_button_info("MC_04", 399, 129, 229, 130, 4), // pxo mission refresh
3664 ui_button_info("MC_05", 567, 123, 467, 132, 5), // pilot info
3665 ui_button_info("MC_06", 1, 161, -1, -1, 6), // scroll mission info up
3666 ui_button_info("MC_08", 1, 304, -1, -1, 8), // scroll mission info down
3667 ui_button_info("MC_09", 613, 160, -1, -1, 9), // scroll players up
3668 ui_button_info("MC_10", 613, 202, -1, -1, 10), // scroll players down
3669 ui_button_info("MC_11", 22, 346, 27, 376, 11), // mission filter
3670 ui_button_info("MC_12", 104, 346, 110, 376, 12), // campaign filter
3671 ui_button_info("MC_13", 392, 341, 328, 364, 13), // cancel
3672 ui_button_info("MC_14", 472, 352, 482, 381, 14), // team 0
3673 ui_button_info("MC_15", 506, 352, 514, 381, 15), // team 1
3674 ui_button_info("MC_16", 539, 346, 539, 381, 16), // kick
3675 ui_button_info("MC_17", 589, 346, 582, 381, 17), // close
3676 ui_button_info("MC_18", 1, 406, -1, -1, 18), // scroll list up
3677 ui_button_info("MC_19", 1, 447, -1, -1, 19), // scroll list down
3678 ui_button_info("MC_20", 499, 434, 436, 423, 20), // host options
3679 ui_button_info("MC_21", 534, 426, -1, -1, 21), // help
3680 ui_button_info("MC_22", 534, 452, -1, -1, 22), // options
3681 ui_button_info("MC_23", 571, 426, 572, 413, 23), // commit
3685 ui_button_info("2_MC_00", 51, 207, 61, 253, 0), // show all missions
3686 ui_button_info("2_MC_01", 122, 207, 124, 253, 1), // show coop missions
3687 ui_button_info("2_MC_02", 193, 207, 194, 253, 2), // show team missions
3688 ui_button_info("2_MC_03", 263, 207, 261, 253, 3), // show dogfight missions
3689 ui_button_info("2_MC_04", 639, 207, 479, 218, 4), // pxo mission refresh
3690 ui_button_info("2_MC_05", 907, 197, 748, 216, 5), // pilot info
3691 ui_button_info("2_MC_06", 1, 258, -1, -1, 6), // scroll mission info up
3692 ui_button_info("2_MC_08", 1, 487, -1, -1, 8), // scroll mission info down
3693 ui_button_info("2_MC_09", 981, 256, -1, -1, 9), // scroll players up
3694 ui_button_info("2_MC_10", 981, 323, -1, -1, 10), // scroll players down
3695 ui_button_info("2_MC_11", 35, 554, 46, 601, 11), // mission filter
3696 ui_button_info("2_MC_12", 166, 554, 174, 601, 12), // campaign filter
3697 ui_button_info("2_MC_13", 628, 545, 559, 582, 13), // cancel
3698 ui_button_info("2_MC_14", 756, 564, 772, 610, 14), // team 0
3699 ui_button_info("2_MC_15", 810, 564, 826, 610, 15), // team 1
3700 ui_button_info("2_MC_16", 862, 554, 872, 610, 16), // kick
3701 ui_button_info("2_MC_17", 943, 554, 949, 610, 17), // close
3702 ui_button_info("2_MC_18", 1, 649, -1, -1, 18), // scroll list up
3703 ui_button_info("2_MC_19", 1, 716, -1, -1, 19), // scroll list down
3704 ui_button_info("2_MC_20", 798, 695, 726, 667, 20), // host options
3705 ui_button_info("2_MC_21", 854, 681, -1, -1, 21), // help
3706 ui_button_info("2_MC_22", 854, 724, -1, -1, 22), // options
3707 ui_button_info("2_MC_23", 914, 681, 932, 667, 23), // commit
3712 #define MULTI_CREATE_NUM_TEXT 0
3714 #define MULTI_CREATE_NUM_TEXT 15
3716 UI_XSTR Multi_create_text[GR_NUM_RESOLUTIONS][MULTI_CREATE_NUM_BUTTONS] = {
3718 // not needed for FS1
3720 {"All", 1256, 36, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_ALL].button},
3721 {"Coop", 1257, 71, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_COOP].button},
3722 {"Team", 1258, 119, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_TEAM].button},
3723 {"Dogfight", 1259, 166, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_DOGFIGHT].button},
3724 {"Refresh Missions", 1260, 229, 130, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_PXO_REFRESH].button},
3725 {"Pilot Info", 1261, 467, 132, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_PILOT_INFO].button},
3726 {"Missions", 1262, 27, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_MISSION_FILTER].button},
3727 {"Campaigns", 1263, 110, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_CAMPAIGN_FILTER].button},
3728 {"Cancel", 387, 328, 364, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_CANCEL].button},
3729 {"1", 1264, 482, 381, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_TEAM0].button},
3730 {"2", 1265, 514, 381, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_TEAM1].button},
3731 {"Kick", 1266, 539, 381, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_KICK].button},
3732 {"Close", 1508, 582, 381, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_CLOSE].button},
3733 {"Host Options", 1267, 436, 423, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_HOST_OPTIONS].button},
3734 {"Commit", 1062, 572, 413, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_ACCEPT].button}
3738 // not needed for FS1
3740 {"All", 1256, 61, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_ALL].button},
3741 {"Coop", 1257, 124, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_COOP].button},
3742 {"Team", 1258, 194, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_TEAM].button},
3743 {"Dogfight", 1259, 261, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_DOGFIGHT].button},
3744 {"Refresh Missions", 1260, 501, 218, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_PXO_REFRESH].button},
3745 {"Pilot Info", 1261, 814, 216, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_PILOT_INFO].button},
3746 {"Missions", 1262, 46, 601, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_MISSION_FILTER].button},
3747 {"Campaigns", 1263, 174, 601, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_CAMPAIGN_FILTER].button},
3748 {"Cancel", 387, 559, 582, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_CANCEL].button},
3749 {"1", 1264, 772, 610, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_TEAM0].button},
3750 {"2", 1265, 826, 610, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_TEAM1].button},
3751 {"Kick", 1266, 872, 610, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_KICK].button},
3752 {"Close", 1508, 949, 610, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_CLOSE].button},
3753 {"Host Options", 1267, 755, 683, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_HOST_OPTIONS].button},
3754 {"Commit", 1062, 932, 667, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_ACCEPT].button}
3760 // squad war checkbox
3761 UI_CHECKBOX Multi_create_sw_checkbox;
3762 const char *Multi_create_sw_checkbox_fname[GR_NUM_RESOLUTIONS] = {
3766 int Multi_create_sw_checkbox_coords[GR_NUM_RESOLUTIONS][2] = {
3774 int Multi_create_sw_checkbox_text[GR_NUM_RESOLUTIONS][2] = {
3784 // game information text areas
3785 int Mc_list_coords[GR_NUM_RESOLUTIONS][4] = {
3798 int Mc_players_coords[GR_NUM_RESOLUTIONS][4] = {
3811 int Mc_info_coords[GR_NUM_RESOLUTIONS][4] = {
3824 // mission icon stuff
3825 int Mc_icon_type_coords[GR_NUM_RESOLUTIONS][2] = {
3827 38, -2 // y is an offset
3830 61, -2 // y is an offset
3834 int Mc_icon_volition_coords[GR_NUM_RESOLUTIONS][2] = {
3836 61, -1 // y is an offset
3839 98, 1 // y is an offset
3843 int Mc_icon_silent_coords[GR_NUM_RESOLUTIONS][2] = {
3845 72, 0 // y is an offset
3848 115, 0 // y is an offset
3852 int Mc_icon_valid_coords[GR_NUM_RESOLUTIONS][2] = {
3854 91, 0 // y is an offset
3857 146, 0 // y is an offset
3861 // mission/campaign list column areas
3862 int Mc_column1_w[GR_NUM_RESOLUTIONS] = {
3867 int Mc_column2_w[GR_NUM_RESOLUTIONS] = {
3872 int Mc_column3_w[GR_NUM_RESOLUTIONS] = {
3877 int Mc_mission_name_x[GR_NUM_RESOLUTIONS] = {
3882 int Mc_mission_count_x[GR_NUM_RESOLUTIONS] = {
3887 int Mc_mission_fname_x[GR_NUM_RESOLUTIONS] = {
3892 int Mc_create_game_text[GR_NUM_RESOLUTIONS][2] = {
3893 {13, 116}, // GR_640
3894 {21, 186} // GR_1024
3897 int Mc_players_text[GR_NUM_RESOLUTIONS][2] = {
3898 {467, 150}, // GR_640
3899 {747, 240} // GR_1024
3902 int Mc_team_text[GR_NUM_RESOLUTIONS][2] = {
3903 {484, 342}, // GR_640
3904 {774, 547} // GR_1024
3907 int Mc_slider_coords[GR_NUM_RESOLUTIONS][4] = {
3909 3, 197, 13, 105 // GR_640
3912 5, 316, 20, 168 // GR_1024
3916 const char *Mc_slider_bitmap[GR_NUM_RESOLUTIONS] = {
3921 // player list control thingie defs
3922 #define MULTI_CREATE_PLIST_MAX_DISPLAY 20
3923 int Multi_create_plist_select_flag; // flag indicating if we have a play selected
3924 short Multi_create_plist_select_id; // the net address of the currently selected player (for lookup)
3926 // master tracker details
3927 int Multi_create_frame_count; // framecount
3928 int Multi_create_mt_tried_login; // attempted to login this server on the MT
3930 // mission filter settings
3931 int Multi_create_filter; // what mode we're in
3933 // game/campaign list control defs
3934 int Multi_create_list_max_display[GR_NUM_RESOLUTIONS] = {
3940 int Multi_create_list_count; // number of items in listbox
3941 int Multi_create_list_mode; // 0 == mission mode, 1 == campaign mode
3942 int Multi_create_list_start; // where to start displaying from
3943 int Multi_create_list_select; // which item is currently highlighted
3944 int Multi_create_files_loaded;
3946 char Multi_create_files_array[MULTI_CREATE_MAX_LIST_ITEMS][MAX_FILENAME_LEN];
3948 int Multi_create_mission_count; // how many we have
3949 int Multi_create_campaign_count;
3950 multi_create_info Multi_create_mission_list[MULTI_CREATE_MAX_LIST_ITEMS];
3951 multi_create_info Multi_create_campaign_list[MULTI_CREATE_MAX_LIST_ITEMS];
3953 // use a pointer for the file list. Will point to either the missions or the campaigns
3954 multi_create_info *Multi_create_file_list;
3956 // LOCAL function definitions
3957 void multi_create_check_buttons();
3958 void multi_create_button_pressed(int n);
3959 void multi_create_init_as_server();
3960 void multi_create_init_as_client();
3961 void multi_create_do_netstuff();
3962 void multi_create_plist_scroll_up();
3963 void multi_create_plist_scroll_down();
3964 void multi_create_plist_process();
3965 void multi_create_plist_blit_normal();
3966 void multi_create_plist_blit_team();
3967 void multi_create_list_scroll_up();
3968 void multi_create_list_scroll_down();
3969 void multi_create_list_do();
3970 void multi_create_list_select_item(int n);
3971 void multi_create_list_blit_icons(int list_index, int y_start);
3972 void multi_create_accept_hit();
3973 void multi_create_draw_filter_buttons();
3974 void multi_create_set_selected_team(int team);
3975 short multi_create_get_mouse_id();
3976 int multi_create_ok_to_commit();
3977 int multi_create_verify_cds();
3978 void multi_create_refresh_pxo();
3979 void multi_create_sw_clicked();
3981 // since we can selectively filter out mission/campaign types we always need to map a selected index (which is relative
3982 // to the displayed list), to an absolute index (which is relative to the total file list - some of which may filtered out)
3983 void multi_create_select_to_filename(int select_index, char *filename, const int max_filelen);
3984 int multi_create_select_to_index(int select_index);
3986 int Multi_create_should_show_popup = 0;
3989 // sorting function to sort mission lists.. Basic sorting on mission name
3990 int multi_create_sort_func(const void *a, const void *b)
3992 multi_create_info *m1, *m2;
3994 m1 = (multi_create_info *)a;
3995 m2 = (multi_create_info *)b;
3997 return ( strcmp(m1->name, m2->name) );
4000 void multi_create_setup_list_data(int mode)
4002 int idx,should_sort,switched_modes;
4004 // set the current mode
4007 if((Multi_create_list_mode != mode) && (mode != -1)){
4008 Multi_create_list_mode = mode;
4011 // set up the list pointers
4012 if ( Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ) {
4013 Multi_create_file_list = Multi_create_mission_list;
4014 } else if ( Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ) {
4015 Multi_create_file_list = Multi_create_campaign_list;
4021 // get the mission count based upon the filter selected
4022 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
4023 switch(Multi_create_filter){
4024 case MISSION_TYPE_MULTI:
4025 Multi_create_list_count = Multi_create_mission_count;
4028 Multi_create_list_count = 0;
4029 // find all missions which match
4030 for(idx=0;idx<Multi_create_mission_count;idx++){
4031 if(Multi_create_mission_list[idx].flags & Multi_create_filter){
4032 Multi_create_list_count++;
4036 // if we switched modes and we have more than 0 items, sort them
4037 if(switched_modes && (Multi_create_list_count > 0)){
4042 } else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
4043 switch(Multi_create_filter){
4044 case MISSION_TYPE_MULTI:
4045 Multi_create_list_count = Multi_create_campaign_count;
4048 Multi_create_list_count = 0;
4049 // find all missions which match
4050 for(idx=0;idx<Multi_create_campaign_count;idx++){
4051 if(Multi_create_campaign_list[idx].flags & Multi_create_filter){
4052 Multi_create_list_count++;
4056 // if we switched modes and we have more than 0 items, sort them
4057 if(switched_modes && (Multi_create_list_count > 0)){
4064 // reset the list start and selected indices
4065 Multi_create_list_start = 0;
4066 Multi_create_list_select = -1;
4067 multi_create_list_select_item(Multi_create_list_start);
4069 // sort the list of missions if necessary
4071 qsort(Multi_create_file_list, Multi_create_list_count, sizeof(multi_create_info), multi_create_sort_func);
4076 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);
4080 void multi_create_game_init()
4085 // now make sure to initialze various netgame stuff based upon whether we're on a standalone or not
4086 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4087 multi_create_init_as_server();
4089 multi_create_init_as_client();
4092 // initialize the player list data
4093 Multi_create_plist_select_flag = 0;
4094 Multi_create_plist_select_id = -1;
4096 // create the interface window
4097 Multi_create_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
4098 Multi_create_window.set_mask_bmap(Multi_create_bitmap_mask_fname[gr_screen.res]);
4100 // load the background bitmap
4101 Multi_create_bitmap = bm_load(Multi_create_bitmap_fname[gr_screen.res]);
4102 if(Multi_create_bitmap < 0){
4103 // we failed to load the bitmap - this is very bad
4107 // close any previous existing instances of the chatbox and create a new one
4111 // load the help overlay
4112 help_overlay_load(MULTI_CREATE_OVERLAY);
4113 help_overlay_set_state(MULTI_CREATE_OVERLAY, 0);
4115 // initialize the common notification messaging
4116 multi_common_notify_init();
4118 // use the common interface palette
4119 multi_common_set_palette();
4121 // create the interface buttons
4122 for(idx=0; idx<MULTI_CREATE_NUM_BUTTONS; idx++){
4123 b = &Multi_create_buttons[gr_screen.res][idx];
4125 // create the object
4126 b->button.create(&Multi_create_window, "", b->x, b->y, 1, 1, ((idx == MC_SCROLL_LIST_UP) || (idx == MC_SCROLL_LIST_DOWN)), 1);
4128 // set the sound to play when highlighted
4129 b->button.set_highlight_action(common_play_highlight_sound);
4131 // set the ani for the button
4132 b->button.set_bmaps(b->filename);
4135 b->button.link_hotspot(b->hotspot);
4137 // some special case stuff for the pxo refresh button
4138 if(idx == MC_PXO_REFRESH){
4139 // if not a PXO game, or if I'm not a server disable and hide the button
4140 if(!MULTI_IS_TRACKER_GAME || !MULTIPLAYER_MASTER){
4142 b->button.disable();
4148 for(idx=0; idx<MULTI_CREATE_NUM_TEXT; idx++){
4149 Multi_create_window.add_XSTR(&Multi_create_text[gr_screen.res][idx]);
4153 // if this is a PXO game, enable the squadwar checkbox
4154 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);
4155 Multi_create_sw_checkbox.set_bmaps(Multi_create_sw_checkbox_fname[gr_screen.res], 6, 0);
4156 if(!MULTI_IS_TRACKER_GAME){
4157 Multi_create_sw_checkbox.hide();
4158 Multi_create_sw_checkbox.disable();
4163 // disable squad war button in demo
4164 Multi_create_sw_checkbox.hide();
4165 Multi_create_sw_checkbox.disable();
4168 // initialize the mission type filtering mode
4169 Multi_create_filter = MISSION_TYPE_MULTI;
4171 // initialize the list mode, and load in a list
4172 memset(Multi_create_mission_list, 0, sizeof(multi_create_info) * MULTI_CREATE_MAX_LIST_ITEMS);
4173 memset(Multi_create_campaign_list, 0, sizeof(multi_create_info) * MULTI_CREATE_MAX_LIST_ITEMS);
4174 for(idx=0; idx<MULTI_CREATE_MAX_LIST_ITEMS; idx++){
4175 Multi_create_mission_list[idx].valid_status = MVALID_STATUS_UNKNOWN;
4176 Multi_create_campaign_list[idx].valid_status = MVALID_STATUS_UNKNOWN;
4178 Multi_create_list_mode = MULTI_CREATE_SHOW_MISSIONS;
4179 Multi_create_list_start = -1;
4180 Multi_create_list_select = -1;
4181 Multi_create_list_count = 0;
4184 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);
4187 // create the player list select button
4188 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);
4189 Multi_create_player_select_button.hide();
4191 // create the mission/campaign list select button
4192 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);
4193 Multi_create_list_select_button.hide();
4195 // set hotkeys for a couple of things.
4196 Multi_create_buttons[gr_screen.res][MC_ACCEPT].button.set_hotkey(KEY_CTRLED+SDLK_RETURN);
4198 // init some master tracker stuff
4199 Multi_create_frame_count = 0;
4200 Multi_create_mt_tried_login = 0;
4202 // remove campaign flags
4203 Game_mode &= ~(GM_CAMPAIGN_MODE);
4205 // send any pilots as appropriate
4206 multi_data_send_my_junk();
4207 Multi_create_file_list = Multi_create_mission_list;
4209 Multi_create_campaign_count = 0;
4210 Multi_create_mission_count = 0;
4211 Multi_create_files_loaded = 0;
4214 void multi_create_game_do()
4218 const char *loading_str = XSTR("Loading", 1336);
4222 // set this if we want to show the pilot info popup
4223 Multi_create_should_show_popup = 0;
4225 // first thing is to load the files
4226 if ( !Multi_create_files_loaded ) {
4227 // if I am a client, send a list request to the server for the missions
4228 if ( MULTIPLAYER_CLIENT ) {
4229 send_mission_list_request( MISSION_LIST_REQUEST );
4233 loading_bitmap = bm_load(Multi_create_loading_fname[gr_screen.res]);
4235 // draw the background, etc
4237 GR_MAYBE_CLEAR_RES(Multi_create_bitmap);
4238 if(Multi_create_bitmap != -1){
4239 gr_set_bitmap(Multi_create_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4243 if ( loading_bitmap > -1 ){
4244 gr_set_bitmap(loading_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4246 gr_bitmap( Please_wait_coords[gr_screen.res][MC_X_COORD], Please_wait_coords[gr_screen.res][MC_Y_COORD] );
4248 // draw "Loading" on it
4250 gr_set_color_fast(&Color_normal);
4252 gr_get_string_size(&str_w, &str_h, loading_str);
4253 gr_string((gr_screen.max_w - str_w) / 2, (gr_screen.max_h - str_h) / 2, loading_str);
4259 multi_create_list_load_missions();
4260 multi_create_list_load_campaigns();
4262 // if this is a tracker game, validate missions
4263 if(MULTI_IS_TRACKER_GAME){
4264 multi_update_valid_missions();
4267 // update the file list
4268 multi_create_setup_list_data(MULTI_CREATE_SHOW_MISSIONS);
4271 // don't bother setting netgame state if ont the server
4272 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4273 Netgame.game_state = NETGAME_STATE_FORMING;
4274 send_netgame_update_packet();
4277 // if we're on the standalone we have to tell him that we're now in the host setup screen
4278 Net_player->state = NETPLAYER_STATE_HOST_SETUP;
4279 send_netplayer_update_packet();
4281 Multi_create_files_loaded = 1;
4284 int k = chatbox_process();
4285 k = Multi_create_window.process(k,0);
4288 // same as the cancel button
4290 if(help_overlay_active(MULTI_CREATE_OVERLAY)){
4291 help_overlay_set_state(MULTI_CREATE_OVERLAY,0);
4293 gamesnd_play_iface(SND_USER_SELECT);
4294 multi_quit_game(PROMPT_HOST);
4299 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
4300 help_overlay_set_state(MULTI_CREATE_OVERLAY, 0);
4303 // process any button clicks
4304 multi_create_check_buttons();
4306 // do any network related stuff
4307 multi_create_do_netstuff();
4309 // draw the background, etc
4311 GR_MAYBE_CLEAR_RES(Multi_create_bitmap);
4312 if(Multi_create_bitmap != -1){
4313 gr_set_bitmap(Multi_create_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4317 // if we're not in team vs. team mode, don't draw the team buttons
4318 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
4319 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.hide();
4320 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.hide();
4321 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.disable();
4322 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.disable();
4324 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.enable();
4325 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.enable();
4326 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.unhide();
4327 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.unhide();
4330 // draw the window itself
4331 Multi_create_window.draw();
4333 gr_set_color_fast(&Color_normal);
4336 // draw Create Game text
4337 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));
4339 // draw players text
4340 gr_string(Mc_players_text[gr_screen.res][MC_X_COORD], Mc_players_text[gr_screen.res][MC_Y_COORD], XSTR("Players", 1269));
4342 // draw players text
4343 gr_string(Mc_team_text[gr_screen.res][MC_X_COORD], Mc_team_text[gr_screen.res][MC_Y_COORD], XSTR("Team", 1258));
4346 // process and display the player list
4347 // NOTE : this must be done before the buttons are checked to insure that a player hasn't dropped
4348 multi_create_plist_process();
4349 if(Netgame.type_flags & NG_TYPE_TEAM){
4350 multi_create_plist_blit_team();
4352 multi_create_plist_blit_normal();
4355 // process and display the game/campaign list
4356 multi_create_list_do();
4358 // draw the correct mission filter button
4359 multi_create_draw_filter_buttons();
4361 // display any text in the info area
4362 multi_common_render_text();
4364 // display any pending notification messages
4365 multi_common_notify_do();
4367 // force the correct mission/campaign button to light up
4368 if( Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ){
4369 Multi_create_buttons[gr_screen.res][MC_MISSION_FILTER].button.draw_forced(2);
4371 Multi_create_buttons[gr_screen.res][MC_CAMPAIGN_FILTER].button.draw_forced(2);
4374 // force draw the closed button if it is toggled on
4375 if(Netgame.flags & NG_FLAG_TEMP_CLOSED){
4376 Multi_create_buttons[gr_screen.res][MC_CLOSE].button.draw_forced(2);
4379 // process and show the chatbox thingie
4383 Multi_create_window.draw_tooltip();
4385 // display the voice status indicator
4386 multi_common_voice_display_status();
4388 // blit the help overlay if necessary
4389 help_overlay_maybe_blit(MULTI_CREATE_OVERLAY);
4392 if(MULTI_IS_TRACKER_GAME){
4393 if(Netgame.type_flags & NG_TYPE_SW){
4394 gr_set_color_fast(&Color_bright);
4396 gr_set_color_fast(&Color_normal);
4399 gr_string(Multi_create_sw_checkbox_text[gr_screen.res][0], Multi_create_sw_checkbox_text[gr_screen.res][1], "SquadWar");
4406 // if we're supposed to show the pilot info popup, do it now
4407 if(Multi_create_should_show_popup){
4408 // get the player index and address of the player item the mouse is currently over
4409 if(Multi_create_plist_select_flag){
4410 player_index = find_player_id(Multi_create_plist_select_id);
4411 if(player_index != -1){
4412 multi_pinfo_popup(&Net_players[player_index]);
4417 // increment the frame count
4418 Multi_create_frame_count++;
4421 void multi_create_game_close()
4423 // unload any bitmaps
4424 if(!bm_unload(Multi_create_bitmap)){
4425 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_create_bitmap_fname[gr_screen.res]));
4428 // unload the help overlay
4429 help_overlay_unload(MULTI_CREATE_OVERLAY);
4431 // destroy the chatbox
4434 // destroy the UI_WINDOW
4435 Multi_create_window.destroy();
4438 void multi_create_check_buttons()
4441 for(idx=0;idx<MULTI_CREATE_NUM_BUTTONS;idx++){
4442 // we only really need to check for one button pressed at a time, so we can break after
4444 if(Multi_create_buttons[gr_screen.res][idx].button.pressed()){
4445 multi_create_button_pressed(idx);
4451 // if the squad war checkbox was clicked
4452 if(Multi_create_sw_checkbox.changed()){
4453 multi_create_sw_clicked();
4458 void multi_create_button_pressed(int n)
4464 gamesnd_play_iface(SND_USER_SELECT);
4465 multi_quit_game(PROMPT_HOST);
4468 // if valid commit conditions have not been met
4469 if(!multi_create_ok_to_commit()){
4474 multi_create_accept_hit();
4479 if(!help_overlay_active(MULTI_CREATE_OVERLAY)){
4480 help_overlay_set_state(MULTI_CREATE_OVERLAY,1);
4482 help_overlay_set_state(MULTI_CREATE_OVERLAY,0);
4486 // scroll the info text box up
4487 case MC_SCROLL_INFO_UP:
4488 multi_common_scroll_text_up();
4491 // scroll the info text box down
4492 case MC_SCROLL_INFO_DOWN:
4493 multi_common_scroll_text_down();
4496 // scroll the player list up
4497 case MC_SCROLL_PLAYERS_UP:
4498 multi_create_plist_scroll_up();
4501 // scroll the player list down
4502 case MC_SCROLL_PLAYERS_DOWN:
4503 multi_create_plist_scroll_down();
4506 // scroll the game/campaign list up
4507 case MC_SCROLL_LIST_UP:
4508 multi_create_list_scroll_up();
4510 Multi_create_slider.forceUp(); // move slider up
4514 // scroll the game/campaign list down
4515 case MC_SCROLL_LIST_DOWN:
4516 multi_create_list_scroll_down();
4518 Multi_create_slider.forceDown(); // move slider down
4522 // go to the options screen
4524 gamesnd_play_iface(SND_USER_SELECT);
4525 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
4528 // show all missions
4530 if(Multi_create_filter != MISSION_TYPE_MULTI){
4531 gamesnd_play_iface(SND_USER_SELECT);
4532 Multi_create_filter = MISSION_TYPE_MULTI;
4533 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4535 gamesnd_play_iface(SND_GENERAL_FAIL);
4539 // show cooperative missions
4541 if(Multi_create_filter != MISSION_TYPE_MULTI_COOP){
4542 gamesnd_play_iface(SND_USER_SELECT);
4543 Multi_create_filter = MISSION_TYPE_MULTI_COOP;
4544 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4546 gamesnd_play_iface(SND_GENERAL_FAIL);
4550 // show team vs. team missions
4552 if(Multi_create_filter != MISSION_TYPE_MULTI_TEAMS){
4553 gamesnd_play_iface(SND_USER_SELECT);
4554 Multi_create_filter = MISSION_TYPE_MULTI_TEAMS;
4555 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4557 gamesnd_play_iface(SND_GENERAL_FAIL);
4561 // show dogfight missions
4563 case MC_SHOW_DOGFIGHT:
4564 if (Multi_create_filter != MISSION_TYPE_MULTI_DOGFIGHT){
4565 gamesnd_play_iface(SND_USER_SELECT);
4566 Multi_create_filter = MISSION_TYPE_MULTI_DOGFIGHT;
4567 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4569 gamesnd_play_iface(SND_GENERAL_FAIL);
4574 // toggle temporary netgame closed on/off
4576 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4577 Netgame.flags ^= NG_FLAG_TEMP_CLOSED;
4579 Netgame.options.flags |= MLO_FLAG_TEMP_CLOSED;
4580 multi_options_update_netgame();
4582 gamesnd_play_iface(SND_USER_SELECT);
4585 // kick the currently selected player (if possible)
4587 // lookup the player at the specified index
4588 if(Multi_create_plist_select_flag){
4589 idx = find_player_id(Multi_create_plist_select_id);
4590 // kick him - but don't ban him
4592 multi_kick_player(idx,0);
4597 // switch to individual mission mode and load in a list
4598 case MC_MISSION_FILTER:
4599 if(Multi_create_list_mode != MULTI_CREATE_SHOW_MISSIONS){
4600 Netgame.campaign_mode = MP_SINGLE;
4602 gamesnd_play_iface(SND_USER_SELECT);
4604 // update the file list
4605 multi_create_setup_list_data(MULTI_CREATE_SHOW_MISSIONS);
4607 gamesnd_play_iface(SND_GENERAL_FAIL);
4611 // switch to campaign mode and load in a list
4612 case MC_CAMPAIGN_FILTER:
4613 // switch off squad war
4615 Multi_create_sw_checkbox.set_state(0);
4617 Netgame.type_flags = NG_TYPE_COOP;
4619 if(Multi_create_list_mode != MULTI_CREATE_SHOW_CAMPAIGNS){
4620 Netgame.campaign_mode = MP_CAMPAIGN;
4622 gamesnd_play_iface(SND_USER_SELECT);
4624 // update the file list
4625 multi_create_setup_list_data(MULTI_CREATE_SHOW_CAMPAIGNS);
4627 gamesnd_play_iface(SND_GENERAL_FAIL);
4631 // attempt to set the selected player's team
4633 multi_create_set_selected_team(0);
4634 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4635 multi_team_send_update();
4639 // attempt to set the selected player's team
4641 multi_create_set_selected_team(1);
4642 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4643 multi_team_send_update();
4647 // popup the pilot info dialog for the currently selected pilot (will occur at the end of the frame)
4649 Multi_create_should_show_popup = 1;
4652 // go to the host options screen
4653 case MC_HOST_OPTIONS:
4654 gamesnd_play_iface(SND_USER_SELECT);
4655 gameseq_post_event(GS_EVENT_MULTI_HOST_OPTIONS);
4658 // refresh PXO file list
4659 case MC_PXO_REFRESH:
4660 if(!MULTI_IS_TRACKER_GAME){
4663 multi_create_refresh_pxo();
4667 gamesnd_play_iface(SND_GENERAL_FAIL);
4668 multi_common_add_notify(XSTR("Not implemented yet!",760));
4673 // do stuff like pinging servers, sending out requests, etc
4674 void multi_create_do_netstuff()
4678 // if not on a standalone
4679 void multi_create_init_as_server()
4681 // set me up as the host and master
4682 Net_player->flags |= (NETINFO_FLAG_AM_MASTER | NETINFO_FLAG_GAME_HOST);
4685 // if on a standalone
4686 void multi_create_init_as_client()
4688 Net_player->flags |= NETINFO_FLAG_GAME_HOST;
4691 // scroll up through the player list
4692 void multi_create_plist_scroll_up()
4694 gamesnd_play_iface(SND_GENERAL_FAIL);
4697 // scroll down through the player list
4698 void multi_create_plist_scroll_down()
4700 gamesnd_play_iface(SND_GENERAL_FAIL);
4703 void multi_create_plist_process()
4705 int test_count,idx,player_index;
4707 // first determine if there are 0 players in the game. This should never happen since the host is _always_ in the game
4709 for(idx=0;idx<MAX_PLAYERS;idx++){
4710 // count anyone except the standalone server (if applicable)
4711 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
4715 if(test_count <= 0){
4719 // if we had a selected item but that player has left, select myself instead
4720 if(Multi_create_plist_select_flag){
4721 player_index = find_player_id(Multi_create_plist_select_id);
4722 if(player_index == -1){
4723 Multi_create_plist_select_id = Net_player->player_id;
4726 Multi_create_plist_select_flag = 1;
4727 Multi_create_plist_select_id = Net_player->player_id;
4730 // if the player has clicked somewhere in the player list area
4731 if(Multi_create_player_select_button.pressed()){
4734 // get the player index and address of the player item the mouse is currently over
4735 player_id = multi_create_get_mouse_id();
4736 player_index = find_player_id(player_id);
4737 if(player_index != -1){
4738 Multi_create_plist_select_flag = 1;
4739 Multi_create_plist_select_id = player_id;
4744 void multi_create_plist_blit_normal()
4747 char str[CALLSIGN_LEN+5];
4748 int y_start = Mc_players_coords[gr_screen.res][MC_Y_COORD];
4751 // display all the players
4752 for(idx=0;idx<MAX_PLAYERS;idx++){
4753 // count anyone except the standalone server (if applicable)
4754 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
4758 // highlight him if he's the host
4759 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4760 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4761 gr_set_color_fast(&Color_text_active_hi);
4763 gr_set_color_fast(&Color_bright);
4766 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4767 gr_set_color_fast(&Color_text_active);
4769 gr_set_color_fast(&Color_text_normal);
4773 // optionally draw his CD status
4774 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4775 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4776 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4778 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4781 // make sure the string will fit, then display it
4782 SDL_strlcpy(str, Net_players[idx].player->callsign, SDL_arraysize(str));
4783 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4784 SDL_strlcat(str, XSTR("(O)",787), SDL_arraysize(str)); // [[ Observer ]]
4786 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4787 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4794 void multi_create_plist_blit_team()
4797 char str[CALLSIGN_LEN+1];
4798 int y_start = Mc_players_coords[gr_screen.res][MC_Y_COORD];
4801 // display all the red players first
4802 for(idx=0;idx<MAX_PLAYERS;idx++){
4803 // count anyone except the standalone server (if applicable)
4804 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
4805 // reset total offset
4808 // highlight him if he's the host
4809 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4810 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4811 gr_set_color_fast(&Color_text_active_hi);
4813 // be sure to blit the correct team button
4814 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.draw_forced(2);
4816 gr_set_color_fast(&Color_bright);
4819 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4820 gr_set_color_fast(&Color_text_active);
4822 // be sure to blit the correct team button
4823 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.draw_forced(2);
4825 gr_set_color_fast(&Color_text_normal);
4829 // optionally draw his CD status
4830 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4831 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4832 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4834 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4837 // blit the red team indicator
4838 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
4839 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
4840 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4841 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4843 total_offset += Multi_common_icon_dims[MICON_TEAM0_SELECT][0] + 1;
4846 if(Multi_common_icons[MICON_TEAM0] != -1){
4847 gr_set_bitmap(Multi_common_icons[MICON_TEAM0], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4848 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4850 total_offset += Multi_common_icon_dims[MICON_TEAM0][0] + 1;
4854 // make sure the string will fit
4855 SDL_strlcpy(str, Net_players[idx].player->callsign, SDL_arraysize(str));
4856 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4857 SDL_strlcat(str, XSTR("(O)",787), SDL_arraysize(str));
4859 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4861 // display him in the correct half of the list depending on his team
4862 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4867 // display all the green players next
4868 for(idx=0;idx<MAX_PLAYERS;idx++){
4869 // count anyone except the standalone server (if applicable)
4870 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
4871 // reset total offset
4874 // highlight him if he's the host
4875 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4876 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4877 gr_set_color_fast(&Color_text_active_hi);
4879 // be sure to blit the correct team button
4880 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.draw_forced(2);
4882 gr_set_color_fast(&Color_bright);
4885 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4886 gr_set_color_fast(&Color_text_active);
4888 // be sure to blit the correct team button
4889 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.draw_forced(2);
4891 gr_set_color_fast(&Color_text_normal);
4895 // optionally draw his CD status
4896 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4897 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4898 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4900 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4903 // blit the red team indicator
4904 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
4905 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
4906 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4907 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4909 total_offset += Multi_common_icon_dims[MICON_TEAM1_SELECT][0] + 1;
4912 if(Multi_common_icons[MICON_TEAM1] != -1){
4913 gr_set_bitmap(Multi_common_icons[MICON_TEAM1], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4914 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4916 total_offset += Multi_common_icon_dims[MICON_TEAM1][0] + 1;
4920 // make sure the string will fit
4921 SDL_strlcpy(str, Net_players[idx].player->callsign, SDL_arraysize(str));
4922 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4923 SDL_strlcat(str, XSTR("(O)",787), SDL_arraysize(str));
4925 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4927 // display him in the correct half of the list depending on his team
4928 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4934 void multi_create_list_scroll_up()
4936 if(Multi_create_list_start > 0){
4937 Multi_create_list_start--;
4939 gamesnd_play_iface(SND_SCROLL);
4941 gamesnd_play_iface(SND_GENERAL_FAIL);
4945 void multi_create_list_scroll_down()
4947 if((Multi_create_list_count - Multi_create_list_start) > Multi_create_list_max_display[gr_screen.res]){
4948 Multi_create_list_start++;
4950 gamesnd_play_iface(SND_SCROLL);
4952 gamesnd_play_iface(SND_GENERAL_FAIL);
4956 // gets a list of multiplayer misisons
4957 void multi_create_list_load_missions()
4959 char *fname, mission_name[NAME_LENGTH+1];
4963 SDL_snprintf(wild_card, SDL_arraysize(wild_card), "*%s", FS_MISSION_FILE_EXT);
4964 file_count = cf_get_file_list_preallocated(MULTI_CREATE_MAX_LIST_ITEMS, Multi_create_files_array, NULL, CF_TYPE_MISSIONS, wild_card);
4965 Multi_create_mission_count = 0;
4967 // maybe create a standalone dialog
4968 if(Game_mode & GM_STANDALONE_SERVER){
4969 std_create_gen_dialog("Loading missions");
4970 std_gen_set_text("Mission:", 1);
4973 for(idx = 0; idx < file_count; idx++){
4974 int flags,max_players;
4978 fname = Multi_create_files_array[idx];
4980 // tack on any necessary file extension
4981 filename = cf_add_ext( fname, FS_MISSION_FILE_EXT );
4983 // for multiplayer beta builds, only accept builtin missions
4984 #if defined(MULTIPLAYER_BETA_BUILD) || defined(FS2_DEMO)
4985 if(game_find_builtin_mission(filename) == NULL){
4988 #elif defined(PD_BUILD)
4989 if((game_find_builtin_mission(filename) == NULL) && !strstr(filename, "peterdrake")){
4994 if(Game_mode & GM_STANDALONE_SERVER){
4995 std_gen_set_text(filename, 2);
4998 flags = mission_parse_is_multi(filename, mission_name);
5000 // if the mission is a multiplayer mission, then add it to the mission list
5002 max_players = mission_parse_get_multi_mission_info( filename );
5003 m_respawn = The_mission.num_respawns;
5005 if ( Multi_create_mission_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
5006 multi_create_info *mcip;
5008 mcip = &Multi_create_mission_list[Multi_create_mission_count];
5009 SDL_strlcpy(mcip->filename, filename, SDL_arraysize(mcip->filename));
5010 SDL_strlcpy(mcip->name, mission_name, SDL_arraysize(mcip->name));
5011 mcip->flags = flags;
5012 mcip->respawn = m_respawn;
5013 mcip->max_players = (ubyte)max_players;
5015 // get any additional information for possibly builtin missions
5016 fs_builtin_mission *fb = game_find_builtin_mission(filename);
5020 Multi_create_mission_count++;
5026 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);
5029 // maybe create a standalone dialog
5030 if(Game_mode & GM_STANDALONE_SERVER){
5031 std_destroy_gen_dialog();
5035 void multi_create_list_load_campaigns()
5038 int idx, file_count;
5039 int campaign_type,max_players;
5043 // maybe create a standalone dialog
5044 if(Game_mode & GM_STANDALONE_SERVER){
5045 std_create_gen_dialog("Loading campaigns");
5046 std_gen_set_text("Campaign:", 1);
5049 Multi_create_campaign_count = 0;
5050 SDL_snprintf(wild_card, SDL_arraysize(wild_card), "*%s", FS_CAMPAIGN_FILE_EXT);
5051 file_count = cf_get_file_list_preallocated(MULTI_CREATE_MAX_LIST_ITEMS, Multi_create_files_array, NULL, CF_TYPE_MISSIONS, wild_card);
5052 for(idx = 0; idx < file_count; idx++){
5054 char *filename, name[NAME_LENGTH];
5056 fname = Multi_create_files_array[idx];
5058 // tack on any necessary file extension
5059 filename = cf_add_ext( fname, FS_CAMPAIGN_FILE_EXT );
5061 // for multiplayer beta builds, only accept builtin missions
5062 #if defined(MULTIPLAYER_BETA_BUILD) || defined(FS2_DEMO)
5063 if(game_find_builtin_mission(filename) == NULL){
5066 #elif defined(PD_BUILD)
5067 if((game_find_builtin_mission(filename) == NULL) && !strstr(filename, "peterdrake")){
5072 if(Game_mode & GM_STANDALONE_SERVER){
5073 std_gen_set_text(filename, 2);
5076 // if the campaign is a multiplayer campaign, then add the data to the campaign list items
5077 flags = mission_campaign_parse_is_multi( filename, name, SDL_arraysize(name) );
5078 if( flags != CAMPAIGN_TYPE_SINGLE && mission_campaign_get_info(filename,title,&campaign_type,&max_players)) {
5079 if ( Multi_create_campaign_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
5080 multi_create_info *mcip;
5082 mcip = &Multi_create_campaign_list[Multi_create_campaign_count];
5083 SDL_strlcpy(mcip->filename, filename, SDL_arraysize(mcip->filename));
5084 SDL_strlcpy(mcip->name, name, SDL_arraysize(mcip->name));
5086 // setup various flags
5087 if ( flags == CAMPAIGN_TYPE_MULTI_COOP ){
5088 mcip->flags = MISSION_TYPE_MULTI_COOP | MISSION_TYPE_MULTI;
5089 } else if ( flags == CAMPAIGN_TYPE_MULTI_TEAMS ) {
5090 mcip->flags = MISSION_TYPE_MULTI_TEAMS | MISSION_TYPE_MULTI;
5092 Int3(); // bogus campaign multi type -- find allender
5095 // 0 respawns for campaign files (should be contained within the mission files themselves)
5098 // 0 max players for campaign files
5099 mcip->max_players = (unsigned char)max_players;
5101 // get any additional information for possibly builtin missions
5102 fs_builtin_mission *fb = game_find_builtin_mission(filename);
5106 Multi_create_campaign_count++;
5111 // maybe create a standalone dialog
5112 if(Game_mode & GM_STANDALONE_SERVER){
5113 std_destroy_gen_dialog();
5117 void multi_create_list_do()
5120 int start_index,stop_index;
5121 char selected_name[255];
5123 // bail early if there aren't any selectable items
5124 if(Multi_create_list_count == 0){
5128 // first check to see if the user has clicked on an item
5129 if(Multi_create_list_select_button.pressed()){
5131 Multi_create_list_select_button.get_mouse_pos(NULL,&y);
5134 // make sure we are selectedin valid indices
5135 if((item < Multi_create_list_max_display[gr_screen.res]) && (item >= 0)){
5136 item += Multi_create_list_start;
5138 if(item < Multi_create_list_count){
5139 multi_create_list_select_item(item);
5140 gamesnd_play_iface(SND_IFACE_MOUSE_CLICK);
5145 // bail early if we don't have a start position
5146 if(Multi_create_list_start == -1){
5150 // display the list of individual campaigns/missions
5152 int y_start = Mc_list_coords[gr_screen.res][MC_Y_COORD];
5154 start_index = multi_create_select_to_index(Multi_create_list_start);
5155 stop_index = Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ? Multi_create_mission_count : Multi_create_campaign_count;
5156 for(idx=start_index; idx<stop_index; idx++){
5157 // see if we should drop out
5158 if(count == Multi_create_list_max_display[gr_screen.res]){
5162 // see if we should filter out this mission
5163 if ( !(Multi_create_file_list[idx].flags & Multi_create_filter) ){
5167 // highlight the selected item
5168 multi_create_select_to_filename(Multi_create_list_select, selected_name, SDL_arraysize(selected_name));
5169 if(!strcmp(selected_name,Multi_create_file_list[idx].filename)){
5170 gr_set_color_fast(&Color_text_selected);
5172 gr_set_color_fast(&Color_text_normal);
5175 // draw the type icon
5176 multi_create_list_blit_icons(idx, y_start);
5178 // force fit the mission name string
5179 SDL_strlcpy(selected_name, Multi_create_file_list[idx].name, SDL_arraysize(selected_name));
5180 gr_force_fit_string(selected_name,255,Mc_column1_w[gr_screen.res]);
5181 gr_string(Mc_mission_name_x[gr_screen.res],y_start,selected_name);
5183 // draw the max players if in mission mode
5184 SDL_snprintf(selected_name,SDL_arraysize(selected_name),"%d",(int)Multi_create_file_list[idx].max_players);
5185 gr_string(Mc_mission_count_x[gr_screen.res],y_start,selected_name);
5187 // force fit the mission filename string
5188 SDL_strlcpy(selected_name, Multi_create_file_list[idx].filename, SDL_arraysize(selected_name));
5189 gr_force_fit_string(selected_name,255,Mc_column3_w[gr_screen.res]);
5190 gr_string(Mc_mission_fname_x[gr_screen.res],y_start,selected_name);
5197 // takes care of stuff like changing indices around and setting up the netgame structure
5198 void multi_create_list_select_item(int n)
5200 int abs_index,campaign_type,max_players;
5201 char title[NAME_LENGTH+1];
5202 netgame_info ng_temp;
5205 char *campaign_desc;
5207 // if not on the standalone server
5208 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5211 // on the standalone
5213 memset(&ng_temp,0,sizeof(netgame_info));
5217 if ( n != Multi_create_list_select ) {
5218 // check to see if this is a valid index, and bail if it is not
5219 abs_index = multi_create_select_to_index(n);
5220 if(abs_index == -1){
5224 Multi_create_list_select = n;
5226 // set the mission name
5227 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5228 multi_create_select_to_filename(n, ng->mission_name, SDL_arraysize(ng->mission_name));
5230 multi_create_select_to_filename(n, ng->campaign_name, SDL_arraysize(ng->campaign_name));
5233 // make sure the netgame type is properly set
5234 int old_type = Netgame.type_flags;
5235 abs_index = multi_create_select_to_index(n);
5236 if(abs_index != -1){
5237 if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_TEAMS){
5239 // if we're in squad war mode, leave it as squad war
5240 if(old_type & NG_TYPE_SW){
5241 ng->type_flags = NG_TYPE_SW;
5243 ng->type_flags = NG_TYPE_TVT;
5246 ng->type_flags = NG_TYPE_TVT;
5248 } else if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_COOP){
5249 ng->type_flags = NG_TYPE_COOP;
5250 } else if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_DOGFIGHT){
5251 ng->type_flags = NG_TYPE_DOGFIGHT;
5256 // if we're no longer in a TvT game, just uncheck the squadwar checkbox
5257 if(!(ng->type_flags & NG_TYPE_TEAM)){
5258 Multi_create_sw_checkbox.set_state(0);
5262 // if we switched from something else to team vs. team mode, do some special processing
5263 if((ng->type_flags & NG_TYPE_TEAM) && (ng->type_flags != old_type) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
5267 switch(Multi_create_list_mode){
5268 case MULTI_CREATE_SHOW_MISSIONS:
5269 // don't forget to update the info box window thingie
5270 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5271 ship_init(); // mwa -- 10/15/97. Call this function to reset number of ships in mission
5272 ng->max_players = mission_parse_get_multi_mission_info( ng->mission_name );
5274 SDL_assert(ng->max_players > 0);
5275 SDL_strlcpy(ng->title, The_mission.name, SDL_arraysize(ng->title));
5277 // set the information area text
5278 multi_common_set_text(The_mission.mission_desc);
5280 // if we're on the standalone, send a request for the description
5282 send_netgame_descript_packet(&Netgame.server_addr,0);
5283 multi_common_set_text("");
5286 // set the respawns as appropriate
5287 if(Netgame.options.respawn <= Multi_create_file_list[abs_index].respawn){
5288 ng->respawn = Netgame.options.respawn;
5289 nprintf(("Network","Using netgame options for respawn count (%d %d)\n",Netgame.options.respawn,Multi_create_file_list[abs_index].respawn));
5291 ng->respawn = Multi_create_file_list[abs_index].respawn;
5292 nprintf(("Network","Using mission settings for respawn count (%d %d)\n",Netgame.options.respawn,Multi_create_file_list[abs_index].respawn));
5295 case MULTI_CREATE_SHOW_CAMPAIGNS:
5296 // if not on the standalone server
5297 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5298 // get the campaign info
5299 memset(title,0,NAME_LENGTH+1);
5300 if(!mission_campaign_get_info(ng->campaign_name,title,&campaign_type,&max_players, &campaign_desc)) {
5301 memset(ng->campaign_name,0,NAME_LENGTH+1);
5302 ng->max_players = 0;
5304 // if we successfully got the # of players
5306 memset(ng->title,0,NAME_LENGTH+1);
5307 SDL_strlcpy(ng->title, title, SDL_arraysize(ng->title));
5308 ng->max_players = max_players;
5311 nprintf(("Network","MC MAX PLAYERS : %d\n",ng->max_players));
5313 // set the information area text
5314 // multi_common_set_text(ng->title);
5315 multi_common_set_text(campaign_desc);
5317 // if on the standalone server, send a request for the description
5319 // no descriptions currently kept for campaigns
5322 // netgame respawns are always 0 for campaigns (until the first mission is loaded)
5327 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5329 send_netgame_update_packet();
5331 // update all machines about stuff like respawns, etc.
5332 multi_options_update_netgame();
5334 multi_options_update_mission(ng, Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ? 1 : 0);
5339 void multi_create_list_blit_icons(int list_index, int y_start)
5341 multi_create_info *mcip;
5342 fs_builtin_mission *fb;
5345 // get a pointer to the list item
5346 max_index = Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ? Multi_create_mission_count - 1 : Multi_create_campaign_count - 1;
5347 if((list_index < 0) || (list_index > max_index)){
5350 mcip = &Multi_create_file_list[list_index];
5352 // blit the multiplayer type icons
5353 if(mcip->flags & MISSION_TYPE_MULTI_COOP){
5354 if(Multi_common_icons[MICON_COOP] >= 0){
5355 gr_set_bitmap(Multi_common_icons[MICON_COOP], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5356 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5358 } else if(mcip->flags & MISSION_TYPE_MULTI_TEAMS){
5359 if(Multi_common_icons[MICON_TVT] >= 0){
5360 gr_set_bitmap(Multi_common_icons[MICON_TVT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5361 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5363 } else if(mcip->flags & MISSION_TYPE_MULTI_DOGFIGHT){
5364 if(Multi_common_icons[MICON_DOGFIGHT] >= 0){
5365 gr_set_bitmap(Multi_common_icons[MICON_DOGFIGHT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5366 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5370 // if its a valid mission, blit the valid mission icon
5371 if(MULTI_IS_TRACKER_GAME && (mcip->valid_status == MVALID_STATUS_VALID)){
5372 if(Multi_common_icons[MICON_VALID] >= 0){
5373 gr_set_bitmap(Multi_common_icons[MICON_VALID], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5374 gr_bitmap(Mc_icon_valid_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_valid_coords[gr_screen.res][MC_Y_COORD]);
5378 // now see if its a builtin mission
5379 fb = game_find_builtin_mission(mcip->filename);
5380 // if the mission is from volition, blit the volition icon
5381 if((fb != NULL) && (fb->flags & FSB_FROM_VOLITION)){
5382 if(Multi_common_icons[MICON_VOLITION] >= 0){
5383 gr_set_bitmap(Multi_common_icons[MICON_VOLITION], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5384 gr_bitmap(Mc_icon_volition_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_volition_coords[gr_screen.res][MC_Y_COORD]);
5389 // check for mdisk mission
5390 fb = game_find_builtin_mission(mcip->filename);
5391 if((fb != NULL) && (fb->flags & FSB_FROM_MDISK)){
5392 if(Multi_common_icons[MICON_MDISK] >= 0){
5393 gr_set_bitmap(Multi_common_icons[MICON_MDISK], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5394 gr_bitmap(Mc_icon_silent_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_silent_coords[gr_screen.res][MC_Y_COORD]);
5400 void multi_create_accept_hit()
5402 char selected_name[255];
5403 int start_campaign = 0;
5405 // make sure all players have finished joining
5406 if(!multi_netplayer_state_check(NETPLAYER_STATE_JOINED,1)){
5407 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("Please wait until all clients have finished joining",788));
5408 gamesnd_play_iface(SND_GENERAL_FAIL);
5411 gamesnd_play_iface(SND_COMMIT_PRESSED);
5414 // do single mission stuff
5415 switch(Multi_create_list_mode){
5416 case MULTI_CREATE_SHOW_MISSIONS:
5417 if(Multi_create_list_select != -1){
5418 // set the netgame mode
5419 Netgame.campaign_mode = MP_SINGLE;
5421 // setup various filenames and mission names
5422 multi_create_select_to_filename(Multi_create_list_select, selected_name, SDL_arraysize(selected_name));
5423 SDL_strlcpy( Game_current_mission_filename, selected_name, MAX_FILENAME_LEN );
5424 SDL_strlcpy( Netgame.mission_name, selected_name, MAX_FILENAME_LEN );
5427 ml_printf(NOX("Starting single mission %s, with %d players"), Game_current_mission_filename, multi_num_players());
5429 multi_common_add_notify(XSTR("No mission selected!",789));
5434 case MULTI_CREATE_SHOW_CAMPAIGNS:
5435 // do campaign related stuff
5436 if(Multi_create_list_select != -1){
5437 // set the netgame mode
5438 Netgame.campaign_mode = MP_CAMPAIGN;
5440 // start a campaign instead of a single mission
5441 multi_create_select_to_filename(Multi_create_list_select, selected_name, SDL_arraysize(selected_name));
5442 multi_campaign_start(selected_name);
5446 ml_printf(NOX("Starting campaign %s, with %d players"), selected_name, multi_num_players());
5448 multi_common_add_notify(XSTR("No campaign selected!",790));
5454 // if this is a team vs team situation, lock the players send a final team update
5455 if((Netgame.type_flags & NG_TYPE_TEAM) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
5456 multi_team_host_lock_all();
5457 multi_team_send_update();
5460 // if not on the standalone, move to the mission sync state which will take care of everything
5461 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5462 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
5463 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
5465 // otherwise tell the standalone to do so
5467 // when the standalone receives this, he'll do the mission syncing himself
5468 send_mission_sync_packet(MULTI_SYNC_PRE_BRIEFING,start_campaign);
5472 void multi_create_draw_filter_buttons()
5474 // highlight the correct filter button
5475 if ( Multi_create_filter == MISSION_TYPE_MULTI ){
5476 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL].button.draw_forced(2);
5477 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_COOP ) {
5478 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 1].button.draw_forced(2);
5479 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_TEAMS ) {
5480 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 2].button.draw_forced(2);
5481 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_DOGFIGHT ){
5482 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 3].button.draw_forced(2);
5488 void multi_create_set_selected_team(int team)
5492 // if we don't currently have a player selected, don't do anything
5493 if(!Multi_create_plist_select_flag){
5494 gamesnd_play_iface(SND_GENERAL_FAIL);
5497 gamesnd_play_iface(SND_USER_SELECT);
5499 // otherwise attempt to set the team for this guy
5500 player_index = find_player_id(Multi_create_plist_select_id);
5501 if(player_index != -1){
5502 multi_team_set_team(&Net_players[player_index],team);
5506 void multi_create_handle_join(net_player *pl)
5508 // for now just play a bloop sound
5509 gamesnd_play_iface(SND_ICON_DROP_ON_WING);
5512 // fill in net address of player the mouse is over, return player index (or -1 if none)
5513 short multi_create_get_mouse_id()
5515 // determine where he clicked (y pixel value)
5517 Multi_create_player_select_button.get_mouse_pos(NULL,&y);
5519 // select things a little differently if we're in team vs. team or non-team vs. team mode
5521 if(Netgame.type_flags & NG_TYPE_TEAM){
5522 int player_index = -1;
5524 // look through all of team red first
5525 for(idx=0;idx<MAX_PLAYERS;idx++){
5526 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
5529 // if this is the _nth_ guy
5537 // if we still haven't found him yet, look through the green team
5538 if(player_index == -1){
5539 for(idx=0;idx<MAX_PLAYERS;idx++){
5540 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
5542 // if this is the _nth_ guy
5551 if(player_index != -1){
5552 return Net_players[player_index].player_id;
5555 // select the nth active player if possible, disregarding the standalone server
5556 for(idx=0;idx<MAX_PLAYERS;idx++){
5557 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
5560 // if this is the _nth_ guy
5562 return Net_players[idx].player_id;
5571 void multi_create_select_to_filename(int select_index, char *filename, const int max_filelen)
5575 // look through the mission list
5576 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5577 for(idx=0;idx<Multi_create_mission_count;idx++){
5578 if(Multi_create_file_list[idx].flags & Multi_create_filter){
5582 // if we found the item
5583 if(select_index < 0){
5584 SDL_strlcpy(filename, Multi_create_file_list[idx].filename, max_filelen);
5589 // look through the campaign list
5590 else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
5591 for(idx=0;idx<Multi_create_campaign_count;idx++){
5594 // if we found the item
5595 if(select_index < 0){
5596 SDL_strlcpy(filename, Multi_create_file_list[idx].filename, max_filelen);
5602 SDL_strlcpy(filename, "", max_filelen);
5605 int multi_create_select_to_index(int select_index)
5608 int lookup_index = 0;
5610 // look through the mission list
5611 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5612 for(idx=0;idx<Multi_create_mission_count;idx++){
5613 if(Multi_create_file_list[idx].flags & Multi_create_filter){
5617 // if we found the item
5618 if(select_index < lookup_index){
5623 // look through the campaign list
5624 else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
5625 for(idx=0;idx<Multi_create_campaign_count;idx++){
5628 // if we found the item
5629 if(select_index < 0){
5638 int multi_create_ok_to_commit()
5640 int player_count, observer_count, idx;
5641 int notify_of_hacked_ships_tbl = 0;
5642 int notify_of_hacked_weapons_tbl = 0;
5643 char err_string[255];
5647 // make sure we have a valid mission selected
5648 if(Multi_create_list_select < 0){
5652 // if this is not a valid mission, let the player know
5653 abs_index = multi_create_select_to_index(Multi_create_list_select);
5658 // if we're playing with a hacked ships.tbl (on PXO)
5659 notify_of_hacked_ships_tbl = 0;
5660 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5661 if(!Game_ships_tbl_valid){
5662 notify_of_hacked_ships_tbl = 1;
5665 if(Netgame.flags & NG_FLAG_HACKED_SHIPS_TBL){
5666 notify_of_hacked_ships_tbl = 1;
5669 if(!MULTI_IS_TRACKER_GAME){
5670 notify_of_hacked_ships_tbl = 0;
5672 if(notify_of_hacked_ships_tbl){
5673 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){
5678 // if we're playing with a hacked weapons.tbl (on PXO)
5679 notify_of_hacked_weapons_tbl = 0;
5680 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5681 if(!Game_weapons_tbl_valid){
5682 notify_of_hacked_weapons_tbl = 1;
5685 if(Netgame.flags & NG_FLAG_HACKED_WEAPONS_TBL){
5686 notify_of_hacked_weapons_tbl = 1;
5689 if(!MULTI_IS_TRACKER_GAME){
5690 notify_of_hacked_weapons_tbl = 0;
5692 if(notify_of_hacked_weapons_tbl){
5693 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){
5698 // if any of the players have hacked data
5700 for(idx=0; idx<MAX_PLAYERS; idx++){
5701 // look for hacked players
5702 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAXOR)){
5706 // message everyone - haha
5707 if(Net_players[idx].player != NULL){
5708 SDL_snprintf(err_string, SDL_arraysize(err_string), "%s %s", Net_players[idx].player->callsign, XSTR("has hacked tables/data", 1271));
5710 SDL_snprintf(err_string, SDL_arraysize(err_string), "somebody %s", XSTR("has hacked tables/data", 1271));
5712 send_game_chat_packet(Net_player, err_string, MULTI_MSG_ALL, NULL, NULL, 1);
5715 // if we found a hacked set of data
5718 if(MULTI_IS_TRACKER_GAME){
5720 // don't allow squad war matches to continue
5721 if(Netgame.type_flags & NG_TYPE_SW){
5723 // if this is squad war, don't allow it to continue
5724 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));
5729 // otherwise, warn the players that stats will not saved
5731 // if this is squad war, don't allow it to continue
5732 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){
5737 // warn the players that stats will not saved
5738 if(popup(PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON, 2, XSTR("&Back", 995), XSTR("&Continue", 780), XSTR("One or more players has hacked data files. If you continue, stats will not be stored at the end of the mission", 1273)) <= 0){
5743 // non-pxo, just give a notice
5745 if(popup(PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON, 2, XSTR("&Back", 995), XSTR("&Continue", 780), XSTR("One or more players has hacked data files", 1274)) <= 0){
5751 // check to see that we don't have too many observers
5752 observer_count = multi_num_observers();
5753 if(observer_count > Netgame.options.max_observers){
5754 // print up the error string
5755 SDL_snprintf(err_string,SDL_arraysize(err_string),XSTR("There are too many observers in the game\n\nMax : %d\nCurrently %d\n\nPlease dump a few",791),Netgame.options.max_observers,observer_count);
5757 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, err_string);
5761 // check to see that we have a valid # of players for the the # of ships in the game
5762 player_count = multi_num_players();
5763 if(player_count > Netgame.max_players){
5764 // print up the error string
5765 SDL_snprintf(err_string,SDL_arraysize(err_string),XSTR("There are too many players in the game\n\nMax : %d\nCurrently %d\n\nPlease dump a few", 792), Netgame.max_players,player_count);
5767 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, err_string);
5771 // check to see if teams are assigned properly in a team vs. team situation
5772 if(Netgame.type_flags & NG_TYPE_TEAM){
5773 if(!multi_team_ok_to_commit()){
5774 gamesnd_play_iface(SND_GENERAL_FAIL);
5775 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("Teams and/or team captains are not assigned properly", 793));
5781 if(!multi_create_verify_cds()){
5782 gamesnd_play_iface(SND_GENERAL_FAIL);
5784 #ifdef MULTIPLAYER_BETA_BUILD
5785 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, "You need 1 CD for every player!");
5787 #ifdef DVD_MESSAGE_HACK
5788 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You need 1 DVD for every 4 players!", 794));
5790 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You need 1 CD for every 4 players!", 794));
5796 // if we're playing on the tracker
5797 if(MULTI_IS_TRACKER_GAME){
5798 #ifdef PXO_CHECK_VALID_MISSIONS
5799 if((Multi_create_file_list == Multi_create_mission_list) && (Multi_create_file_list[abs_index].valid_status != MVALID_STATUS_VALID)){
5800 if(popup(PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON, 2, XSTR("&Back", 995), XSTR("&Continue", 780), XSTR("You have selected a mission which is either invalid or unknown to PXO. Your stats will not be saved if you continue",996)) <= 0){
5807 if(!(Netgame.type_flags & NG_TYPE_SW)){
5808 // if he is playing by himself, tell him stats will not be accepted
5809 if(multi_num_players() == 1){
5810 if(popup(PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG, 2, XSTR("&Back", 995), XSTR("&Continue", 780), XSTR("Warning\n\nIf you start a PXO mission by yourself, your stats will not be updated", 997)) <= 0){
5818 return multi_sw_ok_to_commit();
5826 int multi_create_verify_cds()
5828 int player_count = multi_num_players();
5832 // count how many cds we have
5834 for(idx=0;idx<MAX_PLAYERS;idx++){
5835 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAS_CD)){
5840 // for the beta, everyone must have a CD
5841 #ifdef MULTIPLAYER_BETA_BUILD
5842 if(multi_cd_count < player_count){
5846 // determine if we have enough
5847 float ratio = (float)player_count / (float)multi_cd_count;
5848 // greater than a 4 to 1 ratio
5854 // we meet the conditions
5858 // returns an index into Multi_create_mission_list
5859 int multi_create_lookup_mission(char *fname)
5863 for(idx=0; idx<Multi_create_mission_count; idx++){
5864 if(!SDL_strcasecmp(fname, Multi_create_mission_list[idx].filename)){
5869 // couldn't find the mission
5873 // returns an index into Multi_create_campaign_list
5874 int multi_create_lookup_campaign(char *fname)
5878 for(idx=0; idx<Multi_create_campaign_count; idx++){
5879 if(!SDL_strcasecmp(fname, Multi_create_campaign_list[idx].filename)){
5884 // couldn't find the campaign
5888 void multi_create_refresh_pxo()
5890 // delete mvalid.cfg if it exists
5891 cf_delete(MULTI_VALID_MISSION_FILE, CF_TYPE_DATA);
5893 // refresh missions from the tracker
5894 multi_update_valid_missions();
5897 void multi_create_sw_clicked()
5900 netgame_info ng_temp;
5903 int file_index = multi_create_select_to_index(Multi_create_list_select);
5905 // either a temporary netgame or the real one
5906 if(MULTIPLAYER_MASTER){
5913 // maybe switch squad war off
5914 if(!Multi_create_sw_checkbox.checked()){
5915 // if the mission selected is a coop mission, go back to coop mode
5916 SDL_assert(file_index != -1);
5917 if(file_index == -1){
5918 ng->type_flags = NG_TYPE_COOP;
5920 if(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_TEAMS){
5921 ng->type_flags = NG_TYPE_TVT;
5922 } else if(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_DOGFIGHT){
5923 ng->type_flags = NG_TYPE_DOGFIGHT;
5925 ng->type_flags = NG_TYPE_COOP;
5928 // switch squad war on
5930 SDL_assert(file_index != -1);
5931 if((file_index == -1) || !(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_TEAMS)){
5932 Multi_create_sw_checkbox.set_state(0);
5934 // at this point we know its safe to switch squad war on
5935 ng->type_flags = NG_TYPE_SW;
5940 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5942 send_netgame_update_packet();
5944 // update all machines about stuff like respawns, etc.
5945 multi_options_update_netgame();
5947 // on the standalone
5949 // standalone will take care of polling the usertracker
5950 multi_options_update_mission(ng, Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ? 1 : 0);
5956 // -------------------------------------------------------------------------------------------------------------
5958 // MULTIPLAYER HOST OPTIONS SCREEN
5961 #define MULTI_HO_NUM_BUTTONS 12
5962 #define MULTI_HO_NUM_RADIO_BUTTONS 10
5966 #define MULTI_HO_PALETTE "InterfacePalette"
5968 static const char *Multi_ho_bitmap_fname[GR_NUM_RESOLUTIONS] = {
5969 "MultiHost", // GR_640
5970 "2_MultiHost" // GR_1024
5973 static const char *Multi_ho_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
5974 "MultiHost-M", // GR_640
5975 "2_MultiHost-M" // GR_1024
5979 UI_WINDOW Multi_ho_window; // the window object for the join screen
5980 UI_INPUTBOX Multi_ho_respawns; // the # of respawns allowed in the game
5981 UI_INPUTBOX Multi_ho_time_limit; // mission time limit
5982 UI_INPUTBOX Multi_ho_voice_wait; // wait time between tokens
5983 UI_INPUTBOX Multi_ho_kill_limit; // kill limit in a furball mission
5984 UI_INPUTBOX Multi_ho_obs; // # of observers we'll allow
5985 int Multi_ho_bitmap; // the background bitmap
5987 // constants for coordinate lookup
5988 #define MULTI_HO_X_COORD 0
5989 #define MULTI_HO_Y_COORD 1
5990 #define MULTI_HO_W_COORD 2
5991 #define MULTI_HO_H_COORD 3
5992 #define MULTI_HO_TEXT_X_COORD 4
5993 #define MULTI_HO_TEXT_Y_COORD 5
5996 #define MULTI_HO_MSG_RANK 0 // highest ranking players can do messaging
5997 #define MULTI_HO_MSG_LEADER 1 // wing/team leaders can do messaging
5998 #define MULTI_HO_MSG_ANY 2 // any player can do messaging
5999 #define MULTI_HO_MSG_HOST 3 // only the host can do messaging
6000 #define MULTI_HO_END_RANK 4 // highest rank can and host can end mission
6001 #define MULTI_HO_END_LEADER 5 // wing/team leaders and host can end the mission
6002 #define MULTI_HO_END_ANY 6 // any player can end the mission
6003 #define MULTI_HO_END_HOST 7 // only host can end the mission
6004 #define MULTI_HO_VOICE_ON 8 // voice toggled on
6005 #define MULTI_HO_VOICE_OFF 9 // voice toggled off
6006 #define MULTI_HO_HOST_MODIFIES 10 // only the host or team captains can modify ships/weapons in briefing
6007 #define MULTI_HO_ACCEPT 11 // accept button
6009 ui_button_info Multi_ho_buttons[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_BUTTONS] = {
6012 // who is allowed to message
6013 ui_button_info("MH_00", 13, 157, -1, -1, 0), // highest rank
6014 ui_button_info("MH_01", 13, 179, -1, -1, 1), // team/wing leader
6015 ui_button_info("MH_02", 13, 200, -1, -1, 2), // any
6016 ui_button_info("MH_03", 13, 223, -1, -1, 3), // host
6018 // who is allowed to end the mission
6019 ui_button_info("MH_09", 13, 273, -1, -1, 9), // highest rank
6020 ui_button_info("MH_10", 13, 295, -1, -1, 10), // team/wing leader
6021 ui_button_info("MH_11", 13, 317, -1, -1, 11), // any
6022 ui_button_info("MH_12", 13, 339, -1, -1, 12), // host
6024 // voice on/off button
6025 ui_button_info("MH_14", 396, 156, -1, -1, 14),
6026 ui_button_info("MH_15", 453, 156, -1, -1, 15),
6028 // host modifies ships
6029 ui_button_info("MH_19", 215, 410, -1, -1, 19),
6032 ui_button_info("MH_18", 560, 411, -1, -1, 18),
6034 // who is allowed to message
6035 ui_button_info("MH_00", 3, 160, 46, 166, 0), // highest rank
6036 ui_button_info("MH_01", 3, 179, 46, 185, 1), // team/wing leader
6037 ui_button_info("MH_02", 3, 196, 46, 203, 2), // any
6038 ui_button_info("MH_03", 3, 214, 46, 220, 3), // host
6040 // who is allowed to end the mission
6041 ui_button_info("MH_04", 3, 257, 46, 265, 4), // highest rank
6042 ui_button_info("MH_05", 3, 276, 46, 283, 5), // team/wing leader
6043 ui_button_info("MH_06", 3, 294, 46, 300, 6), // any
6044 ui_button_info("MH_07", 3, 311, 46, 317, 7), // host
6046 // voice on/off button
6047 ui_button_info("MH_09", 542, 158, 545, 185, 9),
6048 ui_button_info("MH_10", 598, 158, 604, 185, 10),
6050 // host modifies ships
6051 ui_button_info("MH_13", 542, 377, 437, 363, 13),
6054 ui_button_info("MH_14", 572, 428, 580, 414, 14),
6058 // who is allowed to message
6059 ui_button_info("2_MH_00", 5, 256, 73, 269, 0), // highest rank
6060 ui_button_info("2_MH_01", 5, 286, 73, 297, 1), // team/wing leader
6061 ui_button_info("2_MH_02", 5, 314, 73, 325, 2), // any
6062 ui_button_info("2_MH_03", 5, 341, 73, 352, 3), // host
6064 // who is allowed to end the mission
6065 ui_button_info("2_MH_04", 5, 412, 73, 425, 4), // highest rank
6066 ui_button_info("2_MH_05", 5, 442, 73, 452, 5), // team/wing leader
6067 ui_button_info("2_MH_06", 5, 470, 73, 480, 6), // any
6068 ui_button_info("2_MH_07", 5, 497, 73, 508, 7), // host
6070 // voice on/off button
6071 ui_button_info("2_MH_09", 867, 253, 872, 296, 9),
6072 ui_button_info("2_MH_10", 957, 253, 966, 296, 10),
6074 // host modifies ships
6075 ui_button_info("2_MH_13", 867, 603, 784, 581, 13),
6078 ui_button_info("2_MH_14", 916, 685, 925, 665, 14),
6081 UI_XSTR Multi_ho_text[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_BUTTONS] = {
6083 // not needed for FS1
6085 {"Highest rank", 1280, 46, 166, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_RANK].button},
6086 {"Team / wing-leader", 1281, 46, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_LEADER].button},
6087 {"Any", 1282, 46, 203, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_ANY].button},
6088 {"Host", 1283, 46, 220, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_HOST].button},
6089 {"Highest rank", 1280, 46, 265, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_RANK].button},
6090 {"Team / wing-leader", 1281, 46, 283, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_LEADER].button},
6091 {"Any", 1282, 46, 300, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_ANY].button},
6092 {"Host", 1283, 46, 317, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_HOST].button},
6093 {"On", 1285, 545, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_VOICE_ON].button},
6094 {"Off", 1286, 604, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_VOICE_OFF].button},
6095 {"Host modifies ships", 1287, 437, 363, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_HOST_MODIFIES].button},
6096 {"Exit", 1417, 572, 418, UI_XSTR_COLOR_PINK, -1, &Multi_ho_buttons[0][MULTI_HO_ACCEPT].button},
6100 // not needed for FS1
6102 {"Highest rank", 1280, 62, 269, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_RANK].button},
6103 {"Team / wing-leader", 1281, 62, 297, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_LEADER].button},
6104 {"Any", 1282, 62, 325, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_ANY].button},
6105 {"Host", 1283, 62, 352, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_HOST].button},
6106 {"Highest rank", 1280, 62, 425, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_RANK].button},
6107 {"Team / wing-leader", 1281, 62, 452, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_LEADER].button},
6108 {"Any", 1282, 62, 480, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_ANY].button},
6109 {"Host", 1283, 62, 508, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_HOST].button},
6110 {"On", 1285, 877, 294, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_VOICE_ON].button},
6111 {"Off", 1286, 967, 293, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_VOICE_OFF].button},
6112 {"Host modifies ships", 1287, 869, 589, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_HOST_MODIFIES].button},
6113 {"Exit", 1417, 953, 672, UI_XSTR_COLOR_PINK, -1, &Multi_ho_buttons[1][MULTI_HO_ACCEPT].button},
6118 // radio button controls
6119 #define MULTI_HO_NUM_RADIO_GROUPS 3
6120 #define MULTI_HO_MSG_GROUP 0 // group dealing with squadmate messaging
6121 #define MULTI_HO_END_GROUP 1 // group dealing with ending the mission
6122 #define MULTI_HO_VOICE_GROUP 2 // group dealing with voice stuff
6123 int Multi_ho_radio_groups[MULTI_HO_NUM_RADIO_GROUPS] = { // currently selected button in the radio button group
6126 int Multi_ho_radio_info[MULTI_HO_NUM_RADIO_BUTTONS][3] = { // info related to each of the radio buttons themselves
6127 // { group #, value, button id# }
6128 {0, 0, 0}, // highest ranking players can do messaging
6129 {0, 1, 1}, // wing/team leaders can do messaging
6130 {0, 2, 2}, // any player can do messaging
6131 {0, 3, 3}, // only host can do messaging
6132 {1, 0, 4}, // highest rank and host can end the mission
6133 {1, 1, 5}, // team/wing leader can end the mission
6134 {1, 2, 6}, // any player can end the mission
6135 {1, 3, 7}, // only the host can end the mission
6136 {2, 0, 8}, // voice toggled on
6137 {2, 1, 9} // voice toggled off
6141 #define MULTI_HO_NUM_SLIDERS 3
6142 #define MULTI_HO_SLIDER_VOICE_QOS 0 // voice quality of sound
6143 #define MULTI_HO_SLIDER_VOICE_DUR 1 // max duration of voice recording
6144 #define MULTI_HO_SLIDER_SKILL 2 // skill level
6146 const char *filename;
6151 UI_DOT_SLIDER_NEW slider; // because we have a class inside this struct, we need the constructor below..
6153 ho_sliders(const char *name, int x1, int y1, int xt1, int yt1, int h, int _dot_w, int _dots) : filename(name), x(x1), y(y1), xt(xt1), yt(yt1), hotspot(h), dot_w(_dot_w), dots(_dots){}
6155 ho_sliders Multi_ho_sliders[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_SLIDERS] = {
6158 ho_sliders("MH_16", 396, 210, -1, -1, 16, 19, 10), // voice qos
6159 ho_sliders("MH_17", 396, 263, -1, -1, 17, 19, 10), // voice duration
6160 ho_sliders("MH_13", 10, 387, -1, -1, 13, 36, 5), // skill level
6162 ho_sliders("MH_11", 428, 214, 437, 199, 11, 19, 10), // voice qos
6163 ho_sliders("MH_12", 428, 261, 437, 246, 12, 19, 10), // voice duration
6164 ho_sliders("MH_08", 237, 454, 230, 411, 8, 36, 5), // skill level
6168 ho_sliders("2_MH_11", 684, 343, 690, 323, 11, 32, 10), // voice qos
6169 ho_sliders("2_MH_12", 685, 418, 837, 468, 12, 32, 10), // voice duration
6170 ho_sliders("2_MH_08", 379, 727, 369, 663, 8, 60, 5), // skill level
6174 int Multi_ho_mission_respawn;
6176 int Multi_ho_host_modifies;
6178 // whether or not any of the inputboxes on this screen had focus last frame
6179 int Multi_ho_lastframe_input = 0;
6181 // game information text areas
6185 #define MULTI_HO_NUM_TITLES 14
6187 UI_XSTR Multi_ho_titles[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_TITLES] = {
6189 { "AI Orders", 1289, 32, 144, UI_XSTR_COLOR_GREEN, -1, NULL },
6190 { "End Mission", 1290, 32, 242, UI_XSTR_COLOR_GREEN, -1, NULL },
6191 { "Time Limit", 1291, 32, 347, UI_XSTR_COLOR_GREEN, -1, NULL },
6192 { "Min", 1292, 74, 362, UI_XSTR_COLOR_GREEN, -1, NULL },
6193 { "Respawn Limit", 1288, 32, 378, UI_XSTR_COLOR_GREEN, -1, NULL },
6194 { "Kill Limit", 1293, 32, 409, UI_XSTR_COLOR_GREEN, -1, NULL },
6195 { "Observers", 1294, 32, 441, UI_XSTR_COLOR_GREEN, -1, NULL },
6196 { "Skill Level", 1284, 230, 411, UI_XSTR_COLOR_GREEN, -1, NULL },
6197 { "Voice Transmission", 1295, 437, 144, UI_XSTR_COLOR_GREEN, -1, NULL },
6198 { "Voice Quality", 1296, 437, 199, UI_XSTR_COLOR_GREEN, -1, NULL },
6199 { "Message Duration", 1297, 437, 246, UI_XSTR_COLOR_GREEN, -1, NULL },
6200 { "sec", 1522, 523, 292, UI_XSTR_COLOR_GREEN, -1, NULL },
6201 { "sec", 1523, 523, 332, UI_XSTR_COLOR_GREEN, -1, NULL },
6202 { "Voice Wait", 1298, 437, 313, UI_XSTR_COLOR_GREEN, -1, NULL },
6205 { "AI Orders", 1289, 48, 238, UI_XSTR_COLOR_GREEN, -1, NULL },
6206 { "End Mission", 1290, 48, 394, UI_XSTR_COLOR_GREEN, -1, NULL },
6207 { "Time Limit", 1291, 50, 568, UI_XSTR_COLOR_GREEN, -1, NULL },
6208 { "Min", 1292, 119, 581, UI_XSTR_COLOR_GREEN, -1, NULL },
6209 { "Respawn Limit", 1288, 50, 618, UI_XSTR_COLOR_GREEN, -1, NULL },
6210 { "Kill Limit", 1293, 50, 668, UI_XSTR_COLOR_GREEN, -1, NULL },
6211 { "Observers", 1294, 50, 718, UI_XSTR_COLOR_GREEN, -1, NULL },
6212 { "Skill Level", 1284, 398, 670, UI_XSTR_COLOR_GREEN, -1, NULL },
6213 { "Voice Transmission", 1295, 869, 239, UI_XSTR_COLOR_GREEN, -1, NULL },
6214 { "Voice Quality", 1296, 690, 331, UI_XSTR_COLOR_GREEN, -1, NULL },
6215 { "Message Duration", 1297, 690, 405, UI_XSTR_COLOR_GREEN, -1, NULL },
6216 { "sec", 1522, 837, 467, UI_XSTR_COLOR_GREEN, -1, NULL },
6217 { "sec", 1523, 837, 534, UI_XSTR_COLOR_GREEN, -1, NULL },
6218 { "Voice Wait", 1298, 742, 510, UI_XSTR_COLOR_GREEN, -1, NULL },
6223 // mission time limit input box
6224 int Ho_time_coords[GR_NUM_RESOLUTIONS][4] = {
6237 // furball kill limit input box
6238 int Ho_kill_coords[GR_NUM_RESOLUTIONS][4] = {
6251 // voice recording duration text display area
6252 int Ho_vd_coords[GR_NUM_RESOLUTIONS][4] = {
6265 // voice token wait input box
6266 int Ho_vw_coords[GR_NUM_RESOLUTIONS][6] = {
6279 // observer count input box
6280 int Ho_obs_coords[GR_NUM_RESOLUTIONS][4] = {
6293 // skill text description area
6294 int Ho_st_coords[GR_NUM_RESOLUTIONS][4] = {
6307 // respawn input box
6308 int Ho_rsp_coords[GR_NUM_RESOLUTIONS][6] = {
6321 // respawn max text area
6322 int Ho_max_rsp_coords[GR_NUM_RESOLUTIONS][2] = {
6335 // maximum values for various input boxes (to notify user of overruns)
6336 #define MULTI_HO_MAX_TIME_LIMIT 500
6337 #define MULTI_HO_MAX_TOKEN_WAIT 5
6338 #define MULTI_HO_MAX_KILL_LIMIT 9999
6339 #define MULTI_HO_MAX_OBS 4
6341 // LOCAL function definitions
6342 void multi_ho_check_buttons();
6343 void multi_ho_button_pressed(int n);
6344 void multi_ho_draw_radio_groups();
6345 void multi_ho_accept_hit();
6346 void multi_ho_get_options();
6347 void multi_ho_apply_options();
6348 void multi_ho_display_record_time();
6349 int multi_ho_check_values();
6350 void multi_ho_check_focus();
6351 void multi_ho_blit_max_respawns();
6352 void multi_ho_display_skill_level();
6354 void multi_host_options_init()
6358 // create the interface window
6359 Multi_ho_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
6360 Multi_ho_window.set_mask_bmap(Multi_ho_bitmap_mask_fname[gr_screen.res]);
6362 // load the background bitmap
6363 Multi_ho_bitmap = bm_load(Multi_ho_bitmap_fname[gr_screen.res]);
6364 if(Multi_ho_bitmap < 0){
6365 // we failed to load the bitmap - this is very bad
6369 // initialize the common notification messaging
6370 multi_common_notify_init();
6372 // use the common interface palette
6373 multi_common_set_palette();
6375 // create the interface buttons
6376 for(idx=0;idx<MULTI_HO_NUM_BUTTONS;idx++){
6377 // create the object
6378 Multi_ho_buttons[gr_screen.res][idx].button.create(&Multi_ho_window, "", Multi_ho_buttons[gr_screen.res][idx].x, Multi_ho_buttons[gr_screen.res][idx].y, 1, 1, 0, 1);
6380 // set the sound to play when highlighted
6381 Multi_ho_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
6383 // set the ani for the button
6384 Multi_ho_buttons[gr_screen.res][idx].button.set_bmaps(Multi_ho_buttons[gr_screen.res][idx].filename);
6386 // set the hotspot, ignoring the skill level button
6387 Multi_ho_buttons[gr_screen.res][idx].button.link_hotspot(Multi_ho_buttons[gr_screen.res][idx].hotspot);
6391 Multi_ho_window.add_XSTR(&Multi_ho_text[gr_screen.res][idx]);
6397 for(idx=0; idx<MULTI_HO_NUM_TITLES; idx++){
6398 Multi_ho_window.add_XSTR(&Multi_ho_titles[gr_screen.res][idx]);
6402 // create the interface sliders
6403 for(idx=0; idx<MULTI_HO_NUM_SLIDERS; idx++){
6404 // create the object
6405 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);
6408 // create the respawn count input box
6409 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);
6410 // if we're in campaign mode, disable it
6411 if(Netgame.campaign_mode == MP_CAMPAIGN){
6412 Multi_ho_respawns.set_text(XSTR("NA",795)); // [[ Not applicable ]]
6413 Multi_ho_respawns.disable();
6415 Multi_ho_respawns.set_valid_chars("0123456789");
6418 // create the time limit input box
6419 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);
6420 Multi_ho_time_limit.set_valid_chars("-0123456789");
6422 // create the voice token wait input box
6423 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);
6424 Multi_ho_voice_wait.set_valid_chars("01243456789");
6426 // create the furball kill limit input box
6427 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);
6428 Multi_ho_kill_limit.set_valid_chars("0123456789");
6430 // create the observer limit input box
6431 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);
6432 Multi_ho_obs.set_valid_chars("01234");
6434 // load in the current netgame defaults
6435 multi_ho_get_options();
6437 // whether or not any of the inputboxes on this screen had focus last frame
6438 Multi_ho_lastframe_input = 0;
6440 // get the # of respawns for the currently selected mission (if any)
6441 if(Multi_create_list_select != -1){
6442 int abs_index = multi_create_select_to_index(Multi_create_list_select);
6444 // if he has a valid mission selected
6446 Multi_ho_mission_respawn = (int)Multi_create_file_list[abs_index].respawn;
6448 Multi_ho_mission_respawn = -1;
6451 Multi_ho_mission_respawn = -1;
6455 void multi_ho_update_sliders()
6457 // game skill slider
6458 if (Game_skill_level != Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos) {
6459 if ( !(Netgame.type_flags & NG_TYPE_TEAM) ){
6460 Game_skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6461 gamesnd_play_iface(SND_USER_SELECT);
6463 Game_skill_level = NUM_SKILL_LEVELS / 2;
6467 // get the voice qos options
6468 if (Netgame.options.voice_qos != (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1)) {
6469 Netgame.options.voice_qos = (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1);
6470 gamesnd_play_iface(SND_USER_SELECT);
6473 // get the voice duration options
6474 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)) {
6475 Netgame.options.voice_record_time = (int)(0.5f * (float)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 1000.0f);
6476 gamesnd_play_iface(SND_USER_SELECT);
6481 void multi_host_options_do()
6486 k = Multi_ho_window.process();
6489 // process any keypresses
6492 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
6495 case KEY_CTRLED + SDLK_RETURN :
6496 gamesnd_play_iface(SND_COMMIT_PRESSED);
6497 multi_ho_accept_hit();
6501 // process any button clicks
6502 multi_ho_check_buttons();
6504 // update the sliders
6505 multi_ho_update_sliders();
6507 // make sure that the chatbox inputbox and any inputbox on this screen are mutually exclusive in terms of focus
6508 multi_ho_check_focus();
6510 // draw the background, etc
6512 GR_MAYBE_CLEAR_RES(Multi_ho_bitmap);
6513 if(Multi_ho_bitmap != -1){
6514 gr_set_bitmap(Multi_ho_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
6517 Multi_ho_window.draw();
6519 // draw all the radio buttons properly
6520 multi_ho_draw_radio_groups();
6522 // display any pending notification messages
6523 multi_common_notify_do();
6525 // display the voice record time settings
6526 multi_ho_display_record_time();
6528 // maybe display the max # of respawns next to the respawn input box
6529 multi_ho_blit_max_respawns();
6531 // blit the proper skill level
6532 multi_ho_display_skill_level();
6534 // blit the "host modifies button"
6535 if(Multi_ho_host_modifies){
6536 Multi_ho_buttons[gr_screen.res][MULTI_HO_HOST_MODIFIES].button.draw_forced(2);
6539 // process and show the chatbox thingie
6543 Multi_ho_window.draw_tooltip();
6545 // display the voice status indicator
6546 multi_common_voice_display_status();
6552 void multi_host_options_close()
6554 // unload any bitmaps
6555 if(!bm_unload(Multi_ho_bitmap)){
6556 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_ho_bitmap_fname[gr_screen.res]));
6559 // destroy the UI_WINDOW
6560 Multi_ho_window.destroy();
6563 void multi_ho_check_buttons()
6566 for(idx=0;idx<MULTI_HO_NUM_BUTTONS;idx++){
6567 // we only really need to check for one button pressed at a time, so we can break after
6569 if(Multi_ho_buttons[gr_screen.res][idx].button.pressed()){
6570 multi_ho_button_pressed(idx);
6576 void multi_ho_button_pressed(int n)
6578 int radio_index,idx;
6579 int x_pixel,y_pixel;
6581 // get the pixel position of the click
6582 Multi_ho_buttons[gr_screen.res][n].button.get_mouse_pos(&x_pixel,&y_pixel);
6585 // clicked on the accept button
6586 case MULTI_HO_ACCEPT:
6587 gamesnd_play_iface(SND_COMMIT_PRESSED);
6588 multi_ho_accept_hit();
6591 // clicked on the host/captains only modify button
6592 case MULTI_HO_HOST_MODIFIES:
6593 // toggle it on or off
6594 Multi_ho_host_modifies = !Multi_ho_host_modifies;
6595 gamesnd_play_iface(SND_USER_SELECT);
6599 // look through the radio buttons and see which one this corresponds to
6601 for(idx=0;idx<MULTI_HO_NUM_RADIO_BUTTONS;idx++){
6602 if(Multi_ho_radio_info[idx][2] == n){
6607 SDL_assert(radio_index != -1);
6609 // check to see if a radio button was pressed
6610 if(radio_index < MULTI_HO_NUM_RADIO_BUTTONS){
6611 // see if this value is already picked for this radio group
6612 if(Multi_ho_radio_groups[Multi_ho_radio_info[radio_index][0]] != Multi_ho_radio_info[radio_index][1]){
6613 gamesnd_play_iface(SND_USER_SELECT);
6614 Multi_ho_radio_groups[Multi_ho_radio_info[radio_index][0]] = Multi_ho_radio_info[radio_index][1];
6616 gamesnd_play_iface(SND_GENERAL_FAIL);
6621 void multi_ho_draw_radio_groups()
6625 // go through each item and draw it if it is the selected button in its respective group
6626 for(idx=0;idx<MULTI_HO_NUM_RADIO_BUTTONS;idx++){
6627 /// if this button is the currently selected one in its group
6628 if(Multi_ho_radio_info[idx][1] == Multi_ho_radio_groups[Multi_ho_radio_info[idx][0]]){
6629 Multi_ho_buttons[gr_screen.res][Multi_ho_radio_info[idx][2]].button.draw_forced(2);
6634 void multi_ho_accept_hit()
6638 // check the values in the input boxes
6639 if(!multi_ho_check_values()){
6643 // zero out the netgame flags
6646 // set default options
6647 Netgame.options.flags = (MSO_FLAG_INGAME_XFER | MSO_FLAG_ACCEPT_PIX);
6649 // set the squadmate messaging flags
6650 switch(Multi_ho_radio_groups[MULTI_HO_MSG_GROUP]){
6652 Netgame.options.squad_set = MSO_SQUAD_RANK;
6655 Netgame.options.squad_set = MSO_SQUAD_LEADER;
6658 Netgame.options.squad_set = MSO_SQUAD_ANY;
6661 Netgame.options.squad_set = MSO_SQUAD_HOST;
6667 // set the end mission flags
6668 switch(Multi_ho_radio_groups[MULTI_HO_END_GROUP]){
6670 Netgame.options.endgame_set = MSO_END_RANK;
6673 Netgame.options.endgame_set = MSO_END_LEADER;
6676 Netgame.options.endgame_set = MSO_END_ANY;
6679 Netgame.options.endgame_set = MSO_END_HOST;
6685 // set the voice toggle
6686 switch(Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP]){
6688 Netgame.options.flags &= ~(MSO_FLAG_NO_VOICE);
6691 Netgame.options.flags |= MSO_FLAG_NO_VOICE;
6697 // get the voice qos options
6698 Netgame.options.voice_qos = (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1);
6700 // get the voice duration options
6701 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);
6703 // set the skill level. If in team vs. team mode, preserve the old setting before saving
6704 // the pilot file. I'll bet that this doesn't work though because the pilot file gets
6705 // written in a bunch of locations....sigh.
6706 if ( !(Netgame.type_flags & NG_TYPE_TEAM) ){
6707 Game_skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6709 Game_skill_level = NUM_SKILL_LEVELS / 2;
6712 // set the netgame respawn count
6713 // maybe warn the user that respawns will not be used for a campaign mission
6714 if(Netgame.campaign_mode == MP_SINGLE){
6715 Multi_ho_respawns.get_text(resp_str);
6716 uint temp_respawn = (uint)atoi(resp_str);
6717 // if he currently has no mission selected, let the user set any # of respawns
6718 if((int)temp_respawn > Multi_ho_mission_respawn){
6719 if(Multi_ho_mission_respawn == -1){
6720 Netgame.respawn = temp_respawn;
6721 Netgame.options.respawn = temp_respawn;
6723 // this should have been taken care of by the interface code
6728 Netgame.options.respawn = temp_respawn;
6729 Netgame.respawn = temp_respawn;
6733 // get the mission time limit
6734 Multi_ho_time_limit.get_text(resp_str);
6735 int temp_time = atoi(resp_str);
6737 Netgame.options.mission_time_limit = fl2f(-1.0f);
6738 } else if(temp_time > MULTI_HO_MAX_TIME_LIMIT){
6741 Netgame.options.mission_time_limit = fl2f(60.0f * (float)temp_time);
6744 // get observer count options
6745 Multi_ho_obs.get_text(resp_str);
6746 int temp_obs = atoi(resp_str);
6747 if(temp_obs > MULTI_HO_MAX_OBS){
6750 Netgame.options.max_observers = (ubyte)temp_obs;
6752 // get the furball kill limit
6753 Multi_ho_kill_limit.get_text(resp_str);
6754 int temp_kills = atoi(resp_str);
6755 if(temp_kills > MULTI_HO_MAX_KILL_LIMIT){
6758 Netgame.options.kill_limit = temp_kills;
6760 // get the token wait limit
6761 Multi_ho_voice_wait.get_text(resp_str);
6762 int temp_wait = atoi(resp_str);
6763 if(temp_wait > MULTI_HO_MAX_TOKEN_WAIT){
6766 Netgame.options.voice_token_wait = (temp_wait * 1000);
6768 // set the netgame option
6769 Netgame.options.skill_level = (ubyte)Game_skill_level;
6771 // get whether we're in host/captains only modify mode
6772 Netgame.options.flags &= ~(MSO_FLAG_SS_LEADERS);
6773 if(Multi_ho_host_modifies){
6774 Netgame.options.flags |= MSO_FLAG_SS_LEADERS;
6777 // store these values locally
6778 memcpy(&Player->m_local_options,&Net_player->p_info.options,sizeof(multi_local_options));
6779 memcpy(&Player->m_server_options,&Netgame.options,sizeof(multi_server_options));
6780 write_pilot_file(Player);
6782 // apply any changes in settings (notify everyone of voice qos changes, etc)
6783 multi_ho_apply_options();
6785 // move back to the create game screen
6786 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
6789 void multi_ho_get_options()
6793 // set the squadmate messaging buttons
6794 switch(Netgame.options.squad_set){
6795 case MSO_SQUAD_RANK :
6796 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 0;
6798 case MSO_SQUAD_LEADER:
6799 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 1;
6802 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 2;
6804 case MSO_SQUAD_HOST:
6805 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 3;
6811 // set the mission end buttons
6812 switch(Netgame.options.endgame_set){
6814 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 0;
6816 case MSO_END_LEADER:
6817 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 1;
6820 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 2;
6823 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 3;
6829 // set the voice toggle buttons
6830 if(Netgame.options.flags & MSO_FLAG_NO_VOICE){
6831 Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP] = 1;
6833 Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP] = 0;
6836 // get the voice qos options
6837 SDL_assert((Netgame.options.voice_qos >= 1) && (Netgame.options.voice_qos <= 10));
6838 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos = (Netgame.options.voice_qos - 1);
6840 // get the voice duration options
6841 SDL_assert((Netgame.options.voice_record_time > 0) && (Netgame.options.voice_record_time <= MULTI_VOICE_MAX_TIME));
6842 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos = ((int)((float)Netgame.options.voice_record_time / 500.0f)) - 1;
6844 // get the current skill level
6845 SDL_assert((Game_skill_level >= 0) && (Game_skill_level < NUM_SKILL_LEVELS));
6846 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos = Game_skill_level;
6848 // get the # of observers
6849 memset(resp_str,0,10);
6850 SDL_snprintf(resp_str,SDL_arraysize(resp_str),"%d",Netgame.options.max_observers);
6851 Multi_ho_obs.set_text(resp_str);
6853 // set the respawn count
6854 if(Netgame.campaign_mode == MP_SINGLE){
6855 memset(resp_str,0,10);
6856 SDL_snprintf(resp_str,SDL_arraysize(resp_str),"%d",Netgame.respawn);
6857 Multi_ho_respawns.set_text(resp_str);
6860 // set the mission time limit
6861 memset(resp_str,0,10);
6862 float tl = f2fl(Netgame.options.mission_time_limit);
6863 SDL_snprintf(resp_str,SDL_arraysize(resp_str),"%d",(int)(tl / 60.0f));
6864 Multi_ho_time_limit.set_text(resp_str);
6866 // set the furball kill limit
6867 memset(resp_str,0,10);
6868 SDL_snprintf(resp_str,SDL_arraysize(resp_str),"%d",Netgame.options.kill_limit);
6869 Multi_ho_kill_limit.set_text(resp_str);
6871 // set the token wait time
6872 memset(resp_str,0,10);
6873 SDL_snprintf(resp_str,SDL_arraysize(resp_str),"%d",Netgame.options.voice_token_wait / 1000);
6874 Multi_ho_voice_wait.set_text(resp_str);
6876 // get whether we're in host/captains only modify mode
6877 if(Netgame.options.flags & MSO_FLAG_SS_LEADERS){
6878 Multi_ho_host_modifies = 1;
6880 Multi_ho_host_modifies = 0;
6884 void multi_ho_apply_options()
6886 // if the voice qos or duration has changed, apply the change
6887 multi_voice_maybe_update_vars(Netgame.options.voice_qos,Netgame.options.voice_record_time);
6889 // send an options update
6890 multi_options_update_netgame();
6893 // display the voice record time settings
6894 void multi_ho_display_record_time()
6897 int full_seconds, half_seconds;
6900 memset(time_str,0,30);
6903 full_seconds = (((Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 500) / 1000);
6905 // get the half-seconds
6906 half_seconds = ((((Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 500) % 1000) / 500) * 5;
6908 // format the string
6909 SDL_snprintf(time_str,SDL_arraysize(time_str),"%d.%d",full_seconds,half_seconds);
6910 gr_set_color_fast(&Color_bright);
6911 gr_string(Ho_vd_coords[gr_screen.res][MULTI_HO_X_COORD],Ho_vd_coords[gr_screen.res][MULTI_HO_Y_COORD],time_str);
6914 int multi_ho_check_values()
6918 memset(val_txt,0,255);
6920 // check against respawn settings
6921 if(Multi_ho_mission_respawn != -1){
6922 Multi_ho_respawns.get_text(val_txt);
6923 // if the value is invalid, let the user know
6924 if(atoi(val_txt) > Multi_ho_mission_respawn){
6925 memset(val_txt,0,255);
6926 SDL_snprintf(val_txt,SDL_arraysize(val_txt),XSTR("Warning\nRespawn count in greater than mission specified max (%d)",796),Multi_ho_mission_respawn);
6927 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6932 // check against mission time limit max
6933 Multi_ho_time_limit.get_text(val_txt);
6934 // if the value is invalid, force it to be valid
6935 if(atoi(val_txt) > MULTI_HO_MAX_TIME_LIMIT){
6936 memset(val_txt,0,255);
6937 SDL_snprintf(val_txt,SDL_arraysize(val_txt),XSTR("Warning\nMission time limit is greater than max allowed (%d)",797),MULTI_HO_MAX_TIME_LIMIT);
6938 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6942 // check against max observer limit
6943 Multi_ho_obs.get_text(val_txt);
6944 // if the value is invalid, force it to be valid
6945 if(atoi(val_txt) > MULTI_HO_MAX_OBS){
6946 memset(val_txt,0,255);
6947 SDL_snprintf(val_txt,SDL_arraysize(val_txt),XSTR("Warning\nObserver count is greater than max allowed (%d)",798),MULTI_HO_MAX_OBS);
6948 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6952 // check against furball kill limit
6953 Multi_ho_kill_limit.get_text(val_txt);
6954 // if the value is invalid, force it to be valid
6955 if(atoi(val_txt) > MULTI_HO_MAX_KILL_LIMIT){
6956 memset(val_txt,0,255);
6957 SDL_snprintf(val_txt,SDL_arraysize(val_txt),XSTR("Warning\nMission kill limit is greater than max allowed (%d)",799),MULTI_HO_MAX_KILL_LIMIT);
6958 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6962 // check against the token wait limit
6963 Multi_ho_voice_wait.get_text(val_txt);
6964 if(atoi(val_txt) > MULTI_HO_MAX_TOKEN_WAIT){
6965 memset(val_txt,0,255);
6966 SDL_snprintf(val_txt,SDL_arraysize(val_txt),XSTR("Warning\nvoice wait time is greater than max allowed (%d)",800),MULTI_HO_MAX_TOKEN_WAIT);
6967 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6971 // all values are valid
6975 void multi_ho_check_focus()
6977 // if an inputbox has been pressed (hit enter), lose its focus
6978 if (Multi_ho_respawns.pressed() || Multi_ho_time_limit.pressed() || Multi_ho_voice_wait.pressed() || Multi_ho_kill_limit.pressed() || Multi_ho_obs.pressed()) {
6979 Multi_ho_respawns.clear_focus();
6980 Multi_ho_time_limit.clear_focus();
6981 Multi_ho_voice_wait.clear_focus();
6982 Multi_ho_kill_limit.clear_focus();
6983 Multi_ho_obs.clear_focus();
6984 gamesnd_play_iface(SND_COMMIT_PRESSED);
6985 chatbox_set_focus();
6986 Multi_ho_lastframe_input = 0;
6988 } else if(!Multi_ho_lastframe_input) {
6989 // if we didn't have focus last frame
6990 if(Multi_ho_respawns.has_focus() || Multi_ho_time_limit.has_focus() || Multi_ho_kill_limit.has_focus() || Multi_ho_voice_wait.has_focus() ){
6991 chatbox_lose_focus();
6993 Multi_ho_lastframe_input = 1;
6996 // if we _did_ have focus last frame
6998 // if we no longer have focus on any of the input boxes, set the focus on the chatbox
6999 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()){
7000 chatbox_set_focus();
7002 // if the chatbox now has focus, clear all focus from our inputboxes
7003 else if (chatbox_has_focus()) {
7004 Multi_ho_respawns.clear_focus();
7005 Multi_ho_time_limit.clear_focus();
7006 Multi_ho_kill_limit.clear_focus();
7007 Multi_ho_voice_wait.clear_focus();
7009 Multi_ho_lastframe_input = 0;
7014 void multi_ho_blit_max_respawns()
7018 // if we're in campaign mode, do nothing
7019 if(Netgame.campaign_mode == MP_CAMPAIGN){
7023 // otherwise blit the max as specified by the current mission file
7024 SDL_snprintf(string,SDL_arraysize(string),"(%d)",Multi_ho_mission_respawn);
7025 gr_set_color_fast(&Color_normal);
7026 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);
7029 void multi_ho_display_skill_level()
7031 int skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
7034 SDL_assert((skill_level >= 0) && (skill_level < NUM_SKILL_LEVELS));
7035 if((skill_level < 0) || (skill_level >= NUM_SKILL_LEVELS)){
7039 gr_set_color_fast(&Color_bright);
7040 gr_string(Ho_st_coords[gr_screen.res][0], Ho_st_coords[gr_screen.res][1], Skill_level_names(skill_level, 1));
7043 // -------------------------------------------------------------------------------------------------------------
7045 // MULTIPLAYER JOIN SCREEN
7048 #define MULTI_JW_NUM_BUTTONS 8
7052 #define MULTI_JW_PALETTE "InterfacePalette"
7054 static const char *Multi_jw_bitmap_fname[GR_NUM_RESOLUTIONS] = {
7055 "MultiJoinWait", // GR_640
7056 "2_MultiJoinWait" // GR_1024
7059 static const char *Multi_jw_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
7060 "MultiJoinWait-M", // GR_640
7061 "2_MultiJoinWait-M" // GR_1024
7067 #define MJW_SCROLL_PLAYERS_UP 0
7068 #define MJW_SCROLL_PLAYERS_DOWN 1
7071 #define MJW_PILOT_INFO 4
7072 #define MJW_SCROLL_INFO_UP 5
7073 #define MJW_SCROLL_INFO_DOWN 6
7074 #define MJW_CANCEL 7
7076 UI_WINDOW Multi_jw_window; // the window object for the join screen
7077 int Multi_jw_bitmap; // the background bitmap
7079 // constants for coordinate lookup
7080 #define MJW_X_COORD 0
7081 #define MJW_Y_COORD 1
7082 #define MJW_W_COORD 2
7083 #define MJW_H_COORD 3
7085 ui_button_info Multi_jw_buttons[GR_NUM_RESOLUTIONS][MULTI_JW_NUM_BUTTONS] = {
7088 ui_button_info("MJW_00", 0, 50, -1, -1, 0),
7089 ui_button_info("MJW_01", 0, 87, -1, -1, 1),
7090 ui_button_info("MJW_02", 20, 219, -1, -1, 2),
7091 ui_button_info("MJW_03", 73, 219, -1, -1, 3),
7092 ui_button_info("MJW_09", 131, 213, -1, -1, 9),
7093 ui_button_info("MJW_05", 0, 398, -1, -1, 5),
7094 ui_button_info("MJW_06", 0, 435, -1, -1, 6),
7095 ui_button_info("MJW_04", 559, 411, -1, -1, 4),
7097 ui_button_info("MJW_00", 1, 24, -1, -1, 0),
7098 ui_button_info("MJW_01", 1, 66, -1, -1, 1),
7099 ui_button_info("MJW_02", 30, 244, 20, 272, 2),
7100 ui_button_info("MJW_03", 84, 244, 73, 272, 3),
7101 ui_button_info("MJW_04", 139, 242, 134, 272, 4),
7102 ui_button_info("MJW_05", 1, 406, -1, -1, 5),
7103 ui_button_info("MJW_06", 1, 447, -1, -1, 6),
7104 ui_button_info("MJW_07", 577, 428, 570, 414, 7),
7108 ui_button_info("2_MJW_00", 2, 38, -1, -1, 0),
7109 ui_button_info("2_MJW_01", 2, 106, -1, -1, 1),
7110 ui_button_info("2_MJW_02", 48, 390, 47, 435, 2),
7111 ui_button_info("2_MJW_03", 134, 390, 133, 435, 3),
7112 ui_button_info("2_MJW_04", 223, 388, 225, 435, 4),
7113 ui_button_info("2_MJW_05", 2, 649, -1, -1, 5),
7114 ui_button_info("2_MJW_06", 2, 715, -1, -1, 6),
7115 ui_button_info("2_MJW_07", 923, 685, 931, 667, 7),
7120 #define MULTI_JW_NUM_TEXT 7
7122 UI_XSTR Multi_jw_text[GR_NUM_RESOLUTIONS][MULTI_JW_NUM_TEXT] = {
7124 { "Team 1", 1308, 20, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_TEAM0].button },
7125 { "Team 2", 1309, 73, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_TEAM1].button },
7126 { "Pilot", 1310, 134, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_PILOT_INFO].button },
7127 { "Info", 1311, 134, 283, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_PILOT_INFO].button },
7128 { "Cancel", 387, 570, 414, UI_XSTR_COLOR_PINK, -1, &Multi_jw_buttons[0][MJW_CANCEL].button },
7129 { "Players", 1269, 38, 8, UI_XSTR_COLOR_GREEN, -1, NULL },
7130 { "Choose Team", 1312, 27, 231, UI_XSTR_COLOR_GREEN, -1, NULL },
7133 { "Team 1", 1308, 47, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_TEAM0].button },
7134 { "Team 2", 1309, 133, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_TEAM1].button },
7135 { "Pilot", 1310, 225, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_PILOT_INFO].button },
7136 { "Info", 1311, 225, 446, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_PILOT_INFO].button },
7137 { "Cancel", 387, 931, 667, UI_XSTR_COLOR_PINK, -1, &Multi_jw_buttons[1][MJW_CANCEL].button },
7138 { "Players", 1269, 165, 12, UI_XSTR_COLOR_GREEN, -1, NULL },
7139 { "Choose Team", 1312, 45, 373, UI_XSTR_COLOR_GREEN, -1, NULL },
7144 int Mjw_players_coords[GR_NUM_RESOLUTIONS][4] = {
7157 int Mjw_mission_name_coords[GR_NUM_RESOLUTIONS][2] = {
7167 // squad war checkbox
7168 UI_CHECKBOX Multi_jw_sw_checkbox;
7169 const char *Multi_jw_sw_checkbox_fname[GR_NUM_RESOLUTIONS] = {
7173 int Multi_jw_sw_checkbox_coords[GR_NUM_RESOLUTIONS][2] = {
7181 int Multi_jw_sw_checkbox_text[GR_NUM_RESOLUTIONS][2] = {
7192 // player list control thingie defs
7193 #define MULTI_JW_PLIST_MAX_DISPLAY 19
7194 int Multi_jw_plist_select_flag; // indicates whether we currently have a selected player
7195 short Multi_jw_plist_select_id; // id of the current selected player
7196 UI_BUTTON Multi_jw_plist_select_button; // for selecting a player
7198 int Multi_jw_should_show_popup = 0;
7200 // LOCAL function definitions
7201 void multi_jw_check_buttons();
7202 void multi_jw_button_pressed(int n);
7203 void multi_jw_do_netstuff();
7204 void multi_jw_scroll_players_up();
7205 void multi_jw_scroll_players_down();
7206 void multi_jw_plist_process();
7207 void multi_jw_plist_blit_normal();
7208 void multi_jw_plist_blit_team();
7209 short multi_jw_get_mouse_id();
7211 void multi_game_client_setup_init()
7215 // create the interface window
7216 Multi_jw_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
7217 Multi_jw_window.set_mask_bmap(Multi_jw_bitmap_mask_fname[gr_screen.res]);
7219 // load the background bitmap
7220 Multi_jw_bitmap = bm_load(Multi_jw_bitmap_fname[gr_screen.res]);
7221 if(Multi_jw_bitmap < 0){
7222 // we failed to load the bitmap - this is very bad
7226 // initialize the player list data
7227 Multi_jw_plist_select_flag = 0;
7228 Multi_jw_plist_select_id = -1;
7230 // kill any old instances of the chatbox and create a new one
7232 chatbox_create(CHATBOX_FLAG_BIG | CHATBOX_FLAG_DRAW_BOX | CHATBOX_FLAG_BUTTONS);
7234 // initialize the common notification messaging
7235 multi_common_notify_init();
7237 // initialize the common mission info display area.
7238 multi_common_set_text("");
7240 // use the common interface palette
7241 multi_common_set_palette();
7243 // create the interface buttons
7244 for(idx=0; idx<MULTI_JW_NUM_BUTTONS; idx++){
7245 // create the object
7246 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);
7248 // set the sound to play when highlighted
7249 Multi_jw_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
7251 // set the ani for the button
7252 Multi_jw_buttons[gr_screen.res][idx].button.set_bmaps(Multi_jw_buttons[gr_screen.res][idx].filename);
7255 Multi_jw_buttons[gr_screen.res][idx].button.link_hotspot(Multi_jw_buttons[gr_screen.res][idx].hotspot);
7259 // if this is a PXO game, enable the squadwar checkbox
7260 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);
7261 Multi_jw_sw_checkbox.set_bmaps(Multi_jw_sw_checkbox_fname[gr_screen.res], 6, 0);
7262 Multi_jw_sw_checkbox.disable();
7263 if(!MULTI_IS_TRACKER_GAME){
7264 Multi_jw_sw_checkbox.hide();
7270 for(idx=0; idx<MULTI_JW_NUM_TEXT; idx++){
7271 Multi_jw_window.add_XSTR(&Multi_jw_text[gr_screen.res][idx]);
7275 // create the player select list button and hide it
7276 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);
7277 Multi_jw_plist_select_button.hide();
7280 Multi_jw_buttons[gr_screen.res][MJW_CANCEL].button.set_hotkey(SDLK_ESCAPE);
7282 // remove campaign flags
7283 Game_mode &= ~(GM_CAMPAIGN_MODE);
7285 // tell the server we have finished joining
7286 Net_player->state = NETPLAYER_STATE_JOINED;
7287 send_netplayer_update_packet();
7290 ml_printf(NOX("Joined netgame %s"), Netgame.name);
7292 // send any appropriate files
7293 multi_data_send_my_junk();
7296 void multi_game_client_setup_do_frame()
7299 int k = chatbox_process();
7300 char mission_text[255];
7301 /*k =*/ Multi_jw_window.process(k,0);
7303 Multi_jw_should_show_popup = 0;
7305 // process any button clicks
7306 multi_jw_check_buttons();
7308 // do any network related stuff
7309 multi_jw_do_netstuff();
7311 // draw the background, etc
7313 GR_MAYBE_CLEAR_RES(Multi_jw_bitmap);
7314 if(Multi_jw_bitmap != -1){
7315 gr_set_bitmap(Multi_jw_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7319 // if we're not in team vs. team mode, don't draw the team buttons
7320 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
7321 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.hide();
7322 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.hide();
7323 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.disable();
7324 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.disable();
7326 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.enable();
7327 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.enable();
7328 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.unhide();
7329 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.unhide();
7333 if(MULTI_IS_TRACKER_GAME){
7334 // maybe check the squadwar button
7335 if(Netgame.type_flags & NG_TYPE_SW){
7336 Multi_jw_sw_checkbox.set_state(1);
7337 gr_set_color_fast(&Color_bright);
7339 Multi_jw_sw_checkbox.set_state(0);
7340 gr_set_color_fast(&Color_normal);
7343 gr_string(Multi_jw_sw_checkbox_text[gr_screen.res][0], Multi_jw_sw_checkbox_text[gr_screen.res][1], "SquadWar");
7347 // draw the UI window
7348 Multi_jw_window.draw();
7350 // process and display the player list
7351 // NOTE : this must be done before the buttons are checked to insure that a player hasn't dropped
7352 multi_jw_plist_process();
7353 if(Netgame.type_flags & NG_TYPE_TEAM){
7354 multi_jw_plist_blit_team();
7356 multi_jw_plist_blit_normal();
7359 // display any text in the info area
7360 multi_common_render_text();
7362 // display any pending notification messages
7363 multi_common_notify_do();
7365 // blit the mission filename if possible
7366 if(Netgame.campaign_mode){
7367 if(strlen(Netgame.campaign_name) > 0){
7368 SDL_strlcpy(mission_text, Netgame.campaign_name, SDL_arraysize(mission_text));
7370 if(strlen(Netgame.title) > 0){
7371 SDL_strlcat(mission_text, ", ", SDL_arraysize(mission_text));
7372 SDL_strlcat(mission_text, Netgame.title, SDL_arraysize(mission_text));
7375 gr_set_color_fast(&Color_bright_white);
7376 gr_string(Mjw_mission_name_coords[gr_screen.res][MJW_X_COORD],Mjw_mission_name_coords[gr_screen.res][MJW_Y_COORD],mission_text);
7379 if(strlen(Netgame.mission_name) > 0){
7380 SDL_strlcpy(mission_text, Netgame.mission_name, SDL_arraysize(mission_text));
7382 if(strlen(Netgame.title) > 0){
7383 SDL_strlcat(mission_text, ", ", SDL_arraysize(mission_text));
7384 SDL_strlcat(mission_text, Netgame.title, SDL_arraysize(mission_text));
7387 gr_set_color_fast(&Color_bright_white);
7388 gr_string(Mjw_mission_name_coords[gr_screen.res][MJW_X_COORD],Mjw_mission_name_coords[gr_screen.res][MJW_Y_COORD],mission_text);
7392 // process and show the chatbox thingie
7396 Multi_jw_window.draw_tooltip();
7398 // display the voice status indicator
7399 multi_common_voice_display_status();
7404 // if we're supposed to be displaying a pilot info popup
7405 if(Multi_jw_should_show_popup){
7406 player_index = find_player_id(Multi_jw_plist_select_id);
7407 if(player_index != -1){
7408 multi_pinfo_popup(&Net_players[player_index]);
7413 void multi_game_client_setup_close()
7415 // unload any bitmaps
7416 if(!bm_unload(Multi_jw_bitmap)){
7417 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_jw_bitmap_fname[gr_screen.res]));
7420 // destroy the chatbox
7423 // destroy the UI_WINDOW
7424 Multi_jw_window.destroy();
7427 if(Netgame.game_state == NETGAME_STATE_MISSION_SYNC){
7428 gamesnd_play_iface(SND_COMMIT_PRESSED);
7433 void multi_jw_check_buttons()
7436 for(idx=0;idx<MULTI_JW_NUM_BUTTONS;idx++){
7437 // we only really need to check for one button pressed at a time, so we can break after
7439 if(Multi_jw_buttons[gr_screen.res][idx].button.pressed()){
7440 multi_jw_button_pressed(idx);
7446 void multi_jw_button_pressed(int n)
7450 gamesnd_play_iface(SND_USER_SELECT);
7451 multi_quit_game(PROMPT_CLIENT);
7453 case MJW_SCROLL_PLAYERS_UP:
7454 multi_jw_scroll_players_up();
7456 case MJW_SCROLL_PLAYERS_DOWN:
7457 multi_jw_scroll_players_down();
7459 case MJW_SCROLL_INFO_UP:
7460 multi_common_scroll_text_up();
7462 case MJW_SCROLL_INFO_DOWN:
7463 multi_common_scroll_text_down();
7466 // request to set myself to team 0
7468 gamesnd_play_iface(SND_USER_SELECT);
7469 multi_team_set_team(Net_player,0);
7472 // request to set myself to team 1
7474 gamesnd_play_iface(SND_USER_SELECT);
7475 multi_team_set_team(Net_player,1);
7479 case MJW_PILOT_INFO:
7480 Multi_jw_should_show_popup = 1;
7484 multi_common_add_notify(XSTR("Not implemented yet!",760));
7489 // do stuff like pinging servers, sending out requests, etc
7490 void multi_jw_do_netstuff()
7494 void multi_jw_scroll_players_up()
7496 gamesnd_play_iface(SND_GENERAL_FAIL);
7499 // scroll down through the player list
7500 void multi_jw_scroll_players_down()
7502 gamesnd_play_iface(SND_GENERAL_FAIL);
7505 void multi_jw_plist_process()
7507 int test_count,player_index,idx;
7509 // first determine if there are 0 players in the game. This should never happen since the host is _always_ in the game
7511 for(idx=0;idx<MAX_PLAYERS;idx++){
7512 // count anyone except the standalone server (if applicable)
7513 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7517 if(test_count <= 0){
7521 // if we had a selected item but that player has left, select myself instead
7522 if(Multi_jw_plist_select_flag){
7523 player_index = find_player_id(Multi_jw_plist_select_id);
7524 if(player_index == -1){
7525 Multi_jw_plist_select_id = Net_player->player_id;
7528 Multi_jw_plist_select_flag = 1;
7529 Multi_jw_plist_select_id = Net_player->player_id;
7532 // if the player has clicked somewhere in the player list area
7533 if(Multi_jw_plist_select_button.pressed()){
7536 player_id = multi_jw_get_mouse_id();
7537 player_index = find_player_id(player_id);
7538 if(player_index != -1){
7539 Multi_jw_plist_select_id = player_id;
7540 Multi_jw_plist_select_flag = 1;
7545 void multi_jw_plist_blit_normal()
7548 char str[CALLSIGN_LEN+1];
7549 int y_start = Mjw_players_coords[gr_screen.res][MJW_Y_COORD];
7552 // display all the players
7553 for(idx=0;idx<MAX_PLAYERS;idx++){
7554 // reset total offset
7557 // count anyone except the standalone server (if applicable)
7558 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7559 // highlight him if he's the host
7560 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7561 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7562 gr_set_color_fast(&Color_text_active_hi);
7564 gr_set_color_fast(&Color_bright);
7567 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7568 gr_set_color_fast(&Color_text_active);
7570 gr_set_color_fast(&Color_text_normal);
7574 // optionally draw his CD status
7575 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7576 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7577 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7579 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7582 // make sure the string will fit, then display it
7583 SDL_strlcpy(str, Net_players[idx].player->callsign, SDL_arraysize(str));
7584 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
7585 SDL_strlcat(str, "(0)", SDL_arraysize(str));
7587 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7588 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7595 void multi_jw_plist_blit_team()
7598 char str[CALLSIGN_LEN+1];
7599 int y_start = Mjw_players_coords[gr_screen.res][MJW_Y_COORD];
7602 // always blit the proper team button based on _my_ team status
7603 if(Net_player->p_info.team == 0){
7604 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.draw_forced(2);
7606 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.draw_forced(2);
7609 // display all the red players first
7610 for(idx=0;idx<MAX_PLAYERS;idx++){
7611 // reset total offset
7614 // count anyone except the standalone server (if applicable)
7615 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
7616 // highlight him if he's the host
7617 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7618 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7619 gr_set_color_fast(&Color_text_active_hi);
7621 gr_set_color_fast(&Color_bright);
7624 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7625 gr_set_color_fast(&Color_text_active);
7627 gr_set_color_fast(&Color_text_normal);
7631 // optionally draw his CD status
7632 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7633 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7634 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7636 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7639 // blit the red team indicator
7640 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
7641 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
7642 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7643 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7645 total_offset += Multi_common_icon_dims[MICON_TEAM0_SELECT][0] + 1;
7648 if(Multi_common_icons[MICON_TEAM0] != -1){
7649 gr_set_bitmap(Multi_common_icons[MICON_TEAM0], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7650 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7652 total_offset += Multi_common_icon_dims[MICON_TEAM0][0] + 1;
7656 // make sure the string will fit
7657 SDL_strlcpy(str, Net_players[idx].player->callsign, SDL_arraysize(str));
7658 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7660 // display him in the correct half of the list depending on his team
7661 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7666 // display all the green players next
7667 for(idx=0;idx<MAX_PLAYERS;idx++){
7668 // reset total offset
7671 // count anyone except the standalone server (if applicable)
7672 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
7673 // highlight him if he's the host
7674 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7675 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7676 gr_set_color_fast(&Color_text_active_hi);
7678 gr_set_color_fast(&Color_bright);
7681 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7682 gr_set_color_fast(&Color_text_active);
7684 gr_set_color_fast(&Color_text_normal);
7688 // optionally draw his CD status
7689 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7690 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7691 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7693 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7696 // blit the red team indicator
7697 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
7698 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
7699 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7700 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7702 total_offset += Multi_common_icon_dims[MICON_TEAM1_SELECT][0] + 1;
7705 if(Multi_common_icons[MICON_TEAM1] != -1){
7706 gr_set_bitmap(Multi_common_icons[MICON_TEAM1], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7707 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7709 total_offset += Multi_common_icon_dims[MICON_TEAM1][0] + 1;
7713 // make sure the string will fit
7714 SDL_strlcpy(str, Net_players[idx].player->callsign, SDL_arraysize(str));
7715 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
7716 SDL_strlcat(str, "(0)", SDL_arraysize(str));
7718 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7720 // display him in the correct half of the list depending on his team
7721 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7727 void multi_jw_handle_join(net_player *pl)
7729 // for now just play a bloop sound
7730 gamesnd_play_iface(SND_ICON_DROP_ON_WING);
7733 short multi_jw_get_mouse_id()
7735 // determine where he clicked (y pixel value)
7737 Multi_jw_plist_select_button.get_mouse_pos(NULL,&y);
7739 // select things a little differently if we're in team vs. team or non-team vs. team mode
7741 if(Netgame.type_flags & NG_TYPE_TEAM){
7742 int player_index = -1;
7744 // look through all of team red first
7745 for(idx=0;idx<MAX_PLAYERS;idx++){
7746 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
7749 // if this is the _nth_ guy
7757 // if we still haven't found him yet, look through the green team
7758 if(player_index == -1){
7759 for(idx=0;idx<MAX_PLAYERS;idx++){
7760 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
7762 // if this is the _nth_ guy
7770 if(player_index != -1){
7771 return Net_players[idx].player_id;
7774 // select the nth active player if possible, disregarding the standalone server
7775 for(idx=0;idx<MAX_PLAYERS;idx++){
7776 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7779 // if this is the _nth_ guy
7781 return Net_players[idx].player_id;
7792 // -------------------------------------------------------------------------------------------------------------
7794 // MULTIPLAYER WAIT/SYNCH SCREEN
7797 #define MULTI_SYNC_HOST_COUNT 4 // host uses 4 buttons (and sometimes 5)
7798 #define MULTI_SYNC_CLIENT_COUNT 3 // client only uses 3 buttons
7800 const char *Multi_sync_bitmap_fname[GR_NUM_RESOLUTIONS] = {
7801 "MultiSynch", // GR_640
7802 "2_MultiSynch" // GR_1024
7805 const char *Multi_sync_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
7806 "MultiSynch-M", // GR_640
7807 "2_MultiSynch-M" // GR_1024
7812 // constants for coordinate lookup
7813 #define MS_X_COORD 0
7814 #define MS_Y_COORD 1
7815 #define MS_W_COORD 2
7816 #define MS_H_COORD 3
7818 UI_WINDOW Multi_sync_window; // the window object for the join screen
7819 int Multi_sync_button_count; // the # of buttons to use for this instance of mission sync
7820 int Multi_sync_bitmap; // the background bitmap
7823 #define MULTI_SYNC_NUM_BUTTONS 5
7824 #define MS_SCROLL_INFO_UP 0
7825 #define MS_SCROLL_INFO_DOWN 1
7829 ui_button_info Multi_sync_buttons[GR_NUM_RESOLUTIONS][MULTI_SYNC_NUM_BUTTONS] = {
7832 ui_button_info("MS_00", 0, 396, -1, -1, 0),
7833 ui_button_info("MS_01", 0, 435, -1, -1, 1),
7834 ui_button_info("MS_02", 496, 411, -1, -1, 2),
7835 ui_button_info("MS_04", 436, 403, -1, -1, 4),
7836 ui_button_info("MS_03", 556, 398, -1, -1, 3),
7838 ui_button_info("MS_00", 1, 404, -1, -1, 0),
7839 ui_button_info("MS_01", 1, 446, -1, -1, 1),
7840 ui_button_info("MS_03", 518, 426, 519, 416, 3),
7841 ui_button_info("MS_02", 469, 426, 479, 416, 2),
7842 ui_button_info("MS_04", 571, 420, 577, 416, 4),
7846 ui_button_info("2_MS_00", 2, 647, -1, -1, 0),
7847 ui_button_info("2_MS_01", 2, 713, -1, -1, 1),
7848 ui_button_info("2_MS_03", 829, 682, 831, 667, 3),
7849 ui_button_info("2_MS_02", 751, 682, 766, 667, 2),
7850 ui_button_info("2_MS_04", 914, 672, 924, 667, 4),
7856 #define MULTI_SYNC_NUM_TEXT 5
7858 #define MST_LAUNCH 2
7859 UI_XSTR Multi_sync_text[GR_NUM_RESOLUTIONS][MULTI_SYNC_NUM_TEXT] = {
7861 { "Kick", 1266, 479, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_KICK].button },
7862 { "Cancel", 387, 519, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_CANCEL].button },
7863 { "Launch", 801, 577, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_LAUNCH].button },
7864 { "Players", 1269, 23, 133, UI_XSTR_COLOR_GREEN, -1, NULL },
7865 { "Status", 1304, 228, 133, UI_XSTR_COLOR_GREEN, -1, NULL }
7868 { "Kick", 1266, 766, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_KICK].button },
7869 { "Cancel", 387, 831, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_CANCEL].button },
7870 { "Launch", 801, 924, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_LAUNCH].button },
7871 { "Players", 1269, 38, 214, UI_XSTR_COLOR_GREEN, -1, NULL },
7872 { "Status", 1304, 366, 214, UI_XSTR_COLOR_GREEN, -1, NULL }
7878 int Ms_status_coords[GR_NUM_RESOLUTIONS][4] = {
7891 // player status coords
7892 int Ms_status2_coords[GR_NUM_RESOLUTIONS][4] = {
7905 int Ms_cd_icon_offset[GR_NUM_RESOLUTIONS] = {
7910 int Ms_team_icon_offset[GR_NUM_RESOLUTIONS] = {
7915 // player currently selected, index into Net_players[]
7916 int Multi_sync_player_select = -1;
7918 // player list control thingie defs
7919 #define MULTI_SYNC_PLIST_MAX_DISPLAY 15
7920 int Multi_sync_plist_start; // where to start displaying from
7921 int Multi_sync_plist_count; // how many we have
7923 // list select button
7924 UI_BUTTON Multi_sync_plist_button;
7926 int Multi_sync_mode = -1;
7928 #define MULTI_SYNC_COUNTDOWN_TIME 5 // in seconds
7929 float Multi_sync_countdown_timer;
7930 int Multi_sync_countdown = -1;
7932 int Multi_launch_button_created;
7935 // countdown animation timer
7936 const char* Multi_sync_countdown_fname[GR_NUM_RESOLUTIONS] = {
7938 "2_Count" // GR_1024
7941 int Multi_sync_countdown_coords[GR_NUM_RESOLUTIONS][2] = {
7952 anim *Multi_sync_countdown_anim = NULL;
7953 anim_instance *Multi_sync_countdown_instance = NULL;
7956 // PREBRIEFING STUFF
7957 // syncing flags used by the server
7958 int Mission_sync_flags = 0;
7959 #define MS_FLAG_SENT_FILESIG (1<<0) // sent filesig requests
7960 #define MS_FLAG_SENT_LOAD (1<<1) // sent load packets
7961 #define MS_FLAG_PUSHED_BRIEFING (1<<2) // pushed everyone else into the briefing
7962 #define MS_FLAG_POST_DATA (1<<3) // sent the post data block
7963 #define MS_FLAG_WSS_SLOTS (1<<4) // all players have received wss slot data
7964 #define MS_FLAG_PSETTINGS (1<<5) // send the player settings packet
7965 #define MS_FLAG_MT_STATS_START (1<<6) // server has started getting player stats from the tracker
7966 #define MS_FLAG_MT_STATS_DONE (1<<7) // server has finished getting player stats from the tracker (success or fail)
7967 #define MS_FLAG_TS_SLOTS (1<<8) // team/ship slots have been sent
7968 #define MS_FLAG_DATA_DONE (1<<9) // done transferring all necessary data
7969 #define MS_FLAG_CAMP_DONE (1<<10) // send campaign pool/goal/event stuff
7971 // POSTBRIEFING STUFF
7972 int Multi_state_timestamp;
7973 int Multi_sync_launch_pressed;
7975 // LOCAL function definitions
7976 void multi_sync_check_buttons();
7977 void multi_sync_button_pressed(int n);
7978 void multi_sync_scroll_info_up();
7979 void multi_sync_scroll_info_down();
7980 void multi_sync_display_name(const char *name, int index, int np_index); // display info on the left hand portion of the status window thingie
7981 void multi_sync_display_status(const char *status, int index); // display info on the right hand portion of the status window thingie
7982 void multi_sync_force_start_pre();
7983 void multi_sync_force_start_post();
7984 void multi_sync_launch();
7985 void multi_sync_create_launch_button();
7986 void multi_sync_blit_screen_all();
7987 void multi_sync_handle_plist();
7989 void multi_sync_common_init();
7990 void multi_sync_common_do();
7991 void multi_sync_common_close();
7993 void multi_sync_pre_init();
7994 void multi_sync_pre_do();
7995 void multi_sync_pre_close();
7997 void multi_sync_post_init();
7998 void multi_sync_post_do();
7999 void multi_sync_post_close();
8004 // perform the correct init functions
8005 void multi_sync_init()
8007 Multi_sync_countdown = -1;
8011 // reset all timestamp
8012 multi_reset_timestamps();
8014 extern time_t Player_multi_died_check;
8015 Player_multi_died_check = -1;
8017 if(!(Game_mode & GM_STANDALONE_SERVER)){
8018 multi_sync_common_init();
8021 switch(Multi_sync_mode){
8022 case MULTI_SYNC_PRE_BRIEFING:
8023 multi_sync_pre_init();
8025 case MULTI_SYNC_POST_BRIEFING:
8026 multi_sync_post_init();
8028 case MULTI_SYNC_INGAME:
8029 multi_ingame_sync_init();
8034 // perform the correct do frame functions
8035 void multi_sync_do()
8037 if(!(Game_mode & GM_STANDALONE_SERVER)){
8038 multi_sync_common_do();
8041 // if the netgame is ending, don't do any sync processing
8042 if(multi_endgame_ending()){
8046 // process appropriateliy
8047 switch(Multi_sync_mode){
8048 case MULTI_SYNC_PRE_BRIEFING:
8049 multi_sync_pre_do();
8051 case MULTI_SYNC_POST_BRIEFING:
8052 multi_sync_post_do();
8054 case MULTI_SYNC_INGAME:
8055 multi_ingame_sync_do();
8058 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8059 if(Multi_sync_bitmap != -1){
8060 gr_set_bitmap(Multi_sync_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8063 Multi_sync_window.draw();
8065 multi_sync_blit_screen_all();
8072 // perform the correct close functions
8073 void multi_sync_close()
8075 switch(Multi_sync_mode){
8076 case MULTI_SYNC_PRE_BRIEFING:
8077 multi_sync_pre_close();
8079 case MULTI_SYNC_POST_BRIEFING:
8080 multi_sync_post_close();
8082 case MULTI_SYNC_INGAME:
8083 multi_ingame_sync_close();
8087 if(!(Game_mode & GM_STANDALONE_SERVER)){
8088 multi_sync_common_close();
8092 const char *multi_sync_tooltip_handler(const char *str)
8094 if (!SDL_strcasecmp(str, NOX("@launch"))) {
8095 if (Multi_launch_button_created){
8096 return XSTR("Launch",801);
8103 void multi_sync_common_init()
8107 // create the interface window
8108 Multi_sync_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
8109 Multi_sync_window.set_mask_bmap(Multi_sync_bitmap_mask_fname[gr_screen.res]);
8110 Multi_sync_window.tooltip_handler = multi_sync_tooltip_handler;
8112 // load the background bitmap
8113 Multi_sync_bitmap = bm_load(Multi_sync_bitmap_fname[gr_screen.res]);
8114 if (Multi_sync_bitmap < 0) {
8115 // we failed to load the bitmap - this is very bad
8119 // initialize the player list data
8120 Multi_sync_plist_start = 0;
8121 Multi_sync_plist_count = 1; // we can pretty safely assume that there's one player in the game - me.
8123 Multi_launch_button_created = 0;
8125 // create the chatbox thingie (shouldn't be necesary to do this, but we'll put it in for good measure)
8128 // force the chatbox to be small
8129 chatbox_force_small();
8131 // initialize the common notification messaging
8132 multi_common_notify_init();
8134 // initialize the common mission info display area.
8135 multi_common_set_text("");
8137 // use the common interface palette
8138 multi_common_set_palette();
8140 // don't select any player yet.
8141 Multi_sync_player_select = -1;
8143 // determine how many of the 5 buttons to create
8144 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8145 Multi_sync_button_count = MULTI_SYNC_HOST_COUNT;
8147 Multi_sync_button_count = MULTI_SYNC_CLIENT_COUNT;
8149 // create the interface buttons
8150 for(idx=0; idx<Multi_sync_button_count; idx++){
8151 // create the object
8152 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);
8154 // set the sound to play when highlighted
8155 Multi_sync_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
8157 // set the ani for the button
8158 // this wierdness is necessary because cancel and kick buttons aren't drawn on the background bitmap,
8159 // so we have to load in frame 0, too (the file should exist)
8160 if ((idx == MS_CANCEL) || (idx == MS_KICK) || (idx == MS_LAUNCH)) {
8161 Multi_sync_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sync_buttons[gr_screen.res][idx].filename, 3, 0);
8163 Multi_sync_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sync_buttons[gr_screen.res][idx].filename);
8167 Multi_sync_buttons[gr_screen.res][idx].button.link_hotspot(Multi_sync_buttons[gr_screen.res][idx].hotspot);
8172 for(idx=0; idx<MULTI_SYNC_NUM_TEXT; idx++) {
8173 // don't create the "launch" button text just yet
8174 if(idx == MST_LAUNCH) {
8177 // multiplayer clients should ignore the kick button
8178 if(!MULTIPLAYER_MASTER && !MULTIPLAYER_HOST && (idx == MST_KICK)) {
8182 Multi_sync_window.add_XSTR(&Multi_sync_text[gr_screen.res][idx]);
8186 // create the player list select button and hide it
8187 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);
8188 Multi_sync_plist_button.hide();
8190 // set up hotkeys for certain common functions
8191 Multi_sync_buttons[gr_screen.res][MS_CANCEL].button.set_hotkey(SDLK_ESCAPE);
8194 void multi_sync_common_do()
8196 int k = chatbox_process();
8197 k = Multi_sync_window.process(k);
8199 // process the player list
8200 multi_sync_handle_plist();
8202 // process any button clicks
8203 multi_sync_check_buttons();
8205 // process any keypresses
8209 gamesnd_play_iface(SND_USER_SELECT);
8210 multi_quit_game(PROMPT_ALL);
8215 void multi_sync_common_close()
8217 // unload any bitmaps
8218 if(!bm_unload(Multi_sync_bitmap)){
8219 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_sync_bitmap_fname[gr_screen.res]));
8222 extern time_t Player_multi_died_check;
8223 Player_multi_died_check = -1;
8225 // destroy the UI_WINDOW
8226 Multi_sync_window.destroy();
8229 void multi_sync_blit_screen_all()
8236 // display any text in the info area
8237 multi_common_render_text();
8239 // display any pending notification messages
8240 multi_common_notify_do();
8242 // display any info about visible players
8244 for(idx=0;idx<MAX_PLAYERS;idx++){
8245 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
8246 // display his name and status
8247 multi_sync_display_name(Net_players[idx].player->callsign,count,idx);
8249 // get the player state
8250 state = Net_players[idx].state;
8252 // if we're ingame joining, show all other players except myself as "playing"
8253 if((&Net_players[idx] != Net_player) && ((Multi_sync_mode == MULTI_SYNC_INGAME) || (Net_player->flags & NETINFO_FLAG_INGAME_JOIN)) ){
8254 state = NETPLAYER_STATE_IN_MISSION;
8258 case NETPLAYER_STATE_MISSION_LOADING:
8259 multi_sync_display_status(XSTR("Mission Loading",802),count);
8261 case NETPLAYER_STATE_INGAME_SHIP_SELECT: // I don't think its possible to see this state, but...
8262 multi_sync_display_status(XSTR("Ingame Ship Select",803),count);
8264 case NETPLAYER_STATE_DEBRIEF:
8265 multi_sync_display_status(XSTR("Debriefing",804),count);
8267 case NETPLAYER_STATE_MISSION_SYNC:
8268 multi_sync_display_status(XSTR("Mission Sync",805),count);
8270 case NETPLAYER_STATE_JOINING:
8271 multi_sync_display_status(XSTR("Joining",806),count);
8273 case NETPLAYER_STATE_JOINED:
8274 multi_sync_display_status(XSTR("Joined",807),count);
8276 case NETPLAYER_STATE_SLOT_ACK :
8277 multi_sync_display_status(XSTR("Slot Ack",808),count);
8279 case NETPLAYER_STATE_BRIEFING:
8280 multi_sync_display_status(XSTR("Briefing",765),count);
8282 case NETPLAYER_STATE_SHIP_SELECT:
8283 multi_sync_display_status(XSTR("Ship Select",809),count);
8285 case NETPLAYER_STATE_WEAPON_SELECT:
8286 multi_sync_display_status(XSTR("Weapon Select",810),count);
8288 case NETPLAYER_STATE_WAITING:
8289 multi_sync_display_status(XSTR("Waiting",811),count);
8291 case NETPLAYER_STATE_IN_MISSION:
8292 multi_sync_display_status(XSTR("In Mission",812),count);
8294 case NETPLAYER_STATE_MISSION_LOADED:
8295 multi_sync_display_status(XSTR("Mission Loaded",813),count);
8297 case NETPLAYER_STATE_DATA_LOAD:
8298 multi_sync_display_status(XSTR("Data loading",814),count);
8300 case NETPLAYER_STATE_SETTINGS_ACK:
8301 multi_sync_display_status(XSTR("Ready To Enter Mission",815),count);
8303 case NETPLAYER_STATE_INGAME_SHIPS:
8304 multi_sync_display_status(XSTR("Ingame Ships Packet Ack",816),count);
8306 case NETPLAYER_STATE_INGAME_WINGS:
8307 multi_sync_display_status(XSTR("Ingame Wings Packet Ack",817),count);
8309 case NETPLAYER_STATE_INGAME_RPTS:
8310 multi_sync_display_status(XSTR("Ingame Respawn Points Ack",818),count);
8312 case NETPLAYER_STATE_SLOTS_ACK:
8313 multi_sync_display_status(XSTR("Ingame Weapon Slots Ack",819),count);
8315 case NETPLAYER_STATE_POST_DATA_ACK:
8316 multi_sync_display_status(XSTR("Post Briefing Data Block Ack",820),count);
8318 case NETPLAYER_STATE_FLAG_ACK :
8319 multi_sync_display_status(XSTR("Flags Ack",821),count);
8321 case NETPLAYER_STATE_MT_STATS :
8322 multi_sync_display_status(XSTR("Parallax Online Stats Updating",822),count);
8324 case NETPLAYER_STATE_WSS_ACK :
8325 multi_sync_display_status(XSTR("Weapon Slots Ack",823),count);
8327 case NETPLAYER_STATE_HOST_SETUP :
8328 multi_sync_display_status(XSTR("Host setup",824),count);
8330 case NETPLAYER_STATE_DEBRIEF_ACCEPT:
8331 multi_sync_display_status(XSTR("Debrief accept",825),count);
8333 case NETPLAYER_STATE_DEBRIEF_REPLAY:
8334 multi_sync_display_status(XSTR("Debrief replay",826),count);
8336 case NETPLAYER_STATE_CPOOL_ACK:
8337 multi_sync_display_status(XSTR("Campaign ship/weapon ack",827),count);
8339 case NETPLAYER_STATE_MISSION_XFER :
8341 // server should display the pct completion of all clients
8342 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8343 if(Net_players[idx].s_info.xfer_handle != -1){
8344 pct_complete = multi_xfer_pct_complete(Net_players[idx].s_info.xfer_handle);
8346 // if we've got a valid xfer handle
8347 if((pct_complete >= 0.0) && (pct_complete <= 1.0)){
8348 SDL_snprintf(txt,SDL_arraysize(txt),XSTR("Mission file xfer %d%%",828),(int)(pct_complete * 100.0f));
8352 SDL_strlcpy(txt, XSTR("Mission file xfer", 829), SDL_arraysize(txt));
8355 SDL_strlcpy(txt, XSTR("Mission file xfer", 829), SDL_arraysize(txt));
8358 // clients should display only for themselves (which is the only thing they know)
8360 // if we've got a valid file xfer handle
8361 if((&Net_players[idx] == Net_player) && (Net_player->s_info.xfer_handle != -1)){
8362 pct_complete = multi_xfer_pct_complete(Net_player->s_info.xfer_handle);
8364 // if we've got a valid xfer handle
8365 if((pct_complete >= 0.0) && (pct_complete <= 1.0)){
8366 SDL_snprintf(txt,SDL_arraysize(txt),XSTR("Mission file xfer %d%%",828),(int)(pct_complete * 100.0f));
8370 SDL_strlcpy(txt, XSTR("Mission file xfer", 829), SDL_arraysize(txt));
8375 SDL_strlcpy(txt, XSTR("Mission file xfer", 829), SDL_arraysize(txt));
8380 multi_sync_display_status(txt,count);
8383 nprintf(("Network","Unhandled player state : %d !\n",Net_players[idx].state));
8390 // display the mission start countdown timer (if any)
8391 anim_render_all(GS_STATE_MULTI_MISSION_SYNC,flFrametime);
8393 // process and show the chatbox thingie
8397 Multi_sync_window.draw_tooltip();
8399 // display the voice status indicator
8400 multi_common_voice_display_status();
8403 void multi_sync_check_buttons()
8406 for(idx=0;idx<Multi_sync_button_count;idx++){
8407 // we only really need to check for one button pressed at a time, so we can break after
8409 if(Multi_sync_buttons[gr_screen.res][idx].button.pressed()){
8410 multi_sync_button_pressed(idx);
8416 void multi_sync_button_pressed(int n)
8421 gamesnd_play_iface(SND_USER_SELECT);
8422 multi_quit_game(PROMPT_ALL);
8425 // scroll the info box up
8426 case MS_SCROLL_INFO_UP:
8427 multi_common_scroll_text_up();
8430 // scroll the info box down
8431 case MS_SCROLL_INFO_DOWN:
8432 multi_common_scroll_text_down();
8437 // if we have a currently selected player, kick him
8438 if(Multi_sync_player_select >= 0){
8439 multi_kick_player(Multi_sync_player_select);
8443 // start the final launch countdown (post-sync only)
8445 multi_sync_start_countdown();
8448 // doesn't do anything
8454 void multi_sync_pre_init()
8458 Netgame.game_state = NETGAME_STATE_MISSION_SYNC;
8460 // if we're in teamplay mode, always force skill level to be medium
8461 if((Netgame.type_flags & NG_TYPE_TEAM) && (Net_player->flags & NETINFO_FLAG_GAME_HOST)){
8462 Netgame.options.skill_level = NUM_SKILL_LEVELS / 2;
8463 Game_skill_level = NUM_SKILL_LEVELS / 2;
8464 multi_options_update_netgame();
8467 // notify everyone of when we get here
8468 if(!(Game_mode & GM_STANDALONE_SERVER)){
8469 Net_player->state = NETPLAYER_STATE_MISSION_SYNC;
8470 send_netplayer_update_packet();
8473 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8475 ml_string(NOX("Server performing pre-briefing data sync"));
8477 if(!(Game_mode & GM_STANDALONE_SERVER)){
8478 multi_common_set_text(XSTR("Server performing sync\n",830),1);
8481 // maybe initialize tvt and squad war stuff
8482 if(Netgame.type_flags & NG_TYPE_TEAM){
8483 multi_team_level_init();
8486 // force everyone into this state
8487 send_netgame_update_packet();
8489 if(!(Game_mode & GM_STANDALONE_SERVER)){
8490 multi_common_add_text(XSTR("Send update packet\n",831),1);
8493 // setup some of my own data
8494 Net_player->flags |= NETINFO_FLAG_MISSION_OK;
8496 // do any output stuff
8497 if(Game_mode & GM_STANDALONE_SERVER){
8498 std_debug_set_standalone_state_string("Mission Sync");
8501 // do this here to insure we have the most up to date file checksum info
8502 multi_get_mission_checksum(Game_current_mission_filename);
8503 // parse_get_file_signature(Game_current_mission_filename);
8505 if(!(Game_mode & GM_STANDALONE_SERVER)){
8506 multi_common_add_text(XSTR("Got file signatures\n",832),1);
8509 if(!(Game_mode & GM_STANDALONE_SERVER)){
8510 multi_common_add_text(XSTR("Sending update state packet\n",833),1);
8514 // if we're not in team vs. team mode - set all player teams to be 0, and unset all captaincy bits
8515 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
8516 for(idx=0;idx<MAX_PLAYERS;idx++){
8517 Net_players[idx].p_info.team = 0;
8518 Net_players[idx].flags &= ~(NETINFO_FLAG_TEAM_CAPTAIN);
8522 // we aren't necessarily xferring the mission file yet
8523 SDL_assert(Net_player->s_info.xfer_handle == -1);
8525 // always call this for good measure
8526 multi_campaign_flush_data();
8528 Mission_sync_flags = 0;
8529 Multi_mission_loaded = 0;
8532 void multi_sync_pre_do()
8536 // If I'm the server, wait for everyone to arrive in this state, then begin transferring data, etc.
8537 // all servers (standalone or no, go through this)
8538 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8539 // wait for everyone to arrive, then request filesig from all of them
8540 if(multi_netplayer_state_check(NETPLAYER_STATE_MISSION_SYNC) && !(Mission_sync_flags & MS_FLAG_SENT_FILESIG) && !multi_endgame_ending()){
8541 send_file_sig_request(Netgame.mission_name);
8542 Mission_sync_flags |= MS_FLAG_SENT_FILESIG;
8544 if(!(Game_mode & GM_STANDALONE_SERVER)){
8545 multi_common_add_text(XSTR("Sent filesig request\n",834),1);
8549 // if we're waiting for players to receive files, then check on their status
8550 if((Mission_sync_flags & MS_FLAG_SENT_FILESIG) && !multi_netplayer_flag_check(NETINFO_FLAG_MISSION_OK) && !multi_endgame_ending()){
8551 for(idx=0;idx<MAX_PLAYERS;idx++){
8552 // if this player is in the process of xferring a file
8553 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].s_info.xfer_handle != -1)){
8554 switch(multi_xfer_get_status(Net_players[idx].s_info.xfer_handle)){
8555 // if it has successfully completed, set his ok flag
8556 case MULTI_XFER_SUCCESS :
8558 Net_players[idx].flags |= NETINFO_FLAG_MISSION_OK;
8560 // release the xfer instance handle
8561 multi_xfer_release_handle(Net_players[idx].s_info.xfer_handle);
8562 Net_players[idx].s_info.xfer_handle = -1;
8564 // if it has failed or timed-out, kick the player
8565 case MULTI_XFER_TIMEDOUT:
8566 case MULTI_XFER_FAIL:
8567 // release the xfer handle
8568 multi_xfer_release_handle(Net_players[idx].s_info.xfer_handle);
8569 Net_players[idx].s_info.xfer_handle = -1;
8572 multi_kick_player(idx, 0, KICK_REASON_BAD_XFER);
8579 // NOTE : this is now obsolete
8580 // once everyone is verified, do any data transfer necessary
8581 if(multi_netplayer_flag_check(NETINFO_FLAG_MISSION_OK) && !(Mission_sync_flags & MS_FLAG_DATA_DONE) && !multi_endgame_ending()){
8582 // do nothing for now
8583 Mission_sync_flags |= MS_FLAG_DATA_DONE;
8585 // send campaign pool data
8586 multi_campaign_send_pool_status();
8589 // wait for everyone to ack on campaign pool data (even in non-campaign situations)
8590 if((Mission_sync_flags & MS_FLAG_DATA_DONE) && !(Mission_sync_flags & MS_FLAG_CAMP_DONE) && !multi_endgame_ending()){
8591 // check to see if everyone has acked the campaign pool data
8592 if(multi_netplayer_state_check(NETPLAYER_STATE_CPOOL_ACK)){
8593 Mission_sync_flags |= MS_FLAG_CAMP_DONE;
8597 // once everyone is verified, tell them to load the mission
8598 // also make sure to load the mission myself _AFTER_ telling everyone to do so. This makes the whole process
8599 // move along faster
8600 if((Mission_sync_flags & MS_FLAG_CAMP_DONE) && !(Mission_sync_flags & MS_FLAG_SENT_LOAD) && !multi_endgame_ending()){
8601 send_netplayer_load_packet(NULL);
8602 Mission_sync_flags |= MS_FLAG_SENT_LOAD;
8604 if(!(Game_mode & GM_STANDALONE_SERVER)){
8605 multi_common_add_text(XSTR("Sent load packet\n",835),1);
8608 // load the mission myself, as soon as possible
8609 if(!Multi_mission_loaded){
8610 nprintf(("Network","Server loading mission..."));
8612 // update everyone about my status
8613 Net_player->state = NETPLAYER_STATE_MISSION_LOADING;
8614 send_netplayer_update_packet();
8616 game_start_mission();
8618 nprintf(("Network","Done\n"));
8619 Multi_mission_loaded = 1;
8621 // update everyone about my status
8622 Net_player->state = NETPLAYER_STATE_MISSION_LOADED;
8623 send_netplayer_update_packet();
8625 if(!(Game_mode & GM_STANDALONE_SERVER)){
8626 multi_common_add_text(XSTR("Loaded mission locally\n",836),1);
8631 // if everyone has loaded the mission, randomly assign players to ships
8632 if(multi_netplayer_state_check(NETPLAYER_STATE_MISSION_LOADED) && !(Mission_sync_flags & MS_FLAG_TS_SLOTS) && !multi_endgame_ending()){
8633 // call the team select function to assign players to their ships, wings, etc
8634 multi_ts_assign_players_all();
8635 send_netplayer_slot_packet();
8638 Mission_sync_flags |= MS_FLAG_TS_SLOTS;
8641 // if everyone has loaded the mission, move to the team select stage
8642 if(Sync_test && multi_netplayer_state_check(NETPLAYER_STATE_SLOT_ACK) && !(Mission_sync_flags & MS_FLAG_PUSHED_BRIEFING) && !multi_endgame_ending()){
8643 Netgame.game_state = NETGAME_STATE_BRIEFING;
8644 send_netgame_update_packet(); // this will push everyone into the next state
8646 // the standalone moves to his own wait state, whereas in the normal game mode, the server/host moves in to the
8647 // team select state
8648 if(Game_mode & GM_STANDALONE_SERVER){
8649 gameseq_post_event(GS_EVENT_MULTI_STD_WAIT);
8651 gameseq_post_event(GS_EVENT_START_GAME);
8654 Mission_sync_flags |= MS_FLAG_PUSHED_BRIEFING;
8656 if(!(Game_mode & GM_STANDALONE_SERVER)){
8657 multi_common_add_text(XSTR("Moving to team select\n",837),1);
8661 // clients should detect here if they are doing a file xfer and do error processing
8662 if((Net_player->state == NETPLAYER_STATE_MISSION_XFER) && (Net_player->s_info.xfer_handle != -1) && !multi_endgame_ending()){
8663 switch(multi_xfer_get_status(Net_player->s_info.xfer_handle)){
8664 // if it has successfully completed, set his ok flag
8665 case MULTI_XFER_SUCCESS :
8666 // release my xfer handle
8667 multi_xfer_release_handle(Net_player->s_info.xfer_handle);
8668 Net_player->s_info.xfer_handle = -1;
8671 // if it has failed or timed-out, kick the player
8672 case MULTI_XFER_TIMEDOUT:
8673 case MULTI_XFER_FAIL:
8674 // release my xfer handle
8675 multi_xfer_release_handle(Net_player->s_info.xfer_handle);
8676 Net_player->s_info.xfer_handle = -1;
8678 // leave the game qith an error code
8679 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_XFER_FAIL);
8686 if(!(Game_mode & GM_STANDALONE_SERVER)){
8688 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8689 if(Multi_sync_bitmap != -1){
8690 gr_set_bitmap(Multi_sync_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8693 Multi_sync_window.draw();
8695 multi_sync_blit_screen_all();
8701 void multi_sync_pre_close()
8703 // at this point, we should shut down any file xfers...
8704 if(Net_player->s_info.xfer_handle != -1){
8705 nprintf(("Network","WARNING - killing file xfer while leaving mission sync state!!!\n"));
8707 multi_xfer_abort(Net_player->s_info.xfer_handle);
8708 Net_player->s_info.xfer_handle = -1;
8712 void multi_sync_post_init()
8714 multi_reset_timestamps();
8716 Multi_state_timestamp = timestamp(0);
8719 ml_string(NOX("Performing post-briefing data sync"));
8721 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8722 multi_common_add_text(XSTR("Server performing sync\n",830),1);
8724 multi_common_add_text(XSTR("Client performing sync\n",838),1);
8727 // everyone should re-initialize these
8728 init_multiplayer_stats();
8730 // reset all sequencing info
8731 multi_oo_reset_sequencing();
8733 // if I am not the master of the game, then send the firing information for my ship
8735 if ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) ){
8736 send_firing_info_packet();
8739 // if I'm not a standalone server, load up the countdown stuff
8740 if(!(Game_mode & GM_STANDALONE_SERVER)){
8741 Multi_sync_countdown_anim = NULL;
8742 Multi_sync_countdown_instance = NULL;
8743 Multi_sync_countdown_anim = anim_load(Multi_sync_countdown_fname[gr_screen.res]);
8744 if(Multi_sync_countdown_anim == NULL){
8745 nprintf(("General","WARNING!, Could not load countdown animation %s!\n",Multi_sync_countdown_fname[gr_screen.res]));
8749 // create objects for all permanent observers
8750 multi_obs_level_init();
8752 // clear the game start countdown timer
8753 Multi_sync_countdown_timer = -1.0f;
8754 Multi_sync_countdown = -1;
8756 // if this is a team vs. team mission, mark all ship teams appropriately
8757 if(Netgame.type_flags & NG_TYPE_TEAM){
8758 multi_team_mark_all_ships();
8761 Mission_sync_flags = 0;
8762 Multi_sync_launch_pressed = 0;
8765 #define MULTI_POST_TIMESTAMP 7000
8767 extern int create_wings();
8769 void multi_sync_post_do()
8773 // only if the host is also the master should he be doing this (non-standalone situation)
8774 if ( Net_player->flags & NETINFO_FLAG_AM_MASTER ) {
8776 // once everyone gets to this screen, send them the ship classes of all ships.
8777 if(multi_netplayer_state_check(NETPLAYER_STATE_WAITING) && !(Mission_sync_flags & MS_FLAG_POST_DATA)) {
8778 // only the host should ever do this
8779 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8780 // at this point we want to delete all necessary ships, change all necessary ship classes, and set all weapons up
8781 multi_ts_create_wings();
8783 // update player ets settings
8784 for(idx=0;idx<MAX_PLAYERS;idx++){
8785 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].player->objnum != -1)){
8786 multi_server_update_player_weapons(&Net_players[idx],&Ships[Objects[Net_players[idx].player->objnum].instance]);
8791 // note that this is done a little differently for standalones and nonstandalones
8792 send_post_sync_data_packet();
8794 multi_common_add_text(XSTR("Sending post briefing block information\n",839),1);
8796 Mission_sync_flags |= MS_FLAG_POST_DATA;
8799 // send weapon slots data
8800 if(multi_netplayer_state_check(NETPLAYER_STATE_POST_DATA_ACK) && !(Mission_sync_flags & MS_FLAG_WSS_SLOTS)) {
8801 // note that this is done a little differently for standalones and nonstandalones
8802 if(Netgame.type_flags & NG_TYPE_TEAM){
8803 send_wss_slots_data_packet(0,0);
8804 send_wss_slots_data_packet(1,1);
8806 send_wss_slots_data_packet(0,1);
8809 multi_common_add_text(XSTR("Sending weapon slots information\n",840),1);
8811 Mission_sync_flags |= MS_FLAG_WSS_SLOTS;
8814 // once weapon information is received, send player settings info
8815 if ( multi_netplayer_state_check(NETPLAYER_STATE_WSS_ACK) && !(Mission_sync_flags & MS_FLAG_PSETTINGS)) {
8816 send_player_settings_packet();
8818 // server (specifically, the standalone), should set this here
8819 Net_player->state = NETPLAYER_STATE_SETTINGS_ACK;
8820 send_netplayer_update_packet();
8822 multi_common_add_text(XSTR("Sending player settings packets\n",841),1);
8824 Mission_sync_flags |= MS_FLAG_PSETTINGS;
8827 // check to see if the countdown timer has started and act appropriately
8828 if( Multi_sync_countdown_timer > -1.0f ) {
8830 // increment by frametime.
8831 Multi_sync_countdown_timer += flFrametime;
8833 // if the animation is not playing, start it
8834 if(!(Game_mode & GM_STANDALONE_SERVER) && (Multi_sync_countdown_instance == NULL) && (Multi_sync_countdown_anim != NULL)){
8835 anim_play_struct aps;
8837 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]);
8838 aps.screen_id = GS_STATE_MULTI_MISSION_SYNC;
8839 aps.framerate_independent = 1;
8841 Multi_sync_countdown_instance = anim_play(&aps);
8844 // if the next second has expired
8845 if( Multi_sync_countdown_timer >= 1.0f ) {
8847 Multi_sync_countdown--;
8848 Multi_sync_countdown_timer = 0.0f;
8850 // if the countdown has reached 0, launch the mission
8851 if(Multi_sync_countdown == 0){
8852 Multi_sync_countdown_timer = -1.0f;
8854 Multi_sync_launch_pressed = 0;
8855 multi_sync_launch();
8857 // otherwise send a countdown packet
8859 send_countdown_packet(Multi_sync_countdown);
8864 // jump into the mission myself
8865 if((Multi_sync_countdown == 0) && multi_netplayer_state_check(NETPLAYER_STATE_IN_MISSION)){
8866 if(!((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !Multi_sync_launch_pressed)){
8867 Net_player->state = NETPLAYER_STATE_IN_MISSION;
8868 Netgame.game_state = NETGAME_STATE_IN_MISSION;
8869 gameseq_post_event(GS_EVENT_ENTER_GAME);
8871 multi_common_add_text(XSTR("Moving into game\n",842),1);
8875 // maybe start the animation countdown
8876 if((Multi_sync_countdown >= 0) && (Multi_sync_countdown_instance == NULL) && (Multi_sync_countdown_anim != NULL)){
8877 anim_play_struct aps;
8879 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]);
8880 aps.screen_id = GS_STATE_MULTI_MISSION_SYNC;
8881 aps.framerate_independent = 1;
8883 Multi_sync_countdown_instance = anim_play(&aps);
8887 // host - specific stuff
8888 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8889 // create the launch button so the host can click
8890 if( Sync_test && multi_netplayer_state_check(NETPLAYER_STATE_SETTINGS_ACK) ){
8891 multi_sync_create_launch_button();
8896 if(!(Game_mode & GM_STANDALONE_SERVER)){
8898 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8899 if(Multi_sync_bitmap != -1){
8900 gr_set_bitmap(Multi_sync_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8903 Multi_sync_window.draw();
8905 multi_sync_blit_screen_all();
8911 void multi_sync_post_close()
8915 // if I'm not a standalone server, unload up the countdown stuff
8916 if(!(Game_mode & GM_STANDALONE_SERVER)){
8917 // release all rendering animation instances (should only be 1)
8918 anim_release_all_instances(GS_STATE_MULTI_MISSION_SYNC);
8919 Multi_sync_countdown_instance = NULL;
8921 // free up the countdown animation
8922 if(Multi_sync_countdown_anim != NULL){
8923 anim_free(Multi_sync_countdown_anim);
8924 Multi_sync_countdown_anim = NULL;
8928 // all players should reset sequencing
8929 for(idx=0;idx<MAX_PLAYERS;idx++){
8930 if(Net_player->flags & NETINFO_FLAG_CONNECTED){
8931 Net_players[idx].client_cinfo_seq = 0;
8932 Net_players[idx].client_server_seq = 0;
8936 // multiplayer dogfight
8937 multi_df_level_pre_enter();
8939 // clients should clear obj_pair array and add pair for themselves
8941 if ( MULTIPLAYER_CLIENT ) {
8943 obj_add_pairs( OBJ_INDEX(Player_obj) );
8948 void multi_sync_display_name(const char *name, int index, int np_index)
8950 char fit[CALLSIGN_LEN];
8952 // make sure the string actually fits
8953 SDL_strlcpy(fit, name, SDL_arraysize(fit));
8955 // if we're in team vs. team mode
8956 if(Netgame.type_flags & NG_TYPE_TEAM){
8957 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]);
8959 // if this is the currently selected player, draw him highlighted
8960 if(np_index == Multi_sync_player_select){
8961 gr_set_color_fast(&Color_text_selected);
8963 gr_set_color_fast(&Color_text_normal);
8967 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);
8969 // blit his team icon
8971 if(Net_players[np_index].p_info.team == 0){
8972 // blit the team captain icon
8973 if(Net_players[np_index].flags & NETINFO_FLAG_TEAM_CAPTAIN){
8974 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
8975 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8976 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);
8979 // normal team member icon
8981 if(Multi_common_icons[MICON_TEAM0] != -1){
8982 gr_set_bitmap(Multi_common_icons[MICON_TEAM0], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8983 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);
8988 else if(Net_players[np_index].p_info.team == 1){
8989 // blit the team captain icon
8990 if(Net_players[np_index].flags & NETINFO_FLAG_TEAM_CAPTAIN){
8991 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
8992 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8993 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);
8996 // normal team member icon
8998 if(Multi_common_icons[MICON_TEAM1] != -1){
8999 gr_set_bitmap(Multi_common_icons[MICON_TEAM1], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
9000 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);
9005 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]);
9007 // if this is the currently selected player, draw him highlighted
9008 if(np_index == Multi_sync_player_select){
9009 gr_set_color_fast(&Color_text_selected);
9011 gr_set_color_fast(&Color_text_normal);
9015 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);
9018 // maybe blit his CD status icon
9019 if((Net_players[np_index].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
9020 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
9021 gr_bitmap(Ms_status_coords[gr_screen.res][MS_X_COORD], Ms_status_coords[gr_screen.res][MS_Y_COORD] + (index * 10));
9025 void multi_sync_display_status(const char *status, int index)
9029 // make sure the string actually fits
9030 SDL_strlcpy(fit, status, SDL_arraysize(fit));
9031 gr_force_fit_string(fit, 250, Ms_status2_coords[gr_screen.res][MS_W_COORD] - 20);
9032 gr_set_color_fast(&Color_bright);
9033 gr_string(Ms_status2_coords[gr_screen.res][MS_X_COORD], Ms_status2_coords[gr_screen.res][MS_Y_COORD] + (index * 10), fit);
9036 void multi_sync_force_start_pre()
9039 int want_state = NETPLAYER_STATE_SLOT_ACK; // kick any players who are still in this state
9041 // go through the player list and boot anyone who isn't in the right state
9042 for(idx=0;idx<MAX_PLAYERS;idx++){
9043 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx]) && (Net_players[idx].state == want_state)){
9044 multi_kick_player(idx,0);
9049 void multi_sync_force_start_post()
9053 int num_kill_states;
9055 // determine the state we want all players in so that we can find those who are not in the state
9056 kill_state[0] = NETPLAYER_STATE_BRIEFING;
9057 kill_state[1] = NETPLAYER_STATE_SHIP_SELECT;
9058 kill_state[2] = NETPLAYER_STATE_WEAPON_SELECT;
9059 num_kill_states = 3;
9061 // go through the player list and boot anyone who isn't in the right state
9062 for(idx=0;idx<MAX_PLAYERS;idx++){
9063 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx])){
9064 // check against all kill state
9065 for(idx2 = 0;idx2<num_kill_states;idx2++){
9066 if(Net_players[idx].state == kill_state[idx2]){
9067 multi_kick_player(idx,0);
9075 void multi_sync_start_countdown()
9077 // don't allow repeat button presses
9078 if(Multi_sync_launch_pressed){
9082 Multi_sync_launch_pressed = 1;
9084 // if I'm the server, begin the countdown
9085 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
9086 gamesnd_play_iface(SND_COMMIT_PRESSED);
9087 Multi_sync_countdown_timer = 0.0f;
9088 Multi_sync_countdown = MULTI_SYNC_COUNTDOWN_TIME;
9090 // send an initial countdown value
9091 send_countdown_packet(Multi_sync_countdown);
9093 // otherwise send the "start countdown" packet to the standalone
9095 SDL_assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
9096 send_countdown_packet(-1);
9100 void multi_sync_launch()
9102 // don't allow repeat button presses
9103 if(Multi_sync_launch_pressed){
9107 Multi_sync_launch_pressed = 1;
9110 ml_printf(NOX("Entering mission %s"), Game_current_mission_filename);
9112 // tell everyone to jump into the mission
9113 send_jump_into_mission_packet();
9114 Multi_state_timestamp = timestamp(MULTI_POST_TIMESTAMP);
9116 // set the # of players at the start of the mission
9117 Multi_num_players_at_start = multi_num_players();
9118 nprintf(("Network","# of players at start of mission : %d\n", Multi_num_players_at_start));
9120 // initialize datarate limiting for all clients
9121 multi_oo_rate_init_all();
9123 multi_common_add_text(XSTR("Sending mission start packet\n",843),1);
9126 void multi_sync_create_launch_button()
9128 if (!Multi_launch_button_created) {
9129 // create the object
9130 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);
9132 // set the sound to play when highlighted
9133 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_highlight_action(common_play_highlight_sound);
9135 // set the ani for the button
9136 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_bmaps(Multi_sync_buttons[gr_screen.res][MS_LAUNCH].filename, 3, 0);
9139 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.link_hotspot(Multi_sync_buttons[gr_screen.res][MS_LAUNCH].hotspot);
9142 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_hotkey(KEY_CTRLED+SDLK_RETURN);
9145 // create the text for the button
9146 Multi_sync_window.add_XSTR(&Multi_sync_text[gr_screen.res][MST_LAUNCH]);
9149 // increment the button count so we start checking this one
9150 Multi_sync_button_count++;
9152 Multi_launch_button_created = 1;
9156 void multi_sync_handle_plist()
9162 // if we don't have a currently selected player, select one
9163 if((Multi_sync_player_select < 0) || !MULTI_CONNECTED(Net_players[Multi_sync_player_select])){
9164 for(idx=0;idx<MAX_PLAYERS;idx++){
9165 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
9166 Multi_sync_player_select = idx;
9172 // check for button list presses
9173 if(Multi_sync_plist_button.pressed()){
9174 // get the y mouse coords
9175 Multi_sync_plist_button.get_mouse_pos(NULL,&my);
9177 // get the index of the item selected
9178 select_index = my / 10;
9180 // if the index is greater than the current # connections, do nothing
9181 if(select_index > (multi_num_connections() - 1)){
9185 // translate into an absolute Net_players[] index (get the Nth net player)
9186 Multi_sync_player_select = -1;
9187 for(idx=0;idx<MAX_PLAYERS;idx++){
9188 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
9192 // if we've found the item we're looking for
9193 if(select_index < 0){
9194 Multi_sync_player_select = idx;
9199 // if for some bizarre reason, this is an invalid player, unselect him and wait for the next interation
9201 if((Multi_sync_player_select >= 0) && (!MULTI_CONNECTED(Net_players[Multi_sync_player_select]) || MULTI_STANDALONE(Net_players[Multi_sync_player_select])) ){
9202 Multi_sync_player_select = -1;
9208 // -------------------------------------------------------------------------------------------------------------
9210 // MULTIPLAYER DEBRIEF SCREEN
9213 // other relevant data
9214 int Multi_debrief_accept_hit;
9215 int Multi_debrief_replay_hit;
9217 // set if the server has left the game
9218 int Multi_debrief_server_left = 0;
9220 // if we've reported on TvT status all players are in the debrief
9221 int Multi_debrief_reported_tvt = 0;
9223 // whether stats are being accepted
9224 // -1 == no decision yet
9227 int Multi_debrief_stats_accept_code = -1;
9229 int Multi_debrief_server_framecount = 0;
9231 float Multi_debrief_time = 0.0f;
9232 float Multi_debrief_resend_time = 10.0f;
9234 void multi_debrief_init()
9238 Multi_debrief_time = 0.0f;
9239 Multi_debrief_resend_time = 10.0f;
9241 // do this to notify the standalone or the normal server that we're in the debrief state and ready to receive packets
9242 if (!(Net_player->flags & NETINFO_FLAG_AM_MASTER)) {
9243 Net_player->state = NETPLAYER_STATE_DEBRIEF;
9244 send_netplayer_update_packet();
9247 // unflag some stuff
9248 for(idx=0;idx<MAX_PLAYERS;idx++){
9249 if(MULTI_CONNECTED(Net_players[idx])){
9250 Net_players[idx].flags &= ~(NETINFO_FLAG_RESPAWNING | NETINFO_FLAG_LIMBO | NETINFO_FLAG_WARPING_OUT);
9254 // if text input mode is active, clear it
9255 multi_msg_text_flush();
9257 // the server has not left yet
9258 Multi_debrief_server_left = 0;
9260 // have not hit accept or replay yet
9261 Multi_debrief_accept_hit = 0;
9262 Multi_debrief_replay_hit = 0;
9264 // stats have not been accepted yet
9265 Multi_debrief_stats_accept_code = -1;
9267 // mark stats as not being store yet
9268 Netgame.flags &= ~(NG_FLAG_STORED_MT_STATS);
9270 // no report on TvT yet
9271 Multi_debrief_reported_tvt = 0;
9273 Multi_debrief_server_framecount = 0;
9276 void multi_debrief_do_frame()
9278 Multi_debrief_time += flFrametime;
9280 // set the netgame state to be debriefing when appropriate
9281 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)){
9282 Netgame.game_state = NETGAME_STATE_DEBRIEF;
9283 send_netgame_update_packet();
9286 // evaluate all server stuff
9287 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
9288 multi_debrief_server_process();
9292 void multi_debrief_close()
9294 if ( MULTIPLAYER_CLIENT && (Netgame.game_state == NETGAME_STATE_MISSION_SYNC) ){
9295 gamesnd_play_iface( SND_COMMIT_PRESSED );
9299 // handle optional mission loop
9300 void multi_maybe_set_mission_loop()
9302 int cur = Campaign.current_mission;
9303 if (Campaign.missions[cur].has_mission_loop) {
9304 SDL_assert(Campaign.loop_mission != CAMPAIGN_LOOP_MISSION_UNINITIALIZED);
9306 bool require_repeat_mission = (Campaign.current_mission == Campaign.next_mission);
9308 // check for (1) mission loop available, (2) dont have to repeat last mission
9309 if ( (Campaign.missions[cur].has_mission_loop && (Campaign.loop_mission != -1)) && !require_repeat_mission ) {
9312 debrief_assemble_optional_mission_popup_text(buffer, SDL_arraysize(buffer), Campaign.missions[cur].mission_loop_desc);
9314 int choice = popup(0 , 2, POPUP_NO, POPUP_YES, buffer);
9316 Campaign.loop_enabled = 1;
9317 Campaign.next_mission = Campaign.loop_mission;
9322 // handle all cases for when the accept key is hit in a multiplayer debriefing
9323 void multi_debrief_accept_hit()
9325 // if we already accepted, do nothing
9326 // but he may need to hit accept again after the server has left the game, so allow this
9327 if(Multi_debrief_accept_hit){
9331 // mark this so that we don't hit it again
9332 Multi_debrief_accept_hit = 1;
9334 gamesnd_play_iface(SND_COMMIT_PRESSED);
9336 // if the server has left the game, always just end the game.
9337 if(Multi_debrief_server_left){
9338 if(!multi_quit_game(PROMPT_ALL)){
9339 Multi_debrief_server_left = 1;
9340 Multi_debrief_accept_hit = 0;
9342 Multi_debrief_server_left = 0;
9345 // query the host and see if he wants to accept stats
9346 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
9347 // if we're on a tracker game, he gets no choice for storing stats
9348 if (MULTI_IS_TRACKER_GAME) {
9349 // if not standalone, send stats
9350 if (Net_player->flags & NETINFO_FLAG_AM_MASTER) {
9351 if ( !(Netgame.flags & NG_FLAG_STORED_MT_STATS) ) {
9352 int stats_saved = multi_fs_tracker_store_stats();
9355 multi_debrief_stats_accept();
9357 Netgame.flags |= NG_FLAG_STORED_MT_STATS;
9358 send_netgame_update_packet();
9360 multi_debrief_stats_toss();
9364 if (Netgame.type_flags & NG_TYPE_SW) {
9365 multi_sw_report(stats_saved);
9371 multi_maybe_set_mission_loop();
9373 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));
9375 // evaluate the result
9380 Multi_debrief_accept_hit = 0;
9383 // set the accept code to be "not accepting"
9385 multi_debrief_stats_toss();
9386 multi_maybe_set_mission_loop();
9389 // accept the stats and continue
9391 multi_debrief_stats_accept();
9392 multi_maybe_set_mission_loop();
9398 // set my netplayer state to be "debrief_accept", and be done with it
9399 Net_player->state = NETPLAYER_STATE_DEBRIEF_ACCEPT;
9400 send_netplayer_update_packet();
9404 // handle all cases for when the escape key is hit in a multiplayer debriefing
9405 void multi_debrief_esc_hit()
9409 // if the server has left
9410 if(Multi_debrief_server_left){
9411 multi_quit_game(PROMPT_ALL);
9416 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
9417 // if the stats have already been accepted
9418 if((Multi_debrief_stats_accept_code != -1) || (MULTI_IS_TRACKER_GAME)){
9419 // if not on standalone, maybe send stats
9420 if ( (Net_player->flags & NETINFO_FLAG_AM_MASTER) && MULTI_IS_TRACKER_GAME ) {
9421 if ( !(Netgame.flags & NG_FLAG_STORED_MT_STATS) ) {
9422 int stats_saved = multi_fs_tracker_store_stats();
9425 multi_debrief_stats_accept();
9427 Netgame.flags |= NG_FLAG_STORED_MT_STATS;
9428 send_netgame_update_packet();
9430 multi_debrief_stats_toss();
9434 if (Netgame.type_flags & NG_TYPE_SW) {
9435 multi_sw_report(stats_saved);
9441 multi_quit_game(PROMPT_HOST);
9443 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));
9445 // evaluate the result
9452 // set the accept code to be "not accepting"
9454 multi_debrief_stats_toss();
9455 multi_quit_game(PROMPT_NONE);
9458 // accept the stats and continue
9460 multi_debrief_stats_accept();
9461 multi_quit_game(PROMPT_NONE);
9466 // if the stats haven't been accepted yet, or this is a tracker game
9467 if((Multi_debrief_stats_accept_code == -1) && !(MULTI_IS_TRACKER_GAME)){
9468 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));
9470 // evaluate the result
9472 multi_quit_game(PROMPT_NONE);
9475 // otherwise go through the normal endgame channels
9477 multi_quit_game(PROMPT_ALL);
9482 void multi_debrief_replay_hit()
9484 // only the host should ever get here
9485 SDL_assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
9487 // if the button was already pressed, do nothing
9488 if(Multi_debrief_accept_hit){
9492 // same as hittin the except button except no stats are kept
9493 Multi_debrief_accept_hit = 1;
9495 // mark myself as being in the replay state so we know what to do next
9496 Net_player->state = NETPLAYER_STATE_DEBRIEF_REPLAY;
9497 send_netplayer_update_packet();
9500 // call this when the server has left and we would otherwise be saying "contact lost with server
9501 void multi_debrief_server_left()
9504 Multi_debrief_server_left = 1;
9506 // undo any "accept" hit so that clients can hit accept again to leave
9507 Multi_debrief_accept_hit = 0;
9510 void multi_debrief_stats_accept()
9512 // don't do anything if we've already accepted
9513 if(Multi_debrief_stats_accept_code != -1){
9517 Multi_debrief_stats_accept_code = 1;
9519 // if we're the host, and we're on a standalone, tell the standalone to begin the stats storing process
9520 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) || (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
9521 // send a packet to the players telling them to store their stats
9522 send_store_stats_packet(1);
9525 // add a chat line saying "stats have been accepted"
9526 multi_display_chat_msg(XSTR("<stats have been accepted>",850),0,0);
9529 ml_string(NOX("Stats stored"));
9532 void multi_debrief_stats_toss()
9534 // don't do anything if we've already accepted
9535 if(Multi_debrief_stats_accept_code != -1){
9539 Multi_debrief_stats_accept_code = 0;
9541 // if we're the host, and we're on a standalone, tell everyone to "toss" stats
9542 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) || (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
9543 // send a packet to the players telling them to store their stats
9544 send_store_stats_packet(0);
9547 // add a chat line saying "stats have been accepted"
9548 multi_display_chat_msg(XSTR("<stats have been tossed>",851),0,0);
9551 ml_string(NOX("Stats tossed"));
9554 int multi_debrief_stats_accept_code()
9556 return Multi_debrief_stats_accept_code;
9559 void multi_debrief_server_process()
9562 int player_status,other_status;
9564 Multi_debrief_server_framecount++;
9566 // if we're > 10 seconds into the debrief and not everyone is here, try warping everyone out again
9567 if((Multi_debrief_time >= Multi_debrief_resend_time) && !multi_netplayer_state_check3(NETPLAYER_STATE_DEBRIEF, NETPLAYER_STATE_DEBRIEF_ACCEPT, NETPLAYER_STATE_DEBRIEF_REPLAY, 1)){
9568 // find all players who are not in the debrief state and hit them with the endgame packet
9569 for(idx=0; idx<MAX_PLAYERS; idx++){
9570 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)) ){
9571 send_endgame_packet(&Net_players[idx]);
9576 Multi_debrief_resend_time += 7.0f;
9579 // evaluate the status of all players in the game (0 == not ready, 1 == ready to continue, 2 == ready to replay)
9582 // check all players
9583 for(idx=0;idx<MAX_PLAYERS;idx++){
9584 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_HOST(Net_players[idx])){
9585 if(Net_players[idx].state != NETPLAYER_STATE_DEBRIEF_ACCEPT){
9592 // if we haven't already reported TvT results
9593 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)){
9594 multi_team_report();
9595 Multi_debrief_reported_tvt = 1;
9598 // if all other players are good to go, check the host
9600 // if he is ready to continue
9601 if(Netgame.host->state == NETPLAYER_STATE_DEBRIEF_ACCEPT){
9604 // if he wants to replay the mission
9605 else if(Netgame.host->state == NETPLAYER_STATE_DEBRIEF_REPLAY){
9608 // if he is not ready
9613 // if all players are _not_ good to go
9618 // if we're in the debriefing state in a campaign mode, process accordingly
9619 if(Netgame.campaign_mode == MP_CAMPAIGN){
9620 multi_campaign_do_debrief(player_status);
9622 // otherwise process as normal (looking for all players to be ready to go to the next mission
9624 if(player_status == 1){
9625 multi_flush_mission_stuff();
9627 // set the netgame state to be forming and continue
9628 Netgame.game_state = NETGAME_STATE_FORMING;
9629 send_netgame_update_packet();
9631 // move to the proper state
9632 if(Game_mode & GM_STANDALONE_SERVER){
9633 gameseq_post_event(GS_EVENT_STANDALONE_MAIN);
9635 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
9638 multi_reset_timestamps();
9639 } else if(player_status == 2){
9640 multi_flush_mission_stuff();
9642 // tell everyone to move into the pre-briefing sync state
9643 Netgame.game_state = NETGAME_STATE_MISSION_SYNC;
9644 send_netgame_update_packet();
9646 // move back to the mission sync screen for the same mission again
9647 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
9648 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
9650 multi_reset_timestamps();
9656 // -------------------------------------------------------------------------------------------------------------
9658 // MULTIPLAYER PASSWORD POPUP
9663 static const char *Multi_pwd_bitmap_fname[GR_NUM_RESOLUTIONS] = {
9664 "Password", // GR_640
9665 "2_Password" // GR_1024
9668 static const char *Multi_pwd_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
9669 "Password-M", // GR_640
9670 "2_Password-M" // GR_1024
9675 // constants for coordinate lookup
9676 #define MPWD_X_COORD 0
9677 #define MPWD_Y_COORD 1
9678 #define MPWD_W_COORD 2
9679 #define MPWD_H_COORD 3
9682 #define MULTI_PWD_NUM_BUTTONS 2
9683 #define MPWD_CANCEL 0
9684 #define MPWD_COMMIT 1
9686 // password area defs
9687 int Mpwd_coords[GR_NUM_RESOLUTIONS][4] = {
9700 UI_WINDOW Multi_pwd_window; // the window object for the join screen
9701 UI_INPUTBOX Multi_pwd_passwd; // for Netgame.passwd
9702 int Multi_pwd_bitmap; // the background bitmap
9703 int Multi_passwd_background = -1;
9704 int Multi_passwd_done = -1;
9705 int Multi_passwd_running = 0;
9708 ui_button_info Multi_pwd_buttons[GR_NUM_RESOLUTIONS][MULTI_PWD_NUM_BUTTONS] = {
9711 ui_button_info("PWB_00", 402, 134, -1, -1, 0),
9712 ui_button_info("PWB_01", 450, 134, -1, -1, 1),
9714 ui_button_info("PWB_00", 411, 151, 405, 141, 0),
9715 ui_button_info("PWB_01", 460, 151, 465, 141, 1),
9719 ui_button_info("2_PWB_00", 659, 242, 649, 225, 0),
9720 ui_button_info("2_PWB_01", 737, 242, 736, 225, 1),
9726 #define MULTI_PWD_NUM_TEXT 3
9728 UI_XSTR Multi_pwd_text[GR_NUM_RESOLUTIONS][MULTI_PWD_NUM_TEXT] = {
9730 { "Cancel", 387, 400, 141, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[0][MPWD_CANCEL].button},
9731 { "Commit", 1062, 455, 141, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[0][MPWD_COMMIT].button},
9732 { "Enter Password", 1332, 149, 92, UI_XSTR_COLOR_GREEN, -1, NULL},
9735 { "Cancel", 387, 649, 225, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[1][MPWD_CANCEL].button},
9736 { "Commit", 1062, 736, 225, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[1][MPWD_COMMIT].button},
9737 { "Enter Password", 1332, 239, 148, UI_XSTR_COLOR_GREEN, -1, NULL},
9742 // initialize all graphics, etc
9743 void multi_passwd_init()
9747 // store the background as it currently is
9748 Multi_passwd_background = gr_save_screen();
9750 // create the interface window
9751 Multi_pwd_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
9752 Multi_pwd_window.set_mask_bmap(Multi_pwd_bitmap_mask_fname[gr_screen.res]);
9754 // load the background bitmap
9755 Multi_pwd_bitmap = bm_load(Multi_pwd_bitmap_fname[gr_screen.res]);
9756 if(Multi_pwd_bitmap < 0){
9757 // we failed to load the bitmap - this is very bad
9761 // create the interface buttons
9762 for(idx=0; idx<MULTI_PWD_NUM_BUTTONS; idx++){
9763 // create the object
9764 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);
9766 // set the sound to play when highlighted
9767 Multi_pwd_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
9769 // set the ani for the button
9770 Multi_pwd_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pwd_buttons[gr_screen.res][idx].filename);
9773 Multi_pwd_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pwd_buttons[gr_screen.res][idx].hotspot);
9778 for(idx=0; idx<MULTI_PWD_NUM_TEXT; idx++){
9779 Multi_pwd_window.add_XSTR(&Multi_pwd_text[gr_screen.res][idx]);
9783 // create the password input box
9784 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);
9785 Multi_pwd_passwd.set_focus();
9787 // link the enter key to ACCEPT
9788 Multi_pwd_buttons[gr_screen.res][MPWD_COMMIT].button.set_hotkey(SDLK_RETURN);
9790 Multi_passwd_done = -1;
9791 Multi_passwd_running = 1;
9794 // close down all graphics, etc
9795 void multi_passwd_close()
9797 // unload any bitmaps
9798 bm_release(Multi_pwd_bitmap);
9800 // destroy the UI_WINDOW
9801 Multi_pwd_window.destroy();
9803 // free up the saved background screen
9804 if(Multi_passwd_background >= 0){
9805 gr_free_screen(Multi_passwd_background);
9806 Multi_passwd_background = -1;
9809 Multi_passwd_running = 0;
9812 // process any button pressed
9813 void multi_passwd_process_buttons()
9815 // if the accept button was pressed
9816 if(Multi_pwd_buttons[gr_screen.res][MPWD_COMMIT].button.pressed()){
9817 gamesnd_play_iface(SND_USER_SELECT);
9818 Multi_passwd_done = 1;
9821 // if the cancel button was pressed
9822 if(Multi_pwd_buttons[gr_screen.res][MPWD_CANCEL].button.pressed()){
9823 gamesnd_play_iface(SND_USER_SELECT);
9824 Multi_passwd_done = 0;
9828 // run the passwd popup
9829 void multi_passwd_do(char *passwd, const int max_passlen)
9833 while(Multi_passwd_done == -1){
9834 // set frametime and run background stuff
9835 game_set_frametime(-1);
9836 game_do_state_common(gameseq_get_state());
9838 k = Multi_pwd_window.process();
9840 // process any keypresses
9843 // set this to indicate the user has cancelled for one reason or another
9844 Multi_passwd_done = 0;
9848 // if the input box text has changed
9849 if(Multi_pwd_passwd.changed()){
9850 SDL_strlcpy(passwd, "", max_passlen);
9851 Multi_pwd_passwd.get_text(passwd);
9854 // process any button pressed
9855 multi_passwd_process_buttons();
9857 // draw the background, etc
9860 if(Multi_passwd_background >= 0){
9861 gr_restore_screen(Multi_passwd_background);
9863 gr_set_bitmap(Multi_pwd_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
9865 Multi_pwd_window.draw();
9872 // bring up the password string popup, fill in passwd (return 1 if accept was pressed, 0 if cancel was pressed)
9873 int multi_passwd_popup(char *passwd, const int max_plen)
9875 // if the popup is already running for some reason, don't do anything
9876 if(Multi_passwd_running){
9880 // initialize all graphics
9881 multi_passwd_init();
9884 multi_passwd_do(passwd, max_plen);
9886 // shut everything down
9887 multi_passwd_close();
9889 return Multi_passwd_done;