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."
471 #include <winsock.h> // for inet_addr()
473 #include <sys/types.h>
474 #include <sys/socket.h>
475 #include <netinet/in.h>
476 #include <arpa/inet.h>
481 #include "multiutil.h"
482 #include "multimsgs.h"
488 #include "gamesequence.h"
489 #include "freespace.h"
490 #include "contexthelp.h"
495 #include "missionshipchoice.h"
496 #include "multi_xfer.h"
498 #include "stand_gui.h"
499 #include "linklist.h"
500 #include "multiteamselect.h"
501 #include "missioncampaign.h"
508 #include "missiondebrief.h"
509 #include "multi_ingame.h"
510 #include "multi_kick.h"
511 #include "multi_data.h"
512 #include "multi_campaign.h"
513 #include "multi_team.h"
514 #include "multi_pinfo.h"
515 #include "multi_observer.h"
516 #include "multi_voice.h"
517 #include "multi_endgame.h"
518 #include "managepilot.h"
521 #include "objcollide.h"
523 #include "multi_pmsg.h"
524 #include "multi_obj.h"
525 #include "multi_log.h"
526 #include "alphacolors.h"
527 #include "animplay.h"
528 #include "multi_dogfight.h"
529 #include "missionpause.h"
531 // -------------------------------------------------------------------------------------------------------------
533 // MULTIPLAYER COMMON interface controls
536 // the common text info box stuff. This is lifted almost directly from Alans briefing code (minus the spiffy colored, scrolling
538 int Multi_common_text_coords[GR_NUM_RESOLUTIONS][4] = {
551 int Multi_common_text_max_display[GR_NUM_RESOLUTIONS] = {
560 #define MULTI_COMMON_TEXT_META_CHAR '$'
561 #define MULTI_COMMON_TEXT_MAX_LINE_LENGTH 100
562 #define MULTI_COMMON_TEXT_MAX_LINES 20
563 #define MULTI_COMMON_MAX_TEXT (MULTI_COMMON_TEXT_MAX_LINES * MULTI_COMMON_TEXT_MAX_LINE_LENGTH)
565 char Multi_common_all_text[MULTI_COMMON_MAX_TEXT];
566 char Multi_common_text[MULTI_COMMON_TEXT_MAX_LINES][MULTI_COMMON_TEXT_MAX_LINE_LENGTH];
568 int Multi_common_top_text_line = -1; // where to start displaying from
569 int Multi_common_num_text_lines = 0; // how many lines we have
571 void multi_common_scroll_text_up();
572 void multi_common_scroll_text_down();
573 void multi_common_move_to_bottom();
574 void multi_common_render_text();
575 void multi_common_split_text();
577 #define MAX_IP_STRING 255 // maximum length for ip string
579 void multi_common_scroll_text_up()
581 Multi_common_top_text_line--;
582 if ( Multi_common_top_text_line < 0 ) {
583 Multi_common_top_text_line = 0;
584 if ( !mouse_down(MOUSE_LEFT_BUTTON) )
585 gamesnd_play_iface(SND_GENERAL_FAIL);
588 gamesnd_play_iface(SND_SCROLL);
592 void multi_common_scroll_text_down()
594 Multi_common_top_text_line++;
595 if ( (Multi_common_num_text_lines - Multi_common_top_text_line) < Multi_common_text_max_display[gr_screen.res] ) {
596 Multi_common_top_text_line--;
597 if ( !mouse_down(MOUSE_LEFT_BUTTON) ){
598 gamesnd_play_iface(SND_GENERAL_FAIL);
601 gamesnd_play_iface(SND_SCROLL);
605 void multi_common_move_to_bottom()
607 // if there's nowhere to scroll down, do nothing
608 if(Multi_common_num_text_lines <= Multi_common_text_max_display[gr_screen.res]){
612 Multi_common_top_text_line = Multi_common_num_text_lines - Multi_common_text_max_display[gr_screen.res];
615 void multi_common_set_text(const char *str, int auto_scroll)
618 // store the entire string as well
619 if(strlen(str) > MULTI_COMMON_MAX_TEXT){
622 SDL_strlcpy(Multi_common_all_text, str, sizeof(Multi_common_all_text));
625 // split the whole thing up
626 multi_common_split_text();
628 // scroll to the bottom if we're supposed to
630 multi_common_move_to_bottom();
634 void multi_common_add_text(const char *str, int auto_scroll)
637 // store the entire string as well
638 if((strlen(str) + strlen(Multi_common_all_text)) > MULTI_COMMON_MAX_TEXT){
641 SDL_strlcat(Multi_common_all_text, str, sizeof(Multi_common_all_text));
644 // split the whole thing up
645 multi_common_split_text();
647 // scroll to the bottom if we're supposed to
649 multi_common_move_to_bottom();
653 void multi_common_split_text()
656 int n_chars[MAX_BRIEF_LINES];
657 char *p_str[MAX_BRIEF_LINES];
659 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);
660 SDL_assert(n_lines != -1);
662 for ( i = 0; i < n_lines; i++ ) {
663 SDL_assert(n_chars[i] < MULTI_COMMON_TEXT_MAX_LINE_LENGTH);
664 int len = min(n_chars[i] + 1, MULTI_COMMON_TEXT_MAX_LINE_LENGTH);
665 SDL_strlcpy(Multi_common_text[i], p_str[i], len);
666 Multi_common_text[i][n_chars[i]] = 0;
667 drop_leading_white_space(Multi_common_text[i]);
670 Multi_common_top_text_line = 0;
671 Multi_common_num_text_lines = n_lines;
674 void multi_common_render_text()
676 int i, fh, line_count;
678 fh = gr_get_font_height();
681 gr_set_color_fast(&Color_text_normal);
682 for ( i = Multi_common_top_text_line; i < Multi_common_num_text_lines; i++ ) {
683 if ( line_count >= Multi_common_text_max_display[gr_screen.res] ){
686 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]);
690 if ( (Multi_common_num_text_lines - Multi_common_top_text_line) > Multi_common_text_max_display[gr_screen.res] ) {
691 gr_set_color_fast(&Color_bright_red);
692 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));
696 // common notification messaging stuff
697 #define MULTI_COMMON_NOTIFY_TIME 3500
698 int Multi_common_join_y[GR_NUM_RESOLUTIONS] = {
702 int Multi_common_create_y[GR_NUM_RESOLUTIONS] = {
707 int Multi_common_jw_y[GR_NUM_RESOLUTIONS] = {
712 int Multi_common_msg_y[GR_NUM_RESOLUTIONS] = {
717 char Multi_common_notify_text[200];
718 int Multi_common_notify_stamp;
720 void multi_common_notify_init()
722 SDL_strlcpy(Multi_common_notify_text, "", sizeof(Multi_common_notify_text));
723 Multi_common_notify_stamp = -1;
726 // add a notification string, drawing appropriately depending on the state/screen we're in
727 void multi_common_add_notify(const char *str)
730 SDL_strlcpy(Multi_common_notify_text, str, sizeof(Multi_common_notify_text));
731 Multi_common_notify_stamp = timestamp(MULTI_COMMON_NOTIFY_TIME);
735 // process/display notification messages
736 void multi_common_notify_do()
738 if(Multi_common_notify_stamp != -1){
739 if(timestamp_elapsed(Multi_common_notify_stamp)){
740 Multi_common_notify_stamp = -1;
743 gr_get_string_size(&w,&h,Multi_common_notify_text);
744 gr_set_color_fast(&Color_white);
746 // determine where it should be placed based upon which screen we're on
748 switch(gameseq_get_state()){
749 case GS_STATE_MULTI_JOIN_GAME :
750 y = Multi_common_join_y[gr_screen.res];
752 case GS_STATE_MULTI_HOST_SETUP :
753 y = Multi_common_create_y[gr_screen.res];
755 case GS_STATE_MULTI_CLIENT_SETUP :
756 y = Multi_common_jw_y[gr_screen.res];
758 case GS_STATE_MULTI_START_GAME :
759 y = Multi_common_msg_y[gr_screen.res];
763 gr_string((gr_screen.max_w - w)/2, y, Multi_common_notify_text);
770 int Multi_common_icons[MULTI_NUM_COMMON_ICONS];
772 const char *Multi_common_icon_names[MULTI_NUM_COMMON_ICONS] = {
773 "DotRed", // voice denied
774 "DotGreen", // voice recording
775 "OvalGreen", // team 0
776 "OvalGreen01", // team 0 select
778 "OvalRed01", // team 1 select
779 "mp_coop", // coop mission
780 "mp_teams", // TvT mission
781 "mp_furball", // furball mission
782 "icon-volition", // volition mission
783 "icon-valid", // mission is valid
788 "icon-silent" // SilentThreat
792 // width and height of the icons
793 int Multi_common_icon_dims[MULTI_NUM_COMMON_ICONS][2] = {
794 {11, 11}, // voice denied
795 {11, 11}, // voice recording
797 {11, 11}, // team 0 select
799 {11, 11}, // team 1 select
802 {18, 11}, // mp furball
803 {9, 9}, // volition mission
804 {8, 8}, // mission is valid
809 {16, 7} // silent threat
813 void multi_load_common_icons()
818 for(idx=0; idx<MULTI_NUM_COMMON_ICONS; idx++){
819 Multi_common_icons[idx] = -1;
820 Multi_common_icons[idx] = bm_load(Multi_common_icon_names[idx]);
824 void multi_unload_common_icons()
829 for(idx=0; idx<MULTI_NUM_COMMON_ICONS; idx++){
830 if(Multi_common_icons[idx] != -1){
831 bm_unload(Multi_common_icons[idx]);
832 Multi_common_icons[idx] = -1;
837 // display any relevant voice status icons
838 void multi_common_voice_display_status()
840 switch(multi_voice_status()){
841 // i have been denied the voice token
842 case MULTI_VOICE_STATUS_DENIED:
843 if(Multi_common_icons[MICON_VOICE_DENIED] != -1){
844 gr_set_bitmap(Multi_common_icons[MICON_VOICE_DENIED], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
849 // i am currently recording
850 case MULTI_VOICE_STATUS_RECORDING:
851 if(Multi_common_icons[MICON_VOICE_RECORDING] != -1){
852 gr_set_bitmap(Multi_common_icons[MICON_VOICE_RECORDING], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
857 // i am currently playing back sound
858 case MULTI_VOICE_STATUS_PLAYING:
861 // the system is currently idle
862 case MULTI_VOICE_STATUS_IDLE:
868 // palette initialization stuff
869 #define MULTI_COMMON_PALETTE_FNAME "InterfacePalette"
872 int Multi_common_interface_palette = -1;
874 void multi_common_load_palette();
875 void multi_common_set_palette();
876 void multi_common_unload_palette();
878 // load in the palette if it doesn't already exist
879 void multi_common_load_palette()
881 if(Multi_common_interface_palette != -1){
885 Multi_common_interface_palette = bm_load(MULTI_COMMON_PALETTE_FNAME);
886 if(Multi_common_interface_palette == -1){
887 nprintf(("Network","Error loading multiplayer common palette!\n"));
891 // set the common palette to be the active one
892 void multi_common_set_palette()
894 // if the palette is not loaded yet, do so now
895 if(Multi_common_interface_palette == -1){
896 multi_common_load_palette();
900 // unload the bitmap palette
901 void multi_common_unload_palette()
903 if(Multi_common_interface_palette != -1){
904 bm_unload(Multi_common_interface_palette);
905 Multi_common_interface_palette = -1;
909 void multi_common_verify_cd()
915 // -------------------------------------------------------------------------------------------------------------
917 // MULTIPLAYER JOIN SCREEN
920 #define MULTI_JOIN_NUM_BUTTONS 11
924 #define MULTI_JOIN_PALETTE "InterfacePalette"
926 static const char *Multi_join_bitmap_fname[GR_NUM_RESOLUTIONS] = {
927 "MultiJoin", // GR_640
928 "2_MultiJoin" // GR_1024
931 static const char *Multi_join_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
932 "MultiJoin-M", // GR_640
933 "2_MultiJoin-M" // GR_1024
939 const char *Mj_slider_name[GR_NUM_RESOLUTIONS] = {
943 int Mj_slider_coords[GR_NUM_RESOLUTIONS][4] = {
954 #define MJ_SCROLL_UP 0
955 #define MJ_SCROLL_DOWN 1
957 #define MJ_SCROLL_INFO_UP 3
958 #define MJ_SCROLL_INFO_DOWN 4
959 #define MJ_JOIN_OBSERVER 5
960 #define MJ_START_GAME 6
966 // uses MULTI_JOIN_REFRESH_TIME as its timestamp
967 int Multi_join_glr_stamp;
969 #define MULTI_JOIN_PING_TIME 15000 // how often we ping all the known servers
970 int Multi_join_ping_stamp;
971 UI_WINDOW Multi_join_window; // the window object for the join screen
972 UI_BUTTON Multi_join_select_button; // for selecting list items
974 UI_SLIDER2 Multi_join_slider; // handy dandy slider
976 int Multi_join_bitmap; // the background bitmap
978 ui_button_info Multi_join_buttons[GR_NUM_RESOLUTIONS][MULTI_JOIN_NUM_BUTTONS] = {
981 ui_button_info("MJ_00", 0, 85, -1, -1, 0),
982 ui_button_info("MJ_01", 0, 125, -1, -1, 1),
983 ui_button_info("MJ_03", 20, 324, -1, -1, 3),
984 ui_button_info("MJ_04", 0, 399, -1, -1, 4),
985 ui_button_info("MJ_05", 0, 436, -1, -1, 5),
986 ui_button_info("MJ_15", 450, 323, -1, -1, 15),
987 ui_button_info("MJ_10", 519, 323, -1, -1, 10),
988 ui_button_info("MJ_06", 574, 323, -1, -1, 6),
989 ui_button_info("MJ_08", 470, 427, -1, -1, 8),
990 ui_button_info("MJ_09", 448, 454, -1, -1, 9),
991 ui_button_info("MJ_07", 563, 411, -1, -1, 7),
993 ui_button_info( "MJ_00", 1, 57, -1, -1, 0 ), // scroll up
994 ui_button_info( "MJ_02", 1, 297, -1, -1, 2 ), // scroll down
995 ui_button_info( "MJ_03", 10, 338, 65, 364, 3 ), // refresh
996 ui_button_info( "MJ_04", 1, 405, -1, -1, 4 ), // scroll info up
997 ui_button_info( "MJ_05", 1, 446, -1, -1, 5 ), // scroll info down
998 ui_button_info( "MJ_06", 489, 339, -1, -1, 6 ), // join as observer
999 ui_button_info( "MJ_07", 538, 339, -1, -1, 7 ), // create game
1000 ui_button_info( "MJ_08", 583, 339, 588, 376, 8 ), // cancel
1001 ui_button_info( "MJ_09", 534, 426, -1, -1, 9 ), // help
1002 ui_button_info( "MJ_10", 534, 454, -1, -1, 10 ), // options
1003 ui_button_info( "MJ_11", 571, 426, 589, 416, 11 ), // join
1007 ui_button_info( "2_MJ_00", 2, 92, -1, -1, 0 ), // scroll up
1008 ui_button_info( "2_MJ_02", 2, 475, -1, -1, 2 ), // scroll down
1009 ui_button_info( "2_MJ_03", 16, 541, 104, 582, 3 ), // refresh
1010 ui_button_info( "2_MJ_04", 2, 648, -1, -1, 4 ), // scroll info up
1011 ui_button_info( "2_MJ_05", 2, 713, -1, -1, 5 ), // scroll info down
1012 ui_button_info( "2_MJ_06", 783, 542, -1, -1, 6 ), // join as observer
1013 ui_button_info( "2_MJ_07", 861, 542, -1, -1, 7 ), // create game
1014 ui_button_info( "2_MJ_08", 933, 542, 588, 376, 8 ), // cancel
1015 ui_button_info( "2_MJ_09", 854, 681, -1, -1, 9 ), // help
1016 ui_button_info( "2_MJ_10", 854, 727, -1, -1, 10 ), // options
1017 ui_button_info( "2_MJ_11", 914, 681, 937, 668, 11 ), // join
1022 #define MULTI_JOIN_NUM_TEXT 13
1024 UI_XSTR Multi_join_text[GR_NUM_RESOLUTIONS][MULTI_JOIN_NUM_TEXT] = {
1026 {"Refresh", 1299, 65, 364, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_REFRESH].button},
1027 {"Join as", 1300, 476, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_JOIN_OBSERVER].button},
1028 {"Observer", 1301, 467, 385, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_JOIN_OBSERVER].button},
1029 {"Create", 1408, 535, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_START_GAME].button},
1030 {"Game", 1302, 541, 385, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_START_GAME].button},
1031 {"Cancel", 387, 588, 376, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[0][MJ_CANCEL].button},
1032 {"Help", 928, 479, 436, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_HELP].button},
1033 {"Options", 1036, 479, 460, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[0][MJ_OPTIONS].button},
1034 {"Join", 1303, 589, 416, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[0][MJ_ACCEPT].button},
1035 {"Status", 1304, 37, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
1036 {"Server", 1305, 116, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
1037 {"Players", 1306, 471, 37, UI_XSTR_COLOR_GREEN, -1, NULL},
1038 {"Ping", 1307, 555, 37, UI_XSTR_COLOR_GREEN, -1, NULL}
1041 {"Refresh", 1299, 104, 582, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_REFRESH].button},
1042 {"Join as", 1300, 783, 602, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_JOIN_OBSERVER].button},
1043 {"Observer", 1301, 774, 611, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_JOIN_OBSERVER].button},
1044 {"Create", 1408, 868, 602, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_START_GAME].button},
1045 {"Game", 1302, 872, 611, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_START_GAME].button},
1046 {"Cancel", 387, 941, 602, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[1][MJ_CANCEL].button},
1047 {"Help", 928, 782, 699, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_HELP].button},
1048 {"Options", 1036, 782, 736, UI_XSTR_COLOR_GREEN, -1, &Multi_join_buttons[1][MJ_OPTIONS].button},
1049 {"Join", 1303, 937, 668, UI_XSTR_COLOR_PINK, -1, &Multi_join_buttons[1][MJ_ACCEPT].button},
1050 {"Status", 1304, 60, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
1051 {"Server", 1305, 186, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
1052 {"Players", 1306, 753, 60, UI_XSTR_COLOR_GREEN, -1, NULL},
1053 {"Ping", 1307, 888, 60, UI_XSTR_COLOR_GREEN, -1, NULL}
1058 // constants for coordinate look ups
1059 #define MJ_X_COORD 0
1060 #define MJ_Y_COORD 1
1061 #define MJ_W_COORD 2
1062 #define MJ_H_COORD 3
1064 #define MULTI_JOIN_SENT_WAIT 10000 // wait this long since a join was sent to allow another
1065 int Multi_join_sent_stamp;
1067 // game information text areas
1068 int Mj_max_game_items[GR_NUM_RESOLUTIONS] = {
1073 int Mj_list_y[GR_NUM_RESOLUTIONS] = {
1078 int Mj_status_coords[GR_NUM_RESOLUTIONS][4] = {
1087 int Mj_game_icon_coords[GR_NUM_RESOLUTIONS][3] = {
1096 int Mj_speed_coords[GR_NUM_RESOLUTIONS][4] = {
1105 int Mj_game_name_coords[GR_NUM_RESOLUTIONS][4] = {
1114 int Mj_players_coords[GR_NUM_RESOLUTIONS][4] = {
1123 int Mj_ping_coords[GR_NUM_RESOLUTIONS][4] = {
1132 // game speed labels
1133 #define MJ_NUM_SPEED_LABELS 5
1134 const char *Multi_join_speed_labels[MJ_NUM_SPEED_LABELS] = {
1141 color *Multi_join_speed_colors[MJ_NUM_SPEED_LABELS] = {
1144 &Color_bright_green,
1145 &Color_bright_green,
1149 int Mj_cd_coords[GR_NUM_RESOLUTIONS] = {
1154 // extents of the entire boundable game info region
1155 // NOTE : these numbers are completely empirical
1156 #define MJ_PING_GREEN 160
1157 #define MJ_PING_YELLOW 300
1159 int Mj_list_area_coords[GR_NUM_RESOLUTIONS][4] = {
1168 // PXO channel filter
1169 #define MJ_PXO_FILTER_Y 0
1171 // special chars to indicate various status modes for servers
1172 #define MJ_CHAR_STANDALONE "*"
1173 #define MJ_CHAR_CAMPAIGN "c"
1176 // various interface indices
1177 int Multi_join_list_start; // where to start displaying from
1178 active_game *Multi_join_list_start_item; // a pointer to the corresponding active_game
1179 int Multi_join_list_selected; // which item we have selected
1180 active_game *Multi_join_selected_item; // a pointer to the corresponding active_game
1182 // use this macro to modify the list start
1183 #define MJ_LIST_START_INC() do { Multi_join_list_start++; } while(0);
1184 #define MJ_LIST_START_DEC() do { Multi_join_list_start--; } while(0);
1185 #define MJ_LIST_START_SET(vl) do { Multi_join_list_start = vl; } while(0);
1187 // if we should be sending a join request at the end of the frame
1188 int Multi_join_should_send = -1;
1190 // master tracker details
1191 int Multi_join_frame_count; // keep a count of frames displayed
1192 int Multi_join_mt_tried_verify; // already tried verifying the pilot with the tracker
1194 // data stuff for auto joining a game
1195 #define MULTI_AUTOJOIN_JOIN_STAMP 2000
1196 #define MULTI_AUTOJOIN_QUERY_STAMP 2000
1198 int Multi_did_autojoin;
1199 net_addr Multi_autojoin_addr;
1200 int Multi_autojoin_join_stamp;
1201 int Multi_autojoin_query_stamp;
1204 join_request Multi_join_request;
1206 // LOCAL function definitions
1207 void multi_join_check_buttons();
1208 void multi_join_button_pressed(int n);
1209 void multi_join_display_games();
1210 void multi_join_blit_game_status(active_game *game, int y);
1211 void multi_join_load_tcp_addrs();
1212 void multi_join_do_netstuff();
1213 void multi_join_ping_all();
1214 void multi_join_process_select();
1215 void multi_join_list_scroll_up();
1216 void multi_join_list_scroll_down();
1217 void multi_join_list_page_up();
1218 void multi_join_list_page_down();
1219 active_game *multi_join_get_game(int n);
1220 void multi_join_cull_timeouts();
1221 void multi_join_handle_item_cull(active_game *item, int item_index);
1222 void multi_join_send_join_request(int as_observer);
1223 void multi_join_create_game();
1224 void multi_join_blit_top_stuff();
1225 int multi_join_maybe_warn();
1226 int multi_join_warn_pxo();
1227 void multi_join_blit_protocol();
1231 active_game ag, *newitem;;
1234 dc_get_arg(ARG_INT);
1235 for(idx=0; idx<Dc_arg_int; idx++){
1236 // stuff some fake info
1237 memset(&ag, 0, sizeof(active_game));
1238 SDL_snprintf(ag.name, sizeof(ag.name), "Game %d", idx);
1239 ag.version = MULTI_FS_SERVER_VERSION;
1240 ag.comp_version = MULTI_FS_SERVER_VERSION;
1241 ag.server_addr.addr[0] = (char)idx;
1242 ag.flags = (AG_FLAG_COOP | AG_FLAG_FORMING | AG_FLAG_STANDALONE);
1245 newitem = multi_update_active_games(&ag);
1247 // timestamp it so we get random timeouts
1248 if(newitem != NULL){
1249 // newitem->heard_from_timer = timestamp((int)frand_range(500.0f, 10000.0f));
1254 void multi_join_notify_new_game()
1257 // reset the # of items
1258 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);
1259 Multi_join_slider.force_currentItem(Multi_join_list_start);
1263 int multi_join_autojoin_do()
1265 // if we have an active game on the list, then return a positive value so that we
1266 // can join the game
1267 if ( Active_game_head && (Active_game_count > 0) ) {
1268 Multi_join_selected_item = Active_game_head;
1272 // send out a server_query again
1273 if ( timestamp_elapsed(Multi_autojoin_query_stamp) ) {
1274 send_server_query(&Multi_autojoin_addr);
1275 Multi_autojoin_query_stamp = timestamp(MULTI_AUTOJOIN_QUERY_STAMP);
1281 void multi_join_game_init()
1285 // do the multiplayer init stuff - multi_level_init() now does all net_player zeroing.
1286 // setup various multiplayer things
1287 SDL_assert( Game_mode & GM_MULTIPLAYER );
1288 SDL_assert( Net_player != NULL );
1290 switch (Multi_options_g.protocol) {
1292 ADDRESS_LENGTH = IPX_ADDRESS_LENGTH;
1293 PORT_LENGTH = IPX_PORT_LENGTH;
1297 ADDRESS_LENGTH = IP_ADDRESS_LENGTH;
1298 PORT_LENGTH = IP_PORT_LENGTH;
1307 memset( &Netgame, 0, sizeof(Netgame) );
1310 Net_player->flags |= NETINFO_FLAG_DO_NETWORKING;
1311 Net_player->player = Player;
1312 memcpy(&Net_player->p_info.addr,&Psnet_my_addr,sizeof(net_addr));
1314 // check for the existence of a CD
1315 multi_common_verify_cd();
1317 // load my local netplayer options
1318 multi_options_local_load(&Net_player->p_info.options, Net_player);
1324 common_set_interface_palette(MULTI_JOIN_PALETTE);
1327 // destroy any chatbox contents which previously existed (from another game)
1330 // create the interface window
1331 Multi_join_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
1332 Multi_join_window.set_mask_bmap(Multi_join_bitmap_mask_fname[gr_screen.res]);
1334 // load the background bitmap
1335 Multi_join_bitmap = bm_load(Multi_join_bitmap_fname[gr_screen.res]);
1336 if(Multi_join_bitmap < 0){
1337 // we failed to load the bitmap - this is very bad
1341 // intialize the endgame system
1342 multi_endgame_init();
1344 // initialize the common notification messaging
1345 multi_common_notify_init();
1347 // initialize the common text area
1348 multi_common_set_text("");
1350 // load and use the common interface palette
1351 multi_common_load_palette();
1352 multi_common_set_palette();
1354 // load the help overlay
1355 help_overlay_load(MULTI_JOIN_OVERLAY);
1356 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1358 // do TCP and VMT specific initialization
1359 if(Multi_options_g.protocol == NET_TCP){
1360 // if this is a TCP (non tracker) game, we'll load up our default address list right now
1361 multi_join_load_tcp_addrs();
1364 // initialize any and all timestamps
1365 Multi_join_glr_stamp = -1;
1366 Multi_join_ping_stamp = -1;
1367 Multi_join_sent_stamp = -1;
1369 // reset frame count
1370 Multi_join_frame_count = 0;
1372 // haven't tried to verify on the tracker yet.
1373 Multi_join_mt_tried_verify = 0;
1375 // clear our all game lists to save hassles
1376 multi_join_clear_game_list();
1378 // create the interface buttons
1379 for(idx=0; idx<MULTI_JOIN_NUM_BUTTONS; idx++){
1380 // create the object
1381 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);
1383 // set the sound to play when highlighted
1384 Multi_join_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
1386 // set the ani for the button
1387 Multi_join_buttons[gr_screen.res][idx].button.set_bmaps(Multi_join_buttons[gr_screen.res][idx].filename);
1390 Multi_join_buttons[gr_screen.res][idx].button.link_hotspot(Multi_join_buttons[gr_screen.res][idx].hotspot);
1395 for(idx=0; idx<MULTI_JOIN_NUM_TEXT; idx++){
1396 Multi_join_window.add_XSTR(&Multi_join_text[gr_screen.res][idx]);
1400 Multi_join_should_send = -1;
1402 // close any previously open chatbox
1405 // create the list item select button
1406 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);
1407 Multi_join_select_button.hide();
1411 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);
1414 // if starting a network game, then go to the create game screen
1415 if ( Cmdline_start_netgame ) {
1416 multi_join_create_game();
1417 } else if ( Cmdline_connect_addr != NULL ) {
1422 // joining a game. Send a join request to the given IP address, and wait for the return.
1423 memset( &Multi_autojoin_addr, 0, sizeof(net_addr) );
1424 Multi_autojoin_addr.type = NET_TCP;
1426 // create the address, looking out for port number at the end
1427 port_num = DEFAULT_GAME_PORT;
1428 p = strrchr(Cmdline_connect_addr, ':');
1432 port_num = (short)atoi(p);
1434 ip_addr = inet_addr(Cmdline_connect_addr);
1435 memcpy(Multi_autojoin_addr.addr, &ip_addr, 4);
1436 Multi_autojoin_addr.port = port_num;
1438 send_server_query(&Multi_autojoin_addr);
1439 Multi_autojoin_query_stamp = timestamp(MULTI_AUTOJOIN_QUERY_STAMP);
1440 Multi_did_autojoin = 0;
1444 void multi_join_clear_game_list()
1447 Multi_join_list_selected = -1;
1448 Multi_join_selected_item = NULL;
1449 MJ_LIST_START_SET(-1);
1450 Multi_join_list_start_item = NULL;
1452 // free up the active game list
1453 multi_free_active_games();
1455 // initialize the active game list
1456 Active_game_head = NULL;
1457 Active_game_count = 0;
1460 void multi_join_game_do_frame()
1462 // check the status of our reliable socket. If not valid, popup error and return to main menu
1463 // I put this code here to avoid nasty gameseq issues with states. Also, we will have nice
1464 // background for the popup
1465 if ( !psnet_rel_check() ) {
1466 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));
1467 gameseq_post_event(GS_EVENT_MAIN_MENU);
1471 // return here since we will be moving to the next stage anyway -- I don't want to see the backgrounds of
1472 // all the screens for < 1 second for every screen we automatically move to.
1473 if ( Cmdline_start_netgame ) {
1477 // when joining a network game, wait for the server query to come back, and then join the game
1478 if ( Cmdline_connect_addr != NULL ) {
1481 if ( !Multi_did_autojoin ) {
1482 rval = popup_till_condition(multi_join_autojoin_do, XSTR("&Cancel", 779), XSTR("Joining netgame", 1500) );
1484 // cancel was hit. Send the user back to the main hall
1485 gameseq_post_event(GS_EVENT_MAIN_MENU);
1486 Cmdline_connect_addr = NULL; // reset this value.
1489 // when we get here, we have the data -- join the game.
1490 multi_join_send_join_request(0);
1491 Multi_autojoin_join_stamp = timestamp(MULTI_AUTOJOIN_JOIN_STAMP);
1492 Multi_did_autojoin = 1;
1495 if ( timestamp_elapsed(Multi_autojoin_join_stamp) ) {
1496 multi_join_send_join_request(0);
1497 Multi_autojoin_join_stamp = timestamp(MULTI_AUTOJOIN_JOIN_STAMP);
1503 // reset the should send var
1504 Multi_join_should_send = -1;
1506 int k = Multi_join_window.process();
1508 // process any keypresses
1511 if(help_overlay_active(MULTI_JOIN_OVERLAY)){
1512 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1514 gameseq_post_event(GS_EVENT_MAIN_MENU);
1515 gamesnd_play_iface(SND_USER_SELECT);
1519 // page up the game list
1521 multi_join_list_page_up();
1523 Multi_join_slider.force_currentItem(Multi_join_list_start);
1528 multi_pinfo_popup(Net_player);
1531 // page down the game list
1533 multi_join_list_page_down();
1535 Multi_join_slider.force_currentItem(Multi_join_list_start);
1539 // send out a ping-all
1541 multi_join_ping_all();
1542 Multi_join_ping_stamp = timestamp(MULTI_JOIN_PING_TIME);
1545 // shortcut to start a game
1547 multi_join_create_game();
1550 // scroll the game list up
1552 multi_join_list_scroll_up();
1554 Multi_join_slider.force_currentItem(Multi_join_list_start);
1558 // scroll the game list down
1560 multi_join_list_scroll_down();
1562 Multi_join_slider.force_currentItem(Multi_join_list_start);
1567 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
1568 help_overlay_set_state(MULTI_JOIN_OVERLAY, 0);
1571 // do any network related stuff
1572 multi_join_do_netstuff();
1574 // process any button clicks
1575 multi_join_check_buttons();
1577 // process any list selection stuff
1578 multi_join_process_select();
1580 // draw the background, etc
1582 GR_MAYBE_CLEAR_RES(Multi_join_bitmap);
1583 if(Multi_join_bitmap != -1){
1584 gr_set_bitmap(Multi_join_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1587 Multi_join_window.draw();
1589 // display the active games
1590 multi_join_display_games();
1592 // display any text in the info area
1593 multi_common_render_text();
1595 // display any pending notification messages
1596 multi_common_notify_do();
1598 // blit the CD icon and any PXO filter stuff
1599 multi_join_blit_top_stuff();
1601 // draw the help overlay
1602 help_overlay_maybe_blit(MULTI_JOIN_OVERLAY);
1607 // if we are supposed to be sending a join request
1608 if(Multi_join_should_send != -1){
1609 multi_join_send_join_request(Multi_join_should_send);
1611 Multi_join_should_send = -1;
1613 // increment the frame count
1614 Multi_join_frame_count++;
1617 void multi_join_game_close()
1619 // unload any bitmaps
1620 if(!bm_unload(Multi_join_bitmap)){
1621 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_join_bitmap_fname[gr_screen.res]));
1624 // unload the help overlay
1625 help_overlay_unload(MULTI_JOIN_OVERLAY);
1627 // free up the active game list
1628 multi_free_active_games();
1630 // destroy the UI_WINDOW
1631 Multi_join_window.destroy();
1634 common_free_interface_palette();
1638 void multi_join_check_buttons()
1641 for(idx=0;idx<MULTI_JOIN_NUM_BUTTONS;idx++){
1642 // we only really need to check for one button pressed at a time, so we can break after
1644 if(Multi_join_buttons[gr_screen.res][idx].button.pressed()){
1645 multi_join_button_pressed(idx);
1651 void multi_join_button_pressed(int n)
1655 // if we're player PXO, go back there
1656 gameseq_post_event(GS_EVENT_MAIN_MENU);
1657 gamesnd_play_iface(SND_USER_SELECT);
1660 if(Active_game_count <= 0){
1661 multi_common_add_notify(XSTR("No games found!",757));
1662 gamesnd_play_iface(SND_GENERAL_FAIL);
1663 } else if(Multi_join_list_selected == -1){
1664 multi_common_add_notify(XSTR("No game selected!",758));
1665 gamesnd_play_iface(SND_GENERAL_FAIL);
1666 } else if((Multi_join_sent_stamp != -1) && !timestamp_elapsed(Multi_join_sent_stamp)){
1667 multi_common_add_notify(XSTR("Still waiting on previous join request!",759));
1668 gamesnd_play_iface(SND_GENERAL_FAIL);
1670 // otherwise, if he's already played PXO games, warn him
1672 if(Player->flags & PLAYER_FLAGS_HAS_PLAYED_PXO){
1673 if(!multi_join_warn_pxo()){
1679 // send the join request here
1680 SDL_assert(Multi_join_selected_item != NULL);
1682 // send a join request packet
1683 Multi_join_should_send = 0;
1685 gamesnd_play_iface(SND_COMMIT_PRESSED);
1691 if(!help_overlay_active(MULTI_JOIN_OVERLAY)){
1692 help_overlay_set_state(MULTI_JOIN_OVERLAY,1);
1694 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1698 // scroll the game list up
1700 multi_join_list_scroll_up();
1702 Multi_join_slider.force_currentItem(Multi_join_list_start);
1706 // scroll the game list down
1707 case MJ_SCROLL_DOWN:
1708 multi_join_list_scroll_down();
1710 Multi_join_slider.force_currentItem(Multi_join_list_start);
1714 // scroll the info text box up
1715 case MJ_SCROLL_INFO_UP:
1716 multi_common_scroll_text_up();
1719 // scroll the info text box down
1720 case MJ_SCROLL_INFO_DOWN:
1721 multi_common_scroll_text_down();
1724 // go to the options screen
1726 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
1729 // go to the start game screen
1731 multi_join_create_game();
1734 // refresh the game/server list
1736 gamesnd_play_iface(SND_USER_SELECT);
1737 broadcast_game_query();
1740 // join a game as an observer
1741 case MJ_JOIN_OBSERVER:
1742 if(Active_game_count <= 0){
1743 multi_common_add_notify(XSTR("No games found!",757));
1744 gamesnd_play_iface(SND_GENERAL_FAIL);
1745 } else if(Multi_join_list_selected == -1){
1746 multi_common_add_notify(XSTR("No game selected!",758));
1747 gamesnd_play_iface(SND_GENERAL_FAIL);
1748 } else if((Multi_join_sent_stamp != -1) && !timestamp_elapsed(Multi_join_sent_stamp)){
1749 multi_common_add_notify(XSTR("Still waiting on previous join request!",759));
1750 gamesnd_play_iface(SND_GENERAL_FAIL);
1752 // send the join request here
1753 SDL_assert(Multi_join_selected_item != NULL);
1755 Multi_join_should_send = 1;
1757 gamesnd_play_iface(SND_COMMIT_PRESSED);
1762 multi_common_add_notify(XSTR("Not implemented yet!",760));
1763 gamesnd_play_iface(SND_GENERAL_FAIL);
1768 // display all relevant info for active games
1769 void multi_join_display_games()
1771 active_game *moveup = Multi_join_list_start_item;
1775 int y_start = Mj_list_y[gr_screen.res];
1780 // blit the game status (including text and type icon)
1781 multi_join_blit_game_status(moveup,y_start);
1783 // get the connection type
1784 con_type = (moveup->flags & AG_FLAG_CONNECTION_SPEED_MASK) >> AG_FLAG_CONNECTION_BIT;
1785 if((con_type > 4) || (con_type < 0)){
1789 // display the connection speed
1791 SDL_strlcpy(str, Multi_join_speed_labels[con_type], sizeof(str));
1792 gr_set_color_fast(Multi_join_speed_colors[con_type]);
1793 gr_string(Mj_speed_coords[gr_screen.res][MJ_X_COORD], y_start, str);
1795 // we'll want to have different colors for highlighted items, etc.
1796 if(moveup == Multi_join_selected_item){
1797 gr_set_color_fast(&Color_text_selected);
1799 gr_set_color_fast(&Color_text_normal);
1802 // display the game name, adding appropriate status chars
1804 if(moveup->flags & AG_FLAG_STANDALONE){
1805 SDL_strlcat(str, MJ_CHAR_STANDALONE, sizeof(str));
1807 if(moveup->flags & AG_FLAG_CAMPAIGN){
1808 SDL_strlcat(str, MJ_CHAR_CAMPAIGN, sizeof(str));
1811 // tack on the actual server name
1812 SDL_strlcat(str, " ", sizeof(str));
1813 SDL_strlcat(str, moveup->name, sizeof(str));
1814 if(strlen(moveup->mission_name) > 0){
1815 SDL_strlcat(str, " / ", sizeof(str));
1816 SDL_strlcat(str, moveup->mission_name, sizeof(str));
1819 // make sure the string fits in the display area and draw it
1820 gr_force_fit_string(str,200,Mj_game_name_coords[gr_screen.res][MJ_W_COORD]);
1821 gr_string(Mj_game_name_coords[gr_screen.res][MJ_X_COORD],y_start,str);
1823 // display the ping time
1824 if(moveup->ping.ping_avg > 0){
1825 if(moveup->ping.ping_avg > 1000){
1826 gr_set_color_fast(&Color_bright_red);
1827 SDL_strlcpy(str, XSTR("> 1 sec",761), sizeof(str));
1829 // set the appropriate ping time color indicator
1830 if(moveup->ping.ping_avg > MJ_PING_YELLOW){
1831 gr_set_color_fast(&Color_bright_red);
1832 } else if(moveup->ping.ping_avg > MJ_PING_YELLOW){
1833 gr_set_color_fast(&Color_bright_yellow);
1835 gr_set_color_fast(&Color_bright_green);
1838 SDL_snprintf(str, sizeof(str), "%d%s", moveup->ping.ping_avg, XSTR(" ms",762));
1841 gr_string(Mj_ping_coords[gr_screen.res][MJ_X_COORD],y_start,str);
1844 // display the number of players (be sure to center it)
1845 if(moveup == Multi_join_selected_item){
1846 gr_set_color_fast(&Color_text_selected);
1848 gr_set_color_fast(&Color_text_normal);
1850 SDL_snprintf(str, sizeof(str), "%d", moveup->num_players);
1851 gr_get_string_size(&w,&h,str);
1852 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);
1856 moveup = moveup->next;
1857 } while((moveup != Active_game_head) && (count < Mj_max_game_items[gr_screen.res]));
1859 // if there are no items on the list, display this info
1861 gr_set_color_fast(&Color_bright);
1862 gr_string(Mj_game_name_coords[gr_screen.res][MJ_X_COORD] - 30,y_start,XSTR("<No game servers found>",763));
1866 void multi_join_blit_game_status(active_game *game, int y)
1869 char status_text[25];
1871 // blit the proper icon
1873 switch( game->flags & AG_FLAG_TYPE_MASK ){
1876 if(Multi_common_icons[MICON_COOP] != -1){
1877 gr_set_bitmap(Multi_common_icons[MICON_COOP], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1882 // team vs. team game
1884 if(Multi_common_icons[MICON_TVT] != -1){
1885 gr_set_bitmap(Multi_common_icons[MICON_TVT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1892 case AG_FLAG_DOGFIGHT:
1893 if(Multi_common_icons[MICON_DOGFIGHT] != -1){
1894 gr_set_bitmap(Multi_common_icons[MICON_DOGFIGHT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1900 // if we're supposed to draw a bitmap
1902 gr_bitmap(Mj_game_icon_coords[gr_screen.res][MJ_X_COORD],y-1);
1905 // blit the proper status text
1906 memset(status_text,0,25);
1908 switch( game->flags & AG_FLAG_STATE_MASK ){
1909 case AG_FLAG_FORMING:
1910 gr_set_color_fast(&Color_bright_green);
1911 SDL_strlcpy(status_text, XSTR("Forming", 764), sizeof(status_text));
1913 case AG_FLAG_BRIEFING:
1914 gr_set_color_fast(&Color_bright_red);
1915 SDL_strlcpy(status_text, XSTR("Briefing", 765), sizeof(status_text));
1917 case AG_FLAG_DEBRIEF:
1918 gr_set_color_fast(&Color_bright_red);
1919 SDL_strlcpy(status_text, XSTR("Debrief", 766), sizeof(status_text));
1922 gr_set_color_fast(&Color_bright_red);
1923 SDL_strlcpy(status_text, XSTR("Paused", 767), sizeof(status_text));
1925 case AG_FLAG_IN_MISSION:
1926 gr_set_color_fast(&Color_bright_red);
1927 SDL_strlcpy(status_text, XSTR("Playing", 768), sizeof(status_text));
1930 gr_set_color_fast(&Color_bright);
1931 SDL_strlcpy(status_text, XSTR("Unknown", 769), sizeof(status_text));
1934 gr_get_string_size(&str_w,NULL,status_text);
1935 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);
1938 // load in a list of active games from our tcp.cfg file
1939 void multi_join_load_tcp_addrs()
1941 char line[MAX_IP_STRING];
1946 // attempt to open the ip list file
1947 file = cfopen(IP_CONFIG_FNAME,"rt",CFILE_NORMAL,CF_TYPE_DATA);
1949 nprintf(("Network","Error loading tcp.cfg file!\n"));
1953 // free up any existing server list
1954 multi_free_server_list();
1956 // read in all the strings in the file
1957 while(!cfeof(file)){
1959 cfgets(line,MAX_IP_STRING,file);
1961 // strip off any newline character
1962 if(line[strlen(line) - 1] == '\n'){
1963 line[strlen(line) - 1] = '\0';
1966 // empty lines don't get processed
1967 if( (line[0] == '\0') || (line[0] == '\n') ){
1971 if ( !psnet_is_valid_ip_string(line) ) {
1972 nprintf(("Network","Invalid ip string (%s)\n",line));
1974 // copy the server ip address
1975 memset(&addr,0,sizeof(net_addr));
1976 addr.type = NET_TCP;
1977 psnet_string_to_addr(&addr, line, sizeof(line));
1978 if ( addr.port == 0 ){
1979 addr.port = DEFAULT_GAME_PORT;
1982 // create a new server item on the list
1983 item = multi_new_server_item();
1985 memcpy(&item->server_addr,&addr,sizeof(net_addr));
1993 // do stuff like pinging servers, sending out requests, etc
1994 void multi_join_do_netstuff()
1996 // handle game query stuff
1997 if(Multi_join_glr_stamp == -1){
1998 broadcast_game_query();
2000 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
2001 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME_LOCAL);
2003 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME);
2006 // otherwise send out game query and restamp
2007 else if(timestamp_elapsed(Multi_join_glr_stamp)){
2008 broadcast_game_query();
2010 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
2011 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME_LOCAL);
2013 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME);
2017 // check to see if we've been accepted. If so, put up message saying so
2018 if ( Net_player->flags & (NETINFO_FLAG_ACCEPT_INGAME|NETINFO_FLAG_ACCEPT_CLIENT|NETINFO_FLAG_ACCEPT_HOST|NETINFO_FLAG_ACCEPT_OBSERVER) ) {
2019 multi_common_add_notify(XSTR("Accepted. Waiting for player data.",770));
2022 // check to see if any join packets we have sent have timed out
2023 if((Multi_join_sent_stamp != -1) && (timestamp_elapsed(Multi_join_sent_stamp))){
2024 Multi_join_sent_stamp = -1;
2025 multi_common_add_notify(XSTR("Join request timed out!",771));
2028 // check to see if we should be pinging everyone
2029 if((Multi_join_ping_stamp == -1) || (timestamp_elapsed(Multi_join_ping_stamp))){
2030 multi_join_ping_all();
2031 Multi_join_ping_stamp = timestamp(MULTI_JOIN_PING_TIME);
2035 multi_join_cull_timeouts();
2038 // evaluate a returned pong.
2039 void multi_join_eval_pong(net_addr *addr, fix pong_time)
2042 active_game *moveup = Active_game_head;
2047 if(psnet_same(&moveup->server_addr,addr)){
2049 multi_ping_eval_pong(&moveup->ping);
2053 moveup = moveup->next;
2055 } while(moveup != Active_game_head);
2058 // update the game's ping
2060 if(found && (moveup->ping_end > moveup->ping_start)){
2061 moveup->ping_time = f2fl(moveup->ping_end - moveup->ping_start);
2062 moveup->ping_start = -1;
2063 moveup->ping_end = -1;
2068 // ping all the server on the list
2069 void multi_join_ping_all()
2071 active_game *moveup = Active_game_head;
2076 moveup->ping_start = timer_get_fixed_seconds();
2077 moveup->ping_end = -1;
2078 send_ping(&moveup->server_addr);
2080 multi_ping_send(&moveup->server_addr,&moveup->ping);
2082 moveup = moveup->next;
2083 } while(moveup != Active_game_head);
2087 void multi_join_process_select()
2089 // if we don't have anything selected and there are items on the list - select the first one
2090 if((Multi_join_list_selected == -1) && (Active_game_count > 0)){
2091 Multi_join_list_selected = 0;
2092 Multi_join_selected_item = multi_join_get_game(0);
2093 MJ_LIST_START_SET(0);
2094 Multi_join_list_start_item = Multi_join_selected_item;
2096 // send a mission description request to this guy
2097 send_netgame_descript_packet(&Multi_join_selected_item->server_addr,0);
2098 multi_common_set_text("");
2100 // I sure hope this doesn't happen
2101 SDL_assert(Multi_join_selected_item != NULL);
2104 // otherwise see if he's clicked on an item
2105 else if(Multi_join_select_button.pressed() && (Active_game_count > 0)){
2107 Multi_join_select_button.get_mouse_pos(NULL,&y);
2109 if(item + Multi_join_list_start < Active_game_count){
2110 gamesnd_play_iface(SND_IFACE_MOUSE_CLICK);
2112 Multi_join_list_selected = item + Multi_join_list_start;
2113 Multi_join_selected_item = multi_join_get_game(Multi_join_list_selected);
2115 // I sure hope this doesn't happen
2116 SDL_assert(Multi_join_selected_item != NULL);
2118 // send a mission description request to this guy
2119 send_netgame_descript_packet(&Multi_join_selected_item->server_addr,0);
2120 multi_common_set_text("");
2124 // if he's double clicked, then select it and accept
2125 if(Multi_join_select_button.double_clicked()){
2127 Multi_join_select_button.get_mouse_pos(NULL,&y);
2129 if(item == Multi_join_list_selected){
2130 multi_join_button_pressed(MJ_ACCEPT);
2135 // return game n (0 based index)
2136 active_game *multi_join_get_game(int n)
2138 active_game *moveup = Active_game_head;
2145 moveup = moveup->next;
2146 while((moveup != Active_game_head) && (count != n)){
2147 moveup = moveup->next;
2150 if(moveup == Active_game_head){
2151 nprintf(("Network","Warning, couldn't find game item %d!\n",n));
2161 // scroll through the game list
2162 void multi_join_list_scroll_up()
2164 // if we're not at the beginning of the list, scroll up
2165 if(Multi_join_list_start_item != Active_game_head){
2166 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2168 MJ_LIST_START_DEC();
2170 gamesnd_play_iface(SND_SCROLL);
2172 gamesnd_play_iface(SND_GENERAL_FAIL);
2176 // scroll through the game list
2177 void multi_join_list_scroll_down()
2179 if((Active_game_count - Multi_join_list_start) > Mj_max_game_items[gr_screen.res]){
2180 Multi_join_list_start_item = Multi_join_list_start_item->next;
2182 MJ_LIST_START_INC();
2184 gamesnd_play_iface(SND_SCROLL);
2186 gamesnd_play_iface(SND_GENERAL_FAIL);
2190 void multi_join_list_page_up()
2192 // in this case, just set us to the beginning of the list
2193 if((Multi_join_list_start - Mj_max_game_items[gr_screen.res]) < 0){
2194 Multi_join_list_start_item = Active_game_head;
2196 MJ_LIST_START_SET(0);
2198 gamesnd_play_iface(SND_SCROLL);
2200 // otherwise page the whole thing up
2202 for(idx=0; idx<Mj_max_game_items[gr_screen.res]; idx++){
2203 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2205 MJ_LIST_START_DEC();
2207 gamesnd_play_iface(SND_SCROLL);
2211 void multi_join_list_page_down()
2215 // page the whole thing down
2216 while((count < Mj_max_game_items[gr_screen.res]) && ((Active_game_count - Multi_join_list_start) > Mj_max_game_items[gr_screen.res])){
2217 Multi_join_list_start_item = Multi_join_list_start_item->next;
2218 MJ_LIST_START_INC();
2223 gamesnd_play_iface(SND_SCROLL);
2226 void multi_join_cull_timeouts()
2228 active_game *backup;
2230 active_game *moveup = Active_game_head;
2232 // traverse through the entire list if any items exist
2236 if((moveup->heard_from_timer != -1) && (timestamp_elapsed(moveup->heard_from_timer))){
2237 Active_game_count--;
2239 // if this is the head of the list
2240 if(moveup == Active_game_head){
2241 // if this is the _only_ item on the list
2242 if(moveup->next == Active_game_head){
2243 // handle any gui details related to deleting this item
2244 multi_join_handle_item_cull(Active_game_head, count);
2246 free(Active_game_head);
2247 Active_game_head = NULL;
2250 // if there are other items on the list
2252 // handle any gui details related to deleting this item
2253 multi_join_handle_item_cull(moveup, count);
2255 Active_game_head = moveup->next;
2256 Active_game_head->prev = moveup->prev;
2257 Active_game_head->prev->next = Active_game_head;
2259 moveup = Active_game_head;
2262 // if its somewhere else on the list
2264 // handle any gui details related to deleting this item
2265 multi_join_handle_item_cull(moveup, count);
2267 // if its the last item on the list
2268 moveup->next->prev = moveup->prev;
2269 moveup->prev->next = moveup->next;
2271 // if it was the last element on the list, return
2272 if(moveup->next == Active_game_head){
2276 backup = moveup->next;
2282 moveup = moveup->next;
2285 } while(moveup != Active_game_head);
2289 // deep magic begins here.
2290 void multi_join_handle_item_cull(active_game *item, int item_index)
2292 // if this is the only item on the list, unset everything
2293 if(item->next == item){
2294 Multi_join_list_selected = -1;
2295 Multi_join_selected_item = NULL;
2298 Multi_join_slider.set_numberItems(0);
2300 MJ_LIST_START_SET(-1);
2301 Multi_join_list_start_item = NULL;
2307 // see if we should be adjusting our currently selected item
2308 if(item_index <= Multi_join_list_selected){
2309 // the selected item is the head of the list
2310 if(Multi_join_selected_item == Active_game_head){
2311 // move the pointer up since this item is about to be destroyed
2312 Multi_join_selected_item = Multi_join_selected_item->next;
2314 // if this is the item being deleted, select the previous one
2315 if(item == Multi_join_selected_item){
2317 Multi_join_selected_item = Multi_join_selected_item->prev;
2319 // decrement the selected index by 1
2320 Multi_join_list_selected--;
2322 // now we know its a previous item, so our pointer stays the same but our index goes down by one, since there will be
2323 // 1 less item on the list
2325 // decrement the selected index by 1
2326 Multi_join_list_selected--;
2331 // see if we should be adjusting out current start position
2332 if(item_index <= Multi_join_list_start){
2333 // the start position is the head of the list
2334 if(Multi_join_list_start_item == Active_game_head){
2335 // move the pointer up since this item is about to be destroyed
2336 Multi_join_list_start_item = Multi_join_list_start_item->next;
2338 // if this is the item being deleted, select the previous one
2339 if(item == Multi_join_list_start_item){
2340 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2342 // decrement the starting index by 1
2343 MJ_LIST_START_DEC();
2345 // but decrement the starting index by 1
2346 MJ_LIST_START_DEC();
2351 // maybe go back up a bit so that we always have a full page of items
2352 if(Active_game_count > Mj_max_game_items[gr_screen.res]){
2353 while((Active_game_count - Multi_join_list_start) < Mj_max_game_items[gr_screen.res]){
2354 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2355 MJ_LIST_START_DEC();
2359 // set slider location
2361 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);
2362 Multi_join_slider.force_currentItem(Multi_join_list_start);
2366 void multi_join_send_join_request(int as_observer)
2368 // don't do anything if we have no items selected
2369 if(Multi_join_selected_item == NULL){
2373 // 5/26/98 -- for team v team games, don't allow ingame joining :-(
2374 if ( (Multi_join_selected_item->flags & AG_FLAG_TEAMS) && (Multi_join_selected_item->flags & (AG_FLAG_PAUSE|AG_FLAG_IN_MISSION)) ) {
2375 popup(0, 1, POPUP_OK, XSTR("Joining ingame is currently not allowed for team vs. team games",772));
2379 memset(&Multi_join_request,0,sizeof(join_request));
2381 // if the netgame is in password mode, put up a request for the password
2382 if(Multi_join_selected_item->flags & AG_FLAG_PASSWD){
2383 if(!multi_passwd_popup(Multi_join_request.passwd, sizeof(Multi_join_request.passwd))){
2387 nprintf(("Password : %s\n",Multi_join_request.passwd));
2390 // fill out the join request struct
2391 SDL_strlcpy(Multi_join_request.callsign, Player->callsign, sizeof(Multi_join_request.callsign));
2392 if(strlen(Player->image_filename) > 0){
2393 SDL_strlcpy(Multi_join_request.image_filename, Player->image_filename, sizeof(Multi_join_request.image_filename));
2396 if(strlen(Player->squad_filename) > 0){
2397 SDL_strlcpy(Multi_join_request.squad_filename, Player->squad_filename, sizeof(Multi_join_request.squad_filename));
2401 // tracker id (if any)
2402 Multi_join_request.tracker_id = Multi_tracker_id;
2404 // player's rank (at least, what he wants you to _believe_)
2405 Multi_join_request.player_rank = (ubyte)Player->stats.rank;
2408 Multi_join_request.flags = 0;
2410 Multi_join_request.flags |= JOIN_FLAG_AS_OBSERVER;
2413 // if the player has hacked data
2414 if(game_hacked_data()){
2415 Multi_join_request.flags |= JOIN_FLAG_HAXOR;
2420 SDL_strlcpy(Multi_join_request.pxo_squad_name, Multi_tracker_squad_name, LOGIN_LEN);
2423 // version of this server
2424 Multi_join_request.version = MULTI_FS_SERVER_VERSION;
2426 // server compatible version
2427 Multi_join_request.comp_version = MULTI_FS_SERVER_COMPATIBLE_VERSION;
2429 // his local player options
2430 memcpy(&Multi_join_request.player_options,&Player->m_local_options,sizeof(multi_local_options));
2432 // set the server address for the netgame
2433 memcpy(&Netgame.server_addr,&Multi_join_selected_item->server_addr,sizeof(net_addr));
2435 // send a join request to the guy
2436 send_join_packet(&Multi_join_selected_item->server_addr,&Multi_join_request);
2439 Multi_join_sent_stamp = timestamp(MULTI_JOIN_SENT_WAIT);
2442 multi_common_add_notify(XSTR("Sending join request...",773));
2445 void multi_join_create_game()
2447 // maybe warn the player about possible crappy server conditions
2448 if(!multi_join_maybe_warn()){
2452 // make sure to flag ourself as being the master
2453 Net_player->flags |= (NETINFO_FLAG_AM_MASTER | NETINFO_FLAG_GAME_HOST);
2454 Net_player->state = NETPLAYER_STATE_HOST_SETUP;
2456 // if we're in PXO mode, mark it down in our player struct
2457 if(MULTI_IS_TRACKER_GAME){
2458 Player->flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
2459 Player->save_flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
2461 // otherwise, if he's already played PXO games, warn him
2464 if(Player->flags & PLAYER_FLAGS_HAS_PLAYED_PXO){
2465 if(!multi_join_warn_pxo()){
2472 gameseq_post_event(GS_EVENT_MULTI_START_GAME);
2473 gamesnd_play_iface(SND_USER_SELECT);
2476 void multi_join_reset_join_stamp()
2478 // unset the timestamp here so the user can immediately send another join request
2479 Multi_join_sent_stamp = -1;
2480 multi_common_add_notify("");
2483 void multi_join_blit_top_stuff()
2485 // blit the cd icon if he has one
2486 if(Multi_has_cd && (Multi_common_icons[MICON_CD] != -1)){
2489 bm_get_info(Multi_common_icons[MICON_CD], &cd_w, NULL, NULL, NULL, NULL);
2491 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
2492 gr_bitmap((gr_screen.max_w / 2) - (cd_w / 2), Mj_cd_coords[gr_screen.res]);
2496 #define CW_CODE_CANCEL 0 // cancel the action
2497 #define CW_CODE_OK 1 // continue anyway
2498 #define CW_CODE_INFO 2 // gimme some more information
2500 #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)
2501 #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)
2503 #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)
2504 #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)
2506 int multi_join_warn_update_low(int code)
2510 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),LOW_WARN_TEXT);
2513 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),LOW_INFO_TEXT);
2516 return CW_CODE_CANCEL;
2519 int multi_join_warn_update_medium(int code)
2523 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),MED_WARN_TEXT);
2526 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),MED_INFO_TEXT);
2529 return CW_CODE_CANCEL;
2532 int multi_join_maybe_warn()
2536 // if the player is set for low updates
2537 if(Player->m_local_options.obj_update_level == OBJ_UPDATE_LOW){
2540 code = multi_join_warn_update_low(code);
2541 } while((code != CW_CODE_CANCEL) && (code != CW_CODE_OK));
2546 // if the player is set for medium updates
2547 else if(Player->m_local_options.obj_update_level == OBJ_UPDATE_MEDIUM){
2550 code = multi_join_warn_update_medium(code);
2551 } while((code != CW_CODE_CANCEL) && (code != CW_CODE_OK));
2559 int multi_join_warn_pxo()
2561 // 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;
2565 void multi_join_blit_protocol()
2567 gr_set_color_fast(&Color_bright);
2569 switch(Socket_type){
2572 gr_string(5, 2, "TCP");
2576 gr_string(5, 2, "IPX");
2582 // -------------------------------------------------------------------------------------------------
2584 // MULTIPLAYER START GAME screen
2589 #define MULTI_SG_PALETTE "InterfacePalette"
2591 static const char *Multi_sg_bitmap_fname[GR_NUM_RESOLUTIONS] = {
2592 "MultiStartGame", // GR_640
2593 "2_MultiStartGame" // GR_1024
2596 static const char *Multi_sg_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
2597 "MultiStartGame-M", // GR_640
2598 "2_MultiStartGame-M" // GR_1024
2603 int Multi_sg_rank_max_display[GR_NUM_RESOLUTIONS] = {
2608 // constants for coordinate look ups
2609 #define MSG_X_COORD 0
2610 #define MSG_Y_COORD 1
2611 #define MSG_W_COORD 2
2612 #define MSG_H_COORD 3
2616 // input password field
2617 int Msg_passwd_coords[GR_NUM_RESOLUTIONS][4] = {
2630 // input game title field
2631 int Msg_title_coords[GR_NUM_RESOLUTIONS][4] = {
2644 // rank selected field
2645 int Msg_rank_sel_coords[GR_NUM_RESOLUTIONS][4] = {
2659 int Msg_rank_list_coords[GR_NUM_RESOLUTIONS][4] = {
2675 #define MULTI_SG_NUM_BUTTONS 12
2676 #define MSG_OPEN_GAME 0
2677 #define MSG_CLOSED_GAME 1
2678 #define MSG_PASSWD_GAME 2
2679 #define MSG_RESTRICTED_GAME 3
2680 #define MSG_RANK_SET_GAME 4
2681 #define MSG_RANK_SCROLL_UP 5
2682 #define MSG_RANK_SCROLL_DOWN 6
2683 #define MSG_RANK_ABOVE 7
2684 #define MSG_RANK_BELOW 8
2686 #define MSG_OPTIONS 10
2687 #define MSG_ACCEPT 11
2689 #define MULTI_SG_NUM_BUTTONS 10
2690 #define MSG_OPEN_GAME 0
2691 //#define MSG_CLOSED_GAME 1
2692 //#define MSG_RESTRICTED_GAME 2
2693 #define MSG_PASSWD_GAME 1
2694 #define MSG_RANK_SET_GAME 2
2695 #define MSG_RANK_SCROLL_UP 3
2696 #define MSG_RANK_SCROLL_DOWN 4
2697 #define MSG_RANK_ABOVE 5
2698 #define MSG_RANK_BELOW 6
2700 #define MSG_OPTIONS 8
2701 #define MSG_ACCEPT 9
2704 UI_WINDOW Multi_sg_window; // the window object for the join screen
2705 UI_BUTTON Multi_sg_rank_button; // for selecting the rank marker
2706 UI_INPUTBOX Multi_sg_game_name; // for Netgame.name
2707 UI_INPUTBOX Multi_sg_game_passwd; // for Netgame.passwd
2708 int Multi_sg_bitmap; // the background bitmap
2710 ui_button_info Multi_sg_buttons[GR_NUM_RESOLUTIONS][MULTI_SG_NUM_BUTTONS] = {
2713 ui_button_info("MSG_00", 75, 111, -1, -1, 0),
2714 ui_button_info("MSG_01", 75, 139, -1, -1, 1),
2715 ui_button_info("MSG_02", 75, 164, -1, -1, 2),
2716 ui_button_info("MSG_03", 75, 199, -1, -1, 3),
2717 ui_button_info("MSG_04", 75, 243, -1, -1, 4),
2718 ui_button_info("MSG_05", 376, 231, -1, -1, 5),
2719 ui_button_info("MSG_06", 376, 258, -1, -1, 6),
2720 ui_button_info("MSG_07", 376, 291, -1, -1, 7),
2721 ui_button_info("MSG_08", 376, 320, -1, -1, 8),
2722 ui_button_info("MSG_09", 469, 427, -1, -1, 9),
2723 ui_button_info("MSG_10", 447, 452, -1, -1, 10),
2724 ui_button_info("MSG_11", 561, 411, -1, -1, 11),
2726 ui_button_info("MSG_00", 1, 184, 34, 191, 2), // open
2727 // ui_button_info("MSG_01", 1, 159, 34, 166, 1), // closed
2728 // ui_button_info("MSG_02", 1, 184, 34, 191, 2), // restricted
2729 ui_button_info("MSG_03", 1, 209, 34, 218, 3), // password
2730 ui_button_info("MSG_04", 1, 257, 34, 266, 4), // rank set
2731 ui_button_info("MSG_05", 1, 282, -1, -1, 5), // rank scroll up
2732 ui_button_info("MSG_06", 1, 307, -1, -1, 6), // rank scroll down
2733 ui_button_info("MSG_07", 177, 282, 210, 290, 7), // rank above
2734 ui_button_info("MSG_08", 177, 307, 210, 315, 8), // rank below
2735 ui_button_info("MSG_09", 536, 429, 500, 440, 9), // help
2736 ui_button_info("MSG_10", 536, 454, 479, 464, 10), // options
2737 ui_button_info("MSG_11", 576, 432, 571, 415, 11), // accept
2741 ui_button_info("2_MSG_00", 2, 295, 51, 307, 2), // open
2742 // ui_button_info("2_MSG_01", 2, 254, 51, 267, 1), // closed
2743 // ui_button_info("2_MSG_02", 2, 295, 51, 307, 2), // restricted
2744 ui_button_info("2_MSG_03", 2, 335, 51, 350, 3), // password
2745 ui_button_info("2_MSG_04", 2, 412, 51, 426, 4), // rank set
2746 ui_button_info("2_MSG_05", 2, 452, -1, -1, 5), // rank scroll up
2747 ui_button_info("2_MSG_06", 2, 492, -1, -1, 6), // rank scroll down
2748 ui_button_info("2_MSG_07", 284, 452, 335, 465, 7), // rank above
2749 ui_button_info("2_MSG_08", 284, 492, 335, 505, 8), // rank below
2750 ui_button_info("2_MSG_09", 858, 687, 817, 706, 9), // help
2751 ui_button_info("2_MSG_10", 858, 728, 797, 743, 10), // options
2752 ui_button_info("2_MSG_11", 921, 692, 921, 664, 11), // accept
2753 #ifdef MAKE_FS1 // filler for extra FS1 buttons
2754 ui_button_info("none", -1, -1, -1, -1, -1),
2755 ui_button_info("none", -1, -1, -1, -1, -1),
2761 #define MULTI_SG_NUM_TEXT 11
2763 UI_XSTR Multi_sg_text[GR_NUM_RESOLUTIONS][MULTI_SG_NUM_TEXT] = {
2765 {"Open", 1322, 34, 191, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_OPEN_GAME].button},
2766 // {"Closed", 1323, 34, 166, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_CLOSED_GAME].button},
2767 // {"Restricted", 1324, 34, 191, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RESTRICTED_GAME].button},
2768 {"Password Protected", 1325, 34, 218, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_PASSWD_GAME].button},
2769 {"Allow Rank", 1326, 34, 266, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_SET_GAME].button},
2770 {"Above", 1327, 210, 290, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_ABOVE].button},
2771 {"Below", 1328, 210, 315, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_BELOW].button},
2772 {"Help", 928, 500, 440, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_HELP].button},
2773 {"Options", 1036, 479, 464, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_OPTIONS].button},
2774 {"Accept", 1035, 571, 415, UI_XSTR_COLOR_PINK, -1, &Multi_sg_buttons[0][MSG_ACCEPT].button},
2775 {"Start Game", 1329, 26, 10, UI_XSTR_COLOR_GREEN, -1, NULL},
2776 {"Title", 1330, 26, 31, UI_XSTR_COLOR_GREEN, -1, NULL},
2777 {"Game Type", 1331, 12, 165, UI_XSTR_COLOR_GREEN, -1, NULL},
2780 {"Open", 1322, 51, 307, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_OPEN_GAME].button},
2781 // {"Closed", 1323, 51, 267, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_CLOSED_GAME].button},
2782 // {"Restricted", 1324, 51, 307, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RESTRICTED_GAME].button},
2783 {"Password Protected", 1325, 51, 350, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_PASSWD_GAME].button},
2784 {"Allow Rank", 1326, 51, 426, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_SET_GAME].button},
2785 {"Above", 1327, 335, 465, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_ABOVE].button},
2786 {"Below", 1328, 335, 505, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_BELOW].button},
2787 {"Help", 928, 817, 706, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_HELP].button},
2788 {"Options", 1036, 797, 743, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_OPTIONS].button},
2789 {"Accept", 1035, 921, 664, UI_XSTR_COLOR_PINK, -1, &Multi_sg_buttons[1][MSG_ACCEPT].button},
2790 {"Start Game", 1329, 42, 22, UI_XSTR_COLOR_GREEN, -1, NULL},
2791 {"Title", 1330, 42, 50, UI_XSTR_COLOR_GREEN, -1, NULL},
2792 {"Game Type", 1331, 20, 264, UI_XSTR_COLOR_GREEN, -1, NULL},
2797 // starting index for displaying ranks
2798 int Multi_sg_rank_start;
2799 int Multi_sg_rank_select;
2801 // netgame pointer to indirect through
2802 netgame_info *Multi_sg_netgame;
2804 // hold temporary values in this structure when on a standalone server
2805 netgame_info Multi_sg_netgame_temp;
2807 // forward declarations
2808 void multi_sg_check_buttons();
2809 void multi_sg_button_pressed(int n);
2810 void multi_sg_init_gamenet();
2811 void multi_sg_draw_radio_buttons();
2812 void multi_sg_rank_scroll_up();
2813 void multi_sg_rank_scroll_down();
2814 void multi_sg_rank_display_stuff();
2815 void multi_sg_rank_process_select();
2816 void multi_sg_rank_build_name(char *in, char *out, const int max_outlen);
2817 void multi_sg_check_passwd();
2818 void multi_sg_check_name();
2819 void multi_sg_release_passwd();
2820 int multi_sg_rank_select_valid(int rank);
2821 void multi_sg_select_rank_default();
2823 // function which takes a rank name and returns the index. Useful for commandline options
2824 // for above and below rank. We return the index of the rank in the Ranks[] array. If
2825 // the rank isn't found, we return -1
2826 int multi_start_game_rank_from_name( char *rank ) {
2830 for ( i = 0; i <= MAX_FREESPACE1_RANK; i++ ) {
2832 for ( i = 0; i <= MAX_FREESPACE2_RANK; i++ ) {
2834 if ( !SDL_strcasecmp(Ranks[i].name, rank) ) {
2842 void multi_start_game_init()
2846 // initialize the gamenet
2847 multi_sg_init_gamenet();
2849 // create the interface window
2850 Multi_sg_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
2851 Multi_sg_window.set_mask_bmap(Multi_sg_bitmap_mask_fname[gr_screen.res]);
2853 // load the background bitmap
2854 Multi_sg_bitmap = bm_load(Multi_sg_bitmap_fname[gr_screen.res]);
2855 if(Multi_sg_bitmap < 0){
2856 // we failed to load the bitmap - this is very bad
2860 // initialize the common notification messaging
2861 multi_common_notify_init();
2863 // initialize the common text area
2864 multi_common_set_text("");
2866 // use the common interface palette
2867 multi_common_set_palette();
2869 // create the interface buttons
2870 for(idx=0; idx<MULTI_SG_NUM_BUTTONS; idx++){
2871 // create the object
2872 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);
2874 // set the sound to play when highlighted
2875 Multi_sg_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
2877 // set the ani for the button
2878 Multi_sg_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sg_buttons[gr_screen.res][idx].filename);
2881 Multi_sg_buttons[gr_screen.res][idx].button.link_hotspot(Multi_sg_buttons[gr_screen.res][idx].hotspot);
2886 for(idx=0; idx<MULTI_SG_NUM_TEXT; idx++){
2887 Multi_sg_window.add_XSTR(&Multi_sg_text[gr_screen.res][idx]);
2891 // load the help overlay
2892 help_overlay_load(MULTI_START_OVERLAY);
2893 help_overlay_set_state(MULTI_START_OVERLAY,0);
2895 // intiialize the rank selection items
2896 multi_sg_select_rank_default();
2897 Multi_sg_rank_start = Multi_sg_rank_select;
2899 // create the rank select button
2900 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);
2901 Multi_sg_rank_button.hide();
2903 // create the netgame name input box
2904 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);
2906 // create the netgame password input box, and disable it by default
2907 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);
2908 Multi_sg_game_passwd.hide();
2909 Multi_sg_game_passwd.disable();
2911 // set the netgame text to this gadget and make it have focus
2912 Multi_sg_game_name.set_text(Multi_sg_netgame->name);
2913 Multi_sg_game_name.set_focus();
2915 // if starting a netgame, set the name of the game and any other options that are appropriate
2916 if ( Cmdline_start_netgame ) {
2917 if ( Cmdline_game_name != NULL ) {
2918 SDL_strlcpy( Multi_sg_netgame->name, Cmdline_game_name, sizeof(Multi_sg_netgame->name) );
2919 Multi_sg_game_name.set_text(Multi_sg_netgame->name);
2922 // deal with the different game types -- only one should even be active, so we will just go down
2923 // the line. Last one wins.
2924 if ( Cmdline_closed_game ) {
2925 Multi_sg_netgame->mode = NG_MODE_CLOSED;
2926 } else if ( Cmdline_restricted_game ) {
2927 Multi_sg_netgame->mode = NG_MODE_RESTRICTED;
2928 } else if ( Cmdline_game_password != NULL ) {
2929 Multi_sg_netgame->mode = NG_MODE_PASSWORD;
2930 SDL_strlcpy(Multi_sg_netgame->passwd, Cmdline_game_password, sizeof(Multi_sg_netgame->passwd));
2931 Multi_sg_game_passwd.set_text(Multi_sg_netgame->passwd);
2934 // deal with rank above and rank below
2935 if ( (Cmdline_rank_above != NULL) || (Cmdline_rank_below != NULL) ) {
2939 if ( Cmdline_rank_above != NULL ) {
2940 rank_str = Cmdline_rank_above;
2942 rank_str = Cmdline_rank_below;
2945 // try and get the rank index from the name -- if found, then set the rank base
2946 // and the game type. apparently we only support either above or below, not both
2947 // together, so I make a random choice
2948 rank = multi_start_game_rank_from_name( rank_str );
2950 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
2952 // now an arbitrary decision
2953 if ( Cmdline_rank_above != NULL ) {
2954 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
2956 Multi_sg_netgame->mode = NG_MODE_RANK_BELOW;
2961 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
2965 void multi_start_game_do()
2967 // return here since we will be moving to the next stage anyway -- I don't want to see the backgrounds of
2968 // all the screens for < 1 second for every screen we automatically move to.
2969 if ( Cmdline_start_netgame ) {
2973 int k = Multi_sg_window.process();
2975 // process any keypresses
2978 if(help_overlay_active(MULTI_START_OVERLAY)){
2979 help_overlay_set_state(MULTI_START_OVERLAY,0);
2981 gamesnd_play_iface(SND_USER_SELECT);
2982 multi_quit_game(PROMPT_NONE);
2987 case SDLK_LCTRL + SDLK_RETURN :
2988 case SDLK_RCTRL + SDLK_RETURN :
2989 gamesnd_play_iface(SND_COMMIT_PRESSED);
2990 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
2994 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
2995 help_overlay_set_state(MULTI_START_OVERLAY, 0);
2998 // check to see if the user has selected a different rank
2999 multi_sg_rank_process_select();
3001 // check any button presses
3002 multi_sg_check_buttons();
3004 // check to see if any of the input boxes have changed, and update the appropriate Netgame fields if necessary
3005 multi_sg_check_passwd();
3006 multi_sg_check_name();
3008 // draw the background, etc
3010 GR_MAYBE_CLEAR_RES(Multi_sg_bitmap);
3011 if(Multi_sg_bitmap != -1){
3012 gr_set_bitmap(Multi_sg_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
3015 Multi_sg_window.draw();
3017 // display rank stuff
3018 multi_sg_rank_display_stuff();
3020 // display any pending notification messages
3021 multi_common_notify_do();
3023 // draw all radio button
3024 multi_sg_draw_radio_buttons();
3026 // draw the help overlay
3027 help_overlay_maybe_blit(MULTI_START_OVERLAY);
3033 void multi_start_game_close()
3035 // if i'm the host on a standalone server, send him my start game options (passwd, mode, etc)
3036 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
3037 multi_options_update_start_game(Multi_sg_netgame);
3040 // unload any bitmaps
3041 if(!bm_unload(Multi_sg_bitmap)){
3042 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_sg_bitmap_fname[gr_screen.res]));
3045 // unload the help overlay
3046 help_overlay_unload(MULTI_START_OVERLAY);
3048 // destroy the UI_WINDOW
3049 Multi_sg_window.destroy();
3052 void multi_sg_check_buttons()
3055 for(idx=0;idx<MULTI_SG_NUM_BUTTONS;idx++){
3056 // we only really need to check for one button pressed at a time, so we can break after
3058 if(Multi_sg_buttons[gr_screen.res][idx].button.pressed()){
3059 multi_sg_button_pressed(idx);
3065 void multi_sg_button_pressed(int n)
3068 // go to the options screen
3070 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
3075 if(!help_overlay_active(MULTI_START_OVERLAY)){
3076 help_overlay_set_state(MULTI_START_OVERLAY,1);
3078 help_overlay_set_state(MULTI_START_OVERLAY,0);
3082 // the open button was pressed
3084 // if the closed option is selected
3085 if(Multi_sg_netgame->mode != NG_MODE_OPEN){
3086 Multi_sg_netgame->mode = NG_MODE_OPEN;
3088 gamesnd_play_iface(SND_USER_SELECT);
3090 // release the password control if necessary
3091 multi_sg_release_passwd();
3093 // if its already selected
3095 gamesnd_play_iface(SND_GENERAL_FAIL);
3100 // the open button was pressed
3101 case MSG_CLOSED_GAME:
3102 // if the closed option is selected
3103 if(Multi_sg_netgame->mode != NG_MODE_CLOSED){
3104 Multi_sg_netgame->mode = NG_MODE_CLOSED;
3106 gamesnd_play_iface(SND_USER_SELECT);
3108 // release the password control if necessary
3109 multi_sg_release_passwd();
3111 // if its already selected
3113 gamesnd_play_iface(SND_GENERAL_FAIL);
3118 // toggle password protection
3119 case MSG_PASSWD_GAME:
3120 // if we selected it
3121 if(Multi_sg_netgame->mode != NG_MODE_PASSWORD){
3122 gamesnd_play_iface(SND_USER_SELECT);
3124 Multi_sg_game_passwd.enable();
3125 Multi_sg_game_passwd.unhide();
3126 Multi_sg_game_passwd.set_focus();
3128 Multi_sg_netgame->mode = NG_MODE_PASSWORD;
3130 // copy in the current network password
3131 Multi_sg_game_passwd.set_text(Multi_sg_netgame->passwd);
3133 gamesnd_play_iface(SND_GENERAL_FAIL);
3138 // toggle "restricted" on or off
3139 case MSG_RESTRICTED_GAME:
3140 if(Multi_sg_netgame->mode != NG_MODE_RESTRICTED){
3141 gamesnd_play_iface(SND_USER_SELECT);
3142 Multi_sg_netgame->mode = NG_MODE_RESTRICTED;
3144 // release the password control if necessary
3145 multi_sg_release_passwd();
3147 gamesnd_play_iface(SND_GENERAL_FAIL);
3152 // turn off all rank requirements
3153 case MSG_RANK_SET_GAME:
3154 // if either is set, then turn then both off
3155 if((Multi_sg_netgame->mode != NG_MODE_RANK_BELOW) && (Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE)){
3156 gamesnd_play_iface(SND_USER_SELECT);
3158 // set it to the default case if we're turning it off
3159 multi_sg_select_rank_default();
3160 Multi_sg_rank_start = Multi_sg_rank_select;
3162 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
3164 // release the password control if necessary
3165 multi_sg_release_passwd();
3167 gamesnd_play_iface(SND_GENERAL_FAIL);
3171 // rank above was pressed
3172 case MSG_RANK_ABOVE :
3173 if((Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE) || (Multi_sg_netgame->mode == NG_MODE_RANK_BELOW)){
3174 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
3176 // select the first item
3177 multi_sg_select_rank_default();
3178 Multi_sg_rank_start = Multi_sg_rank_select;
3181 gamesnd_play_iface(SND_USER_SELECT);
3183 gamesnd_play_iface(SND_GENERAL_FAIL);
3187 // rank below was pressed
3188 case MSG_RANK_BELOW :
3189 if((Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE) || (Multi_sg_netgame->mode == NG_MODE_RANK_BELOW)){
3190 Multi_sg_netgame->mode = NG_MODE_RANK_BELOW;
3192 // select the first item
3193 multi_sg_select_rank_default();
3194 Multi_sg_rank_start = Multi_sg_rank_select;
3197 gamesnd_play_iface(SND_USER_SELECT);
3199 gamesnd_play_iface(SND_GENERAL_FAIL);
3203 // scroll the rank list up
3204 case MSG_RANK_SCROLL_UP:
3205 multi_sg_rank_scroll_up();
3208 // scroll the rank list down
3209 case MSG_RANK_SCROLL_DOWN:
3210 multi_sg_rank_scroll_down();
3213 // move to the create game screen
3215 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
3216 gamesnd_play_iface(SND_COMMIT_PRESSED);
3220 gamesnd_play_iface(SND_GENERAL_FAIL);
3221 multi_common_add_notify(XSTR("Not implemented yet!",760));
3226 // NOTE : this is where all Netgame initialization should take place on the host
3227 void multi_sg_init_gamenet()
3229 char buf[128],out_name[128];
3231 net_player *server_save;
3233 // back this data up in case we are already connected to a standalone
3234 memcpy(&save,&Netgame.server_addr,sizeof(net_addr));
3235 server_save = Netgame.server;
3237 // remove campaign flags
3238 Game_mode &= ~(GM_CAMPAIGN_MODE);
3240 // clear out the Netgame structure and start filling in the values
3241 memset( &Netgame, 0, sizeof(Netgame) );
3242 memset( &Multi_sg_netgame_temp, 0, sizeof(netgame_info) );
3244 // if we're on the standalone, we're not the server, so we don't care about setting the netgame state
3245 if(Net_player->state != NETPLAYER_STATE_STD_HOST_SETUP){
3246 Netgame.game_state = NETGAME_STATE_HOST_SETUP;
3247 Multi_sg_netgame = &Netgame;
3250 ml_string(NOX("Starting netgame as Host/Server"));
3252 Multi_sg_netgame = &Multi_sg_netgame_temp;
3256 memcpy(&temp_addr.s_addr, &Netgame.server_addr, 4);
3257 char *server_addr = inet_ntoa(temp_addr);
3258 ml_printf(NOX("Starting netgame as Host on Standalone server : %s"), (server_addr == NULL) ? NOX("Unknown") : server_addr);
3261 Net_player->tracker_player_id = Multi_tracker_id;
3263 Multi_sg_netgame->security = (rand() % 32766) + 1; // get some random security number
3264 Multi_sg_netgame->mode = NG_MODE_OPEN;
3265 Multi_sg_netgame->rank_base = RANK_ENSIGN;
3266 if(Multi_sg_netgame->security < 16){
3267 Multi_sg_netgame->security += 16;
3270 // set the version_info field
3271 Multi_sg_netgame->version_info = NG_VERSION_ID;
3273 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
3274 Netgame.host = Net_player;
3277 // set the default netgame flags
3278 Multi_sg_netgame->flags = 0;
3280 // intialize endgame stuff
3281 multi_endgame_init();
3283 // load in my netgame options
3284 multi_options_netgame_load(&Netgame.options);
3286 // load my local netplayer options
3287 multi_options_local_load(&Net_player->p_info.options, Net_player);
3289 // setup the default game name, taking care of string length and player callsigns
3290 memset(out_name,0,128);
3292 pilot_format_callsign_personal(Player->callsign, out_name, sizeof(out_name));
3293 SDL_snprintf(buf, sizeof(buf), XSTR("%s game",782), out_name); // [[ %s will be a pilot's name ]]
3294 if ( strlen(buf) > MAX_GAMENAME_LEN ){
3295 SDL_strlcpy(buf, XSTR("Temporary name",783), sizeof(buf));
3297 SDL_strlcpy(Multi_sg_netgame->name, buf, sizeof(Multi_sg_netgame->name));
3299 // set the default qos and duration
3300 multi_voice_maybe_update_vars(Netgame.options.voice_qos,Netgame.options.voice_record_time);
3302 // make sure to set the server correctly (me or the standalone)
3303 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3304 memcpy(&Netgame.server_addr, &Psnet_my_addr,sizeof(net_addr));
3305 Netgame.server = Net_player;
3306 Net_player->player_id = multi_get_new_id();
3308 // setup debug flags
3309 Netgame.debug_flags = 0;
3311 if(!Cmdline_server_firing){
3312 Netgame.debug_flags |= NETD_FLAG_CLIENT_FIRING;
3314 if(!Cmdline_client_dodamage){
3315 Netgame.debug_flags |= NETD_FLAG_CLIENT_NODAMAGE;
3319 memcpy(&Netgame.server_addr,&save,sizeof(net_addr));
3320 Netgame.server = server_save;
3323 // if I have a cd or not
3325 Net_player->flags |= NETINFO_FLAG_HAS_CD;
3328 // if I have hacked data
3329 if(game_hacked_data()){
3330 Net_player->flags |= NETINFO_FLAG_HAXOR;
3333 // assign my player struct and other data
3334 Net_player->flags |= (NETINFO_FLAG_CONNECTED | NETINFO_FLAG_DO_NETWORKING);
3335 Net_player->s_info.voice_token_timestamp = -1;
3337 // if we're supposed to flush our cache directory, do so now
3338 if(Net_player->p_info.options.flags & MLO_FLAG_FLUSH_CACHE){
3339 multi_flush_multidata_cache();
3342 ml_string(NOX("Flushing multi-data cache"));
3348 void multi_sg_draw_radio_buttons()
3350 // draw the appropriate radio button
3351 switch(Multi_sg_netgame->mode){
3353 Multi_sg_buttons[gr_screen.res][MSG_OPEN_GAME].button.draw_forced(2);
3357 case NG_MODE_CLOSED:
3358 Multi_sg_buttons[gr_screen.res][MSG_CLOSED_GAME].button.draw_forced(2);
3362 case NG_MODE_PASSWORD:
3363 Multi_sg_buttons[gr_screen.res][MSG_PASSWD_GAME].button.draw_forced(2);
3367 case NG_MODE_RESTRICTED:
3368 Multi_sg_buttons[gr_screen.res][MSG_RESTRICTED_GAME].button.draw_forced(2);
3372 case NG_MODE_RANK_ABOVE:
3373 Multi_sg_buttons[gr_screen.res][MSG_RANK_SET_GAME].button.draw_forced(2);
3374 Multi_sg_buttons[gr_screen.res][MSG_RANK_ABOVE].button.draw_forced(2);
3376 case NG_MODE_RANK_BELOW:
3377 Multi_sg_buttons[gr_screen.res][MSG_RANK_SET_GAME].button.draw_forced(2);
3378 Multi_sg_buttons[gr_screen.res][MSG_RANK_BELOW].button.draw_forced(2);
3383 void multi_sg_rank_scroll_up()
3385 // if he doesn't have either of the rank flags set, then ignore this
3386 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3390 if(Multi_sg_rank_start > 0){
3391 Multi_sg_rank_start--;
3392 gamesnd_play_iface(SND_SCROLL);
3394 gamesnd_play_iface(SND_GENERAL_FAIL);
3398 void multi_sg_rank_scroll_down()
3400 // if he doesn't have either of the rank flags set, then ignore this
3401 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3405 if((NUM_RANKS - Multi_sg_rank_start) > Multi_sg_rank_max_display[gr_screen.res]){
3406 Multi_sg_rank_start++;
3407 gamesnd_play_iface(SND_SCROLL);
3409 gamesnd_play_iface(SND_GENERAL_FAIL);
3413 void multi_sg_rank_display_stuff()
3418 // if he doesn't have either of the rank flags set, then ignore this
3419 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3423 // display the list of ranks
3424 y = Msg_rank_list_coords[gr_screen.res][MSG_Y_COORD];
3425 idx = Multi_sg_rank_start;
3427 while((count < NUM_RANKS) && (count < Multi_sg_rank_max_display[gr_screen.res]) && (idx < NUM_RANKS)){
3428 // if its the selected item, then color it differently
3429 if(idx == Multi_sg_rank_select){
3430 gr_set_color_fast(&Color_text_selected);
3432 gr_set_color_fast(&Color_text_normal);
3436 multi_sg_rank_build_name(Ranks[idx].name, rank_name, sizeof(rank_name));
3437 gr_string(Msg_rank_list_coords[gr_screen.res][MSG_X_COORD],y,rank_name);
3445 // display the selected rank
3447 gr_set_color_fast(&Color_bright);
3448 multi_sg_rank_build_name(Ranks[Multi_sg_netgame->rank_base].name,rank_name);
3449 gr_string(Msg_rank_sel_coords[gr_screen.res][MSG_X_COORD],Msg_rank_sel_coords[gr_screen.res][MSG_Y_COORD],rank_name);
3453 void multi_sg_rank_process_select()
3457 // if he doesn't have either of the rank flags set, then ignore this
3458 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3462 // see if he's clicked on an item on the rank list
3463 if(Multi_sg_rank_button.pressed()){
3465 Multi_sg_rank_button.get_mouse_pos(NULL,&y);
3468 if(item + Multi_sg_rank_start < NUM_RANKS){
3469 // evaluate whether this rank is valid for the guy to pick
3470 if(multi_sg_rank_select_valid(item + Multi_sg_rank_start)){
3471 gamesnd_play_iface(SND_USER_SELECT);
3473 Multi_sg_rank_select = item + Multi_sg_rank_start;
3475 // set the Netgame rank
3476 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
3478 gamesnd_play_iface(SND_GENERAL_FAIL);
3480 memset(string,0,255);
3481 SDL_snprintf(string,sizeof(string),XSTR("Illegal value for a host of your rank (%s)\n",784),Ranks[Net_player->player->stats.rank].name);
3482 multi_common_add_notify(string);
3488 void multi_sg_rank_build_name(char *in, char *out, const int max_outlen)
3493 SDL_strlcpy(use, in, sizeof(use));
3494 first = strtok(use," ");
3496 // just copy the string
3498 SDL_strlcpy(out, in, max_outlen);
3501 // if the first part of the string is lieutenant, then abbreivate it and tack on the rest of the string
3502 if (SDL_strcasecmp(first,XSTR("lieutenant",785)) == 0) {
3503 first = strtok(NULL, NOX("\n"));
3505 // if he's not just a plain lieutenant
3507 SDL_snprintf(out, max_outlen, "%s%s", XSTR("Lt. ",786), first); // [[ lieutenant ]]
3509 // if he _is_ just a plain lieutenant
3511 SDL_strlcpy(out, in, max_outlen);
3514 SDL_strlcpy(out, in, max_outlen);
3518 void multi_sg_check_passwd()
3520 // check to see if the password input box has been pressed
3521 if(Multi_sg_game_passwd.changed()){
3522 Multi_sg_game_passwd.get_text(Multi_sg_netgame->passwd);
3526 void multi_sg_check_name()
3528 // check to see if the game name input box has been pressed
3529 if(Multi_sg_game_name.changed()){
3530 Multi_sg_game_name.get_text(Multi_sg_netgame->name);
3534 void multi_sg_release_passwd()
3536 // hide and disable the password input box
3537 Multi_sg_game_passwd.hide();
3538 Multi_sg_game_passwd.disable();
3540 // set the focus back to the name input box
3541 Multi_sg_game_name.set_focus();
3544 int multi_sg_rank_select_valid(int rank)
3547 if(Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE){
3548 if(Net_player->player->stats.rank >= rank){
3554 if(Net_player->player->stats.rank <= rank){
3562 void multi_sg_select_rank_default()
3564 // pick our rank for now
3565 Multi_sg_rank_select = Net_player->player->stats.rank;
3567 // set the Netgame rank
3568 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
3571 // -------------------------------------------------------------------------------------------------
3573 // MULTIPLAYER CREATE GAME screen
3578 const char *Multi_create_bitmap_fname[GR_NUM_RESOLUTIONS] = {
3579 "MultiCreate", // GR_640
3580 "2_MultiCreate" // GR_1024
3583 const char *Multi_create_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
3584 "MultiCreate-M", // GR_640
3585 "2_MultiCreate-M" // GR_1024
3588 const char *Multi_create_loading_fname[GR_NUM_RESOLUTIONS] = {
3589 "PleaseWait", // GR_640
3590 "2_PleaseWait" // GR_1024
3594 #define MULTI_CREATE_NUM_BUTTONS 23
3597 #define MC_SHOW_ALL 0
3598 #define MC_SHOW_COOP 1
3599 #define MC_SHOW_TEAM 2
3600 #define MC_SHOW_DOGFIGHT 3
3601 #define MC_PXO_REFRESH 4
3602 #define MC_PILOT_INFO 5
3603 #define MC_SCROLL_LIST_UP 6
3604 #define MC_SCROLL_LIST_DOWN 7
3605 #define MC_SCROLL_PLAYERS_UP 8
3606 #define MC_SCROLL_PLAYERS_DOWN 9
3607 #define MC_MISSION_FILTER 10
3608 #define MC_CAMPAIGN_FILTER 11
3609 #define MC_CANCEL 12
3614 #define MC_SCROLL_INFO_UP 17
3615 #define MC_SCROLL_INFO_DOWN 18
3616 #define MC_HOST_OPTIONS 19
3618 #define MC_OPTIONS 21
3619 #define MC_ACCEPT 22
3622 UI_WINDOW Multi_create_window; // the window object for the create screen
3623 UI_BUTTON Multi_create_player_select_button; // for selecting players
3624 UI_BUTTON Multi_create_list_select_button; // for selecting missions/campaigns
3625 int Multi_create_bitmap; // the background bitmap
3626 UI_SLIDER2 Multi_create_slider; // for create list
3628 // constants for coordinate look ups
3629 #define MC_X_COORD 0
3630 #define MC_Y_COORD 1
3631 #define MC_W_COORD 2
3632 #define MC_H_COORD 3
3634 ui_button_info Multi_create_buttons[GR_NUM_RESOLUTIONS][MULTI_CREATE_NUM_BUTTONS] = {
3637 ui_button_info("MC_18", 34, 131, -1, -1, 18), // all
3638 ui_button_info("MC_19", 72, 131, -1, -1, 19), // coop
3639 ui_button_info("MC_20", 120, 131, -1, -1, 20), // team
3640 // ui_button_info("MC_21", 166, 131, -1, -1, 21), // dogfight
3641 ui_button_info("none", -1, -1, -1, -1, -1), // dogfight (not used)
3642 ui_button_info("none", -1, -1, -1, -1, -1), // pxo?
3643 ui_button_info("MC_26", 540, 114, -1, -1, 26), // pilot info
3644 ui_button_info("MC_03", 0, 187, -1, -1, 2), // scroll list up
3645 ui_button_info("MC_02", 0, 227, -1, -1, 3), // scroll list down
3646 ui_button_info("MC_04", 611, 182, -1, -1, 4), // scroll players up
3647 ui_button_info("MC_05", 611, 221, -1, -1, 5), // scroll players down
3648 ui_button_info("MC_06", 18, 322, -1, -1, 6), // mission filter
3649 ui_button_info("MC_07", 18, 344, -1, -1, 7), // campaign filter
3650 ui_button_info("MC_10", 317, 339, -1, -1, 10), // cancel
3651 ui_button_info("MC_14", 464, 350, -1, -1, 14), // team 1
3652 ui_button_info("MC_15", 498, 350, -1, -1, 15), // team 2
3653 ui_button_info("MC_16", 527, 346, -1, -1, 16), // kick
3654 ui_button_info("MC_17", 572, 346, -1, -1, 17), // close
3655 ui_button_info("MC_08", 0, 398, -1, -1, 8), // scroll mission info up
3656 ui_button_info("MC_09", 0, 435, -1, -1, 9), // scroll mission info down
3657 ui_button_info("MC_27", 447, 402, -1, -1, 27), // host options
3658 ui_button_info("MC_11", 510, 428, -1, -1, 11), // help
3659 ui_button_info("MC_12", 510, 453, -1, -1, 12), // options
3660 ui_button_info("Mc_13", 562, 412, -1, -1, 13), // commit
3662 ui_button_info("MC_00", 32, 129, 36, 158, 0), // show all missions
3663 ui_button_info("MC_01", 76, 129, 71, 158, 1), // show coop missions
3664 ui_button_info("MC_02", 121, 129, 119, 158, 2), // show team missions
3665 ui_button_info("MC_03", 164, 129, 166, 158, 3), // show dogfight missions
3666 ui_button_info("MC_04", 399, 129, 229, 130, 4), // pxo mission refresh
3667 ui_button_info("MC_05", 567, 123, 467, 132, 5), // pilot info
3668 ui_button_info("MC_06", 1, 161, -1, -1, 6), // scroll mission info up
3669 ui_button_info("MC_08", 1, 304, -1, -1, 8), // scroll mission info down
3670 ui_button_info("MC_09", 613, 160, -1, -1, 9), // scroll players up
3671 ui_button_info("MC_10", 613, 202, -1, -1, 10), // scroll players down
3672 ui_button_info("MC_11", 22, 346, 27, 376, 11), // mission filter
3673 ui_button_info("MC_12", 104, 346, 110, 376, 12), // campaign filter
3674 ui_button_info("MC_13", 392, 341, 328, 364, 13), // cancel
3675 ui_button_info("MC_14", 472, 352, 482, 381, 14), // team 0
3676 ui_button_info("MC_15", 506, 352, 514, 381, 15), // team 1
3677 ui_button_info("MC_16", 539, 346, 539, 381, 16), // kick
3678 ui_button_info("MC_17", 589, 346, 582, 381, 17), // close
3679 ui_button_info("MC_18", 1, 406, -1, -1, 18), // scroll list up
3680 ui_button_info("MC_19", 1, 447, -1, -1, 19), // scroll list down
3681 ui_button_info("MC_20", 499, 434, 436, 423, 20), // host options
3682 ui_button_info("MC_21", 534, 426, -1, -1, 21), // help
3683 ui_button_info("MC_22", 534, 452, -1, -1, 22), // options
3684 ui_button_info("MC_23", 571, 426, 572, 413, 23), // commit
3688 ui_button_info("2_MC_00", 51, 207, 61, 253, 0), // show all missions
3689 ui_button_info("2_MC_01", 122, 207, 124, 253, 1), // show coop missions
3690 ui_button_info("2_MC_02", 193, 207, 194, 253, 2), // show team missions
3691 ui_button_info("2_MC_03", 263, 207, 261, 253, 3), // show dogfight missions
3692 ui_button_info("2_MC_04", 639, 207, 479, 218, 4), // pxo mission refresh
3693 ui_button_info("2_MC_05", 907, 197, 748, 216, 5), // pilot info
3694 ui_button_info("2_MC_06", 1, 258, -1, -1, 6), // scroll mission info up
3695 ui_button_info("2_MC_08", 1, 487, -1, -1, 8), // scroll mission info down
3696 ui_button_info("2_MC_09", 981, 256, -1, -1, 9), // scroll players up
3697 ui_button_info("2_MC_10", 981, 323, -1, -1, 10), // scroll players down
3698 ui_button_info("2_MC_11", 35, 554, 46, 601, 11), // mission filter
3699 ui_button_info("2_MC_12", 166, 554, 174, 601, 12), // campaign filter
3700 ui_button_info("2_MC_13", 628, 545, 559, 582, 13), // cancel
3701 ui_button_info("2_MC_14", 756, 564, 772, 610, 14), // team 0
3702 ui_button_info("2_MC_15", 810, 564, 826, 610, 15), // team 1
3703 ui_button_info("2_MC_16", 862, 554, 872, 610, 16), // kick
3704 ui_button_info("2_MC_17", 943, 554, 949, 610, 17), // close
3705 ui_button_info("2_MC_18", 1, 649, -1, -1, 18), // scroll list up
3706 ui_button_info("2_MC_19", 1, 716, -1, -1, 19), // scroll list down
3707 ui_button_info("2_MC_20", 798, 695, 726, 667, 20), // host options
3708 ui_button_info("2_MC_21", 854, 681, -1, -1, 21), // help
3709 ui_button_info("2_MC_22", 854, 724, -1, -1, 22), // options
3710 ui_button_info("2_MC_23", 914, 681, 932, 667, 23), // commit
3715 #define MULTI_CREATE_NUM_TEXT 0
3717 #define MULTI_CREATE_NUM_TEXT 15
3719 UI_XSTR Multi_create_text[GR_NUM_RESOLUTIONS][MULTI_CREATE_NUM_BUTTONS] = {
3721 // not needed for FS1
3723 {"All", 1256, 36, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_ALL].button},
3724 {"Coop", 1257, 71, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_COOP].button},
3725 {"Team", 1258, 119, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_TEAM].button},
3726 {"Dogfight", 1259, 166, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_DOGFIGHT].button},
3727 {"Refresh Missions", 1260, 229, 130, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_PXO_REFRESH].button},
3728 {"Pilot Info", 1261, 467, 132, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_PILOT_INFO].button},
3729 {"Missions", 1262, 27, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_MISSION_FILTER].button},
3730 {"Campaigns", 1263, 110, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_CAMPAIGN_FILTER].button},
3731 {"Cancel", 387, 328, 364, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_CANCEL].button},
3732 {"1", 1264, 482, 381, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_TEAM0].button},
3733 {"2", 1265, 514, 381, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_TEAM1].button},
3734 {"Kick", 1266, 539, 381, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_KICK].button},
3735 {"Close", 1508, 582, 381, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_CLOSE].button},
3736 {"Host Options", 1267, 436, 423, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_HOST_OPTIONS].button},
3737 {"Commit", 1062, 572, 413, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_ACCEPT].button}
3741 // not needed for FS1
3743 {"All", 1256, 61, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_ALL].button},
3744 {"Coop", 1257, 124, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_COOP].button},
3745 {"Team", 1258, 194, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_TEAM].button},
3746 {"Dogfight", 1259, 261, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_DOGFIGHT].button},
3747 {"Refresh Missions", 1260, 501, 218, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_PXO_REFRESH].button},
3748 {"Pilot Info", 1261, 814, 216, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_PILOT_INFO].button},
3749 {"Missions", 1262, 46, 601, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_MISSION_FILTER].button},
3750 {"Campaigns", 1263, 174, 601, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_CAMPAIGN_FILTER].button},
3751 {"Cancel", 387, 559, 582, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_CANCEL].button},
3752 {"1", 1264, 772, 610, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_TEAM0].button},
3753 {"2", 1265, 826, 610, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_TEAM1].button},
3754 {"Kick", 1266, 872, 610, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_KICK].button},
3755 {"Close", 1508, 949, 610, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_CLOSE].button},
3756 {"Host Options", 1267, 755, 683, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_HOST_OPTIONS].button},
3757 {"Commit", 1062, 932, 667, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_ACCEPT].button}
3762 // squad war checkbox
3763 UI_CHECKBOX Multi_create_sw_checkbox;
3764 const char *Multi_create_sw_checkbox_fname[GR_NUM_RESOLUTIONS] = {
3768 int Multi_create_sw_checkbox_coords[GR_NUM_RESOLUTIONS][2] = {
3776 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]);
4153 // if this is a PXO game, enable the squadwar checkbox
4154 Multi_create_sw_checkbox.create(&Multi_create_window, "", Multi_create_sw_checkbox_coords[gr_screen.res][0], Multi_create_sw_checkbox_coords[gr_screen.res][1], 0);
4155 Multi_create_sw_checkbox.set_bmaps(Multi_create_sw_checkbox_fname[gr_screen.res], 6, 0);
4156 if(!MULTI_IS_TRACKER_GAME){
4157 Multi_create_sw_checkbox.hide();
4158 Multi_create_sw_checkbox.disable();
4162 // disable squad war button in demo
4163 Multi_create_sw_checkbox.hide();
4164 Multi_create_sw_checkbox.disable();
4167 // initialize the mission type filtering mode
4168 Multi_create_filter = MISSION_TYPE_MULTI;
4170 // initialize the list mode, and load in a list
4171 memset(Multi_create_mission_list, 0, sizeof(multi_create_info) * MULTI_CREATE_MAX_LIST_ITEMS);
4172 memset(Multi_create_campaign_list, 0, sizeof(multi_create_info) * MULTI_CREATE_MAX_LIST_ITEMS);
4173 for(idx=0; idx<MULTI_CREATE_MAX_LIST_ITEMS; idx++){
4174 Multi_create_mission_list[idx].valid_status = MVALID_STATUS_UNKNOWN;
4175 Multi_create_campaign_list[idx].valid_status = MVALID_STATUS_UNKNOWN;
4177 Multi_create_list_mode = MULTI_CREATE_SHOW_MISSIONS;
4178 Multi_create_list_start = -1;
4179 Multi_create_list_select = -1;
4180 Multi_create_list_count = 0;
4183 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);
4186 // create the player list select button
4187 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);
4188 Multi_create_player_select_button.hide();
4190 // create the mission/campaign list select button
4191 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);
4192 Multi_create_list_select_button.hide();
4194 // set hotkeys for a couple of things.
4195 Multi_create_buttons[gr_screen.res][MC_ACCEPT].button.set_hotkey(KEY_CTRLED+SDLK_RETURN);
4197 // init some master tracker stuff
4198 Multi_create_frame_count = 0;
4199 Multi_create_mt_tried_login = 0;
4201 // remove campaign flags
4202 Game_mode &= ~(GM_CAMPAIGN_MODE);
4204 // send any pilots as appropriate
4205 multi_data_send_my_junk();
4206 Multi_create_file_list = Multi_create_mission_list;
4208 Multi_create_campaign_count = 0;
4209 Multi_create_mission_count = 0;
4210 Multi_create_files_loaded = 0;
4213 void multi_create_game_do()
4217 const char *loading_str = XSTR("Loading", 1336);
4221 // set this if we want to show the pilot info popup
4222 Multi_create_should_show_popup = 0;
4224 // first thing is to load the files
4225 if ( !Multi_create_files_loaded ) {
4226 // if I am a client, send a list request to the server for the missions
4227 if ( MULTIPLAYER_CLIENT ) {
4228 send_mission_list_request( MISSION_LIST_REQUEST );
4232 loading_bitmap = bm_load(Multi_create_loading_fname[gr_screen.res]);
4234 // draw the background, etc
4236 GR_MAYBE_CLEAR_RES(Multi_create_bitmap);
4237 if(Multi_create_bitmap != -1){
4238 gr_set_bitmap(Multi_create_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4242 if ( loading_bitmap > -1 ){
4243 gr_set_bitmap(loading_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4245 gr_bitmap( Please_wait_coords[gr_screen.res][MC_X_COORD], Please_wait_coords[gr_screen.res][MC_Y_COORD] );
4247 // draw "Loading" on it
4249 gr_set_color_fast(&Color_normal);
4251 gr_get_string_size(&str_w, &str_h, loading_str);
4252 gr_string((gr_screen.max_w - str_w) / 2, (gr_screen.max_h - str_h) / 2, loading_str);
4258 multi_create_list_load_missions();
4259 multi_create_list_load_campaigns();
4261 // if this is a tracker game, validate missions
4262 if(MULTI_IS_TRACKER_GAME){
4263 multi_update_valid_missions();
4266 // update the file list
4267 multi_create_setup_list_data(MULTI_CREATE_SHOW_MISSIONS);
4270 // don't bother setting netgame state if ont the server
4271 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4272 Netgame.game_state = NETGAME_STATE_FORMING;
4273 send_netgame_update_packet();
4276 // if we're on the standalone we have to tell him that we're now in the host setup screen
4277 Net_player->state = NETPLAYER_STATE_HOST_SETUP;
4278 send_netplayer_update_packet();
4280 Multi_create_files_loaded = 1;
4283 int k = chatbox_process();
4284 k = Multi_create_window.process(k,0);
4287 // same as the cancel button
4289 if(help_overlay_active(MULTI_CREATE_OVERLAY)){
4290 help_overlay_set_state(MULTI_CREATE_OVERLAY,0);
4292 gamesnd_play_iface(SND_USER_SELECT);
4293 multi_quit_game(PROMPT_HOST);
4298 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
4299 help_overlay_set_state(MULTI_CREATE_OVERLAY, 0);
4302 // process any button clicks
4303 multi_create_check_buttons();
4305 // do any network related stuff
4306 multi_create_do_netstuff();
4308 // draw the background, etc
4310 GR_MAYBE_CLEAR_RES(Multi_create_bitmap);
4311 if(Multi_create_bitmap != -1){
4312 gr_set_bitmap(Multi_create_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4316 // if we're not in team vs. team mode, don't draw the team buttons
4317 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
4318 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.hide();
4319 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.hide();
4320 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.disable();
4321 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.disable();
4323 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.enable();
4324 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.enable();
4325 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.unhide();
4326 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.unhide();
4329 // draw the window itself
4330 Multi_create_window.draw();
4332 gr_set_color_fast(&Color_normal);
4335 // draw Create Game text
4336 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));
4338 // draw players text
4339 gr_string(Mc_players_text[gr_screen.res][MC_X_COORD], Mc_players_text[gr_screen.res][MC_Y_COORD], XSTR("Players", 1269));
4341 // draw players text
4342 gr_string(Mc_team_text[gr_screen.res][MC_X_COORD], Mc_team_text[gr_screen.res][MC_Y_COORD], XSTR("Team", 1258));
4345 // process and display the player list
4346 // NOTE : this must be done before the buttons are checked to insure that a player hasn't dropped
4347 multi_create_plist_process();
4348 if(Netgame.type_flags & NG_TYPE_TEAM){
4349 multi_create_plist_blit_team();
4351 multi_create_plist_blit_normal();
4354 // process and display the game/campaign list
4355 multi_create_list_do();
4357 // draw the correct mission filter button
4358 multi_create_draw_filter_buttons();
4360 // display any text in the info area
4361 multi_common_render_text();
4363 // display any pending notification messages
4364 multi_common_notify_do();
4366 // force the correct mission/campaign button to light up
4367 if( Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ){
4368 Multi_create_buttons[gr_screen.res][MC_MISSION_FILTER].button.draw_forced(2);
4370 Multi_create_buttons[gr_screen.res][MC_CAMPAIGN_FILTER].button.draw_forced(2);
4373 // force draw the closed button if it is toggled on
4374 if(Netgame.flags & NG_FLAG_TEMP_CLOSED){
4375 Multi_create_buttons[gr_screen.res][MC_CLOSE].button.draw_forced(2);
4378 // process and show the chatbox thingie
4382 Multi_create_window.draw_tooltip();
4384 // display the voice status indicator
4385 multi_common_voice_display_status();
4387 // blit the help overlay if necessary
4388 help_overlay_maybe_blit(MULTI_CREATE_OVERLAY);
4391 if(MULTI_IS_TRACKER_GAME){
4392 if(Netgame.type_flags & NG_TYPE_SW){
4393 gr_set_color_fast(&Color_bright);
4395 gr_set_color_fast(&Color_normal);
4397 gr_string(Multi_create_sw_checkbox_text[gr_screen.res][0], Multi_create_sw_checkbox_text[gr_screen.res][1], "SquadWar");
4403 // if we're supposed to show the pilot info popup, do it now
4404 if(Multi_create_should_show_popup){
4405 // get the player index and address of the player item the mouse is currently over
4406 if(Multi_create_plist_select_flag){
4407 player_index = find_player_id(Multi_create_plist_select_id);
4408 if(player_index != -1){
4409 multi_pinfo_popup(&Net_players[player_index]);
4414 // increment the frame count
4415 Multi_create_frame_count++;
4418 void multi_create_game_close()
4420 // unload any bitmaps
4421 if(!bm_unload(Multi_create_bitmap)){
4422 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_create_bitmap_fname[gr_screen.res]));
4425 // unload the help overlay
4426 help_overlay_unload(MULTI_CREATE_OVERLAY);
4428 // destroy the chatbox
4431 // destroy the UI_WINDOW
4432 Multi_create_window.destroy();
4435 void multi_create_check_buttons()
4438 for(idx=0;idx<MULTI_CREATE_NUM_BUTTONS;idx++){
4439 // we only really need to check for one button pressed at a time, so we can break after
4441 if(Multi_create_buttons[gr_screen.res][idx].button.pressed()){
4442 multi_create_button_pressed(idx);
4447 // if the squad war checkbox was clicked
4448 if(Multi_create_sw_checkbox.changed()){
4449 multi_create_sw_clicked();
4453 void multi_create_button_pressed(int n)
4459 gamesnd_play_iface(SND_USER_SELECT);
4460 multi_quit_game(PROMPT_HOST);
4463 // if valid commit conditions have not been met
4464 if(!multi_create_ok_to_commit()){
4469 multi_create_accept_hit();
4474 if(!help_overlay_active(MULTI_CREATE_OVERLAY)){
4475 help_overlay_set_state(MULTI_CREATE_OVERLAY,1);
4477 help_overlay_set_state(MULTI_CREATE_OVERLAY,0);
4481 // scroll the info text box up
4482 case MC_SCROLL_INFO_UP:
4483 multi_common_scroll_text_up();
4486 // scroll the info text box down
4487 case MC_SCROLL_INFO_DOWN:
4488 multi_common_scroll_text_down();
4491 // scroll the player list up
4492 case MC_SCROLL_PLAYERS_UP:
4493 multi_create_plist_scroll_up();
4496 // scroll the player list down
4497 case MC_SCROLL_PLAYERS_DOWN:
4498 multi_create_plist_scroll_down();
4501 // scroll the game/campaign list up
4502 case MC_SCROLL_LIST_UP:
4503 multi_create_list_scroll_up();
4505 Multi_create_slider.forceUp(); // move slider up
4509 // scroll the game/campaign list down
4510 case MC_SCROLL_LIST_DOWN:
4511 multi_create_list_scroll_down();
4513 Multi_create_slider.forceDown(); // move slider down
4517 // go to the options screen
4519 gamesnd_play_iface(SND_USER_SELECT);
4520 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
4523 // show all missions
4525 if(Multi_create_filter != MISSION_TYPE_MULTI){
4526 gamesnd_play_iface(SND_USER_SELECT);
4527 Multi_create_filter = MISSION_TYPE_MULTI;
4528 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4530 gamesnd_play_iface(SND_GENERAL_FAIL);
4534 // show cooperative missions
4536 if(Multi_create_filter != MISSION_TYPE_MULTI_COOP){
4537 gamesnd_play_iface(SND_USER_SELECT);
4538 Multi_create_filter = MISSION_TYPE_MULTI_COOP;
4539 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4541 gamesnd_play_iface(SND_GENERAL_FAIL);
4545 // show team vs. team missions
4547 if(Multi_create_filter != MISSION_TYPE_MULTI_TEAMS){
4548 gamesnd_play_iface(SND_USER_SELECT);
4549 Multi_create_filter = MISSION_TYPE_MULTI_TEAMS;
4550 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4552 gamesnd_play_iface(SND_GENERAL_FAIL);
4556 // show dogfight missions
4558 case MC_SHOW_DOGFIGHT:
4559 if (Multi_create_filter != MISSION_TYPE_MULTI_DOGFIGHT){
4560 gamesnd_play_iface(SND_USER_SELECT);
4561 Multi_create_filter = MISSION_TYPE_MULTI_DOGFIGHT;
4562 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4564 gamesnd_play_iface(SND_GENERAL_FAIL);
4569 // toggle temporary netgame closed on/off
4571 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4572 Netgame.flags ^= NG_FLAG_TEMP_CLOSED;
4574 Netgame.options.flags |= MLO_FLAG_TEMP_CLOSED;
4575 multi_options_update_netgame();
4577 gamesnd_play_iface(SND_USER_SELECT);
4580 // kick the currently selected player (if possible)
4582 // lookup the player at the specified index
4583 if(Multi_create_plist_select_flag){
4584 idx = find_player_id(Multi_create_plist_select_id);
4585 // kick him - but don't ban him
4587 multi_kick_player(idx,0);
4592 // switch to individual mission mode and load in a list
4593 case MC_MISSION_FILTER:
4594 if(Multi_create_list_mode != MULTI_CREATE_SHOW_MISSIONS){
4595 Netgame.campaign_mode = MP_SINGLE;
4597 gamesnd_play_iface(SND_USER_SELECT);
4599 // update the file list
4600 multi_create_setup_list_data(MULTI_CREATE_SHOW_MISSIONS);
4602 gamesnd_play_iface(SND_GENERAL_FAIL);
4606 // switch to campaign mode and load in a list
4607 case MC_CAMPAIGN_FILTER:
4608 // switch off squad war
4609 Multi_create_sw_checkbox.set_state(0);
4610 Netgame.type_flags = NG_TYPE_COOP;
4612 if(Multi_create_list_mode != MULTI_CREATE_SHOW_CAMPAIGNS){
4613 Netgame.campaign_mode = MP_CAMPAIGN;
4615 gamesnd_play_iface(SND_USER_SELECT);
4617 // update the file list
4618 multi_create_setup_list_data(MULTI_CREATE_SHOW_CAMPAIGNS);
4620 gamesnd_play_iface(SND_GENERAL_FAIL);
4624 // attempt to set the selected player's team
4626 multi_create_set_selected_team(0);
4627 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4628 multi_team_send_update();
4632 // attempt to set the selected player's team
4634 multi_create_set_selected_team(1);
4635 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4636 multi_team_send_update();
4640 // popup the pilot info dialog for the currently selected pilot (will occur at the end of the frame)
4642 Multi_create_should_show_popup = 1;
4645 // go to the host options screen
4646 case MC_HOST_OPTIONS:
4647 gamesnd_play_iface(SND_USER_SELECT);
4648 gameseq_post_event(GS_EVENT_MULTI_HOST_OPTIONS);
4651 // refresh PXO file list
4652 case MC_PXO_REFRESH:
4653 if(!MULTI_IS_TRACKER_GAME){
4656 multi_create_refresh_pxo();
4660 gamesnd_play_iface(SND_GENERAL_FAIL);
4661 multi_common_add_notify(XSTR("Not implemented yet!",760));
4666 // do stuff like pinging servers, sending out requests, etc
4667 void multi_create_do_netstuff()
4671 // if not on a standalone
4672 void multi_create_init_as_server()
4674 // set me up as the host and master
4675 Net_player->flags |= (NETINFO_FLAG_AM_MASTER | NETINFO_FLAG_GAME_HOST);
4678 // if on a standalone
4679 void multi_create_init_as_client()
4681 Net_player->flags |= NETINFO_FLAG_GAME_HOST;
4684 // scroll up through the player list
4685 void multi_create_plist_scroll_up()
4687 gamesnd_play_iface(SND_GENERAL_FAIL);
4690 // scroll down through the player list
4691 void multi_create_plist_scroll_down()
4693 gamesnd_play_iface(SND_GENERAL_FAIL);
4696 void multi_create_plist_process()
4698 int test_count,idx,player_index;
4700 // first determine if there are 0 players in the game. This should never happen since the host is _always_ in the game
4702 for(idx=0;idx<MAX_PLAYERS;idx++){
4703 // count anyone except the standalone server (if applicable)
4704 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
4708 if(test_count <= 0){
4712 // if we had a selected item but that player has left, select myself instead
4713 if(Multi_create_plist_select_flag){
4714 player_index = find_player_id(Multi_create_plist_select_id);
4715 if(player_index == -1){
4716 Multi_create_plist_select_id = Net_player->player_id;
4719 Multi_create_plist_select_flag = 1;
4720 Multi_create_plist_select_id = Net_player->player_id;
4723 // if the player has clicked somewhere in the player list area
4724 if(Multi_create_player_select_button.pressed()){
4727 // get the player index and address of the player item the mouse is currently over
4728 player_id = multi_create_get_mouse_id();
4729 player_index = find_player_id(player_id);
4730 if(player_index != -1){
4731 Multi_create_plist_select_flag = 1;
4732 Multi_create_plist_select_id = player_id;
4737 void multi_create_plist_blit_normal()
4740 char str[CALLSIGN_LEN+5];
4741 int y_start = Mc_players_coords[gr_screen.res][MC_Y_COORD];
4744 // display all the players
4745 for(idx=0;idx<MAX_PLAYERS;idx++){
4746 // count anyone except the standalone server (if applicable)
4747 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
4751 // highlight him if he's the host
4752 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4753 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4754 gr_set_color_fast(&Color_text_active_hi);
4756 gr_set_color_fast(&Color_bright);
4759 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4760 gr_set_color_fast(&Color_text_active);
4762 gr_set_color_fast(&Color_text_normal);
4766 // optionally draw his CD status
4767 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4768 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4769 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4771 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4774 // make sure the string will fit, then display it
4775 SDL_strlcpy(str, Net_players[idx].player->callsign, sizeof(str));
4776 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4777 SDL_strlcat(str, XSTR("(O)",787), sizeof(str)); // [[ Observer ]]
4779 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4780 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4787 void multi_create_plist_blit_team()
4790 char str[CALLSIGN_LEN+1];
4791 int y_start = Mc_players_coords[gr_screen.res][MC_Y_COORD];
4794 // display all the red players first
4795 for(idx=0;idx<MAX_PLAYERS;idx++){
4796 // count anyone except the standalone server (if applicable)
4797 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
4798 // reset total offset
4801 // highlight him if he's the host
4802 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4803 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4804 gr_set_color_fast(&Color_text_active_hi);
4806 // be sure to blit the correct team button
4807 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.draw_forced(2);
4809 gr_set_color_fast(&Color_bright);
4812 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4813 gr_set_color_fast(&Color_text_active);
4815 // be sure to blit the correct team button
4816 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.draw_forced(2);
4818 gr_set_color_fast(&Color_text_normal);
4822 // optionally draw his CD status
4823 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4824 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4825 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4827 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4830 // blit the red team indicator
4831 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
4832 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
4833 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4834 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4836 total_offset += Multi_common_icon_dims[MICON_TEAM0_SELECT][0] + 1;
4839 if(Multi_common_icons[MICON_TEAM0] != -1){
4840 gr_set_bitmap(Multi_common_icons[MICON_TEAM0], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4841 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4843 total_offset += Multi_common_icon_dims[MICON_TEAM0][0] + 1;
4847 // make sure the string will fit
4848 SDL_strlcpy(str, Net_players[idx].player->callsign, sizeof(str));
4849 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4850 SDL_strlcat(str, XSTR("(O)",787), sizeof(str));
4852 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4854 // display him in the correct half of the list depending on his team
4855 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4860 // display all the green players next
4861 for(idx=0;idx<MAX_PLAYERS;idx++){
4862 // count anyone except the standalone server (if applicable)
4863 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
4864 // reset total offset
4867 // highlight him if he's the host
4868 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4869 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4870 gr_set_color_fast(&Color_text_active_hi);
4872 // be sure to blit the correct team button
4873 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.draw_forced(2);
4875 gr_set_color_fast(&Color_bright);
4878 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4879 gr_set_color_fast(&Color_text_active);
4881 // be sure to blit the correct team button
4882 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.draw_forced(2);
4884 gr_set_color_fast(&Color_text_normal);
4888 // optionally draw his CD status
4889 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4890 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4891 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4893 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4896 // blit the red team indicator
4897 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
4898 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
4899 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4900 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4902 total_offset += Multi_common_icon_dims[MICON_TEAM1_SELECT][0] + 1;
4905 if(Multi_common_icons[MICON_TEAM1] != -1){
4906 gr_set_bitmap(Multi_common_icons[MICON_TEAM1], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4907 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4909 total_offset += Multi_common_icon_dims[MICON_TEAM1][0] + 1;
4913 // make sure the string will fit
4914 SDL_strlcpy(str, Net_players[idx].player->callsign, sizeof(str));
4915 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4916 SDL_strlcat(str, XSTR("(O)",787), sizeof(str));
4918 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4920 // display him in the correct half of the list depending on his team
4921 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4927 void multi_create_list_scroll_up()
4929 if(Multi_create_list_start > 0){
4930 Multi_create_list_start--;
4932 gamesnd_play_iface(SND_SCROLL);
4934 gamesnd_play_iface(SND_GENERAL_FAIL);
4938 void multi_create_list_scroll_down()
4940 if((Multi_create_list_count - Multi_create_list_start) > Multi_create_list_max_display[gr_screen.res]){
4941 Multi_create_list_start++;
4943 gamesnd_play_iface(SND_SCROLL);
4945 gamesnd_play_iface(SND_GENERAL_FAIL);
4949 // gets a list of multiplayer misisons
4950 void multi_create_list_load_missions()
4952 char *fname, mission_name[NAME_LENGTH+1];
4956 SDL_snprintf(wild_card, sizeof(wild_card), "*%s", FS_MISSION_FILE_EXT);
4957 file_count = cf_get_file_list_preallocated(MULTI_CREATE_MAX_LIST_ITEMS, Multi_create_files_array, NULL, CF_TYPE_MISSIONS, wild_card);
4958 Multi_create_mission_count = 0;
4960 // maybe create a standalone dialog
4961 if(Game_mode & GM_STANDALONE_SERVER){
4962 std_create_gen_dialog("Loading missions");
4963 std_gen_set_text("Mission:", 1);
4966 for(idx = 0; idx < file_count; idx++){
4967 int flags,max_players;
4971 fname = Multi_create_files_array[idx];
4973 // tack on any necessary file extension
4974 filename = cf_add_ext( fname, FS_MISSION_FILE_EXT );
4976 // for multiplayer beta builds, only accept builtin missions
4977 #if defined(MULTIPLAYER_BETA_BUILD) || defined(FS2_DEMO)
4978 if(game_find_builtin_mission(filename) == NULL){
4981 #elif defined(PD_BUILD)
4982 if((game_find_builtin_mission(filename) == NULL) && !strstr(filename, "peterdrake")){
4987 if(Game_mode & GM_STANDALONE_SERVER){
4988 std_gen_set_text(filename, 2);
4991 flags = mission_parse_is_multi(filename, mission_name);
4993 // if the mission is a multiplayer mission, then add it to the mission list
4995 max_players = mission_parse_get_multi_mission_info( filename );
4996 m_respawn = The_mission.num_respawns;
4998 if ( Multi_create_mission_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
4999 multi_create_info *mcip;
5001 mcip = &Multi_create_mission_list[Multi_create_mission_count];
5002 SDL_strlcpy(mcip->filename, filename, sizeof(mcip->filename));
5003 SDL_strlcpy(mcip->name, mission_name, sizeof(mcip->name));
5004 mcip->flags = flags;
5005 mcip->respawn = m_respawn;
5006 mcip->max_players = (ubyte)max_players;
5008 // get any additional information for possibly builtin missions
5009 fs_builtin_mission *fb = game_find_builtin_mission(filename);
5013 Multi_create_mission_count++;
5019 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);
5022 // maybe create a standalone dialog
5023 if(Game_mode & GM_STANDALONE_SERVER){
5024 std_destroy_gen_dialog();
5028 void multi_create_list_load_campaigns()
5031 int idx, file_count;
5032 int campaign_type,max_players;
5036 // maybe create a standalone dialog
5037 if(Game_mode & GM_STANDALONE_SERVER){
5038 std_create_gen_dialog("Loading campaigns");
5039 std_gen_set_text("Campaign:", 1);
5042 Multi_create_campaign_count = 0;
5043 SDL_snprintf(wild_card, sizeof(wild_card), "*%s", FS_CAMPAIGN_FILE_EXT);
5044 file_count = cf_get_file_list_preallocated(MULTI_CREATE_MAX_LIST_ITEMS, Multi_create_files_array, NULL, CF_TYPE_MISSIONS, wild_card);
5045 for(idx = 0; idx < file_count; idx++){
5047 char *filename, name[NAME_LENGTH];
5049 fname = Multi_create_files_array[idx];
5051 // tack on any necessary file extension
5052 filename = cf_add_ext( fname, FS_CAMPAIGN_FILE_EXT );
5054 // for multiplayer beta builds, only accept builtin missions
5055 #if defined(MULTIPLAYER_BETA_BUILD) || defined(FS2_DEMO)
5056 if(game_find_builtin_mission(filename) == NULL){
5059 #elif defined(PD_BUILD)
5060 if((game_find_builtin_mission(filename) == NULL) && !strstr(filename, "peterdrake")){
5065 if(Game_mode & GM_STANDALONE_SERVER){
5066 std_gen_set_text(filename, 2);
5069 // if the campaign is a multiplayer campaign, then add the data to the campaign list items
5070 flags = mission_campaign_parse_is_multi( filename, name, sizeof(name) );
5071 if( flags != CAMPAIGN_TYPE_SINGLE && mission_campaign_get_info(filename,title,&campaign_type,&max_players)) {
5072 if ( Multi_create_campaign_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
5073 multi_create_info *mcip;
5075 mcip = &Multi_create_campaign_list[Multi_create_campaign_count];
5076 SDL_strlcpy(mcip->filename, filename, sizeof(mcip->filename));
5077 SDL_strlcpy(mcip->name, name, sizeof(mcip->name));
5079 // setup various flags
5080 if ( flags == CAMPAIGN_TYPE_MULTI_COOP ){
5081 mcip->flags = MISSION_TYPE_MULTI_COOP | MISSION_TYPE_MULTI;
5082 } else if ( flags == CAMPAIGN_TYPE_MULTI_TEAMS ) {
5083 mcip->flags = MISSION_TYPE_MULTI_TEAMS | MISSION_TYPE_MULTI;
5085 Int3(); // bogus campaign multi type -- find allender
5088 // 0 respawns for campaign files (should be contained within the mission files themselves)
5091 // 0 max players for campaign files
5092 mcip->max_players = (unsigned char)max_players;
5094 // get any additional information for possibly builtin missions
5095 fs_builtin_mission *fb = game_find_builtin_mission(filename);
5099 Multi_create_campaign_count++;
5104 // maybe create a standalone dialog
5105 if(Game_mode & GM_STANDALONE_SERVER){
5106 std_destroy_gen_dialog();
5110 void multi_create_list_do()
5113 int start_index,stop_index;
5114 char selected_name[255];
5116 // bail early if there aren't any selectable items
5117 if(Multi_create_list_count == 0){
5121 // first check to see if the user has clicked on an item
5122 if(Multi_create_list_select_button.pressed()){
5124 Multi_create_list_select_button.get_mouse_pos(NULL,&y);
5127 // make sure we are selectedin valid indices
5128 if((item < Multi_create_list_max_display[gr_screen.res]) && (item >= 0)){
5129 item += Multi_create_list_start;
5131 if(item < Multi_create_list_count){
5132 multi_create_list_select_item(item);
5133 gamesnd_play_iface(SND_IFACE_MOUSE_CLICK);
5138 // bail early if we don't have a start position
5139 if(Multi_create_list_start == -1){
5143 // display the list of individual campaigns/missions
5145 int y_start = Mc_list_coords[gr_screen.res][MC_Y_COORD];
5147 start_index = multi_create_select_to_index(Multi_create_list_start);
5148 stop_index = Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ? Multi_create_mission_count : Multi_create_campaign_count;
5149 for(idx=start_index; idx<stop_index; idx++){
5150 // see if we should drop out
5151 if(count == Multi_create_list_max_display[gr_screen.res]){
5155 // see if we should filter out this mission
5156 if ( !(Multi_create_file_list[idx].flags & Multi_create_filter) ){
5160 // highlight the selected item
5161 multi_create_select_to_filename(Multi_create_list_select, selected_name, sizeof(selected_name));
5162 if(!strcmp(selected_name,Multi_create_file_list[idx].filename)){
5163 gr_set_color_fast(&Color_text_selected);
5165 gr_set_color_fast(&Color_text_normal);
5168 // draw the type icon
5169 multi_create_list_blit_icons(idx, y_start);
5171 // force fit the mission name string
5172 SDL_strlcpy(selected_name, Multi_create_file_list[idx].name, sizeof(selected_name));
5173 gr_force_fit_string(selected_name,255,Mc_column1_w[gr_screen.res]);
5174 gr_string(Mc_mission_name_x[gr_screen.res],y_start,selected_name);
5176 // draw the max players if in mission mode
5177 SDL_snprintf(selected_name,sizeof(selected_name),"%d",(int)Multi_create_file_list[idx].max_players);
5178 gr_string(Mc_mission_count_x[gr_screen.res],y_start,selected_name);
5180 // force fit the mission filename string
5181 SDL_strlcpy(selected_name, Multi_create_file_list[idx].filename, sizeof(selected_name));
5182 gr_force_fit_string(selected_name,255,Mc_column3_w[gr_screen.res]);
5183 gr_string(Mc_mission_fname_x[gr_screen.res],y_start,selected_name);
5190 // takes care of stuff like changing indices around and setting up the netgame structure
5191 void multi_create_list_select_item(int n)
5193 int abs_index,campaign_type,max_players;
5194 char title[NAME_LENGTH+1];
5195 netgame_info ng_temp;
5198 char *campaign_desc;
5200 // if not on the standalone server
5201 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5204 // on the standalone
5206 memset(&ng_temp,0,sizeof(netgame_info));
5210 if ( n != Multi_create_list_select ) {
5211 // check to see if this is a valid index, and bail if it is not
5212 abs_index = multi_create_select_to_index(n);
5213 if(abs_index == -1){
5217 Multi_create_list_select = n;
5219 // set the mission name
5220 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5221 multi_create_select_to_filename(n, ng->mission_name, sizeof(ng->mission_name));
5223 multi_create_select_to_filename(n, ng->campaign_name, sizeof(ng->campaign_name));
5226 // make sure the netgame type is properly set
5227 int old_type = Netgame.type_flags;
5228 abs_index = multi_create_select_to_index(n);
5229 if(abs_index != -1){
5230 if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_TEAMS){
5231 // if we're in squad war mode, leave it as squad war
5232 if(old_type & NG_TYPE_SW){
5233 ng->type_flags = NG_TYPE_SW;
5235 ng->type_flags = NG_TYPE_TVT;
5237 } else if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_COOP){
5238 ng->type_flags = NG_TYPE_COOP;
5239 } else if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_DOGFIGHT){
5240 ng->type_flags = NG_TYPE_DOGFIGHT;
5244 // if we're no longer in a TvT game, just uncheck the squadwar checkbox
5245 if(!(ng->type_flags & NG_TYPE_TEAM)){
5246 Multi_create_sw_checkbox.set_state(0);
5249 // if we switched from something else to team vs. team mode, do some special processing
5250 if((ng->type_flags & NG_TYPE_TEAM) && (ng->type_flags != old_type) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
5254 switch(Multi_create_list_mode){
5255 case MULTI_CREATE_SHOW_MISSIONS:
5256 // don't forget to update the info box window thingie
5257 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5258 ship_init(); // mwa -- 10/15/97. Call this function to reset number of ships in mission
5259 ng->max_players = mission_parse_get_multi_mission_info( ng->mission_name );
5261 SDL_assert(ng->max_players > 0);
5262 SDL_strlcpy(ng->title, The_mission.name, sizeof(ng->title));
5264 // set the information area text
5265 multi_common_set_text(The_mission.mission_desc);
5267 // if we're on the standalone, send a request for the description
5269 send_netgame_descript_packet(&Netgame.server_addr,0);
5270 multi_common_set_text("");
5273 // set the respawns as appropriate
5274 if(Netgame.options.respawn <= Multi_create_file_list[abs_index].respawn){
5275 ng->respawn = Netgame.options.respawn;
5276 nprintf(("Network","Using netgame options for respawn count (%d %d)\n",Netgame.options.respawn,Multi_create_file_list[abs_index].respawn));
5278 ng->respawn = Multi_create_file_list[abs_index].respawn;
5279 nprintf(("Network","Using mission settings for respawn count (%d %d)\n",Netgame.options.respawn,Multi_create_file_list[abs_index].respawn));
5282 case MULTI_CREATE_SHOW_CAMPAIGNS:
5283 // if not on the standalone server
5284 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5285 // get the campaign info
5286 memset(title,0,NAME_LENGTH+1);
5287 if(!mission_campaign_get_info(ng->campaign_name,title,&campaign_type,&max_players, &campaign_desc)) {
5288 memset(ng->campaign_name,0,NAME_LENGTH+1);
5289 ng->max_players = 0;
5291 // if we successfully got the # of players
5293 memset(ng->title,0,NAME_LENGTH+1);
5294 SDL_strlcpy(ng->title, title, sizeof(ng->title));
5295 ng->max_players = max_players;
5298 nprintf(("Network","MC MAX PLAYERS : %d\n",ng->max_players));
5300 // set the information area text
5301 // multi_common_set_text(ng->title);
5302 multi_common_set_text(campaign_desc);
5304 // if on the standalone server, send a request for the description
5306 // no descriptions currently kept for campaigns
5309 // netgame respawns are always 0 for campaigns (until the first mission is loaded)
5314 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5316 send_netgame_update_packet();
5318 // update all machines about stuff like respawns, etc.
5319 multi_options_update_netgame();
5321 multi_options_update_mission(ng, Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ? 1 : 0);
5326 void multi_create_list_blit_icons(int list_index, int y_start)
5328 multi_create_info *mcip;
5329 fs_builtin_mission *fb;
5332 // get a pointer to the list item
5333 max_index = Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ? Multi_create_mission_count - 1 : Multi_create_campaign_count - 1;
5334 if((list_index < 0) || (list_index > max_index)){
5337 mcip = &Multi_create_file_list[list_index];
5339 // blit the multiplayer type icons
5340 if(mcip->flags & MISSION_TYPE_MULTI_COOP){
5341 if(Multi_common_icons[MICON_COOP] >= 0){
5342 gr_set_bitmap(Multi_common_icons[MICON_COOP], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5343 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5345 } else if(mcip->flags & MISSION_TYPE_MULTI_TEAMS){
5346 if(Multi_common_icons[MICON_TVT] >= 0){
5347 gr_set_bitmap(Multi_common_icons[MICON_TVT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5348 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5350 } else if(mcip->flags & MISSION_TYPE_MULTI_DOGFIGHT){
5351 if(Multi_common_icons[MICON_DOGFIGHT] >= 0){
5352 gr_set_bitmap(Multi_common_icons[MICON_DOGFIGHT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5353 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5357 // if its a valid mission, blit the valid mission icon
5358 if(MULTI_IS_TRACKER_GAME && (mcip->valid_status == MVALID_STATUS_VALID)){
5359 if(Multi_common_icons[MICON_VALID] >= 0){
5360 gr_set_bitmap(Multi_common_icons[MICON_VALID], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5361 gr_bitmap(Mc_icon_valid_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_valid_coords[gr_screen.res][MC_Y_COORD]);
5365 // now see if its a builtin mission
5366 fb = game_find_builtin_mission(mcip->filename);
5367 // if the mission is from volition, blit the volition icon
5368 if((fb != NULL) && (fb->flags & FSB_FROM_VOLITION)){
5369 if(Multi_common_icons[MICON_VOLITION] >= 0){
5370 gr_set_bitmap(Multi_common_icons[MICON_VOLITION], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5371 gr_bitmap(Mc_icon_volition_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_volition_coords[gr_screen.res][MC_Y_COORD]);
5376 // check for mdisk mission
5377 fb = game_find_builtin_mission(mcip->filename);
5378 if((fb != NULL) && (fb->flags & FSB_FROM_MDISK)){
5379 if(Multi_common_icons[MICON_MDISK] >= 0){
5380 gr_set_bitmap(Multi_common_icons[MICON_MDISK], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5381 gr_bitmap(Mc_icon_silent_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_silent_coords[gr_screen.res][MC_Y_COORD]);
5387 void multi_create_accept_hit()
5389 char selected_name[255];
5390 int start_campaign = 0;
5392 // make sure all players have finished joining
5393 if(!multi_netplayer_state_check(NETPLAYER_STATE_JOINED,1)){
5394 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("Please wait until all clients have finished joining",788));
5395 gamesnd_play_iface(SND_GENERAL_FAIL);
5398 gamesnd_play_iface(SND_COMMIT_PRESSED);
5401 // do single mission stuff
5402 switch(Multi_create_list_mode){
5403 case MULTI_CREATE_SHOW_MISSIONS:
5404 if(Multi_create_list_select != -1){
5405 // set the netgame mode
5406 Netgame.campaign_mode = MP_SINGLE;
5408 // setup various filenames and mission names
5409 multi_create_select_to_filename(Multi_create_list_select, selected_name, sizeof(selected_name));
5410 SDL_strlcpy( Game_current_mission_filename, selected_name, MAX_FILENAME_LEN );
5411 SDL_strlcpy( Netgame.mission_name, selected_name, MAX_FILENAME_LEN );
5414 ml_printf(NOX("Starting single mission %s, with %d players"), Game_current_mission_filename, multi_num_players());
5416 multi_common_add_notify(XSTR("No mission selected!",789));
5421 case MULTI_CREATE_SHOW_CAMPAIGNS:
5422 // do campaign related stuff
5423 if(Multi_create_list_select != -1){
5424 // set the netgame mode
5425 Netgame.campaign_mode = MP_CAMPAIGN;
5427 // start a campaign instead of a single mission
5428 multi_create_select_to_filename(Multi_create_list_select, selected_name, sizeof(selected_name));
5429 multi_campaign_start(selected_name);
5433 ml_printf(NOX("Starting campaign %s, with %d players"), selected_name, multi_num_players());
5435 multi_common_add_notify(XSTR("No campaign selected!",790));
5441 // if this is a team vs team situation, lock the players send a final team update
5442 if((Netgame.type_flags & NG_TYPE_TEAM) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
5443 multi_team_host_lock_all();
5444 multi_team_send_update();
5447 // if not on the standalone, move to the mission sync state which will take care of everything
5448 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5449 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
5450 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
5452 // otherwise tell the standalone to do so
5454 // when the standalone receives this, he'll do the mission syncing himself
5455 send_mission_sync_packet(MULTI_SYNC_PRE_BRIEFING,start_campaign);
5459 void multi_create_draw_filter_buttons()
5461 // highlight the correct filter button
5462 if ( Multi_create_filter == MISSION_TYPE_MULTI ){
5463 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL].button.draw_forced(2);
5464 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_COOP ) {
5465 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 1].button.draw_forced(2);
5466 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_TEAMS ) {
5467 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 2].button.draw_forced(2);
5468 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_DOGFIGHT ){
5469 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 3].button.draw_forced(2);
5475 void multi_create_set_selected_team(int team)
5479 // if we don't currently have a player selected, don't do anything
5480 if(!Multi_create_plist_select_flag){
5481 gamesnd_play_iface(SND_GENERAL_FAIL);
5484 gamesnd_play_iface(SND_USER_SELECT);
5486 // otherwise attempt to set the team for this guy
5487 player_index = find_player_id(Multi_create_plist_select_id);
5488 if(player_index != -1){
5489 multi_team_set_team(&Net_players[player_index],team);
5493 void multi_create_handle_join(net_player *pl)
5495 // for now just play a bloop sound
5496 gamesnd_play_iface(SND_ICON_DROP_ON_WING);
5499 // fill in net address of player the mouse is over, return player index (or -1 if none)
5500 short multi_create_get_mouse_id()
5502 // determine where he clicked (y pixel value)
5504 Multi_create_player_select_button.get_mouse_pos(NULL,&y);
5506 // select things a little differently if we're in team vs. team or non-team vs. team mode
5508 if(Netgame.type_flags & NG_TYPE_TEAM){
5509 int player_index = -1;
5511 // look through all of team red first
5512 for(idx=0;idx<MAX_PLAYERS;idx++){
5513 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
5516 // if this is the _nth_ guy
5524 // if we still haven't found him yet, look through the green team
5525 if(player_index == -1){
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 == 1)){
5529 // if this is the _nth_ guy
5538 if(player_index != -1){
5539 return Net_players[player_index].player_id;
5542 // select the nth active player if possible, disregarding the standalone server
5543 for(idx=0;idx<MAX_PLAYERS;idx++){
5544 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
5547 // if this is the _nth_ guy
5549 return Net_players[idx].player_id;
5558 void multi_create_select_to_filename(int select_index, char *filename, const int max_filelen)
5562 // look through the mission list
5563 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5564 for(idx=0;idx<Multi_create_mission_count;idx++){
5565 if(Multi_create_file_list[idx].flags & Multi_create_filter){
5569 // if we found the item
5570 if(select_index < 0){
5571 SDL_strlcpy(filename, Multi_create_file_list[idx].filename, max_filelen);
5576 // look through the campaign list
5577 else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
5578 for(idx=0;idx<Multi_create_campaign_count;idx++){
5581 // if we found the item
5582 if(select_index < 0){
5583 SDL_strlcpy(filename, Multi_create_file_list[idx].filename, max_filelen);
5589 SDL_strlcpy(filename, "", max_filelen);
5592 int multi_create_select_to_index(int select_index)
5595 int lookup_index = 0;
5597 // look through the mission list
5598 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5599 for(idx=0;idx<Multi_create_mission_count;idx++){
5600 if(Multi_create_file_list[idx].flags & Multi_create_filter){
5604 // if we found the item
5605 if(select_index < lookup_index){
5610 // look through the campaign list
5611 else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
5612 for(idx=0;idx<Multi_create_campaign_count;idx++){
5615 // if we found the item
5616 if(select_index < 0){
5625 int multi_create_ok_to_commit()
5627 int player_count, observer_count, idx;
5628 int notify_of_hacked_ships_tbl = 0;
5629 int notify_of_hacked_weapons_tbl = 0;
5630 char err_string[255];
5634 // make sure we have a valid mission selected
5635 if(Multi_create_list_select < 0){
5639 // if this is not a valid mission, let the player know
5640 abs_index = multi_create_select_to_index(Multi_create_list_select);
5645 // if we're playing with a hacked ships.tbl (on PXO)
5646 notify_of_hacked_ships_tbl = 0;
5647 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5648 if(!Game_ships_tbl_valid){
5649 notify_of_hacked_ships_tbl = 1;
5652 if(Netgame.flags & NG_FLAG_HACKED_SHIPS_TBL){
5653 notify_of_hacked_ships_tbl = 1;
5656 if(!MULTI_IS_TRACKER_GAME){
5657 notify_of_hacked_ships_tbl = 0;
5659 if(notify_of_hacked_ships_tbl){
5660 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){
5665 // if we're playing with a hacked weapons.tbl (on PXO)
5666 notify_of_hacked_weapons_tbl = 0;
5667 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5668 if(!Game_weapons_tbl_valid){
5669 notify_of_hacked_weapons_tbl = 1;
5672 if(Netgame.flags & NG_FLAG_HACKED_WEAPONS_TBL){
5673 notify_of_hacked_weapons_tbl = 1;
5676 if(!MULTI_IS_TRACKER_GAME){
5677 notify_of_hacked_weapons_tbl = 0;
5679 if(notify_of_hacked_weapons_tbl){
5680 if(popup(PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON, 2, XSTR("&Back", 995), XSTR("&Continue", 780), XSTR("You or the server you are playing on has a hacked weapons.tbl. Your stats will not be updated on PXO", 1052)) <= 0){
5685 // if any of the players have hacked data
5687 for(idx=0; idx<MAX_PLAYERS; idx++){
5688 // look for hacked players
5689 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAXOR)){
5693 // message everyone - haha
5694 if(Net_players[idx].player != NULL){
5695 SDL_snprintf(err_string, sizeof(err_string), "%s %s", Net_players[idx].player->callsign, XSTR("has hacked tables/data", 1271));
5697 SDL_snprintf(err_string, sizeof(err_string), "somebody %s", XSTR("has hacked tables/data", 1271));
5699 send_game_chat_packet(Net_player, err_string, MULTI_MSG_ALL, NULL, NULL, 1);
5702 // if we found a hacked set of data
5705 if(MULTI_IS_TRACKER_GAME){
5706 // don't allow squad war matches to continue
5707 if(Netgame.type_flags & NG_TYPE_SW){
5709 // if this is squad war, don't allow it to continue
5710 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));
5715 // otherwise, warn the players that stats will not saved
5717 // if this is squad war, don't allow it to continue
5718 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){
5723 // non-pxo, just give a notice
5725 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){
5731 // check to see that we don't have too many observers
5732 observer_count = multi_num_observers();
5733 if(observer_count > Netgame.options.max_observers){
5734 // print up the error string
5735 SDL_snprintf(err_string,sizeof(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);
5737 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, err_string);
5741 // check to see that we have a valid # of players for the the # of ships in the game
5742 player_count = multi_num_players();
5743 if(player_count > Netgame.max_players){
5744 // print up the error string
5745 SDL_snprintf(err_string,sizeof(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);
5747 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, err_string);
5751 // check to see if teams are assigned properly in a team vs. team situation
5752 if(Netgame.type_flags & NG_TYPE_TEAM){
5753 if(!multi_team_ok_to_commit()){
5754 gamesnd_play_iface(SND_GENERAL_FAIL);
5755 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("Teams and/or team captains are not assigned properly", 793));
5761 if(!multi_create_verify_cds()){
5762 gamesnd_play_iface(SND_GENERAL_FAIL);
5764 #ifdef MULTIPLAYER_BETA_BUILD
5765 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, "You need 1 CD for every player!");
5767 #ifdef DVD_MESSAGE_HACK
5768 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You need 1 DVD for every 4 players!", 794));
5770 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You need 1 CD for every 4 players!", 794));
5776 // if we're playing on the tracker
5777 if(MULTI_IS_TRACKER_GAME){
5778 #ifdef PXO_CHECK_VALID_MISSIONS
5779 if((Multi_create_file_list == Multi_create_mission_list) && (Multi_create_file_list[abs_index].valid_status != MVALID_STATUS_VALID)){
5780 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){
5787 if(!(Netgame.type_flags & NG_TYPE_SW)){
5788 // if he is playing by himself, tell him stats will not be accepted
5789 if(multi_num_players() == 1){
5790 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){
5803 int multi_create_verify_cds()
5805 int player_count = multi_num_players();
5809 // count how many cds we have
5811 for(idx=0;idx<MAX_PLAYERS;idx++){
5812 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAS_CD)){
5817 // for the beta, everyone must have a CD
5818 #ifdef MULTIPLAYER_BETA_BUILD
5819 if(multi_cd_count < player_count){
5823 // determine if we have enough
5824 float ratio = (float)player_count / (float)multi_cd_count;
5825 // greater than a 4 to 1 ratio
5831 // we meet the conditions
5835 // returns an index into Multi_create_mission_list
5836 int multi_create_lookup_mission(char *fname)
5840 for(idx=0; idx<Multi_create_mission_count; idx++){
5841 if(!SDL_strcasecmp(fname, Multi_create_mission_list[idx].filename)){
5846 // couldn't find the mission
5850 // returns an index into Multi_create_campaign_list
5851 int multi_create_lookup_campaign(char *fname)
5855 for(idx=0; idx<Multi_create_campaign_count; idx++){
5856 if(!SDL_strcasecmp(fname, Multi_create_campaign_list[idx].filename)){
5861 // couldn't find the campaign
5865 void multi_create_refresh_pxo()
5867 // delete mvalid.cfg if it exists
5868 cf_delete(MULTI_VALID_MISSION_FILE, CF_TYPE_DATA);
5870 // refresh missions from the tracker
5871 multi_update_valid_missions();
5874 void multi_create_sw_clicked()
5876 netgame_info ng_temp;
5879 int file_index = multi_create_select_to_index(Multi_create_list_select);
5881 // either a temporary netgame or the real one
5882 if(MULTIPLAYER_MASTER){
5889 // maybe switch squad war off
5890 if(!Multi_create_sw_checkbox.checked()){
5891 // if the mission selected is a coop mission, go back to coop mode
5892 SDL_assert(file_index != -1);
5893 if(file_index == -1){
5894 ng->type_flags = NG_TYPE_COOP;
5896 if(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_TEAMS){
5897 ng->type_flags = NG_TYPE_TVT;
5898 } else if(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_DOGFIGHT){
5899 ng->type_flags = NG_TYPE_DOGFIGHT;
5901 ng->type_flags = NG_TYPE_COOP;
5904 // switch squad war on
5906 SDL_assert(file_index != -1);
5907 if((file_index == -1) || !(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_TEAMS)){
5908 Multi_create_sw_checkbox.set_state(0);
5910 // at this point we know its safe to switch squad war on
5911 ng->type_flags = NG_TYPE_SW;
5916 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5918 send_netgame_update_packet();
5920 // update all machines about stuff like respawns, etc.
5921 multi_options_update_netgame();
5923 // on the standalone
5925 // standalone will take care of polling the usertracker
5926 multi_options_update_mission(ng, Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ? 1 : 0);
5931 // -------------------------------------------------------------------------------------------------------------
5933 // MULTIPLAYER HOST OPTIONS SCREEN
5936 #define MULTI_HO_NUM_BUTTONS 12
5937 #define MULTI_HO_NUM_RADIO_BUTTONS 10
5941 #define MULTI_HO_PALETTE "InterfacePalette"
5943 static const char *Multi_ho_bitmap_fname[GR_NUM_RESOLUTIONS] = {
5944 "MultiHost", // GR_640
5945 "2_MultiHost" // GR_1024
5948 static const char *Multi_ho_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
5949 "MultiHost-M", // GR_640
5950 "2_MultiHost-M" // GR_1024
5954 UI_WINDOW Multi_ho_window; // the window object for the join screen
5955 UI_INPUTBOX Multi_ho_respawns; // the # of respawns allowed in the game
5956 UI_INPUTBOX Multi_ho_time_limit; // mission time limit
5957 UI_INPUTBOX Multi_ho_voice_wait; // wait time between tokens
5958 UI_INPUTBOX Multi_ho_kill_limit; // kill limit in a furball mission
5959 UI_INPUTBOX Multi_ho_obs; // # of observers we'll allow
5960 int Multi_ho_bitmap; // the background bitmap
5962 // constants for coordinate lookup
5963 #define MULTI_HO_X_COORD 0
5964 #define MULTI_HO_Y_COORD 1
5965 #define MULTI_HO_W_COORD 2
5966 #define MULTI_HO_H_COORD 3
5967 #define MULTI_HO_TEXT_X_COORD 4
5968 #define MULTI_HO_TEXT_Y_COORD 5
5971 #define MULTI_HO_MSG_RANK 0 // highest ranking players can do messaging
5972 #define MULTI_HO_MSG_LEADER 1 // wing/team leaders can do messaging
5973 #define MULTI_HO_MSG_ANY 2 // any player can do messaging
5974 #define MULTI_HO_MSG_HOST 3 // only the host can do messaging
5975 #define MULTI_HO_END_RANK 4 // highest rank can and host can end mission
5976 #define MULTI_HO_END_LEADER 5 // wing/team leaders and host can end the mission
5977 #define MULTI_HO_END_ANY 6 // any player can end the mission
5978 #define MULTI_HO_END_HOST 7 // only host can end the mission
5979 #define MULTI_HO_VOICE_ON 8 // voice toggled on
5980 #define MULTI_HO_VOICE_OFF 9 // voice toggled off
5981 #define MULTI_HO_HOST_MODIFIES 10 // only the host or team captains can modify ships/weapons in briefing
5982 #define MULTI_HO_ACCEPT 11 // accept button
5984 ui_button_info Multi_ho_buttons[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_BUTTONS] = {
5987 // who is allowed to message
5988 ui_button_info("MH_00", 13, 157, -1, -1, 0), // highest rank
5989 ui_button_info("MH_01", 13, 179, -1, -1, 1), // team/wing leader
5990 ui_button_info("MH_02", 13, 200, -1, -1, 2), // any
5991 ui_button_info("MH_03", 13, 223, -1, -1, 3), // host
5993 // who is allowed to end the mission
5994 ui_button_info("MH_09", 13, 273, -1, -1, 9), // highest rank
5995 ui_button_info("MH_10", 13, 295, -1, -1, 10), // team/wing leader
5996 ui_button_info("MH_11", 13, 317, -1, -1, 11), // any
5997 ui_button_info("MH_12", 13, 339, -1, -1, 12), // host
5999 // voice on/off button
6000 ui_button_info("MH_14", 396, 156, -1, -1, 14),
6001 ui_button_info("MH_15", 453, 156, -1, -1, 15),
6003 // host modifies ships
6004 ui_button_info("MH_19", 215, 410, -1, -1, 19),
6007 ui_button_info("MH_18", 560, 411, -1, -1, 18),
6009 // who is allowed to message
6010 ui_button_info("MH_00", 3, 160, 46, 166, 0), // highest rank
6011 ui_button_info("MH_01", 3, 179, 46, 185, 1), // team/wing leader
6012 ui_button_info("MH_02", 3, 196, 46, 203, 2), // any
6013 ui_button_info("MH_03", 3, 214, 46, 220, 3), // host
6015 // who is allowed to end the mission
6016 ui_button_info("MH_04", 3, 257, 46, 265, 4), // highest rank
6017 ui_button_info("MH_05", 3, 276, 46, 283, 5), // team/wing leader
6018 ui_button_info("MH_06", 3, 294, 46, 300, 6), // any
6019 ui_button_info("MH_07", 3, 311, 46, 317, 7), // host
6021 // voice on/off button
6022 ui_button_info("MH_09", 542, 158, 545, 185, 9),
6023 ui_button_info("MH_10", 598, 158, 604, 185, 10),
6025 // host modifies ships
6026 ui_button_info("MH_13", 542, 377, 437, 363, 13),
6029 ui_button_info("MH_14", 572, 428, 580, 414, 14),
6033 // who is allowed to message
6034 ui_button_info("2_MH_00", 5, 256, 73, 269, 0), // highest rank
6035 ui_button_info("2_MH_01", 5, 286, 73, 297, 1), // team/wing leader
6036 ui_button_info("2_MH_02", 5, 314, 73, 325, 2), // any
6037 ui_button_info("2_MH_03", 5, 341, 73, 352, 3), // host
6039 // who is allowed to end the mission
6040 ui_button_info("2_MH_04", 5, 412, 73, 425, 4), // highest rank
6041 ui_button_info("2_MH_05", 5, 442, 73, 452, 5), // team/wing leader
6042 ui_button_info("2_MH_06", 5, 470, 73, 480, 6), // any
6043 ui_button_info("2_MH_07", 5, 497, 73, 508, 7), // host
6045 // voice on/off button
6046 ui_button_info("2_MH_09", 867, 253, 872, 296, 9),
6047 ui_button_info("2_MH_10", 957, 253, 966, 296, 10),
6049 // host modifies ships
6050 ui_button_info("2_MH_13", 867, 603, 784, 581, 13),
6053 ui_button_info("2_MH_14", 916, 685, 925, 665, 14),
6056 UI_XSTR Multi_ho_text[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_BUTTONS] = {
6058 // not needed for FS1
6060 {"Highest rank", 1280, 46, 166, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_RANK].button},
6061 {"Team / wing-leader", 1281, 46, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_LEADER].button},
6062 {"Any", 1282, 46, 203, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_ANY].button},
6063 {"Host", 1283, 46, 220, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_HOST].button},
6064 {"Highest rank", 1280, 46, 265, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_RANK].button},
6065 {"Team / wing-leader", 1281, 46, 283, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_LEADER].button},
6066 {"Any", 1282, 46, 300, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_ANY].button},
6067 {"Host", 1283, 46, 317, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_HOST].button},
6068 {"On", 1285, 545, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_VOICE_ON].button},
6069 {"Off", 1286, 604, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_VOICE_OFF].button},
6070 {"Host modifies ships", 1287, 437, 363, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_HOST_MODIFIES].button},
6071 {"Exit", 1417, 572, 418, UI_XSTR_COLOR_PINK, -1, &Multi_ho_buttons[0][MULTI_HO_ACCEPT].button},
6075 // not needed for FS1
6077 {"Highest rank", 1280, 62, 269, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_RANK].button},
6078 {"Team / wing-leader", 1281, 62, 297, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_LEADER].button},
6079 {"Any", 1282, 62, 325, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_ANY].button},
6080 {"Host", 1283, 62, 352, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_HOST].button},
6081 {"Highest rank", 1280, 62, 425, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_RANK].button},
6082 {"Team / wing-leader", 1281, 62, 452, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_LEADER].button},
6083 {"Any", 1282, 62, 480, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_ANY].button},
6084 {"Host", 1283, 62, 508, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_HOST].button},
6085 {"On", 1285, 877, 294, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_VOICE_ON].button},
6086 {"Off", 1286, 967, 293, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_VOICE_OFF].button},
6087 {"Host modifies ships", 1287, 869, 589, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_HOST_MODIFIES].button},
6088 {"Exit", 1417, 953, 672, UI_XSTR_COLOR_PINK, -1, &Multi_ho_buttons[1][MULTI_HO_ACCEPT].button},
6093 // radio button controls
6094 #define MULTI_HO_NUM_RADIO_GROUPS 3
6095 #define MULTI_HO_MSG_GROUP 0 // group dealing with squadmate messaging
6096 #define MULTI_HO_END_GROUP 1 // group dealing with ending the mission
6097 #define MULTI_HO_VOICE_GROUP 2 // group dealing with voice stuff
6098 int Multi_ho_radio_groups[MULTI_HO_NUM_RADIO_GROUPS] = { // currently selected button in the radio button group
6101 int Multi_ho_radio_info[MULTI_HO_NUM_RADIO_BUTTONS][3] = { // info related to each of the radio buttons themselves
6102 // { group #, value, button id# }
6103 {0, 0, 0}, // highest ranking players can do messaging
6104 {0, 1, 1}, // wing/team leaders can do messaging
6105 {0, 2, 2}, // any player can do messaging
6106 {0, 3, 3}, // only host can do messaging
6107 {1, 0, 4}, // highest rank and host can end the mission
6108 {1, 1, 5}, // team/wing leader can end the mission
6109 {1, 2, 6}, // any player can end the mission
6110 {1, 3, 7}, // only the host can end the mission
6111 {2, 0, 8}, // voice toggled on
6112 {2, 1, 9} // voice toggled off
6116 #define MULTI_HO_NUM_SLIDERS 3
6117 #define MULTI_HO_SLIDER_VOICE_QOS 0 // voice quality of sound
6118 #define MULTI_HO_SLIDER_VOICE_DUR 1 // max duration of voice recording
6119 #define MULTI_HO_SLIDER_SKILL 2 // skill level
6121 const char *filename;
6126 UI_DOT_SLIDER_NEW slider; // because we have a class inside this struct, we need the constructor below..
6128 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){}
6130 ho_sliders Multi_ho_sliders[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_SLIDERS] = {
6133 ho_sliders("MH_16", 396, 210, -1, -1, 16, 19, 10), // voice qos
6134 ho_sliders("MH_17", 396, 263, -1, -1, 17, 19, 10), // voice duration
6135 ho_sliders("MH_13", 10, 387, -1, -1, 13, 36, 5), // skill level
6137 ho_sliders("MH_11", 428, 214, 437, 199, 11, 19, 10), // voice qos
6138 ho_sliders("MH_12", 428, 261, 437, 246, 12, 19, 10), // voice duration
6139 ho_sliders("MH_08", 237, 454, 230, 411, 8, 36, 5), // skill level
6143 ho_sliders("2_MH_11", 684, 343, 690, 323, 11, 32, 10), // voice qos
6144 ho_sliders("2_MH_12", 685, 418, 837, 468, 12, 32, 10), // voice duration
6145 ho_sliders("2_MH_08", 379, 727, 369, 663, 8, 60, 5), // skill level
6149 int Multi_ho_mission_respawn;
6151 int Multi_ho_host_modifies;
6153 // whether or not any of the inputboxes on this screen had focus last frame
6154 int Multi_ho_lastframe_input = 0;
6156 // game information text areas
6160 #define MULTI_HO_NUM_TITLES 14
6162 UI_XSTR Multi_ho_titles[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_TITLES] = {
6164 { "AI Orders", 1289, 32, 144, UI_XSTR_COLOR_GREEN, -1, NULL },
6165 { "End Mission", 1290, 32, 242, UI_XSTR_COLOR_GREEN, -1, NULL },
6166 { "Time Limit", 1291, 32, 347, UI_XSTR_COLOR_GREEN, -1, NULL },
6167 { "Min", 1292, 74, 362, UI_XSTR_COLOR_GREEN, -1, NULL },
6168 { "Respawn Limit", 1288, 32, 378, UI_XSTR_COLOR_GREEN, -1, NULL },
6169 { "Kill Limit", 1293, 32, 409, UI_XSTR_COLOR_GREEN, -1, NULL },
6170 { "Observers", 1294, 32, 441, UI_XSTR_COLOR_GREEN, -1, NULL },
6171 { "Skill Level", 1284, 230, 411, UI_XSTR_COLOR_GREEN, -1, NULL },
6172 { "Voice Transmission", 1295, 437, 144, UI_XSTR_COLOR_GREEN, -1, NULL },
6173 { "Voice Quality", 1296, 437, 199, UI_XSTR_COLOR_GREEN, -1, NULL },
6174 { "Message Duration", 1297, 437, 246, UI_XSTR_COLOR_GREEN, -1, NULL },
6175 { "sec", 1522, 523, 292, UI_XSTR_COLOR_GREEN, -1, NULL },
6176 { "sec", 1523, 523, 332, UI_XSTR_COLOR_GREEN, -1, NULL },
6177 { "Voice Wait", 1298, 437, 313, UI_XSTR_COLOR_GREEN, -1, NULL },
6180 { "AI Orders", 1289, 48, 238, UI_XSTR_COLOR_GREEN, -1, NULL },
6181 { "End Mission", 1290, 48, 394, UI_XSTR_COLOR_GREEN, -1, NULL },
6182 { "Time Limit", 1291, 50, 568, UI_XSTR_COLOR_GREEN, -1, NULL },
6183 { "Min", 1292, 119, 581, UI_XSTR_COLOR_GREEN, -1, NULL },
6184 { "Respawn Limit", 1288, 50, 618, UI_XSTR_COLOR_GREEN, -1, NULL },
6185 { "Kill Limit", 1293, 50, 668, UI_XSTR_COLOR_GREEN, -1, NULL },
6186 { "Observers", 1294, 50, 718, UI_XSTR_COLOR_GREEN, -1, NULL },
6187 { "Skill Level", 1284, 398, 670, UI_XSTR_COLOR_GREEN, -1, NULL },
6188 { "Voice Transmission", 1295, 869, 239, UI_XSTR_COLOR_GREEN, -1, NULL },
6189 { "Voice Quality", 1296, 690, 331, UI_XSTR_COLOR_GREEN, -1, NULL },
6190 { "Message Duration", 1297, 690, 405, UI_XSTR_COLOR_GREEN, -1, NULL },
6191 { "sec", 1522, 837, 467, UI_XSTR_COLOR_GREEN, -1, NULL },
6192 { "sec", 1523, 837, 534, UI_XSTR_COLOR_GREEN, -1, NULL },
6193 { "Voice Wait", 1298, 742, 510, UI_XSTR_COLOR_GREEN, -1, NULL },
6198 // mission time limit input box
6199 int Ho_time_coords[GR_NUM_RESOLUTIONS][4] = {
6212 // furball kill limit input box
6213 int Ho_kill_coords[GR_NUM_RESOLUTIONS][4] = {
6226 // voice recording duration text display area
6227 int Ho_vd_coords[GR_NUM_RESOLUTIONS][4] = {
6240 // voice token wait input box
6241 int Ho_vw_coords[GR_NUM_RESOLUTIONS][6] = {
6254 // observer count input box
6255 int Ho_obs_coords[GR_NUM_RESOLUTIONS][4] = {
6268 // skill text description area
6269 int Ho_st_coords[GR_NUM_RESOLUTIONS][4] = {
6282 // respawn input box
6283 int Ho_rsp_coords[GR_NUM_RESOLUTIONS][6] = {
6296 // respawn max text area
6297 int Ho_max_rsp_coords[GR_NUM_RESOLUTIONS][2] = {
6310 // maximum values for various input boxes (to notify user of overruns)
6311 #define MULTI_HO_MAX_TIME_LIMIT 500
6312 #define MULTI_HO_MAX_TOKEN_WAIT 5
6313 #define MULTI_HO_MAX_KILL_LIMIT 9999
6314 #define MULTI_HO_MAX_OBS 4
6316 // LOCAL function definitions
6317 void multi_ho_check_buttons();
6318 void multi_ho_button_pressed(int n);
6319 void multi_ho_draw_radio_groups();
6320 void multi_ho_accept_hit();
6321 void multi_ho_get_options();
6322 void multi_ho_apply_options();
6323 void multi_ho_display_record_time();
6324 int multi_ho_check_values();
6325 void multi_ho_check_focus();
6326 void multi_ho_blit_max_respawns();
6327 void multi_ho_display_skill_level();
6329 void multi_host_options_init()
6333 // create the interface window
6334 Multi_ho_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
6335 Multi_ho_window.set_mask_bmap(Multi_ho_bitmap_mask_fname[gr_screen.res]);
6337 // load the background bitmap
6338 Multi_ho_bitmap = bm_load(Multi_ho_bitmap_fname[gr_screen.res]);
6339 if(Multi_ho_bitmap < 0){
6340 // we failed to load the bitmap - this is very bad
6344 // initialize the common notification messaging
6345 multi_common_notify_init();
6347 // use the common interface palette
6348 multi_common_set_palette();
6350 // create the interface buttons
6351 for(idx=0;idx<MULTI_HO_NUM_BUTTONS;idx++){
6352 // create the object
6353 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);
6355 // set the sound to play when highlighted
6356 Multi_ho_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
6358 // set the ani for the button
6359 Multi_ho_buttons[gr_screen.res][idx].button.set_bmaps(Multi_ho_buttons[gr_screen.res][idx].filename);
6361 // set the hotspot, ignoring the skill level button
6362 Multi_ho_buttons[gr_screen.res][idx].button.link_hotspot(Multi_ho_buttons[gr_screen.res][idx].hotspot);
6366 Multi_ho_window.add_XSTR(&Multi_ho_text[gr_screen.res][idx]);
6372 for(idx=0; idx<MULTI_HO_NUM_TITLES; idx++){
6373 Multi_ho_window.add_XSTR(&Multi_ho_titles[gr_screen.res][idx]);
6377 // create the interface sliders
6378 for(idx=0; idx<MULTI_HO_NUM_SLIDERS; idx++){
6379 // create the object
6380 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);
6383 // create the respawn count input box
6384 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);
6385 // if we're in campaign mode, disable it
6386 if(Netgame.campaign_mode == MP_CAMPAIGN){
6387 Multi_ho_respawns.set_text(XSTR("NA",795)); // [[ Not applicable ]]
6388 Multi_ho_respawns.disable();
6390 Multi_ho_respawns.set_valid_chars("0123456789");
6393 // create the time limit input box
6394 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);
6395 Multi_ho_time_limit.set_valid_chars("-0123456789");
6397 // create the voice token wait input box
6398 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);
6399 Multi_ho_voice_wait.set_valid_chars("01243456789");
6401 // create the furball kill limit input box
6402 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);
6403 Multi_ho_kill_limit.set_valid_chars("0123456789");
6405 // create the observer limit input box
6406 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);
6407 Multi_ho_obs.set_valid_chars("01234");
6409 // load in the current netgame defaults
6410 multi_ho_get_options();
6412 // whether or not any of the inputboxes on this screen had focus last frame
6413 Multi_ho_lastframe_input = 0;
6415 // get the # of respawns for the currently selected mission (if any)
6416 if(Multi_create_list_select != -1){
6417 int abs_index = multi_create_select_to_index(Multi_create_list_select);
6419 // if he has a valid mission selected
6421 Multi_ho_mission_respawn = (int)Multi_create_file_list[abs_index].respawn;
6423 Multi_ho_mission_respawn = -1;
6426 Multi_ho_mission_respawn = -1;
6430 void multi_ho_update_sliders()
6432 // game skill slider
6433 if (Game_skill_level != Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos) {
6434 if ( !(Netgame.type_flags & NG_TYPE_TEAM) ){
6435 Game_skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6436 gamesnd_play_iface(SND_USER_SELECT);
6438 Game_skill_level = NUM_SKILL_LEVELS / 2;
6442 // get the voice qos options
6443 if (Netgame.options.voice_qos != (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1)) {
6444 Netgame.options.voice_qos = (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1);
6445 gamesnd_play_iface(SND_USER_SELECT);
6448 // get the voice duration options
6449 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)) {
6450 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);
6451 gamesnd_play_iface(SND_USER_SELECT);
6456 void multi_host_options_do()
6461 k = Multi_ho_window.process();
6464 // process any keypresses
6467 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
6470 case KEY_CTRLED + SDLK_RETURN :
6471 gamesnd_play_iface(SND_COMMIT_PRESSED);
6472 multi_ho_accept_hit();
6476 // process any button clicks
6477 multi_ho_check_buttons();
6479 // update the sliders
6480 multi_ho_update_sliders();
6482 // make sure that the chatbox inputbox and any inputbox on this screen are mutually exclusive in terms of focus
6483 multi_ho_check_focus();
6485 // draw the background, etc
6487 GR_MAYBE_CLEAR_RES(Multi_ho_bitmap);
6488 if(Multi_ho_bitmap != -1){
6489 gr_set_bitmap(Multi_ho_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
6492 Multi_ho_window.draw();
6494 // draw all the radio buttons properly
6495 multi_ho_draw_radio_groups();
6497 // display any pending notification messages
6498 multi_common_notify_do();
6500 // display the voice record time settings
6501 multi_ho_display_record_time();
6503 // maybe display the max # of respawns next to the respawn input box
6504 multi_ho_blit_max_respawns();
6506 // blit the proper skill level
6507 multi_ho_display_skill_level();
6509 // blit the "host modifies button"
6510 if(Multi_ho_host_modifies){
6511 Multi_ho_buttons[gr_screen.res][MULTI_HO_HOST_MODIFIES].button.draw_forced(2);
6514 // process and show the chatbox thingie
6518 Multi_ho_window.draw_tooltip();
6520 // display the voice status indicator
6521 multi_common_voice_display_status();
6527 void multi_host_options_close()
6529 // unload any bitmaps
6530 if(!bm_unload(Multi_ho_bitmap)){
6531 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_ho_bitmap_fname[gr_screen.res]));
6534 // destroy the UI_WINDOW
6535 Multi_ho_window.destroy();
6538 void multi_ho_check_buttons()
6541 for(idx=0;idx<MULTI_HO_NUM_BUTTONS;idx++){
6542 // we only really need to check for one button pressed at a time, so we can break after
6544 if(Multi_ho_buttons[gr_screen.res][idx].button.pressed()){
6545 multi_ho_button_pressed(idx);
6551 void multi_ho_button_pressed(int n)
6553 int radio_index,idx;
6554 int x_pixel,y_pixel;
6556 // get the pixel position of the click
6557 Multi_ho_buttons[gr_screen.res][n].button.get_mouse_pos(&x_pixel,&y_pixel);
6560 // clicked on the accept button
6561 case MULTI_HO_ACCEPT:
6562 gamesnd_play_iface(SND_COMMIT_PRESSED);
6563 multi_ho_accept_hit();
6566 // clicked on the host/captains only modify button
6567 case MULTI_HO_HOST_MODIFIES:
6568 // toggle it on or off
6569 Multi_ho_host_modifies = !Multi_ho_host_modifies;
6570 gamesnd_play_iface(SND_USER_SELECT);
6574 // look through the radio buttons and see which one this corresponds to
6576 for(idx=0;idx<MULTI_HO_NUM_RADIO_BUTTONS;idx++){
6577 if(Multi_ho_radio_info[idx][2] == n){
6582 SDL_assert(radio_index != -1);
6584 // check to see if a radio button was pressed
6585 if(radio_index < MULTI_HO_NUM_RADIO_BUTTONS){
6586 // see if this value is already picked for this radio group
6587 if(Multi_ho_radio_groups[Multi_ho_radio_info[radio_index][0]] != Multi_ho_radio_info[radio_index][1]){
6588 gamesnd_play_iface(SND_USER_SELECT);
6589 Multi_ho_radio_groups[Multi_ho_radio_info[radio_index][0]] = Multi_ho_radio_info[radio_index][1];
6591 gamesnd_play_iface(SND_GENERAL_FAIL);
6596 void multi_ho_draw_radio_groups()
6600 // go through each item and draw it if it is the selected button in its respective group
6601 for(idx=0;idx<MULTI_HO_NUM_RADIO_BUTTONS;idx++){
6602 /// if this button is the currently selected one in its group
6603 if(Multi_ho_radio_info[idx][1] == Multi_ho_radio_groups[Multi_ho_radio_info[idx][0]]){
6604 Multi_ho_buttons[gr_screen.res][Multi_ho_radio_info[idx][2]].button.draw_forced(2);
6609 void multi_ho_accept_hit()
6613 // check the values in the input boxes
6614 if(!multi_ho_check_values()){
6618 // zero out the netgame flags
6621 // set default options
6622 Netgame.options.flags = (MSO_FLAG_INGAME_XFER | MSO_FLAG_ACCEPT_PIX);
6624 // set the squadmate messaging flags
6625 switch(Multi_ho_radio_groups[MULTI_HO_MSG_GROUP]){
6627 Netgame.options.squad_set = MSO_SQUAD_RANK;
6630 Netgame.options.squad_set = MSO_SQUAD_LEADER;
6633 Netgame.options.squad_set = MSO_SQUAD_ANY;
6636 Netgame.options.squad_set = MSO_SQUAD_HOST;
6642 // set the end mission flags
6643 switch(Multi_ho_radio_groups[MULTI_HO_END_GROUP]){
6645 Netgame.options.endgame_set = MSO_END_RANK;
6648 Netgame.options.endgame_set = MSO_END_LEADER;
6651 Netgame.options.endgame_set = MSO_END_ANY;
6654 Netgame.options.endgame_set = MSO_END_HOST;
6660 // set the voice toggle
6661 switch(Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP]){
6663 Netgame.options.flags &= ~(MSO_FLAG_NO_VOICE);
6666 Netgame.options.flags |= MSO_FLAG_NO_VOICE;
6672 // get the voice qos options
6673 Netgame.options.voice_qos = (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1);
6675 // get the voice duration options
6676 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);
6678 // set the skill level. If in team vs. team mode, preserve the old setting before saving
6679 // the pilot file. I'll bet that this doesn't work though because the pilot file gets
6680 // written in a bunch of locations....sigh.
6681 if ( !(Netgame.type_flags & NG_TYPE_TEAM) ){
6682 Game_skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6684 Game_skill_level = NUM_SKILL_LEVELS / 2;
6687 // set the netgame respawn count
6688 // maybe warn the user that respawns will not be used for a campaign mission
6689 if(Netgame.campaign_mode == MP_SINGLE){
6690 Multi_ho_respawns.get_text(resp_str);
6691 uint temp_respawn = (uint)atoi(resp_str);
6692 // if he currently has no mission selected, let the user set any # of respawns
6693 if((int)temp_respawn > Multi_ho_mission_respawn){
6694 if(Multi_ho_mission_respawn == -1){
6695 Netgame.respawn = temp_respawn;
6696 Netgame.options.respawn = temp_respawn;
6698 // this should have been taken care of by the interface code
6703 Netgame.options.respawn = temp_respawn;
6704 Netgame.respawn = temp_respawn;
6708 // get the mission time limit
6709 Multi_ho_time_limit.get_text(resp_str);
6710 int temp_time = atoi(resp_str);
6712 Netgame.options.mission_time_limit = fl2f(-1.0f);
6713 } else if(temp_time > MULTI_HO_MAX_TIME_LIMIT){
6716 Netgame.options.mission_time_limit = fl2f(60.0f * (float)temp_time);
6719 // get observer count options
6720 Multi_ho_obs.get_text(resp_str);
6721 int temp_obs = atoi(resp_str);
6722 if(temp_obs > MULTI_HO_MAX_OBS){
6725 Netgame.options.max_observers = (ubyte)temp_obs;
6727 // get the furball kill limit
6728 Multi_ho_kill_limit.get_text(resp_str);
6729 int temp_kills = atoi(resp_str);
6730 if(temp_kills > MULTI_HO_MAX_KILL_LIMIT){
6733 Netgame.options.kill_limit = temp_kills;
6735 // get the token wait limit
6736 Multi_ho_voice_wait.get_text(resp_str);
6737 int temp_wait = atoi(resp_str);
6738 if(temp_wait > MULTI_HO_MAX_TOKEN_WAIT){
6741 Netgame.options.voice_token_wait = (temp_wait * 1000);
6743 // set the netgame option
6744 Netgame.options.skill_level = (ubyte)Game_skill_level;
6746 // get whether we're in host/captains only modify mode
6747 Netgame.options.flags &= ~(MSO_FLAG_SS_LEADERS);
6748 if(Multi_ho_host_modifies){
6749 Netgame.options.flags |= MSO_FLAG_SS_LEADERS;
6752 // store these values locally
6753 memcpy(&Player->m_local_options,&Net_player->p_info.options,sizeof(multi_local_options));
6754 memcpy(&Player->m_server_options,&Netgame.options,sizeof(multi_server_options));
6755 write_pilot_file(Player);
6757 // apply any changes in settings (notify everyone of voice qos changes, etc)
6758 multi_ho_apply_options();
6760 // move back to the create game screen
6761 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
6764 void multi_ho_get_options()
6768 // set the squadmate messaging buttons
6769 switch(Netgame.options.squad_set){
6770 case MSO_SQUAD_RANK :
6771 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 0;
6773 case MSO_SQUAD_LEADER:
6774 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 1;
6777 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 2;
6779 case MSO_SQUAD_HOST:
6780 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 3;
6786 // set the mission end buttons
6787 switch(Netgame.options.endgame_set){
6789 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 0;
6791 case MSO_END_LEADER:
6792 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 1;
6795 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 2;
6798 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 3;
6804 // set the voice toggle buttons
6805 if(Netgame.options.flags & MSO_FLAG_NO_VOICE){
6806 Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP] = 1;
6808 Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP] = 0;
6811 // get the voice qos options
6812 SDL_assert((Netgame.options.voice_qos >= 1) && (Netgame.options.voice_qos <= 10));
6813 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos = (Netgame.options.voice_qos - 1);
6815 // get the voice duration options
6816 SDL_assert((Netgame.options.voice_record_time > 0) && (Netgame.options.voice_record_time <= MULTI_VOICE_MAX_TIME));
6817 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos = ((int)((float)Netgame.options.voice_record_time / 500.0f)) - 1;
6819 // get the current skill level
6820 SDL_assert((Game_skill_level >= 0) && (Game_skill_level < NUM_SKILL_LEVELS));
6821 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos = Game_skill_level;
6823 // get the # of observers
6824 memset(resp_str,0,10);
6825 SDL_snprintf(resp_str,sizeof(resp_str),"%d",Netgame.options.max_observers);
6826 Multi_ho_obs.set_text(resp_str);
6828 // set the respawn count
6829 if(Netgame.campaign_mode == MP_SINGLE){
6830 memset(resp_str,0,10);
6831 SDL_snprintf(resp_str,sizeof(resp_str),"%d",Netgame.respawn);
6832 Multi_ho_respawns.set_text(resp_str);
6835 // set the mission time limit
6836 memset(resp_str,0,10);
6837 float tl = f2fl(Netgame.options.mission_time_limit);
6838 SDL_snprintf(resp_str,sizeof(resp_str),"%d",(int)(tl / 60.0f));
6839 Multi_ho_time_limit.set_text(resp_str);
6841 // set the furball kill limit
6842 memset(resp_str,0,10);
6843 SDL_snprintf(resp_str,sizeof(resp_str),"%d",Netgame.options.kill_limit);
6844 Multi_ho_kill_limit.set_text(resp_str);
6846 // set the token wait time
6847 memset(resp_str,0,10);
6848 SDL_snprintf(resp_str,sizeof(resp_str),"%d",Netgame.options.voice_token_wait / 1000);
6849 Multi_ho_voice_wait.set_text(resp_str);
6851 // get whether we're in host/captains only modify mode
6852 if(Netgame.options.flags & MSO_FLAG_SS_LEADERS){
6853 Multi_ho_host_modifies = 1;
6855 Multi_ho_host_modifies = 0;
6859 void multi_ho_apply_options()
6861 // if the voice qos or duration has changed, apply the change
6862 multi_voice_maybe_update_vars(Netgame.options.voice_qos,Netgame.options.voice_record_time);
6864 // send an options update
6865 multi_options_update_netgame();
6868 // display the voice record time settings
6869 void multi_ho_display_record_time()
6872 int full_seconds, half_seconds;
6875 memset(time_str,0,30);
6878 full_seconds = (((Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 500) / 1000);
6880 // get the half-seconds
6881 half_seconds = ((((Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 500) % 1000) / 500) * 5;
6883 // format the string
6884 SDL_snprintf(time_str,sizeof(time_str),"%d.%d",full_seconds,half_seconds);
6885 gr_set_color_fast(&Color_bright);
6886 gr_string(Ho_vd_coords[gr_screen.res][MULTI_HO_X_COORD],Ho_vd_coords[gr_screen.res][MULTI_HO_Y_COORD],time_str);
6889 int multi_ho_check_values()
6893 memset(val_txt,0,255);
6895 // check against respawn settings
6896 if(Multi_ho_mission_respawn != -1){
6897 Multi_ho_respawns.get_text(val_txt);
6898 // if the value is invalid, let the user know
6899 if(atoi(val_txt) > Multi_ho_mission_respawn){
6900 memset(val_txt,0,255);
6901 SDL_snprintf(val_txt,sizeof(val_txt),XSTR("Warning\nRespawn count in greater than mission specified max (%d)",796),Multi_ho_mission_respawn);
6902 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6907 // check against mission time limit max
6908 Multi_ho_time_limit.get_text(val_txt);
6909 // if the value is invalid, force it to be valid
6910 if(atoi(val_txt) > MULTI_HO_MAX_TIME_LIMIT){
6911 memset(val_txt,0,255);
6912 SDL_snprintf(val_txt,sizeof(val_txt),XSTR("Warning\nMission time limit is greater than max allowed (%d)",797),MULTI_HO_MAX_TIME_LIMIT);
6913 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6917 // check against max observer limit
6918 Multi_ho_obs.get_text(val_txt);
6919 // if the value is invalid, force it to be valid
6920 if(atoi(val_txt) > MULTI_HO_MAX_OBS){
6921 memset(val_txt,0,255);
6922 SDL_snprintf(val_txt,sizeof(val_txt),XSTR("Warning\nObserver count is greater than max allowed (%d)",798),MULTI_HO_MAX_OBS);
6923 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6927 // check against furball kill limit
6928 Multi_ho_kill_limit.get_text(val_txt);
6929 // if the value is invalid, force it to be valid
6930 if(atoi(val_txt) > MULTI_HO_MAX_KILL_LIMIT){
6931 memset(val_txt,0,255);
6932 SDL_snprintf(val_txt,sizeof(val_txt),XSTR("Warning\nMission kill limit is greater than max allowed (%d)",799),MULTI_HO_MAX_KILL_LIMIT);
6933 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6937 // check against the token wait limit
6938 Multi_ho_voice_wait.get_text(val_txt);
6939 if(atoi(val_txt) > MULTI_HO_MAX_TOKEN_WAIT){
6940 memset(val_txt,0,255);
6941 SDL_snprintf(val_txt,sizeof(val_txt),XSTR("Warning\nvoice wait time is greater than max allowed (%d)",800),MULTI_HO_MAX_TOKEN_WAIT);
6942 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6946 // all values are valid
6950 void multi_ho_check_focus()
6952 // if an inputbox has been pressed (hit enter), lose its focus
6953 if (Multi_ho_respawns.pressed() || Multi_ho_time_limit.pressed() || Multi_ho_voice_wait.pressed() || Multi_ho_kill_limit.pressed() || Multi_ho_obs.pressed()) {
6954 Multi_ho_respawns.clear_focus();
6955 Multi_ho_time_limit.clear_focus();
6956 Multi_ho_voice_wait.clear_focus();
6957 Multi_ho_kill_limit.clear_focus();
6958 Multi_ho_obs.clear_focus();
6959 gamesnd_play_iface(SND_COMMIT_PRESSED);
6960 chatbox_set_focus();
6961 Multi_ho_lastframe_input = 0;
6963 } else if(!Multi_ho_lastframe_input) {
6964 // if we didn't have focus last frame
6965 if(Multi_ho_respawns.has_focus() || Multi_ho_time_limit.has_focus() || Multi_ho_kill_limit.has_focus() || Multi_ho_voice_wait.has_focus() ){
6966 chatbox_lose_focus();
6968 Multi_ho_lastframe_input = 1;
6971 // if we _did_ have focus last frame
6973 // if we no longer have focus on any of the input boxes, set the focus on the chatbox
6974 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()){
6975 chatbox_set_focus();
6977 // if the chatbox now has focus, clear all focus from our inputboxes
6978 else if (chatbox_has_focus()) {
6979 Multi_ho_respawns.clear_focus();
6980 Multi_ho_time_limit.clear_focus();
6981 Multi_ho_kill_limit.clear_focus();
6982 Multi_ho_voice_wait.clear_focus();
6984 Multi_ho_lastframe_input = 0;
6989 void multi_ho_blit_max_respawns()
6993 // if we're in campaign mode, do nothing
6994 if(Netgame.campaign_mode == MP_CAMPAIGN){
6998 // otherwise blit the max as specified by the current mission file
6999 SDL_snprintf(string,sizeof(string),"(%d)",Multi_ho_mission_respawn);
7000 gr_set_color_fast(&Color_normal);
7001 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);
7004 void multi_ho_display_skill_level()
7006 int skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
7009 SDL_assert((skill_level >= 0) && (skill_level < NUM_SKILL_LEVELS));
7010 if((skill_level < 0) || (skill_level >= NUM_SKILL_LEVELS)){
7014 gr_set_color_fast(&Color_bright);
7015 gr_string(Ho_st_coords[gr_screen.res][0], Ho_st_coords[gr_screen.res][1], Skill_level_names(skill_level, 1));
7018 // -------------------------------------------------------------------------------------------------------------
7020 // MULTIPLAYER JOIN SCREEN
7023 #define MULTI_JW_NUM_BUTTONS 8
7027 #define MULTI_JW_PALETTE "InterfacePalette"
7029 static const char *Multi_jw_bitmap_fname[GR_NUM_RESOLUTIONS] = {
7030 "MultiJoinWait", // GR_640
7031 "2_MultiJoinWait" // GR_1024
7034 static const char *Multi_jw_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
7035 "MultiJoinWait-M", // GR_640
7036 "2_MultiJoinWait-M" // GR_1024
7042 #define MJW_SCROLL_PLAYERS_UP 0
7043 #define MJW_SCROLL_PLAYERS_DOWN 1
7046 #define MJW_PILOT_INFO 4
7047 #define MJW_SCROLL_INFO_UP 5
7048 #define MJW_SCROLL_INFO_DOWN 6
7049 #define MJW_CANCEL 7
7051 UI_WINDOW Multi_jw_window; // the window object for the join screen
7052 int Multi_jw_bitmap; // the background bitmap
7054 // constants for coordinate lookup
7055 #define MJW_X_COORD 0
7056 #define MJW_Y_COORD 1
7057 #define MJW_W_COORD 2
7058 #define MJW_H_COORD 3
7060 ui_button_info Multi_jw_buttons[GR_NUM_RESOLUTIONS][MULTI_JW_NUM_BUTTONS] = {
7063 ui_button_info("MJW_00", 0, 50, -1, -1, 0),
7064 ui_button_info("MJW_01", 0, 87, -1, -1, 1),
7065 ui_button_info("MJW_02", 20, 219, -1, -1, 2),
7066 ui_button_info("MJW_03", 73, 219, -1, -1, 3),
7067 ui_button_info("MJW_09", 131, 213, -1, -1, 9),
7068 ui_button_info("MJW_05", 0, 398, -1, -1, 5),
7069 ui_button_info("MJW_06", 0, 435, -1, -1, 6),
7070 ui_button_info("MJW_04", 559, 411, -1, -1, 4),
7072 ui_button_info("MJW_00", 1, 24, -1, -1, 0),
7073 ui_button_info("MJW_01", 1, 66, -1, -1, 1),
7074 ui_button_info("MJW_02", 30, 244, 20, 272, 2),
7075 ui_button_info("MJW_03", 84, 244, 73, 272, 3),
7076 ui_button_info("MJW_04", 139, 242, 134, 272, 4),
7077 ui_button_info("MJW_05", 1, 406, -1, -1, 5),
7078 ui_button_info("MJW_06", 1, 447, -1, -1, 6),
7079 ui_button_info("MJW_07", 577, 428, 570, 414, 7),
7083 ui_button_info("2_MJW_00", 2, 38, -1, -1, 0),
7084 ui_button_info("2_MJW_01", 2, 106, -1, -1, 1),
7085 ui_button_info("2_MJW_02", 48, 390, 47, 435, 2),
7086 ui_button_info("2_MJW_03", 134, 390, 133, 435, 3),
7087 ui_button_info("2_MJW_04", 223, 388, 225, 435, 4),
7088 ui_button_info("2_MJW_05", 2, 649, -1, -1, 5),
7089 ui_button_info("2_MJW_06", 2, 715, -1, -1, 6),
7090 ui_button_info("2_MJW_07", 923, 685, 931, 667, 7),
7095 #define MULTI_JW_NUM_TEXT 7
7097 UI_XSTR Multi_jw_text[GR_NUM_RESOLUTIONS][MULTI_JW_NUM_TEXT] = {
7099 { "Team 1", 1308, 20, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_TEAM0].button },
7100 { "Team 2", 1309, 73, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_TEAM1].button },
7101 { "Pilot", 1310, 134, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_PILOT_INFO].button },
7102 { "Info", 1311, 134, 283, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_PILOT_INFO].button },
7103 { "Cancel", 387, 570, 414, UI_XSTR_COLOR_PINK, -1, &Multi_jw_buttons[0][MJW_CANCEL].button },
7104 { "Players", 1269, 38, 8, UI_XSTR_COLOR_GREEN, -1, NULL },
7105 { "Choose Team", 1312, 27, 231, UI_XSTR_COLOR_GREEN, -1, NULL },
7108 { "Team 1", 1308, 47, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_TEAM0].button },
7109 { "Team 2", 1309, 133, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_TEAM1].button },
7110 { "Pilot", 1310, 225, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_PILOT_INFO].button },
7111 { "Info", 1311, 225, 446, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_PILOT_INFO].button },
7112 { "Cancel", 387, 931, 667, UI_XSTR_COLOR_PINK, -1, &Multi_jw_buttons[1][MJW_CANCEL].button },
7113 { "Players", 1269, 165, 12, UI_XSTR_COLOR_GREEN, -1, NULL },
7114 { "Choose Team", 1312, 45, 373, UI_XSTR_COLOR_GREEN, -1, NULL },
7119 int Mjw_players_coords[GR_NUM_RESOLUTIONS][4] = {
7132 int Mjw_mission_name_coords[GR_NUM_RESOLUTIONS][2] = {
7141 // squad war checkbox
7142 UI_CHECKBOX Multi_jw_sw_checkbox;
7143 const char *Multi_jw_sw_checkbox_fname[GR_NUM_RESOLUTIONS] = {
7147 int Multi_jw_sw_checkbox_coords[GR_NUM_RESOLUTIONS][2] = {
7155 int Multi_jw_sw_checkbox_text[GR_NUM_RESOLUTIONS][2] = {
7165 // player list control thingie defs
7166 #define MULTI_JW_PLIST_MAX_DISPLAY 19
7167 int Multi_jw_plist_select_flag; // indicates whether we currently have a selected player
7168 short Multi_jw_plist_select_id; // id of the current selected player
7169 UI_BUTTON Multi_jw_plist_select_button; // for selecting a player
7171 int Multi_jw_should_show_popup = 0;
7173 // LOCAL function definitions
7174 void multi_jw_check_buttons();
7175 void multi_jw_button_pressed(int n);
7176 void multi_jw_do_netstuff();
7177 void multi_jw_scroll_players_up();
7178 void multi_jw_scroll_players_down();
7179 void multi_jw_plist_process();
7180 void multi_jw_plist_blit_normal();
7181 void multi_jw_plist_blit_team();
7182 short multi_jw_get_mouse_id();
7184 void multi_game_client_setup_init()
7188 // create the interface window
7189 Multi_jw_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
7190 Multi_jw_window.set_mask_bmap(Multi_jw_bitmap_mask_fname[gr_screen.res]);
7192 // load the background bitmap
7193 Multi_jw_bitmap = bm_load(Multi_jw_bitmap_fname[gr_screen.res]);
7194 if(Multi_jw_bitmap < 0){
7195 // we failed to load the bitmap - this is very bad
7199 // initialize the player list data
7200 Multi_jw_plist_select_flag = 0;
7201 Multi_jw_plist_select_id = -1;
7203 // kill any old instances of the chatbox and create a new one
7205 chatbox_create(CHATBOX_FLAG_BIG | CHATBOX_FLAG_DRAW_BOX | CHATBOX_FLAG_BUTTONS);
7207 // initialize the common notification messaging
7208 multi_common_notify_init();
7210 // initialize the common mission info display area.
7211 multi_common_set_text("");
7213 // use the common interface palette
7214 multi_common_set_palette();
7216 // create the interface buttons
7217 for(idx=0; idx<MULTI_JW_NUM_BUTTONS; idx++){
7218 // create the object
7219 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);
7221 // set the sound to play when highlighted
7222 Multi_jw_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
7224 // set the ani for the button
7225 Multi_jw_buttons[gr_screen.res][idx].button.set_bmaps(Multi_jw_buttons[gr_screen.res][idx].filename);
7228 Multi_jw_buttons[gr_screen.res][idx].button.link_hotspot(Multi_jw_buttons[gr_screen.res][idx].hotspot);
7231 // if this is a PXO game, enable the squadwar checkbox
7232 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);
7233 Multi_jw_sw_checkbox.set_bmaps(Multi_jw_sw_checkbox_fname[gr_screen.res], 6, 0);
7234 Multi_jw_sw_checkbox.disable();
7235 if(!MULTI_IS_TRACKER_GAME){
7236 Multi_jw_sw_checkbox.hide();
7241 for(idx=0; idx<MULTI_JW_NUM_TEXT; idx++){
7242 Multi_jw_window.add_XSTR(&Multi_jw_text[gr_screen.res][idx]);
7246 // create the player select list button and hide it
7247 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);
7248 Multi_jw_plist_select_button.hide();
7251 Multi_jw_buttons[gr_screen.res][MJW_CANCEL].button.set_hotkey(SDLK_ESCAPE);
7253 // remove campaign flags
7254 Game_mode &= ~(GM_CAMPAIGN_MODE);
7256 // tell the server we have finished joining
7257 Net_player->state = NETPLAYER_STATE_JOINED;
7258 send_netplayer_update_packet();
7261 ml_printf(NOX("Joined netgame %s"), Netgame.name);
7263 // send any appropriate files
7264 multi_data_send_my_junk();
7267 void multi_game_client_setup_do_frame()
7270 int k = chatbox_process();
7271 char mission_text[255];
7272 k = Multi_jw_window.process(k,0);
7274 Multi_jw_should_show_popup = 0;
7276 // process any button clicks
7277 multi_jw_check_buttons();
7279 // do any network related stuff
7280 multi_jw_do_netstuff();
7282 // draw the background, etc
7284 GR_MAYBE_CLEAR_RES(Multi_jw_bitmap);
7285 if(Multi_jw_bitmap != -1){
7286 gr_set_bitmap(Multi_jw_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7290 // if we're not in team vs. team mode, don't draw the team buttons
7291 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
7292 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.hide();
7293 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.hide();
7294 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.disable();
7295 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.disable();
7297 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.enable();
7298 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.enable();
7299 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.unhide();
7300 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.unhide();
7303 if(MULTI_IS_TRACKER_GAME){
7304 // maybe check the squadwar button
7305 if(Netgame.type_flags & NG_TYPE_SW){
7306 Multi_jw_sw_checkbox.set_state(1);
7307 gr_set_color_fast(&Color_bright);
7309 Multi_jw_sw_checkbox.set_state(0);
7310 gr_set_color_fast(&Color_normal);
7313 gr_string(Multi_jw_sw_checkbox_text[gr_screen.res][0], Multi_jw_sw_checkbox_text[gr_screen.res][1], "SquadWar");
7316 // draw the UI window
7317 Multi_jw_window.draw();
7319 // process and display the player list
7320 // NOTE : this must be done before the buttons are checked to insure that a player hasn't dropped
7321 multi_jw_plist_process();
7322 if(Netgame.type_flags & NG_TYPE_TEAM){
7323 multi_jw_plist_blit_team();
7325 multi_jw_plist_blit_normal();
7328 // display any text in the info area
7329 multi_common_render_text();
7331 // display any pending notification messages
7332 multi_common_notify_do();
7334 // blit the mission filename if possible
7335 if(Netgame.campaign_mode){
7336 if(strlen(Netgame.campaign_name) > 0){
7337 SDL_strlcpy(mission_text, Netgame.campaign_name, sizeof(mission_text));
7339 if(strlen(Netgame.title) > 0){
7340 SDL_strlcat(mission_text, ", ", sizeof(mission_text));
7341 SDL_strlcat(mission_text, Netgame.title, sizeof(mission_text));
7344 gr_set_color_fast(&Color_bright_white);
7345 gr_string(Mjw_mission_name_coords[gr_screen.res][MJW_X_COORD],Mjw_mission_name_coords[gr_screen.res][MJW_Y_COORD],mission_text);
7348 if(strlen(Netgame.mission_name) > 0){
7349 SDL_strlcpy(mission_text, Netgame.mission_name, sizeof(mission_text));
7351 if(strlen(Netgame.title) > 0){
7352 SDL_strlcat(mission_text, ", ", sizeof(mission_text));
7353 SDL_strlcat(mission_text, Netgame.title, sizeof(mission_text));
7356 gr_set_color_fast(&Color_bright_white);
7357 gr_string(Mjw_mission_name_coords[gr_screen.res][MJW_X_COORD],Mjw_mission_name_coords[gr_screen.res][MJW_Y_COORD],mission_text);
7361 // process and show the chatbox thingie
7365 Multi_jw_window.draw_tooltip();
7367 // display the voice status indicator
7368 multi_common_voice_display_status();
7373 // if we're supposed to be displaying a pilot info popup
7374 if(Multi_jw_should_show_popup){
7375 player_index = find_player_id(Multi_jw_plist_select_id);
7376 if(player_index != -1){
7377 multi_pinfo_popup(&Net_players[player_index]);
7382 void multi_game_client_setup_close()
7384 // unload any bitmaps
7385 if(!bm_unload(Multi_jw_bitmap)){
7386 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_jw_bitmap_fname[gr_screen.res]));
7389 // destroy the chatbox
7392 // destroy the UI_WINDOW
7393 Multi_jw_window.destroy();
7396 if(Netgame.game_state == NETGAME_STATE_MISSION_SYNC){
7397 gamesnd_play_iface(SND_COMMIT_PRESSED);
7402 void multi_jw_check_buttons()
7405 for(idx=0;idx<MULTI_JW_NUM_BUTTONS;idx++){
7406 // we only really need to check for one button pressed at a time, so we can break after
7408 if(Multi_jw_buttons[gr_screen.res][idx].button.pressed()){
7409 multi_jw_button_pressed(idx);
7415 void multi_jw_button_pressed(int n)
7419 gamesnd_play_iface(SND_USER_SELECT);
7420 multi_quit_game(PROMPT_CLIENT);
7422 case MJW_SCROLL_PLAYERS_UP:
7423 multi_jw_scroll_players_up();
7425 case MJW_SCROLL_PLAYERS_DOWN:
7426 multi_jw_scroll_players_down();
7428 case MJW_SCROLL_INFO_UP:
7429 multi_common_scroll_text_up();
7431 case MJW_SCROLL_INFO_DOWN:
7432 multi_common_scroll_text_down();
7435 // request to set myself to team 0
7437 gamesnd_play_iface(SND_USER_SELECT);
7438 multi_team_set_team(Net_player,0);
7441 // request to set myself to team 1
7443 gamesnd_play_iface(SND_USER_SELECT);
7444 multi_team_set_team(Net_player,1);
7448 case MJW_PILOT_INFO:
7449 Multi_jw_should_show_popup = 1;
7453 multi_common_add_notify(XSTR("Not implemented yet!",760));
7458 // do stuff like pinging servers, sending out requests, etc
7459 void multi_jw_do_netstuff()
7463 void multi_jw_scroll_players_up()
7465 gamesnd_play_iface(SND_GENERAL_FAIL);
7468 // scroll down through the player list
7469 void multi_jw_scroll_players_down()
7471 gamesnd_play_iface(SND_GENERAL_FAIL);
7474 void multi_jw_plist_process()
7476 int test_count,player_index,idx;
7478 // first determine if there are 0 players in the game. This should never happen since the host is _always_ in the game
7480 for(idx=0;idx<MAX_PLAYERS;idx++){
7481 // count anyone except the standalone server (if applicable)
7482 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7486 if(test_count <= 0){
7490 // if we had a selected item but that player has left, select myself instead
7491 if(Multi_jw_plist_select_flag){
7492 player_index = find_player_id(Multi_jw_plist_select_id);
7493 if(player_index == -1){
7494 Multi_jw_plist_select_id = Net_player->player_id;
7497 Multi_jw_plist_select_flag = 1;
7498 Multi_jw_plist_select_id = Net_player->player_id;
7501 // if the player has clicked somewhere in the player list area
7502 if(Multi_jw_plist_select_button.pressed()){
7506 player_id = multi_jw_get_mouse_id();
7507 player_index = find_player_id(player_id);
7508 if(player_index != -1){
7509 Multi_jw_plist_select_id = player_id;
7510 Multi_jw_plist_select_flag = 1;
7515 void multi_jw_plist_blit_normal()
7518 char str[CALLSIGN_LEN+1];
7519 int y_start = Mjw_players_coords[gr_screen.res][MJW_Y_COORD];
7522 // display all the players
7523 for(idx=0;idx<MAX_PLAYERS;idx++){
7524 // reset total offset
7527 // count anyone except the standalone server (if applicable)
7528 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7529 // highlight him if he's the host
7530 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7531 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7532 gr_set_color_fast(&Color_text_active_hi);
7534 gr_set_color_fast(&Color_bright);
7537 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7538 gr_set_color_fast(&Color_text_active);
7540 gr_set_color_fast(&Color_text_normal);
7544 // optionally draw his CD status
7545 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7546 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7547 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7549 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7552 // make sure the string will fit, then display it
7553 SDL_strlcpy(str, Net_players[idx].player->callsign, sizeof(str));
7554 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
7555 SDL_strlcat(str, "(0)", sizeof(str));
7557 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7558 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7565 void multi_jw_plist_blit_team()
7568 char str[CALLSIGN_LEN+1];
7569 int y_start = Mjw_players_coords[gr_screen.res][MJW_Y_COORD];
7572 // always blit the proper team button based on _my_ team status
7573 if(Net_player->p_info.team == 0){
7574 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.draw_forced(2);
7576 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.draw_forced(2);
7579 // display all the red players first
7580 for(idx=0;idx<MAX_PLAYERS;idx++){
7581 // reset total offset
7584 // count anyone except the standalone server (if applicable)
7585 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
7586 // highlight him if he's the host
7587 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7588 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7589 gr_set_color_fast(&Color_text_active_hi);
7591 gr_set_color_fast(&Color_bright);
7594 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7595 gr_set_color_fast(&Color_text_active);
7597 gr_set_color_fast(&Color_text_normal);
7601 // optionally draw his CD status
7602 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7603 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7604 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7606 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7609 // blit the red team indicator
7610 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
7611 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
7612 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7613 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7615 total_offset += Multi_common_icon_dims[MICON_TEAM0_SELECT][0] + 1;
7618 if(Multi_common_icons[MICON_TEAM0] != -1){
7619 gr_set_bitmap(Multi_common_icons[MICON_TEAM0], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7620 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7622 total_offset += Multi_common_icon_dims[MICON_TEAM0][0] + 1;
7626 // make sure the string will fit
7627 SDL_strlcpy(str, Net_players[idx].player->callsign, sizeof(str));
7628 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7630 // display him in the correct half of the list depending on his team
7631 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7636 // display all the green players next
7637 for(idx=0;idx<MAX_PLAYERS;idx++){
7638 // reset total offset
7641 // count anyone except the standalone server (if applicable)
7642 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
7643 // highlight him if he's the host
7644 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7645 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7646 gr_set_color_fast(&Color_text_active_hi);
7648 gr_set_color_fast(&Color_bright);
7651 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7652 gr_set_color_fast(&Color_text_active);
7654 gr_set_color_fast(&Color_text_normal);
7658 // optionally draw his CD status
7659 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7660 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7661 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7663 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7666 // blit the red team indicator
7667 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
7668 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
7669 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7670 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7672 total_offset += Multi_common_icon_dims[MICON_TEAM1_SELECT][0] + 1;
7675 if(Multi_common_icons[MICON_TEAM1] != -1){
7676 gr_set_bitmap(Multi_common_icons[MICON_TEAM1], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7677 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7679 total_offset += Multi_common_icon_dims[MICON_TEAM1][0] + 1;
7683 // make sure the string will fit
7684 SDL_strlcpy(str, Net_players[idx].player->callsign, sizeof(str));
7685 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
7686 SDL_strlcat(str, "(0)", sizeof(str));
7688 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7690 // display him in the correct half of the list depending on his team
7691 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7697 void multi_jw_handle_join(net_player *pl)
7699 // for now just play a bloop sound
7700 gamesnd_play_iface(SND_ICON_DROP_ON_WING);
7703 short multi_jw_get_mouse_id()
7705 // determine where he clicked (y pixel value)
7707 Multi_jw_plist_select_button.get_mouse_pos(NULL,&y);
7709 // select things a little differently if we're in team vs. team or non-team vs. team mode
7711 if(Netgame.type_flags & NG_TYPE_TEAM){
7712 int player_index = -1;
7714 // look through all of team red first
7715 for(idx=0;idx<MAX_PLAYERS;idx++){
7716 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
7719 // if this is the _nth_ guy
7727 // if we still haven't found him yet, look through the green team
7728 if(player_index == -1){
7729 for(idx=0;idx<MAX_PLAYERS;idx++){
7730 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
7732 // if this is the _nth_ guy
7740 if(player_index != -1){
7741 return Net_players[idx].player_id;
7744 // select the nth active player if possible, disregarding the standalone server
7745 for(idx=0;idx<MAX_PLAYERS;idx++){
7746 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7749 // if this is the _nth_ guy
7751 return Net_players[idx].player_id;
7762 // -------------------------------------------------------------------------------------------------------------
7764 // MULTIPLAYER WAIT/SYNCH SCREEN
7767 #define MULTI_SYNC_HOST_COUNT 4 // host uses 4 buttons (and sometimes 5)
7768 #define MULTI_SYNC_CLIENT_COUNT 3 // client only uses 3 buttons
7770 const char *Multi_sync_bitmap_fname[GR_NUM_RESOLUTIONS] = {
7771 "MultiSynch", // GR_640
7772 "2_MultiSynch" // GR_1024
7775 const char *Multi_sync_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
7776 "MultiSynch-M", // GR_640
7777 "2_MultiSynch-M" // GR_1024
7782 // constants for coordinate lookup
7783 #define MS_X_COORD 0
7784 #define MS_Y_COORD 1
7785 #define MS_W_COORD 2
7786 #define MS_H_COORD 3
7788 UI_WINDOW Multi_sync_window; // the window object for the join screen
7789 int Multi_sync_button_count; // the # of buttons to use for this instance of mission sync
7790 int Multi_sync_bitmap; // the background bitmap
7793 #define MULTI_SYNC_NUM_BUTTONS 5
7794 #define MS_SCROLL_INFO_UP 0
7795 #define MS_SCROLL_INFO_DOWN 1
7799 ui_button_info Multi_sync_buttons[GR_NUM_RESOLUTIONS][MULTI_SYNC_NUM_BUTTONS] = {
7802 ui_button_info("MS_00", 0, 396, -1, -1, 0),
7803 ui_button_info("MS_01", 0, 435, -1, -1, 1),
7804 ui_button_info("MS_02", 496, 411, -1, -1, 2),
7805 ui_button_info("MS_04", 436, 403, -1, -1, 4),
7806 ui_button_info("MS_03", 556, 398, -1, -1, 3),
7808 ui_button_info("MS_00", 1, 404, -1, -1, 0),
7809 ui_button_info("MS_01", 1, 446, -1, -1, 1),
7810 ui_button_info("MS_03", 518, 426, 519, 416, 3),
7811 ui_button_info("MS_02", 469, 426, 479, 416, 2),
7812 ui_button_info("MS_04", 571, 420, 577, 416, 4),
7816 ui_button_info("2_MS_00", 2, 647, -1, -1, 0),
7817 ui_button_info("2_MS_01", 2, 713, -1, -1, 1),
7818 ui_button_info("2_MS_03", 829, 682, 831, 667, 3),
7819 ui_button_info("2_MS_02", 751, 682, 766, 667, 2),
7820 ui_button_info("2_MS_04", 914, 672, 924, 667, 4),
7826 #define MULTI_SYNC_NUM_TEXT 5
7828 #define MST_LAUNCH 2
7829 UI_XSTR Multi_sync_text[GR_NUM_RESOLUTIONS][MULTI_SYNC_NUM_TEXT] = {
7831 { "Kick", 1266, 479, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_KICK].button },
7832 { "Cancel", 387, 519, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_CANCEL].button },
7833 { "Launch", 801, 577, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_LAUNCH].button },
7834 { "Players", 1269, 23, 133, UI_XSTR_COLOR_GREEN, -1, NULL },
7835 { "Status", 1304, 228, 133, UI_XSTR_COLOR_GREEN, -1, NULL }
7838 { "Kick", 1266, 766, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_KICK].button },
7839 { "Cancel", 387, 831, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_CANCEL].button },
7840 { "Launch", 801, 924, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_LAUNCH].button },
7841 { "Players", 1269, 38, 214, UI_XSTR_COLOR_GREEN, -1, NULL },
7842 { "Status", 1304, 366, 214, UI_XSTR_COLOR_GREEN, -1, NULL }
7848 int Ms_status_coords[GR_NUM_RESOLUTIONS][4] = {
7857 // player status coords
7858 int Ms_status2_coords[GR_NUM_RESOLUTIONS][4] = {
7867 int Ms_cd_icon_offset[GR_NUM_RESOLUTIONS] = {
7872 int Ms_team_icon_offset[GR_NUM_RESOLUTIONS] = {
7877 // player currently selected, index into Net_players[]
7878 int Multi_sync_player_select = -1;
7880 // player list control thingie defs
7881 #define MULTI_SYNC_PLIST_MAX_DISPLAY 15
7882 int Multi_sync_plist_start; // where to start displaying from
7883 int Multi_sync_plist_count; // how many we have
7885 // list select button
7886 UI_BUTTON Multi_sync_plist_button;
7888 int Multi_sync_mode = -1;
7890 #define MULTI_SYNC_COUNTDOWN_TIME 5 // in seconds
7891 float Multi_sync_countdown_timer;
7892 int Multi_sync_countdown = -1;
7894 int Multi_launch_button_created;
7897 // countdown animation timer
7898 const char* Multi_sync_countdown_fname[GR_NUM_RESOLUTIONS] = {
7900 "2_Count" // GR_1024
7903 int Multi_sync_countdown_coords[GR_NUM_RESOLUTIONS][2] = {
7914 anim *Multi_sync_countdown_anim = NULL;
7915 anim_instance *Multi_sync_countdown_instance = NULL;
7918 // PREBRIEFING STUFF
7919 // syncing flags used by the server
7920 int Mission_sync_flags = 0;
7921 #define MS_FLAG_SENT_FILESIG (1<<0) // sent filesig requests
7922 #define MS_FLAG_SENT_LOAD (1<<1) // sent load packets
7923 #define MS_FLAG_PUSHED_BRIEFING (1<<2) // pushed everyone else into the briefing
7924 #define MS_FLAG_POST_DATA (1<<3) // sent the post data block
7925 #define MS_FLAG_WSS_SLOTS (1<<4) // all players have received wss slot data
7926 #define MS_FLAG_PSETTINGS (1<<5) // send the player settings packet
7927 #define MS_FLAG_MT_STATS_START (1<<6) // server has started getting player stats from the tracker
7928 #define MS_FLAG_MT_STATS_DONE (1<<7) // server has finished getting player stats from the tracker (success or fail)
7929 #define MS_FLAG_TS_SLOTS (1<<8) // team/ship slots have been sent
7930 #define MS_FLAG_DATA_DONE (1<<9) // done transferring all necessary data
7931 #define MS_FLAG_CAMP_DONE (1<<10) // send campaign pool/goal/event stuff
7933 // POSTBRIEFING STUFF
7934 int Multi_state_timestamp;
7935 int Multi_sync_launch_pressed;
7937 // LOCAL function definitions
7938 void multi_sync_check_buttons();
7939 void multi_sync_button_pressed(int n);
7940 void multi_sync_scroll_info_up();
7941 void multi_sync_scroll_info_down();
7942 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
7943 void multi_sync_display_status(const char *status, int index); // display info on the right hand portion of the status window thingie
7944 void multi_sync_force_start_pre();
7945 void multi_sync_force_start_post();
7946 void multi_sync_launch();
7947 void multi_sync_create_launch_button();
7948 void multi_sync_blit_screen_all();
7949 void multi_sync_handle_plist();
7951 void multi_sync_common_init();
7952 void multi_sync_common_do();
7953 void multi_sync_common_close();
7955 void multi_sync_pre_init();
7956 void multi_sync_pre_do();
7957 void multi_sync_pre_close();
7959 void multi_sync_post_init();
7960 void multi_sync_post_do();
7961 void multi_sync_post_close();
7966 // perform the correct init functions
7967 void multi_sync_init()
7969 Multi_sync_countdown = -1;
7973 // reset all timestamp
7974 multi_reset_timestamps();
7976 extern time_t Player_multi_died_check;
7977 Player_multi_died_check = -1;
7979 if(!(Game_mode & GM_STANDALONE_SERVER)){
7980 multi_sync_common_init();
7983 switch(Multi_sync_mode){
7984 case MULTI_SYNC_PRE_BRIEFING:
7985 multi_sync_pre_init();
7987 case MULTI_SYNC_POST_BRIEFING:
7988 multi_sync_post_init();
7990 case MULTI_SYNC_INGAME:
7991 multi_ingame_sync_init();
7996 // perform the correct do frame functions
7997 void multi_sync_do()
7999 if(!(Game_mode & GM_STANDALONE_SERVER)){
8000 multi_sync_common_do();
8003 // if the netgame is ending, don't do any sync processing
8004 if(multi_endgame_ending()){
8008 // process appropriateliy
8009 switch(Multi_sync_mode){
8010 case MULTI_SYNC_PRE_BRIEFING:
8011 multi_sync_pre_do();
8013 case MULTI_SYNC_POST_BRIEFING:
8014 multi_sync_post_do();
8016 case MULTI_SYNC_INGAME:
8017 multi_ingame_sync_do();
8020 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8021 if(Multi_sync_bitmap != -1){
8022 gr_set_bitmap(Multi_sync_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8025 Multi_sync_window.draw();
8027 multi_sync_blit_screen_all();
8034 // perform the correct close functions
8035 void multi_sync_close()
8037 switch(Multi_sync_mode){
8038 case MULTI_SYNC_PRE_BRIEFING:
8039 multi_sync_pre_close();
8041 case MULTI_SYNC_POST_BRIEFING:
8042 multi_sync_post_close();
8044 case MULTI_SYNC_INGAME:
8045 multi_ingame_sync_close();
8049 if(!(Game_mode & GM_STANDALONE_SERVER)){
8050 multi_sync_common_close();
8054 const char *multi_sync_tooltip_handler(const char *str)
8056 if (!SDL_strcasecmp(str, NOX("@launch"))) {
8057 if (Multi_launch_button_created){
8058 return XSTR("Launch",801);
8065 void multi_sync_common_init()
8069 // create the interface window
8070 Multi_sync_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
8071 Multi_sync_window.set_mask_bmap(Multi_sync_bitmap_mask_fname[gr_screen.res]);
8072 Multi_sync_window.tooltip_handler = multi_sync_tooltip_handler;
8074 // load the background bitmap
8075 Multi_sync_bitmap = bm_load(Multi_sync_bitmap_fname[gr_screen.res]);
8076 if (Multi_sync_bitmap < 0) {
8077 // we failed to load the bitmap - this is very bad
8081 // initialize the player list data
8082 Multi_sync_plist_start = 0;
8083 Multi_sync_plist_count = 1; // we can pretty safely assume that there's one player in the game - me.
8085 Multi_launch_button_created = 0;
8087 // create the chatbox thingie (shouldn't be necesary to do this, but we'll put it in for good measure)
8090 // force the chatbox to be small
8091 chatbox_force_small();
8093 // initialize the common notification messaging
8094 multi_common_notify_init();
8096 // initialize the common mission info display area.
8097 multi_common_set_text("");
8099 // use the common interface palette
8100 multi_common_set_palette();
8102 // don't select any player yet.
8103 Multi_sync_player_select = -1;
8105 // determine how many of the 5 buttons to create
8106 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8107 Multi_sync_button_count = MULTI_SYNC_HOST_COUNT;
8109 Multi_sync_button_count = MULTI_SYNC_CLIENT_COUNT;
8111 // create the interface buttons
8112 for(idx=0; idx<Multi_sync_button_count; idx++){
8113 // create the object
8114 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);
8116 // set the sound to play when highlighted
8117 Multi_sync_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
8119 // set the ani for the button
8120 // this wierdness is necessary because cancel and kick buttons aren't drawn on the background bitmap,
8121 // so we have to load in frame 0, too (the file should exist)
8122 if ((idx == MS_CANCEL) || (idx == MS_KICK) || (idx == MS_LAUNCH)) {
8123 Multi_sync_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sync_buttons[gr_screen.res][idx].filename, 3, 0);
8125 Multi_sync_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sync_buttons[gr_screen.res][idx].filename);
8129 Multi_sync_buttons[gr_screen.res][idx].button.link_hotspot(Multi_sync_buttons[gr_screen.res][idx].hotspot);
8134 for(idx=0; idx<MULTI_SYNC_NUM_TEXT; idx++) {
8135 // don't create the "launch" button text just yet
8136 if(idx == MST_LAUNCH) {
8139 // multiplayer clients should ignore the kick button
8140 if(!MULTIPLAYER_MASTER && !MULTIPLAYER_HOST && (idx == MST_KICK)) {
8144 Multi_sync_window.add_XSTR(&Multi_sync_text[gr_screen.res][idx]);
8148 // create the player list select button and hide it
8149 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);
8150 Multi_sync_plist_button.hide();
8152 // set up hotkeys for certain common functions
8153 Multi_sync_buttons[gr_screen.res][MS_CANCEL].button.set_hotkey(SDLK_ESCAPE);
8156 void multi_sync_common_do()
8158 int k = chatbox_process();
8159 k = Multi_sync_window.process(k);
8161 // process the player list
8162 multi_sync_handle_plist();
8164 // process any button clicks
8165 multi_sync_check_buttons();
8167 // process any keypresses
8171 gamesnd_play_iface(SND_USER_SELECT);
8172 multi_quit_game(PROMPT_ALL);
8177 void multi_sync_common_close()
8179 // unload any bitmaps
8180 if(!bm_unload(Multi_sync_bitmap)){
8181 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_sync_bitmap_fname[gr_screen.res]));
8184 extern time_t Player_multi_died_check;
8185 Player_multi_died_check = -1;
8187 // destroy the UI_WINDOW
8188 Multi_sync_window.destroy();
8191 void multi_sync_blit_screen_all()
8198 // display any text in the info area
8199 multi_common_render_text();
8201 // display any pending notification messages
8202 multi_common_notify_do();
8204 // display any info about visible players
8206 for(idx=0;idx<MAX_PLAYERS;idx++){
8207 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
8208 // display his name and status
8209 multi_sync_display_name(Net_players[idx].player->callsign,count,idx);
8211 // get the player state
8212 state = Net_players[idx].state;
8214 // if we're ingame joining, show all other players except myself as "playing"
8215 if((Net_player != NULL) && (&Net_players[idx] != Net_player) && ((Multi_sync_mode == MULTI_SYNC_INGAME) || (Net_player->flags & NETINFO_FLAG_INGAME_JOIN)) ){
8216 state = NETPLAYER_STATE_IN_MISSION;
8220 case NETPLAYER_STATE_MISSION_LOADING:
8221 multi_sync_display_status(XSTR("Mission Loading",802),count);
8223 case NETPLAYER_STATE_INGAME_SHIP_SELECT: // I don't think its possible to see this state, but...
8224 multi_sync_display_status(XSTR("Ingame Ship Select",803),count);
8226 case NETPLAYER_STATE_DEBRIEF:
8227 multi_sync_display_status(XSTR("Debriefing",804),count);
8229 case NETPLAYER_STATE_MISSION_SYNC:
8230 multi_sync_display_status(XSTR("Mission Sync",805),count);
8232 case NETPLAYER_STATE_JOINING:
8233 multi_sync_display_status(XSTR("Joining",806),count);
8235 case NETPLAYER_STATE_JOINED:
8236 multi_sync_display_status(XSTR("Joined",807),count);
8238 case NETPLAYER_STATE_SLOT_ACK :
8239 multi_sync_display_status(XSTR("Slot Ack",808),count);
8241 case NETPLAYER_STATE_BRIEFING:
8242 multi_sync_display_status(XSTR("Briefing",765),count);
8244 case NETPLAYER_STATE_SHIP_SELECT:
8245 multi_sync_display_status(XSTR("Ship Select",809),count);
8247 case NETPLAYER_STATE_WEAPON_SELECT:
8248 multi_sync_display_status(XSTR("Weapon Select",810),count);
8250 case NETPLAYER_STATE_WAITING:
8251 multi_sync_display_status(XSTR("Waiting",811),count);
8253 case NETPLAYER_STATE_IN_MISSION:
8254 multi_sync_display_status(XSTR("In Mission",812),count);
8256 case NETPLAYER_STATE_MISSION_LOADED:
8257 multi_sync_display_status(XSTR("Mission Loaded",813),count);
8259 case NETPLAYER_STATE_DATA_LOAD:
8260 multi_sync_display_status(XSTR("Data loading",814),count);
8262 case NETPLAYER_STATE_SETTINGS_ACK:
8263 multi_sync_display_status(XSTR("Ready To Enter Mission",815),count);
8265 case NETPLAYER_STATE_INGAME_SHIPS:
8266 multi_sync_display_status(XSTR("Ingame Ships Packet Ack",816),count);
8268 case NETPLAYER_STATE_INGAME_WINGS:
8269 multi_sync_display_status(XSTR("Ingame Wings Packet Ack",817),count);
8271 case NETPLAYER_STATE_INGAME_RPTS:
8272 multi_sync_display_status(XSTR("Ingame Respawn Points Ack",818),count);
8274 case NETPLAYER_STATE_SLOTS_ACK:
8275 multi_sync_display_status(XSTR("Ingame Weapon Slots Ack",819),count);
8277 case NETPLAYER_STATE_POST_DATA_ACK:
8278 multi_sync_display_status(XSTR("Post Briefing Data Block Ack",820),count);
8280 case NETPLAYER_STATE_FLAG_ACK :
8281 multi_sync_display_status(XSTR("Flags Ack",821),count);
8283 case NETPLAYER_STATE_MT_STATS :
8284 multi_sync_display_status(XSTR("Parallax Online Stats Updating",822),count);
8286 case NETPLAYER_STATE_WSS_ACK :
8287 multi_sync_display_status(XSTR("Weapon Slots Ack",823),count);
8289 case NETPLAYER_STATE_HOST_SETUP :
8290 multi_sync_display_status(XSTR("Host setup",824),count);
8292 case NETPLAYER_STATE_DEBRIEF_ACCEPT:
8293 multi_sync_display_status(XSTR("Debrief accept",825),count);
8295 case NETPLAYER_STATE_DEBRIEF_REPLAY:
8296 multi_sync_display_status(XSTR("Debrief replay",826),count);
8298 case NETPLAYER_STATE_CPOOL_ACK:
8299 multi_sync_display_status(XSTR("Campaign ship/weapon ack",827),count);
8301 case NETPLAYER_STATE_MISSION_XFER :
8303 // server should display the pct completion of all clients
8304 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8305 if(Net_players[idx].s_info.xfer_handle != -1){
8306 pct_complete = multi_xfer_pct_complete(Net_players[idx].s_info.xfer_handle);
8308 // if we've got a valid xfer handle
8309 if((pct_complete >= 0.0) && (pct_complete <= 1.0)){
8310 SDL_snprintf(txt,sizeof(txt),XSTR("Mission file xfer %d%%",828),(int)(pct_complete * 100.0f));
8314 SDL_strlcpy(txt, XSTR("Mission file xfer", 829), sizeof(txt));
8317 SDL_strlcpy(txt, XSTR("Mission file xfer", 829), sizeof(txt));
8320 // clients should display only for themselves (which is the only thing they know)
8322 // if we've got a valid file xfer handle
8323 if((&Net_players[idx] == Net_player) && (Net_player->s_info.xfer_handle != -1)){
8324 pct_complete = multi_xfer_pct_complete(Net_player->s_info.xfer_handle);
8326 // if we've got a valid xfer handle
8327 if((pct_complete >= 0.0) && (pct_complete <= 1.0)){
8328 SDL_snprintf(txt,sizeof(txt),XSTR("Mission file xfer %d%%",828),(int)(pct_complete * 100.0f));
8332 SDL_strlcpy(txt, XSTR("Mission file xfer", 829), sizeof(txt));
8337 SDL_strlcpy(txt, XSTR("Mission file xfer", 829), sizeof(txt));
8342 multi_sync_display_status(txt,count);
8345 nprintf(("Network","Unhandled player state : %d !\n",Net_players[idx].state));
8352 // display the mission start countdown timer (if any)
8353 anim_render_all(GS_STATE_MULTI_MISSION_SYNC,flFrametime);
8355 // process and show the chatbox thingie
8359 Multi_sync_window.draw_tooltip();
8361 // display the voice status indicator
8362 multi_common_voice_display_status();
8365 void multi_sync_check_buttons()
8368 for(idx=0;idx<Multi_sync_button_count;idx++){
8369 // we only really need to check for one button pressed at a time, so we can break after
8371 if(Multi_sync_buttons[gr_screen.res][idx].button.pressed()){
8372 multi_sync_button_pressed(idx);
8378 void multi_sync_button_pressed(int n)
8383 gamesnd_play_iface(SND_USER_SELECT);
8384 multi_quit_game(PROMPT_ALL);
8387 // scroll the info box up
8388 case MS_SCROLL_INFO_UP:
8389 multi_common_scroll_text_up();
8392 // scroll the info box down
8393 case MS_SCROLL_INFO_DOWN:
8394 multi_common_scroll_text_down();
8399 // if we have a currently selected player, kick him
8400 if(Multi_sync_player_select >= 0){
8401 multi_kick_player(Multi_sync_player_select);
8405 // start the final launch countdown (post-sync only)
8407 multi_sync_start_countdown();
8410 // doesn't do anything
8416 void multi_sync_pre_init()
8420 Netgame.game_state = NETGAME_STATE_MISSION_SYNC;
8422 // if we're in teamplay mode, always force skill level to be medium
8423 if((Netgame.type_flags & NG_TYPE_TEAM) && (Net_player->flags & NETINFO_FLAG_GAME_HOST)){
8424 Netgame.options.skill_level = NUM_SKILL_LEVELS / 2;
8425 Game_skill_level = NUM_SKILL_LEVELS / 2;
8426 multi_options_update_netgame();
8429 // notify everyone of when we get here
8430 if(!(Game_mode & GM_STANDALONE_SERVER)){
8431 Net_player->state = NETPLAYER_STATE_MISSION_SYNC;
8432 send_netplayer_update_packet();
8435 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8437 ml_string(NOX("Server performing pre-briefing data sync"));
8439 if(!(Game_mode & GM_STANDALONE_SERVER)){
8440 multi_common_set_text(XSTR("Server performing sync\n",830),1);
8443 // maybe initialize tvt and squad war stuff
8444 if(Netgame.type_flags & NG_TYPE_TEAM){
8445 multi_team_level_init();
8448 // force everyone into this state
8449 send_netgame_update_packet();
8451 if(!(Game_mode & GM_STANDALONE_SERVER)){
8452 multi_common_add_text(XSTR("Send update packet\n",831),1);
8455 // setup some of my own data
8456 Net_player->flags |= NETINFO_FLAG_MISSION_OK;
8458 // do any output stuff
8459 if(Game_mode & GM_STANDALONE_SERVER){
8460 std_debug_set_standalone_state_string("Mission Sync");
8463 // do this here to insure we have the most up to date file checksum info
8464 multi_get_mission_checksum(Game_current_mission_filename);
8465 // parse_get_file_signature(Game_current_mission_filename);
8467 if(!(Game_mode & GM_STANDALONE_SERVER)){
8468 multi_common_add_text(XSTR("Got file signatures\n",832),1);
8471 if(!(Game_mode & GM_STANDALONE_SERVER)){
8472 multi_common_add_text(XSTR("Sending update state packet\n",833),1);
8476 // if we're not in team vs. team mode - set all player teams to be 0, and unset all captaincy bits
8477 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
8478 for(idx=0;idx<MAX_PLAYERS;idx++){
8479 Net_players[idx].p_info.team = 0;
8480 Net_players[idx].flags &= ~(NETINFO_FLAG_TEAM_CAPTAIN);
8484 // we aren't necessarily xferring the mission file yet
8485 SDL_assert(Net_player->s_info.xfer_handle == -1);
8487 // always call this for good measure
8488 multi_campaign_flush_data();
8490 Mission_sync_flags = 0;
8491 Multi_mission_loaded = 0;
8494 void multi_sync_pre_do()
8498 // If I'm the server, wait for everyone to arrive in this state, then begin transferring data, etc.
8499 // all servers (standalone or no, go through this)
8500 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8501 // wait for everyone to arrive, then request filesig from all of them
8502 if(multi_netplayer_state_check(NETPLAYER_STATE_MISSION_SYNC) && !(Mission_sync_flags & MS_FLAG_SENT_FILESIG) && !multi_endgame_ending()){
8503 send_file_sig_request(Netgame.mission_name);
8504 Mission_sync_flags |= MS_FLAG_SENT_FILESIG;
8506 if(!(Game_mode & GM_STANDALONE_SERVER)){
8507 multi_common_add_text(XSTR("Sent filesig request\n",834),1);
8511 // if we're waiting for players to receive files, then check on their status
8512 if((Mission_sync_flags & MS_FLAG_SENT_FILESIG) && !multi_netplayer_flag_check(NETINFO_FLAG_MISSION_OK) && !multi_endgame_ending()){
8513 for(idx=0;idx<MAX_PLAYERS;idx++){
8514 // if this player is in the process of xferring a file
8515 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].s_info.xfer_handle != -1)){
8516 switch(multi_xfer_get_status(Net_players[idx].s_info.xfer_handle)){
8517 // if it has successfully completed, set his ok flag
8518 case MULTI_XFER_SUCCESS :
8520 Net_players[idx].flags |= NETINFO_FLAG_MISSION_OK;
8522 // release the xfer instance handle
8523 multi_xfer_release_handle(Net_players[idx].s_info.xfer_handle);
8524 Net_players[idx].s_info.xfer_handle = -1;
8526 // if it has failed or timed-out, kick the player
8527 case MULTI_XFER_TIMEDOUT:
8528 case MULTI_XFER_FAIL:
8529 // release the xfer handle
8530 multi_xfer_release_handle(Net_players[idx].s_info.xfer_handle);
8531 Net_players[idx].s_info.xfer_handle = -1;
8534 multi_kick_player(idx, 0, KICK_REASON_BAD_XFER);
8541 // NOTE : this is now obsolete
8542 // once everyone is verified, do any data transfer necessary
8543 if(multi_netplayer_flag_check(NETINFO_FLAG_MISSION_OK) && !(Mission_sync_flags & MS_FLAG_DATA_DONE) && !multi_endgame_ending()){
8544 // do nothing for now
8545 Mission_sync_flags |= MS_FLAG_DATA_DONE;
8547 // send campaign pool data
8548 multi_campaign_send_pool_status();
8551 // wait for everyone to ack on campaign pool data (even in non-campaign situations)
8552 if((Mission_sync_flags & MS_FLAG_DATA_DONE) && !(Mission_sync_flags & MS_FLAG_CAMP_DONE) && !multi_endgame_ending()){
8553 // check to see if everyone has acked the campaign pool data
8554 if(multi_netplayer_state_check(NETPLAYER_STATE_CPOOL_ACK)){
8555 Mission_sync_flags |= MS_FLAG_CAMP_DONE;
8559 // once everyone is verified, tell them to load the mission
8560 // also make sure to load the mission myself _AFTER_ telling everyone to do so. This makes the whole process
8561 // move along faster
8562 if((Mission_sync_flags & MS_FLAG_CAMP_DONE) && !(Mission_sync_flags & MS_FLAG_SENT_LOAD) && !multi_endgame_ending()){
8563 send_netplayer_load_packet(NULL);
8564 Mission_sync_flags |= MS_FLAG_SENT_LOAD;
8566 if(!(Game_mode & GM_STANDALONE_SERVER)){
8567 multi_common_add_text(XSTR("Sent load packet\n",835),1);
8570 // load the mission myself, as soon as possible
8571 if(!Multi_mission_loaded){
8572 nprintf(("Network","Server loading mission..."));
8574 // update everyone about my status
8575 Net_player->state = NETPLAYER_STATE_MISSION_LOADING;
8576 send_netplayer_update_packet();
8578 game_start_mission();
8580 nprintf(("Network","Done\n"));
8581 Multi_mission_loaded = 1;
8583 // update everyone about my status
8584 Net_player->state = NETPLAYER_STATE_MISSION_LOADED;
8585 send_netplayer_update_packet();
8587 if(!(Game_mode & GM_STANDALONE_SERVER)){
8588 multi_common_add_text(XSTR("Loaded mission locally\n",836),1);
8593 // if everyone has loaded the mission, randomly assign players to ships
8594 if(multi_netplayer_state_check(NETPLAYER_STATE_MISSION_LOADED) && !(Mission_sync_flags & MS_FLAG_TS_SLOTS) && !multi_endgame_ending()){
8595 // call the team select function to assign players to their ships, wings, etc
8596 multi_ts_assign_players_all();
8597 send_netplayer_slot_packet();
8600 Mission_sync_flags |= MS_FLAG_TS_SLOTS;
8603 // if everyone has loaded the mission, move to the team select stage
8604 if(Sync_test && multi_netplayer_state_check(NETPLAYER_STATE_SLOT_ACK) && !(Mission_sync_flags & MS_FLAG_PUSHED_BRIEFING) && !multi_endgame_ending()){
8605 Netgame.game_state = NETGAME_STATE_BRIEFING;
8606 send_netgame_update_packet(); // this will push everyone into the next state
8608 // the standalone moves to his own wait state, whereas in the normal game mode, the server/host moves in to the
8609 // team select state
8610 if(Game_mode & GM_STANDALONE_SERVER){
8611 gameseq_post_event(GS_EVENT_MULTI_STD_WAIT);
8613 gameseq_post_event(GS_EVENT_START_GAME);
8616 Mission_sync_flags |= MS_FLAG_PUSHED_BRIEFING;
8618 if(!(Game_mode & GM_STANDALONE_SERVER)){
8619 multi_common_add_text(XSTR("Moving to team select\n",837),1);
8623 // clients should detect here if they are doing a file xfer and do error processing
8624 if((Net_player->state == NETPLAYER_STATE_MISSION_XFER) && (Net_player->s_info.xfer_handle != -1) && !multi_endgame_ending()){
8625 switch(multi_xfer_get_status(Net_player->s_info.xfer_handle)){
8626 // if it has successfully completed, set his ok flag
8627 case MULTI_XFER_SUCCESS :
8628 // release my xfer handle
8629 multi_xfer_release_handle(Net_player->s_info.xfer_handle);
8630 Net_player->s_info.xfer_handle = -1;
8633 // if it has failed or timed-out, kick the player
8634 case MULTI_XFER_TIMEDOUT:
8635 case MULTI_XFER_FAIL:
8636 // release my xfer handle
8637 multi_xfer_release_handle(Net_player->s_info.xfer_handle);
8638 Net_player->s_info.xfer_handle = -1;
8640 // leave the game qith an error code
8641 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_XFER_FAIL);
8648 if(!(Game_mode & GM_STANDALONE_SERVER)){
8650 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8651 if(Multi_sync_bitmap != -1){
8652 gr_set_bitmap(Multi_sync_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8655 Multi_sync_window.draw();
8657 multi_sync_blit_screen_all();
8663 void multi_sync_pre_close()
8665 // at this point, we should shut down any file xfers...
8666 if(Net_player->s_info.xfer_handle != -1){
8667 nprintf(("Network","WARNING - killing file xfer while leaving mission sync state!!!\n"));
8669 multi_xfer_abort(Net_player->s_info.xfer_handle);
8670 Net_player->s_info.xfer_handle = -1;
8674 void multi_sync_post_init()
8676 multi_reset_timestamps();
8678 Multi_state_timestamp = timestamp(0);
8681 ml_string(NOX("Performing post-briefing data sync"));
8683 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8684 multi_common_add_text(XSTR("Server performing sync\n",830),1);
8686 multi_common_add_text(XSTR("Client performing sync\n",838),1);
8689 // everyone should re-initialize these
8690 init_multiplayer_stats();
8692 // reset all sequencing info
8693 multi_oo_reset_sequencing();
8695 // if I am not the master of the game, then send the firing information for my ship
8697 if ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) ){
8698 send_firing_info_packet();
8701 // if I'm not a standalone server, load up the countdown stuff
8702 if(!(Game_mode & GM_STANDALONE_SERVER)){
8703 Multi_sync_countdown_anim = NULL;
8704 Multi_sync_countdown_instance = NULL;
8705 Multi_sync_countdown_anim = anim_load(Multi_sync_countdown_fname[gr_screen.res]);
8706 if(Multi_sync_countdown_anim == NULL){
8707 nprintf(("General","WARNING!, Could not load countdown animation %s!\n",Multi_sync_countdown_fname[gr_screen.res]));
8711 // create objects for all permanent observers
8712 multi_obs_level_init();
8714 // clear the game start countdown timer
8715 Multi_sync_countdown_timer = -1.0f;
8716 Multi_sync_countdown = -1;
8718 // if this is a team vs. team mission, mark all ship teams appropriately
8719 if(Netgame.type_flags & NG_TYPE_TEAM){
8720 multi_team_mark_all_ships();
8723 Mission_sync_flags = 0;
8724 Multi_sync_launch_pressed = 0;
8727 #define MULTI_POST_TIMESTAMP 7000
8729 extern int create_wings();
8731 void multi_sync_post_do()
8735 // only if the host is also the master should he be doing this (non-standalone situation)
8736 if ( Net_player->flags & NETINFO_FLAG_AM_MASTER ) {
8738 // once everyone gets to this screen, send them the ship classes of all ships.
8739 if(multi_netplayer_state_check(NETPLAYER_STATE_WAITING) && !(Mission_sync_flags & MS_FLAG_POST_DATA)) {
8740 // only the host should ever do this
8741 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8742 // at this point we want to delete all necessary ships, change all necessary ship classes, and set all weapons up
8743 multi_ts_create_wings();
8745 // update player ets settings
8746 for(idx=0;idx<MAX_PLAYERS;idx++){
8747 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].player->objnum != -1)){
8748 multi_server_update_player_weapons(&Net_players[idx],&Ships[Objects[Net_players[idx].player->objnum].instance]);
8753 // note that this is done a little differently for standalones and nonstandalones
8754 send_post_sync_data_packet();
8756 multi_common_add_text(XSTR("Sending post briefing block information\n",839),1);
8758 Mission_sync_flags |= MS_FLAG_POST_DATA;
8761 // send weapon slots data
8762 if(multi_netplayer_state_check(NETPLAYER_STATE_POST_DATA_ACK) && !(Mission_sync_flags & MS_FLAG_WSS_SLOTS)) {
8763 // note that this is done a little differently for standalones and nonstandalones
8764 if(Netgame.type_flags & NG_TYPE_TEAM){
8765 send_wss_slots_data_packet(0,0);
8766 send_wss_slots_data_packet(1,1);
8768 send_wss_slots_data_packet(0,1);
8771 multi_common_add_text(XSTR("Sending weapon slots information\n",840),1);
8773 Mission_sync_flags |= MS_FLAG_WSS_SLOTS;
8776 // once weapon information is received, send player settings info
8777 if ( multi_netplayer_state_check(NETPLAYER_STATE_WSS_ACK) && !(Mission_sync_flags & MS_FLAG_PSETTINGS)) {
8778 send_player_settings_packet();
8780 // server (specifically, the standalone), should set this here
8781 Net_player->state = NETPLAYER_STATE_SETTINGS_ACK;
8782 send_netplayer_update_packet();
8784 multi_common_add_text(XSTR("Sending player settings packets\n",841),1);
8786 Mission_sync_flags |= MS_FLAG_PSETTINGS;
8789 // check to see if the countdown timer has started and act appropriately
8790 if( Multi_sync_countdown_timer > -1.0f ) {
8792 // increment by frametime.
8793 Multi_sync_countdown_timer += flFrametime;
8795 // if the animation is not playing, start it
8796 if(!(Game_mode & GM_STANDALONE_SERVER) && (Multi_sync_countdown_instance == NULL) && (Multi_sync_countdown_anim != NULL)){
8797 anim_play_struct aps;
8799 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]);
8800 aps.screen_id = GS_STATE_MULTI_MISSION_SYNC;
8801 aps.framerate_independent = 1;
8803 Multi_sync_countdown_instance = anim_play(&aps);
8806 // if the next second has expired
8807 if( Multi_sync_countdown_timer >= 1.0f ) {
8809 Multi_sync_countdown--;
8810 Multi_sync_countdown_timer = 0.0f;
8812 // if the countdown has reached 0, launch the mission
8813 if(Multi_sync_countdown == 0){
8814 Multi_sync_countdown_timer = -1.0f;
8816 Multi_sync_launch_pressed = 0;
8817 multi_sync_launch();
8819 // otherwise send a countdown packet
8821 send_countdown_packet(Multi_sync_countdown);
8826 // jump into the mission myself
8827 if((Multi_sync_countdown == 0) && multi_netplayer_state_check(NETPLAYER_STATE_IN_MISSION)){
8828 if(!((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !Multi_sync_launch_pressed)){
8829 Net_player->state = NETPLAYER_STATE_IN_MISSION;
8830 Netgame.game_state = NETGAME_STATE_IN_MISSION;
8831 gameseq_post_event(GS_EVENT_ENTER_GAME);
8833 multi_common_add_text(XSTR("Moving into game\n",842),1);
8837 // maybe start the animation countdown
8838 if((Multi_sync_countdown >= 0) && (Multi_sync_countdown_instance == NULL) && (Multi_sync_countdown_anim != NULL)){
8839 anim_play_struct aps;
8841 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]);
8842 aps.screen_id = GS_STATE_MULTI_MISSION_SYNC;
8843 aps.framerate_independent = 1;
8845 Multi_sync_countdown_instance = anim_play(&aps);
8849 // host - specific stuff
8850 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8851 // create the launch button so the host can click
8852 if( Sync_test && multi_netplayer_state_check(NETPLAYER_STATE_SETTINGS_ACK) ){
8853 multi_sync_create_launch_button();
8858 if(!(Game_mode & GM_STANDALONE_SERVER)){
8860 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8861 if(Multi_sync_bitmap != -1){
8862 gr_set_bitmap(Multi_sync_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8865 Multi_sync_window.draw();
8867 multi_sync_blit_screen_all();
8873 void multi_sync_post_close()
8877 // if I'm not a standalone server, unload up the countdown stuff
8878 if(!(Game_mode & GM_STANDALONE_SERVER)){
8879 // release all rendering animation instances (should only be 1)
8880 anim_release_all_instances(GS_STATE_MULTI_MISSION_SYNC);
8881 Multi_sync_countdown_instance = NULL;
8883 // free up the countdown animation
8884 if(Multi_sync_countdown_anim != NULL){
8885 anim_free(Multi_sync_countdown_anim);
8886 Multi_sync_countdown_anim = NULL;
8890 // all players should reset sequencing
8891 for(idx=0;idx<MAX_PLAYERS;idx++){
8892 if(Net_player->flags & NETINFO_FLAG_CONNECTED){
8893 Net_players[idx].client_cinfo_seq = 0;
8894 Net_players[idx].client_server_seq = 0;
8898 // multiplayer dogfight
8899 multi_df_level_pre_enter();
8901 // clients should clear obj_pair array and add pair for themselves
8903 if ( MULTIPLAYER_CLIENT ) {
8905 obj_add_pairs( OBJ_INDEX(Player_obj) );
8910 void multi_sync_display_name(const char *name, int index, int np_index)
8912 char fit[CALLSIGN_LEN];
8914 // make sure the string actually fits
8915 SDL_strlcpy(fit, name, sizeof(fit));
8917 // if we're in team vs. team mode
8918 if(Netgame.type_flags & NG_TYPE_TEAM){
8919 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]);
8921 // if this is the currently selected player, draw him highlighted
8922 if(np_index == Multi_sync_player_select){
8923 gr_set_color_fast(&Color_text_selected);
8925 gr_set_color_fast(&Color_text_normal);
8929 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);
8931 // blit his team icon
8933 if(Net_players[np_index].p_info.team == 0){
8934 // blit the team captain icon
8935 if(Net_players[np_index].flags & NETINFO_FLAG_TEAM_CAPTAIN){
8936 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
8937 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8938 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);
8941 // normal team member icon
8943 if(Multi_common_icons[MICON_TEAM0] != -1){
8944 gr_set_bitmap(Multi_common_icons[MICON_TEAM0], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8945 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);
8950 else if(Net_players[np_index].p_info.team == 1){
8951 // blit the team captain icon
8952 if(Net_players[np_index].flags & NETINFO_FLAG_TEAM_CAPTAIN){
8953 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
8954 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8955 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);
8958 // normal team member icon
8960 if(Multi_common_icons[MICON_TEAM1] != -1){
8961 gr_set_bitmap(Multi_common_icons[MICON_TEAM1], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8962 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);
8967 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]);
8969 // if this is the currently selected player, draw him highlighted
8970 if(np_index == Multi_sync_player_select){
8971 gr_set_color_fast(&Color_text_selected);
8973 gr_set_color_fast(&Color_text_normal);
8977 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);
8980 // maybe blit his CD status icon
8981 if((Net_players[np_index].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
8982 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8983 gr_bitmap(Ms_status_coords[gr_screen.res][MS_X_COORD], Ms_status_coords[gr_screen.res][MS_Y_COORD] + (index * 10));
8987 void multi_sync_display_status(const char *status, int index)
8991 // make sure the string actually fits
8992 SDL_strlcpy(fit, status, sizeof(fit));
8993 gr_force_fit_string(fit, 250, Ms_status2_coords[gr_screen.res][MS_W_COORD] - 20);
8994 gr_set_color_fast(&Color_bright);
8995 gr_string(Ms_status2_coords[gr_screen.res][MS_X_COORD], Ms_status2_coords[gr_screen.res][MS_Y_COORD] + (index * 10), fit);
8998 void multi_sync_force_start_pre()
9001 int want_state = NETPLAYER_STATE_SLOT_ACK; // kick any players who are still in this state
9003 // go through the player list and boot anyone who isn't in the right state
9004 for(idx=0;idx<MAX_PLAYERS;idx++){
9005 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx]) && (Net_players[idx].state == want_state)){
9006 multi_kick_player(idx,0);
9011 void multi_sync_force_start_post()
9015 int num_kill_states;
9017 // determine the state we want all players in so that we can find those who are not in the state
9018 kill_state[0] = NETPLAYER_STATE_BRIEFING;
9019 kill_state[1] = NETPLAYER_STATE_SHIP_SELECT;
9020 kill_state[2] = NETPLAYER_STATE_WEAPON_SELECT;
9021 num_kill_states = 3;
9023 // go through the player list and boot anyone who isn't in the right state
9024 for(idx=0;idx<MAX_PLAYERS;idx++){
9025 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx])){
9026 // check against all kill state
9027 for(idx2 = 0;idx2<num_kill_states;idx2++){
9028 if(Net_players[idx].state == kill_state[idx2]){
9029 multi_kick_player(idx,0);
9037 void multi_sync_start_countdown()
9039 // don't allow repeat button presses
9040 if(Multi_sync_launch_pressed){
9044 Multi_sync_launch_pressed = 1;
9046 // if I'm the server, begin the countdown
9047 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
9048 gamesnd_play_iface(SND_COMMIT_PRESSED);
9049 Multi_sync_countdown_timer = 0.0f;
9050 Multi_sync_countdown = MULTI_SYNC_COUNTDOWN_TIME;
9052 // send an initial countdown value
9053 send_countdown_packet(Multi_sync_countdown);
9055 // otherwise send the "start countdown" packet to the standalone
9057 SDL_assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
9058 send_countdown_packet(-1);
9062 void multi_sync_launch()
9064 // don't allow repeat button presses
9065 if(Multi_sync_launch_pressed){
9069 Multi_sync_launch_pressed = 1;
9072 ml_printf(NOX("Entering mission %s"), Game_current_mission_filename);
9074 // tell everyone to jump into the mission
9075 send_jump_into_mission_packet();
9076 Multi_state_timestamp = timestamp(MULTI_POST_TIMESTAMP);
9078 // set the # of players at the start of the mission
9079 Multi_num_players_at_start = multi_num_players();
9080 nprintf(("Network","# of players at start of mission : %d\n", Multi_num_players_at_start));
9082 // initialize datarate limiting for all clients
9083 multi_oo_rate_init_all();
9085 multi_common_add_text(XSTR("Sending mission start packet\n",843),1);
9088 void multi_sync_create_launch_button()
9090 if (!Multi_launch_button_created) {
9091 // create the object
9092 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);
9094 // set the sound to play when highlighted
9095 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_highlight_action(common_play_highlight_sound);
9097 // set the ani for the button
9098 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_bmaps(Multi_sync_buttons[gr_screen.res][MS_LAUNCH].filename, 3, 0);
9101 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.link_hotspot(Multi_sync_buttons[gr_screen.res][MS_LAUNCH].hotspot);
9104 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_hotkey(KEY_CTRLED+SDLK_RETURN);
9107 // create the text for the button
9108 Multi_sync_window.add_XSTR(&Multi_sync_text[gr_screen.res][MST_LAUNCH]);
9111 // increment the button count so we start checking this one
9112 Multi_sync_button_count++;
9114 Multi_launch_button_created = 1;
9118 void multi_sync_handle_plist()
9124 // if we don't have a currently selected player, select one
9125 if((Multi_sync_player_select < 0) || !MULTI_CONNECTED(Net_players[Multi_sync_player_select])){
9126 for(idx=0;idx<MAX_PLAYERS;idx++){
9127 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
9128 Multi_sync_player_select = idx;
9134 // check for button list presses
9135 if(Multi_sync_plist_button.pressed()){
9136 // get the y mouse coords
9137 Multi_sync_plist_button.get_mouse_pos(NULL,&my);
9139 // get the index of the item selected
9140 select_index = my / 10;
9142 // if the index is greater than the current # connections, do nothing
9143 if(select_index > (multi_num_connections() - 1)){
9147 // translate into an absolute Net_players[] index (get the Nth net player)
9148 Multi_sync_player_select = -1;
9149 for(idx=0;idx<MAX_PLAYERS;idx++){
9150 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
9154 // if we've found the item we're looking for
9155 if(select_index < 0){
9156 Multi_sync_player_select = idx;
9161 // if for some bizarre reason, this is an invalid player, unselect him and wait for the next interation
9163 if((Multi_sync_player_select >= 0) && (!MULTI_CONNECTED(Net_players[Multi_sync_player_select]) || MULTI_STANDALONE(Net_players[Multi_sync_player_select])) ){
9164 Multi_sync_player_select = -1;
9170 // -------------------------------------------------------------------------------------------------------------
9172 // MULTIPLAYER DEBRIEF SCREEN
9175 // other relevant data
9176 int Multi_debrief_accept_hit;
9177 int Multi_debrief_replay_hit;
9179 // set if the server has left the game
9180 int Multi_debrief_server_left = 0;
9182 // if we've reported on TvT status all players are in the debrief
9183 int Multi_debrief_reported_tvt = 0;
9185 // whether stats are being accepted
9186 // -1 == no decision yet
9189 int Multi_debrief_stats_accept_code = -1;
9191 int Multi_debrief_server_framecount = 0;
9193 float Multi_debrief_time = 0.0f;
9194 float Multi_debrief_resend_time = 10.0f;
9196 void multi_debrief_init()
9200 Multi_debrief_time = 0.0f;
9201 Multi_debrief_resend_time = 10.0f;
9203 // do this to notify the standalone or the normal server that we're in the debrief state and ready to receive packets
9204 if (!(Net_player->flags & NETINFO_FLAG_AM_MASTER)) {
9205 Net_player->state = NETPLAYER_STATE_DEBRIEF;
9206 send_netplayer_update_packet();
9209 // unflag some stuff
9210 for(idx=0;idx<MAX_PLAYERS;idx++){
9211 if(MULTI_CONNECTED(Net_players[idx])){
9212 Net_players[idx].flags &= ~(NETINFO_FLAG_RESPAWNING | NETINFO_FLAG_LIMBO | NETINFO_FLAG_WARPING_OUT);
9216 // if text input mode is active, clear it
9217 multi_msg_text_flush();
9219 // the server has not left yet
9220 Multi_debrief_server_left = 0;
9222 // have not hit accept or replay yet
9223 Multi_debrief_accept_hit = 0;
9224 Multi_debrief_replay_hit = 0;
9226 // stats have not been accepted yet
9227 Multi_debrief_stats_accept_code = -1;
9229 // mark stats as not being store yet
9230 Netgame.flags &= ~(NG_FLAG_STORED_MT_STATS);
9232 // no report on TvT yet
9233 Multi_debrief_reported_tvt = 0;
9235 Multi_debrief_server_framecount = 0;
9238 void multi_debrief_do_frame()
9240 Multi_debrief_time += flFrametime;
9242 // set the netgame state to be debriefing when appropriate
9243 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)){
9244 Netgame.game_state = NETGAME_STATE_DEBRIEF;
9245 send_netgame_update_packet();
9248 // evaluate all server stuff
9249 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
9250 multi_debrief_server_process();
9254 void multi_debrief_close()
9256 if ( MULTIPLAYER_CLIENT && (Netgame.game_state == NETGAME_STATE_MISSION_SYNC) ){
9257 gamesnd_play_iface( SND_COMMIT_PRESSED );
9261 // handle optional mission loop
9262 void multi_maybe_set_mission_loop()
9264 int cur = Campaign.current_mission;
9265 if (Campaign.missions[cur].has_mission_loop) {
9266 SDL_assert(Campaign.loop_mission != CAMPAIGN_LOOP_MISSION_UNINITIALIZED);
9268 bool require_repeat_mission = (Campaign.current_mission == Campaign.next_mission);
9270 // check for (1) mission loop available, (2) dont have to repeat last mission
9271 if ( (Campaign.missions[cur].has_mission_loop && (Campaign.loop_mission != -1)) && !require_repeat_mission ) {
9274 debrief_assemble_optional_mission_popup_text(buffer, sizeof(buffer), Campaign.missions[cur].mission_loop_desc);
9276 int choice = popup(0 , 2, POPUP_NO, POPUP_YES, buffer);
9278 Campaign.loop_enabled = 1;
9279 Campaign.next_mission = Campaign.loop_mission;
9284 // handle all cases for when the accept key is hit in a multiplayer debriefing
9285 void multi_debrief_accept_hit()
9287 // if we already accepted, do nothing
9288 // but he may need to hit accept again after the server has left the game, so allow this
9289 if(Multi_debrief_accept_hit){
9293 // mark this so that we don't hit it again
9294 Multi_debrief_accept_hit = 1;
9296 gamesnd_play_iface(SND_COMMIT_PRESSED);
9298 // if the server has left the game, always just end the game.
9299 if(Multi_debrief_server_left){
9300 if(!multi_quit_game(PROMPT_ALL)){
9301 Multi_debrief_server_left = 1;
9302 Multi_debrief_accept_hit = 0;
9304 Multi_debrief_server_left = 0;
9307 // query the host and see if he wants to accept stats
9308 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
9309 // if we're on a tracker game, he gets no choice for storing stats
9310 if(MULTI_IS_TRACKER_GAME){
9311 multi_maybe_set_mission_loop();
9313 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));
9315 // evaluate the result
9320 Multi_debrief_accept_hit = 0;
9323 // set the accept code to be "not accepting"
9325 multi_debrief_stats_toss();
9326 multi_maybe_set_mission_loop();
9329 // accept the stats and continue
9331 multi_debrief_stats_accept();
9332 multi_maybe_set_mission_loop();
9338 // set my netplayer state to be "debrief_accept", and be done with it
9339 Net_player->state = NETPLAYER_STATE_DEBRIEF_ACCEPT;
9340 send_netplayer_update_packet();
9344 // handle all cases for when the escape key is hit in a multiplayer debriefing
9345 void multi_debrief_esc_hit()
9349 // if the server has left
9350 if(Multi_debrief_server_left){
9351 multi_quit_game(PROMPT_ALL);
9356 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
9357 // if the stats have already been accepted
9358 if((Multi_debrief_stats_accept_code != -1) || (MULTI_IS_TRACKER_GAME)){
9359 multi_quit_game(PROMPT_HOST);
9361 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));
9363 // evaluate the result
9370 // set the accept code to be "not accepting"
9372 multi_debrief_stats_toss();
9373 multi_quit_game(PROMPT_NONE);
9376 // accept the stats and continue
9378 multi_debrief_stats_accept();
9379 multi_quit_game(PROMPT_NONE);
9384 // if the stats haven't been accepted yet, or this is a tracker game
9385 if((Multi_debrief_stats_accept_code == -1) && !(MULTI_IS_TRACKER_GAME)){
9386 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));
9388 // evaluate the result
9390 multi_quit_game(PROMPT_NONE);
9393 // otherwise go through the normal endgame channels
9395 multi_quit_game(PROMPT_ALL);
9400 void multi_debrief_replay_hit()
9402 // only the host should ever get here
9403 SDL_assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
9405 // if the button was already pressed, do nothing
9406 if(Multi_debrief_accept_hit){
9410 // same as hittin the except button except no stats are kept
9411 Multi_debrief_accept_hit = 1;
9413 // mark myself as being in the replay state so we know what to do next
9414 Net_player->state = NETPLAYER_STATE_DEBRIEF_REPLAY;
9415 send_netplayer_update_packet();
9418 // call this when the server has left and we would otherwise be saying "contact lost with server
9419 void multi_debrief_server_left()
9422 Multi_debrief_server_left = 1;
9424 // undo any "accept" hit so that clients can hit accept again to leave
9425 Multi_debrief_accept_hit = 0;
9428 void multi_debrief_stats_accept()
9430 // don't do anything if we've already accepted
9431 if(Multi_debrief_stats_accept_code != -1){
9435 Multi_debrief_stats_accept_code = 1;
9437 // if we're the host, and we're on a standalone, tell the standalone to begin the stats storing process
9438 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) || (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
9439 // send a packet to the players telling them to store their stats
9440 send_store_stats_packet(1);
9443 // add a chat line saying "stats have been accepted"
9444 multi_display_chat_msg(XSTR("<stats have been accepted>",850),0,0);
9447 ml_string(NOX("Stats stored"));
9450 void multi_debrief_stats_toss()
9452 // don't do anything if we've already accepted
9453 if(Multi_debrief_stats_accept_code != -1){
9457 Multi_debrief_stats_accept_code = 0;
9459 // if we're the host, and we're on a standalone, tell everyone to "toss" stats
9460 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) || (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
9461 // send a packet to the players telling them to store their stats
9462 send_store_stats_packet(0);
9465 // add a chat line saying "stats have been accepted"
9466 multi_display_chat_msg(XSTR("<stats have been tossed>",851),0,0);
9469 ml_string(NOX("Stats tossed"));
9472 int multi_debrief_stats_accept_code()
9474 return Multi_debrief_stats_accept_code;
9477 void multi_debrief_server_process()
9480 int player_status,other_status;
9482 Multi_debrief_server_framecount++;
9484 // if we're > 10 seconds into the debrief and not everyone is here, try warping everyone out again
9485 if((Multi_debrief_time >= Multi_debrief_resend_time) && !multi_netplayer_state_check3(NETPLAYER_STATE_DEBRIEF, NETPLAYER_STATE_DEBRIEF_ACCEPT, NETPLAYER_STATE_DEBRIEF_REPLAY, 1)){
9486 // find all players who are not in the debrief state and hit them with the endgame packet
9487 for(idx=0; idx<MAX_PLAYERS; idx++){
9488 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)) ){
9489 send_endgame_packet(&Net_players[idx]);
9494 Multi_debrief_resend_time += 7.0f;
9497 // evaluate the status of all players in the game (0 == not ready, 1 == ready to continue, 2 == ready to replay)
9500 // check all players
9501 for(idx=0;idx<MAX_PLAYERS;idx++){
9502 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_HOST(Net_players[idx])){
9503 if(Net_players[idx].state != NETPLAYER_STATE_DEBRIEF_ACCEPT){
9510 // if we haven't already reported TvT results
9511 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)){
9512 multi_team_report();
9513 Multi_debrief_reported_tvt = 1;
9516 // if all other players are good to go, check the host
9518 // if he is ready to continue
9519 if(Netgame.host->state == NETPLAYER_STATE_DEBRIEF_ACCEPT){
9522 // if he wants to replay the mission
9523 else if(Netgame.host->state == NETPLAYER_STATE_DEBRIEF_REPLAY){
9526 // if he is not ready
9531 // if all players are _not_ good to go
9536 // if we're in the debriefing state in a campaign mode, process accordingly
9537 if(Netgame.campaign_mode == MP_CAMPAIGN){
9538 multi_campaign_do_debrief(player_status);
9540 // otherwise process as normal (looking for all players to be ready to go to the next mission
9542 if(player_status == 1){
9543 multi_flush_mission_stuff();
9545 // set the netgame state to be forming and continue
9546 Netgame.game_state = NETGAME_STATE_FORMING;
9547 send_netgame_update_packet();
9549 // move to the proper state
9550 if(Game_mode & GM_STANDALONE_SERVER){
9551 gameseq_post_event(GS_EVENT_STANDALONE_MAIN);
9553 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
9556 multi_reset_timestamps();
9557 } else if(player_status == 2){
9558 multi_flush_mission_stuff();
9560 // tell everyone to move into the pre-briefing sync state
9561 Netgame.game_state = NETGAME_STATE_MISSION_SYNC;
9562 send_netgame_update_packet();
9564 // move back to the mission sync screen for the same mission again
9565 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
9566 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
9568 multi_reset_timestamps();
9574 // -------------------------------------------------------------------------------------------------------------
9576 // MULTIPLAYER PASSWORD POPUP
9581 static const char *Multi_pwd_bitmap_fname[GR_NUM_RESOLUTIONS] = {
9582 "Password", // GR_640
9583 "2_Password" // GR_1024
9586 static const char *Multi_pwd_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
9587 "Password-M", // GR_640
9588 "2_Password-M" // GR_1024
9593 // constants for coordinate lookup
9594 #define MPWD_X_COORD 0
9595 #define MPWD_Y_COORD 1
9596 #define MPWD_W_COORD 2
9597 #define MPWD_H_COORD 3
9600 #define MULTI_PWD_NUM_BUTTONS 2
9601 #define MPWD_CANCEL 0
9602 #define MPWD_COMMIT 1
9604 // password area defs
9605 int Mpwd_coords[GR_NUM_RESOLUTIONS][4] = {
9618 UI_WINDOW Multi_pwd_window; // the window object for the join screen
9619 UI_INPUTBOX Multi_pwd_passwd; // for Netgame.passwd
9620 int Multi_pwd_bitmap; // the background bitmap
9621 int Multi_passwd_background = -1;
9622 int Multi_passwd_done = -1;
9623 int Multi_passwd_running = 0;
9626 ui_button_info Multi_pwd_buttons[GR_NUM_RESOLUTIONS][MULTI_PWD_NUM_BUTTONS] = {
9629 ui_button_info("PWB_00", 402, 134, -1, -1, 0),
9630 ui_button_info("PWB_01", 450, 134, -1, -1, 1),
9632 ui_button_info("PWB_00", 411, 151, 405, 141, 0),
9633 ui_button_info("PWB_01", 460, 151, 465, 141, 1),
9637 ui_button_info("2_PWB_00", 659, 242, 649, 225, 0),
9638 ui_button_info("2_PWB_01", 737, 242, 736, 225, 1),
9644 #define MULTI_PWD_NUM_TEXT 3
9646 UI_XSTR Multi_pwd_text[GR_NUM_RESOLUTIONS][MULTI_PWD_NUM_TEXT] = {
9648 { "Cancel", 387, 400, 141, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[0][MPWD_CANCEL].button},
9649 { "Commit", 1062, 455, 141, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[0][MPWD_COMMIT].button},
9650 { "Enter Password", 1332, 149, 92, UI_XSTR_COLOR_GREEN, -1, NULL},
9653 { "Cancel", 387, 649, 225, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[1][MPWD_CANCEL].button},
9654 { "Commit", 1062, 736, 225, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[1][MPWD_COMMIT].button},
9655 { "Enter Password", 1332, 239, 148, UI_XSTR_COLOR_GREEN, -1, NULL},
9660 // initialize all graphics, etc
9661 void multi_passwd_init()
9665 // store the background as it currently is
9666 Multi_passwd_background = gr_save_screen();
9668 // create the interface window
9669 Multi_pwd_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
9670 Multi_pwd_window.set_mask_bmap(Multi_pwd_bitmap_mask_fname[gr_screen.res]);
9672 // load the background bitmap
9673 Multi_pwd_bitmap = bm_load(Multi_pwd_bitmap_fname[gr_screen.res]);
9674 if(Multi_pwd_bitmap < 0){
9675 // we failed to load the bitmap - this is very bad
9679 // create the interface buttons
9680 for(idx=0; idx<MULTI_PWD_NUM_BUTTONS; idx++){
9681 // create the object
9682 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);
9684 // set the sound to play when highlighted
9685 Multi_pwd_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
9687 // set the ani for the button
9688 Multi_pwd_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pwd_buttons[gr_screen.res][idx].filename);
9691 Multi_pwd_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pwd_buttons[gr_screen.res][idx].hotspot);
9696 for(idx=0; idx<MULTI_PWD_NUM_TEXT; idx++){
9697 Multi_pwd_window.add_XSTR(&Multi_pwd_text[gr_screen.res][idx]);
9701 // create the password input box
9702 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);
9703 Multi_pwd_passwd.set_focus();
9705 // link the enter key to ACCEPT
9706 Multi_pwd_buttons[gr_screen.res][MPWD_COMMIT].button.set_hotkey(SDLK_RETURN);
9708 Multi_passwd_done = -1;
9709 Multi_passwd_running = 1;
9712 // close down all graphics, etc
9713 void multi_passwd_close()
9715 // unload any bitmaps
9716 bm_release(Multi_pwd_bitmap);
9718 // destroy the UI_WINDOW
9719 Multi_pwd_window.destroy();
9721 // free up the saved background screen
9722 if(Multi_passwd_background >= 0){
9723 gr_free_screen(Multi_passwd_background);
9724 Multi_passwd_background = -1;
9727 Multi_passwd_running = 0;
9730 // process any button pressed
9731 void multi_passwd_process_buttons()
9733 // if the accept button was pressed
9734 if(Multi_pwd_buttons[gr_screen.res][MPWD_COMMIT].button.pressed()){
9735 gamesnd_play_iface(SND_USER_SELECT);
9736 Multi_passwd_done = 1;
9739 // if the cancel button was pressed
9740 if(Multi_pwd_buttons[gr_screen.res][MPWD_CANCEL].button.pressed()){
9741 gamesnd_play_iface(SND_USER_SELECT);
9742 Multi_passwd_done = 0;
9746 // run the passwd popup
9747 void multi_passwd_do(char *passwd, const int max_passlen)
9751 while(Multi_passwd_done == -1){
9752 // set frametime and run background stuff
9753 game_set_frametime(-1);
9754 game_do_state_common(gameseq_get_state());
9756 k = Multi_pwd_window.process();
9758 // process any keypresses
9761 // set this to indicate the user has cancelled for one reason or another
9762 Multi_passwd_done = 0;
9766 // if the input box text has changed
9767 if(Multi_pwd_passwd.changed()){
9768 SDL_strlcpy(passwd, "", max_passlen);
9769 Multi_pwd_passwd.get_text(passwd);
9772 // process any button pressed
9773 multi_passwd_process_buttons();
9775 // draw the background, etc
9778 if(Multi_passwd_background >= 0){
9779 gr_restore_screen(Multi_passwd_background);
9781 gr_set_bitmap(Multi_pwd_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
9783 Multi_pwd_window.draw();
9790 // bring up the password string popup, fill in passwd (return 1 if accept was pressed, 0 if cancel was pressed)
9791 int multi_passwd_popup(char *passwd, const int max_plen)
9793 // if the popup is already running for some reason, don't do anything
9794 if(Multi_passwd_running){
9798 // initialize all graphics
9799 multi_passwd_init();
9802 multi_passwd_do(passwd, max_plen);
9804 // shut everything down
9805 multi_passwd_close();
9807 return Multi_passwd_done;