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 = 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();
1347 multi_fs_tracker_send_game_request();
1350 // initialize any and all timestamps
1351 Multi_join_glr_stamp = -1;
1352 Multi_join_ping_stamp = -1;
1353 Multi_join_sent_stamp = -1;
1355 // reset frame count
1356 Multi_join_frame_count = 0;
1358 // haven't tried to verify on the tracker yet.
1359 Multi_join_mt_tried_verify = 0;
1361 // clear our all game lists to save hassles
1362 multi_join_clear_game_list();
1364 // create the interface buttons
1365 for(idx=0; idx<MULTI_JOIN_NUM_BUTTONS; idx++){
1366 // create the object
1367 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);
1369 // set the sound to play when highlighted
1370 Multi_join_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
1372 // set the ani for the button
1373 Multi_join_buttons[gr_screen.res][idx].button.set_bmaps(Multi_join_buttons[gr_screen.res][idx].filename);
1376 Multi_join_buttons[gr_screen.res][idx].button.link_hotspot(Multi_join_buttons[gr_screen.res][idx].hotspot);
1381 for(idx=0; idx<MULTI_JOIN_NUM_TEXT; idx++){
1382 Multi_join_window.add_XSTR(&Multi_join_text[gr_screen.res][idx]);
1386 Multi_join_should_send = -1;
1388 // close any previously open chatbox
1391 // create the list item select button
1392 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);
1393 Multi_join_select_button.hide();
1397 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);
1400 // if starting a network game, then go to the create game screen
1401 if ( Cmdline_start_netgame ) {
1402 multi_join_create_game();
1403 } else if ( Cmdline_connect_addr != NULL ) {
1408 // joining a game. Send a join request to the given IP address, and wait for the return.
1409 memset( &Multi_autojoin_addr, 0, sizeof(net_addr) );
1410 Multi_autojoin_addr.type = NET_TCP;
1412 // create the address, looking out for port number at the end
1413 port_num = DEFAULT_GAME_PORT;
1414 p = strrchr(Cmdline_connect_addr, ':');
1418 port_num = (short)atoi(p);
1420 ip_addr = inet_addr(Cmdline_connect_addr);
1421 memcpy(Multi_autojoin_addr.addr, &ip_addr, IP_ADDRESS_LENGTH);
1422 Multi_autojoin_addr.port = port_num;
1424 send_server_query(&Multi_autojoin_addr);
1425 Multi_autojoin_query_stamp = timestamp(MULTI_AUTOJOIN_QUERY_STAMP);
1426 Multi_did_autojoin = 0;
1430 void multi_join_clear_game_list()
1433 Multi_join_list_selected = -1;
1434 Multi_join_selected_item = NULL;
1435 MJ_LIST_START_SET(-1);
1436 Multi_join_list_start_item = NULL;
1438 // free up the active game list
1439 multi_free_active_games();
1441 // initialize the active game list
1442 Active_game_head = NULL;
1443 Active_game_count = 0;
1446 void multi_join_game_do_frame()
1448 // check the status of our reliable socket. If not valid, popup error and return to main menu
1449 // I put this code here to avoid nasty gameseq issues with states. Also, we will have nice
1450 // background for the popup
1451 if ( !psnet_rel_check() ) {
1452 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));
1453 gameseq_post_event(GS_EVENT_MAIN_MENU);
1457 // return here since we will be moving to the next stage anyway -- I don't want to see the backgrounds of
1458 // all the screens for < 1 second for every screen we automatically move to.
1459 if ( Cmdline_start_netgame ) {
1463 // when joining a network game, wait for the server query to come back, and then join the game
1464 if ( Cmdline_connect_addr != NULL ) {
1467 if ( !Multi_did_autojoin ) {
1468 rval = popup_till_condition(multi_join_autojoin_do, XSTR("&Cancel", 779), XSTR("Joining netgame", 1500) );
1470 // cancel was hit. Send the user back to the main hall
1471 gameseq_post_event(GS_EVENT_MAIN_MENU);
1472 Cmdline_connect_addr = NULL; // reset this value.
1475 // when we get here, we have the data -- join the game.
1476 multi_join_send_join_request(0);
1477 Multi_autojoin_join_stamp = timestamp(MULTI_AUTOJOIN_JOIN_STAMP);
1478 Multi_did_autojoin = 1;
1481 if ( timestamp_elapsed(Multi_autojoin_join_stamp) ) {
1482 multi_join_send_join_request(0);
1483 Multi_autojoin_join_stamp = timestamp(MULTI_AUTOJOIN_JOIN_STAMP);
1489 // reset the should send var
1490 Multi_join_should_send = -1;
1492 int k = Multi_join_window.process();
1494 // process any keypresses
1497 if(help_overlay_active(MULTI_JOIN_OVERLAY)){
1498 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1500 if (Multi_options_g.pxo == 1) {
1501 gameseq_post_event(GS_EVENT_PXO);
1503 gameseq_post_event(GS_EVENT_MAIN_MENU);
1506 gamesnd_play_iface(SND_USER_SELECT);
1510 // page up the game list
1512 multi_join_list_page_up();
1514 Multi_join_slider.force_currentItem(Multi_join_list_start);
1519 multi_pinfo_popup(Net_player);
1522 // page down the game list
1524 multi_join_list_page_down();
1526 Multi_join_slider.force_currentItem(Multi_join_list_start);
1530 // send out a ping-all
1532 multi_join_ping_all();
1533 Multi_join_ping_stamp = timestamp(MULTI_JOIN_PING_TIME);
1536 // shortcut to start a game
1538 multi_join_create_game();
1541 // scroll the game list up
1543 multi_join_list_scroll_up();
1545 Multi_join_slider.force_currentItem(Multi_join_list_start);
1549 // scroll the game list down
1551 multi_join_list_scroll_down();
1553 Multi_join_slider.force_currentItem(Multi_join_list_start);
1558 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
1559 help_overlay_set_state(MULTI_JOIN_OVERLAY, 0);
1562 // do any network related stuff
1563 multi_join_do_netstuff();
1565 // process any button clicks
1566 multi_join_check_buttons();
1568 // process any list selection stuff
1569 multi_join_process_select();
1571 // draw the background, etc
1573 GR_MAYBE_CLEAR_RES(Multi_join_bitmap);
1574 if(Multi_join_bitmap != -1){
1575 gr_set_bitmap(Multi_join_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1578 Multi_join_window.draw();
1580 // display the active games
1581 multi_join_display_games();
1583 // display any text in the info area
1584 multi_common_render_text();
1586 // display any pending notification messages
1587 multi_common_notify_do();
1589 // blit the CD icon and any PXO filter stuff
1590 multi_join_blit_top_stuff();
1592 // draw the help overlay
1593 help_overlay_maybe_blit(MULTI_JOIN_OVERLAY);
1598 // if we are supposed to be sending a join request
1599 if(Multi_join_should_send != -1){
1600 multi_join_send_join_request(Multi_join_should_send);
1602 Multi_join_should_send = -1;
1604 // increment the frame count
1605 Multi_join_frame_count++;
1608 void multi_join_game_close()
1610 // unload any bitmaps
1611 if(!bm_unload(Multi_join_bitmap)){
1612 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_join_bitmap_fname[gr_screen.res]));
1615 // unload the help overlay
1616 help_overlay_unload(MULTI_JOIN_OVERLAY);
1618 // free up the active game list
1619 multi_free_active_games();
1621 // destroy the UI_WINDOW
1622 Multi_join_window.destroy();
1625 common_free_interface_palette();
1629 void multi_join_check_buttons()
1632 for(idx=0;idx<MULTI_JOIN_NUM_BUTTONS;idx++){
1633 // we only really need to check for one button pressed at a time, so we can break after
1635 if(Multi_join_buttons[gr_screen.res][idx].button.pressed()){
1636 multi_join_button_pressed(idx);
1642 void multi_join_button_pressed(int n)
1646 // if we're player PXO, go back there
1647 if (Multi_options_g.pxo == 1) {
1648 gameseq_post_event(GS_EVENT_PXO);
1650 gameseq_post_event(GS_EVENT_MAIN_MENU);
1652 gamesnd_play_iface(SND_USER_SELECT);
1655 if(Active_game_count <= 0){
1656 multi_common_add_notify(XSTR("No games found!",757));
1657 gamesnd_play_iface(SND_GENERAL_FAIL);
1658 } else if(Multi_join_list_selected == -1){
1659 multi_common_add_notify(XSTR("No game selected!",758));
1660 gamesnd_play_iface(SND_GENERAL_FAIL);
1661 } else if((Multi_join_sent_stamp != -1) && !timestamp_elapsed(Multi_join_sent_stamp)){
1662 multi_common_add_notify(XSTR("Still waiting on previous join request!",759));
1663 gamesnd_play_iface(SND_GENERAL_FAIL);
1665 // otherwise, if he's already played PXO games, warn him
1667 if(Player->flags & PLAYER_FLAGS_HAS_PLAYED_PXO){
1668 if(!multi_join_warn_pxo()){
1674 // send the join request here
1675 SDL_assert(Multi_join_selected_item != NULL);
1677 // send a join request packet
1678 Multi_join_should_send = 0;
1680 gamesnd_play_iface(SND_COMMIT_PRESSED);
1686 if(!help_overlay_active(MULTI_JOIN_OVERLAY)){
1687 help_overlay_set_state(MULTI_JOIN_OVERLAY,1);
1689 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1693 // scroll the game list up
1695 multi_join_list_scroll_up();
1697 Multi_join_slider.force_currentItem(Multi_join_list_start);
1701 // scroll the game list down
1702 case MJ_SCROLL_DOWN:
1703 multi_join_list_scroll_down();
1705 Multi_join_slider.force_currentItem(Multi_join_list_start);
1709 // scroll the info text box up
1710 case MJ_SCROLL_INFO_UP:
1711 multi_common_scroll_text_up();
1714 // scroll the info text box down
1715 case MJ_SCROLL_INFO_DOWN:
1716 multi_common_scroll_text_down();
1719 // go to the options screen
1721 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
1724 // go to the start game screen
1726 multi_join_create_game();
1729 // refresh the game/server list
1731 gamesnd_play_iface(SND_USER_SELECT);
1732 broadcast_game_query();
1735 // join a game as an observer
1736 case MJ_JOIN_OBSERVER:
1737 if(Active_game_count <= 0){
1738 multi_common_add_notify(XSTR("No games found!",757));
1739 gamesnd_play_iface(SND_GENERAL_FAIL);
1740 } else if(Multi_join_list_selected == -1){
1741 multi_common_add_notify(XSTR("No game selected!",758));
1742 gamesnd_play_iface(SND_GENERAL_FAIL);
1743 } else if((Multi_join_sent_stamp != -1) && !timestamp_elapsed(Multi_join_sent_stamp)){
1744 multi_common_add_notify(XSTR("Still waiting on previous join request!",759));
1745 gamesnd_play_iface(SND_GENERAL_FAIL);
1747 // send the join request here
1748 SDL_assert(Multi_join_selected_item != NULL);
1750 Multi_join_should_send = 1;
1752 gamesnd_play_iface(SND_COMMIT_PRESSED);
1757 multi_common_add_notify(XSTR("Not implemented yet!",760));
1758 gamesnd_play_iface(SND_GENERAL_FAIL);
1763 // display all relevant info for active games
1764 void multi_join_display_games()
1766 active_game *moveup = Multi_join_list_start_item;
1770 int y_start = Mj_list_y[gr_screen.res];
1775 // blit the game status (including text and type icon)
1776 multi_join_blit_game_status(moveup,y_start);
1778 // get the connection type
1779 con_type = (moveup->flags & AG_FLAG_CONNECTION_SPEED_MASK) >> AG_FLAG_CONNECTION_BIT;
1780 if((con_type > 4) || (con_type < 0)){
1784 // display the connection speed
1786 SDL_strlcpy(str, Multi_join_speed_labels[con_type], SDL_arraysize(str));
1787 gr_set_color_fast(Multi_join_speed_colors[con_type]);
1788 gr_string(Mj_speed_coords[gr_screen.res][MJ_X_COORD], y_start, str);
1790 // we'll want to have different colors for highlighted items, etc.
1791 if(moveup == Multi_join_selected_item){
1792 gr_set_color_fast(&Color_text_selected);
1794 gr_set_color_fast(&Color_text_normal);
1797 // display the game name, adding appropriate status chars
1799 if(moveup->flags & AG_FLAG_STANDALONE){
1800 SDL_strlcat(str, MJ_CHAR_STANDALONE, SDL_arraysize(str));
1802 if(moveup->flags & AG_FLAG_CAMPAIGN){
1803 SDL_strlcat(str, MJ_CHAR_CAMPAIGN, SDL_arraysize(str));
1806 // tack on the actual server name
1807 SDL_strlcat(str, " ", SDL_arraysize(str));
1808 SDL_strlcat(str, moveup->name, SDL_arraysize(str));
1809 if(strlen(moveup->mission_name) > 0){
1810 SDL_strlcat(str, " / ", SDL_arraysize(str));
1811 SDL_strlcat(str, moveup->mission_name, SDL_arraysize(str));
1814 // make sure the string fits in the display area and draw it
1815 gr_force_fit_string(str,200,Mj_game_name_coords[gr_screen.res][MJ_W_COORD]);
1816 gr_string(Mj_game_name_coords[gr_screen.res][MJ_X_COORD],y_start,str);
1818 // display the ping time
1819 if(moveup->ping.ping_avg > 0){
1820 if(moveup->ping.ping_avg > 1000){
1821 gr_set_color_fast(&Color_bright_red);
1822 SDL_strlcpy(str, XSTR("> 1 sec",761), SDL_arraysize(str));
1824 // set the appropriate ping time color indicator
1825 if(moveup->ping.ping_avg > MJ_PING_YELLOW){
1826 gr_set_color_fast(&Color_bright_red);
1827 } else if(moveup->ping.ping_avg > MJ_PING_YELLOW){
1828 gr_set_color_fast(&Color_bright_yellow);
1830 gr_set_color_fast(&Color_bright_green);
1833 SDL_snprintf(str, SDL_arraysize(str), "%d%s", moveup->ping.ping_avg, XSTR(" ms",762));
1836 gr_string(Mj_ping_coords[gr_screen.res][MJ_X_COORD],y_start,str);
1839 // display the number of players (be sure to center it)
1840 if(moveup == Multi_join_selected_item){
1841 gr_set_color_fast(&Color_text_selected);
1843 gr_set_color_fast(&Color_text_normal);
1845 SDL_snprintf(str, SDL_arraysize(str), "%d", moveup->num_players);
1846 gr_get_string_size(&w,&h,str);
1847 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);
1851 moveup = moveup->next;
1852 } while((moveup != Active_game_head) && (count < Mj_max_game_items[gr_screen.res]));
1854 // if there are no items on the list, display this info
1856 gr_set_color_fast(&Color_bright);
1857 gr_string(Mj_game_name_coords[gr_screen.res][MJ_X_COORD] - 30,y_start,XSTR("<No game servers found>",763));
1861 void multi_join_blit_game_status(active_game *game, int y)
1864 char status_text[25];
1866 // blit the proper icon
1868 switch( game->flags & AG_FLAG_TYPE_MASK ){
1871 if(Multi_common_icons[MICON_COOP] != -1){
1872 gr_set_bitmap(Multi_common_icons[MICON_COOP], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1877 // team vs. team game
1879 if(Multi_common_icons[MICON_TVT] != -1){
1880 gr_set_bitmap(Multi_common_icons[MICON_TVT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1887 case AG_FLAG_DOGFIGHT:
1888 if(Multi_common_icons[MICON_DOGFIGHT] != -1){
1889 gr_set_bitmap(Multi_common_icons[MICON_DOGFIGHT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1895 // if we're supposed to draw a bitmap
1897 gr_bitmap(Mj_game_icon_coords[gr_screen.res][MJ_X_COORD],y-1);
1900 // blit the proper status text
1901 memset(status_text,0,25);
1903 switch( game->flags & AG_FLAG_STATE_MASK ){
1904 case AG_FLAG_FORMING:
1905 gr_set_color_fast(&Color_bright_green);
1906 SDL_strlcpy(status_text, XSTR("Forming", 764), SDL_arraysize(status_text));
1908 case AG_FLAG_BRIEFING:
1909 gr_set_color_fast(&Color_bright_red);
1910 SDL_strlcpy(status_text, XSTR("Briefing", 765), SDL_arraysize(status_text));
1912 case AG_FLAG_DEBRIEF:
1913 gr_set_color_fast(&Color_bright_red);
1914 SDL_strlcpy(status_text, XSTR("Debrief", 766), SDL_arraysize(status_text));
1917 gr_set_color_fast(&Color_bright_red);
1918 SDL_strlcpy(status_text, XSTR("Paused", 767), SDL_arraysize(status_text));
1920 case AG_FLAG_IN_MISSION:
1921 gr_set_color_fast(&Color_bright_red);
1922 SDL_strlcpy(status_text, XSTR("Playing", 768), SDL_arraysize(status_text));
1925 gr_set_color_fast(&Color_bright);
1926 SDL_strlcpy(status_text, XSTR("Unknown", 769), SDL_arraysize(status_text));
1929 gr_get_string_size(&str_w,NULL,status_text);
1930 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);
1933 // load in a list of active games from our tcp.cfg file
1934 void multi_join_load_tcp_addrs()
1936 char line[MAX_IP_STRING];
1941 // attempt to open the ip list file
1942 file = cfopen(IP_CONFIG_FNAME,"rt",CFILE_NORMAL,CF_TYPE_DATA);
1944 nprintf(("Network","Error loading tcp.cfg file!\n"));
1948 // free up any existing server list
1949 multi_free_server_list();
1951 // read in all the strings in the file
1952 while(!cfeof(file)){
1954 cfgets(line,MAX_IP_STRING,file);
1956 // strip off any newline character
1957 if(line[strlen(line) - 1] == '\n'){
1958 line[strlen(line) - 1] = '\0';
1961 // empty lines don't get processed
1962 if( (line[0] == '\0') || (line[0] == '\n') ){
1966 if ( !psnet_is_valid_ip_string(line) ) {
1967 nprintf(("Network","Invalid ip string (%s)\n",line));
1969 // copy the server ip address
1970 memset(&addr,0,sizeof(net_addr));
1971 addr.type = NET_TCP;
1972 psnet_string_to_addr(&addr, line, SDL_arraysize(line));
1973 if ( addr.port == 0 ){
1974 addr.port = DEFAULT_GAME_PORT;
1977 // create a new server item on the list
1978 item = multi_new_server_item();
1980 memcpy(&item->server_addr,&addr,sizeof(net_addr));
1988 // do stuff like pinging servers, sending out requests, etc
1989 void multi_join_do_netstuff()
1991 // handle game query stuff
1992 if(Multi_join_glr_stamp == -1){
1993 broadcast_game_query();
1995 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
1996 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME_LOCAL);
1998 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME);
2001 // otherwise send out game query and restamp
2002 else if(timestamp_elapsed(Multi_join_glr_stamp)){
2003 broadcast_game_query();
2005 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
2006 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME_LOCAL);
2008 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME);
2012 // check to see if we've been accepted. If so, put up message saying so
2013 if ( Net_player->flags & (NETINFO_FLAG_ACCEPT_INGAME|NETINFO_FLAG_ACCEPT_CLIENT|NETINFO_FLAG_ACCEPT_HOST|NETINFO_FLAG_ACCEPT_OBSERVER) ) {
2014 multi_common_add_notify(XSTR("Accepted. Waiting for player data.",770));
2017 // check to see if any join packets we have sent have timed out
2018 if((Multi_join_sent_stamp != -1) && (timestamp_elapsed(Multi_join_sent_stamp))){
2019 Multi_join_sent_stamp = -1;
2020 multi_common_add_notify(XSTR("Join request timed out!",771));
2023 // check to see if we should be pinging everyone
2024 if((Multi_join_ping_stamp == -1) || (timestamp_elapsed(Multi_join_ping_stamp))){
2025 multi_join_ping_all();
2026 Multi_join_ping_stamp = timestamp(MULTI_JOIN_PING_TIME);
2030 multi_join_cull_timeouts();
2033 // evaluate a returned pong.
2034 void multi_join_eval_pong(net_addr *addr, fix pong_time)
2037 active_game *moveup = Active_game_head;
2042 if(psnet_same(&moveup->server_addr,addr)){
2044 multi_ping_eval_pong(&moveup->ping);
2048 moveup = moveup->next;
2050 } while(moveup != Active_game_head);
2053 // update the game's ping
2055 if(found && (moveup->ping_end > moveup->ping_start)){
2056 moveup->ping_time = f2fl(moveup->ping_end - moveup->ping_start);
2057 moveup->ping_start = -1;
2058 moveup->ping_end = -1;
2063 // ping all the server on the list
2064 void multi_join_ping_all()
2066 active_game *moveup = Active_game_head;
2071 moveup->ping_start = timer_get_fixed_seconds();
2072 moveup->ping_end = -1;
2073 send_ping(&moveup->server_addr);
2075 multi_ping_send(&moveup->server_addr,&moveup->ping);
2077 moveup = moveup->next;
2078 } while(moveup != Active_game_head);
2082 void multi_join_process_select()
2084 // if we don't have anything selected and there are items on the list - select the first one
2085 if((Multi_join_list_selected == -1) && (Active_game_count > 0)){
2086 Multi_join_list_selected = 0;
2087 Multi_join_selected_item = multi_join_get_game(0);
2088 MJ_LIST_START_SET(0);
2089 Multi_join_list_start_item = Multi_join_selected_item;
2091 // send a mission description request to this guy
2092 send_netgame_descript_packet(&Multi_join_selected_item->server_addr,0);
2093 multi_common_set_text("");
2095 // I sure hope this doesn't happen
2096 SDL_assert(Multi_join_selected_item != NULL);
2099 // otherwise see if he's clicked on an item
2100 else if(Multi_join_select_button.pressed() && (Active_game_count > 0)){
2102 Multi_join_select_button.get_mouse_pos(NULL,&y);
2104 if(item + Multi_join_list_start < Active_game_count){
2105 gamesnd_play_iface(SND_IFACE_MOUSE_CLICK);
2107 Multi_join_list_selected = item + Multi_join_list_start;
2108 Multi_join_selected_item = multi_join_get_game(Multi_join_list_selected);
2110 // I sure hope this doesn't happen
2111 SDL_assert(Multi_join_selected_item != NULL);
2113 // send a mission description request to this guy
2114 send_netgame_descript_packet(&Multi_join_selected_item->server_addr,0);
2115 multi_common_set_text("");
2119 // if he's double clicked, then select it and accept
2120 if(Multi_join_select_button.double_clicked()){
2122 Multi_join_select_button.get_mouse_pos(NULL,&y);
2124 if(item == Multi_join_list_selected){
2125 multi_join_button_pressed(MJ_ACCEPT);
2130 // return game n (0 based index)
2131 active_game *multi_join_get_game(int n)
2133 active_game *moveup = Active_game_head;
2140 moveup = moveup->next;
2141 while((moveup != Active_game_head) && (count != n)){
2142 moveup = moveup->next;
2145 if(moveup == Active_game_head){
2146 nprintf(("Network","Warning, couldn't find game item %d!\n",n));
2156 // scroll through the game list
2157 void multi_join_list_scroll_up()
2159 // if we're not at the beginning of the list, scroll up
2160 if(Multi_join_list_start_item != Active_game_head){
2161 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2163 MJ_LIST_START_DEC();
2165 gamesnd_play_iface(SND_SCROLL);
2167 gamesnd_play_iface(SND_GENERAL_FAIL);
2171 // scroll through the game list
2172 void multi_join_list_scroll_down()
2174 if((Active_game_count - Multi_join_list_start) > Mj_max_game_items[gr_screen.res]){
2175 Multi_join_list_start_item = Multi_join_list_start_item->next;
2177 MJ_LIST_START_INC();
2179 gamesnd_play_iface(SND_SCROLL);
2181 gamesnd_play_iface(SND_GENERAL_FAIL);
2185 void multi_join_list_page_up()
2187 // in this case, just set us to the beginning of the list
2188 if((Multi_join_list_start - Mj_max_game_items[gr_screen.res]) < 0){
2189 Multi_join_list_start_item = Active_game_head;
2191 MJ_LIST_START_SET(0);
2193 gamesnd_play_iface(SND_SCROLL);
2195 // otherwise page the whole thing up
2197 for(idx=0; idx<Mj_max_game_items[gr_screen.res]; idx++){
2198 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2200 MJ_LIST_START_DEC();
2202 gamesnd_play_iface(SND_SCROLL);
2206 void multi_join_list_page_down()
2210 // page the whole thing down
2211 while((count < Mj_max_game_items[gr_screen.res]) && ((Active_game_count - Multi_join_list_start) > Mj_max_game_items[gr_screen.res])){
2212 Multi_join_list_start_item = Multi_join_list_start_item->next;
2213 MJ_LIST_START_INC();
2218 gamesnd_play_iface(SND_SCROLL);
2221 void multi_join_cull_timeouts()
2223 active_game *backup;
2225 active_game *moveup = Active_game_head;
2227 // traverse through the entire list if any items exist
2231 if((moveup->heard_from_timer != -1) && (timestamp_elapsed(moveup->heard_from_timer))){
2232 Active_game_count--;
2234 // if this is the head of the list
2235 if(moveup == Active_game_head){
2236 // if this is the _only_ item on the list
2237 if(moveup->next == Active_game_head){
2238 // handle any gui details related to deleting this item
2239 multi_join_handle_item_cull(Active_game_head, count);
2241 free(Active_game_head);
2242 Active_game_head = NULL;
2245 // if there are other items on the list
2247 // handle any gui details related to deleting this item
2248 multi_join_handle_item_cull(moveup, count);
2250 Active_game_head = moveup->next;
2251 Active_game_head->prev = moveup->prev;
2252 Active_game_head->prev->next = Active_game_head;
2254 moveup = Active_game_head;
2257 // if its somewhere else on the list
2259 // handle any gui details related to deleting this item
2260 multi_join_handle_item_cull(moveup, count);
2262 // if its the last item on the list
2263 moveup->next->prev = moveup->prev;
2264 moveup->prev->next = moveup->next;
2266 // if it was the last element on the list, return
2267 if(moveup->next == Active_game_head){
2271 backup = moveup->next;
2277 moveup = moveup->next;
2280 } while(moveup != Active_game_head);
2284 // deep magic begins here.
2285 void multi_join_handle_item_cull(active_game *item, int item_index)
2287 // if this is the only item on the list, unset everything
2288 if(item->next == item){
2289 Multi_join_list_selected = -1;
2290 Multi_join_selected_item = NULL;
2293 Multi_join_slider.set_numberItems(0);
2295 MJ_LIST_START_SET(-1);
2296 Multi_join_list_start_item = NULL;
2302 // see if we should be adjusting our currently selected item
2303 if(item_index <= Multi_join_list_selected){
2304 // the selected item is the head of the list
2305 if(Multi_join_selected_item == Active_game_head){
2306 // move the pointer up since this item is about to be destroyed
2307 Multi_join_selected_item = Multi_join_selected_item->next;
2309 // if this is the item being deleted, select the previous one
2310 if(item == Multi_join_selected_item){
2312 Multi_join_selected_item = Multi_join_selected_item->prev;
2314 // decrement the selected index by 1
2315 Multi_join_list_selected--;
2317 // now we know its a previous item, so our pointer stays the same but our index goes down by one, since there will be
2318 // 1 less item on the list
2320 // decrement the selected index by 1
2321 Multi_join_list_selected--;
2326 // see if we should be adjusting out current start position
2327 if(item_index <= Multi_join_list_start){
2328 // the start position is the head of the list
2329 if(Multi_join_list_start_item == Active_game_head){
2330 // move the pointer up since this item is about to be destroyed
2331 Multi_join_list_start_item = Multi_join_list_start_item->next;
2333 // if this is the item being deleted, select the previous one
2334 if(item == Multi_join_list_start_item){
2335 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2337 // decrement the starting index by 1
2338 MJ_LIST_START_DEC();
2340 // but decrement the starting index by 1
2341 MJ_LIST_START_DEC();
2346 // maybe go back up a bit so that we always have a full page of items
2347 if(Active_game_count > Mj_max_game_items[gr_screen.res]){
2348 while((Active_game_count - Multi_join_list_start) < Mj_max_game_items[gr_screen.res]){
2349 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2350 MJ_LIST_START_DEC();
2354 // set slider location
2356 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);
2357 Multi_join_slider.force_currentItem(Multi_join_list_start);
2361 void multi_join_send_join_request(int as_observer)
2363 // don't do anything if we have no items selected
2364 if(Multi_join_selected_item == NULL){
2368 // 5/26/98 -- for team v team games, don't allow ingame joining :-(
2369 if ( (Multi_join_selected_item->flags & AG_FLAG_TEAMS) && (Multi_join_selected_item->flags & (AG_FLAG_PAUSE|AG_FLAG_IN_MISSION)) ) {
2370 popup(0, 1, POPUP_OK, XSTR("Joining ingame is currently not allowed for team vs. team games",772));
2374 memset(&Multi_join_request,0,sizeof(join_request));
2376 // if the netgame is in password mode, put up a request for the password
2377 if(Multi_join_selected_item->flags & AG_FLAG_PASSWD){
2378 if(!multi_passwd_popup(Multi_join_request.passwd, SDL_arraysize(Multi_join_request.passwd))){
2382 nprintf(("Password : %s\n",Multi_join_request.passwd));
2385 // fill out the join request struct
2386 SDL_strlcpy(Multi_join_request.callsign, Player->callsign, SDL_arraysize(Multi_join_request.callsign));
2387 if(strlen(Player->image_filename) > 0){
2388 SDL_strlcpy(Multi_join_request.image_filename, Player->image_filename, SDL_arraysize(Multi_join_request.image_filename));
2391 if(strlen(Player->squad_filename) > 0){
2392 SDL_strlcpy(Multi_join_request.squad_filename, Player->squad_filename, SDL_arraysize(Multi_join_request.squad_filename));
2396 // tracker id (if any)
2397 Multi_join_request.tracker_id = Multi_tracker_id;
2399 // player's rank (at least, what he wants you to _believe_)
2400 Multi_join_request.player_rank = (ubyte)Player->stats.rank;
2403 Multi_join_request.flags = 0;
2405 Multi_join_request.flags |= JOIN_FLAG_AS_OBSERVER;
2408 // if the player has hacked data
2409 if(game_hacked_data()){
2410 Multi_join_request.flags |= JOIN_FLAG_HAXOR;
2415 SDL_strlcpy(Multi_join_request.pxo_squad_name, Multi_tracker_squad_name, LOGIN_LEN);
2418 // version of this server
2419 Multi_join_request.version = MULTI_FS_SERVER_VERSION;
2421 // server compatible version
2422 Multi_join_request.comp_version = MULTI_FS_SERVER_COMPATIBLE_VERSION;
2424 // his local player options
2425 memcpy(&Multi_join_request.player_options,&Player->m_local_options,sizeof(multi_local_options));
2427 // set the server address for the netgame
2428 memcpy(&Netgame.server_addr,&Multi_join_selected_item->server_addr,sizeof(net_addr));
2430 // send a join request to the guy
2431 send_join_packet(&Multi_join_selected_item->server_addr,&Multi_join_request);
2434 Multi_join_sent_stamp = timestamp(MULTI_JOIN_SENT_WAIT);
2437 multi_common_add_notify(XSTR("Sending join request...",773));
2440 void multi_join_create_game()
2442 // maybe warn the player about possible crappy server conditions
2443 if(!multi_join_maybe_warn()){
2447 // make sure to flag ourself as being the master
2448 Net_player->flags |= (NETINFO_FLAG_AM_MASTER | NETINFO_FLAG_GAME_HOST);
2449 Net_player->state = NETPLAYER_STATE_HOST_SETUP;
2451 // if we're in PXO mode, mark it down in our player struct
2452 if(MULTI_IS_TRACKER_GAME){
2453 Player->flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
2454 Player->save_flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
2456 // otherwise, if he's already played PXO games, warn him
2459 if(Player->flags & PLAYER_FLAGS_HAS_PLAYED_PXO){
2460 if(!multi_join_warn_pxo()){
2467 gameseq_post_event(GS_EVENT_MULTI_START_GAME);
2468 gamesnd_play_iface(SND_USER_SELECT);
2471 void multi_join_reset_join_stamp()
2473 // unset the timestamp here so the user can immediately send another join request
2474 Multi_join_sent_stamp = -1;
2475 multi_common_add_notify("");
2478 void multi_join_blit_top_stuff()
2480 // blit the cd icon if he has one
2481 if(Multi_has_cd && (Multi_common_icons[MICON_CD] != -1)){
2484 bm_get_info(Multi_common_icons[MICON_CD], &cd_w, NULL, NULL, NULL, NULL);
2486 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
2487 gr_bitmap((gr_screen.max_w / 2) - (cd_w / 2), Mj_cd_coords[gr_screen.res]);
2491 #define CW_CODE_CANCEL 0 // cancel the action
2492 #define CW_CODE_OK 1 // continue anyway
2493 #define CW_CODE_INFO 2 // gimme some more information
2495 #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)
2496 #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)
2498 #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)
2499 #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)
2501 int multi_join_warn_update_low(int code)
2505 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),LOW_WARN_TEXT);
2508 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),LOW_INFO_TEXT);
2511 return CW_CODE_CANCEL;
2514 int multi_join_warn_update_medium(int code)
2518 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),MED_WARN_TEXT);
2521 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),MED_INFO_TEXT);
2524 return CW_CODE_CANCEL;
2527 int multi_join_maybe_warn()
2531 // if the player is set for low updates
2532 if(Player->m_local_options.obj_update_level == OBJ_UPDATE_LOW){
2535 code = multi_join_warn_update_low(code);
2536 } while((code != CW_CODE_CANCEL) && (code != CW_CODE_OK));
2541 // if the player is set for medium updates
2542 else if(Player->m_local_options.obj_update_level == OBJ_UPDATE_MEDIUM){
2545 code = multi_join_warn_update_medium(code);
2546 } while((code != CW_CODE_CANCEL) && (code != CW_CODE_OK));
2554 int multi_join_warn_pxo()
2556 // 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;
2560 void multi_join_blit_protocol()
2562 gr_set_color_fast(&Color_bright);
2564 switch(Socket_type){
2567 gr_string(5, 2, "TCP");
2576 // -------------------------------------------------------------------------------------------------
2578 // MULTIPLAYER START GAME screen
2583 #define MULTI_SG_PALETTE "InterfacePalette"
2585 static const char *Multi_sg_bitmap_fname[GR_NUM_RESOLUTIONS] = {
2586 "MultiStartGame", // GR_640
2587 "2_MultiStartGame" // GR_1024
2590 static const char *Multi_sg_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
2591 "MultiStartGame-M", // GR_640
2592 "2_MultiStartGame-M" // GR_1024
2597 int Multi_sg_rank_max_display[GR_NUM_RESOLUTIONS] = {
2602 // constants for coordinate look ups
2603 #define MSG_X_COORD 0
2604 #define MSG_Y_COORD 1
2605 #define MSG_W_COORD 2
2606 #define MSG_H_COORD 3
2610 // input password field
2611 int Msg_passwd_coords[GR_NUM_RESOLUTIONS][4] = {
2624 // input game title field
2625 int Msg_title_coords[GR_NUM_RESOLUTIONS][4] = {
2638 // rank selected field
2639 int Msg_rank_sel_coords[GR_NUM_RESOLUTIONS][4] = {
2653 int Msg_rank_list_coords[GR_NUM_RESOLUTIONS][4] = {
2669 #define MULTI_SG_NUM_BUTTONS 12
2670 #define MSG_OPEN_GAME 0
2671 #define MSG_CLOSED_GAME 1
2672 #define MSG_PASSWD_GAME 2
2673 #define MSG_RESTRICTED_GAME 3
2674 #define MSG_RANK_SET_GAME 4
2675 #define MSG_RANK_SCROLL_UP 5
2676 #define MSG_RANK_SCROLL_DOWN 6
2677 #define MSG_RANK_ABOVE 7
2678 #define MSG_RANK_BELOW 8
2680 #define MSG_OPTIONS 10
2681 #define MSG_ACCEPT 11
2683 #define MULTI_SG_NUM_BUTTONS 10
2684 #define MSG_OPEN_GAME 0
2685 //#define MSG_CLOSED_GAME 1
2686 //#define MSG_RESTRICTED_GAME 2
2687 #define MSG_PASSWD_GAME 1
2688 #define MSG_RANK_SET_GAME 2
2689 #define MSG_RANK_SCROLL_UP 3
2690 #define MSG_RANK_SCROLL_DOWN 4
2691 #define MSG_RANK_ABOVE 5
2692 #define MSG_RANK_BELOW 6
2694 #define MSG_OPTIONS 8
2695 #define MSG_ACCEPT 9
2698 UI_WINDOW Multi_sg_window; // the window object for the join screen
2699 UI_BUTTON Multi_sg_rank_button; // for selecting the rank marker
2700 UI_INPUTBOX Multi_sg_game_name; // for Netgame.name
2701 UI_INPUTBOX Multi_sg_game_passwd; // for Netgame.passwd
2702 int Multi_sg_bitmap; // the background bitmap
2704 ui_button_info Multi_sg_buttons[GR_NUM_RESOLUTIONS][MULTI_SG_NUM_BUTTONS] = {
2707 ui_button_info("MSG_00", 75, 111, -1, -1, 0),
2708 ui_button_info("MSG_01", 75, 139, -1, -1, 1),
2709 ui_button_info("MSG_02", 75, 164, -1, -1, 2),
2710 ui_button_info("MSG_03", 75, 199, -1, -1, 3),
2711 ui_button_info("MSG_04", 75, 243, -1, -1, 4),
2712 ui_button_info("MSG_05", 376, 231, -1, -1, 5),
2713 ui_button_info("MSG_06", 376, 258, -1, -1, 6),
2714 ui_button_info("MSG_07", 376, 291, -1, -1, 7),
2715 ui_button_info("MSG_08", 376, 320, -1, -1, 8),
2716 ui_button_info("MSG_09", 469, 427, -1, -1, 9),
2717 ui_button_info("MSG_10", 447, 452, -1, -1, 10),
2718 ui_button_info("MSG_11", 561, 411, -1, -1, 11),
2720 ui_button_info("MSG_00", 1, 184, 34, 191, 2), // open
2721 // ui_button_info("MSG_01", 1, 159, 34, 166, 1), // closed
2722 // ui_button_info("MSG_02", 1, 184, 34, 191, 2), // restricted
2723 ui_button_info("MSG_03", 1, 209, 34, 218, 3), // password
2724 ui_button_info("MSG_04", 1, 257, 34, 266, 4), // rank set
2725 ui_button_info("MSG_05", 1, 282, -1, -1, 5), // rank scroll up
2726 ui_button_info("MSG_06", 1, 307, -1, -1, 6), // rank scroll down
2727 ui_button_info("MSG_07", 177, 282, 210, 290, 7), // rank above
2728 ui_button_info("MSG_08", 177, 307, 210, 315, 8), // rank below
2729 ui_button_info("MSG_09", 536, 429, 500, 440, 9), // help
2730 ui_button_info("MSG_10", 536, 454, 479, 464, 10), // options
2731 ui_button_info("MSG_11", 576, 432, 571, 415, 11), // accept
2735 ui_button_info("2_MSG_00", 2, 295, 51, 307, 2), // open
2736 // ui_button_info("2_MSG_01", 2, 254, 51, 267, 1), // closed
2737 // ui_button_info("2_MSG_02", 2, 295, 51, 307, 2), // restricted
2738 ui_button_info("2_MSG_03", 2, 335, 51, 350, 3), // password
2739 ui_button_info("2_MSG_04", 2, 412, 51, 426, 4), // rank set
2740 ui_button_info("2_MSG_05", 2, 452, -1, -1, 5), // rank scroll up
2741 ui_button_info("2_MSG_06", 2, 492, -1, -1, 6), // rank scroll down
2742 ui_button_info("2_MSG_07", 284, 452, 335, 465, 7), // rank above
2743 ui_button_info("2_MSG_08", 284, 492, 335, 505, 8), // rank below
2744 ui_button_info("2_MSG_09", 858, 687, 817, 706, 9), // help
2745 ui_button_info("2_MSG_10", 858, 728, 797, 743, 10), // options
2746 ui_button_info("2_MSG_11", 921, 692, 921, 664, 11), // accept
2747 #ifdef MAKE_FS1 // filler for extra FS1 buttons
2748 ui_button_info("none", -1, -1, -1, -1, -1),
2749 ui_button_info("none", -1, -1, -1, -1, -1),
2755 #define MULTI_SG_NUM_TEXT 11
2757 UI_XSTR Multi_sg_text[GR_NUM_RESOLUTIONS][MULTI_SG_NUM_TEXT] = {
2759 {"Open", 1322, 34, 191, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_OPEN_GAME].button},
2760 // {"Closed", 1323, 34, 166, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_CLOSED_GAME].button},
2761 // {"Restricted", 1324, 34, 191, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RESTRICTED_GAME].button},
2762 {"Password Protected", 1325, 34, 218, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_PASSWD_GAME].button},
2763 {"Allow Rank", 1326, 34, 266, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_SET_GAME].button},
2764 {"Above", 1327, 210, 290, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_ABOVE].button},
2765 {"Below", 1328, 210, 315, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_BELOW].button},
2766 {"Help", 928, 500, 440, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_HELP].button},
2767 {"Options", 1036, 479, 464, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_OPTIONS].button},
2768 {"Accept", 1035, 571, 415, UI_XSTR_COLOR_PINK, -1, &Multi_sg_buttons[0][MSG_ACCEPT].button},
2769 {"Start Game", 1329, 26, 10, UI_XSTR_COLOR_GREEN, -1, NULL},
2770 {"Title", 1330, 26, 31, UI_XSTR_COLOR_GREEN, -1, NULL},
2771 {"Game Type", 1331, 12, 165, UI_XSTR_COLOR_GREEN, -1, NULL},
2774 {"Open", 1322, 51, 307, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_OPEN_GAME].button},
2775 // {"Closed", 1323, 51, 267, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_CLOSED_GAME].button},
2776 // {"Restricted", 1324, 51, 307, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RESTRICTED_GAME].button},
2777 {"Password Protected", 1325, 51, 350, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_PASSWD_GAME].button},
2778 {"Allow Rank", 1326, 51, 426, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_SET_GAME].button},
2779 {"Above", 1327, 335, 465, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_ABOVE].button},
2780 {"Below", 1328, 335, 505, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_BELOW].button},
2781 {"Help", 928, 817, 706, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_HELP].button},
2782 {"Options", 1036, 797, 743, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_OPTIONS].button},
2783 {"Accept", 1035, 921, 664, UI_XSTR_COLOR_PINK, -1, &Multi_sg_buttons[1][MSG_ACCEPT].button},
2784 {"Start Game", 1329, 42, 22, UI_XSTR_COLOR_GREEN, -1, NULL},
2785 {"Title", 1330, 42, 50, UI_XSTR_COLOR_GREEN, -1, NULL},
2786 {"Game Type", 1331, 20, 264, UI_XSTR_COLOR_GREEN, -1, NULL},
2791 // starting index for displaying ranks
2792 int Multi_sg_rank_start;
2793 int Multi_sg_rank_select;
2795 // netgame pointer to indirect through
2796 netgame_info *Multi_sg_netgame;
2798 // hold temporary values in this structure when on a standalone server
2799 netgame_info Multi_sg_netgame_temp;
2801 // forward declarations
2802 void multi_sg_check_buttons();
2803 void multi_sg_button_pressed(int n);
2804 void multi_sg_init_gamenet();
2805 void multi_sg_draw_radio_buttons();
2806 void multi_sg_rank_scroll_up();
2807 void multi_sg_rank_scroll_down();
2808 void multi_sg_rank_display_stuff();
2809 void multi_sg_rank_process_select();
2810 void multi_sg_rank_build_name(char *in, char *out, const int max_outlen);
2811 void multi_sg_check_passwd();
2812 void multi_sg_check_name();
2813 void multi_sg_release_passwd();
2814 int multi_sg_rank_select_valid(int rank);
2815 void multi_sg_select_rank_default();
2817 // function which takes a rank name and returns the index. Useful for commandline options
2818 // for above and below rank. We return the index of the rank in the Ranks[] array. If
2819 // the rank isn't found, we return -1
2820 int multi_start_game_rank_from_name( char *rank ) {
2824 for ( i = 0; i <= MAX_FREESPACE1_RANK; i++ ) {
2826 for ( i = 0; i <= MAX_FREESPACE2_RANK; i++ ) {
2828 if ( !SDL_strcasecmp(Ranks[i].name, rank) ) {
2836 void multi_start_game_init()
2840 // initialize the gamenet
2841 multi_sg_init_gamenet();
2843 // create the interface window
2844 Multi_sg_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
2845 Multi_sg_window.set_mask_bmap(Multi_sg_bitmap_mask_fname[gr_screen.res]);
2847 // load the background bitmap
2848 Multi_sg_bitmap = bm_load(Multi_sg_bitmap_fname[gr_screen.res]);
2849 if(Multi_sg_bitmap < 0){
2850 // we failed to load the bitmap - this is very bad
2854 // initialize the common notification messaging
2855 multi_common_notify_init();
2857 // initialize the common text area
2858 multi_common_set_text("");
2860 // use the common interface palette
2861 multi_common_set_palette();
2863 // create the interface buttons
2864 for(idx=0; idx<MULTI_SG_NUM_BUTTONS; idx++){
2865 // create the object
2866 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);
2868 // set the sound to play when highlighted
2869 Multi_sg_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
2871 // set the ani for the button
2872 Multi_sg_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sg_buttons[gr_screen.res][idx].filename);
2875 Multi_sg_buttons[gr_screen.res][idx].button.link_hotspot(Multi_sg_buttons[gr_screen.res][idx].hotspot);
2880 for(idx=0; idx<MULTI_SG_NUM_TEXT; idx++){
2881 Multi_sg_window.add_XSTR(&Multi_sg_text[gr_screen.res][idx]);
2885 // load the help overlay
2886 help_overlay_load(MULTI_START_OVERLAY);
2887 help_overlay_set_state(MULTI_START_OVERLAY,0);
2889 // intiialize the rank selection items
2890 multi_sg_select_rank_default();
2891 Multi_sg_rank_start = Multi_sg_rank_select;
2893 // create the rank select button
2894 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);
2895 Multi_sg_rank_button.hide();
2897 // create the netgame name input box
2898 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);
2900 // create the netgame password input box, and disable it by default
2901 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);
2902 Multi_sg_game_passwd.hide();
2903 Multi_sg_game_passwd.disable();
2905 // set the netgame text to this gadget and make it have focus
2906 Multi_sg_game_name.set_text(Multi_sg_netgame->name);
2907 Multi_sg_game_name.set_focus();
2909 // if starting a netgame, set the name of the game and any other options that are appropriate
2910 if ( Cmdline_start_netgame ) {
2911 if ( Cmdline_game_name != NULL ) {
2912 SDL_strlcpy( Multi_sg_netgame->name, Cmdline_game_name, SDL_arraysize(Multi_sg_netgame->name) );
2913 Multi_sg_game_name.set_text(Multi_sg_netgame->name);
2916 // deal with the different game types -- only one should even be active, so we will just go down
2917 // the line. Last one wins.
2918 if ( Cmdline_closed_game ) {
2919 Multi_sg_netgame->mode = NG_MODE_CLOSED;
2920 } else if ( Cmdline_restricted_game ) {
2921 Multi_sg_netgame->mode = NG_MODE_RESTRICTED;
2922 } else if ( Cmdline_game_password != NULL ) {
2923 Multi_sg_netgame->mode = NG_MODE_PASSWORD;
2924 SDL_strlcpy(Multi_sg_netgame->passwd, Cmdline_game_password, SDL_arraysize(Multi_sg_netgame->passwd));
2925 Multi_sg_game_passwd.set_text(Multi_sg_netgame->passwd);
2928 // deal with rank above and rank below
2929 if ( (Cmdline_rank_above != NULL) || (Cmdline_rank_below != NULL) ) {
2933 if ( Cmdline_rank_above != NULL ) {
2934 rank_str = Cmdline_rank_above;
2936 rank_str = Cmdline_rank_below;
2939 // try and get the rank index from the name -- if found, then set the rank base
2940 // and the game type. apparently we only support either above or below, not both
2941 // together, so I make a random choice
2942 rank = multi_start_game_rank_from_name( rank_str );
2944 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
2946 // now an arbitrary decision
2947 if ( Cmdline_rank_above != NULL ) {
2948 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
2950 Multi_sg_netgame->mode = NG_MODE_RANK_BELOW;
2955 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
2958 if ( multi_fs_tracker_inited() ) {
2959 multi_fs_tracker_login_freespace();
2963 void multi_start_game_do()
2965 // return here since we will be moving to the next stage anyway -- I don't want to see the backgrounds of
2966 // all the screens for < 1 second for every screen we automatically move to.
2967 if ( Cmdline_start_netgame ) {
2971 int k = Multi_sg_window.process();
2973 // process any keypresses
2976 if(help_overlay_active(MULTI_START_OVERLAY)){
2977 help_overlay_set_state(MULTI_START_OVERLAY,0);
2979 gamesnd_play_iface(SND_USER_SELECT);
2980 multi_quit_game(PROMPT_NONE);
2985 case SDLK_LCTRL + SDLK_RETURN :
2986 case SDLK_RCTRL + SDLK_RETURN :
2987 gamesnd_play_iface(SND_COMMIT_PRESSED);
2988 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
2992 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
2993 help_overlay_set_state(MULTI_START_OVERLAY, 0);
2996 // check to see if the user has selected a different rank
2997 multi_sg_rank_process_select();
2999 // check any button presses
3000 multi_sg_check_buttons();
3002 // check to see if any of the input boxes have changed, and update the appropriate Netgame fields if necessary
3003 multi_sg_check_passwd();
3004 multi_sg_check_name();
3006 // draw the background, etc
3008 GR_MAYBE_CLEAR_RES(Multi_sg_bitmap);
3009 if(Multi_sg_bitmap != -1){
3010 gr_set_bitmap(Multi_sg_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
3013 Multi_sg_window.draw();
3015 // display rank stuff
3016 multi_sg_rank_display_stuff();
3018 // display any pending notification messages
3019 multi_common_notify_do();
3021 // draw all radio button
3022 multi_sg_draw_radio_buttons();
3024 // draw the help overlay
3025 help_overlay_maybe_blit(MULTI_START_OVERLAY);
3031 void multi_start_game_close()
3033 // if i'm the host on a standalone server, send him my start game options (passwd, mode, etc)
3034 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
3035 multi_options_update_start_game(Multi_sg_netgame);
3038 // unload any bitmaps
3039 if(!bm_unload(Multi_sg_bitmap)){
3040 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_sg_bitmap_fname[gr_screen.res]));
3043 // unload the help overlay
3044 help_overlay_unload(MULTI_START_OVERLAY);
3046 // destroy the UI_WINDOW
3047 Multi_sg_window.destroy();
3050 void multi_sg_check_buttons()
3053 for(idx=0;idx<MULTI_SG_NUM_BUTTONS;idx++){
3054 // we only really need to check for one button pressed at a time, so we can break after
3056 if(Multi_sg_buttons[gr_screen.res][idx].button.pressed()){
3057 multi_sg_button_pressed(idx);
3063 void multi_sg_button_pressed(int n)
3066 // go to the options screen
3068 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
3073 if(!help_overlay_active(MULTI_START_OVERLAY)){
3074 help_overlay_set_state(MULTI_START_OVERLAY,1);
3076 help_overlay_set_state(MULTI_START_OVERLAY,0);
3080 // the open button was pressed
3082 // if the closed option is selected
3083 if(Multi_sg_netgame->mode != NG_MODE_OPEN){
3084 Multi_sg_netgame->mode = NG_MODE_OPEN;
3086 gamesnd_play_iface(SND_USER_SELECT);
3088 // release the password control if necessary
3089 multi_sg_release_passwd();
3091 // if its already selected
3093 gamesnd_play_iface(SND_GENERAL_FAIL);
3098 // the open button was pressed
3099 case MSG_CLOSED_GAME:
3100 // if the closed option is selected
3101 if(Multi_sg_netgame->mode != NG_MODE_CLOSED){
3102 Multi_sg_netgame->mode = NG_MODE_CLOSED;
3104 gamesnd_play_iface(SND_USER_SELECT);
3106 // release the password control if necessary
3107 multi_sg_release_passwd();
3109 // if its already selected
3111 gamesnd_play_iface(SND_GENERAL_FAIL);
3116 // toggle password protection
3117 case MSG_PASSWD_GAME:
3118 // if we selected it
3119 if(Multi_sg_netgame->mode != NG_MODE_PASSWORD){
3120 gamesnd_play_iface(SND_USER_SELECT);
3122 Multi_sg_game_passwd.enable();
3123 Multi_sg_game_passwd.unhide();
3124 Multi_sg_game_passwd.set_focus();
3126 Multi_sg_netgame->mode = NG_MODE_PASSWORD;
3128 // copy in the current network password
3129 Multi_sg_game_passwd.set_text(Multi_sg_netgame->passwd);
3131 gamesnd_play_iface(SND_GENERAL_FAIL);
3136 // toggle "restricted" on or off
3137 case MSG_RESTRICTED_GAME:
3138 if(Multi_sg_netgame->mode != NG_MODE_RESTRICTED){
3139 gamesnd_play_iface(SND_USER_SELECT);
3140 Multi_sg_netgame->mode = NG_MODE_RESTRICTED;
3142 // release the password control if necessary
3143 multi_sg_release_passwd();
3145 gamesnd_play_iface(SND_GENERAL_FAIL);
3150 // turn off all rank requirements
3151 case MSG_RANK_SET_GAME:
3152 // if either is set, then turn then both off
3153 if((Multi_sg_netgame->mode != NG_MODE_RANK_BELOW) && (Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE)){
3154 gamesnd_play_iface(SND_USER_SELECT);
3156 // set it to the default case if we're turning it off
3157 multi_sg_select_rank_default();
3158 Multi_sg_rank_start = Multi_sg_rank_select;
3160 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
3162 // release the password control if necessary
3163 multi_sg_release_passwd();
3165 gamesnd_play_iface(SND_GENERAL_FAIL);
3169 // rank above was pressed
3170 case MSG_RANK_ABOVE :
3171 if((Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE) || (Multi_sg_netgame->mode == NG_MODE_RANK_BELOW)){
3172 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
3174 // select the first item
3175 multi_sg_select_rank_default();
3176 Multi_sg_rank_start = Multi_sg_rank_select;
3179 gamesnd_play_iface(SND_USER_SELECT);
3181 gamesnd_play_iface(SND_GENERAL_FAIL);
3185 // rank below was pressed
3186 case MSG_RANK_BELOW :
3187 if((Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE) || (Multi_sg_netgame->mode == NG_MODE_RANK_BELOW)){
3188 Multi_sg_netgame->mode = NG_MODE_RANK_BELOW;
3190 // select the first item
3191 multi_sg_select_rank_default();
3192 Multi_sg_rank_start = Multi_sg_rank_select;
3195 gamesnd_play_iface(SND_USER_SELECT);
3197 gamesnd_play_iface(SND_GENERAL_FAIL);
3201 // scroll the rank list up
3202 case MSG_RANK_SCROLL_UP:
3203 multi_sg_rank_scroll_up();
3206 // scroll the rank list down
3207 case MSG_RANK_SCROLL_DOWN:
3208 multi_sg_rank_scroll_down();
3211 // move to the create game screen
3213 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
3214 gamesnd_play_iface(SND_COMMIT_PRESSED);
3218 gamesnd_play_iface(SND_GENERAL_FAIL);
3219 multi_common_add_notify(XSTR("Not implemented yet!",760));
3224 // NOTE : this is where all Netgame initialization should take place on the host
3225 void multi_sg_init_gamenet()
3227 char buf[128],out_name[128];
3229 net_player *server_save;
3231 // back this data up in case we are already connected to a standalone
3232 memcpy(&save,&Netgame.server_addr,sizeof(net_addr));
3233 server_save = Netgame.server;
3235 // remove campaign flags
3236 Game_mode &= ~(GM_CAMPAIGN_MODE);
3238 // clear out the Netgame structure and start filling in the values
3239 memset( &Netgame, 0, sizeof(Netgame) );
3240 memset( &Multi_sg_netgame_temp, 0, sizeof(netgame_info) );
3242 // if we're on the standalone, we're not the server, so we don't care about setting the netgame state
3243 if(Net_player->state != NETPLAYER_STATE_STD_HOST_SETUP){
3244 Netgame.game_state = NETGAME_STATE_HOST_SETUP;
3245 Multi_sg_netgame = &Netgame;
3248 ml_string(NOX("Starting netgame as Host/Server"));
3250 Multi_sg_netgame = &Multi_sg_netgame_temp;
3254 memcpy(&temp_addr.s_addr, &Netgame.server_addr, 4);
3255 char *server_addr = inet_ntoa(temp_addr);
3256 ml_printf(NOX("Starting netgame as Host on Standalone server : %s"), (server_addr == NULL) ? NOX("Unknown") : server_addr);
3259 Net_player->tracker_player_id = Multi_tracker_id;
3261 Multi_sg_netgame->security = (rand() % 32766) + 1; // get some random security number
3262 Multi_sg_netgame->mode = NG_MODE_OPEN;
3263 Multi_sg_netgame->rank_base = RANK_ENSIGN;
3264 if(Multi_sg_netgame->security < 16){
3265 Multi_sg_netgame->security += 16;
3268 // set the version_info field
3269 Multi_sg_netgame->version_info = NG_VERSION_ID;
3271 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
3272 Netgame.host = Net_player;
3275 // set the default netgame flags
3276 Multi_sg_netgame->flags = 0;
3278 // intialize endgame stuff
3279 multi_endgame_init();
3281 // load in my netgame options
3282 multi_options_netgame_load(&Netgame.options);
3284 // load my local netplayer options
3285 multi_options_local_load(&Net_player->p_info.options, Net_player);
3287 // setup the default game name, taking care of string length and player callsigns
3288 memset(out_name,0,128);
3290 pilot_format_callsign_personal(Player->callsign, out_name, SDL_arraysize(out_name));
3291 SDL_snprintf(buf, SDL_arraysize(buf), XSTR("%s game",782), out_name); // [[ %s will be a pilot's name ]]
3292 if ( strlen(buf) > MAX_GAMENAME_LEN ){
3293 SDL_strlcpy(buf, XSTR("Temporary name",783), SDL_arraysize(buf));
3295 SDL_strlcpy(Multi_sg_netgame->name, buf, SDL_arraysize(Multi_sg_netgame->name));
3297 // set the default qos and duration
3298 multi_voice_maybe_update_vars(Netgame.options.voice_qos,Netgame.options.voice_record_time);
3300 // make sure to set the server correctly (me or the standalone)
3301 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3302 memcpy(&Netgame.server_addr, &Psnet_my_addr,sizeof(net_addr));
3303 Netgame.server = Net_player;
3304 Net_player->player_id = multi_get_new_id();
3306 // setup debug flags
3307 Netgame.debug_flags = 0;
3309 if(!Cmdline_server_firing){
3310 Netgame.debug_flags |= NETD_FLAG_CLIENT_FIRING;
3312 if(!Cmdline_client_dodamage){
3313 Netgame.debug_flags |= NETD_FLAG_CLIENT_NODAMAGE;
3317 memcpy(&Netgame.server_addr,&save,sizeof(net_addr));
3318 Netgame.server = server_save;
3321 // if I have a cd or not
3323 Net_player->flags |= NETINFO_FLAG_HAS_CD;
3326 // if I have hacked data
3327 if(game_hacked_data()){
3328 Net_player->flags |= NETINFO_FLAG_HAXOR;
3331 // assign my player struct and other data
3332 Net_player->flags |= (NETINFO_FLAG_CONNECTED | NETINFO_FLAG_DO_NETWORKING);
3333 Net_player->s_info.voice_token_timestamp = -1;
3335 // if we're supposed to flush our cache directory, do so now
3336 if(Net_player->p_info.options.flags & MLO_FLAG_FLUSH_CACHE){
3337 multi_flush_multidata_cache();
3340 ml_string(NOX("Flushing multi-data cache"));
3346 void multi_sg_draw_radio_buttons()
3348 // draw the appropriate radio button
3349 switch(Multi_sg_netgame->mode){
3351 Multi_sg_buttons[gr_screen.res][MSG_OPEN_GAME].button.draw_forced(2);
3355 case NG_MODE_CLOSED:
3356 Multi_sg_buttons[gr_screen.res][MSG_CLOSED_GAME].button.draw_forced(2);
3360 case NG_MODE_PASSWORD:
3361 Multi_sg_buttons[gr_screen.res][MSG_PASSWD_GAME].button.draw_forced(2);
3365 case NG_MODE_RESTRICTED:
3366 Multi_sg_buttons[gr_screen.res][MSG_RESTRICTED_GAME].button.draw_forced(2);
3370 case NG_MODE_RANK_ABOVE:
3371 Multi_sg_buttons[gr_screen.res][MSG_RANK_SET_GAME].button.draw_forced(2);
3372 Multi_sg_buttons[gr_screen.res][MSG_RANK_ABOVE].button.draw_forced(2);
3374 case NG_MODE_RANK_BELOW:
3375 Multi_sg_buttons[gr_screen.res][MSG_RANK_SET_GAME].button.draw_forced(2);
3376 Multi_sg_buttons[gr_screen.res][MSG_RANK_BELOW].button.draw_forced(2);
3381 void multi_sg_rank_scroll_up()
3383 // if he doesn't have either of the rank flags set, then ignore this
3384 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3388 if(Multi_sg_rank_start > 0){
3389 Multi_sg_rank_start--;
3390 gamesnd_play_iface(SND_SCROLL);
3392 gamesnd_play_iface(SND_GENERAL_FAIL);
3396 void multi_sg_rank_scroll_down()
3398 // if he doesn't have either of the rank flags set, then ignore this
3399 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3403 if((NUM_RANKS - Multi_sg_rank_start) > Multi_sg_rank_max_display[gr_screen.res]){
3404 Multi_sg_rank_start++;
3405 gamesnd_play_iface(SND_SCROLL);
3407 gamesnd_play_iface(SND_GENERAL_FAIL);
3411 void multi_sg_rank_display_stuff()
3416 // if he doesn't have either of the rank flags set, then ignore this
3417 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3421 // display the list of ranks
3422 y = Msg_rank_list_coords[gr_screen.res][MSG_Y_COORD];
3423 idx = Multi_sg_rank_start;
3425 while((count < NUM_RANKS) && (count < Multi_sg_rank_max_display[gr_screen.res]) && (idx < NUM_RANKS)){
3426 // if its the selected item, then color it differently
3427 if(idx == Multi_sg_rank_select){
3428 gr_set_color_fast(&Color_text_selected);
3430 gr_set_color_fast(&Color_text_normal);
3434 multi_sg_rank_build_name(Ranks[idx].name, rank_name, sizeof(rank_name));
3435 gr_string(Msg_rank_list_coords[gr_screen.res][MSG_X_COORD],y,rank_name);
3443 // display the selected rank
3445 gr_set_color_fast(&Color_bright);
3446 multi_sg_rank_build_name(Ranks[Multi_sg_netgame->rank_base].name, rank_name, SDL_arraysize(rank_name));
3447 gr_string(Msg_rank_sel_coords[gr_screen.res][MSG_X_COORD],Msg_rank_sel_coords[gr_screen.res][MSG_Y_COORD],rank_name);
3451 void multi_sg_rank_process_select()
3455 // if he doesn't have either of the rank flags set, then ignore this
3456 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3460 // see if he's clicked on an item on the rank list
3461 if(Multi_sg_rank_button.pressed()){
3463 Multi_sg_rank_button.get_mouse_pos(NULL,&y);
3466 if(item + Multi_sg_rank_start < NUM_RANKS){
3467 // evaluate whether this rank is valid for the guy to pick
3468 if(multi_sg_rank_select_valid(item + Multi_sg_rank_start)){
3469 gamesnd_play_iface(SND_USER_SELECT);
3471 Multi_sg_rank_select = item + Multi_sg_rank_start;
3473 // set the Netgame rank
3474 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
3476 gamesnd_play_iface(SND_GENERAL_FAIL);
3478 memset(string,0,255);
3479 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);
3480 multi_common_add_notify(string);
3486 void multi_sg_rank_build_name(char *in, char *out, const int max_outlen)
3491 SDL_strlcpy(use, in, SDL_arraysize(use));
3492 first = strtok(use," ");
3494 // just copy the string
3496 SDL_strlcpy(out, in, max_outlen);
3499 // if the first part of the string is lieutenant, then abbreivate it and tack on the rest of the string
3500 if (SDL_strcasecmp(first,XSTR("lieutenant",785)) == 0) {
3501 first = strtok(NULL, NOX("\n"));
3503 // if he's not just a plain lieutenant
3505 SDL_snprintf(out, max_outlen, "%s%s", XSTR("Lt. ",786), first); // [[ lieutenant ]]
3507 // if he _is_ just a plain lieutenant
3509 SDL_strlcpy(out, in, max_outlen);
3512 SDL_strlcpy(out, in, max_outlen);
3516 void multi_sg_check_passwd()
3518 // check to see if the password input box has been pressed
3519 if(Multi_sg_game_passwd.changed()){
3520 Multi_sg_game_passwd.get_text(Multi_sg_netgame->passwd);
3524 void multi_sg_check_name()
3526 // check to see if the game name input box has been pressed
3527 if(Multi_sg_game_name.changed()){
3528 Multi_sg_game_name.get_text(Multi_sg_netgame->name);
3532 void multi_sg_release_passwd()
3534 // hide and disable the password input box
3535 Multi_sg_game_passwd.hide();
3536 Multi_sg_game_passwd.disable();
3538 // set the focus back to the name input box
3539 Multi_sg_game_name.set_focus();
3542 int multi_sg_rank_select_valid(int rank)
3545 if(Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE){
3546 if(Net_player->player->stats.rank >= rank){
3552 if(Net_player->player->stats.rank <= rank){
3560 void multi_sg_select_rank_default()
3562 // pick our rank for now
3563 Multi_sg_rank_select = Net_player->player->stats.rank;
3565 // set the Netgame rank
3566 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
3569 // -------------------------------------------------------------------------------------------------
3571 // MULTIPLAYER CREATE GAME screen
3576 const char *Multi_create_bitmap_fname[GR_NUM_RESOLUTIONS] = {
3577 "MultiCreate", // GR_640
3578 "2_MultiCreate" // GR_1024
3581 const char *Multi_create_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
3582 "MultiCreate-M", // GR_640
3583 "2_MultiCreate-M" // GR_1024
3586 const char *Multi_create_loading_fname[GR_NUM_RESOLUTIONS] = {
3587 "PleaseWait", // GR_640
3588 "2_PleaseWait" // GR_1024
3592 #define MULTI_CREATE_NUM_BUTTONS 23
3595 #define MC_SHOW_ALL 0
3596 #define MC_SHOW_COOP 1
3597 #define MC_SHOW_TEAM 2
3598 #define MC_SHOW_DOGFIGHT 3
3599 #define MC_PXO_REFRESH 4
3600 #define MC_PILOT_INFO 5
3601 #define MC_SCROLL_LIST_UP 6
3602 #define MC_SCROLL_LIST_DOWN 7
3603 #define MC_SCROLL_PLAYERS_UP 8
3604 #define MC_SCROLL_PLAYERS_DOWN 9
3605 #define MC_MISSION_FILTER 10
3606 #define MC_CAMPAIGN_FILTER 11
3607 #define MC_CANCEL 12
3612 #define MC_SCROLL_INFO_UP 17
3613 #define MC_SCROLL_INFO_DOWN 18
3614 #define MC_HOST_OPTIONS 19
3616 #define MC_OPTIONS 21
3617 #define MC_ACCEPT 22
3620 UI_WINDOW Multi_create_window; // the window object for the create screen
3621 UI_BUTTON Multi_create_player_select_button; // for selecting players
3622 UI_BUTTON Multi_create_list_select_button; // for selecting missions/campaigns
3623 int Multi_create_bitmap; // the background bitmap
3624 UI_SLIDER2 Multi_create_slider; // for create list
3626 // constants for coordinate look ups
3627 #define MC_X_COORD 0
3628 #define MC_Y_COORD 1
3629 #define MC_W_COORD 2
3630 #define MC_H_COORD 3
3632 ui_button_info Multi_create_buttons[GR_NUM_RESOLUTIONS][MULTI_CREATE_NUM_BUTTONS] = {
3635 ui_button_info("MC_18", 34, 131, -1, -1, 18), // all
3636 ui_button_info("MC_19", 72, 131, -1, -1, 19), // coop
3637 ui_button_info("MC_20", 120, 131, -1, -1, 20), // team
3638 // ui_button_info("MC_21", 166, 131, -1, -1, 21), // dogfight
3639 ui_button_info("none", -1, -1, -1, -1, -1), // dogfight (not used)
3640 ui_button_info("none", -1, -1, -1, -1, -1), // pxo?
3641 ui_button_info("MC_26", 540, 114, -1, -1, 26), // pilot info
3642 ui_button_info("MC_03", 0, 187, -1, -1, 2), // scroll list up
3643 ui_button_info("MC_02", 0, 227, -1, -1, 3), // scroll list down
3644 ui_button_info("MC_04", 611, 182, -1, -1, 4), // scroll players up
3645 ui_button_info("MC_05", 611, 221, -1, -1, 5), // scroll players down
3646 ui_button_info("MC_06", 18, 322, -1, -1, 6), // mission filter
3647 ui_button_info("MC_07", 18, 344, -1, -1, 7), // campaign filter
3648 ui_button_info("MC_10", 317, 339, -1, -1, 10), // cancel
3649 ui_button_info("MC_14", 464, 350, -1, -1, 14), // team 1
3650 ui_button_info("MC_15", 498, 350, -1, -1, 15), // team 2
3651 ui_button_info("MC_16", 527, 346, -1, -1, 16), // kick
3652 ui_button_info("MC_17", 572, 346, -1, -1, 17), // close
3653 ui_button_info("MC_08", 0, 398, -1, -1, 8), // scroll mission info up
3654 ui_button_info("MC_09", 0, 435, -1, -1, 9), // scroll mission info down
3655 ui_button_info("MC_27", 447, 402, -1, -1, 27), // host options
3656 ui_button_info("MC_11", 510, 428, -1, -1, 11), // help
3657 ui_button_info("MC_12", 510, 453, -1, -1, 12), // options
3658 ui_button_info("Mc_13", 562, 412, -1, -1, 13), // commit
3660 ui_button_info("MC_00", 32, 129, 36, 158, 0), // show all missions
3661 ui_button_info("MC_01", 76, 129, 71, 158, 1), // show coop missions
3662 ui_button_info("MC_02", 121, 129, 119, 158, 2), // show team missions
3663 ui_button_info("MC_03", 164, 129, 166, 158, 3), // show dogfight missions
3664 ui_button_info("MC_04", 399, 129, 229, 130, 4), // pxo mission refresh
3665 ui_button_info("MC_05", 567, 123, 467, 132, 5), // pilot info
3666 ui_button_info("MC_06", 1, 161, -1, -1, 6), // scroll mission info up
3667 ui_button_info("MC_08", 1, 304, -1, -1, 8), // scroll mission info down
3668 ui_button_info("MC_09", 613, 160, -1, -1, 9), // scroll players up
3669 ui_button_info("MC_10", 613, 202, -1, -1, 10), // scroll players down
3670 ui_button_info("MC_11", 22, 346, 27, 376, 11), // mission filter
3671 ui_button_info("MC_12", 104, 346, 110, 376, 12), // campaign filter
3672 ui_button_info("MC_13", 392, 341, 328, 364, 13), // cancel
3673 ui_button_info("MC_14", 472, 352, 482, 381, 14), // team 0
3674 ui_button_info("MC_15", 506, 352, 514, 381, 15), // team 1
3675 ui_button_info("MC_16", 539, 346, 539, 381, 16), // kick
3676 ui_button_info("MC_17", 589, 346, 582, 381, 17), // close
3677 ui_button_info("MC_18", 1, 406, -1, -1, 18), // scroll list up
3678 ui_button_info("MC_19", 1, 447, -1, -1, 19), // scroll list down
3679 ui_button_info("MC_20", 499, 434, 436, 423, 20), // host options
3680 ui_button_info("MC_21", 534, 426, -1, -1, 21), // help
3681 ui_button_info("MC_22", 534, 452, -1, -1, 22), // options
3682 ui_button_info("MC_23", 571, 426, 572, 413, 23), // commit
3686 ui_button_info("2_MC_00", 51, 207, 61, 253, 0), // show all missions
3687 ui_button_info("2_MC_01", 122, 207, 124, 253, 1), // show coop missions
3688 ui_button_info("2_MC_02", 193, 207, 194, 253, 2), // show team missions
3689 ui_button_info("2_MC_03", 263, 207, 261, 253, 3), // show dogfight missions
3690 ui_button_info("2_MC_04", 639, 207, 479, 218, 4), // pxo mission refresh
3691 ui_button_info("2_MC_05", 907, 197, 748, 216, 5), // pilot info
3692 ui_button_info("2_MC_06", 1, 258, -1, -1, 6), // scroll mission info up
3693 ui_button_info("2_MC_08", 1, 487, -1, -1, 8), // scroll mission info down
3694 ui_button_info("2_MC_09", 981, 256, -1, -1, 9), // scroll players up
3695 ui_button_info("2_MC_10", 981, 323, -1, -1, 10), // scroll players down
3696 ui_button_info("2_MC_11", 35, 554, 46, 601, 11), // mission filter
3697 ui_button_info("2_MC_12", 166, 554, 174, 601, 12), // campaign filter
3698 ui_button_info("2_MC_13", 628, 545, 559, 582, 13), // cancel
3699 ui_button_info("2_MC_14", 756, 564, 772, 610, 14), // team 0
3700 ui_button_info("2_MC_15", 810, 564, 826, 610, 15), // team 1
3701 ui_button_info("2_MC_16", 862, 554, 872, 610, 16), // kick
3702 ui_button_info("2_MC_17", 943, 554, 949, 610, 17), // close
3703 ui_button_info("2_MC_18", 1, 649, -1, -1, 18), // scroll list up
3704 ui_button_info("2_MC_19", 1, 716, -1, -1, 19), // scroll list down
3705 ui_button_info("2_MC_20", 798, 695, 726, 667, 20), // host options
3706 ui_button_info("2_MC_21", 854, 681, -1, -1, 21), // help
3707 ui_button_info("2_MC_22", 854, 724, -1, -1, 22), // options
3708 ui_button_info("2_MC_23", 914, 681, 932, 667, 23), // commit
3713 #define MULTI_CREATE_NUM_TEXT 0
3715 #define MULTI_CREATE_NUM_TEXT 15
3717 UI_XSTR Multi_create_text[GR_NUM_RESOLUTIONS][MULTI_CREATE_NUM_BUTTONS] = {
3719 // not needed for FS1
3721 {"All", 1256, 36, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_ALL].button},
3722 {"Coop", 1257, 71, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_COOP].button},
3723 {"Team", 1258, 119, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_TEAM].button},
3724 {"Dogfight", 1259, 166, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_DOGFIGHT].button},
3725 {"Refresh Missions", 1260, 229, 130, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_PXO_REFRESH].button},
3726 {"Pilot Info", 1261, 467, 132, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_PILOT_INFO].button},
3727 {"Missions", 1262, 27, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_MISSION_FILTER].button},
3728 {"Campaigns", 1263, 110, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_CAMPAIGN_FILTER].button},
3729 {"Cancel", 387, 328, 364, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_CANCEL].button},
3730 {"1", 1264, 482, 381, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_TEAM0].button},
3731 {"2", 1265, 514, 381, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_TEAM1].button},
3732 {"Kick", 1266, 539, 381, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_KICK].button},
3733 {"Close", 1508, 582, 381, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_CLOSE].button},
3734 {"Host Options", 1267, 436, 423, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_HOST_OPTIONS].button},
3735 {"Commit", 1062, 572, 413, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_ACCEPT].button}
3739 // not needed for FS1
3741 {"All", 1256, 61, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_ALL].button},
3742 {"Coop", 1257, 124, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_COOP].button},
3743 {"Team", 1258, 194, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_TEAM].button},
3744 {"Dogfight", 1259, 261, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_DOGFIGHT].button},
3745 {"Refresh Missions", 1260, 501, 218, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_PXO_REFRESH].button},
3746 {"Pilot Info", 1261, 814, 216, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_PILOT_INFO].button},
3747 {"Missions", 1262, 46, 601, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_MISSION_FILTER].button},
3748 {"Campaigns", 1263, 174, 601, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_CAMPAIGN_FILTER].button},
3749 {"Cancel", 387, 559, 582, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_CANCEL].button},
3750 {"1", 1264, 772, 610, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_TEAM0].button},
3751 {"2", 1265, 826, 610, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_TEAM1].button},
3752 {"Kick", 1266, 872, 610, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_KICK].button},
3753 {"Close", 1508, 949, 610, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_CLOSE].button},
3754 {"Host Options", 1267, 755, 683, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_HOST_OPTIONS].button},
3755 {"Commit", 1062, 932, 667, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_ACCEPT].button}
3761 // squad war checkbox
3762 UI_CHECKBOX Multi_create_sw_checkbox;
3763 const char *Multi_create_sw_checkbox_fname[GR_NUM_RESOLUTIONS] = {
3767 int Multi_create_sw_checkbox_coords[GR_NUM_RESOLUTIONS][2] = {
3775 int Multi_create_sw_checkbox_text[GR_NUM_RESOLUTIONS][2] = {
3785 // game information text areas
3786 int Mc_list_coords[GR_NUM_RESOLUTIONS][4] = {
3799 int Mc_players_coords[GR_NUM_RESOLUTIONS][4] = {
3812 int Mc_info_coords[GR_NUM_RESOLUTIONS][4] = {
3825 // mission icon stuff
3826 int Mc_icon_type_coords[GR_NUM_RESOLUTIONS][2] = {
3828 38, -2 // y is an offset
3831 61, -2 // y is an offset
3835 int Mc_icon_volition_coords[GR_NUM_RESOLUTIONS][2] = {
3837 61, -1 // y is an offset
3840 98, 1 // y is an offset
3844 int Mc_icon_silent_coords[GR_NUM_RESOLUTIONS][2] = {
3846 72, 0 // y is an offset
3849 115, 0 // y is an offset
3853 int Mc_icon_valid_coords[GR_NUM_RESOLUTIONS][2] = {
3855 91, 0 // y is an offset
3858 146, 0 // y is an offset
3862 // mission/campaign list column areas
3863 int Mc_column1_w[GR_NUM_RESOLUTIONS] = {
3868 int Mc_column2_w[GR_NUM_RESOLUTIONS] = {
3873 int Mc_column3_w[GR_NUM_RESOLUTIONS] = {
3878 int Mc_mission_name_x[GR_NUM_RESOLUTIONS] = {
3883 int Mc_mission_count_x[GR_NUM_RESOLUTIONS] = {
3888 int Mc_mission_fname_x[GR_NUM_RESOLUTIONS] = {
3893 int Mc_create_game_text[GR_NUM_RESOLUTIONS][2] = {
3894 {13, 116}, // GR_640
3895 {21, 186} // GR_1024
3898 int Mc_players_text[GR_NUM_RESOLUTIONS][2] = {
3899 {467, 150}, // GR_640
3900 {747, 240} // GR_1024
3903 int Mc_team_text[GR_NUM_RESOLUTIONS][2] = {
3904 {484, 342}, // GR_640
3905 {774, 547} // GR_1024
3908 int Mc_slider_coords[GR_NUM_RESOLUTIONS][4] = {
3910 3, 197, 13, 105 // GR_640
3913 5, 316, 20, 168 // GR_1024
3917 const char *Mc_slider_bitmap[GR_NUM_RESOLUTIONS] = {
3922 // player list control thingie defs
3923 #define MULTI_CREATE_PLIST_MAX_DISPLAY 20
3924 int Multi_create_plist_select_flag; // flag indicating if we have a play selected
3925 short Multi_create_plist_select_id; // the net address of the currently selected player (for lookup)
3927 // master tracker details
3928 int Multi_create_frame_count; // framecount
3929 int Multi_create_mt_tried_login; // attempted to login this server on the MT
3931 // mission filter settings
3932 int Multi_create_filter; // what mode we're in
3934 // game/campaign list control defs
3935 int Multi_create_list_max_display[GR_NUM_RESOLUTIONS] = {
3941 int Multi_create_list_count; // number of items in listbox
3942 int Multi_create_list_mode; // 0 == mission mode, 1 == campaign mode
3943 int Multi_create_list_start; // where to start displaying from
3944 int Multi_create_list_select; // which item is currently highlighted
3945 int Multi_create_files_loaded;
3947 char Multi_create_files_array[MULTI_CREATE_MAX_LIST_ITEMS][MAX_FILENAME_LEN];
3949 int Multi_create_mission_count; // how many we have
3950 int Multi_create_campaign_count;
3951 multi_create_info Multi_create_mission_list[MULTI_CREATE_MAX_LIST_ITEMS];
3952 multi_create_info Multi_create_campaign_list[MULTI_CREATE_MAX_LIST_ITEMS];
3954 // use a pointer for the file list. Will point to either the missions or the campaigns
3955 multi_create_info *Multi_create_file_list;
3957 // LOCAL function definitions
3958 void multi_create_check_buttons();
3959 void multi_create_button_pressed(int n);
3960 void multi_create_init_as_server();
3961 void multi_create_init_as_client();
3962 void multi_create_do_netstuff();
3963 void multi_create_plist_scroll_up();
3964 void multi_create_plist_scroll_down();
3965 void multi_create_plist_process();
3966 void multi_create_plist_blit_normal();
3967 void multi_create_plist_blit_team();
3968 void multi_create_list_scroll_up();
3969 void multi_create_list_scroll_down();
3970 void multi_create_list_do();
3971 void multi_create_list_select_item(int n);
3972 void multi_create_list_blit_icons(int list_index, int y_start);
3973 void multi_create_accept_hit();
3974 void multi_create_draw_filter_buttons();
3975 void multi_create_set_selected_team(int team);
3976 short multi_create_get_mouse_id();
3977 int multi_create_ok_to_commit();
3978 int multi_create_verify_cds();
3979 void multi_create_refresh_pxo();
3980 void multi_create_sw_clicked();
3982 // since we can selectively filter out mission/campaign types we always need to map a selected index (which is relative
3983 // to the displayed list), to an absolute index (which is relative to the total file list - some of which may filtered out)
3984 void multi_create_select_to_filename(int select_index, char *filename, const int max_filelen);
3985 int multi_create_select_to_index(int select_index);
3987 int Multi_create_should_show_popup = 0;
3990 // sorting function to sort mission lists.. Basic sorting on mission name
3991 int multi_create_sort_func(const void *a, const void *b)
3993 multi_create_info *m1, *m2;
3995 m1 = (multi_create_info *)a;
3996 m2 = (multi_create_info *)b;
3998 return ( strcmp(m1->name, m2->name) );
4001 void multi_create_setup_list_data(int mode)
4003 int idx,should_sort,switched_modes;
4005 // set the current mode
4008 if((Multi_create_list_mode != mode) && (mode != -1)){
4009 Multi_create_list_mode = mode;
4012 // set up the list pointers
4013 if ( Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ) {
4014 Multi_create_file_list = Multi_create_mission_list;
4015 } else if ( Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ) {
4016 Multi_create_file_list = Multi_create_campaign_list;
4022 // get the mission count based upon the filter selected
4023 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
4024 switch(Multi_create_filter){
4025 case MISSION_TYPE_MULTI:
4026 Multi_create_list_count = Multi_create_mission_count;
4029 Multi_create_list_count = 0;
4030 // find all missions which match
4031 for(idx=0;idx<Multi_create_mission_count;idx++){
4032 if(Multi_create_mission_list[idx].flags & Multi_create_filter){
4033 Multi_create_list_count++;
4037 // if we switched modes and we have more than 0 items, sort them
4038 if(switched_modes && (Multi_create_list_count > 0)){
4043 } else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
4044 switch(Multi_create_filter){
4045 case MISSION_TYPE_MULTI:
4046 Multi_create_list_count = Multi_create_campaign_count;
4049 Multi_create_list_count = 0;
4050 // find all missions which match
4051 for(idx=0;idx<Multi_create_campaign_count;idx++){
4052 if(Multi_create_campaign_list[idx].flags & Multi_create_filter){
4053 Multi_create_list_count++;
4057 // if we switched modes and we have more than 0 items, sort them
4058 if(switched_modes && (Multi_create_list_count > 0)){
4065 // reset the list start and selected indices
4066 Multi_create_list_start = 0;
4067 Multi_create_list_select = -1;
4068 multi_create_list_select_item(Multi_create_list_start);
4070 // sort the list of missions if necessary
4072 qsort(Multi_create_file_list, Multi_create_list_count, sizeof(multi_create_info), multi_create_sort_func);
4077 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);
4081 void multi_create_game_init()
4086 // now make sure to initialze various netgame stuff based upon whether we're on a standalone or not
4087 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4088 multi_create_init_as_server();
4090 multi_create_init_as_client();
4093 // initialize the player list data
4094 Multi_create_plist_select_flag = 0;
4095 Multi_create_plist_select_id = -1;
4097 // create the interface window
4098 Multi_create_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
4099 Multi_create_window.set_mask_bmap(Multi_create_bitmap_mask_fname[gr_screen.res]);
4101 // load the background bitmap
4102 Multi_create_bitmap = bm_load(Multi_create_bitmap_fname[gr_screen.res]);
4103 if(Multi_create_bitmap < 0){
4104 // we failed to load the bitmap - this is very bad
4108 // close any previous existing instances of the chatbox and create a new one
4112 // load the help overlay
4113 help_overlay_load(MULTI_CREATE_OVERLAY);
4114 help_overlay_set_state(MULTI_CREATE_OVERLAY, 0);
4116 // initialize the common notification messaging
4117 multi_common_notify_init();
4119 // use the common interface palette
4120 multi_common_set_palette();
4122 // create the interface buttons
4123 for(idx=0; idx<MULTI_CREATE_NUM_BUTTONS; idx++){
4124 b = &Multi_create_buttons[gr_screen.res][idx];
4126 // create the object
4127 b->button.create(&Multi_create_window, "", b->x, b->y, 1, 1, ((idx == MC_SCROLL_LIST_UP) || (idx == MC_SCROLL_LIST_DOWN)), 1);
4129 // set the sound to play when highlighted
4130 b->button.set_highlight_action(common_play_highlight_sound);
4132 // set the ani for the button
4133 b->button.set_bmaps(b->filename);
4136 b->button.link_hotspot(b->hotspot);
4138 // some special case stuff for the pxo refresh button
4139 if(idx == MC_PXO_REFRESH){
4140 // if not a PXO game, or if I'm not a server disable and hide the button
4141 if(!MULTI_IS_TRACKER_GAME || !MULTIPLAYER_MASTER){
4143 b->button.disable();
4149 for(idx=0; idx<MULTI_CREATE_NUM_TEXT; idx++){
4150 Multi_create_window.add_XSTR(&Multi_create_text[gr_screen.res][idx]);
4154 // if this is a PXO game, enable the squadwar checkbox
4155 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);
4156 Multi_create_sw_checkbox.set_bmaps(Multi_create_sw_checkbox_fname[gr_screen.res], 6, 0);
4157 if(!MULTI_IS_TRACKER_GAME){
4158 Multi_create_sw_checkbox.hide();
4159 Multi_create_sw_checkbox.disable();
4164 // disable squad war button in demo
4165 Multi_create_sw_checkbox.hide();
4166 Multi_create_sw_checkbox.disable();
4169 // initialize the mission type filtering mode
4170 Multi_create_filter = MISSION_TYPE_MULTI;
4172 // initialize the list mode, and load in a list
4173 memset(Multi_create_mission_list, 0, sizeof(multi_create_info) * MULTI_CREATE_MAX_LIST_ITEMS);
4174 memset(Multi_create_campaign_list, 0, sizeof(multi_create_info) * MULTI_CREATE_MAX_LIST_ITEMS);
4175 for(idx=0; idx<MULTI_CREATE_MAX_LIST_ITEMS; idx++){
4176 Multi_create_mission_list[idx].valid_status = MVALID_STATUS_UNKNOWN;
4177 Multi_create_campaign_list[idx].valid_status = MVALID_STATUS_UNKNOWN;
4179 Multi_create_list_mode = MULTI_CREATE_SHOW_MISSIONS;
4180 Multi_create_list_start = -1;
4181 Multi_create_list_select = -1;
4182 Multi_create_list_count = 0;
4185 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);
4188 // create the player list select button
4189 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);
4190 Multi_create_player_select_button.hide();
4192 // create the mission/campaign list select button
4193 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);
4194 Multi_create_list_select_button.hide();
4196 // set hotkeys for a couple of things.
4197 Multi_create_buttons[gr_screen.res][MC_ACCEPT].button.set_hotkey(KEY_CTRLED+SDLK_RETURN);
4199 // init some master tracker stuff
4200 Multi_create_frame_count = 0;
4201 Multi_create_mt_tried_login = 0;
4203 // remove campaign flags
4204 Game_mode &= ~(GM_CAMPAIGN_MODE);
4206 // send any pilots as appropriate
4207 multi_data_send_my_junk();
4208 Multi_create_file_list = Multi_create_mission_list;
4210 Multi_create_campaign_count = 0;
4211 Multi_create_mission_count = 0;
4212 Multi_create_files_loaded = 0;
4215 void multi_create_game_do()
4219 const char *loading_str = XSTR("Loading", 1336);
4223 // set this if we want to show the pilot info popup
4224 Multi_create_should_show_popup = 0;
4226 // first thing is to load the files
4227 if ( !Multi_create_files_loaded ) {
4228 // if I am a client, send a list request to the server for the missions
4229 if ( MULTIPLAYER_CLIENT ) {
4230 send_mission_list_request( MISSION_LIST_REQUEST );
4234 loading_bitmap = bm_load(Multi_create_loading_fname[gr_screen.res]);
4236 // draw the background, etc
4238 GR_MAYBE_CLEAR_RES(Multi_create_bitmap);
4239 if(Multi_create_bitmap != -1){
4240 gr_set_bitmap(Multi_create_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4244 if ( loading_bitmap > -1 ){
4245 gr_set_bitmap(loading_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4247 gr_bitmap( Please_wait_coords[gr_screen.res][MC_X_COORD], Please_wait_coords[gr_screen.res][MC_Y_COORD] );
4249 // draw "Loading" on it
4251 gr_set_color_fast(&Color_normal);
4253 gr_get_string_size(&str_w, &str_h, loading_str);
4254 gr_string((gr_screen.max_w - str_w) / 2, (gr_screen.max_h - str_h) / 2, loading_str);
4260 multi_create_list_load_missions();
4261 multi_create_list_load_campaigns();
4263 // if this is a tracker game, validate missions
4264 if(MULTI_IS_TRACKER_GAME){
4265 multi_update_valid_missions();
4268 // update the file list
4269 multi_create_setup_list_data(MULTI_CREATE_SHOW_MISSIONS);
4272 // don't bother setting netgame state if ont the server
4273 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4274 Netgame.game_state = NETGAME_STATE_FORMING;
4275 send_netgame_update_packet();
4278 // if we're on the standalone we have to tell him that we're now in the host setup screen
4279 Net_player->state = NETPLAYER_STATE_HOST_SETUP;
4280 send_netplayer_update_packet();
4282 Multi_create_files_loaded = 1;
4285 int k = chatbox_process();
4286 k = Multi_create_window.process(k,0);
4289 // same as the cancel button
4291 if(help_overlay_active(MULTI_CREATE_OVERLAY)){
4292 help_overlay_set_state(MULTI_CREATE_OVERLAY,0);
4294 gamesnd_play_iface(SND_USER_SELECT);
4295 multi_quit_game(PROMPT_HOST);
4300 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
4301 help_overlay_set_state(MULTI_CREATE_OVERLAY, 0);
4304 // process any button clicks
4305 multi_create_check_buttons();
4307 // do any network related stuff
4308 multi_create_do_netstuff();
4310 // draw the background, etc
4312 GR_MAYBE_CLEAR_RES(Multi_create_bitmap);
4313 if(Multi_create_bitmap != -1){
4314 gr_set_bitmap(Multi_create_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4318 // if we're not in team vs. team mode, don't draw the team buttons
4319 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
4320 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.hide();
4321 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.hide();
4322 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.disable();
4323 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.disable();
4325 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.enable();
4326 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.enable();
4327 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.unhide();
4328 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.unhide();
4331 // draw the window itself
4332 Multi_create_window.draw();
4334 gr_set_color_fast(&Color_normal);
4337 // draw Create Game text
4338 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));
4340 // draw players text
4341 gr_string(Mc_players_text[gr_screen.res][MC_X_COORD], Mc_players_text[gr_screen.res][MC_Y_COORD], XSTR("Players", 1269));
4343 // draw players text
4344 gr_string(Mc_team_text[gr_screen.res][MC_X_COORD], Mc_team_text[gr_screen.res][MC_Y_COORD], XSTR("Team", 1258));
4347 // process and display the player list
4348 // NOTE : this must be done before the buttons are checked to insure that a player hasn't dropped
4349 multi_create_plist_process();
4350 if(Netgame.type_flags & NG_TYPE_TEAM){
4351 multi_create_plist_blit_team();
4353 multi_create_plist_blit_normal();
4356 // process and display the game/campaign list
4357 multi_create_list_do();
4359 // draw the correct mission filter button
4360 multi_create_draw_filter_buttons();
4362 // display any text in the info area
4363 multi_common_render_text();
4365 // display any pending notification messages
4366 multi_common_notify_do();
4368 // force the correct mission/campaign button to light up
4369 if( Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ){
4370 Multi_create_buttons[gr_screen.res][MC_MISSION_FILTER].button.draw_forced(2);
4372 Multi_create_buttons[gr_screen.res][MC_CAMPAIGN_FILTER].button.draw_forced(2);
4375 // force draw the closed button if it is toggled on
4376 if(Netgame.flags & NG_FLAG_TEMP_CLOSED){
4377 Multi_create_buttons[gr_screen.res][MC_CLOSE].button.draw_forced(2);
4380 // process and show the chatbox thingie
4384 Multi_create_window.draw_tooltip();
4386 // display the voice status indicator
4387 multi_common_voice_display_status();
4389 // blit the help overlay if necessary
4390 help_overlay_maybe_blit(MULTI_CREATE_OVERLAY);
4393 if(MULTI_IS_TRACKER_GAME){
4394 if(Netgame.type_flags & NG_TYPE_SW){
4395 gr_set_color_fast(&Color_bright);
4397 gr_set_color_fast(&Color_normal);
4400 gr_string(Multi_create_sw_checkbox_text[gr_screen.res][0], Multi_create_sw_checkbox_text[gr_screen.res][1], "SquadWar");
4407 // if we're supposed to show the pilot info popup, do it now
4408 if(Multi_create_should_show_popup){
4409 // get the player index and address of the player item the mouse is currently over
4410 if(Multi_create_plist_select_flag){
4411 player_index = find_player_id(Multi_create_plist_select_id);
4412 if(player_index != -1){
4413 multi_pinfo_popup(&Net_players[player_index]);
4418 // increment the frame count
4419 Multi_create_frame_count++;
4422 void multi_create_game_close()
4424 // unload any bitmaps
4425 if(!bm_unload(Multi_create_bitmap)){
4426 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_create_bitmap_fname[gr_screen.res]));
4429 // unload the help overlay
4430 help_overlay_unload(MULTI_CREATE_OVERLAY);
4432 // destroy the chatbox
4435 // destroy the UI_WINDOW
4436 Multi_create_window.destroy();
4439 void multi_create_check_buttons()
4442 for(idx=0;idx<MULTI_CREATE_NUM_BUTTONS;idx++){
4443 // we only really need to check for one button pressed at a time, so we can break after
4445 if(Multi_create_buttons[gr_screen.res][idx].button.pressed()){
4446 multi_create_button_pressed(idx);
4452 // if the squad war checkbox was clicked
4453 if(Multi_create_sw_checkbox.changed()){
4454 multi_create_sw_clicked();
4459 void multi_create_button_pressed(int n)
4465 gamesnd_play_iface(SND_USER_SELECT);
4466 multi_quit_game(PROMPT_HOST);
4469 // if valid commit conditions have not been met
4470 if(!multi_create_ok_to_commit()){
4475 multi_create_accept_hit();
4480 if(!help_overlay_active(MULTI_CREATE_OVERLAY)){
4481 help_overlay_set_state(MULTI_CREATE_OVERLAY,1);
4483 help_overlay_set_state(MULTI_CREATE_OVERLAY,0);
4487 // scroll the info text box up
4488 case MC_SCROLL_INFO_UP:
4489 multi_common_scroll_text_up();
4492 // scroll the info text box down
4493 case MC_SCROLL_INFO_DOWN:
4494 multi_common_scroll_text_down();
4497 // scroll the player list up
4498 case MC_SCROLL_PLAYERS_UP:
4499 multi_create_plist_scroll_up();
4502 // scroll the player list down
4503 case MC_SCROLL_PLAYERS_DOWN:
4504 multi_create_plist_scroll_down();
4507 // scroll the game/campaign list up
4508 case MC_SCROLL_LIST_UP:
4509 multi_create_list_scroll_up();
4511 Multi_create_slider.forceUp(); // move slider up
4515 // scroll the game/campaign list down
4516 case MC_SCROLL_LIST_DOWN:
4517 multi_create_list_scroll_down();
4519 Multi_create_slider.forceDown(); // move slider down
4523 // go to the options screen
4525 gamesnd_play_iface(SND_USER_SELECT);
4526 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
4529 // show all missions
4531 if(Multi_create_filter != MISSION_TYPE_MULTI){
4532 gamesnd_play_iface(SND_USER_SELECT);
4533 Multi_create_filter = MISSION_TYPE_MULTI;
4534 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4536 gamesnd_play_iface(SND_GENERAL_FAIL);
4540 // show cooperative missions
4542 if(Multi_create_filter != MISSION_TYPE_MULTI_COOP){
4543 gamesnd_play_iface(SND_USER_SELECT);
4544 Multi_create_filter = MISSION_TYPE_MULTI_COOP;
4545 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4547 gamesnd_play_iface(SND_GENERAL_FAIL);
4551 // show team vs. team missions
4553 if(Multi_create_filter != MISSION_TYPE_MULTI_TEAMS){
4554 gamesnd_play_iface(SND_USER_SELECT);
4555 Multi_create_filter = MISSION_TYPE_MULTI_TEAMS;
4556 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4558 gamesnd_play_iface(SND_GENERAL_FAIL);
4562 // show dogfight missions
4564 case MC_SHOW_DOGFIGHT:
4565 if (Multi_create_filter != MISSION_TYPE_MULTI_DOGFIGHT){
4566 gamesnd_play_iface(SND_USER_SELECT);
4567 Multi_create_filter = MISSION_TYPE_MULTI_DOGFIGHT;
4568 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4570 gamesnd_play_iface(SND_GENERAL_FAIL);
4575 // toggle temporary netgame closed on/off
4577 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4578 Netgame.flags ^= NG_FLAG_TEMP_CLOSED;
4580 Netgame.options.flags |= MLO_FLAG_TEMP_CLOSED;
4581 multi_options_update_netgame();
4583 gamesnd_play_iface(SND_USER_SELECT);
4586 // kick the currently selected player (if possible)
4588 // lookup the player at the specified index
4589 if(Multi_create_plist_select_flag){
4590 idx = find_player_id(Multi_create_plist_select_id);
4591 // kick him - but don't ban him
4593 multi_kick_player(idx,0);
4598 // switch to individual mission mode and load in a list
4599 case MC_MISSION_FILTER:
4600 if(Multi_create_list_mode != MULTI_CREATE_SHOW_MISSIONS){
4601 Netgame.campaign_mode = MP_SINGLE;
4603 gamesnd_play_iface(SND_USER_SELECT);
4605 // update the file list
4606 multi_create_setup_list_data(MULTI_CREATE_SHOW_MISSIONS);
4608 gamesnd_play_iface(SND_GENERAL_FAIL);
4612 // switch to campaign mode and load in a list
4613 case MC_CAMPAIGN_FILTER:
4614 // switch off squad war
4616 Multi_create_sw_checkbox.set_state(0);
4618 Netgame.type_flags = NG_TYPE_COOP;
4620 if(Multi_create_list_mode != MULTI_CREATE_SHOW_CAMPAIGNS){
4621 Netgame.campaign_mode = MP_CAMPAIGN;
4623 gamesnd_play_iface(SND_USER_SELECT);
4625 // update the file list
4626 multi_create_setup_list_data(MULTI_CREATE_SHOW_CAMPAIGNS);
4628 gamesnd_play_iface(SND_GENERAL_FAIL);
4632 // attempt to set the selected player's team
4634 multi_create_set_selected_team(0);
4635 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4636 multi_team_send_update();
4640 // attempt to set the selected player's team
4642 multi_create_set_selected_team(1);
4643 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4644 multi_team_send_update();
4648 // popup the pilot info dialog for the currently selected pilot (will occur at the end of the frame)
4650 Multi_create_should_show_popup = 1;
4653 // go to the host options screen
4654 case MC_HOST_OPTIONS:
4655 gamesnd_play_iface(SND_USER_SELECT);
4656 gameseq_post_event(GS_EVENT_MULTI_HOST_OPTIONS);
4659 // refresh PXO file list
4660 case MC_PXO_REFRESH:
4661 if(!MULTI_IS_TRACKER_GAME){
4664 multi_create_refresh_pxo();
4668 gamesnd_play_iface(SND_GENERAL_FAIL);
4669 multi_common_add_notify(XSTR("Not implemented yet!",760));
4674 // do stuff like pinging servers, sending out requests, etc
4675 void multi_create_do_netstuff()
4679 // if not on a standalone
4680 void multi_create_init_as_server()
4682 // set me up as the host and master
4683 Net_player->flags |= (NETINFO_FLAG_AM_MASTER | NETINFO_FLAG_GAME_HOST);
4686 // if on a standalone
4687 void multi_create_init_as_client()
4689 Net_player->flags |= NETINFO_FLAG_GAME_HOST;
4692 // scroll up through the player list
4693 void multi_create_plist_scroll_up()
4695 gamesnd_play_iface(SND_GENERAL_FAIL);
4698 // scroll down through the player list
4699 void multi_create_plist_scroll_down()
4701 gamesnd_play_iface(SND_GENERAL_FAIL);
4704 void multi_create_plist_process()
4706 int test_count,idx,player_index;
4708 // first determine if there are 0 players in the game. This should never happen since the host is _always_ in the game
4710 for(idx=0;idx<MAX_PLAYERS;idx++){
4711 // count anyone except the standalone server (if applicable)
4712 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
4716 if(test_count <= 0){
4720 // if we had a selected item but that player has left, select myself instead
4721 if(Multi_create_plist_select_flag){
4722 player_index = find_player_id(Multi_create_plist_select_id);
4723 if(player_index == -1){
4724 Multi_create_plist_select_id = Net_player->player_id;
4727 Multi_create_plist_select_flag = 1;
4728 Multi_create_plist_select_id = Net_player->player_id;
4731 // if the player has clicked somewhere in the player list area
4732 if(Multi_create_player_select_button.pressed()){
4735 // get the player index and address of the player item the mouse is currently over
4736 player_id = multi_create_get_mouse_id();
4737 player_index = find_player_id(player_id);
4738 if(player_index != -1){
4739 Multi_create_plist_select_flag = 1;
4740 Multi_create_plist_select_id = player_id;
4745 void multi_create_plist_blit_normal()
4748 char str[CALLSIGN_LEN+5];
4749 int y_start = Mc_players_coords[gr_screen.res][MC_Y_COORD];
4752 // display all the players
4753 for(idx=0;idx<MAX_PLAYERS;idx++){
4754 // count anyone except the standalone server (if applicable)
4755 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
4759 // highlight him if he's the host
4760 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4761 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4762 gr_set_color_fast(&Color_text_active_hi);
4764 gr_set_color_fast(&Color_bright);
4767 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4768 gr_set_color_fast(&Color_text_active);
4770 gr_set_color_fast(&Color_text_normal);
4774 // optionally draw his CD status
4775 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4776 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4777 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4779 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4782 // make sure the string will fit, then display it
4783 SDL_strlcpy(str, Net_players[idx].player->callsign, SDL_arraysize(str));
4784 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4785 SDL_strlcat(str, XSTR("(O)",787), SDL_arraysize(str)); // [[ Observer ]]
4787 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4788 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4795 void multi_create_plist_blit_team()
4798 char str[CALLSIGN_LEN+1];
4799 int y_start = Mc_players_coords[gr_screen.res][MC_Y_COORD];
4802 // display all the red players first
4803 for(idx=0;idx<MAX_PLAYERS;idx++){
4804 // count anyone except the standalone server (if applicable)
4805 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
4806 // reset total offset
4809 // highlight him if he's the host
4810 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4811 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4812 gr_set_color_fast(&Color_text_active_hi);
4814 // be sure to blit the correct team button
4815 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.draw_forced(2);
4817 gr_set_color_fast(&Color_bright);
4820 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4821 gr_set_color_fast(&Color_text_active);
4823 // be sure to blit the correct team button
4824 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.draw_forced(2);
4826 gr_set_color_fast(&Color_text_normal);
4830 // optionally draw his CD status
4831 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4832 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4833 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4835 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4838 // blit the red team indicator
4839 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
4840 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
4841 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4842 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4844 total_offset += Multi_common_icon_dims[MICON_TEAM0_SELECT][0] + 1;
4847 if(Multi_common_icons[MICON_TEAM0] != -1){
4848 gr_set_bitmap(Multi_common_icons[MICON_TEAM0], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4849 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4851 total_offset += Multi_common_icon_dims[MICON_TEAM0][0] + 1;
4855 // make sure the string will fit
4856 SDL_strlcpy(str, Net_players[idx].player->callsign, SDL_arraysize(str));
4857 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4858 SDL_strlcat(str, XSTR("(O)",787), SDL_arraysize(str));
4860 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4862 // display him in the correct half of the list depending on his team
4863 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4868 // display all the green players next
4869 for(idx=0;idx<MAX_PLAYERS;idx++){
4870 // count anyone except the standalone server (if applicable)
4871 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
4872 // reset total offset
4875 // highlight him if he's the host
4876 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4877 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4878 gr_set_color_fast(&Color_text_active_hi);
4880 // be sure to blit the correct team button
4881 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.draw_forced(2);
4883 gr_set_color_fast(&Color_bright);
4886 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4887 gr_set_color_fast(&Color_text_active);
4889 // be sure to blit the correct team button
4890 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.draw_forced(2);
4892 gr_set_color_fast(&Color_text_normal);
4896 // optionally draw his CD status
4897 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4898 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4899 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4901 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4904 // blit the red team indicator
4905 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
4906 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
4907 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4908 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4910 total_offset += Multi_common_icon_dims[MICON_TEAM1_SELECT][0] + 1;
4913 if(Multi_common_icons[MICON_TEAM1] != -1){
4914 gr_set_bitmap(Multi_common_icons[MICON_TEAM1], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4915 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4917 total_offset += Multi_common_icon_dims[MICON_TEAM1][0] + 1;
4921 // make sure the string will fit
4922 SDL_strlcpy(str, Net_players[idx].player->callsign, SDL_arraysize(str));
4923 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4924 SDL_strlcat(str, XSTR("(O)",787), SDL_arraysize(str));
4926 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4928 // display him in the correct half of the list depending on his team
4929 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4935 void multi_create_list_scroll_up()
4937 if(Multi_create_list_start > 0){
4938 Multi_create_list_start--;
4940 gamesnd_play_iface(SND_SCROLL);
4942 gamesnd_play_iface(SND_GENERAL_FAIL);
4946 void multi_create_list_scroll_down()
4948 if((Multi_create_list_count - Multi_create_list_start) > Multi_create_list_max_display[gr_screen.res]){
4949 Multi_create_list_start++;
4951 gamesnd_play_iface(SND_SCROLL);
4953 gamesnd_play_iface(SND_GENERAL_FAIL);
4957 // gets a list of multiplayer misisons
4958 void multi_create_list_load_missions()
4960 char *fname, mission_name[NAME_LENGTH+1];
4964 SDL_snprintf(wild_card, SDL_arraysize(wild_card), "*%s", FS_MISSION_FILE_EXT);
4965 file_count = cf_get_file_list_preallocated(MULTI_CREATE_MAX_LIST_ITEMS, Multi_create_files_array, NULL, CF_TYPE_MISSIONS, wild_card);
4966 Multi_create_mission_count = 0;
4968 // maybe create a standalone dialog
4969 if(Game_mode & GM_STANDALONE_SERVER){
4970 std_create_gen_dialog("Loading missions");
4971 std_gen_set_text("Mission:", 1);
4974 for(idx = 0; idx < file_count; idx++){
4975 int flags,max_players;
4979 fname = Multi_create_files_array[idx];
4981 // tack on any necessary file extension
4982 filename = cf_add_ext( fname, FS_MISSION_FILE_EXT );
4984 // for multiplayer beta builds, only accept builtin missions
4985 #if defined(MULTIPLAYER_BETA_BUILD) || defined(FS2_DEMO)
4986 if(game_find_builtin_mission(filename) == NULL){
4989 #elif defined(PD_BUILD)
4990 if((game_find_builtin_mission(filename) == NULL) && !strstr(filename, "peterdrake")){
4995 if(Game_mode & GM_STANDALONE_SERVER){
4996 std_gen_set_text(filename, 2);
4999 flags = mission_parse_is_multi(filename, mission_name);
5001 // if the mission is a multiplayer mission, then add it to the mission list
5003 max_players = mission_parse_get_multi_mission_info( filename );
5004 m_respawn = The_mission.num_respawns;
5006 if ( Multi_create_mission_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
5007 multi_create_info *mcip;
5009 mcip = &Multi_create_mission_list[Multi_create_mission_count];
5010 SDL_strlcpy(mcip->filename, filename, SDL_arraysize(mcip->filename));
5011 SDL_strlcpy(mcip->name, mission_name, SDL_arraysize(mcip->name));
5012 mcip->flags = flags;
5013 mcip->respawn = m_respawn;
5014 mcip->max_players = (ubyte)max_players;
5016 // get any additional information for possibly builtin missions
5017 fs_builtin_mission *fb = game_find_builtin_mission(filename);
5021 Multi_create_mission_count++;
5027 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);
5030 // maybe create a standalone dialog
5031 if(Game_mode & GM_STANDALONE_SERVER){
5032 std_destroy_gen_dialog();
5036 void multi_create_list_load_campaigns()
5039 int idx, file_count;
5040 int campaign_type,max_players;
5044 // maybe create a standalone dialog
5045 if(Game_mode & GM_STANDALONE_SERVER){
5046 std_create_gen_dialog("Loading campaigns");
5047 std_gen_set_text("Campaign:", 1);
5050 Multi_create_campaign_count = 0;
5051 SDL_snprintf(wild_card, SDL_arraysize(wild_card), "*%s", FS_CAMPAIGN_FILE_EXT);
5052 file_count = cf_get_file_list_preallocated(MULTI_CREATE_MAX_LIST_ITEMS, Multi_create_files_array, NULL, CF_TYPE_MISSIONS, wild_card);
5053 for(idx = 0; idx < file_count; idx++){
5055 char *filename, name[NAME_LENGTH];
5057 fname = Multi_create_files_array[idx];
5059 // tack on any necessary file extension
5060 filename = cf_add_ext( fname, FS_CAMPAIGN_FILE_EXT );
5062 // for multiplayer beta builds, only accept builtin missions
5063 #if defined(MULTIPLAYER_BETA_BUILD) || defined(FS2_DEMO)
5064 if(game_find_builtin_mission(filename) == NULL){
5067 #elif defined(PD_BUILD)
5068 if((game_find_builtin_mission(filename) == NULL) && !strstr(filename, "peterdrake")){
5073 if(Game_mode & GM_STANDALONE_SERVER){
5074 std_gen_set_text(filename, 2);
5077 // if the campaign is a multiplayer campaign, then add the data to the campaign list items
5078 flags = mission_campaign_parse_is_multi( filename, name, SDL_arraysize(name) );
5079 if( flags != CAMPAIGN_TYPE_SINGLE && mission_campaign_get_info(filename,title,&campaign_type,&max_players)) {
5080 if ( Multi_create_campaign_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
5081 multi_create_info *mcip;
5083 mcip = &Multi_create_campaign_list[Multi_create_campaign_count];
5084 SDL_strlcpy(mcip->filename, filename, SDL_arraysize(mcip->filename));
5085 SDL_strlcpy(mcip->name, name, SDL_arraysize(mcip->name));
5087 // setup various flags
5088 if ( flags == CAMPAIGN_TYPE_MULTI_COOP ){
5089 mcip->flags = MISSION_TYPE_MULTI_COOP | MISSION_TYPE_MULTI;
5090 } else if ( flags == CAMPAIGN_TYPE_MULTI_TEAMS ) {
5091 mcip->flags = MISSION_TYPE_MULTI_TEAMS | MISSION_TYPE_MULTI;
5093 Int3(); // bogus campaign multi type -- find allender
5096 // 0 respawns for campaign files (should be contained within the mission files themselves)
5099 // 0 max players for campaign files
5100 mcip->max_players = (unsigned char)max_players;
5102 // get any additional information for possibly builtin missions
5103 fs_builtin_mission *fb = game_find_builtin_mission(filename);
5107 Multi_create_campaign_count++;
5112 // maybe create a standalone dialog
5113 if(Game_mode & GM_STANDALONE_SERVER){
5114 std_destroy_gen_dialog();
5118 void multi_create_list_do()
5121 int start_index,stop_index;
5122 char selected_name[255];
5124 // bail early if there aren't any selectable items
5125 if(Multi_create_list_count == 0){
5129 // first check to see if the user has clicked on an item
5130 if(Multi_create_list_select_button.pressed()){
5132 Multi_create_list_select_button.get_mouse_pos(NULL,&y);
5135 // make sure we are selectedin valid indices
5136 if((item < Multi_create_list_max_display[gr_screen.res]) && (item >= 0)){
5137 item += Multi_create_list_start;
5139 if(item < Multi_create_list_count){
5140 multi_create_list_select_item(item);
5141 gamesnd_play_iface(SND_IFACE_MOUSE_CLICK);
5146 // bail early if we don't have a start position
5147 if(Multi_create_list_start == -1){
5151 // display the list of individual campaigns/missions
5153 int y_start = Mc_list_coords[gr_screen.res][MC_Y_COORD];
5155 start_index = multi_create_select_to_index(Multi_create_list_start);
5156 stop_index = Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ? Multi_create_mission_count : Multi_create_campaign_count;
5157 for(idx=start_index; idx<stop_index; idx++){
5158 // see if we should drop out
5159 if(count == Multi_create_list_max_display[gr_screen.res]){
5163 // see if we should filter out this mission
5164 if ( !(Multi_create_file_list[idx].flags & Multi_create_filter) ){
5168 // highlight the selected item
5169 multi_create_select_to_filename(Multi_create_list_select, selected_name, SDL_arraysize(selected_name));
5170 if(!strcmp(selected_name,Multi_create_file_list[idx].filename)){
5171 gr_set_color_fast(&Color_text_selected);
5173 gr_set_color_fast(&Color_text_normal);
5176 // draw the type icon
5177 multi_create_list_blit_icons(idx, y_start);
5179 // force fit the mission name string
5180 SDL_strlcpy(selected_name, Multi_create_file_list[idx].name, SDL_arraysize(selected_name));
5181 gr_force_fit_string(selected_name,255,Mc_column1_w[gr_screen.res]);
5182 gr_string(Mc_mission_name_x[gr_screen.res],y_start,selected_name);
5184 // draw the max players if in mission mode
5185 SDL_snprintf(selected_name,SDL_arraysize(selected_name),"%d",(int)Multi_create_file_list[idx].max_players);
5186 gr_string(Mc_mission_count_x[gr_screen.res],y_start,selected_name);
5188 // force fit the mission filename string
5189 SDL_strlcpy(selected_name, Multi_create_file_list[idx].filename, SDL_arraysize(selected_name));
5190 gr_force_fit_string(selected_name,255,Mc_column3_w[gr_screen.res]);
5191 gr_string(Mc_mission_fname_x[gr_screen.res],y_start,selected_name);
5198 // takes care of stuff like changing indices around and setting up the netgame structure
5199 void multi_create_list_select_item(int n)
5201 int abs_index,campaign_type,max_players;
5202 char title[NAME_LENGTH+1];
5203 netgame_info ng_temp;
5206 char *campaign_desc;
5208 // if not on the standalone server
5209 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5212 // on the standalone
5214 memset(&ng_temp,0,sizeof(netgame_info));
5218 if ( n != Multi_create_list_select ) {
5219 // check to see if this is a valid index, and bail if it is not
5220 abs_index = multi_create_select_to_index(n);
5221 if(abs_index == -1){
5225 Multi_create_list_select = n;
5227 // set the mission name
5228 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5229 multi_create_select_to_filename(n, ng->mission_name, SDL_arraysize(ng->mission_name));
5231 multi_create_select_to_filename(n, ng->campaign_name, SDL_arraysize(ng->campaign_name));
5234 // make sure the netgame type is properly set
5235 int old_type = Netgame.type_flags;
5236 abs_index = multi_create_select_to_index(n);
5237 if(abs_index != -1){
5238 if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_TEAMS){
5240 // if we're in squad war mode, leave it as squad war
5241 if(old_type & NG_TYPE_SW){
5242 ng->type_flags = NG_TYPE_SW;
5244 ng->type_flags = NG_TYPE_TVT;
5247 ng->type_flags = NG_TYPE_TVT;
5249 } else if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_COOP){
5250 ng->type_flags = NG_TYPE_COOP;
5251 } else if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_DOGFIGHT){
5252 ng->type_flags = NG_TYPE_DOGFIGHT;
5257 // if we're no longer in a TvT game, just uncheck the squadwar checkbox
5258 if(!(ng->type_flags & NG_TYPE_TEAM)){
5259 Multi_create_sw_checkbox.set_state(0);
5263 // if we switched from something else to team vs. team mode, do some special processing
5264 if((ng->type_flags & NG_TYPE_TEAM) && (ng->type_flags != old_type) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
5268 switch(Multi_create_list_mode){
5269 case MULTI_CREATE_SHOW_MISSIONS:
5270 // don't forget to update the info box window thingie
5271 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5272 ship_init(); // mwa -- 10/15/97. Call this function to reset number of ships in mission
5273 ng->max_players = mission_parse_get_multi_mission_info( ng->mission_name );
5275 SDL_assert(ng->max_players > 0);
5276 SDL_strlcpy(ng->title, The_mission.name, SDL_arraysize(ng->title));
5278 // set the information area text
5279 multi_common_set_text(The_mission.mission_desc);
5281 // if we're on the standalone, send a request for the description
5283 send_netgame_descript_packet(&Netgame.server_addr,0);
5284 multi_common_set_text("");
5287 // set the respawns as appropriate
5288 if(Netgame.options.respawn <= Multi_create_file_list[abs_index].respawn){
5289 ng->respawn = Netgame.options.respawn;
5290 nprintf(("Network","Using netgame options for respawn count (%d %d)\n",Netgame.options.respawn,Multi_create_file_list[abs_index].respawn));
5292 ng->respawn = Multi_create_file_list[abs_index].respawn;
5293 nprintf(("Network","Using mission settings for respawn count (%d %d)\n",Netgame.options.respawn,Multi_create_file_list[abs_index].respawn));
5296 case MULTI_CREATE_SHOW_CAMPAIGNS:
5297 // if not on the standalone server
5298 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5299 // get the campaign info
5300 memset(title,0,NAME_LENGTH+1);
5301 if(!mission_campaign_get_info(ng->campaign_name,title,&campaign_type,&max_players, &campaign_desc)) {
5302 memset(ng->campaign_name,0,NAME_LENGTH+1);
5303 ng->max_players = 0;
5305 // if we successfully got the # of players
5307 memset(ng->title,0,NAME_LENGTH+1);
5308 SDL_strlcpy(ng->title, title, SDL_arraysize(ng->title));
5309 ng->max_players = max_players;
5312 nprintf(("Network","MC MAX PLAYERS : %d\n",ng->max_players));
5314 // set the information area text
5315 // multi_common_set_text(ng->title);
5316 multi_common_set_text(campaign_desc);
5318 // if on the standalone server, send a request for the description
5320 // no descriptions currently kept for campaigns
5323 // netgame respawns are always 0 for campaigns (until the first mission is loaded)
5328 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5330 send_netgame_update_packet();
5332 // update all machines about stuff like respawns, etc.
5333 multi_options_update_netgame();
5335 multi_options_update_mission(ng, Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ? 1 : 0);
5340 void multi_create_list_blit_icons(int list_index, int y_start)
5342 multi_create_info *mcip;
5343 fs_builtin_mission *fb;
5346 // get a pointer to the list item
5347 max_index = Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ? Multi_create_mission_count - 1 : Multi_create_campaign_count - 1;
5348 if((list_index < 0) || (list_index > max_index)){
5351 mcip = &Multi_create_file_list[list_index];
5353 // blit the multiplayer type icons
5354 if(mcip->flags & MISSION_TYPE_MULTI_COOP){
5355 if(Multi_common_icons[MICON_COOP] >= 0){
5356 gr_set_bitmap(Multi_common_icons[MICON_COOP], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5357 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5359 } else if(mcip->flags & MISSION_TYPE_MULTI_TEAMS){
5360 if(Multi_common_icons[MICON_TVT] >= 0){
5361 gr_set_bitmap(Multi_common_icons[MICON_TVT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5362 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5364 } else if(mcip->flags & MISSION_TYPE_MULTI_DOGFIGHT){
5365 if(Multi_common_icons[MICON_DOGFIGHT] >= 0){
5366 gr_set_bitmap(Multi_common_icons[MICON_DOGFIGHT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5367 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5371 // if its a valid mission, blit the valid mission icon
5372 if(MULTI_IS_TRACKER_GAME && (mcip->valid_status == MVALID_STATUS_VALID)){
5373 if(Multi_common_icons[MICON_VALID] >= 0){
5374 gr_set_bitmap(Multi_common_icons[MICON_VALID], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5375 gr_bitmap(Mc_icon_valid_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_valid_coords[gr_screen.res][MC_Y_COORD]);
5379 // now see if its a builtin mission
5380 fb = game_find_builtin_mission(mcip->filename);
5381 // if the mission is from volition, blit the volition icon
5382 if((fb != NULL) && (fb->flags & FSB_FROM_VOLITION)){
5383 if(Multi_common_icons[MICON_VOLITION] >= 0){
5384 gr_set_bitmap(Multi_common_icons[MICON_VOLITION], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5385 gr_bitmap(Mc_icon_volition_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_volition_coords[gr_screen.res][MC_Y_COORD]);
5390 // check for mdisk mission
5391 fb = game_find_builtin_mission(mcip->filename);
5392 if((fb != NULL) && (fb->flags & FSB_FROM_MDISK)){
5393 if(Multi_common_icons[MICON_MDISK] >= 0){
5394 gr_set_bitmap(Multi_common_icons[MICON_MDISK], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5395 gr_bitmap(Mc_icon_silent_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_silent_coords[gr_screen.res][MC_Y_COORD]);
5401 void multi_create_accept_hit()
5403 char selected_name[255];
5404 int start_campaign = 0;
5406 // make sure all players have finished joining
5407 if(!multi_netplayer_state_check(NETPLAYER_STATE_JOINED,1)){
5408 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("Please wait until all clients have finished joining",788));
5409 gamesnd_play_iface(SND_GENERAL_FAIL);
5412 gamesnd_play_iface(SND_COMMIT_PRESSED);
5415 // do single mission stuff
5416 switch(Multi_create_list_mode){
5417 case MULTI_CREATE_SHOW_MISSIONS:
5418 if(Multi_create_list_select != -1){
5419 // set the netgame mode
5420 Netgame.campaign_mode = MP_SINGLE;
5422 // setup various filenames and mission names
5423 multi_create_select_to_filename(Multi_create_list_select, selected_name, SDL_arraysize(selected_name));
5424 SDL_strlcpy( Game_current_mission_filename, selected_name, MAX_FILENAME_LEN );
5425 SDL_strlcpy( Netgame.mission_name, selected_name, MAX_FILENAME_LEN );
5428 ml_printf(NOX("Starting single mission %s, with %d players"), Game_current_mission_filename, multi_num_players());
5430 multi_common_add_notify(XSTR("No mission selected!",789));
5435 case MULTI_CREATE_SHOW_CAMPAIGNS:
5436 // do campaign related stuff
5437 if(Multi_create_list_select != -1){
5438 // set the netgame mode
5439 Netgame.campaign_mode = MP_CAMPAIGN;
5441 // start a campaign instead of a single mission
5442 multi_create_select_to_filename(Multi_create_list_select, selected_name, SDL_arraysize(selected_name));
5443 multi_campaign_start(selected_name);
5447 ml_printf(NOX("Starting campaign %s, with %d players"), selected_name, multi_num_players());
5449 multi_common_add_notify(XSTR("No campaign selected!",790));
5455 // if this is a team vs team situation, lock the players send a final team update
5456 if((Netgame.type_flags & NG_TYPE_TEAM) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
5457 multi_team_host_lock_all();
5458 multi_team_send_update();
5461 // if not on the standalone, move to the mission sync state which will take care of everything
5462 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5463 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
5464 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
5466 // otherwise tell the standalone to do so
5468 // when the standalone receives this, he'll do the mission syncing himself
5469 send_mission_sync_packet(MULTI_SYNC_PRE_BRIEFING,start_campaign);
5473 void multi_create_draw_filter_buttons()
5475 // highlight the correct filter button
5476 if ( Multi_create_filter == MISSION_TYPE_MULTI ){
5477 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL].button.draw_forced(2);
5478 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_COOP ) {
5479 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 1].button.draw_forced(2);
5480 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_TEAMS ) {
5481 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 2].button.draw_forced(2);
5482 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_DOGFIGHT ){
5483 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 3].button.draw_forced(2);
5489 void multi_create_set_selected_team(int team)
5493 // if we don't currently have a player selected, don't do anything
5494 if(!Multi_create_plist_select_flag){
5495 gamesnd_play_iface(SND_GENERAL_FAIL);
5498 gamesnd_play_iface(SND_USER_SELECT);
5500 // otherwise attempt to set the team for this guy
5501 player_index = find_player_id(Multi_create_plist_select_id);
5502 if(player_index != -1){
5503 multi_team_set_team(&Net_players[player_index],team);
5507 void multi_create_handle_join(net_player *pl)
5509 // for now just play a bloop sound
5510 gamesnd_play_iface(SND_ICON_DROP_ON_WING);
5513 // fill in net address of player the mouse is over, return player index (or -1 if none)
5514 short multi_create_get_mouse_id()
5516 // determine where he clicked (y pixel value)
5518 Multi_create_player_select_button.get_mouse_pos(NULL,&y);
5520 // select things a little differently if we're in team vs. team or non-team vs. team mode
5522 if(Netgame.type_flags & NG_TYPE_TEAM){
5523 int player_index = -1;
5525 // look through all of team red first
5526 for(idx=0;idx<MAX_PLAYERS;idx++){
5527 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
5530 // if this is the _nth_ guy
5538 // if we still haven't found him yet, look through the green team
5539 if(player_index == -1){
5540 for(idx=0;idx<MAX_PLAYERS;idx++){
5541 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
5543 // if this is the _nth_ guy
5552 if(player_index != -1){
5553 return Net_players[player_index].player_id;
5556 // select the nth active player if possible, disregarding the standalone server
5557 for(idx=0;idx<MAX_PLAYERS;idx++){
5558 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
5561 // if this is the _nth_ guy
5563 return Net_players[idx].player_id;
5572 void multi_create_select_to_filename(int select_index, char *filename, const int max_filelen)
5576 // look through the mission list
5577 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5578 for(idx=0;idx<Multi_create_mission_count;idx++){
5579 if(Multi_create_file_list[idx].flags & Multi_create_filter){
5583 // if we found the item
5584 if(select_index < 0){
5585 SDL_strlcpy(filename, Multi_create_file_list[idx].filename, max_filelen);
5590 // look through the campaign list
5591 else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
5592 for(idx=0;idx<Multi_create_campaign_count;idx++){
5595 // if we found the item
5596 if(select_index < 0){
5597 SDL_strlcpy(filename, Multi_create_file_list[idx].filename, max_filelen);
5603 SDL_strlcpy(filename, "", max_filelen);
5606 int multi_create_select_to_index(int select_index)
5609 int lookup_index = 0;
5611 // look through the mission list
5612 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5613 for(idx=0;idx<Multi_create_mission_count;idx++){
5614 if(Multi_create_file_list[idx].flags & Multi_create_filter){
5618 // if we found the item
5619 if(select_index < lookup_index){
5624 // look through the campaign list
5625 else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
5626 for(idx=0;idx<Multi_create_campaign_count;idx++){
5629 // if we found the item
5630 if(select_index < 0){
5639 int multi_create_ok_to_commit()
5641 int player_count, observer_count, idx;
5642 int notify_of_hacked_ships_tbl = 0;
5643 int notify_of_hacked_weapons_tbl = 0;
5644 char err_string[255];
5648 // make sure we have a valid mission selected
5649 if(Multi_create_list_select < 0){
5653 // if this is not a valid mission, let the player know
5654 abs_index = multi_create_select_to_index(Multi_create_list_select);
5659 // if we're playing with a hacked ships.tbl (on PXO)
5660 notify_of_hacked_ships_tbl = 0;
5661 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5662 if(!Game_ships_tbl_valid){
5663 notify_of_hacked_ships_tbl = 1;
5666 if(Netgame.flags & NG_FLAG_HACKED_SHIPS_TBL){
5667 notify_of_hacked_ships_tbl = 1;
5670 if(!MULTI_IS_TRACKER_GAME){
5671 notify_of_hacked_ships_tbl = 0;
5673 if(notify_of_hacked_ships_tbl){
5674 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){
5679 // if we're playing with a hacked weapons.tbl (on PXO)
5680 notify_of_hacked_weapons_tbl = 0;
5681 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5682 if(!Game_weapons_tbl_valid){
5683 notify_of_hacked_weapons_tbl = 1;
5686 if(Netgame.flags & NG_FLAG_HACKED_WEAPONS_TBL){
5687 notify_of_hacked_weapons_tbl = 1;
5690 if(!MULTI_IS_TRACKER_GAME){
5691 notify_of_hacked_weapons_tbl = 0;
5693 if(notify_of_hacked_weapons_tbl){
5694 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){
5699 // if any of the players have hacked data
5701 for(idx=0; idx<MAX_PLAYERS; idx++){
5702 // look for hacked players
5703 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAXOR)){
5707 // message everyone - haha
5708 if(Net_players[idx].player != NULL){
5709 SDL_snprintf(err_string, SDL_arraysize(err_string), "%s %s", Net_players[idx].player->callsign, XSTR("has hacked tables/data", 1271));
5711 SDL_snprintf(err_string, SDL_arraysize(err_string), "somebody %s", XSTR("has hacked tables/data", 1271));
5713 send_game_chat_packet(Net_player, err_string, MULTI_MSG_ALL, NULL, NULL, 1);
5716 // if we found a hacked set of data
5719 if(MULTI_IS_TRACKER_GAME){
5721 // don't allow squad war matches to continue
5722 if(Netgame.type_flags & NG_TYPE_SW){
5724 // if this is squad war, don't allow it to continue
5725 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));
5730 // otherwise, warn the players that stats will not saved
5732 // if this is squad war, don't allow it to continue
5733 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){
5738 // warn the players that stats will not saved
5739 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){
5744 // non-pxo, just give a notice
5746 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){
5752 // check to see that we don't have too many observers
5753 observer_count = multi_num_observers();
5754 if(observer_count > Netgame.options.max_observers){
5755 // print up the error string
5756 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);
5758 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, err_string);
5762 // check to see that we have a valid # of players for the the # of ships in the game
5763 player_count = multi_num_players();
5764 if(player_count > Netgame.max_players){
5765 // print up the error string
5766 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);
5768 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, err_string);
5772 // check to see if teams are assigned properly in a team vs. team situation
5773 if(Netgame.type_flags & NG_TYPE_TEAM){
5774 if(!multi_team_ok_to_commit()){
5775 gamesnd_play_iface(SND_GENERAL_FAIL);
5776 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("Teams and/or team captains are not assigned properly", 793));
5782 if(!multi_create_verify_cds()){
5783 gamesnd_play_iface(SND_GENERAL_FAIL);
5785 #ifdef MULTIPLAYER_BETA_BUILD
5786 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, "You need 1 CD for every player!");
5788 #ifdef DVD_MESSAGE_HACK
5789 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You need 1 DVD for every 4 players!", 794));
5791 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You need 1 CD for every 4 players!", 794));
5797 // if we're playing on the tracker
5798 if(MULTI_IS_TRACKER_GAME){
5799 #ifdef PXO_CHECK_VALID_MISSIONS
5800 if((Multi_create_file_list == Multi_create_mission_list) && (Multi_create_file_list[abs_index].valid_status != MVALID_STATUS_VALID)){
5801 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){
5808 if(!(Netgame.type_flags & NG_TYPE_SW)){
5809 // if he is playing by himself, tell him stats will not be accepted
5810 if(multi_num_players() == 1){
5811 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){
5819 return multi_sw_ok_to_commit();
5827 int multi_create_verify_cds()
5829 int player_count = multi_num_players();
5833 // count how many cds we have
5835 for(idx=0;idx<MAX_PLAYERS;idx++){
5836 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAS_CD)){
5841 // for the beta, everyone must have a CD
5842 #ifdef MULTIPLAYER_BETA_BUILD
5843 if(multi_cd_count < player_count){
5847 // determine if we have enough
5848 float ratio = (float)player_count / (float)multi_cd_count;
5849 // greater than a 4 to 1 ratio
5855 // we meet the conditions
5859 // returns an index into Multi_create_mission_list
5860 int multi_create_lookup_mission(char *fname)
5864 for(idx=0; idx<Multi_create_mission_count; idx++){
5865 if(!SDL_strcasecmp(fname, Multi_create_mission_list[idx].filename)){
5870 // couldn't find the mission
5874 // returns an index into Multi_create_campaign_list
5875 int multi_create_lookup_campaign(char *fname)
5879 for(idx=0; idx<Multi_create_campaign_count; idx++){
5880 if(!SDL_strcasecmp(fname, Multi_create_campaign_list[idx].filename)){
5885 // couldn't find the campaign
5889 void multi_create_refresh_pxo()
5891 // delete mvalid.cfg if it exists
5892 cf_delete(MULTI_VALID_MISSION_FILE, CF_TYPE_DATA);
5894 // refresh missions from the tracker
5895 multi_update_valid_missions();
5898 void multi_create_sw_clicked()
5901 netgame_info ng_temp;
5904 int file_index = multi_create_select_to_index(Multi_create_list_select);
5906 // either a temporary netgame or the real one
5907 if(MULTIPLAYER_MASTER){
5914 // maybe switch squad war off
5915 if(!Multi_create_sw_checkbox.checked()){
5916 // if the mission selected is a coop mission, go back to coop mode
5917 SDL_assert(file_index != -1);
5918 if(file_index == -1){
5919 ng->type_flags = NG_TYPE_COOP;
5921 if(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_TEAMS){
5922 ng->type_flags = NG_TYPE_TVT;
5923 } else if(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_DOGFIGHT){
5924 ng->type_flags = NG_TYPE_DOGFIGHT;
5926 ng->type_flags = NG_TYPE_COOP;
5929 // switch squad war on
5931 SDL_assert(file_index != -1);
5932 if((file_index == -1) || !(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_TEAMS)){
5933 Multi_create_sw_checkbox.set_state(0);
5935 // at this point we know its safe to switch squad war on
5936 ng->type_flags = NG_TYPE_SW;
5941 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5943 send_netgame_update_packet();
5945 // update all machines about stuff like respawns, etc.
5946 multi_options_update_netgame();
5948 // on the standalone
5950 // standalone will take care of polling the usertracker
5951 multi_options_update_mission(ng, Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ? 1 : 0);
5957 // -------------------------------------------------------------------------------------------------------------
5959 // MULTIPLAYER HOST OPTIONS SCREEN
5962 #define MULTI_HO_NUM_BUTTONS 12
5963 #define MULTI_HO_NUM_RADIO_BUTTONS 10
5967 #define MULTI_HO_PALETTE "InterfacePalette"
5969 static const char *Multi_ho_bitmap_fname[GR_NUM_RESOLUTIONS] = {
5970 "MultiHost", // GR_640
5971 "2_MultiHost" // GR_1024
5974 static const char *Multi_ho_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
5975 "MultiHost-M", // GR_640
5976 "2_MultiHost-M" // GR_1024
5980 UI_WINDOW Multi_ho_window; // the window object for the join screen
5981 UI_INPUTBOX Multi_ho_respawns; // the # of respawns allowed in the game
5982 UI_INPUTBOX Multi_ho_time_limit; // mission time limit
5983 UI_INPUTBOX Multi_ho_voice_wait; // wait time between tokens
5984 UI_INPUTBOX Multi_ho_kill_limit; // kill limit in a furball mission
5985 UI_INPUTBOX Multi_ho_obs; // # of observers we'll allow
5986 int Multi_ho_bitmap; // the background bitmap
5988 // constants for coordinate lookup
5989 #define MULTI_HO_X_COORD 0
5990 #define MULTI_HO_Y_COORD 1
5991 #define MULTI_HO_W_COORD 2
5992 #define MULTI_HO_H_COORD 3
5993 #define MULTI_HO_TEXT_X_COORD 4
5994 #define MULTI_HO_TEXT_Y_COORD 5
5997 #define MULTI_HO_MSG_RANK 0 // highest ranking players can do messaging
5998 #define MULTI_HO_MSG_LEADER 1 // wing/team leaders can do messaging
5999 #define MULTI_HO_MSG_ANY 2 // any player can do messaging
6000 #define MULTI_HO_MSG_HOST 3 // only the host can do messaging
6001 #define MULTI_HO_END_RANK 4 // highest rank can and host can end mission
6002 #define MULTI_HO_END_LEADER 5 // wing/team leaders and host can end the mission
6003 #define MULTI_HO_END_ANY 6 // any player can end the mission
6004 #define MULTI_HO_END_HOST 7 // only host can end the mission
6005 #define MULTI_HO_VOICE_ON 8 // voice toggled on
6006 #define MULTI_HO_VOICE_OFF 9 // voice toggled off
6007 #define MULTI_HO_HOST_MODIFIES 10 // only the host or team captains can modify ships/weapons in briefing
6008 #define MULTI_HO_ACCEPT 11 // accept button
6010 ui_button_info Multi_ho_buttons[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_BUTTONS] = {
6013 // who is allowed to message
6014 ui_button_info("MH_00", 13, 157, -1, -1, 0), // highest rank
6015 ui_button_info("MH_01", 13, 179, -1, -1, 1), // team/wing leader
6016 ui_button_info("MH_02", 13, 200, -1, -1, 2), // any
6017 ui_button_info("MH_03", 13, 223, -1, -1, 3), // host
6019 // who is allowed to end the mission
6020 ui_button_info("MH_09", 13, 273, -1, -1, 9), // highest rank
6021 ui_button_info("MH_10", 13, 295, -1, -1, 10), // team/wing leader
6022 ui_button_info("MH_11", 13, 317, -1, -1, 11), // any
6023 ui_button_info("MH_12", 13, 339, -1, -1, 12), // host
6025 // voice on/off button
6026 ui_button_info("MH_14", 396, 156, -1, -1, 14),
6027 ui_button_info("MH_15", 453, 156, -1, -1, 15),
6029 // host modifies ships
6030 ui_button_info("MH_19", 215, 410, -1, -1, 19),
6033 ui_button_info("MH_18", 560, 411, -1, -1, 18),
6035 // who is allowed to message
6036 ui_button_info("MH_00", 3, 160, 46, 166, 0), // highest rank
6037 ui_button_info("MH_01", 3, 179, 46, 185, 1), // team/wing leader
6038 ui_button_info("MH_02", 3, 196, 46, 203, 2), // any
6039 ui_button_info("MH_03", 3, 214, 46, 220, 3), // host
6041 // who is allowed to end the mission
6042 ui_button_info("MH_04", 3, 257, 46, 265, 4), // highest rank
6043 ui_button_info("MH_05", 3, 276, 46, 283, 5), // team/wing leader
6044 ui_button_info("MH_06", 3, 294, 46, 300, 6), // any
6045 ui_button_info("MH_07", 3, 311, 46, 317, 7), // host
6047 // voice on/off button
6048 ui_button_info("MH_09", 542, 158, 545, 185, 9),
6049 ui_button_info("MH_10", 598, 158, 604, 185, 10),
6051 // host modifies ships
6052 ui_button_info("MH_13", 542, 377, 437, 363, 13),
6055 ui_button_info("MH_14", 572, 428, 580, 414, 14),
6059 // who is allowed to message
6060 ui_button_info("2_MH_00", 5, 256, 73, 269, 0), // highest rank
6061 ui_button_info("2_MH_01", 5, 286, 73, 297, 1), // team/wing leader
6062 ui_button_info("2_MH_02", 5, 314, 73, 325, 2), // any
6063 ui_button_info("2_MH_03", 5, 341, 73, 352, 3), // host
6065 // who is allowed to end the mission
6066 ui_button_info("2_MH_04", 5, 412, 73, 425, 4), // highest rank
6067 ui_button_info("2_MH_05", 5, 442, 73, 452, 5), // team/wing leader
6068 ui_button_info("2_MH_06", 5, 470, 73, 480, 6), // any
6069 ui_button_info("2_MH_07", 5, 497, 73, 508, 7), // host
6071 // voice on/off button
6072 ui_button_info("2_MH_09", 867, 253, 872, 296, 9),
6073 ui_button_info("2_MH_10", 957, 253, 966, 296, 10),
6075 // host modifies ships
6076 ui_button_info("2_MH_13", 867, 603, 784, 581, 13),
6079 ui_button_info("2_MH_14", 916, 685, 925, 665, 14),
6082 UI_XSTR Multi_ho_text[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_BUTTONS] = {
6084 // not needed for FS1
6086 {"Highest rank", 1280, 46, 166, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_RANK].button},
6087 {"Team / wing-leader", 1281, 46, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_LEADER].button},
6088 {"Any", 1282, 46, 203, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_ANY].button},
6089 {"Host", 1283, 46, 220, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_HOST].button},
6090 {"Highest rank", 1280, 46, 265, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_RANK].button},
6091 {"Team / wing-leader", 1281, 46, 283, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_LEADER].button},
6092 {"Any", 1282, 46, 300, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_ANY].button},
6093 {"Host", 1283, 46, 317, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_HOST].button},
6094 {"On", 1285, 545, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_VOICE_ON].button},
6095 {"Off", 1286, 604, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_VOICE_OFF].button},
6096 {"Host modifies ships", 1287, 437, 363, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_HOST_MODIFIES].button},
6097 {"Exit", 1417, 572, 418, UI_XSTR_COLOR_PINK, -1, &Multi_ho_buttons[0][MULTI_HO_ACCEPT].button},
6101 // not needed for FS1
6103 {"Highest rank", 1280, 62, 269, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_RANK].button},
6104 {"Team / wing-leader", 1281, 62, 297, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_LEADER].button},
6105 {"Any", 1282, 62, 325, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_ANY].button},
6106 {"Host", 1283, 62, 352, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_HOST].button},
6107 {"Highest rank", 1280, 62, 425, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_RANK].button},
6108 {"Team / wing-leader", 1281, 62, 452, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_LEADER].button},
6109 {"Any", 1282, 62, 480, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_ANY].button},
6110 {"Host", 1283, 62, 508, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_HOST].button},
6111 {"On", 1285, 877, 294, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_VOICE_ON].button},
6112 {"Off", 1286, 967, 293, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_VOICE_OFF].button},
6113 {"Host modifies ships", 1287, 869, 589, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_HOST_MODIFIES].button},
6114 {"Exit", 1417, 953, 672, UI_XSTR_COLOR_PINK, -1, &Multi_ho_buttons[1][MULTI_HO_ACCEPT].button},
6119 // radio button controls
6120 #define MULTI_HO_NUM_RADIO_GROUPS 3
6121 #define MULTI_HO_MSG_GROUP 0 // group dealing with squadmate messaging
6122 #define MULTI_HO_END_GROUP 1 // group dealing with ending the mission
6123 #define MULTI_HO_VOICE_GROUP 2 // group dealing with voice stuff
6124 int Multi_ho_radio_groups[MULTI_HO_NUM_RADIO_GROUPS] = { // currently selected button in the radio button group
6127 int Multi_ho_radio_info[MULTI_HO_NUM_RADIO_BUTTONS][3] = { // info related to each of the radio buttons themselves
6128 // { group #, value, button id# }
6129 {0, 0, 0}, // highest ranking players can do messaging
6130 {0, 1, 1}, // wing/team leaders can do messaging
6131 {0, 2, 2}, // any player can do messaging
6132 {0, 3, 3}, // only host can do messaging
6133 {1, 0, 4}, // highest rank and host can end the mission
6134 {1, 1, 5}, // team/wing leader can end the mission
6135 {1, 2, 6}, // any player can end the mission
6136 {1, 3, 7}, // only the host can end the mission
6137 {2, 0, 8}, // voice toggled on
6138 {2, 1, 9} // voice toggled off
6142 #define MULTI_HO_NUM_SLIDERS 3
6143 #define MULTI_HO_SLIDER_VOICE_QOS 0 // voice quality of sound
6144 #define MULTI_HO_SLIDER_VOICE_DUR 1 // max duration of voice recording
6145 #define MULTI_HO_SLIDER_SKILL 2 // skill level
6147 const char *filename;
6152 UI_DOT_SLIDER_NEW slider; // because we have a class inside this struct, we need the constructor below..
6154 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){}
6156 ho_sliders Multi_ho_sliders[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_SLIDERS] = {
6159 ho_sliders("MH_16", 396, 210, -1, -1, 16, 19, 10), // voice qos
6160 ho_sliders("MH_17", 396, 263, -1, -1, 17, 19, 10), // voice duration
6161 ho_sliders("MH_13", 10, 387, -1, -1, 13, 36, 5), // skill level
6163 ho_sliders("MH_11", 428, 214, 437, 199, 11, 19, 10), // voice qos
6164 ho_sliders("MH_12", 428, 261, 437, 246, 12, 19, 10), // voice duration
6165 ho_sliders("MH_08", 237, 454, 230, 411, 8, 36, 5), // skill level
6169 ho_sliders("2_MH_11", 684, 343, 690, 323, 11, 32, 10), // voice qos
6170 ho_sliders("2_MH_12", 685, 418, 837, 468, 12, 32, 10), // voice duration
6171 ho_sliders("2_MH_08", 379, 727, 369, 663, 8, 60, 5), // skill level
6175 int Multi_ho_mission_respawn;
6177 int Multi_ho_host_modifies;
6179 // whether or not any of the inputboxes on this screen had focus last frame
6180 int Multi_ho_lastframe_input = 0;
6182 // game information text areas
6186 #define MULTI_HO_NUM_TITLES 14
6188 UI_XSTR Multi_ho_titles[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_TITLES] = {
6190 { "AI Orders", 1289, 32, 144, UI_XSTR_COLOR_GREEN, -1, NULL },
6191 { "End Mission", 1290, 32, 242, UI_XSTR_COLOR_GREEN, -1, NULL },
6192 { "Time Limit", 1291, 32, 347, UI_XSTR_COLOR_GREEN, -1, NULL },
6193 { "Min", 1292, 74, 362, UI_XSTR_COLOR_GREEN, -1, NULL },
6194 { "Respawn Limit", 1288, 32, 378, UI_XSTR_COLOR_GREEN, -1, NULL },
6195 { "Kill Limit", 1293, 32, 409, UI_XSTR_COLOR_GREEN, -1, NULL },
6196 { "Observers", 1294, 32, 441, UI_XSTR_COLOR_GREEN, -1, NULL },
6197 { "Skill Level", 1284, 230, 411, UI_XSTR_COLOR_GREEN, -1, NULL },
6198 { "Voice Transmission", 1295, 437, 144, UI_XSTR_COLOR_GREEN, -1, NULL },
6199 { "Voice Quality", 1296, 437, 199, UI_XSTR_COLOR_GREEN, -1, NULL },
6200 { "Message Duration", 1297, 437, 246, UI_XSTR_COLOR_GREEN, -1, NULL },
6201 { "sec", 1522, 523, 292, UI_XSTR_COLOR_GREEN, -1, NULL },
6202 { "sec", 1523, 523, 332, UI_XSTR_COLOR_GREEN, -1, NULL },
6203 { "Voice Wait", 1298, 437, 313, UI_XSTR_COLOR_GREEN, -1, NULL },
6206 { "AI Orders", 1289, 48, 238, UI_XSTR_COLOR_GREEN, -1, NULL },
6207 { "End Mission", 1290, 48, 394, UI_XSTR_COLOR_GREEN, -1, NULL },
6208 { "Time Limit", 1291, 50, 568, UI_XSTR_COLOR_GREEN, -1, NULL },
6209 { "Min", 1292, 119, 581, UI_XSTR_COLOR_GREEN, -1, NULL },
6210 { "Respawn Limit", 1288, 50, 618, UI_XSTR_COLOR_GREEN, -1, NULL },
6211 { "Kill Limit", 1293, 50, 668, UI_XSTR_COLOR_GREEN, -1, NULL },
6212 { "Observers", 1294, 50, 718, UI_XSTR_COLOR_GREEN, -1, NULL },
6213 { "Skill Level", 1284, 398, 670, UI_XSTR_COLOR_GREEN, -1, NULL },
6214 { "Voice Transmission", 1295, 869, 239, UI_XSTR_COLOR_GREEN, -1, NULL },
6215 { "Voice Quality", 1296, 690, 331, UI_XSTR_COLOR_GREEN, -1, NULL },
6216 { "Message Duration", 1297, 690, 405, UI_XSTR_COLOR_GREEN, -1, NULL },
6217 { "sec", 1522, 837, 467, UI_XSTR_COLOR_GREEN, -1, NULL },
6218 { "sec", 1523, 837, 534, UI_XSTR_COLOR_GREEN, -1, NULL },
6219 { "Voice Wait", 1298, 742, 510, UI_XSTR_COLOR_GREEN, -1, NULL },
6224 // mission time limit input box
6225 int Ho_time_coords[GR_NUM_RESOLUTIONS][4] = {
6238 // furball kill limit input box
6239 int Ho_kill_coords[GR_NUM_RESOLUTIONS][4] = {
6252 // voice recording duration text display area
6253 int Ho_vd_coords[GR_NUM_RESOLUTIONS][4] = {
6266 // voice token wait input box
6267 int Ho_vw_coords[GR_NUM_RESOLUTIONS][6] = {
6280 // observer count input box
6281 int Ho_obs_coords[GR_NUM_RESOLUTIONS][4] = {
6294 // skill text description area
6295 int Ho_st_coords[GR_NUM_RESOLUTIONS][4] = {
6308 // respawn input box
6309 int Ho_rsp_coords[GR_NUM_RESOLUTIONS][6] = {
6322 // respawn max text area
6323 int Ho_max_rsp_coords[GR_NUM_RESOLUTIONS][2] = {
6336 // maximum values for various input boxes (to notify user of overruns)
6337 #define MULTI_HO_MAX_TIME_LIMIT 500
6338 #define MULTI_HO_MAX_TOKEN_WAIT 5
6339 #define MULTI_HO_MAX_KILL_LIMIT 9999
6340 #define MULTI_HO_MAX_OBS 4
6342 // LOCAL function definitions
6343 void multi_ho_check_buttons();
6344 void multi_ho_button_pressed(int n);
6345 void multi_ho_draw_radio_groups();
6346 void multi_ho_accept_hit();
6347 void multi_ho_get_options();
6348 void multi_ho_apply_options();
6349 void multi_ho_display_record_time();
6350 int multi_ho_check_values();
6351 void multi_ho_check_focus();
6352 void multi_ho_blit_max_respawns();
6353 void multi_ho_display_skill_level();
6355 void multi_host_options_init()
6359 // create the interface window
6360 Multi_ho_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
6361 Multi_ho_window.set_mask_bmap(Multi_ho_bitmap_mask_fname[gr_screen.res]);
6363 // load the background bitmap
6364 Multi_ho_bitmap = bm_load(Multi_ho_bitmap_fname[gr_screen.res]);
6365 if(Multi_ho_bitmap < 0){
6366 // we failed to load the bitmap - this is very bad
6370 // initialize the common notification messaging
6371 multi_common_notify_init();
6373 // use the common interface palette
6374 multi_common_set_palette();
6376 // create the interface buttons
6377 for(idx=0;idx<MULTI_HO_NUM_BUTTONS;idx++){
6378 // create the object
6379 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);
6381 // set the sound to play when highlighted
6382 Multi_ho_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
6384 // set the ani for the button
6385 Multi_ho_buttons[gr_screen.res][idx].button.set_bmaps(Multi_ho_buttons[gr_screen.res][idx].filename);
6387 // set the hotspot, ignoring the skill level button
6388 Multi_ho_buttons[gr_screen.res][idx].button.link_hotspot(Multi_ho_buttons[gr_screen.res][idx].hotspot);
6392 Multi_ho_window.add_XSTR(&Multi_ho_text[gr_screen.res][idx]);
6398 for(idx=0; idx<MULTI_HO_NUM_TITLES; idx++){
6399 Multi_ho_window.add_XSTR(&Multi_ho_titles[gr_screen.res][idx]);
6403 // create the interface sliders
6404 for(idx=0; idx<MULTI_HO_NUM_SLIDERS; idx++){
6405 // create the object
6406 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);
6409 // create the respawn count input box
6410 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);
6411 // if we're in campaign mode, disable it
6412 if(Netgame.campaign_mode == MP_CAMPAIGN){
6413 Multi_ho_respawns.set_text(XSTR("NA",795)); // [[ Not applicable ]]
6414 Multi_ho_respawns.disable();
6416 Multi_ho_respawns.set_valid_chars("0123456789");
6419 // create the time limit input box
6420 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);
6421 Multi_ho_time_limit.set_valid_chars("-0123456789");
6423 // create the voice token wait input box
6424 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);
6425 Multi_ho_voice_wait.set_valid_chars("01243456789");
6427 // create the furball kill limit input box
6428 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);
6429 Multi_ho_kill_limit.set_valid_chars("0123456789");
6431 // create the observer limit input box
6432 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);
6433 Multi_ho_obs.set_valid_chars("01234");
6435 // load in the current netgame defaults
6436 multi_ho_get_options();
6438 // whether or not any of the inputboxes on this screen had focus last frame
6439 Multi_ho_lastframe_input = 0;
6441 // get the # of respawns for the currently selected mission (if any)
6442 if(Multi_create_list_select != -1){
6443 int abs_index = multi_create_select_to_index(Multi_create_list_select);
6445 // if he has a valid mission selected
6447 Multi_ho_mission_respawn = (int)Multi_create_file_list[abs_index].respawn;
6449 Multi_ho_mission_respawn = -1;
6452 Multi_ho_mission_respawn = -1;
6456 void multi_ho_update_sliders()
6458 // game skill slider
6459 if (Game_skill_level != Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos) {
6460 if ( !(Netgame.type_flags & NG_TYPE_TEAM) ){
6461 Game_skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6462 gamesnd_play_iface(SND_USER_SELECT);
6464 Game_skill_level = NUM_SKILL_LEVELS / 2;
6468 // get the voice qos options
6469 if (Netgame.options.voice_qos != (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1)) {
6470 Netgame.options.voice_qos = (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1);
6471 gamesnd_play_iface(SND_USER_SELECT);
6474 // get the voice duration options
6475 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)) {
6476 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);
6477 gamesnd_play_iface(SND_USER_SELECT);
6482 void multi_host_options_do()
6487 k = Multi_ho_window.process();
6490 // process any keypresses
6493 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
6496 case KEY_CTRLED + SDLK_RETURN :
6497 gamesnd_play_iface(SND_COMMIT_PRESSED);
6498 multi_ho_accept_hit();
6502 // process any button clicks
6503 multi_ho_check_buttons();
6505 // update the sliders
6506 multi_ho_update_sliders();
6508 // make sure that the chatbox inputbox and any inputbox on this screen are mutually exclusive in terms of focus
6509 multi_ho_check_focus();
6511 // draw the background, etc
6513 GR_MAYBE_CLEAR_RES(Multi_ho_bitmap);
6514 if(Multi_ho_bitmap != -1){
6515 gr_set_bitmap(Multi_ho_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
6518 Multi_ho_window.draw();
6520 // draw all the radio buttons properly
6521 multi_ho_draw_radio_groups();
6523 // display any pending notification messages
6524 multi_common_notify_do();
6526 // display the voice record time settings
6527 multi_ho_display_record_time();
6529 // maybe display the max # of respawns next to the respawn input box
6530 multi_ho_blit_max_respawns();
6532 // blit the proper skill level
6533 multi_ho_display_skill_level();
6535 // blit the "host modifies button"
6536 if(Multi_ho_host_modifies){
6537 Multi_ho_buttons[gr_screen.res][MULTI_HO_HOST_MODIFIES].button.draw_forced(2);
6540 // process and show the chatbox thingie
6544 Multi_ho_window.draw_tooltip();
6546 // display the voice status indicator
6547 multi_common_voice_display_status();
6553 void multi_host_options_close()
6555 // unload any bitmaps
6556 if(!bm_unload(Multi_ho_bitmap)){
6557 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_ho_bitmap_fname[gr_screen.res]));
6560 // destroy the UI_WINDOW
6561 Multi_ho_window.destroy();
6564 void multi_ho_check_buttons()
6567 for(idx=0;idx<MULTI_HO_NUM_BUTTONS;idx++){
6568 // we only really need to check for one button pressed at a time, so we can break after
6570 if(Multi_ho_buttons[gr_screen.res][idx].button.pressed()){
6571 multi_ho_button_pressed(idx);
6577 void multi_ho_button_pressed(int n)
6579 int radio_index,idx;
6580 int x_pixel,y_pixel;
6582 // get the pixel position of the click
6583 Multi_ho_buttons[gr_screen.res][n].button.get_mouse_pos(&x_pixel,&y_pixel);
6586 // clicked on the accept button
6587 case MULTI_HO_ACCEPT:
6588 gamesnd_play_iface(SND_COMMIT_PRESSED);
6589 multi_ho_accept_hit();
6592 // clicked on the host/captains only modify button
6593 case MULTI_HO_HOST_MODIFIES:
6594 // toggle it on or off
6595 Multi_ho_host_modifies = !Multi_ho_host_modifies;
6596 gamesnd_play_iface(SND_USER_SELECT);
6600 // look through the radio buttons and see which one this corresponds to
6602 for(idx=0;idx<MULTI_HO_NUM_RADIO_BUTTONS;idx++){
6603 if(Multi_ho_radio_info[idx][2] == n){
6608 SDL_assert(radio_index != -1);
6610 // check to see if a radio button was pressed
6611 if(radio_index < MULTI_HO_NUM_RADIO_BUTTONS){
6612 // see if this value is already picked for this radio group
6613 if(Multi_ho_radio_groups[Multi_ho_radio_info[radio_index][0]] != Multi_ho_radio_info[radio_index][1]){
6614 gamesnd_play_iface(SND_USER_SELECT);
6615 Multi_ho_radio_groups[Multi_ho_radio_info[radio_index][0]] = Multi_ho_radio_info[radio_index][1];
6617 gamesnd_play_iface(SND_GENERAL_FAIL);
6622 void multi_ho_draw_radio_groups()
6626 // go through each item and draw it if it is the selected button in its respective group
6627 for(idx=0;idx<MULTI_HO_NUM_RADIO_BUTTONS;idx++){
6628 /// if this button is the currently selected one in its group
6629 if(Multi_ho_radio_info[idx][1] == Multi_ho_radio_groups[Multi_ho_radio_info[idx][0]]){
6630 Multi_ho_buttons[gr_screen.res][Multi_ho_radio_info[idx][2]].button.draw_forced(2);
6635 void multi_ho_accept_hit()
6639 // check the values in the input boxes
6640 if(!multi_ho_check_values()){
6644 // zero out the netgame flags
6647 // set default options
6648 Netgame.options.flags = (MSO_FLAG_INGAME_XFER | MSO_FLAG_ACCEPT_PIX);
6650 // set the squadmate messaging flags
6651 switch(Multi_ho_radio_groups[MULTI_HO_MSG_GROUP]){
6653 Netgame.options.squad_set = MSO_SQUAD_RANK;
6656 Netgame.options.squad_set = MSO_SQUAD_LEADER;
6659 Netgame.options.squad_set = MSO_SQUAD_ANY;
6662 Netgame.options.squad_set = MSO_SQUAD_HOST;
6668 // set the end mission flags
6669 switch(Multi_ho_radio_groups[MULTI_HO_END_GROUP]){
6671 Netgame.options.endgame_set = MSO_END_RANK;
6674 Netgame.options.endgame_set = MSO_END_LEADER;
6677 Netgame.options.endgame_set = MSO_END_ANY;
6680 Netgame.options.endgame_set = MSO_END_HOST;
6686 // set the voice toggle
6687 switch(Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP]){
6689 Netgame.options.flags &= ~(MSO_FLAG_NO_VOICE);
6692 Netgame.options.flags |= MSO_FLAG_NO_VOICE;
6698 // get the voice qos options
6699 Netgame.options.voice_qos = (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1);
6701 // get the voice duration options
6702 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);
6704 // set the skill level. If in team vs. team mode, preserve the old setting before saving
6705 // the pilot file. I'll bet that this doesn't work though because the pilot file gets
6706 // written in a bunch of locations....sigh.
6707 if ( !(Netgame.type_flags & NG_TYPE_TEAM) ){
6708 Game_skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6710 Game_skill_level = NUM_SKILL_LEVELS / 2;
6713 // set the netgame respawn count
6714 // maybe warn the user that respawns will not be used for a campaign mission
6715 if(Netgame.campaign_mode == MP_SINGLE){
6716 Multi_ho_respawns.get_text(resp_str);
6717 uint temp_respawn = (uint)atoi(resp_str);
6718 // if he currently has no mission selected, let the user set any # of respawns
6719 if((int)temp_respawn > Multi_ho_mission_respawn){
6720 if(Multi_ho_mission_respawn == -1){
6721 Netgame.respawn = temp_respawn;
6722 Netgame.options.respawn = temp_respawn;
6724 // this should have been taken care of by the interface code
6729 Netgame.options.respawn = temp_respawn;
6730 Netgame.respawn = temp_respawn;
6734 // get the mission time limit
6735 Multi_ho_time_limit.get_text(resp_str);
6736 int temp_time = atoi(resp_str);
6738 Netgame.options.mission_time_limit = fl2f(-1.0f);
6739 } else if(temp_time > MULTI_HO_MAX_TIME_LIMIT){
6742 Netgame.options.mission_time_limit = fl2f(60.0f * (float)temp_time);
6745 // get observer count options
6746 Multi_ho_obs.get_text(resp_str);
6747 int temp_obs = atoi(resp_str);
6748 if(temp_obs > MULTI_HO_MAX_OBS){
6751 Netgame.options.max_observers = (ubyte)temp_obs;
6753 // get the furball kill limit
6754 Multi_ho_kill_limit.get_text(resp_str);
6755 int temp_kills = atoi(resp_str);
6756 if(temp_kills > MULTI_HO_MAX_KILL_LIMIT){
6759 Netgame.options.kill_limit = temp_kills;
6761 // get the token wait limit
6762 Multi_ho_voice_wait.get_text(resp_str);
6763 int temp_wait = atoi(resp_str);
6764 if(temp_wait > MULTI_HO_MAX_TOKEN_WAIT){
6767 Netgame.options.voice_token_wait = (temp_wait * 1000);
6769 // set the netgame option
6770 Netgame.options.skill_level = (ubyte)Game_skill_level;
6772 // get whether we're in host/captains only modify mode
6773 Netgame.options.flags &= ~(MSO_FLAG_SS_LEADERS);
6774 if(Multi_ho_host_modifies){
6775 Netgame.options.flags |= MSO_FLAG_SS_LEADERS;
6778 // store these values locally
6779 memcpy(&Player->m_local_options,&Net_player->p_info.options,sizeof(multi_local_options));
6780 memcpy(&Player->m_server_options,&Netgame.options,sizeof(multi_server_options));
6781 write_pilot_file(Player);
6783 // apply any changes in settings (notify everyone of voice qos changes, etc)
6784 multi_ho_apply_options();
6786 // move back to the create game screen
6787 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
6790 void multi_ho_get_options()
6794 // set the squadmate messaging buttons
6795 switch(Netgame.options.squad_set){
6796 case MSO_SQUAD_RANK :
6797 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 0;
6799 case MSO_SQUAD_LEADER:
6800 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 1;
6803 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 2;
6805 case MSO_SQUAD_HOST:
6806 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 3;
6812 // set the mission end buttons
6813 switch(Netgame.options.endgame_set){
6815 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 0;
6817 case MSO_END_LEADER:
6818 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 1;
6821 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 2;
6824 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 3;
6830 // set the voice toggle buttons
6831 if(Netgame.options.flags & MSO_FLAG_NO_VOICE){
6832 Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP] = 1;
6834 Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP] = 0;
6837 // get the voice qos options
6838 SDL_assert((Netgame.options.voice_qos >= 1) && (Netgame.options.voice_qos <= 10));
6839 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos = (Netgame.options.voice_qos - 1);
6841 // get the voice duration options
6842 SDL_assert((Netgame.options.voice_record_time > 0) && (Netgame.options.voice_record_time <= MULTI_VOICE_MAX_TIME));
6843 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos = ((int)((float)Netgame.options.voice_record_time / 500.0f)) - 1;
6845 // get the current skill level
6846 SDL_assert((Game_skill_level >= 0) && (Game_skill_level < NUM_SKILL_LEVELS));
6847 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos = Game_skill_level;
6849 // get the # of observers
6850 memset(resp_str,0,10);
6851 SDL_snprintf(resp_str,SDL_arraysize(resp_str),"%d",Netgame.options.max_observers);
6852 Multi_ho_obs.set_text(resp_str);
6854 // set the respawn count
6855 if(Netgame.campaign_mode == MP_SINGLE){
6856 memset(resp_str,0,10);
6857 SDL_snprintf(resp_str,SDL_arraysize(resp_str),"%d",Netgame.respawn);
6858 Multi_ho_respawns.set_text(resp_str);
6861 // set the mission time limit
6862 memset(resp_str,0,10);
6863 float tl = f2fl(Netgame.options.mission_time_limit);
6864 SDL_snprintf(resp_str,SDL_arraysize(resp_str),"%d",(int)(tl / 60.0f));
6865 Multi_ho_time_limit.set_text(resp_str);
6867 // set the furball kill limit
6868 memset(resp_str,0,10);
6869 SDL_snprintf(resp_str,SDL_arraysize(resp_str),"%d",Netgame.options.kill_limit);
6870 Multi_ho_kill_limit.set_text(resp_str);
6872 // set the token wait time
6873 memset(resp_str,0,10);
6874 SDL_snprintf(resp_str,SDL_arraysize(resp_str),"%d",Netgame.options.voice_token_wait / 1000);
6875 Multi_ho_voice_wait.set_text(resp_str);
6877 // get whether we're in host/captains only modify mode
6878 if(Netgame.options.flags & MSO_FLAG_SS_LEADERS){
6879 Multi_ho_host_modifies = 1;
6881 Multi_ho_host_modifies = 0;
6885 void multi_ho_apply_options()
6887 // if the voice qos or duration has changed, apply the change
6888 multi_voice_maybe_update_vars(Netgame.options.voice_qos,Netgame.options.voice_record_time);
6890 // send an options update
6891 multi_options_update_netgame();
6894 // display the voice record time settings
6895 void multi_ho_display_record_time()
6898 int full_seconds, half_seconds;
6901 memset(time_str,0,30);
6904 full_seconds = (((Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 500) / 1000);
6906 // get the half-seconds
6907 half_seconds = ((((Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 500) % 1000) / 500) * 5;
6909 // format the string
6910 SDL_snprintf(time_str,SDL_arraysize(time_str),"%d.%d",full_seconds,half_seconds);
6911 gr_set_color_fast(&Color_bright);
6912 gr_string(Ho_vd_coords[gr_screen.res][MULTI_HO_X_COORD],Ho_vd_coords[gr_screen.res][MULTI_HO_Y_COORD],time_str);
6915 int multi_ho_check_values()
6919 memset(val_txt,0,255);
6921 // check against respawn settings
6922 if(Multi_ho_mission_respawn != -1){
6923 Multi_ho_respawns.get_text(val_txt);
6924 // if the value is invalid, let the user know
6925 if(atoi(val_txt) > Multi_ho_mission_respawn){
6926 memset(val_txt,0,255);
6927 SDL_snprintf(val_txt,SDL_arraysize(val_txt),XSTR("Warning\nRespawn count in greater than mission specified max (%d)",796),Multi_ho_mission_respawn);
6928 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6933 // check against mission time limit max
6934 Multi_ho_time_limit.get_text(val_txt);
6935 // if the value is invalid, force it to be valid
6936 if(atoi(val_txt) > MULTI_HO_MAX_TIME_LIMIT){
6937 memset(val_txt,0,255);
6938 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);
6939 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6943 // check against max observer limit
6944 Multi_ho_obs.get_text(val_txt);
6945 // if the value is invalid, force it to be valid
6946 if(atoi(val_txt) > MULTI_HO_MAX_OBS){
6947 memset(val_txt,0,255);
6948 SDL_snprintf(val_txt,SDL_arraysize(val_txt),XSTR("Warning\nObserver count is greater than max allowed (%d)",798),MULTI_HO_MAX_OBS);
6949 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6953 // check against furball kill limit
6954 Multi_ho_kill_limit.get_text(val_txt);
6955 // if the value is invalid, force it to be valid
6956 if(atoi(val_txt) > MULTI_HO_MAX_KILL_LIMIT){
6957 memset(val_txt,0,255);
6958 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);
6959 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6963 // check against the token wait limit
6964 Multi_ho_voice_wait.get_text(val_txt);
6965 if(atoi(val_txt) > MULTI_HO_MAX_TOKEN_WAIT){
6966 memset(val_txt,0,255);
6967 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);
6968 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6972 // all values are valid
6976 void multi_ho_check_focus()
6978 // if an inputbox has been pressed (hit enter), lose its focus
6979 if (Multi_ho_respawns.pressed() || Multi_ho_time_limit.pressed() || Multi_ho_voice_wait.pressed() || Multi_ho_kill_limit.pressed() || Multi_ho_obs.pressed()) {
6980 Multi_ho_respawns.clear_focus();
6981 Multi_ho_time_limit.clear_focus();
6982 Multi_ho_voice_wait.clear_focus();
6983 Multi_ho_kill_limit.clear_focus();
6984 Multi_ho_obs.clear_focus();
6985 gamesnd_play_iface(SND_COMMIT_PRESSED);
6986 chatbox_set_focus();
6987 Multi_ho_lastframe_input = 0;
6989 } else if(!Multi_ho_lastframe_input) {
6990 // if we didn't have focus last frame
6991 if(Multi_ho_respawns.has_focus() || Multi_ho_time_limit.has_focus() || Multi_ho_kill_limit.has_focus() || Multi_ho_voice_wait.has_focus() ){
6992 chatbox_lose_focus();
6994 Multi_ho_lastframe_input = 1;
6997 // if we _did_ have focus last frame
6999 // if we no longer have focus on any of the input boxes, set the focus on the chatbox
7000 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()){
7001 chatbox_set_focus();
7003 // if the chatbox now has focus, clear all focus from our inputboxes
7004 else if (chatbox_has_focus()) {
7005 Multi_ho_respawns.clear_focus();
7006 Multi_ho_time_limit.clear_focus();
7007 Multi_ho_kill_limit.clear_focus();
7008 Multi_ho_voice_wait.clear_focus();
7010 Multi_ho_lastframe_input = 0;
7015 void multi_ho_blit_max_respawns()
7019 // if we're in campaign mode, do nothing
7020 if(Netgame.campaign_mode == MP_CAMPAIGN){
7024 // otherwise blit the max as specified by the current mission file
7025 SDL_snprintf(string,SDL_arraysize(string),"(%d)",Multi_ho_mission_respawn);
7026 gr_set_color_fast(&Color_normal);
7027 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);
7030 void multi_ho_display_skill_level()
7032 int skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
7035 SDL_assert((skill_level >= 0) && (skill_level < NUM_SKILL_LEVELS));
7036 if((skill_level < 0) || (skill_level >= NUM_SKILL_LEVELS)){
7040 gr_set_color_fast(&Color_bright);
7041 gr_string(Ho_st_coords[gr_screen.res][0], Ho_st_coords[gr_screen.res][1], Skill_level_names(skill_level, 1));
7044 // -------------------------------------------------------------------------------------------------------------
7046 // MULTIPLAYER JOIN SCREEN
7049 #define MULTI_JW_NUM_BUTTONS 8
7053 #define MULTI_JW_PALETTE "InterfacePalette"
7055 static const char *Multi_jw_bitmap_fname[GR_NUM_RESOLUTIONS] = {
7056 "MultiJoinWait", // GR_640
7057 "2_MultiJoinWait" // GR_1024
7060 static const char *Multi_jw_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
7061 "MultiJoinWait-M", // GR_640
7062 "2_MultiJoinWait-M" // GR_1024
7068 #define MJW_SCROLL_PLAYERS_UP 0
7069 #define MJW_SCROLL_PLAYERS_DOWN 1
7072 #define MJW_PILOT_INFO 4
7073 #define MJW_SCROLL_INFO_UP 5
7074 #define MJW_SCROLL_INFO_DOWN 6
7075 #define MJW_CANCEL 7
7077 UI_WINDOW Multi_jw_window; // the window object for the join screen
7078 int Multi_jw_bitmap; // the background bitmap
7080 // constants for coordinate lookup
7081 #define MJW_X_COORD 0
7082 #define MJW_Y_COORD 1
7083 #define MJW_W_COORD 2
7084 #define MJW_H_COORD 3
7086 ui_button_info Multi_jw_buttons[GR_NUM_RESOLUTIONS][MULTI_JW_NUM_BUTTONS] = {
7089 ui_button_info("MJW_00", 0, 50, -1, -1, 0),
7090 ui_button_info("MJW_01", 0, 87, -1, -1, 1),
7091 ui_button_info("MJW_02", 20, 219, -1, -1, 2),
7092 ui_button_info("MJW_03", 73, 219, -1, -1, 3),
7093 ui_button_info("MJW_09", 131, 213, -1, -1, 9),
7094 ui_button_info("MJW_05", 0, 398, -1, -1, 5),
7095 ui_button_info("MJW_06", 0, 435, -1, -1, 6),
7096 ui_button_info("MJW_04", 559, 411, -1, -1, 4),
7098 ui_button_info("MJW_00", 1, 24, -1, -1, 0),
7099 ui_button_info("MJW_01", 1, 66, -1, -1, 1),
7100 ui_button_info("MJW_02", 30, 244, 20, 272, 2),
7101 ui_button_info("MJW_03", 84, 244, 73, 272, 3),
7102 ui_button_info("MJW_04", 139, 242, 134, 272, 4),
7103 ui_button_info("MJW_05", 1, 406, -1, -1, 5),
7104 ui_button_info("MJW_06", 1, 447, -1, -1, 6),
7105 ui_button_info("MJW_07", 577, 428, 570, 414, 7),
7109 ui_button_info("2_MJW_00", 2, 38, -1, -1, 0),
7110 ui_button_info("2_MJW_01", 2, 106, -1, -1, 1),
7111 ui_button_info("2_MJW_02", 48, 390, 47, 435, 2),
7112 ui_button_info("2_MJW_03", 134, 390, 133, 435, 3),
7113 ui_button_info("2_MJW_04", 223, 388, 225, 435, 4),
7114 ui_button_info("2_MJW_05", 2, 649, -1, -1, 5),
7115 ui_button_info("2_MJW_06", 2, 715, -1, -1, 6),
7116 ui_button_info("2_MJW_07", 923, 685, 931, 667, 7),
7121 #define MULTI_JW_NUM_TEXT 7
7123 UI_XSTR Multi_jw_text[GR_NUM_RESOLUTIONS][MULTI_JW_NUM_TEXT] = {
7125 { "Team 1", 1308, 20, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_TEAM0].button },
7126 { "Team 2", 1309, 73, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_TEAM1].button },
7127 { "Pilot", 1310, 134, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_PILOT_INFO].button },
7128 { "Info", 1311, 134, 283, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_PILOT_INFO].button },
7129 { "Cancel", 387, 570, 414, UI_XSTR_COLOR_PINK, -1, &Multi_jw_buttons[0][MJW_CANCEL].button },
7130 { "Players", 1269, 38, 8, UI_XSTR_COLOR_GREEN, -1, NULL },
7131 { "Choose Team", 1312, 27, 231, UI_XSTR_COLOR_GREEN, -1, NULL },
7134 { "Team 1", 1308, 47, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_TEAM0].button },
7135 { "Team 2", 1309, 133, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_TEAM1].button },
7136 { "Pilot", 1310, 225, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_PILOT_INFO].button },
7137 { "Info", 1311, 225, 446, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_PILOT_INFO].button },
7138 { "Cancel", 387, 931, 667, UI_XSTR_COLOR_PINK, -1, &Multi_jw_buttons[1][MJW_CANCEL].button },
7139 { "Players", 1269, 165, 12, UI_XSTR_COLOR_GREEN, -1, NULL },
7140 { "Choose Team", 1312, 45, 373, UI_XSTR_COLOR_GREEN, -1, NULL },
7145 int Mjw_players_coords[GR_NUM_RESOLUTIONS][4] = {
7158 int Mjw_mission_name_coords[GR_NUM_RESOLUTIONS][2] = {
7168 // squad war checkbox
7169 UI_CHECKBOX Multi_jw_sw_checkbox;
7170 const char *Multi_jw_sw_checkbox_fname[GR_NUM_RESOLUTIONS] = {
7174 int Multi_jw_sw_checkbox_coords[GR_NUM_RESOLUTIONS][2] = {
7182 int Multi_jw_sw_checkbox_text[GR_NUM_RESOLUTIONS][2] = {
7193 // player list control thingie defs
7194 #define MULTI_JW_PLIST_MAX_DISPLAY 19
7195 int Multi_jw_plist_select_flag; // indicates whether we currently have a selected player
7196 short Multi_jw_plist_select_id; // id of the current selected player
7197 UI_BUTTON Multi_jw_plist_select_button; // for selecting a player
7199 int Multi_jw_should_show_popup = 0;
7201 // LOCAL function definitions
7202 void multi_jw_check_buttons();
7203 void multi_jw_button_pressed(int n);
7204 void multi_jw_do_netstuff();
7205 void multi_jw_scroll_players_up();
7206 void multi_jw_scroll_players_down();
7207 void multi_jw_plist_process();
7208 void multi_jw_plist_blit_normal();
7209 void multi_jw_plist_blit_team();
7210 short multi_jw_get_mouse_id();
7212 void multi_game_client_setup_init()
7216 // create the interface window
7217 Multi_jw_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
7218 Multi_jw_window.set_mask_bmap(Multi_jw_bitmap_mask_fname[gr_screen.res]);
7220 // load the background bitmap
7221 Multi_jw_bitmap = bm_load(Multi_jw_bitmap_fname[gr_screen.res]);
7222 if(Multi_jw_bitmap < 0){
7223 // we failed to load the bitmap - this is very bad
7227 // initialize the player list data
7228 Multi_jw_plist_select_flag = 0;
7229 Multi_jw_plist_select_id = -1;
7231 // kill any old instances of the chatbox and create a new one
7233 chatbox_create(CHATBOX_FLAG_BIG | CHATBOX_FLAG_DRAW_BOX | CHATBOX_FLAG_BUTTONS);
7235 // initialize the common notification messaging
7236 multi_common_notify_init();
7238 // initialize the common mission info display area.
7239 multi_common_set_text("");
7241 // use the common interface palette
7242 multi_common_set_palette();
7244 // create the interface buttons
7245 for(idx=0; idx<MULTI_JW_NUM_BUTTONS; idx++){
7246 // create the object
7247 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);
7249 // set the sound to play when highlighted
7250 Multi_jw_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
7252 // set the ani for the button
7253 Multi_jw_buttons[gr_screen.res][idx].button.set_bmaps(Multi_jw_buttons[gr_screen.res][idx].filename);
7256 Multi_jw_buttons[gr_screen.res][idx].button.link_hotspot(Multi_jw_buttons[gr_screen.res][idx].hotspot);
7260 // if this is a PXO game, enable the squadwar checkbox
7261 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);
7262 Multi_jw_sw_checkbox.set_bmaps(Multi_jw_sw_checkbox_fname[gr_screen.res], 6, 0);
7263 Multi_jw_sw_checkbox.disable();
7264 if(!MULTI_IS_TRACKER_GAME){
7265 Multi_jw_sw_checkbox.hide();
7271 for(idx=0; idx<MULTI_JW_NUM_TEXT; idx++){
7272 Multi_jw_window.add_XSTR(&Multi_jw_text[gr_screen.res][idx]);
7276 // create the player select list button and hide it
7277 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);
7278 Multi_jw_plist_select_button.hide();
7281 Multi_jw_buttons[gr_screen.res][MJW_CANCEL].button.set_hotkey(SDLK_ESCAPE);
7283 // remove campaign flags
7284 Game_mode &= ~(GM_CAMPAIGN_MODE);
7286 // tell the server we have finished joining
7287 Net_player->state = NETPLAYER_STATE_JOINED;
7288 send_netplayer_update_packet();
7291 ml_printf(NOX("Joined netgame %s"), Netgame.name);
7293 // send any appropriate files
7294 multi_data_send_my_junk();
7297 void multi_game_client_setup_do_frame()
7300 int k = chatbox_process();
7301 char mission_text[255];
7302 /*k =*/ Multi_jw_window.process(k,0);
7304 Multi_jw_should_show_popup = 0;
7306 // process any button clicks
7307 multi_jw_check_buttons();
7309 // do any network related stuff
7310 multi_jw_do_netstuff();
7312 // draw the background, etc
7314 GR_MAYBE_CLEAR_RES(Multi_jw_bitmap);
7315 if(Multi_jw_bitmap != -1){
7316 gr_set_bitmap(Multi_jw_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7320 // if we're not in team vs. team mode, don't draw the team buttons
7321 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
7322 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.hide();
7323 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.hide();
7324 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.disable();
7325 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.disable();
7327 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.enable();
7328 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.enable();
7329 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.unhide();
7330 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.unhide();
7334 if(MULTI_IS_TRACKER_GAME){
7335 // maybe check the squadwar button
7336 if(Netgame.type_flags & NG_TYPE_SW){
7337 Multi_jw_sw_checkbox.set_state(1);
7338 gr_set_color_fast(&Color_bright);
7340 Multi_jw_sw_checkbox.set_state(0);
7341 gr_set_color_fast(&Color_normal);
7344 gr_string(Multi_jw_sw_checkbox_text[gr_screen.res][0], Multi_jw_sw_checkbox_text[gr_screen.res][1], "SquadWar");
7348 // draw the UI window
7349 Multi_jw_window.draw();
7351 // process and display the player list
7352 // NOTE : this must be done before the buttons are checked to insure that a player hasn't dropped
7353 multi_jw_plist_process();
7354 if(Netgame.type_flags & NG_TYPE_TEAM){
7355 multi_jw_plist_blit_team();
7357 multi_jw_plist_blit_normal();
7360 // display any text in the info area
7361 multi_common_render_text();
7363 // display any pending notification messages
7364 multi_common_notify_do();
7366 // blit the mission filename if possible
7367 if(Netgame.campaign_mode){
7368 if(strlen(Netgame.campaign_name) > 0){
7369 SDL_strlcpy(mission_text, Netgame.campaign_name, SDL_arraysize(mission_text));
7371 if(strlen(Netgame.title) > 0){
7372 SDL_strlcat(mission_text, ", ", SDL_arraysize(mission_text));
7373 SDL_strlcat(mission_text, Netgame.title, SDL_arraysize(mission_text));
7376 gr_set_color_fast(&Color_bright_white);
7377 gr_string(Mjw_mission_name_coords[gr_screen.res][MJW_X_COORD],Mjw_mission_name_coords[gr_screen.res][MJW_Y_COORD],mission_text);
7380 if(strlen(Netgame.mission_name) > 0){
7381 SDL_strlcpy(mission_text, Netgame.mission_name, SDL_arraysize(mission_text));
7383 if(strlen(Netgame.title) > 0){
7384 SDL_strlcat(mission_text, ", ", SDL_arraysize(mission_text));
7385 SDL_strlcat(mission_text, Netgame.title, SDL_arraysize(mission_text));
7388 gr_set_color_fast(&Color_bright_white);
7389 gr_string(Mjw_mission_name_coords[gr_screen.res][MJW_X_COORD],Mjw_mission_name_coords[gr_screen.res][MJW_Y_COORD],mission_text);
7393 // process and show the chatbox thingie
7397 Multi_jw_window.draw_tooltip();
7399 // display the voice status indicator
7400 multi_common_voice_display_status();
7405 // if we're supposed to be displaying a pilot info popup
7406 if(Multi_jw_should_show_popup){
7407 player_index = find_player_id(Multi_jw_plist_select_id);
7408 if(player_index != -1){
7409 multi_pinfo_popup(&Net_players[player_index]);
7414 void multi_game_client_setup_close()
7416 // unload any bitmaps
7417 if(!bm_unload(Multi_jw_bitmap)){
7418 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_jw_bitmap_fname[gr_screen.res]));
7421 // destroy the chatbox
7424 // destroy the UI_WINDOW
7425 Multi_jw_window.destroy();
7428 if(Netgame.game_state == NETGAME_STATE_MISSION_SYNC){
7429 gamesnd_play_iface(SND_COMMIT_PRESSED);
7434 void multi_jw_check_buttons()
7437 for(idx=0;idx<MULTI_JW_NUM_BUTTONS;idx++){
7438 // we only really need to check for one button pressed at a time, so we can break after
7440 if(Multi_jw_buttons[gr_screen.res][idx].button.pressed()){
7441 multi_jw_button_pressed(idx);
7447 void multi_jw_button_pressed(int n)
7451 gamesnd_play_iface(SND_USER_SELECT);
7452 multi_quit_game(PROMPT_CLIENT);
7454 case MJW_SCROLL_PLAYERS_UP:
7455 multi_jw_scroll_players_up();
7457 case MJW_SCROLL_PLAYERS_DOWN:
7458 multi_jw_scroll_players_down();
7460 case MJW_SCROLL_INFO_UP:
7461 multi_common_scroll_text_up();
7463 case MJW_SCROLL_INFO_DOWN:
7464 multi_common_scroll_text_down();
7467 // request to set myself to team 0
7469 gamesnd_play_iface(SND_USER_SELECT);
7470 multi_team_set_team(Net_player,0);
7473 // request to set myself to team 1
7475 gamesnd_play_iface(SND_USER_SELECT);
7476 multi_team_set_team(Net_player,1);
7480 case MJW_PILOT_INFO:
7481 Multi_jw_should_show_popup = 1;
7485 multi_common_add_notify(XSTR("Not implemented yet!",760));
7490 // do stuff like pinging servers, sending out requests, etc
7491 void multi_jw_do_netstuff()
7495 void multi_jw_scroll_players_up()
7497 gamesnd_play_iface(SND_GENERAL_FAIL);
7500 // scroll down through the player list
7501 void multi_jw_scroll_players_down()
7503 gamesnd_play_iface(SND_GENERAL_FAIL);
7506 void multi_jw_plist_process()
7508 int test_count,player_index,idx;
7510 // first determine if there are 0 players in the game. This should never happen since the host is _always_ in the game
7512 for(idx=0;idx<MAX_PLAYERS;idx++){
7513 // count anyone except the standalone server (if applicable)
7514 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7518 if(test_count <= 0){
7522 // if we had a selected item but that player has left, select myself instead
7523 if(Multi_jw_plist_select_flag){
7524 player_index = find_player_id(Multi_jw_plist_select_id);
7525 if(player_index == -1){
7526 Multi_jw_plist_select_id = Net_player->player_id;
7529 Multi_jw_plist_select_flag = 1;
7530 Multi_jw_plist_select_id = Net_player->player_id;
7533 // if the player has clicked somewhere in the player list area
7534 if(Multi_jw_plist_select_button.pressed()){
7538 player_id = multi_jw_get_mouse_id();
7539 player_index = find_player_id(player_id);
7540 if(player_index != -1){
7541 Multi_jw_plist_select_id = player_id;
7542 Multi_jw_plist_select_flag = 1;
7547 void multi_jw_plist_blit_normal()
7550 char str[CALLSIGN_LEN+1];
7551 int y_start = Mjw_players_coords[gr_screen.res][MJW_Y_COORD];
7554 // display all the players
7555 for(idx=0;idx<MAX_PLAYERS;idx++){
7556 // reset total offset
7559 // count anyone except the standalone server (if applicable)
7560 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7561 // highlight him if he's the host
7562 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7563 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7564 gr_set_color_fast(&Color_text_active_hi);
7566 gr_set_color_fast(&Color_bright);
7569 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7570 gr_set_color_fast(&Color_text_active);
7572 gr_set_color_fast(&Color_text_normal);
7576 // optionally draw his CD status
7577 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7578 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7579 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7581 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7584 // make sure the string will fit, then display it
7585 SDL_strlcpy(str, Net_players[idx].player->callsign, SDL_arraysize(str));
7586 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
7587 SDL_strlcat(str, "(0)", SDL_arraysize(str));
7589 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7590 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7597 void multi_jw_plist_blit_team()
7600 char str[CALLSIGN_LEN+1];
7601 int y_start = Mjw_players_coords[gr_screen.res][MJW_Y_COORD];
7604 // always blit the proper team button based on _my_ team status
7605 if(Net_player->p_info.team == 0){
7606 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.draw_forced(2);
7608 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.draw_forced(2);
7611 // display all the red players first
7612 for(idx=0;idx<MAX_PLAYERS;idx++){
7613 // reset total offset
7616 // count anyone except the standalone server (if applicable)
7617 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
7618 // highlight him if he's the host
7619 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7620 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7621 gr_set_color_fast(&Color_text_active_hi);
7623 gr_set_color_fast(&Color_bright);
7626 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7627 gr_set_color_fast(&Color_text_active);
7629 gr_set_color_fast(&Color_text_normal);
7633 // optionally draw his CD status
7634 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7635 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7636 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7638 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7641 // blit the red team indicator
7642 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
7643 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
7644 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7645 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7647 total_offset += Multi_common_icon_dims[MICON_TEAM0_SELECT][0] + 1;
7650 if(Multi_common_icons[MICON_TEAM0] != -1){
7651 gr_set_bitmap(Multi_common_icons[MICON_TEAM0], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7652 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7654 total_offset += Multi_common_icon_dims[MICON_TEAM0][0] + 1;
7658 // make sure the string will fit
7659 SDL_strlcpy(str, Net_players[idx].player->callsign, SDL_arraysize(str));
7660 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7662 // display him in the correct half of the list depending on his team
7663 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7668 // display all the green players next
7669 for(idx=0;idx<MAX_PLAYERS;idx++){
7670 // reset total offset
7673 // count anyone except the standalone server (if applicable)
7674 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
7675 // highlight him if he's the host
7676 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7677 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7678 gr_set_color_fast(&Color_text_active_hi);
7680 gr_set_color_fast(&Color_bright);
7683 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7684 gr_set_color_fast(&Color_text_active);
7686 gr_set_color_fast(&Color_text_normal);
7690 // optionally draw his CD status
7691 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7692 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7693 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7695 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7698 // blit the red team indicator
7699 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
7700 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
7701 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7702 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7704 total_offset += Multi_common_icon_dims[MICON_TEAM1_SELECT][0] + 1;
7707 if(Multi_common_icons[MICON_TEAM1] != -1){
7708 gr_set_bitmap(Multi_common_icons[MICON_TEAM1], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7709 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7711 total_offset += Multi_common_icon_dims[MICON_TEAM1][0] + 1;
7715 // make sure the string will fit
7716 SDL_strlcpy(str, Net_players[idx].player->callsign, SDL_arraysize(str));
7717 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
7718 SDL_strlcat(str, "(0)", SDL_arraysize(str));
7720 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7722 // display him in the correct half of the list depending on his team
7723 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7729 void multi_jw_handle_join(net_player *pl)
7731 // for now just play a bloop sound
7732 gamesnd_play_iface(SND_ICON_DROP_ON_WING);
7735 short multi_jw_get_mouse_id()
7737 // determine where he clicked (y pixel value)
7739 Multi_jw_plist_select_button.get_mouse_pos(NULL,&y);
7741 // select things a little differently if we're in team vs. team or non-team vs. team mode
7743 if(Netgame.type_flags & NG_TYPE_TEAM){
7744 int player_index = -1;
7746 // look through all of team red first
7747 for(idx=0;idx<MAX_PLAYERS;idx++){
7748 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
7751 // if this is the _nth_ guy
7759 // if we still haven't found him yet, look through the green team
7760 if(player_index == -1){
7761 for(idx=0;idx<MAX_PLAYERS;idx++){
7762 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
7764 // if this is the _nth_ guy
7772 if(player_index != -1){
7773 return Net_players[idx].player_id;
7776 // select the nth active player if possible, disregarding the standalone server
7777 for(idx=0;idx<MAX_PLAYERS;idx++){
7778 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7781 // if this is the _nth_ guy
7783 return Net_players[idx].player_id;
7794 // -------------------------------------------------------------------------------------------------------------
7796 // MULTIPLAYER WAIT/SYNCH SCREEN
7799 #define MULTI_SYNC_HOST_COUNT 4 // host uses 4 buttons (and sometimes 5)
7800 #define MULTI_SYNC_CLIENT_COUNT 3 // client only uses 3 buttons
7802 const char *Multi_sync_bitmap_fname[GR_NUM_RESOLUTIONS] = {
7803 "MultiSynch", // GR_640
7804 "2_MultiSynch" // GR_1024
7807 const char *Multi_sync_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
7808 "MultiSynch-M", // GR_640
7809 "2_MultiSynch-M" // GR_1024
7814 // constants for coordinate lookup
7815 #define MS_X_COORD 0
7816 #define MS_Y_COORD 1
7817 #define MS_W_COORD 2
7818 #define MS_H_COORD 3
7820 UI_WINDOW Multi_sync_window; // the window object for the join screen
7821 int Multi_sync_button_count; // the # of buttons to use for this instance of mission sync
7822 int Multi_sync_bitmap; // the background bitmap
7825 #define MULTI_SYNC_NUM_BUTTONS 5
7826 #define MS_SCROLL_INFO_UP 0
7827 #define MS_SCROLL_INFO_DOWN 1
7831 ui_button_info Multi_sync_buttons[GR_NUM_RESOLUTIONS][MULTI_SYNC_NUM_BUTTONS] = {
7834 ui_button_info("MS_00", 0, 396, -1, -1, 0),
7835 ui_button_info("MS_01", 0, 435, -1, -1, 1),
7836 ui_button_info("MS_02", 496, 411, -1, -1, 2),
7837 ui_button_info("MS_04", 436, 403, -1, -1, 4),
7838 ui_button_info("MS_03", 556, 398, -1, -1, 3),
7840 ui_button_info("MS_00", 1, 404, -1, -1, 0),
7841 ui_button_info("MS_01", 1, 446, -1, -1, 1),
7842 ui_button_info("MS_03", 518, 426, 519, 416, 3),
7843 ui_button_info("MS_02", 469, 426, 479, 416, 2),
7844 ui_button_info("MS_04", 571, 420, 577, 416, 4),
7848 ui_button_info("2_MS_00", 2, 647, -1, -1, 0),
7849 ui_button_info("2_MS_01", 2, 713, -1, -1, 1),
7850 ui_button_info("2_MS_03", 829, 682, 831, 667, 3),
7851 ui_button_info("2_MS_02", 751, 682, 766, 667, 2),
7852 ui_button_info("2_MS_04", 914, 672, 924, 667, 4),
7858 #define MULTI_SYNC_NUM_TEXT 5
7860 #define MST_LAUNCH 2
7861 UI_XSTR Multi_sync_text[GR_NUM_RESOLUTIONS][MULTI_SYNC_NUM_TEXT] = {
7863 { "Kick", 1266, 479, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_KICK].button },
7864 { "Cancel", 387, 519, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_CANCEL].button },
7865 { "Launch", 801, 577, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_LAUNCH].button },
7866 { "Players", 1269, 23, 133, UI_XSTR_COLOR_GREEN, -1, NULL },
7867 { "Status", 1304, 228, 133, UI_XSTR_COLOR_GREEN, -1, NULL }
7870 { "Kick", 1266, 766, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_KICK].button },
7871 { "Cancel", 387, 831, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_CANCEL].button },
7872 { "Launch", 801, 924, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_LAUNCH].button },
7873 { "Players", 1269, 38, 214, UI_XSTR_COLOR_GREEN, -1, NULL },
7874 { "Status", 1304, 366, 214, UI_XSTR_COLOR_GREEN, -1, NULL }
7880 int Ms_status_coords[GR_NUM_RESOLUTIONS][4] = {
7893 // player status coords
7894 int Ms_status2_coords[GR_NUM_RESOLUTIONS][4] = {
7907 int Ms_cd_icon_offset[GR_NUM_RESOLUTIONS] = {
7912 int Ms_team_icon_offset[GR_NUM_RESOLUTIONS] = {
7917 // player currently selected, index into Net_players[]
7918 int Multi_sync_player_select = -1;
7920 // player list control thingie defs
7921 #define MULTI_SYNC_PLIST_MAX_DISPLAY 15
7922 int Multi_sync_plist_start; // where to start displaying from
7923 int Multi_sync_plist_count; // how many we have
7925 // list select button
7926 UI_BUTTON Multi_sync_plist_button;
7928 int Multi_sync_mode = -1;
7930 #define MULTI_SYNC_COUNTDOWN_TIME 5 // in seconds
7931 float Multi_sync_countdown_timer;
7932 int Multi_sync_countdown = -1;
7934 int Multi_launch_button_created;
7937 // countdown animation timer
7938 const char* Multi_sync_countdown_fname[GR_NUM_RESOLUTIONS] = {
7940 "2_Count" // GR_1024
7943 int Multi_sync_countdown_coords[GR_NUM_RESOLUTIONS][2] = {
7954 anim *Multi_sync_countdown_anim = NULL;
7955 anim_instance *Multi_sync_countdown_instance = NULL;
7958 // PREBRIEFING STUFF
7959 // syncing flags used by the server
7960 int Mission_sync_flags = 0;
7961 #define MS_FLAG_SENT_FILESIG (1<<0) // sent filesig requests
7962 #define MS_FLAG_SENT_LOAD (1<<1) // sent load packets
7963 #define MS_FLAG_PUSHED_BRIEFING (1<<2) // pushed everyone else into the briefing
7964 #define MS_FLAG_POST_DATA (1<<3) // sent the post data block
7965 #define MS_FLAG_WSS_SLOTS (1<<4) // all players have received wss slot data
7966 #define MS_FLAG_PSETTINGS (1<<5) // send the player settings packet
7967 #define MS_FLAG_MT_STATS_START (1<<6) // server has started getting player stats from the tracker
7968 #define MS_FLAG_MT_STATS_DONE (1<<7) // server has finished getting player stats from the tracker (success or fail)
7969 #define MS_FLAG_TS_SLOTS (1<<8) // team/ship slots have been sent
7970 #define MS_FLAG_DATA_DONE (1<<9) // done transferring all necessary data
7971 #define MS_FLAG_CAMP_DONE (1<<10) // send campaign pool/goal/event stuff
7973 // POSTBRIEFING STUFF
7974 int Multi_state_timestamp;
7975 int Multi_sync_launch_pressed;
7977 // LOCAL function definitions
7978 void multi_sync_check_buttons();
7979 void multi_sync_button_pressed(int n);
7980 void multi_sync_scroll_info_up();
7981 void multi_sync_scroll_info_down();
7982 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
7983 void multi_sync_display_status(const char *status, int index); // display info on the right hand portion of the status window thingie
7984 void multi_sync_force_start_pre();
7985 void multi_sync_force_start_post();
7986 void multi_sync_launch();
7987 void multi_sync_create_launch_button();
7988 void multi_sync_blit_screen_all();
7989 void multi_sync_handle_plist();
7991 void multi_sync_common_init();
7992 void multi_sync_common_do();
7993 void multi_sync_common_close();
7995 void multi_sync_pre_init();
7996 void multi_sync_pre_do();
7997 void multi_sync_pre_close();
7999 void multi_sync_post_init();
8000 void multi_sync_post_do();
8001 void multi_sync_post_close();
8006 // perform the correct init functions
8007 void multi_sync_init()
8009 Multi_sync_countdown = -1;
8013 // reset all timestamp
8014 multi_reset_timestamps();
8016 extern time_t Player_multi_died_check;
8017 Player_multi_died_check = -1;
8019 if(!(Game_mode & GM_STANDALONE_SERVER)){
8020 multi_sync_common_init();
8023 switch(Multi_sync_mode){
8024 case MULTI_SYNC_PRE_BRIEFING:
8025 multi_sync_pre_init();
8027 case MULTI_SYNC_POST_BRIEFING:
8028 multi_sync_post_init();
8030 case MULTI_SYNC_INGAME:
8031 multi_ingame_sync_init();
8036 // perform the correct do frame functions
8037 void multi_sync_do()
8039 if(!(Game_mode & GM_STANDALONE_SERVER)){
8040 multi_sync_common_do();
8043 // if the netgame is ending, don't do any sync processing
8044 if(multi_endgame_ending()){
8048 // process appropriateliy
8049 switch(Multi_sync_mode){
8050 case MULTI_SYNC_PRE_BRIEFING:
8051 multi_sync_pre_do();
8053 case MULTI_SYNC_POST_BRIEFING:
8054 multi_sync_post_do();
8056 case MULTI_SYNC_INGAME:
8057 multi_ingame_sync_do();
8060 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8061 if(Multi_sync_bitmap != -1){
8062 gr_set_bitmap(Multi_sync_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8065 Multi_sync_window.draw();
8067 multi_sync_blit_screen_all();
8074 // perform the correct close functions
8075 void multi_sync_close()
8077 switch(Multi_sync_mode){
8078 case MULTI_SYNC_PRE_BRIEFING:
8079 multi_sync_pre_close();
8081 case MULTI_SYNC_POST_BRIEFING:
8082 multi_sync_post_close();
8084 case MULTI_SYNC_INGAME:
8085 multi_ingame_sync_close();
8089 if(!(Game_mode & GM_STANDALONE_SERVER)){
8090 multi_sync_common_close();
8094 const char *multi_sync_tooltip_handler(const char *str)
8096 if (!SDL_strcasecmp(str, NOX("@launch"))) {
8097 if (Multi_launch_button_created){
8098 return XSTR("Launch",801);
8105 void multi_sync_common_init()
8109 // create the interface window
8110 Multi_sync_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
8111 Multi_sync_window.set_mask_bmap(Multi_sync_bitmap_mask_fname[gr_screen.res]);
8112 Multi_sync_window.tooltip_handler = multi_sync_tooltip_handler;
8114 // load the background bitmap
8115 Multi_sync_bitmap = bm_load(Multi_sync_bitmap_fname[gr_screen.res]);
8116 if (Multi_sync_bitmap < 0) {
8117 // we failed to load the bitmap - this is very bad
8121 // initialize the player list data
8122 Multi_sync_plist_start = 0;
8123 Multi_sync_plist_count = 1; // we can pretty safely assume that there's one player in the game - me.
8125 Multi_launch_button_created = 0;
8127 // create the chatbox thingie (shouldn't be necesary to do this, but we'll put it in for good measure)
8130 // force the chatbox to be small
8131 chatbox_force_small();
8133 // initialize the common notification messaging
8134 multi_common_notify_init();
8136 // initialize the common mission info display area.
8137 multi_common_set_text("");
8139 // use the common interface palette
8140 multi_common_set_palette();
8142 // don't select any player yet.
8143 Multi_sync_player_select = -1;
8145 // determine how many of the 5 buttons to create
8146 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8147 Multi_sync_button_count = MULTI_SYNC_HOST_COUNT;
8149 Multi_sync_button_count = MULTI_SYNC_CLIENT_COUNT;
8151 // create the interface buttons
8152 for(idx=0; idx<Multi_sync_button_count; idx++){
8153 // create the object
8154 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);
8156 // set the sound to play when highlighted
8157 Multi_sync_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
8159 // set the ani for the button
8160 // this wierdness is necessary because cancel and kick buttons aren't drawn on the background bitmap,
8161 // so we have to load in frame 0, too (the file should exist)
8162 if ((idx == MS_CANCEL) || (idx == MS_KICK) || (idx == MS_LAUNCH)) {
8163 Multi_sync_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sync_buttons[gr_screen.res][idx].filename, 3, 0);
8165 Multi_sync_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sync_buttons[gr_screen.res][idx].filename);
8169 Multi_sync_buttons[gr_screen.res][idx].button.link_hotspot(Multi_sync_buttons[gr_screen.res][idx].hotspot);
8174 for(idx=0; idx<MULTI_SYNC_NUM_TEXT; idx++) {
8175 // don't create the "launch" button text just yet
8176 if(idx == MST_LAUNCH) {
8179 // multiplayer clients should ignore the kick button
8180 if(!MULTIPLAYER_MASTER && !MULTIPLAYER_HOST && (idx == MST_KICK)) {
8184 Multi_sync_window.add_XSTR(&Multi_sync_text[gr_screen.res][idx]);
8188 // create the player list select button and hide it
8189 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);
8190 Multi_sync_plist_button.hide();
8192 // set up hotkeys for certain common functions
8193 Multi_sync_buttons[gr_screen.res][MS_CANCEL].button.set_hotkey(SDLK_ESCAPE);
8196 void multi_sync_common_do()
8198 int k = chatbox_process();
8199 k = Multi_sync_window.process(k);
8201 // process the player list
8202 multi_sync_handle_plist();
8204 // process any button clicks
8205 multi_sync_check_buttons();
8207 // process any keypresses
8211 gamesnd_play_iface(SND_USER_SELECT);
8212 multi_quit_game(PROMPT_ALL);
8217 void multi_sync_common_close()
8219 // unload any bitmaps
8220 if(!bm_unload(Multi_sync_bitmap)){
8221 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_sync_bitmap_fname[gr_screen.res]));
8224 extern time_t Player_multi_died_check;
8225 Player_multi_died_check = -1;
8227 // destroy the UI_WINDOW
8228 Multi_sync_window.destroy();
8231 void multi_sync_blit_screen_all()
8238 // display any text in the info area
8239 multi_common_render_text();
8241 // display any pending notification messages
8242 multi_common_notify_do();
8244 // display any info about visible players
8246 for(idx=0;idx<MAX_PLAYERS;idx++){
8247 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
8248 // display his name and status
8249 multi_sync_display_name(Net_players[idx].player->callsign,count,idx);
8251 // get the player state
8252 state = Net_players[idx].state;
8254 // if we're ingame joining, show all other players except myself as "playing"
8255 if((Net_player != NULL) && (&Net_players[idx] != Net_player) && ((Multi_sync_mode == MULTI_SYNC_INGAME) || (Net_player->flags & NETINFO_FLAG_INGAME_JOIN)) ){
8256 state = NETPLAYER_STATE_IN_MISSION;
8260 case NETPLAYER_STATE_MISSION_LOADING:
8261 multi_sync_display_status(XSTR("Mission Loading",802),count);
8263 case NETPLAYER_STATE_INGAME_SHIP_SELECT: // I don't think its possible to see this state, but...
8264 multi_sync_display_status(XSTR("Ingame Ship Select",803),count);
8266 case NETPLAYER_STATE_DEBRIEF:
8267 multi_sync_display_status(XSTR("Debriefing",804),count);
8269 case NETPLAYER_STATE_MISSION_SYNC:
8270 multi_sync_display_status(XSTR("Mission Sync",805),count);
8272 case NETPLAYER_STATE_JOINING:
8273 multi_sync_display_status(XSTR("Joining",806),count);
8275 case NETPLAYER_STATE_JOINED:
8276 multi_sync_display_status(XSTR("Joined",807),count);
8278 case NETPLAYER_STATE_SLOT_ACK :
8279 multi_sync_display_status(XSTR("Slot Ack",808),count);
8281 case NETPLAYER_STATE_BRIEFING:
8282 multi_sync_display_status(XSTR("Briefing",765),count);
8284 case NETPLAYER_STATE_SHIP_SELECT:
8285 multi_sync_display_status(XSTR("Ship Select",809),count);
8287 case NETPLAYER_STATE_WEAPON_SELECT:
8288 multi_sync_display_status(XSTR("Weapon Select",810),count);
8290 case NETPLAYER_STATE_WAITING:
8291 multi_sync_display_status(XSTR("Waiting",811),count);
8293 case NETPLAYER_STATE_IN_MISSION:
8294 multi_sync_display_status(XSTR("In Mission",812),count);
8296 case NETPLAYER_STATE_MISSION_LOADED:
8297 multi_sync_display_status(XSTR("Mission Loaded",813),count);
8299 case NETPLAYER_STATE_DATA_LOAD:
8300 multi_sync_display_status(XSTR("Data loading",814),count);
8302 case NETPLAYER_STATE_SETTINGS_ACK:
8303 multi_sync_display_status(XSTR("Ready To Enter Mission",815),count);
8305 case NETPLAYER_STATE_INGAME_SHIPS:
8306 multi_sync_display_status(XSTR("Ingame Ships Packet Ack",816),count);
8308 case NETPLAYER_STATE_INGAME_WINGS:
8309 multi_sync_display_status(XSTR("Ingame Wings Packet Ack",817),count);
8311 case NETPLAYER_STATE_INGAME_RPTS:
8312 multi_sync_display_status(XSTR("Ingame Respawn Points Ack",818),count);
8314 case NETPLAYER_STATE_SLOTS_ACK:
8315 multi_sync_display_status(XSTR("Ingame Weapon Slots Ack",819),count);
8317 case NETPLAYER_STATE_POST_DATA_ACK:
8318 multi_sync_display_status(XSTR("Post Briefing Data Block Ack",820),count);
8320 case NETPLAYER_STATE_FLAG_ACK :
8321 multi_sync_display_status(XSTR("Flags Ack",821),count);
8323 case NETPLAYER_STATE_MT_STATS :
8324 multi_sync_display_status(XSTR("Parallax Online Stats Updating",822),count);
8326 case NETPLAYER_STATE_WSS_ACK :
8327 multi_sync_display_status(XSTR("Weapon Slots Ack",823),count);
8329 case NETPLAYER_STATE_HOST_SETUP :
8330 multi_sync_display_status(XSTR("Host setup",824),count);
8332 case NETPLAYER_STATE_DEBRIEF_ACCEPT:
8333 multi_sync_display_status(XSTR("Debrief accept",825),count);
8335 case NETPLAYER_STATE_DEBRIEF_REPLAY:
8336 multi_sync_display_status(XSTR("Debrief replay",826),count);
8338 case NETPLAYER_STATE_CPOOL_ACK:
8339 multi_sync_display_status(XSTR("Campaign ship/weapon ack",827),count);
8341 case NETPLAYER_STATE_MISSION_XFER :
8343 // server should display the pct completion of all clients
8344 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8345 if(Net_players[idx].s_info.xfer_handle != -1){
8346 pct_complete = multi_xfer_pct_complete(Net_players[idx].s_info.xfer_handle);
8348 // if we've got a valid xfer handle
8349 if((pct_complete >= 0.0) && (pct_complete <= 1.0)){
8350 SDL_snprintf(txt,SDL_arraysize(txt),XSTR("Mission file xfer %d%%",828),(int)(pct_complete * 100.0f));
8354 SDL_strlcpy(txt, XSTR("Mission file xfer", 829), SDL_arraysize(txt));
8357 SDL_strlcpy(txt, XSTR("Mission file xfer", 829), SDL_arraysize(txt));
8360 // clients should display only for themselves (which is the only thing they know)
8362 // if we've got a valid file xfer handle
8363 if((&Net_players[idx] == Net_player) && (Net_player->s_info.xfer_handle != -1)){
8364 pct_complete = multi_xfer_pct_complete(Net_player->s_info.xfer_handle);
8366 // if we've got a valid xfer handle
8367 if((pct_complete >= 0.0) && (pct_complete <= 1.0)){
8368 SDL_snprintf(txt,SDL_arraysize(txt),XSTR("Mission file xfer %d%%",828),(int)(pct_complete * 100.0f));
8372 SDL_strlcpy(txt, XSTR("Mission file xfer", 829), SDL_arraysize(txt));
8377 SDL_strlcpy(txt, XSTR("Mission file xfer", 829), SDL_arraysize(txt));
8382 multi_sync_display_status(txt,count);
8385 nprintf(("Network","Unhandled player state : %d !\n",Net_players[idx].state));
8392 // display the mission start countdown timer (if any)
8393 anim_render_all(GS_STATE_MULTI_MISSION_SYNC,flFrametime);
8395 // process and show the chatbox thingie
8399 Multi_sync_window.draw_tooltip();
8401 // display the voice status indicator
8402 multi_common_voice_display_status();
8405 void multi_sync_check_buttons()
8408 for(idx=0;idx<Multi_sync_button_count;idx++){
8409 // we only really need to check for one button pressed at a time, so we can break after
8411 if(Multi_sync_buttons[gr_screen.res][idx].button.pressed()){
8412 multi_sync_button_pressed(idx);
8418 void multi_sync_button_pressed(int n)
8423 gamesnd_play_iface(SND_USER_SELECT);
8424 multi_quit_game(PROMPT_ALL);
8427 // scroll the info box up
8428 case MS_SCROLL_INFO_UP:
8429 multi_common_scroll_text_up();
8432 // scroll the info box down
8433 case MS_SCROLL_INFO_DOWN:
8434 multi_common_scroll_text_down();
8439 // if we have a currently selected player, kick him
8440 if(Multi_sync_player_select >= 0){
8441 multi_kick_player(Multi_sync_player_select);
8445 // start the final launch countdown (post-sync only)
8447 multi_sync_start_countdown();
8450 // doesn't do anything
8456 void multi_sync_pre_init()
8460 Netgame.game_state = NETGAME_STATE_MISSION_SYNC;
8462 // if we're in teamplay mode, always force skill level to be medium
8463 if((Netgame.type_flags & NG_TYPE_TEAM) && (Net_player->flags & NETINFO_FLAG_GAME_HOST)){
8464 Netgame.options.skill_level = NUM_SKILL_LEVELS / 2;
8465 Game_skill_level = NUM_SKILL_LEVELS / 2;
8466 multi_options_update_netgame();
8469 // notify everyone of when we get here
8470 if(!(Game_mode & GM_STANDALONE_SERVER)){
8471 Net_player->state = NETPLAYER_STATE_MISSION_SYNC;
8472 send_netplayer_update_packet();
8475 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8477 ml_string(NOX("Server performing pre-briefing data sync"));
8479 if(!(Game_mode & GM_STANDALONE_SERVER)){
8480 multi_common_set_text(XSTR("Server performing sync\n",830),1);
8483 // maybe initialize tvt and squad war stuff
8484 if(Netgame.type_flags & NG_TYPE_TEAM){
8485 multi_team_level_init();
8488 // force everyone into this state
8489 send_netgame_update_packet();
8491 if(!(Game_mode & GM_STANDALONE_SERVER)){
8492 multi_common_add_text(XSTR("Send update packet\n",831),1);
8495 // setup some of my own data
8496 Net_player->flags |= NETINFO_FLAG_MISSION_OK;
8498 // do any output stuff
8499 if(Game_mode & GM_STANDALONE_SERVER){
8500 std_debug_set_standalone_state_string("Mission Sync");
8503 // do this here to insure we have the most up to date file checksum info
8504 multi_get_mission_checksum(Game_current_mission_filename);
8505 // parse_get_file_signature(Game_current_mission_filename);
8507 if(!(Game_mode & GM_STANDALONE_SERVER)){
8508 multi_common_add_text(XSTR("Got file signatures\n",832),1);
8511 if(!(Game_mode & GM_STANDALONE_SERVER)){
8512 multi_common_add_text(XSTR("Sending update state packet\n",833),1);
8516 // if we're not in team vs. team mode - set all player teams to be 0, and unset all captaincy bits
8517 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
8518 for(idx=0;idx<MAX_PLAYERS;idx++){
8519 Net_players[idx].p_info.team = 0;
8520 Net_players[idx].flags &= ~(NETINFO_FLAG_TEAM_CAPTAIN);
8524 // we aren't necessarily xferring the mission file yet
8525 SDL_assert(Net_player->s_info.xfer_handle == -1);
8527 // always call this for good measure
8528 multi_campaign_flush_data();
8530 Mission_sync_flags = 0;
8531 Multi_mission_loaded = 0;
8534 void multi_sync_pre_do()
8538 // If I'm the server, wait for everyone to arrive in this state, then begin transferring data, etc.
8539 // all servers (standalone or no, go through this)
8540 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8541 // wait for everyone to arrive, then request filesig from all of them
8542 if(multi_netplayer_state_check(NETPLAYER_STATE_MISSION_SYNC) && !(Mission_sync_flags & MS_FLAG_SENT_FILESIG) && !multi_endgame_ending()){
8543 send_file_sig_request(Netgame.mission_name);
8544 Mission_sync_flags |= MS_FLAG_SENT_FILESIG;
8546 if(!(Game_mode & GM_STANDALONE_SERVER)){
8547 multi_common_add_text(XSTR("Sent filesig request\n",834),1);
8551 // if we're waiting for players to receive files, then check on their status
8552 if((Mission_sync_flags & MS_FLAG_SENT_FILESIG) && !multi_netplayer_flag_check(NETINFO_FLAG_MISSION_OK) && !multi_endgame_ending()){
8553 for(idx=0;idx<MAX_PLAYERS;idx++){
8554 // if this player is in the process of xferring a file
8555 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].s_info.xfer_handle != -1)){
8556 switch(multi_xfer_get_status(Net_players[idx].s_info.xfer_handle)){
8557 // if it has successfully completed, set his ok flag
8558 case MULTI_XFER_SUCCESS :
8560 Net_players[idx].flags |= NETINFO_FLAG_MISSION_OK;
8562 // release the xfer instance handle
8563 multi_xfer_release_handle(Net_players[idx].s_info.xfer_handle);
8564 Net_players[idx].s_info.xfer_handle = -1;
8566 // if it has failed or timed-out, kick the player
8567 case MULTI_XFER_TIMEDOUT:
8568 case MULTI_XFER_FAIL:
8569 // release the xfer handle
8570 multi_xfer_release_handle(Net_players[idx].s_info.xfer_handle);
8571 Net_players[idx].s_info.xfer_handle = -1;
8574 multi_kick_player(idx, 0, KICK_REASON_BAD_XFER);
8581 // NOTE : this is now obsolete
8582 // once everyone is verified, do any data transfer necessary
8583 if(multi_netplayer_flag_check(NETINFO_FLAG_MISSION_OK) && !(Mission_sync_flags & MS_FLAG_DATA_DONE) && !multi_endgame_ending()){
8584 // do nothing for now
8585 Mission_sync_flags |= MS_FLAG_DATA_DONE;
8587 // send campaign pool data
8588 multi_campaign_send_pool_status();
8591 // wait for everyone to ack on campaign pool data (even in non-campaign situations)
8592 if((Mission_sync_flags & MS_FLAG_DATA_DONE) && !(Mission_sync_flags & MS_FLAG_CAMP_DONE) && !multi_endgame_ending()){
8593 // check to see if everyone has acked the campaign pool data
8594 if(multi_netplayer_state_check(NETPLAYER_STATE_CPOOL_ACK)){
8595 Mission_sync_flags |= MS_FLAG_CAMP_DONE;
8599 // once everyone is verified, tell them to load the mission
8600 // also make sure to load the mission myself _AFTER_ telling everyone to do so. This makes the whole process
8601 // move along faster
8602 if((Mission_sync_flags & MS_FLAG_CAMP_DONE) && !(Mission_sync_flags & MS_FLAG_SENT_LOAD) && !multi_endgame_ending()){
8603 send_netplayer_load_packet(NULL);
8604 Mission_sync_flags |= MS_FLAG_SENT_LOAD;
8606 if(!(Game_mode & GM_STANDALONE_SERVER)){
8607 multi_common_add_text(XSTR("Sent load packet\n",835),1);
8610 // load the mission myself, as soon as possible
8611 if(!Multi_mission_loaded){
8612 nprintf(("Network","Server loading mission..."));
8614 // update everyone about my status
8615 Net_player->state = NETPLAYER_STATE_MISSION_LOADING;
8616 send_netplayer_update_packet();
8618 game_start_mission();
8620 nprintf(("Network","Done\n"));
8621 Multi_mission_loaded = 1;
8623 // update everyone about my status
8624 Net_player->state = NETPLAYER_STATE_MISSION_LOADED;
8625 send_netplayer_update_packet();
8627 if(!(Game_mode & GM_STANDALONE_SERVER)){
8628 multi_common_add_text(XSTR("Loaded mission locally\n",836),1);
8633 // if everyone has loaded the mission, randomly assign players to ships
8634 if(multi_netplayer_state_check(NETPLAYER_STATE_MISSION_LOADED) && !(Mission_sync_flags & MS_FLAG_TS_SLOTS) && !multi_endgame_ending()){
8635 // call the team select function to assign players to their ships, wings, etc
8636 multi_ts_assign_players_all();
8637 send_netplayer_slot_packet();
8640 Mission_sync_flags |= MS_FLAG_TS_SLOTS;
8643 // if everyone has loaded the mission, move to the team select stage
8644 if(Sync_test && multi_netplayer_state_check(NETPLAYER_STATE_SLOT_ACK) && !(Mission_sync_flags & MS_FLAG_PUSHED_BRIEFING) && !multi_endgame_ending()){
8645 Netgame.game_state = NETGAME_STATE_BRIEFING;
8646 send_netgame_update_packet(); // this will push everyone into the next state
8648 // the standalone moves to his own wait state, whereas in the normal game mode, the server/host moves in to the
8649 // team select state
8650 if(Game_mode & GM_STANDALONE_SERVER){
8651 gameseq_post_event(GS_EVENT_MULTI_STD_WAIT);
8653 gameseq_post_event(GS_EVENT_START_GAME);
8656 Mission_sync_flags |= MS_FLAG_PUSHED_BRIEFING;
8658 if(!(Game_mode & GM_STANDALONE_SERVER)){
8659 multi_common_add_text(XSTR("Moving to team select\n",837),1);
8663 // clients should detect here if they are doing a file xfer and do error processing
8664 if((Net_player->state == NETPLAYER_STATE_MISSION_XFER) && (Net_player->s_info.xfer_handle != -1) && !multi_endgame_ending()){
8665 switch(multi_xfer_get_status(Net_player->s_info.xfer_handle)){
8666 // if it has successfully completed, set his ok flag
8667 case MULTI_XFER_SUCCESS :
8668 // release my xfer handle
8669 multi_xfer_release_handle(Net_player->s_info.xfer_handle);
8670 Net_player->s_info.xfer_handle = -1;
8673 // if it has failed or timed-out, kick the player
8674 case MULTI_XFER_TIMEDOUT:
8675 case MULTI_XFER_FAIL:
8676 // release my xfer handle
8677 multi_xfer_release_handle(Net_player->s_info.xfer_handle);
8678 Net_player->s_info.xfer_handle = -1;
8680 // leave the game qith an error code
8681 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_XFER_FAIL);
8688 if(!(Game_mode & GM_STANDALONE_SERVER)){
8690 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8691 if(Multi_sync_bitmap != -1){
8692 gr_set_bitmap(Multi_sync_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8695 Multi_sync_window.draw();
8697 multi_sync_blit_screen_all();
8703 void multi_sync_pre_close()
8705 // at this point, we should shut down any file xfers...
8706 if(Net_player->s_info.xfer_handle != -1){
8707 nprintf(("Network","WARNING - killing file xfer while leaving mission sync state!!!\n"));
8709 multi_xfer_abort(Net_player->s_info.xfer_handle);
8710 Net_player->s_info.xfer_handle = -1;
8714 void multi_sync_post_init()
8716 multi_reset_timestamps();
8718 Multi_state_timestamp = timestamp(0);
8721 ml_string(NOX("Performing post-briefing data sync"));
8723 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8724 multi_common_add_text(XSTR("Server performing sync\n",830),1);
8726 multi_common_add_text(XSTR("Client performing sync\n",838),1);
8729 // everyone should re-initialize these
8730 init_multiplayer_stats();
8732 // reset all sequencing info
8733 multi_oo_reset_sequencing();
8735 // if I am not the master of the game, then send the firing information for my ship
8737 if ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) ){
8738 send_firing_info_packet();
8741 // if I'm not a standalone server, load up the countdown stuff
8742 if(!(Game_mode & GM_STANDALONE_SERVER)){
8743 Multi_sync_countdown_anim = NULL;
8744 Multi_sync_countdown_instance = NULL;
8745 Multi_sync_countdown_anim = anim_load(Multi_sync_countdown_fname[gr_screen.res]);
8746 if(Multi_sync_countdown_anim == NULL){
8747 nprintf(("General","WARNING!, Could not load countdown animation %s!\n",Multi_sync_countdown_fname[gr_screen.res]));
8751 // create objects for all permanent observers
8752 multi_obs_level_init();
8754 // clear the game start countdown timer
8755 Multi_sync_countdown_timer = -1.0f;
8756 Multi_sync_countdown = -1;
8758 // if this is a team vs. team mission, mark all ship teams appropriately
8759 if(Netgame.type_flags & NG_TYPE_TEAM){
8760 multi_team_mark_all_ships();
8763 Mission_sync_flags = 0;
8764 Multi_sync_launch_pressed = 0;
8767 #define MULTI_POST_TIMESTAMP 7000
8769 extern int create_wings();
8771 void multi_sync_post_do()
8775 // only if the host is also the master should he be doing this (non-standalone situation)
8776 if ( Net_player->flags & NETINFO_FLAG_AM_MASTER ) {
8778 // once everyone gets to this screen, send them the ship classes of all ships.
8779 if(multi_netplayer_state_check(NETPLAYER_STATE_WAITING) && !(Mission_sync_flags & MS_FLAG_POST_DATA)) {
8780 // only the host should ever do this
8781 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8782 // at this point we want to delete all necessary ships, change all necessary ship classes, and set all weapons up
8783 multi_ts_create_wings();
8785 // update player ets settings
8786 for(idx=0;idx<MAX_PLAYERS;idx++){
8787 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].player->objnum != -1)){
8788 multi_server_update_player_weapons(&Net_players[idx],&Ships[Objects[Net_players[idx].player->objnum].instance]);
8793 // note that this is done a little differently for standalones and nonstandalones
8794 send_post_sync_data_packet();
8796 multi_common_add_text(XSTR("Sending post briefing block information\n",839),1);
8798 Mission_sync_flags |= MS_FLAG_POST_DATA;
8801 // send weapon slots data
8802 if(multi_netplayer_state_check(NETPLAYER_STATE_POST_DATA_ACK) && !(Mission_sync_flags & MS_FLAG_WSS_SLOTS)) {
8803 // note that this is done a little differently for standalones and nonstandalones
8804 if(Netgame.type_flags & NG_TYPE_TEAM){
8805 send_wss_slots_data_packet(0,0);
8806 send_wss_slots_data_packet(1,1);
8808 send_wss_slots_data_packet(0,1);
8811 multi_common_add_text(XSTR("Sending weapon slots information\n",840),1);
8813 Mission_sync_flags |= MS_FLAG_WSS_SLOTS;
8816 // once weapon information is received, send player settings info
8817 if ( multi_netplayer_state_check(NETPLAYER_STATE_WSS_ACK) && !(Mission_sync_flags & MS_FLAG_PSETTINGS)) {
8818 send_player_settings_packet();
8820 // server (specifically, the standalone), should set this here
8821 Net_player->state = NETPLAYER_STATE_SETTINGS_ACK;
8822 send_netplayer_update_packet();
8824 multi_common_add_text(XSTR("Sending player settings packets\n",841),1);
8826 Mission_sync_flags |= MS_FLAG_PSETTINGS;
8829 // check to see if the countdown timer has started and act appropriately
8830 if( Multi_sync_countdown_timer > -1.0f ) {
8832 // increment by frametime.
8833 Multi_sync_countdown_timer += flFrametime;
8835 // if the animation is not playing, start it
8836 if(!(Game_mode & GM_STANDALONE_SERVER) && (Multi_sync_countdown_instance == NULL) && (Multi_sync_countdown_anim != NULL)){
8837 anim_play_struct aps;
8839 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]);
8840 aps.screen_id = GS_STATE_MULTI_MISSION_SYNC;
8841 aps.framerate_independent = 1;
8843 Multi_sync_countdown_instance = anim_play(&aps);
8846 // if the next second has expired
8847 if( Multi_sync_countdown_timer >= 1.0f ) {
8849 Multi_sync_countdown--;
8850 Multi_sync_countdown_timer = 0.0f;
8852 // if the countdown has reached 0, launch the mission
8853 if(Multi_sync_countdown == 0){
8854 Multi_sync_countdown_timer = -1.0f;
8856 Multi_sync_launch_pressed = 0;
8857 multi_sync_launch();
8859 // otherwise send a countdown packet
8861 send_countdown_packet(Multi_sync_countdown);
8866 // jump into the mission myself
8867 if((Multi_sync_countdown == 0) && multi_netplayer_state_check(NETPLAYER_STATE_IN_MISSION)){
8868 if(!((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !Multi_sync_launch_pressed)){
8869 Net_player->state = NETPLAYER_STATE_IN_MISSION;
8870 Netgame.game_state = NETGAME_STATE_IN_MISSION;
8871 gameseq_post_event(GS_EVENT_ENTER_GAME);
8873 multi_common_add_text(XSTR("Moving into game\n",842),1);
8877 // maybe start the animation countdown
8878 if((Multi_sync_countdown >= 0) && (Multi_sync_countdown_instance == NULL) && (Multi_sync_countdown_anim != NULL)){
8879 anim_play_struct aps;
8881 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]);
8882 aps.screen_id = GS_STATE_MULTI_MISSION_SYNC;
8883 aps.framerate_independent = 1;
8885 Multi_sync_countdown_instance = anim_play(&aps);
8889 // host - specific stuff
8890 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8891 // create the launch button so the host can click
8892 if( Sync_test && multi_netplayer_state_check(NETPLAYER_STATE_SETTINGS_ACK) ){
8893 multi_sync_create_launch_button();
8898 if(!(Game_mode & GM_STANDALONE_SERVER)){
8900 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8901 if(Multi_sync_bitmap != -1){
8902 gr_set_bitmap(Multi_sync_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8905 Multi_sync_window.draw();
8907 multi_sync_blit_screen_all();
8913 void multi_sync_post_close()
8917 // if I'm not a standalone server, unload up the countdown stuff
8918 if(!(Game_mode & GM_STANDALONE_SERVER)){
8919 // release all rendering animation instances (should only be 1)
8920 anim_release_all_instances(GS_STATE_MULTI_MISSION_SYNC);
8921 Multi_sync_countdown_instance = NULL;
8923 // free up the countdown animation
8924 if(Multi_sync_countdown_anim != NULL){
8925 anim_free(Multi_sync_countdown_anim);
8926 Multi_sync_countdown_anim = NULL;
8930 // all players should reset sequencing
8931 for(idx=0;idx<MAX_PLAYERS;idx++){
8932 if(Net_player->flags & NETINFO_FLAG_CONNECTED){
8933 Net_players[idx].client_cinfo_seq = 0;
8934 Net_players[idx].client_server_seq = 0;
8938 // multiplayer dogfight
8939 multi_df_level_pre_enter();
8941 // clients should clear obj_pair array and add pair for themselves
8943 if ( MULTIPLAYER_CLIENT ) {
8945 obj_add_pairs( OBJ_INDEX(Player_obj) );
8950 void multi_sync_display_name(const char *name, int index, int np_index)
8952 char fit[CALLSIGN_LEN];
8954 // make sure the string actually fits
8955 SDL_strlcpy(fit, name, SDL_arraysize(fit));
8957 // if we're in team vs. team mode
8958 if(Netgame.type_flags & NG_TYPE_TEAM){
8959 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]);
8961 // if this is the currently selected player, draw him highlighted
8962 if(np_index == Multi_sync_player_select){
8963 gr_set_color_fast(&Color_text_selected);
8965 gr_set_color_fast(&Color_text_normal);
8969 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);
8971 // blit his team icon
8973 if(Net_players[np_index].p_info.team == 0){
8974 // blit the team captain icon
8975 if(Net_players[np_index].flags & NETINFO_FLAG_TEAM_CAPTAIN){
8976 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
8977 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8978 gr_bitmap(Ms_status_coords[gr_screen.res][MS_X_COORD] + Ms_cd_icon_offset[gr_screen.res], Ms_status_coords[gr_screen.res][MS_Y_COORD] + (index * 10) - 2);
8981 // normal team member icon
8983 if(Multi_common_icons[MICON_TEAM0] != -1){
8984 gr_set_bitmap(Multi_common_icons[MICON_TEAM0], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8985 gr_bitmap(Ms_status_coords[gr_screen.res][MS_X_COORD] + Ms_cd_icon_offset[gr_screen.res], Ms_status_coords[gr_screen.res][MS_Y_COORD] + (index * 10) - 2);
8990 else if(Net_players[np_index].p_info.team == 1){
8991 // blit the team captain icon
8992 if(Net_players[np_index].flags & NETINFO_FLAG_TEAM_CAPTAIN){
8993 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
8994 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8995 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);
8998 // normal team member icon
9000 if(Multi_common_icons[MICON_TEAM1] != -1){
9001 gr_set_bitmap(Multi_common_icons[MICON_TEAM1], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
9002 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);
9007 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]);
9009 // if this is the currently selected player, draw him highlighted
9010 if(np_index == Multi_sync_player_select){
9011 gr_set_color_fast(&Color_text_selected);
9013 gr_set_color_fast(&Color_text_normal);
9017 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);
9020 // maybe blit his CD status icon
9021 if((Net_players[np_index].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
9022 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
9023 gr_bitmap(Ms_status_coords[gr_screen.res][MS_X_COORD], Ms_status_coords[gr_screen.res][MS_Y_COORD] + (index * 10));
9027 void multi_sync_display_status(const char *status, int index)
9031 // make sure the string actually fits
9032 SDL_strlcpy(fit, status, SDL_arraysize(fit));
9033 gr_force_fit_string(fit, 250, Ms_status2_coords[gr_screen.res][MS_W_COORD] - 20);
9034 gr_set_color_fast(&Color_bright);
9035 gr_string(Ms_status2_coords[gr_screen.res][MS_X_COORD], Ms_status2_coords[gr_screen.res][MS_Y_COORD] + (index * 10), fit);
9038 void multi_sync_force_start_pre()
9041 int want_state = NETPLAYER_STATE_SLOT_ACK; // kick any players who are still in this state
9043 // go through the player list and boot anyone who isn't in the right state
9044 for(idx=0;idx<MAX_PLAYERS;idx++){
9045 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx]) && (Net_players[idx].state == want_state)){
9046 multi_kick_player(idx,0);
9051 void multi_sync_force_start_post()
9055 int num_kill_states;
9057 // determine the state we want all players in so that we can find those who are not in the state
9058 kill_state[0] = NETPLAYER_STATE_BRIEFING;
9059 kill_state[1] = NETPLAYER_STATE_SHIP_SELECT;
9060 kill_state[2] = NETPLAYER_STATE_WEAPON_SELECT;
9061 num_kill_states = 3;
9063 // go through the player list and boot anyone who isn't in the right state
9064 for(idx=0;idx<MAX_PLAYERS;idx++){
9065 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx])){
9066 // check against all kill state
9067 for(idx2 = 0;idx2<num_kill_states;idx2++){
9068 if(Net_players[idx].state == kill_state[idx2]){
9069 multi_kick_player(idx,0);
9077 void multi_sync_start_countdown()
9079 // don't allow repeat button presses
9080 if(Multi_sync_launch_pressed){
9084 Multi_sync_launch_pressed = 1;
9086 // if I'm the server, begin the countdown
9087 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
9088 gamesnd_play_iface(SND_COMMIT_PRESSED);
9089 Multi_sync_countdown_timer = 0.0f;
9090 Multi_sync_countdown = MULTI_SYNC_COUNTDOWN_TIME;
9092 // send an initial countdown value
9093 send_countdown_packet(Multi_sync_countdown);
9095 // otherwise send the "start countdown" packet to the standalone
9097 SDL_assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
9098 send_countdown_packet(-1);
9102 void multi_sync_launch()
9104 // don't allow repeat button presses
9105 if(Multi_sync_launch_pressed){
9109 Multi_sync_launch_pressed = 1;
9112 ml_printf(NOX("Entering mission %s"), Game_current_mission_filename);
9114 // tell everyone to jump into the mission
9115 send_jump_into_mission_packet();
9116 Multi_state_timestamp = timestamp(MULTI_POST_TIMESTAMP);
9118 // set the # of players at the start of the mission
9119 Multi_num_players_at_start = multi_num_players();
9120 nprintf(("Network","# of players at start of mission : %d\n", Multi_num_players_at_start));
9122 // initialize datarate limiting for all clients
9123 multi_oo_rate_init_all();
9125 multi_common_add_text(XSTR("Sending mission start packet\n",843),1);
9128 void multi_sync_create_launch_button()
9130 if (!Multi_launch_button_created) {
9131 // create the object
9132 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);
9134 // set the sound to play when highlighted
9135 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_highlight_action(common_play_highlight_sound);
9137 // set the ani for the button
9138 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_bmaps(Multi_sync_buttons[gr_screen.res][MS_LAUNCH].filename, 3, 0);
9141 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.link_hotspot(Multi_sync_buttons[gr_screen.res][MS_LAUNCH].hotspot);
9144 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_hotkey(KEY_CTRLED+SDLK_RETURN);
9147 // create the text for the button
9148 Multi_sync_window.add_XSTR(&Multi_sync_text[gr_screen.res][MST_LAUNCH]);
9151 // increment the button count so we start checking this one
9152 Multi_sync_button_count++;
9154 Multi_launch_button_created = 1;
9158 void multi_sync_handle_plist()
9164 // if we don't have a currently selected player, select one
9165 if((Multi_sync_player_select < 0) || !MULTI_CONNECTED(Net_players[Multi_sync_player_select])){
9166 for(idx=0;idx<MAX_PLAYERS;idx++){
9167 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
9168 Multi_sync_player_select = idx;
9174 // check for button list presses
9175 if(Multi_sync_plist_button.pressed()){
9176 // get the y mouse coords
9177 Multi_sync_plist_button.get_mouse_pos(NULL,&my);
9179 // get the index of the item selected
9180 select_index = my / 10;
9182 // if the index is greater than the current # connections, do nothing
9183 if(select_index > (multi_num_connections() - 1)){
9187 // translate into an absolute Net_players[] index (get the Nth net player)
9188 Multi_sync_player_select = -1;
9189 for(idx=0;idx<MAX_PLAYERS;idx++){
9190 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
9194 // if we've found the item we're looking for
9195 if(select_index < 0){
9196 Multi_sync_player_select = idx;
9201 // if for some bizarre reason, this is an invalid player, unselect him and wait for the next interation
9203 if((Multi_sync_player_select >= 0) && (!MULTI_CONNECTED(Net_players[Multi_sync_player_select]) || MULTI_STANDALONE(Net_players[Multi_sync_player_select])) ){
9204 Multi_sync_player_select = -1;
9210 // -------------------------------------------------------------------------------------------------------------
9212 // MULTIPLAYER DEBRIEF SCREEN
9215 // other relevant data
9216 int Multi_debrief_accept_hit;
9217 int Multi_debrief_replay_hit;
9219 // set if the server has left the game
9220 int Multi_debrief_server_left = 0;
9222 // if we've reported on TvT status all players are in the debrief
9223 int Multi_debrief_reported_tvt = 0;
9225 // whether stats are being accepted
9226 // -1 == no decision yet
9229 int Multi_debrief_stats_accept_code = -1;
9231 int Multi_debrief_server_framecount = 0;
9233 float Multi_debrief_time = 0.0f;
9234 float Multi_debrief_resend_time = 10.0f;
9236 void multi_debrief_init()
9240 Multi_debrief_time = 0.0f;
9241 Multi_debrief_resend_time = 10.0f;
9243 // do this to notify the standalone or the normal server that we're in the debrief state and ready to receive packets
9244 if (!(Net_player->flags & NETINFO_FLAG_AM_MASTER)) {
9245 Net_player->state = NETPLAYER_STATE_DEBRIEF;
9246 send_netplayer_update_packet();
9249 // unflag some stuff
9250 for(idx=0;idx<MAX_PLAYERS;idx++){
9251 if(MULTI_CONNECTED(Net_players[idx])){
9252 Net_players[idx].flags &= ~(NETINFO_FLAG_RESPAWNING | NETINFO_FLAG_LIMBO | NETINFO_FLAG_WARPING_OUT);
9256 // if text input mode is active, clear it
9257 multi_msg_text_flush();
9259 // the server has not left yet
9260 Multi_debrief_server_left = 0;
9262 // have not hit accept or replay yet
9263 Multi_debrief_accept_hit = 0;
9264 Multi_debrief_replay_hit = 0;
9266 // stats have not been accepted yet
9267 Multi_debrief_stats_accept_code = -1;
9269 // mark stats as not being store yet
9270 Netgame.flags &= ~(NG_FLAG_STORED_MT_STATS);
9272 // no report on TvT yet
9273 Multi_debrief_reported_tvt = 0;
9275 Multi_debrief_server_framecount = 0;
9278 void multi_debrief_do_frame()
9280 Multi_debrief_time += flFrametime;
9282 // set the netgame state to be debriefing when appropriate
9283 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)){
9284 Netgame.game_state = NETGAME_STATE_DEBRIEF;
9285 send_netgame_update_packet();
9288 // evaluate all server stuff
9289 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
9290 multi_debrief_server_process();
9294 void multi_debrief_close()
9296 if ( MULTIPLAYER_CLIENT && (Netgame.game_state == NETGAME_STATE_MISSION_SYNC) ){
9297 gamesnd_play_iface( SND_COMMIT_PRESSED );
9301 // handle optional mission loop
9302 void multi_maybe_set_mission_loop()
9304 int cur = Campaign.current_mission;
9305 if (Campaign.missions[cur].has_mission_loop) {
9306 SDL_assert(Campaign.loop_mission != CAMPAIGN_LOOP_MISSION_UNINITIALIZED);
9308 bool require_repeat_mission = (Campaign.current_mission == Campaign.next_mission);
9310 // check for (1) mission loop available, (2) dont have to repeat last mission
9311 if ( (Campaign.missions[cur].has_mission_loop && (Campaign.loop_mission != -1)) && !require_repeat_mission ) {
9314 debrief_assemble_optional_mission_popup_text(buffer, SDL_arraysize(buffer), Campaign.missions[cur].mission_loop_desc);
9316 int choice = popup(0 , 2, POPUP_NO, POPUP_YES, buffer);
9318 Campaign.loop_enabled = 1;
9319 Campaign.next_mission = Campaign.loop_mission;
9324 // handle all cases for when the accept key is hit in a multiplayer debriefing
9325 void multi_debrief_accept_hit()
9327 // if we already accepted, do nothing
9328 // but he may need to hit accept again after the server has left the game, so allow this
9329 if(Multi_debrief_accept_hit){
9333 // mark this so that we don't hit it again
9334 Multi_debrief_accept_hit = 1;
9336 gamesnd_play_iface(SND_COMMIT_PRESSED);
9338 // if the server has left the game, always just end the game.
9339 if(Multi_debrief_server_left){
9340 if(!multi_quit_game(PROMPT_ALL)){
9341 Multi_debrief_server_left = 1;
9342 Multi_debrief_accept_hit = 0;
9344 Multi_debrief_server_left = 0;
9347 // query the host and see if he wants to accept stats
9348 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
9349 // if we're on a tracker game, he gets no choice for storing stats
9350 if(MULTI_IS_TRACKER_GAME){
9352 int stats_saved = multi_fs_tracker_store_stats();
9354 if (Netgame.type_flags & NG_TYPE_SW) {
9355 multi_sw_report(stats_saved);
9358 multi_fs_tracker_store_stats();
9361 multi_maybe_set_mission_loop();
9363 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));
9365 // evaluate the result
9370 Multi_debrief_accept_hit = 0;
9373 // set the accept code to be "not accepting"
9375 multi_debrief_stats_toss();
9376 multi_maybe_set_mission_loop();
9379 // accept the stats and continue
9381 multi_debrief_stats_accept();
9382 multi_maybe_set_mission_loop();
9388 // set my netplayer state to be "debrief_accept", and be done with it
9389 Net_player->state = NETPLAYER_STATE_DEBRIEF_ACCEPT;
9390 send_netplayer_update_packet();
9394 // handle all cases for when the escape key is hit in a multiplayer debriefing
9395 void multi_debrief_esc_hit()
9399 // if the server has left
9400 if(Multi_debrief_server_left){
9401 multi_quit_game(PROMPT_ALL);
9406 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
9407 // if the stats have already been accepted
9408 if((Multi_debrief_stats_accept_code != -1) || (MULTI_IS_TRACKER_GAME)){
9409 if (Multi_debrief_stats_accept_code == 1) {
9411 int stats_saved = multi_fs_tracker_store_stats();
9413 if (Netgame.type_flags & NG_TYPE_SW) {
9414 multi_sw_report(stats_saved);
9417 multi_fs_tracker_store_stats();
9421 multi_quit_game(PROMPT_HOST);
9423 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));
9425 // evaluate the result
9432 // set the accept code to be "not accepting"
9434 multi_debrief_stats_toss();
9435 multi_quit_game(PROMPT_NONE);
9438 // accept the stats and continue
9440 multi_debrief_stats_accept();
9441 multi_quit_game(PROMPT_NONE);
9446 // if the stats haven't been accepted yet, or this is a tracker game
9447 if((Multi_debrief_stats_accept_code == -1) && !(MULTI_IS_TRACKER_GAME)){
9448 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));
9450 // evaluate the result
9452 multi_quit_game(PROMPT_NONE);
9455 // otherwise go through the normal endgame channels
9457 multi_quit_game(PROMPT_ALL);
9462 void multi_debrief_replay_hit()
9464 // only the host should ever get here
9465 SDL_assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
9467 // if the button was already pressed, do nothing
9468 if(Multi_debrief_accept_hit){
9472 // same as hittin the except button except no stats are kept
9473 Multi_debrief_accept_hit = 1;
9475 // mark myself as being in the replay state so we know what to do next
9476 Net_player->state = NETPLAYER_STATE_DEBRIEF_REPLAY;
9477 send_netplayer_update_packet();
9480 // call this when the server has left and we would otherwise be saying "contact lost with server
9481 void multi_debrief_server_left()
9484 Multi_debrief_server_left = 1;
9486 // undo any "accept" hit so that clients can hit accept again to leave
9487 Multi_debrief_accept_hit = 0;
9490 void multi_debrief_stats_accept()
9492 // don't do anything if we've already accepted
9493 if(Multi_debrief_stats_accept_code != -1){
9497 Multi_debrief_stats_accept_code = 1;
9499 // if we're the host, and we're on a standalone, tell the standalone to begin the stats storing process
9500 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) || (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
9501 // send a packet to the players telling them to store their stats
9502 send_store_stats_packet(1);
9505 // add a chat line saying "stats have been accepted"
9506 multi_display_chat_msg(XSTR("<stats have been accepted>",850),0,0);
9509 ml_string(NOX("Stats stored"));
9512 void multi_debrief_stats_toss()
9514 // don't do anything if we've already accepted
9515 if(Multi_debrief_stats_accept_code != -1){
9519 Multi_debrief_stats_accept_code = 0;
9521 // if we're the host, and we're on a standalone, tell everyone to "toss" stats
9522 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) || (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
9523 // send a packet to the players telling them to store their stats
9524 send_store_stats_packet(0);
9527 // add a chat line saying "stats have been accepted"
9528 multi_display_chat_msg(XSTR("<stats have been tossed>",851),0,0);
9531 ml_string(NOX("Stats tossed"));
9534 int multi_debrief_stats_accept_code()
9536 return Multi_debrief_stats_accept_code;
9539 void multi_debrief_server_process()
9542 int player_status,other_status;
9544 Multi_debrief_server_framecount++;
9546 // if we're > 10 seconds into the debrief and not everyone is here, try warping everyone out again
9547 if((Multi_debrief_time >= Multi_debrief_resend_time) && !multi_netplayer_state_check3(NETPLAYER_STATE_DEBRIEF, NETPLAYER_STATE_DEBRIEF_ACCEPT, NETPLAYER_STATE_DEBRIEF_REPLAY, 1)){
9548 // find all players who are not in the debrief state and hit them with the endgame packet
9549 for(idx=0; idx<MAX_PLAYERS; idx++){
9550 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)) ){
9551 send_endgame_packet(&Net_players[idx]);
9556 Multi_debrief_resend_time += 7.0f;
9559 // evaluate the status of all players in the game (0 == not ready, 1 == ready to continue, 2 == ready to replay)
9562 // check all players
9563 for(idx=0;idx<MAX_PLAYERS;idx++){
9564 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_HOST(Net_players[idx])){
9565 if(Net_players[idx].state != NETPLAYER_STATE_DEBRIEF_ACCEPT){
9572 // if we haven't already reported TvT results
9573 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)){
9574 multi_team_report();
9575 Multi_debrief_reported_tvt = 1;
9578 // if all other players are good to go, check the host
9580 // if he is ready to continue
9581 if(Netgame.host->state == NETPLAYER_STATE_DEBRIEF_ACCEPT){
9584 // if he wants to replay the mission
9585 else if(Netgame.host->state == NETPLAYER_STATE_DEBRIEF_REPLAY){
9588 // if he is not ready
9593 // if all players are _not_ good to go
9598 // if we're in the debriefing state in a campaign mode, process accordingly
9599 if(Netgame.campaign_mode == MP_CAMPAIGN){
9600 multi_campaign_do_debrief(player_status);
9602 // otherwise process as normal (looking for all players to be ready to go to the next mission
9604 if(player_status == 1){
9605 multi_flush_mission_stuff();
9607 // set the netgame state to be forming and continue
9608 Netgame.game_state = NETGAME_STATE_FORMING;
9609 send_netgame_update_packet();
9611 // move to the proper state
9612 if(Game_mode & GM_STANDALONE_SERVER){
9613 gameseq_post_event(GS_EVENT_STANDALONE_MAIN);
9615 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
9618 multi_reset_timestamps();
9619 } else if(player_status == 2){
9620 multi_flush_mission_stuff();
9622 // tell everyone to move into the pre-briefing sync state
9623 Netgame.game_state = NETGAME_STATE_MISSION_SYNC;
9624 send_netgame_update_packet();
9626 // move back to the mission sync screen for the same mission again
9627 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
9628 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
9630 multi_reset_timestamps();
9636 // -------------------------------------------------------------------------------------------------------------
9638 // MULTIPLAYER PASSWORD POPUP
9643 static const char *Multi_pwd_bitmap_fname[GR_NUM_RESOLUTIONS] = {
9644 "Password", // GR_640
9645 "2_Password" // GR_1024
9648 static const char *Multi_pwd_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
9649 "Password-M", // GR_640
9650 "2_Password-M" // GR_1024
9655 // constants for coordinate lookup
9656 #define MPWD_X_COORD 0
9657 #define MPWD_Y_COORD 1
9658 #define MPWD_W_COORD 2
9659 #define MPWD_H_COORD 3
9662 #define MULTI_PWD_NUM_BUTTONS 2
9663 #define MPWD_CANCEL 0
9664 #define MPWD_COMMIT 1
9666 // password area defs
9667 int Mpwd_coords[GR_NUM_RESOLUTIONS][4] = {
9680 UI_WINDOW Multi_pwd_window; // the window object for the join screen
9681 UI_INPUTBOX Multi_pwd_passwd; // for Netgame.passwd
9682 int Multi_pwd_bitmap; // the background bitmap
9683 int Multi_passwd_background = -1;
9684 int Multi_passwd_done = -1;
9685 int Multi_passwd_running = 0;
9688 ui_button_info Multi_pwd_buttons[GR_NUM_RESOLUTIONS][MULTI_PWD_NUM_BUTTONS] = {
9691 ui_button_info("PWB_00", 402, 134, -1, -1, 0),
9692 ui_button_info("PWB_01", 450, 134, -1, -1, 1),
9694 ui_button_info("PWB_00", 411, 151, 405, 141, 0),
9695 ui_button_info("PWB_01", 460, 151, 465, 141, 1),
9699 ui_button_info("2_PWB_00", 659, 242, 649, 225, 0),
9700 ui_button_info("2_PWB_01", 737, 242, 736, 225, 1),
9706 #define MULTI_PWD_NUM_TEXT 3
9708 UI_XSTR Multi_pwd_text[GR_NUM_RESOLUTIONS][MULTI_PWD_NUM_TEXT] = {
9710 { "Cancel", 387, 400, 141, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[0][MPWD_CANCEL].button},
9711 { "Commit", 1062, 455, 141, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[0][MPWD_COMMIT].button},
9712 { "Enter Password", 1332, 149, 92, UI_XSTR_COLOR_GREEN, -1, NULL},
9715 { "Cancel", 387, 649, 225, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[1][MPWD_CANCEL].button},
9716 { "Commit", 1062, 736, 225, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[1][MPWD_COMMIT].button},
9717 { "Enter Password", 1332, 239, 148, UI_XSTR_COLOR_GREEN, -1, NULL},
9722 // initialize all graphics, etc
9723 void multi_passwd_init()
9727 // store the background as it currently is
9728 Multi_passwd_background = gr_save_screen();
9730 // create the interface window
9731 Multi_pwd_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
9732 Multi_pwd_window.set_mask_bmap(Multi_pwd_bitmap_mask_fname[gr_screen.res]);
9734 // load the background bitmap
9735 Multi_pwd_bitmap = bm_load(Multi_pwd_bitmap_fname[gr_screen.res]);
9736 if(Multi_pwd_bitmap < 0){
9737 // we failed to load the bitmap - this is very bad
9741 // create the interface buttons
9742 for(idx=0; idx<MULTI_PWD_NUM_BUTTONS; idx++){
9743 // create the object
9744 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);
9746 // set the sound to play when highlighted
9747 Multi_pwd_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
9749 // set the ani for the button
9750 Multi_pwd_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pwd_buttons[gr_screen.res][idx].filename);
9753 Multi_pwd_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pwd_buttons[gr_screen.res][idx].hotspot);
9758 for(idx=0; idx<MULTI_PWD_NUM_TEXT; idx++){
9759 Multi_pwd_window.add_XSTR(&Multi_pwd_text[gr_screen.res][idx]);
9763 // create the password input box
9764 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);
9765 Multi_pwd_passwd.set_focus();
9767 // link the enter key to ACCEPT
9768 Multi_pwd_buttons[gr_screen.res][MPWD_COMMIT].button.set_hotkey(SDLK_RETURN);
9770 Multi_passwd_done = -1;
9771 Multi_passwd_running = 1;
9774 // close down all graphics, etc
9775 void multi_passwd_close()
9777 // unload any bitmaps
9778 bm_release(Multi_pwd_bitmap);
9780 // destroy the UI_WINDOW
9781 Multi_pwd_window.destroy();
9783 // free up the saved background screen
9784 if(Multi_passwd_background >= 0){
9785 gr_free_screen(Multi_passwd_background);
9786 Multi_passwd_background = -1;
9789 Multi_passwd_running = 0;
9792 // process any button pressed
9793 void multi_passwd_process_buttons()
9795 // if the accept button was pressed
9796 if(Multi_pwd_buttons[gr_screen.res][MPWD_COMMIT].button.pressed()){
9797 gamesnd_play_iface(SND_USER_SELECT);
9798 Multi_passwd_done = 1;
9801 // if the cancel button was pressed
9802 if(Multi_pwd_buttons[gr_screen.res][MPWD_CANCEL].button.pressed()){
9803 gamesnd_play_iface(SND_USER_SELECT);
9804 Multi_passwd_done = 0;
9808 // run the passwd popup
9809 void multi_passwd_do(char *passwd, const int max_passlen)
9813 while(Multi_passwd_done == -1){
9814 // set frametime and run background stuff
9815 game_set_frametime(-1);
9816 game_do_state_common(gameseq_get_state());
9818 k = Multi_pwd_window.process();
9820 // process any keypresses
9823 // set this to indicate the user has cancelled for one reason or another
9824 Multi_passwd_done = 0;
9828 // if the input box text has changed
9829 if(Multi_pwd_passwd.changed()){
9830 SDL_strlcpy(passwd, "", max_passlen);
9831 Multi_pwd_passwd.get_text(passwd);
9834 // process any button pressed
9835 multi_passwd_process_buttons();
9837 // draw the background, etc
9840 if(Multi_passwd_background >= 0){
9841 gr_restore_screen(Multi_passwd_background);
9843 gr_set_bitmap(Multi_pwd_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
9845 Multi_pwd_window.draw();
9852 // bring up the password string popup, fill in passwd (return 1 if accept was pressed, 0 if cancel was pressed)
9853 int multi_passwd_popup(char *passwd, const int max_plen)
9855 // if the popup is already running for some reason, don't do anything
9856 if(Multi_passwd_running){
9860 // initialize all graphics
9861 multi_passwd_init();
9864 multi_passwd_do(passwd, max_plen);
9866 // shut everything down
9867 multi_passwd_close();
9869 return Multi_passwd_done;