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, SDL_arraysize(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, SDL_arraysize(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, "", SDL_arraysize(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, SDL_arraysize(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, SDL_arraysize(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 memset( &Netgame, 0, sizeof(Netgame) );
1293 Net_player->flags |= NETINFO_FLAG_DO_NETWORKING;
1294 Net_player->player = Player;
1295 memcpy(&Net_player->p_info.addr,&Psnet_my_addr,sizeof(net_addr));
1297 // check for the existence of a CD
1298 multi_common_verify_cd();
1300 // load my local netplayer options
1301 multi_options_local_load(&Net_player->p_info.options, Net_player);
1307 common_set_interface_palette(MULTI_JOIN_PALETTE);
1310 // destroy any chatbox contents which previously existed (from another game)
1313 // create the interface window
1314 Multi_join_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
1315 Multi_join_window.set_mask_bmap(Multi_join_bitmap_mask_fname[gr_screen.res]);
1317 // load the background bitmap
1318 Multi_join_bitmap = bm_load(Multi_join_bitmap_fname[gr_screen.res]);
1319 if(Multi_join_bitmap < 0){
1320 // we failed to load the bitmap - this is very bad
1324 // intialize the endgame system
1325 multi_endgame_init();
1327 // initialize the common notification messaging
1328 multi_common_notify_init();
1330 // initialize the common text area
1331 multi_common_set_text("");
1333 // load and use the common interface palette
1334 multi_common_load_palette();
1335 multi_common_set_palette();
1337 // load the help overlay
1338 help_overlay_load(MULTI_JOIN_OVERLAY);
1339 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1341 // do TCP and VMT specific initialization
1342 if ( !Multi_options_g.pxo ) {
1343 // if this is a TCP (non tracker) game, we'll load up our default address list right now
1344 multi_join_load_tcp_addrs();
1347 // initialize any and all timestamps
1348 Multi_join_glr_stamp = -1;
1349 Multi_join_ping_stamp = -1;
1350 Multi_join_sent_stamp = -1;
1352 // reset frame count
1353 Multi_join_frame_count = 0;
1355 // haven't tried to verify on the tracker yet.
1356 Multi_join_mt_tried_verify = 0;
1358 // clear our all game lists to save hassles
1359 multi_join_clear_game_list();
1361 // create the interface buttons
1362 for(idx=0; idx<MULTI_JOIN_NUM_BUTTONS; idx++){
1363 // create the object
1364 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);
1366 // set the sound to play when highlighted
1367 Multi_join_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
1369 // set the ani for the button
1370 Multi_join_buttons[gr_screen.res][idx].button.set_bmaps(Multi_join_buttons[gr_screen.res][idx].filename);
1373 Multi_join_buttons[gr_screen.res][idx].button.link_hotspot(Multi_join_buttons[gr_screen.res][idx].hotspot);
1378 for(idx=0; idx<MULTI_JOIN_NUM_TEXT; idx++){
1379 Multi_join_window.add_XSTR(&Multi_join_text[gr_screen.res][idx]);
1383 Multi_join_should_send = -1;
1385 // close any previously open chatbox
1388 // create the list item select button
1389 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);
1390 Multi_join_select_button.hide();
1394 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);
1397 // if starting a network game, then go to the create game screen
1398 if ( Cmdline_start_netgame ) {
1399 multi_join_create_game();
1400 } else if ( Cmdline_connect_addr != NULL ) {
1405 // joining a game. Send a join request to the given IP address, and wait for the return.
1406 memset( &Multi_autojoin_addr, 0, sizeof(net_addr) );
1407 Multi_autojoin_addr.type = NET_TCP;
1409 // create the address, looking out for port number at the end
1410 port_num = DEFAULT_GAME_PORT;
1411 p = strrchr(Cmdline_connect_addr, ':');
1415 port_num = (short)atoi(p);
1417 ip_addr = inet_addr(Cmdline_connect_addr);
1418 memcpy(Multi_autojoin_addr.addr, &ip_addr, IP_ADDRESS_LENGTH);
1419 Multi_autojoin_addr.port = port_num;
1421 send_server_query(&Multi_autojoin_addr);
1422 Multi_autojoin_query_stamp = timestamp(MULTI_AUTOJOIN_QUERY_STAMP);
1423 Multi_did_autojoin = 0;
1427 void multi_join_clear_game_list()
1430 Multi_join_list_selected = -1;
1431 Multi_join_selected_item = NULL;
1432 MJ_LIST_START_SET(-1);
1433 Multi_join_list_start_item = NULL;
1435 // free up the active game list
1436 multi_free_active_games();
1438 // initialize the active game list
1439 Active_game_head = NULL;
1440 Active_game_count = 0;
1443 void multi_join_game_do_frame()
1445 // check the status of our reliable socket. If not valid, popup error and return to main menu
1446 // I put this code here to avoid nasty gameseq issues with states. Also, we will have nice
1447 // background for the popup
1448 if ( !psnet_rel_check() ) {
1449 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));
1450 gameseq_post_event(GS_EVENT_MAIN_MENU);
1454 // return here since we will be moving to the next stage anyway -- I don't want to see the backgrounds of
1455 // all the screens for < 1 second for every screen we automatically move to.
1456 if ( Cmdline_start_netgame ) {
1460 // when joining a network game, wait for the server query to come back, and then join the game
1461 if ( Cmdline_connect_addr != NULL ) {
1464 if ( !Multi_did_autojoin ) {
1465 rval = popup_till_condition(multi_join_autojoin_do, XSTR("&Cancel", 779), XSTR("Joining netgame", 1500) );
1467 // cancel was hit. Send the user back to the main hall
1468 gameseq_post_event(GS_EVENT_MAIN_MENU);
1469 Cmdline_connect_addr = NULL; // reset this value.
1472 // when we get here, we have the data -- join the game.
1473 multi_join_send_join_request(0);
1474 Multi_autojoin_join_stamp = timestamp(MULTI_AUTOJOIN_JOIN_STAMP);
1475 Multi_did_autojoin = 1;
1478 if ( timestamp_elapsed(Multi_autojoin_join_stamp) ) {
1479 multi_join_send_join_request(0);
1480 Multi_autojoin_join_stamp = timestamp(MULTI_AUTOJOIN_JOIN_STAMP);
1486 // reset the should send var
1487 Multi_join_should_send = -1;
1489 int k = Multi_join_window.process();
1491 // process any keypresses
1494 if(help_overlay_active(MULTI_JOIN_OVERLAY)){
1495 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1497 if (Multi_options_g.pxo == 1) {
1498 gameseq_post_event(GS_EVENT_PXO);
1500 gameseq_post_event(GS_EVENT_MAIN_MENU);
1503 gamesnd_play_iface(SND_USER_SELECT);
1507 // page up the game list
1509 multi_join_list_page_up();
1511 Multi_join_slider.force_currentItem(Multi_join_list_start);
1516 multi_pinfo_popup(Net_player);
1519 // page down the game list
1521 multi_join_list_page_down();
1523 Multi_join_slider.force_currentItem(Multi_join_list_start);
1527 // send out a ping-all
1529 multi_join_ping_all();
1530 Multi_join_ping_stamp = timestamp(MULTI_JOIN_PING_TIME);
1533 // shortcut to start a game
1535 multi_join_create_game();
1538 // scroll the game list up
1540 multi_join_list_scroll_up();
1542 Multi_join_slider.force_currentItem(Multi_join_list_start);
1546 // scroll the game list down
1548 multi_join_list_scroll_down();
1550 Multi_join_slider.force_currentItem(Multi_join_list_start);
1555 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
1556 help_overlay_set_state(MULTI_JOIN_OVERLAY, 0);
1559 // do any network related stuff
1560 multi_join_do_netstuff();
1562 // process any button clicks
1563 multi_join_check_buttons();
1565 // process any list selection stuff
1566 multi_join_process_select();
1568 // draw the background, etc
1570 GR_MAYBE_CLEAR_RES(Multi_join_bitmap);
1571 if(Multi_join_bitmap != -1){
1572 gr_set_bitmap(Multi_join_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1575 Multi_join_window.draw();
1577 // display the active games
1578 multi_join_display_games();
1580 // display any text in the info area
1581 multi_common_render_text();
1583 // display any pending notification messages
1584 multi_common_notify_do();
1586 // blit the CD icon and any PXO filter stuff
1587 multi_join_blit_top_stuff();
1589 // draw the help overlay
1590 help_overlay_maybe_blit(MULTI_JOIN_OVERLAY);
1595 // if we are supposed to be sending a join request
1596 if(Multi_join_should_send != -1){
1597 multi_join_send_join_request(Multi_join_should_send);
1599 Multi_join_should_send = -1;
1601 // increment the frame count
1602 Multi_join_frame_count++;
1605 void multi_join_game_close()
1607 // unload any bitmaps
1608 if(!bm_unload(Multi_join_bitmap)){
1609 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_join_bitmap_fname[gr_screen.res]));
1612 // unload the help overlay
1613 help_overlay_unload(MULTI_JOIN_OVERLAY);
1615 // free up the active game list
1616 multi_free_active_games();
1618 // destroy the UI_WINDOW
1619 Multi_join_window.destroy();
1622 common_free_interface_palette();
1626 void multi_join_check_buttons()
1629 for(idx=0;idx<MULTI_JOIN_NUM_BUTTONS;idx++){
1630 // we only really need to check for one button pressed at a time, so we can break after
1632 if(Multi_join_buttons[gr_screen.res][idx].button.pressed()){
1633 multi_join_button_pressed(idx);
1639 void multi_join_button_pressed(int n)
1643 // if we're player PXO, go back there
1644 if (Multi_options_g.pxo == 1) {
1645 gameseq_post_event(GS_EVENT_PXO);
1647 gameseq_post_event(GS_EVENT_MAIN_MENU);
1649 gamesnd_play_iface(SND_USER_SELECT);
1652 if(Active_game_count <= 0){
1653 multi_common_add_notify(XSTR("No games found!",757));
1654 gamesnd_play_iface(SND_GENERAL_FAIL);
1655 } else if(Multi_join_list_selected == -1){
1656 multi_common_add_notify(XSTR("No game selected!",758));
1657 gamesnd_play_iface(SND_GENERAL_FAIL);
1658 } else if((Multi_join_sent_stamp != -1) && !timestamp_elapsed(Multi_join_sent_stamp)){
1659 multi_common_add_notify(XSTR("Still waiting on previous join request!",759));
1660 gamesnd_play_iface(SND_GENERAL_FAIL);
1662 // otherwise, if he's already played PXO games, warn him
1664 if(Player->flags & PLAYER_FLAGS_HAS_PLAYED_PXO){
1665 if(!multi_join_warn_pxo()){
1671 // send the join request here
1672 SDL_assert(Multi_join_selected_item != NULL);
1674 // send a join request packet
1675 Multi_join_should_send = 0;
1677 gamesnd_play_iface(SND_COMMIT_PRESSED);
1683 if(!help_overlay_active(MULTI_JOIN_OVERLAY)){
1684 help_overlay_set_state(MULTI_JOIN_OVERLAY,1);
1686 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1690 // scroll the game list up
1692 multi_join_list_scroll_up();
1694 Multi_join_slider.force_currentItem(Multi_join_list_start);
1698 // scroll the game list down
1699 case MJ_SCROLL_DOWN:
1700 multi_join_list_scroll_down();
1702 Multi_join_slider.force_currentItem(Multi_join_list_start);
1706 // scroll the info text box up
1707 case MJ_SCROLL_INFO_UP:
1708 multi_common_scroll_text_up();
1711 // scroll the info text box down
1712 case MJ_SCROLL_INFO_DOWN:
1713 multi_common_scroll_text_down();
1716 // go to the options screen
1718 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
1721 // go to the start game screen
1723 multi_join_create_game();
1726 // refresh the game/server list
1728 gamesnd_play_iface(SND_USER_SELECT);
1729 broadcast_game_query();
1732 // join a game as an observer
1733 case MJ_JOIN_OBSERVER:
1734 if(Active_game_count <= 0){
1735 multi_common_add_notify(XSTR("No games found!",757));
1736 gamesnd_play_iface(SND_GENERAL_FAIL);
1737 } else if(Multi_join_list_selected == -1){
1738 multi_common_add_notify(XSTR("No game selected!",758));
1739 gamesnd_play_iface(SND_GENERAL_FAIL);
1740 } else if((Multi_join_sent_stamp != -1) && !timestamp_elapsed(Multi_join_sent_stamp)){
1741 multi_common_add_notify(XSTR("Still waiting on previous join request!",759));
1742 gamesnd_play_iface(SND_GENERAL_FAIL);
1744 // send the join request here
1745 SDL_assert(Multi_join_selected_item != NULL);
1747 Multi_join_should_send = 1;
1749 gamesnd_play_iface(SND_COMMIT_PRESSED);
1754 multi_common_add_notify(XSTR("Not implemented yet!",760));
1755 gamesnd_play_iface(SND_GENERAL_FAIL);
1760 // display all relevant info for active games
1761 void multi_join_display_games()
1763 active_game *moveup = Multi_join_list_start_item;
1767 int y_start = Mj_list_y[gr_screen.res];
1772 // blit the game status (including text and type icon)
1773 multi_join_blit_game_status(moveup,y_start);
1775 // get the connection type
1776 con_type = (moveup->flags & AG_FLAG_CONNECTION_SPEED_MASK) >> AG_FLAG_CONNECTION_BIT;
1777 if((con_type > 4) || (con_type < 0)){
1781 // display the connection speed
1783 SDL_strlcpy(str, Multi_join_speed_labels[con_type], SDL_arraysize(str));
1784 gr_set_color_fast(Multi_join_speed_colors[con_type]);
1785 gr_string(Mj_speed_coords[gr_screen.res][MJ_X_COORD], y_start, str);
1787 // we'll want to have different colors for highlighted items, etc.
1788 if(moveup == Multi_join_selected_item){
1789 gr_set_color_fast(&Color_text_selected);
1791 gr_set_color_fast(&Color_text_normal);
1794 // display the game name, adding appropriate status chars
1796 if(moveup->flags & AG_FLAG_STANDALONE){
1797 SDL_strlcat(str, MJ_CHAR_STANDALONE, SDL_arraysize(str));
1799 if(moveup->flags & AG_FLAG_CAMPAIGN){
1800 SDL_strlcat(str, MJ_CHAR_CAMPAIGN, SDL_arraysize(str));
1803 // tack on the actual server name
1804 SDL_strlcat(str, " ", SDL_arraysize(str));
1805 SDL_strlcat(str, moveup->name, SDL_arraysize(str));
1806 if(strlen(moveup->mission_name) > 0){
1807 SDL_strlcat(str, " / ", SDL_arraysize(str));
1808 SDL_strlcat(str, moveup->mission_name, SDL_arraysize(str));
1811 // make sure the string fits in the display area and draw it
1812 gr_force_fit_string(str,200,Mj_game_name_coords[gr_screen.res][MJ_W_COORD]);
1813 gr_string(Mj_game_name_coords[gr_screen.res][MJ_X_COORD],y_start,str);
1815 // display the ping time
1816 if(moveup->ping.ping_avg > 0){
1817 if(moveup->ping.ping_avg > 1000){
1818 gr_set_color_fast(&Color_bright_red);
1819 SDL_strlcpy(str, XSTR("> 1 sec",761), SDL_arraysize(str));
1821 // set the appropriate ping time color indicator
1822 if(moveup->ping.ping_avg > MJ_PING_YELLOW){
1823 gr_set_color_fast(&Color_bright_red);
1824 } else if(moveup->ping.ping_avg > MJ_PING_YELLOW){
1825 gr_set_color_fast(&Color_bright_yellow);
1827 gr_set_color_fast(&Color_bright_green);
1830 SDL_snprintf(str, SDL_arraysize(str), "%d%s", moveup->ping.ping_avg, XSTR(" ms",762));
1833 gr_string(Mj_ping_coords[gr_screen.res][MJ_X_COORD],y_start,str);
1836 // display the number of players (be sure to center it)
1837 if(moveup == Multi_join_selected_item){
1838 gr_set_color_fast(&Color_text_selected);
1840 gr_set_color_fast(&Color_text_normal);
1842 SDL_snprintf(str, SDL_arraysize(str), "%d", moveup->num_players);
1843 gr_get_string_size(&w,&h,str);
1844 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);
1848 moveup = moveup->next;
1849 } while((moveup != Active_game_head) && (count < Mj_max_game_items[gr_screen.res]));
1851 // if there are no items on the list, display this info
1853 gr_set_color_fast(&Color_bright);
1854 gr_string(Mj_game_name_coords[gr_screen.res][MJ_X_COORD] - 30,y_start,XSTR("<No game servers found>",763));
1858 void multi_join_blit_game_status(active_game *game, int y)
1861 char status_text[25];
1863 // blit the proper icon
1865 switch( game->flags & AG_FLAG_TYPE_MASK ){
1868 if(Multi_common_icons[MICON_COOP] != -1){
1869 gr_set_bitmap(Multi_common_icons[MICON_COOP], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1874 // team vs. team game
1876 if(Multi_common_icons[MICON_TVT] != -1){
1877 gr_set_bitmap(Multi_common_icons[MICON_TVT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1884 case AG_FLAG_DOGFIGHT:
1885 if(Multi_common_icons[MICON_DOGFIGHT] != -1){
1886 gr_set_bitmap(Multi_common_icons[MICON_DOGFIGHT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1892 // if we're supposed to draw a bitmap
1894 gr_bitmap(Mj_game_icon_coords[gr_screen.res][MJ_X_COORD],y-1);
1897 // blit the proper status text
1898 memset(status_text,0,25);
1900 switch( game->flags & AG_FLAG_STATE_MASK ){
1901 case AG_FLAG_FORMING:
1902 gr_set_color_fast(&Color_bright_green);
1903 SDL_strlcpy(status_text, XSTR("Forming", 764), SDL_arraysize(status_text));
1905 case AG_FLAG_BRIEFING:
1906 gr_set_color_fast(&Color_bright_red);
1907 SDL_strlcpy(status_text, XSTR("Briefing", 765), SDL_arraysize(status_text));
1909 case AG_FLAG_DEBRIEF:
1910 gr_set_color_fast(&Color_bright_red);
1911 SDL_strlcpy(status_text, XSTR("Debrief", 766), SDL_arraysize(status_text));
1914 gr_set_color_fast(&Color_bright_red);
1915 SDL_strlcpy(status_text, XSTR("Paused", 767), SDL_arraysize(status_text));
1917 case AG_FLAG_IN_MISSION:
1918 gr_set_color_fast(&Color_bright_red);
1919 SDL_strlcpy(status_text, XSTR("Playing", 768), SDL_arraysize(status_text));
1922 gr_set_color_fast(&Color_bright);
1923 SDL_strlcpy(status_text, XSTR("Unknown", 769), SDL_arraysize(status_text));
1926 gr_get_string_size(&str_w,NULL,status_text);
1927 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);
1930 // load in a list of active games from our tcp.cfg file
1931 void multi_join_load_tcp_addrs()
1933 char line[MAX_IP_STRING];
1938 // attempt to open the ip list file
1939 file = cfopen(IP_CONFIG_FNAME,"rt",CFILE_NORMAL,CF_TYPE_DATA);
1941 nprintf(("Network","Error loading tcp.cfg file!\n"));
1945 // free up any existing server list
1946 multi_free_server_list();
1948 // read in all the strings in the file
1949 while(!cfeof(file)){
1951 cfgets(line,MAX_IP_STRING,file);
1953 // strip off any newline character
1954 if(line[strlen(line) - 1] == '\n'){
1955 line[strlen(line) - 1] = '\0';
1958 // empty lines don't get processed
1959 if( (line[0] == '\0') || (line[0] == '\n') ){
1963 if ( !psnet_is_valid_ip_string(line) ) {
1964 nprintf(("Network","Invalid ip string (%s)\n",line));
1966 // copy the server ip address
1967 memset(&addr,0,sizeof(net_addr));
1968 addr.type = NET_TCP;
1969 psnet_string_to_addr(&addr, line, SDL_arraysize(line));
1970 if ( addr.port == 0 ){
1971 addr.port = DEFAULT_GAME_PORT;
1974 // create a new server item on the list
1975 item = multi_new_server_item();
1977 memcpy(&item->server_addr,&addr,sizeof(net_addr));
1985 // do stuff like pinging servers, sending out requests, etc
1986 void multi_join_do_netstuff()
1988 // handle game query stuff
1989 if(Multi_join_glr_stamp == -1){
1990 broadcast_game_query();
1992 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
1993 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME_LOCAL);
1995 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME);
1998 // otherwise send out game query and restamp
1999 else if(timestamp_elapsed(Multi_join_glr_stamp)){
2000 broadcast_game_query();
2002 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
2003 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME_LOCAL);
2005 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME);
2009 // check to see if we've been accepted. If so, put up message saying so
2010 if ( Net_player->flags & (NETINFO_FLAG_ACCEPT_INGAME|NETINFO_FLAG_ACCEPT_CLIENT|NETINFO_FLAG_ACCEPT_HOST|NETINFO_FLAG_ACCEPT_OBSERVER) ) {
2011 multi_common_add_notify(XSTR("Accepted. Waiting for player data.",770));
2014 // check to see if any join packets we have sent have timed out
2015 if((Multi_join_sent_stamp != -1) && (timestamp_elapsed(Multi_join_sent_stamp))){
2016 Multi_join_sent_stamp = -1;
2017 multi_common_add_notify(XSTR("Join request timed out!",771));
2020 // check to see if we should be pinging everyone
2021 if((Multi_join_ping_stamp == -1) || (timestamp_elapsed(Multi_join_ping_stamp))){
2022 multi_join_ping_all();
2023 Multi_join_ping_stamp = timestamp(MULTI_JOIN_PING_TIME);
2027 multi_join_cull_timeouts();
2030 // evaluate a returned pong.
2031 void multi_join_eval_pong(net_addr *addr, fix pong_time)
2034 active_game *moveup = Active_game_head;
2039 if(psnet_same(&moveup->server_addr,addr)){
2041 multi_ping_eval_pong(&moveup->ping);
2045 moveup = moveup->next;
2047 } while(moveup != Active_game_head);
2050 // update the game's ping
2052 if(found && (moveup->ping_end > moveup->ping_start)){
2053 moveup->ping_time = f2fl(moveup->ping_end - moveup->ping_start);
2054 moveup->ping_start = -1;
2055 moveup->ping_end = -1;
2060 // ping all the server on the list
2061 void multi_join_ping_all()
2063 active_game *moveup = Active_game_head;
2068 moveup->ping_start = timer_get_fixed_seconds();
2069 moveup->ping_end = -1;
2070 send_ping(&moveup->server_addr);
2072 multi_ping_send(&moveup->server_addr,&moveup->ping);
2074 moveup = moveup->next;
2075 } while(moveup != Active_game_head);
2079 void multi_join_process_select()
2081 // if we don't have anything selected and there are items on the list - select the first one
2082 if((Multi_join_list_selected == -1) && (Active_game_count > 0)){
2083 Multi_join_list_selected = 0;
2084 Multi_join_selected_item = multi_join_get_game(0);
2085 MJ_LIST_START_SET(0);
2086 Multi_join_list_start_item = Multi_join_selected_item;
2088 // send a mission description request to this guy
2089 send_netgame_descript_packet(&Multi_join_selected_item->server_addr,0);
2090 multi_common_set_text("");
2092 // I sure hope this doesn't happen
2093 SDL_assert(Multi_join_selected_item != NULL);
2096 // otherwise see if he's clicked on an item
2097 else if(Multi_join_select_button.pressed() && (Active_game_count > 0)){
2099 Multi_join_select_button.get_mouse_pos(NULL,&y);
2101 if(item + Multi_join_list_start < Active_game_count){
2102 gamesnd_play_iface(SND_IFACE_MOUSE_CLICK);
2104 Multi_join_list_selected = item + Multi_join_list_start;
2105 Multi_join_selected_item = multi_join_get_game(Multi_join_list_selected);
2107 // I sure hope this doesn't happen
2108 SDL_assert(Multi_join_selected_item != NULL);
2110 // send a mission description request to this guy
2111 send_netgame_descript_packet(&Multi_join_selected_item->server_addr,0);
2112 multi_common_set_text("");
2116 // if he's double clicked, then select it and accept
2117 if(Multi_join_select_button.double_clicked()){
2119 Multi_join_select_button.get_mouse_pos(NULL,&y);
2121 if(item == Multi_join_list_selected){
2122 multi_join_button_pressed(MJ_ACCEPT);
2127 // return game n (0 based index)
2128 active_game *multi_join_get_game(int n)
2130 active_game *moveup = Active_game_head;
2137 moveup = moveup->next;
2138 while((moveup != Active_game_head) && (count != n)){
2139 moveup = moveup->next;
2142 if(moveup == Active_game_head){
2143 nprintf(("Network","Warning, couldn't find game item %d!\n",n));
2153 // scroll through the game list
2154 void multi_join_list_scroll_up()
2156 // if we're not at the beginning of the list, scroll up
2157 if(Multi_join_list_start_item != Active_game_head){
2158 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2160 MJ_LIST_START_DEC();
2162 gamesnd_play_iface(SND_SCROLL);
2164 gamesnd_play_iface(SND_GENERAL_FAIL);
2168 // scroll through the game list
2169 void multi_join_list_scroll_down()
2171 if((Active_game_count - Multi_join_list_start) > Mj_max_game_items[gr_screen.res]){
2172 Multi_join_list_start_item = Multi_join_list_start_item->next;
2174 MJ_LIST_START_INC();
2176 gamesnd_play_iface(SND_SCROLL);
2178 gamesnd_play_iface(SND_GENERAL_FAIL);
2182 void multi_join_list_page_up()
2184 // in this case, just set us to the beginning of the list
2185 if((Multi_join_list_start - Mj_max_game_items[gr_screen.res]) < 0){
2186 Multi_join_list_start_item = Active_game_head;
2188 MJ_LIST_START_SET(0);
2190 gamesnd_play_iface(SND_SCROLL);
2192 // otherwise page the whole thing up
2194 for(idx=0; idx<Mj_max_game_items[gr_screen.res]; idx++){
2195 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2197 MJ_LIST_START_DEC();
2199 gamesnd_play_iface(SND_SCROLL);
2203 void multi_join_list_page_down()
2207 // page the whole thing down
2208 while((count < Mj_max_game_items[gr_screen.res]) && ((Active_game_count - Multi_join_list_start) > Mj_max_game_items[gr_screen.res])){
2209 Multi_join_list_start_item = Multi_join_list_start_item->next;
2210 MJ_LIST_START_INC();
2215 gamesnd_play_iface(SND_SCROLL);
2218 void multi_join_cull_timeouts()
2220 active_game *backup;
2222 active_game *moveup = Active_game_head;
2224 // traverse through the entire list if any items exist
2228 if((moveup->heard_from_timer != -1) && (timestamp_elapsed(moveup->heard_from_timer))){
2229 Active_game_count--;
2231 // if this is the head of the list
2232 if(moveup == Active_game_head){
2233 // if this is the _only_ item on the list
2234 if(moveup->next == Active_game_head){
2235 // handle any gui details related to deleting this item
2236 multi_join_handle_item_cull(Active_game_head, count);
2238 free(Active_game_head);
2239 Active_game_head = NULL;
2242 // if there are other items on the list
2244 // handle any gui details related to deleting this item
2245 multi_join_handle_item_cull(moveup, count);
2247 Active_game_head = moveup->next;
2248 Active_game_head->prev = moveup->prev;
2249 Active_game_head->prev->next = Active_game_head;
2251 moveup = Active_game_head;
2254 // if its somewhere else on the list
2256 // handle any gui details related to deleting this item
2257 multi_join_handle_item_cull(moveup, count);
2259 // if its the last item on the list
2260 moveup->next->prev = moveup->prev;
2261 moveup->prev->next = moveup->next;
2263 // if it was the last element on the list, return
2264 if(moveup->next == Active_game_head){
2268 backup = moveup->next;
2274 moveup = moveup->next;
2277 } while(moveup != Active_game_head);
2281 // deep magic begins here.
2282 void multi_join_handle_item_cull(active_game *item, int item_index)
2284 // if this is the only item on the list, unset everything
2285 if(item->next == item){
2286 Multi_join_list_selected = -1;
2287 Multi_join_selected_item = NULL;
2290 Multi_join_slider.set_numberItems(0);
2292 MJ_LIST_START_SET(-1);
2293 Multi_join_list_start_item = NULL;
2299 // see if we should be adjusting our currently selected item
2300 if(item_index <= Multi_join_list_selected){
2301 // the selected item is the head of the list
2302 if(Multi_join_selected_item == Active_game_head){
2303 // move the pointer up since this item is about to be destroyed
2304 Multi_join_selected_item = Multi_join_selected_item->next;
2306 // if this is the item being deleted, select the previous one
2307 if(item == Multi_join_selected_item){
2309 Multi_join_selected_item = Multi_join_selected_item->prev;
2311 // decrement the selected index by 1
2312 Multi_join_list_selected--;
2314 // now we know its a previous item, so our pointer stays the same but our index goes down by one, since there will be
2315 // 1 less item on the list
2317 // decrement the selected index by 1
2318 Multi_join_list_selected--;
2323 // see if we should be adjusting out current start position
2324 if(item_index <= Multi_join_list_start){
2325 // the start position is the head of the list
2326 if(Multi_join_list_start_item == Active_game_head){
2327 // move the pointer up since this item is about to be destroyed
2328 Multi_join_list_start_item = Multi_join_list_start_item->next;
2330 // if this is the item being deleted, select the previous one
2331 if(item == Multi_join_list_start_item){
2332 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2334 // decrement the starting index by 1
2335 MJ_LIST_START_DEC();
2337 // but decrement the starting index by 1
2338 MJ_LIST_START_DEC();
2343 // maybe go back up a bit so that we always have a full page of items
2344 if(Active_game_count > Mj_max_game_items[gr_screen.res]){
2345 while((Active_game_count - Multi_join_list_start) < Mj_max_game_items[gr_screen.res]){
2346 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2347 MJ_LIST_START_DEC();
2351 // set slider location
2353 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);
2354 Multi_join_slider.force_currentItem(Multi_join_list_start);
2358 void multi_join_send_join_request(int as_observer)
2360 // don't do anything if we have no items selected
2361 if(Multi_join_selected_item == NULL){
2365 // 5/26/98 -- for team v team games, don't allow ingame joining :-(
2366 if ( (Multi_join_selected_item->flags & AG_FLAG_TEAMS) && (Multi_join_selected_item->flags & (AG_FLAG_PAUSE|AG_FLAG_IN_MISSION)) ) {
2367 popup(0, 1, POPUP_OK, XSTR("Joining ingame is currently not allowed for team vs. team games",772));
2371 memset(&Multi_join_request,0,sizeof(join_request));
2373 // if the netgame is in password mode, put up a request for the password
2374 if(Multi_join_selected_item->flags & AG_FLAG_PASSWD){
2375 if(!multi_passwd_popup(Multi_join_request.passwd, SDL_arraysize(Multi_join_request.passwd))){
2379 nprintf(("Password : %s\n",Multi_join_request.passwd));
2382 // fill out the join request struct
2383 SDL_strlcpy(Multi_join_request.callsign, Player->callsign, SDL_arraysize(Multi_join_request.callsign));
2384 if(strlen(Player->image_filename) > 0){
2385 SDL_strlcpy(Multi_join_request.image_filename, Player->image_filename, SDL_arraysize(Multi_join_request.image_filename));
2388 if(strlen(Player->squad_filename) > 0){
2389 SDL_strlcpy(Multi_join_request.squad_filename, Player->squad_filename, SDL_arraysize(Multi_join_request.squad_filename));
2393 // tracker id (if any)
2394 Multi_join_request.tracker_id = Multi_tracker_id;
2396 // player's rank (at least, what he wants you to _believe_)
2397 Multi_join_request.player_rank = (ubyte)Player->stats.rank;
2400 Multi_join_request.flags = 0;
2402 Multi_join_request.flags |= JOIN_FLAG_AS_OBSERVER;
2405 // if the player has hacked data
2406 if(game_hacked_data()){
2407 Multi_join_request.flags |= JOIN_FLAG_HAXOR;
2412 SDL_strlcpy(Multi_join_request.pxo_squad_name, Multi_tracker_squad_name, LOGIN_LEN);
2415 // version of this server
2416 Multi_join_request.version = MULTI_FS_SERVER_VERSION;
2418 // server compatible version
2419 Multi_join_request.comp_version = MULTI_FS_SERVER_COMPATIBLE_VERSION;
2421 // his local player options
2422 memcpy(&Multi_join_request.player_options,&Player->m_local_options,sizeof(multi_local_options));
2424 // set the server address for the netgame
2425 memcpy(&Netgame.server_addr,&Multi_join_selected_item->server_addr,sizeof(net_addr));
2427 // send a join request to the guy
2428 send_join_packet(&Multi_join_selected_item->server_addr,&Multi_join_request);
2431 Multi_join_sent_stamp = timestamp(MULTI_JOIN_SENT_WAIT);
2434 multi_common_add_notify(XSTR("Sending join request...",773));
2437 void multi_join_create_game()
2439 // maybe warn the player about possible crappy server conditions
2440 if(!multi_join_maybe_warn()){
2444 // make sure to flag ourself as being the master
2445 Net_player->flags |= (NETINFO_FLAG_AM_MASTER | NETINFO_FLAG_GAME_HOST);
2446 Net_player->state = NETPLAYER_STATE_HOST_SETUP;
2448 // if we're in PXO mode, mark it down in our player struct
2449 if(MULTI_IS_TRACKER_GAME){
2450 Player->flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
2451 Player->save_flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
2453 // otherwise, if he's already played PXO games, warn him
2456 if(Player->flags & PLAYER_FLAGS_HAS_PLAYED_PXO){
2457 if(!multi_join_warn_pxo()){
2464 gameseq_post_event(GS_EVENT_MULTI_START_GAME);
2465 gamesnd_play_iface(SND_USER_SELECT);
2468 void multi_join_reset_join_stamp()
2470 // unset the timestamp here so the user can immediately send another join request
2471 Multi_join_sent_stamp = -1;
2472 multi_common_add_notify("");
2475 void multi_join_blit_top_stuff()
2477 // blit the cd icon if he has one
2478 if(Multi_has_cd && (Multi_common_icons[MICON_CD] != -1)){
2481 bm_get_info(Multi_common_icons[MICON_CD], &cd_w, NULL, NULL, NULL, NULL);
2483 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
2484 gr_bitmap((gr_screen.max_w / 2) - (cd_w / 2), Mj_cd_coords[gr_screen.res]);
2488 #define CW_CODE_CANCEL 0 // cancel the action
2489 #define CW_CODE_OK 1 // continue anyway
2490 #define CW_CODE_INFO 2 // gimme some more information
2492 #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)
2493 #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)
2495 #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)
2496 #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)
2498 int multi_join_warn_update_low(int code)
2502 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),LOW_WARN_TEXT);
2505 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),LOW_INFO_TEXT);
2508 return CW_CODE_CANCEL;
2511 int multi_join_warn_update_medium(int code)
2515 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),MED_WARN_TEXT);
2518 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),MED_INFO_TEXT);
2521 return CW_CODE_CANCEL;
2524 int multi_join_maybe_warn()
2528 // if the player is set for low updates
2529 if(Player->m_local_options.obj_update_level == OBJ_UPDATE_LOW){
2532 code = multi_join_warn_update_low(code);
2533 } while((code != CW_CODE_CANCEL) && (code != CW_CODE_OK));
2538 // if the player is set for medium updates
2539 else if(Player->m_local_options.obj_update_level == OBJ_UPDATE_MEDIUM){
2542 code = multi_join_warn_update_medium(code);
2543 } while((code != CW_CODE_CANCEL) && (code != CW_CODE_OK));
2551 int multi_join_warn_pxo()
2553 // 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;
2557 void multi_join_blit_protocol()
2559 gr_set_color_fast(&Color_bright);
2561 switch(Socket_type){
2564 gr_string(5, 2, "TCP");
2573 // -------------------------------------------------------------------------------------------------
2575 // MULTIPLAYER START GAME screen
2580 #define MULTI_SG_PALETTE "InterfacePalette"
2582 static const char *Multi_sg_bitmap_fname[GR_NUM_RESOLUTIONS] = {
2583 "MultiStartGame", // GR_640
2584 "2_MultiStartGame" // GR_1024
2587 static const char *Multi_sg_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
2588 "MultiStartGame-M", // GR_640
2589 "2_MultiStartGame-M" // GR_1024
2594 int Multi_sg_rank_max_display[GR_NUM_RESOLUTIONS] = {
2599 // constants for coordinate look ups
2600 #define MSG_X_COORD 0
2601 #define MSG_Y_COORD 1
2602 #define MSG_W_COORD 2
2603 #define MSG_H_COORD 3
2607 // input password field
2608 int Msg_passwd_coords[GR_NUM_RESOLUTIONS][4] = {
2621 // input game title field
2622 int Msg_title_coords[GR_NUM_RESOLUTIONS][4] = {
2635 // rank selected field
2636 int Msg_rank_sel_coords[GR_NUM_RESOLUTIONS][4] = {
2650 int Msg_rank_list_coords[GR_NUM_RESOLUTIONS][4] = {
2666 #define MULTI_SG_NUM_BUTTONS 12
2667 #define MSG_OPEN_GAME 0
2668 #define MSG_CLOSED_GAME 1
2669 #define MSG_PASSWD_GAME 2
2670 #define MSG_RESTRICTED_GAME 3
2671 #define MSG_RANK_SET_GAME 4
2672 #define MSG_RANK_SCROLL_UP 5
2673 #define MSG_RANK_SCROLL_DOWN 6
2674 #define MSG_RANK_ABOVE 7
2675 #define MSG_RANK_BELOW 8
2677 #define MSG_OPTIONS 10
2678 #define MSG_ACCEPT 11
2680 #define MULTI_SG_NUM_BUTTONS 10
2681 #define MSG_OPEN_GAME 0
2682 //#define MSG_CLOSED_GAME 1
2683 //#define MSG_RESTRICTED_GAME 2
2684 #define MSG_PASSWD_GAME 1
2685 #define MSG_RANK_SET_GAME 2
2686 #define MSG_RANK_SCROLL_UP 3
2687 #define MSG_RANK_SCROLL_DOWN 4
2688 #define MSG_RANK_ABOVE 5
2689 #define MSG_RANK_BELOW 6
2691 #define MSG_OPTIONS 8
2692 #define MSG_ACCEPT 9
2695 UI_WINDOW Multi_sg_window; // the window object for the join screen
2696 UI_BUTTON Multi_sg_rank_button; // for selecting the rank marker
2697 UI_INPUTBOX Multi_sg_game_name; // for Netgame.name
2698 UI_INPUTBOX Multi_sg_game_passwd; // for Netgame.passwd
2699 int Multi_sg_bitmap; // the background bitmap
2701 ui_button_info Multi_sg_buttons[GR_NUM_RESOLUTIONS][MULTI_SG_NUM_BUTTONS] = {
2704 ui_button_info("MSG_00", 75, 111, -1, -1, 0),
2705 ui_button_info("MSG_01", 75, 139, -1, -1, 1),
2706 ui_button_info("MSG_02", 75, 164, -1, -1, 2),
2707 ui_button_info("MSG_03", 75, 199, -1, -1, 3),
2708 ui_button_info("MSG_04", 75, 243, -1, -1, 4),
2709 ui_button_info("MSG_05", 376, 231, -1, -1, 5),
2710 ui_button_info("MSG_06", 376, 258, -1, -1, 6),
2711 ui_button_info("MSG_07", 376, 291, -1, -1, 7),
2712 ui_button_info("MSG_08", 376, 320, -1, -1, 8),
2713 ui_button_info("MSG_09", 469, 427, -1, -1, 9),
2714 ui_button_info("MSG_10", 447, 452, -1, -1, 10),
2715 ui_button_info("MSG_11", 561, 411, -1, -1, 11),
2717 ui_button_info("MSG_00", 1, 184, 34, 191, 2), // open
2718 // ui_button_info("MSG_01", 1, 159, 34, 166, 1), // closed
2719 // ui_button_info("MSG_02", 1, 184, 34, 191, 2), // restricted
2720 ui_button_info("MSG_03", 1, 209, 34, 218, 3), // password
2721 ui_button_info("MSG_04", 1, 257, 34, 266, 4), // rank set
2722 ui_button_info("MSG_05", 1, 282, -1, -1, 5), // rank scroll up
2723 ui_button_info("MSG_06", 1, 307, -1, -1, 6), // rank scroll down
2724 ui_button_info("MSG_07", 177, 282, 210, 290, 7), // rank above
2725 ui_button_info("MSG_08", 177, 307, 210, 315, 8), // rank below
2726 ui_button_info("MSG_09", 536, 429, 500, 440, 9), // help
2727 ui_button_info("MSG_10", 536, 454, 479, 464, 10), // options
2728 ui_button_info("MSG_11", 576, 432, 571, 415, 11), // accept
2732 ui_button_info("2_MSG_00", 2, 295, 51, 307, 2), // open
2733 // ui_button_info("2_MSG_01", 2, 254, 51, 267, 1), // closed
2734 // ui_button_info("2_MSG_02", 2, 295, 51, 307, 2), // restricted
2735 ui_button_info("2_MSG_03", 2, 335, 51, 350, 3), // password
2736 ui_button_info("2_MSG_04", 2, 412, 51, 426, 4), // rank set
2737 ui_button_info("2_MSG_05", 2, 452, -1, -1, 5), // rank scroll up
2738 ui_button_info("2_MSG_06", 2, 492, -1, -1, 6), // rank scroll down
2739 ui_button_info("2_MSG_07", 284, 452, 335, 465, 7), // rank above
2740 ui_button_info("2_MSG_08", 284, 492, 335, 505, 8), // rank below
2741 ui_button_info("2_MSG_09", 858, 687, 817, 706, 9), // help
2742 ui_button_info("2_MSG_10", 858, 728, 797, 743, 10), // options
2743 ui_button_info("2_MSG_11", 921, 692, 921, 664, 11), // accept
2744 #ifdef MAKE_FS1 // filler for extra FS1 buttons
2745 ui_button_info("none", -1, -1, -1, -1, -1),
2746 ui_button_info("none", -1, -1, -1, -1, -1),
2752 #define MULTI_SG_NUM_TEXT 11
2754 UI_XSTR Multi_sg_text[GR_NUM_RESOLUTIONS][MULTI_SG_NUM_TEXT] = {
2756 {"Open", 1322, 34, 191, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_OPEN_GAME].button},
2757 // {"Closed", 1323, 34, 166, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_CLOSED_GAME].button},
2758 // {"Restricted", 1324, 34, 191, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RESTRICTED_GAME].button},
2759 {"Password Protected", 1325, 34, 218, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_PASSWD_GAME].button},
2760 {"Allow Rank", 1326, 34, 266, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_SET_GAME].button},
2761 {"Above", 1327, 210, 290, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_ABOVE].button},
2762 {"Below", 1328, 210, 315, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_BELOW].button},
2763 {"Help", 928, 500, 440, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_HELP].button},
2764 {"Options", 1036, 479, 464, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_OPTIONS].button},
2765 {"Accept", 1035, 571, 415, UI_XSTR_COLOR_PINK, -1, &Multi_sg_buttons[0][MSG_ACCEPT].button},
2766 {"Start Game", 1329, 26, 10, UI_XSTR_COLOR_GREEN, -1, NULL},
2767 {"Title", 1330, 26, 31, UI_XSTR_COLOR_GREEN, -1, NULL},
2768 {"Game Type", 1331, 12, 165, UI_XSTR_COLOR_GREEN, -1, NULL},
2771 {"Open", 1322, 51, 307, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_OPEN_GAME].button},
2772 // {"Closed", 1323, 51, 267, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_CLOSED_GAME].button},
2773 // {"Restricted", 1324, 51, 307, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RESTRICTED_GAME].button},
2774 {"Password Protected", 1325, 51, 350, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_PASSWD_GAME].button},
2775 {"Allow Rank", 1326, 51, 426, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_SET_GAME].button},
2776 {"Above", 1327, 335, 465, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_ABOVE].button},
2777 {"Below", 1328, 335, 505, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_BELOW].button},
2778 {"Help", 928, 817, 706, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_HELP].button},
2779 {"Options", 1036, 797, 743, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_OPTIONS].button},
2780 {"Accept", 1035, 921, 664, UI_XSTR_COLOR_PINK, -1, &Multi_sg_buttons[1][MSG_ACCEPT].button},
2781 {"Start Game", 1329, 42, 22, UI_XSTR_COLOR_GREEN, -1, NULL},
2782 {"Title", 1330, 42, 50, UI_XSTR_COLOR_GREEN, -1, NULL},
2783 {"Game Type", 1331, 20, 264, UI_XSTR_COLOR_GREEN, -1, NULL},
2788 // starting index for displaying ranks
2789 int Multi_sg_rank_start;
2790 int Multi_sg_rank_select;
2792 // netgame pointer to indirect through
2793 netgame_info *Multi_sg_netgame;
2795 // hold temporary values in this structure when on a standalone server
2796 netgame_info Multi_sg_netgame_temp;
2798 // forward declarations
2799 void multi_sg_check_buttons();
2800 void multi_sg_button_pressed(int n);
2801 void multi_sg_init_gamenet();
2802 void multi_sg_draw_radio_buttons();
2803 void multi_sg_rank_scroll_up();
2804 void multi_sg_rank_scroll_down();
2805 void multi_sg_rank_display_stuff();
2806 void multi_sg_rank_process_select();
2807 void multi_sg_rank_build_name(char *in, char *out, const int max_outlen);
2808 void multi_sg_check_passwd();
2809 void multi_sg_check_name();
2810 void multi_sg_release_passwd();
2811 int multi_sg_rank_select_valid(int rank);
2812 void multi_sg_select_rank_default();
2814 // function which takes a rank name and returns the index. Useful for commandline options
2815 // for above and below rank. We return the index of the rank in the Ranks[] array. If
2816 // the rank isn't found, we return -1
2817 int multi_start_game_rank_from_name( char *rank ) {
2821 for ( i = 0; i <= MAX_FREESPACE1_RANK; i++ ) {
2823 for ( i = 0; i <= MAX_FREESPACE2_RANK; i++ ) {
2825 if ( !SDL_strcasecmp(Ranks[i].name, rank) ) {
2833 void multi_start_game_init()
2837 // initialize the gamenet
2838 multi_sg_init_gamenet();
2840 // create the interface window
2841 Multi_sg_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
2842 Multi_sg_window.set_mask_bmap(Multi_sg_bitmap_mask_fname[gr_screen.res]);
2844 // load the background bitmap
2845 Multi_sg_bitmap = bm_load(Multi_sg_bitmap_fname[gr_screen.res]);
2846 if(Multi_sg_bitmap < 0){
2847 // we failed to load the bitmap - this is very bad
2851 // initialize the common notification messaging
2852 multi_common_notify_init();
2854 // initialize the common text area
2855 multi_common_set_text("");
2857 // use the common interface palette
2858 multi_common_set_palette();
2860 // create the interface buttons
2861 for(idx=0; idx<MULTI_SG_NUM_BUTTONS; idx++){
2862 // create the object
2863 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);
2865 // set the sound to play when highlighted
2866 Multi_sg_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
2868 // set the ani for the button
2869 Multi_sg_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sg_buttons[gr_screen.res][idx].filename);
2872 Multi_sg_buttons[gr_screen.res][idx].button.link_hotspot(Multi_sg_buttons[gr_screen.res][idx].hotspot);
2877 for(idx=0; idx<MULTI_SG_NUM_TEXT; idx++){
2878 Multi_sg_window.add_XSTR(&Multi_sg_text[gr_screen.res][idx]);
2882 // load the help overlay
2883 help_overlay_load(MULTI_START_OVERLAY);
2884 help_overlay_set_state(MULTI_START_OVERLAY,0);
2886 // intiialize the rank selection items
2887 multi_sg_select_rank_default();
2888 Multi_sg_rank_start = Multi_sg_rank_select;
2890 // create the rank select button
2891 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);
2892 Multi_sg_rank_button.hide();
2894 // create the netgame name input box
2895 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);
2897 // create the netgame password input box, and disable it by default
2898 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);
2899 Multi_sg_game_passwd.hide();
2900 Multi_sg_game_passwd.disable();
2902 // set the netgame text to this gadget and make it have focus
2903 Multi_sg_game_name.set_text(Multi_sg_netgame->name);
2904 Multi_sg_game_name.set_focus();
2906 // if starting a netgame, set the name of the game and any other options that are appropriate
2907 if ( Cmdline_start_netgame ) {
2908 if ( Cmdline_game_name != NULL ) {
2909 SDL_strlcpy( Multi_sg_netgame->name, Cmdline_game_name, SDL_arraysize(Multi_sg_netgame->name) );
2910 Multi_sg_game_name.set_text(Multi_sg_netgame->name);
2913 // deal with the different game types -- only one should even be active, so we will just go down
2914 // the line. Last one wins.
2915 if ( Cmdline_closed_game ) {
2916 Multi_sg_netgame->mode = NG_MODE_CLOSED;
2917 } else if ( Cmdline_restricted_game ) {
2918 Multi_sg_netgame->mode = NG_MODE_RESTRICTED;
2919 } else if ( Cmdline_game_password != NULL ) {
2920 Multi_sg_netgame->mode = NG_MODE_PASSWORD;
2921 SDL_strlcpy(Multi_sg_netgame->passwd, Cmdline_game_password, SDL_arraysize(Multi_sg_netgame->passwd));
2922 Multi_sg_game_passwd.set_text(Multi_sg_netgame->passwd);
2925 // deal with rank above and rank below
2926 if ( (Cmdline_rank_above != NULL) || (Cmdline_rank_below != NULL) ) {
2930 if ( Cmdline_rank_above != NULL ) {
2931 rank_str = Cmdline_rank_above;
2933 rank_str = Cmdline_rank_below;
2936 // try and get the rank index from the name -- if found, then set the rank base
2937 // and the game type. apparently we only support either above or below, not both
2938 // together, so I make a random choice
2939 rank = multi_start_game_rank_from_name( rank_str );
2941 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
2943 // now an arbitrary decision
2944 if ( Cmdline_rank_above != NULL ) {
2945 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
2947 Multi_sg_netgame->mode = NG_MODE_RANK_BELOW;
2952 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
2956 void multi_start_game_do()
2958 // return here since we will be moving to the next stage anyway -- I don't want to see the backgrounds of
2959 // all the screens for < 1 second for every screen we automatically move to.
2960 if ( Cmdline_start_netgame ) {
2964 int k = Multi_sg_window.process();
2966 // process any keypresses
2969 if(help_overlay_active(MULTI_START_OVERLAY)){
2970 help_overlay_set_state(MULTI_START_OVERLAY,0);
2972 gamesnd_play_iface(SND_USER_SELECT);
2973 multi_quit_game(PROMPT_NONE);
2978 case SDLK_LCTRL + SDLK_RETURN :
2979 case SDLK_RCTRL + SDLK_RETURN :
2980 gamesnd_play_iface(SND_COMMIT_PRESSED);
2981 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
2985 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
2986 help_overlay_set_state(MULTI_START_OVERLAY, 0);
2989 // check to see if the user has selected a different rank
2990 multi_sg_rank_process_select();
2992 // check any button presses
2993 multi_sg_check_buttons();
2995 // check to see if any of the input boxes have changed, and update the appropriate Netgame fields if necessary
2996 multi_sg_check_passwd();
2997 multi_sg_check_name();
2999 // draw the background, etc
3001 GR_MAYBE_CLEAR_RES(Multi_sg_bitmap);
3002 if(Multi_sg_bitmap != -1){
3003 gr_set_bitmap(Multi_sg_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
3006 Multi_sg_window.draw();
3008 // display rank stuff
3009 multi_sg_rank_display_stuff();
3011 // display any pending notification messages
3012 multi_common_notify_do();
3014 // draw all radio button
3015 multi_sg_draw_radio_buttons();
3017 // draw the help overlay
3018 help_overlay_maybe_blit(MULTI_START_OVERLAY);
3024 void multi_start_game_close()
3026 // if i'm the host on a standalone server, send him my start game options (passwd, mode, etc)
3027 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
3028 multi_options_update_start_game(Multi_sg_netgame);
3031 // unload any bitmaps
3032 if(!bm_unload(Multi_sg_bitmap)){
3033 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_sg_bitmap_fname[gr_screen.res]));
3036 // unload the help overlay
3037 help_overlay_unload(MULTI_START_OVERLAY);
3039 // destroy the UI_WINDOW
3040 Multi_sg_window.destroy();
3043 void multi_sg_check_buttons()
3046 for(idx=0;idx<MULTI_SG_NUM_BUTTONS;idx++){
3047 // we only really need to check for one button pressed at a time, so we can break after
3049 if(Multi_sg_buttons[gr_screen.res][idx].button.pressed()){
3050 multi_sg_button_pressed(idx);
3056 void multi_sg_button_pressed(int n)
3059 // go to the options screen
3061 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
3066 if(!help_overlay_active(MULTI_START_OVERLAY)){
3067 help_overlay_set_state(MULTI_START_OVERLAY,1);
3069 help_overlay_set_state(MULTI_START_OVERLAY,0);
3073 // the open button was pressed
3075 // if the closed option is selected
3076 if(Multi_sg_netgame->mode != NG_MODE_OPEN){
3077 Multi_sg_netgame->mode = NG_MODE_OPEN;
3079 gamesnd_play_iface(SND_USER_SELECT);
3081 // release the password control if necessary
3082 multi_sg_release_passwd();
3084 // if its already selected
3086 gamesnd_play_iface(SND_GENERAL_FAIL);
3091 // the open button was pressed
3092 case MSG_CLOSED_GAME:
3093 // if the closed option is selected
3094 if(Multi_sg_netgame->mode != NG_MODE_CLOSED){
3095 Multi_sg_netgame->mode = NG_MODE_CLOSED;
3097 gamesnd_play_iface(SND_USER_SELECT);
3099 // release the password control if necessary
3100 multi_sg_release_passwd();
3102 // if its already selected
3104 gamesnd_play_iface(SND_GENERAL_FAIL);
3109 // toggle password protection
3110 case MSG_PASSWD_GAME:
3111 // if we selected it
3112 if(Multi_sg_netgame->mode != NG_MODE_PASSWORD){
3113 gamesnd_play_iface(SND_USER_SELECT);
3115 Multi_sg_game_passwd.enable();
3116 Multi_sg_game_passwd.unhide();
3117 Multi_sg_game_passwd.set_focus();
3119 Multi_sg_netgame->mode = NG_MODE_PASSWORD;
3121 // copy in the current network password
3122 Multi_sg_game_passwd.set_text(Multi_sg_netgame->passwd);
3124 gamesnd_play_iface(SND_GENERAL_FAIL);
3129 // toggle "restricted" on or off
3130 case MSG_RESTRICTED_GAME:
3131 if(Multi_sg_netgame->mode != NG_MODE_RESTRICTED){
3132 gamesnd_play_iface(SND_USER_SELECT);
3133 Multi_sg_netgame->mode = NG_MODE_RESTRICTED;
3135 // release the password control if necessary
3136 multi_sg_release_passwd();
3138 gamesnd_play_iface(SND_GENERAL_FAIL);
3143 // turn off all rank requirements
3144 case MSG_RANK_SET_GAME:
3145 // if either is set, then turn then both off
3146 if((Multi_sg_netgame->mode != NG_MODE_RANK_BELOW) && (Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE)){
3147 gamesnd_play_iface(SND_USER_SELECT);
3149 // set it to the default case if we're turning it off
3150 multi_sg_select_rank_default();
3151 Multi_sg_rank_start = Multi_sg_rank_select;
3153 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
3155 // release the password control if necessary
3156 multi_sg_release_passwd();
3158 gamesnd_play_iface(SND_GENERAL_FAIL);
3162 // rank above was pressed
3163 case MSG_RANK_ABOVE :
3164 if((Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE) || (Multi_sg_netgame->mode == NG_MODE_RANK_BELOW)){
3165 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
3167 // select the first item
3168 multi_sg_select_rank_default();
3169 Multi_sg_rank_start = Multi_sg_rank_select;
3172 gamesnd_play_iface(SND_USER_SELECT);
3174 gamesnd_play_iface(SND_GENERAL_FAIL);
3178 // rank below was pressed
3179 case MSG_RANK_BELOW :
3180 if((Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE) || (Multi_sg_netgame->mode == NG_MODE_RANK_BELOW)){
3181 Multi_sg_netgame->mode = NG_MODE_RANK_BELOW;
3183 // select the first item
3184 multi_sg_select_rank_default();
3185 Multi_sg_rank_start = Multi_sg_rank_select;
3188 gamesnd_play_iface(SND_USER_SELECT);
3190 gamesnd_play_iface(SND_GENERAL_FAIL);
3194 // scroll the rank list up
3195 case MSG_RANK_SCROLL_UP:
3196 multi_sg_rank_scroll_up();
3199 // scroll the rank list down
3200 case MSG_RANK_SCROLL_DOWN:
3201 multi_sg_rank_scroll_down();
3204 // move to the create game screen
3206 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
3207 gamesnd_play_iface(SND_COMMIT_PRESSED);
3211 gamesnd_play_iface(SND_GENERAL_FAIL);
3212 multi_common_add_notify(XSTR("Not implemented yet!",760));
3217 // NOTE : this is where all Netgame initialization should take place on the host
3218 void multi_sg_init_gamenet()
3220 char buf[128],out_name[128];
3222 net_player *server_save;
3224 // back this data up in case we are already connected to a standalone
3225 memcpy(&save,&Netgame.server_addr,sizeof(net_addr));
3226 server_save = Netgame.server;
3228 // remove campaign flags
3229 Game_mode &= ~(GM_CAMPAIGN_MODE);
3231 // clear out the Netgame structure and start filling in the values
3232 memset( &Netgame, 0, sizeof(Netgame) );
3233 memset( &Multi_sg_netgame_temp, 0, sizeof(netgame_info) );
3235 // if we're on the standalone, we're not the server, so we don't care about setting the netgame state
3236 if(Net_player->state != NETPLAYER_STATE_STD_HOST_SETUP){
3237 Netgame.game_state = NETGAME_STATE_HOST_SETUP;
3238 Multi_sg_netgame = &Netgame;
3241 ml_string(NOX("Starting netgame as Host/Server"));
3243 Multi_sg_netgame = &Multi_sg_netgame_temp;
3247 memcpy(&temp_addr.s_addr, &Netgame.server_addr, 4);
3248 char *server_addr = inet_ntoa(temp_addr);
3249 ml_printf(NOX("Starting netgame as Host on Standalone server : %s"), (server_addr == NULL) ? NOX("Unknown") : server_addr);
3252 Net_player->tracker_player_id = Multi_tracker_id;
3254 Multi_sg_netgame->security = (rand() % 32766) + 1; // get some random security number
3255 Multi_sg_netgame->mode = NG_MODE_OPEN;
3256 Multi_sg_netgame->rank_base = RANK_ENSIGN;
3257 if(Multi_sg_netgame->security < 16){
3258 Multi_sg_netgame->security += 16;
3261 // set the version_info field
3262 Multi_sg_netgame->version_info = NG_VERSION_ID;
3264 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
3265 Netgame.host = Net_player;
3268 // set the default netgame flags
3269 Multi_sg_netgame->flags = 0;
3271 // intialize endgame stuff
3272 multi_endgame_init();
3274 // load in my netgame options
3275 multi_options_netgame_load(&Netgame.options);
3277 // load my local netplayer options
3278 multi_options_local_load(&Net_player->p_info.options, Net_player);
3280 // setup the default game name, taking care of string length and player callsigns
3281 memset(out_name,0,128);
3283 pilot_format_callsign_personal(Player->callsign, out_name, SDL_arraysize(out_name));
3284 SDL_snprintf(buf, SDL_arraysize(buf), XSTR("%s game",782), out_name); // [[ %s will be a pilot's name ]]
3285 if ( strlen(buf) > MAX_GAMENAME_LEN ){
3286 SDL_strlcpy(buf, XSTR("Temporary name",783), SDL_arraysize(buf));
3288 SDL_strlcpy(Multi_sg_netgame->name, buf, SDL_arraysize(Multi_sg_netgame->name));
3290 // set the default qos and duration
3291 multi_voice_maybe_update_vars(Netgame.options.voice_qos,Netgame.options.voice_record_time);
3293 // make sure to set the server correctly (me or the standalone)
3294 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3295 memcpy(&Netgame.server_addr, &Psnet_my_addr,sizeof(net_addr));
3296 Netgame.server = Net_player;
3297 Net_player->player_id = multi_get_new_id();
3299 // setup debug flags
3300 Netgame.debug_flags = 0;
3302 if(!Cmdline_server_firing){
3303 Netgame.debug_flags |= NETD_FLAG_CLIENT_FIRING;
3305 if(!Cmdline_client_dodamage){
3306 Netgame.debug_flags |= NETD_FLAG_CLIENT_NODAMAGE;
3310 memcpy(&Netgame.server_addr,&save,sizeof(net_addr));
3311 Netgame.server = server_save;
3314 // if I have a cd or not
3316 Net_player->flags |= NETINFO_FLAG_HAS_CD;
3319 // if I have hacked data
3320 if(game_hacked_data()){
3321 Net_player->flags |= NETINFO_FLAG_HAXOR;
3324 // assign my player struct and other data
3325 Net_player->flags |= (NETINFO_FLAG_CONNECTED | NETINFO_FLAG_DO_NETWORKING);
3326 Net_player->s_info.voice_token_timestamp = -1;
3328 // if we're supposed to flush our cache directory, do so now
3329 if(Net_player->p_info.options.flags & MLO_FLAG_FLUSH_CACHE){
3330 multi_flush_multidata_cache();
3333 ml_string(NOX("Flushing multi-data cache"));
3339 void multi_sg_draw_radio_buttons()
3341 // draw the appropriate radio button
3342 switch(Multi_sg_netgame->mode){
3344 Multi_sg_buttons[gr_screen.res][MSG_OPEN_GAME].button.draw_forced(2);
3348 case NG_MODE_CLOSED:
3349 Multi_sg_buttons[gr_screen.res][MSG_CLOSED_GAME].button.draw_forced(2);
3353 case NG_MODE_PASSWORD:
3354 Multi_sg_buttons[gr_screen.res][MSG_PASSWD_GAME].button.draw_forced(2);
3358 case NG_MODE_RESTRICTED:
3359 Multi_sg_buttons[gr_screen.res][MSG_RESTRICTED_GAME].button.draw_forced(2);
3363 case NG_MODE_RANK_ABOVE:
3364 Multi_sg_buttons[gr_screen.res][MSG_RANK_SET_GAME].button.draw_forced(2);
3365 Multi_sg_buttons[gr_screen.res][MSG_RANK_ABOVE].button.draw_forced(2);
3367 case NG_MODE_RANK_BELOW:
3368 Multi_sg_buttons[gr_screen.res][MSG_RANK_SET_GAME].button.draw_forced(2);
3369 Multi_sg_buttons[gr_screen.res][MSG_RANK_BELOW].button.draw_forced(2);
3374 void multi_sg_rank_scroll_up()
3376 // if he doesn't have either of the rank flags set, then ignore this
3377 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3381 if(Multi_sg_rank_start > 0){
3382 Multi_sg_rank_start--;
3383 gamesnd_play_iface(SND_SCROLL);
3385 gamesnd_play_iface(SND_GENERAL_FAIL);
3389 void multi_sg_rank_scroll_down()
3391 // if he doesn't have either of the rank flags set, then ignore this
3392 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3396 if((NUM_RANKS - Multi_sg_rank_start) > Multi_sg_rank_max_display[gr_screen.res]){
3397 Multi_sg_rank_start++;
3398 gamesnd_play_iface(SND_SCROLL);
3400 gamesnd_play_iface(SND_GENERAL_FAIL);
3404 void multi_sg_rank_display_stuff()
3409 // if he doesn't have either of the rank flags set, then ignore this
3410 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3414 // display the list of ranks
3415 y = Msg_rank_list_coords[gr_screen.res][MSG_Y_COORD];
3416 idx = Multi_sg_rank_start;
3418 while((count < NUM_RANKS) && (count < Multi_sg_rank_max_display[gr_screen.res]) && (idx < NUM_RANKS)){
3419 // if its the selected item, then color it differently
3420 if(idx == Multi_sg_rank_select){
3421 gr_set_color_fast(&Color_text_selected);
3423 gr_set_color_fast(&Color_text_normal);
3427 multi_sg_rank_build_name(Ranks[idx].name, rank_name, sizeof(rank_name));
3428 gr_string(Msg_rank_list_coords[gr_screen.res][MSG_X_COORD],y,rank_name);
3436 // display the selected rank
3438 gr_set_color_fast(&Color_bright);
3439 multi_sg_rank_build_name(Ranks[Multi_sg_netgame->rank_base].name, rank_name, SDL_arraysize(rank_name));
3440 gr_string(Msg_rank_sel_coords[gr_screen.res][MSG_X_COORD],Msg_rank_sel_coords[gr_screen.res][MSG_Y_COORD],rank_name);
3444 void multi_sg_rank_process_select()
3448 // if he doesn't have either of the rank flags set, then ignore this
3449 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3453 // see if he's clicked on an item on the rank list
3454 if(Multi_sg_rank_button.pressed()){
3456 Multi_sg_rank_button.get_mouse_pos(NULL,&y);
3459 if(item + Multi_sg_rank_start < NUM_RANKS){
3460 // evaluate whether this rank is valid for the guy to pick
3461 if(multi_sg_rank_select_valid(item + Multi_sg_rank_start)){
3462 gamesnd_play_iface(SND_USER_SELECT);
3464 Multi_sg_rank_select = item + Multi_sg_rank_start;
3466 // set the Netgame rank
3467 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
3469 gamesnd_play_iface(SND_GENERAL_FAIL);
3471 memset(string,0,255);
3472 SDL_snprintf(string,SDL_arraysize(string),XSTR("Illegal value for a host of your rank (%s)\n",784),Ranks[Net_player->player->stats.rank].name);
3473 multi_common_add_notify(string);
3479 void multi_sg_rank_build_name(char *in, char *out, const int max_outlen)
3484 SDL_strlcpy(use, in, SDL_arraysize(use));
3485 first = strtok(use," ");
3487 // just copy the string
3489 SDL_strlcpy(out, in, max_outlen);
3492 // if the first part of the string is lieutenant, then abbreivate it and tack on the rest of the string
3493 if (SDL_strcasecmp(first,XSTR("lieutenant",785)) == 0) {
3494 first = strtok(NULL, NOX("\n"));
3496 // if he's not just a plain lieutenant
3498 SDL_snprintf(out, max_outlen, "%s%s", XSTR("Lt. ",786), first); // [[ lieutenant ]]
3500 // if he _is_ just a plain lieutenant
3502 SDL_strlcpy(out, in, max_outlen);
3505 SDL_strlcpy(out, in, max_outlen);
3509 void multi_sg_check_passwd()
3511 // check to see if the password input box has been pressed
3512 if(Multi_sg_game_passwd.changed()){
3513 Multi_sg_game_passwd.get_text(Multi_sg_netgame->passwd);
3517 void multi_sg_check_name()
3519 // check to see if the game name input box has been pressed
3520 if(Multi_sg_game_name.changed()){
3521 Multi_sg_game_name.get_text(Multi_sg_netgame->name);
3525 void multi_sg_release_passwd()
3527 // hide and disable the password input box
3528 Multi_sg_game_passwd.hide();
3529 Multi_sg_game_passwd.disable();
3531 // set the focus back to the name input box
3532 Multi_sg_game_name.set_focus();
3535 int multi_sg_rank_select_valid(int rank)
3538 if(Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE){
3539 if(Net_player->player->stats.rank >= rank){
3545 if(Net_player->player->stats.rank <= rank){
3553 void multi_sg_select_rank_default()
3555 // pick our rank for now
3556 Multi_sg_rank_select = Net_player->player->stats.rank;
3558 // set the Netgame rank
3559 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
3562 // -------------------------------------------------------------------------------------------------
3564 // MULTIPLAYER CREATE GAME screen
3569 const char *Multi_create_bitmap_fname[GR_NUM_RESOLUTIONS] = {
3570 "MultiCreate", // GR_640
3571 "2_MultiCreate" // GR_1024
3574 const char *Multi_create_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
3575 "MultiCreate-M", // GR_640
3576 "2_MultiCreate-M" // GR_1024
3579 const char *Multi_create_loading_fname[GR_NUM_RESOLUTIONS] = {
3580 "PleaseWait", // GR_640
3581 "2_PleaseWait" // GR_1024
3585 #define MULTI_CREATE_NUM_BUTTONS 23
3588 #define MC_SHOW_ALL 0
3589 #define MC_SHOW_COOP 1
3590 #define MC_SHOW_TEAM 2
3591 #define MC_SHOW_DOGFIGHT 3
3592 #define MC_PXO_REFRESH 4
3593 #define MC_PILOT_INFO 5
3594 #define MC_SCROLL_LIST_UP 6
3595 #define MC_SCROLL_LIST_DOWN 7
3596 #define MC_SCROLL_PLAYERS_UP 8
3597 #define MC_SCROLL_PLAYERS_DOWN 9
3598 #define MC_MISSION_FILTER 10
3599 #define MC_CAMPAIGN_FILTER 11
3600 #define MC_CANCEL 12
3605 #define MC_SCROLL_INFO_UP 17
3606 #define MC_SCROLL_INFO_DOWN 18
3607 #define MC_HOST_OPTIONS 19
3609 #define MC_OPTIONS 21
3610 #define MC_ACCEPT 22
3613 UI_WINDOW Multi_create_window; // the window object for the create screen
3614 UI_BUTTON Multi_create_player_select_button; // for selecting players
3615 UI_BUTTON Multi_create_list_select_button; // for selecting missions/campaigns
3616 int Multi_create_bitmap; // the background bitmap
3617 UI_SLIDER2 Multi_create_slider; // for create list
3619 // constants for coordinate look ups
3620 #define MC_X_COORD 0
3621 #define MC_Y_COORD 1
3622 #define MC_W_COORD 2
3623 #define MC_H_COORD 3
3625 ui_button_info Multi_create_buttons[GR_NUM_RESOLUTIONS][MULTI_CREATE_NUM_BUTTONS] = {
3628 ui_button_info("MC_18", 34, 131, -1, -1, 18), // all
3629 ui_button_info("MC_19", 72, 131, -1, -1, 19), // coop
3630 ui_button_info("MC_20", 120, 131, -1, -1, 20), // team
3631 // ui_button_info("MC_21", 166, 131, -1, -1, 21), // dogfight
3632 ui_button_info("none", -1, -1, -1, -1, -1), // dogfight (not used)
3633 ui_button_info("none", -1, -1, -1, -1, -1), // pxo?
3634 ui_button_info("MC_26", 540, 114, -1, -1, 26), // pilot info
3635 ui_button_info("MC_03", 0, 187, -1, -1, 2), // scroll list up
3636 ui_button_info("MC_02", 0, 227, -1, -1, 3), // scroll list down
3637 ui_button_info("MC_04", 611, 182, -1, -1, 4), // scroll players up
3638 ui_button_info("MC_05", 611, 221, -1, -1, 5), // scroll players down
3639 ui_button_info("MC_06", 18, 322, -1, -1, 6), // mission filter
3640 ui_button_info("MC_07", 18, 344, -1, -1, 7), // campaign filter
3641 ui_button_info("MC_10", 317, 339, -1, -1, 10), // cancel
3642 ui_button_info("MC_14", 464, 350, -1, -1, 14), // team 1
3643 ui_button_info("MC_15", 498, 350, -1, -1, 15), // team 2
3644 ui_button_info("MC_16", 527, 346, -1, -1, 16), // kick
3645 ui_button_info("MC_17", 572, 346, -1, -1, 17), // close
3646 ui_button_info("MC_08", 0, 398, -1, -1, 8), // scroll mission info up
3647 ui_button_info("MC_09", 0, 435, -1, -1, 9), // scroll mission info down
3648 ui_button_info("MC_27", 447, 402, -1, -1, 27), // host options
3649 ui_button_info("MC_11", 510, 428, -1, -1, 11), // help
3650 ui_button_info("MC_12", 510, 453, -1, -1, 12), // options
3651 ui_button_info("Mc_13", 562, 412, -1, -1, 13), // commit
3653 ui_button_info("MC_00", 32, 129, 36, 158, 0), // show all missions
3654 ui_button_info("MC_01", 76, 129, 71, 158, 1), // show coop missions
3655 ui_button_info("MC_02", 121, 129, 119, 158, 2), // show team missions
3656 ui_button_info("MC_03", 164, 129, 166, 158, 3), // show dogfight missions
3657 ui_button_info("MC_04", 399, 129, 229, 130, 4), // pxo mission refresh
3658 ui_button_info("MC_05", 567, 123, 467, 132, 5), // pilot info
3659 ui_button_info("MC_06", 1, 161, -1, -1, 6), // scroll mission info up
3660 ui_button_info("MC_08", 1, 304, -1, -1, 8), // scroll mission info down
3661 ui_button_info("MC_09", 613, 160, -1, -1, 9), // scroll players up
3662 ui_button_info("MC_10", 613, 202, -1, -1, 10), // scroll players down
3663 ui_button_info("MC_11", 22, 346, 27, 376, 11), // mission filter
3664 ui_button_info("MC_12", 104, 346, 110, 376, 12), // campaign filter
3665 ui_button_info("MC_13", 392, 341, 328, 364, 13), // cancel
3666 ui_button_info("MC_14", 472, 352, 482, 381, 14), // team 0
3667 ui_button_info("MC_15", 506, 352, 514, 381, 15), // team 1
3668 ui_button_info("MC_16", 539, 346, 539, 381, 16), // kick
3669 ui_button_info("MC_17", 589, 346, 582, 381, 17), // close
3670 ui_button_info("MC_18", 1, 406, -1, -1, 18), // scroll list up
3671 ui_button_info("MC_19", 1, 447, -1, -1, 19), // scroll list down
3672 ui_button_info("MC_20", 499, 434, 436, 423, 20), // host options
3673 ui_button_info("MC_21", 534, 426, -1, -1, 21), // help
3674 ui_button_info("MC_22", 534, 452, -1, -1, 22), // options
3675 ui_button_info("MC_23", 571, 426, 572, 413, 23), // commit
3679 ui_button_info("2_MC_00", 51, 207, 61, 253, 0), // show all missions
3680 ui_button_info("2_MC_01", 122, 207, 124, 253, 1), // show coop missions
3681 ui_button_info("2_MC_02", 193, 207, 194, 253, 2), // show team missions
3682 ui_button_info("2_MC_03", 263, 207, 261, 253, 3), // show dogfight missions
3683 ui_button_info("2_MC_04", 639, 207, 479, 218, 4), // pxo mission refresh
3684 ui_button_info("2_MC_05", 907, 197, 748, 216, 5), // pilot info
3685 ui_button_info("2_MC_06", 1, 258, -1, -1, 6), // scroll mission info up
3686 ui_button_info("2_MC_08", 1, 487, -1, -1, 8), // scroll mission info down
3687 ui_button_info("2_MC_09", 981, 256, -1, -1, 9), // scroll players up
3688 ui_button_info("2_MC_10", 981, 323, -1, -1, 10), // scroll players down
3689 ui_button_info("2_MC_11", 35, 554, 46, 601, 11), // mission filter
3690 ui_button_info("2_MC_12", 166, 554, 174, 601, 12), // campaign filter
3691 ui_button_info("2_MC_13", 628, 545, 559, 582, 13), // cancel
3692 ui_button_info("2_MC_14", 756, 564, 772, 610, 14), // team 0
3693 ui_button_info("2_MC_15", 810, 564, 826, 610, 15), // team 1
3694 ui_button_info("2_MC_16", 862, 554, 872, 610, 16), // kick
3695 ui_button_info("2_MC_17", 943, 554, 949, 610, 17), // close
3696 ui_button_info("2_MC_18", 1, 649, -1, -1, 18), // scroll list up
3697 ui_button_info("2_MC_19", 1, 716, -1, -1, 19), // scroll list down
3698 ui_button_info("2_MC_20", 798, 695, 726, 667, 20), // host options
3699 ui_button_info("2_MC_21", 854, 681, -1, -1, 21), // help
3700 ui_button_info("2_MC_22", 854, 724, -1, -1, 22), // options
3701 ui_button_info("2_MC_23", 914, 681, 932, 667, 23), // commit
3706 #define MULTI_CREATE_NUM_TEXT 0
3708 #define MULTI_CREATE_NUM_TEXT 15
3710 UI_XSTR Multi_create_text[GR_NUM_RESOLUTIONS][MULTI_CREATE_NUM_BUTTONS] = {
3712 // not needed for FS1
3714 {"All", 1256, 36, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_ALL].button},
3715 {"Coop", 1257, 71, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_COOP].button},
3716 {"Team", 1258, 119, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_TEAM].button},
3717 {"Dogfight", 1259, 166, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_DOGFIGHT].button},
3718 {"Refresh Missions", 1260, 229, 130, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_PXO_REFRESH].button},
3719 {"Pilot Info", 1261, 467, 132, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_PILOT_INFO].button},
3720 {"Missions", 1262, 27, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_MISSION_FILTER].button},
3721 {"Campaigns", 1263, 110, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_CAMPAIGN_FILTER].button},
3722 {"Cancel", 387, 328, 364, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_CANCEL].button},
3723 {"1", 1264, 482, 381, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_TEAM0].button},
3724 {"2", 1265, 514, 381, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_TEAM1].button},
3725 {"Kick", 1266, 539, 381, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_KICK].button},
3726 {"Close", 1508, 582, 381, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_CLOSE].button},
3727 {"Host Options", 1267, 436, 423, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_HOST_OPTIONS].button},
3728 {"Commit", 1062, 572, 413, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_ACCEPT].button}
3732 // not needed for FS1
3734 {"All", 1256, 61, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_ALL].button},
3735 {"Coop", 1257, 124, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_COOP].button},
3736 {"Team", 1258, 194, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_TEAM].button},
3737 {"Dogfight", 1259, 261, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_DOGFIGHT].button},
3738 {"Refresh Missions", 1260, 501, 218, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_PXO_REFRESH].button},
3739 {"Pilot Info", 1261, 814, 216, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_PILOT_INFO].button},
3740 {"Missions", 1262, 46, 601, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_MISSION_FILTER].button},
3741 {"Campaigns", 1263, 174, 601, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_CAMPAIGN_FILTER].button},
3742 {"Cancel", 387, 559, 582, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_CANCEL].button},
3743 {"1", 1264, 772, 610, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_TEAM0].button},
3744 {"2", 1265, 826, 610, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_TEAM1].button},
3745 {"Kick", 1266, 872, 610, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_KICK].button},
3746 {"Close", 1508, 949, 610, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_CLOSE].button},
3747 {"Host Options", 1267, 755, 683, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_HOST_OPTIONS].button},
3748 {"Commit", 1062, 932, 667, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_ACCEPT].button}
3753 // squad war checkbox
3754 UI_CHECKBOX Multi_create_sw_checkbox;
3755 const char *Multi_create_sw_checkbox_fname[GR_NUM_RESOLUTIONS] = {
3759 int Multi_create_sw_checkbox_coords[GR_NUM_RESOLUTIONS][2] = {
3767 int Multi_create_sw_checkbox_text[GR_NUM_RESOLUTIONS][2] = {
3776 // game information text areas
3777 int Mc_list_coords[GR_NUM_RESOLUTIONS][4] = {
3790 int Mc_players_coords[GR_NUM_RESOLUTIONS][4] = {
3803 int Mc_info_coords[GR_NUM_RESOLUTIONS][4] = {
3816 // mission icon stuff
3817 int Mc_icon_type_coords[GR_NUM_RESOLUTIONS][2] = {
3819 38, -2 // y is an offset
3822 61, -2 // y is an offset
3826 int Mc_icon_volition_coords[GR_NUM_RESOLUTIONS][2] = {
3828 61, -1 // y is an offset
3831 98, 1 // y is an offset
3835 int Mc_icon_silent_coords[GR_NUM_RESOLUTIONS][2] = {
3837 72, 0 // y is an offset
3840 115, 0 // y is an offset
3844 int Mc_icon_valid_coords[GR_NUM_RESOLUTIONS][2] = {
3846 91, 0 // y is an offset
3849 146, 0 // y is an offset
3853 // mission/campaign list column areas
3854 int Mc_column1_w[GR_NUM_RESOLUTIONS] = {
3859 int Mc_column2_w[GR_NUM_RESOLUTIONS] = {
3864 int Mc_column3_w[GR_NUM_RESOLUTIONS] = {
3869 int Mc_mission_name_x[GR_NUM_RESOLUTIONS] = {
3874 int Mc_mission_count_x[GR_NUM_RESOLUTIONS] = {
3879 int Mc_mission_fname_x[GR_NUM_RESOLUTIONS] = {
3884 int Mc_create_game_text[GR_NUM_RESOLUTIONS][2] = {
3885 {13, 116}, // GR_640
3886 {21, 186} // GR_1024
3889 int Mc_players_text[GR_NUM_RESOLUTIONS][2] = {
3890 {467, 150}, // GR_640
3891 {747, 240} // GR_1024
3894 int Mc_team_text[GR_NUM_RESOLUTIONS][2] = {
3895 {484, 342}, // GR_640
3896 {774, 547} // GR_1024
3899 int Mc_slider_coords[GR_NUM_RESOLUTIONS][4] = {
3901 3, 197, 13, 105 // GR_640
3904 5, 316, 20, 168 // GR_1024
3908 const char *Mc_slider_bitmap[GR_NUM_RESOLUTIONS] = {
3913 // player list control thingie defs
3914 #define MULTI_CREATE_PLIST_MAX_DISPLAY 20
3915 int Multi_create_plist_select_flag; // flag indicating if we have a play selected
3916 short Multi_create_plist_select_id; // the net address of the currently selected player (for lookup)
3918 // master tracker details
3919 int Multi_create_frame_count; // framecount
3920 int Multi_create_mt_tried_login; // attempted to login this server on the MT
3922 // mission filter settings
3923 int Multi_create_filter; // what mode we're in
3925 // game/campaign list control defs
3926 int Multi_create_list_max_display[GR_NUM_RESOLUTIONS] = {
3932 int Multi_create_list_count; // number of items in listbox
3933 int Multi_create_list_mode; // 0 == mission mode, 1 == campaign mode
3934 int Multi_create_list_start; // where to start displaying from
3935 int Multi_create_list_select; // which item is currently highlighted
3936 int Multi_create_files_loaded;
3938 char Multi_create_files_array[MULTI_CREATE_MAX_LIST_ITEMS][MAX_FILENAME_LEN];
3940 int Multi_create_mission_count; // how many we have
3941 int Multi_create_campaign_count;
3942 multi_create_info Multi_create_mission_list[MULTI_CREATE_MAX_LIST_ITEMS];
3943 multi_create_info Multi_create_campaign_list[MULTI_CREATE_MAX_LIST_ITEMS];
3945 // use a pointer for the file list. Will point to either the missions or the campaigns
3946 multi_create_info *Multi_create_file_list;
3948 // LOCAL function definitions
3949 void multi_create_check_buttons();
3950 void multi_create_button_pressed(int n);
3951 void multi_create_init_as_server();
3952 void multi_create_init_as_client();
3953 void multi_create_do_netstuff();
3954 void multi_create_plist_scroll_up();
3955 void multi_create_plist_scroll_down();
3956 void multi_create_plist_process();
3957 void multi_create_plist_blit_normal();
3958 void multi_create_plist_blit_team();
3959 void multi_create_list_scroll_up();
3960 void multi_create_list_scroll_down();
3961 void multi_create_list_do();
3962 void multi_create_list_select_item(int n);
3963 void multi_create_list_blit_icons(int list_index, int y_start);
3964 void multi_create_accept_hit();
3965 void multi_create_draw_filter_buttons();
3966 void multi_create_set_selected_team(int team);
3967 short multi_create_get_mouse_id();
3968 int multi_create_ok_to_commit();
3969 int multi_create_verify_cds();
3970 void multi_create_refresh_pxo();
3971 void multi_create_sw_clicked();
3973 // since we can selectively filter out mission/campaign types we always need to map a selected index (which is relative
3974 // to the displayed list), to an absolute index (which is relative to the total file list - some of which may filtered out)
3975 void multi_create_select_to_filename(int select_index, char *filename, const int max_filelen);
3976 int multi_create_select_to_index(int select_index);
3978 int Multi_create_should_show_popup = 0;
3981 // sorting function to sort mission lists.. Basic sorting on mission name
3982 int multi_create_sort_func(const void *a, const void *b)
3984 multi_create_info *m1, *m2;
3986 m1 = (multi_create_info *)a;
3987 m2 = (multi_create_info *)b;
3989 return ( strcmp(m1->name, m2->name) );
3992 void multi_create_setup_list_data(int mode)
3994 int idx,should_sort,switched_modes;
3996 // set the current mode
3999 if((Multi_create_list_mode != mode) && (mode != -1)){
4000 Multi_create_list_mode = mode;
4003 // set up the list pointers
4004 if ( Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ) {
4005 Multi_create_file_list = Multi_create_mission_list;
4006 } else if ( Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ) {
4007 Multi_create_file_list = Multi_create_campaign_list;
4013 // get the mission count based upon the filter selected
4014 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
4015 switch(Multi_create_filter){
4016 case MISSION_TYPE_MULTI:
4017 Multi_create_list_count = Multi_create_mission_count;
4020 Multi_create_list_count = 0;
4021 // find all missions which match
4022 for(idx=0;idx<Multi_create_mission_count;idx++){
4023 if(Multi_create_mission_list[idx].flags & Multi_create_filter){
4024 Multi_create_list_count++;
4028 // if we switched modes and we have more than 0 items, sort them
4029 if(switched_modes && (Multi_create_list_count > 0)){
4034 } else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
4035 switch(Multi_create_filter){
4036 case MISSION_TYPE_MULTI:
4037 Multi_create_list_count = Multi_create_campaign_count;
4040 Multi_create_list_count = 0;
4041 // find all missions which match
4042 for(idx=0;idx<Multi_create_campaign_count;idx++){
4043 if(Multi_create_campaign_list[idx].flags & Multi_create_filter){
4044 Multi_create_list_count++;
4048 // if we switched modes and we have more than 0 items, sort them
4049 if(switched_modes && (Multi_create_list_count > 0)){
4056 // reset the list start and selected indices
4057 Multi_create_list_start = 0;
4058 Multi_create_list_select = -1;
4059 multi_create_list_select_item(Multi_create_list_start);
4061 // sort the list of missions if necessary
4063 qsort(Multi_create_file_list, Multi_create_list_count, sizeof(multi_create_info), multi_create_sort_func);
4068 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);
4072 void multi_create_game_init()
4077 // now make sure to initialze various netgame stuff based upon whether we're on a standalone or not
4078 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4079 multi_create_init_as_server();
4081 multi_create_init_as_client();
4084 // initialize the player list data
4085 Multi_create_plist_select_flag = 0;
4086 Multi_create_plist_select_id = -1;
4088 // create the interface window
4089 Multi_create_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
4090 Multi_create_window.set_mask_bmap(Multi_create_bitmap_mask_fname[gr_screen.res]);
4092 // load the background bitmap
4093 Multi_create_bitmap = bm_load(Multi_create_bitmap_fname[gr_screen.res]);
4094 if(Multi_create_bitmap < 0){
4095 // we failed to load the bitmap - this is very bad
4099 // close any previous existing instances of the chatbox and create a new one
4103 // load the help overlay
4104 help_overlay_load(MULTI_CREATE_OVERLAY);
4105 help_overlay_set_state(MULTI_CREATE_OVERLAY, 0);
4107 // initialize the common notification messaging
4108 multi_common_notify_init();
4110 // use the common interface palette
4111 multi_common_set_palette();
4113 // create the interface buttons
4114 for(idx=0; idx<MULTI_CREATE_NUM_BUTTONS; idx++){
4115 b = &Multi_create_buttons[gr_screen.res][idx];
4117 // create the object
4118 b->button.create(&Multi_create_window, "", b->x, b->y, 1, 1, ((idx == MC_SCROLL_LIST_UP) || (idx == MC_SCROLL_LIST_DOWN)), 1);
4120 // set the sound to play when highlighted
4121 b->button.set_highlight_action(common_play_highlight_sound);
4123 // set the ani for the button
4124 b->button.set_bmaps(b->filename);
4127 b->button.link_hotspot(b->hotspot);
4129 // some special case stuff for the pxo refresh button
4130 if(idx == MC_PXO_REFRESH){
4131 // if not a PXO game, or if I'm not a server disable and hide the button
4132 if(!MULTI_IS_TRACKER_GAME || !MULTIPLAYER_MASTER){
4134 b->button.disable();
4140 for(idx=0; idx<MULTI_CREATE_NUM_TEXT; idx++){
4141 Multi_create_window.add_XSTR(&Multi_create_text[gr_screen.res][idx]);
4144 // if this is a PXO game, enable the squadwar checkbox
4145 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);
4146 Multi_create_sw_checkbox.set_bmaps(Multi_create_sw_checkbox_fname[gr_screen.res], 6, 0);
4147 if(!MULTI_IS_TRACKER_GAME){
4148 Multi_create_sw_checkbox.hide();
4149 Multi_create_sw_checkbox.disable();
4153 // disable squad war button in demo
4154 Multi_create_sw_checkbox.hide();
4155 Multi_create_sw_checkbox.disable();
4158 // initialize the mission type filtering mode
4159 Multi_create_filter = MISSION_TYPE_MULTI;
4161 // initialize the list mode, and load in a list
4162 memset(Multi_create_mission_list, 0, sizeof(multi_create_info) * MULTI_CREATE_MAX_LIST_ITEMS);
4163 memset(Multi_create_campaign_list, 0, sizeof(multi_create_info) * MULTI_CREATE_MAX_LIST_ITEMS);
4164 for(idx=0; idx<MULTI_CREATE_MAX_LIST_ITEMS; idx++){
4165 Multi_create_mission_list[idx].valid_status = MVALID_STATUS_UNKNOWN;
4166 Multi_create_campaign_list[idx].valid_status = MVALID_STATUS_UNKNOWN;
4168 Multi_create_list_mode = MULTI_CREATE_SHOW_MISSIONS;
4169 Multi_create_list_start = -1;
4170 Multi_create_list_select = -1;
4171 Multi_create_list_count = 0;
4174 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);
4177 // create the player list select button
4178 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);
4179 Multi_create_player_select_button.hide();
4181 // create the mission/campaign list select button
4182 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);
4183 Multi_create_list_select_button.hide();
4185 // set hotkeys for a couple of things.
4186 Multi_create_buttons[gr_screen.res][MC_ACCEPT].button.set_hotkey(KEY_CTRLED+SDLK_RETURN);
4188 // init some master tracker stuff
4189 Multi_create_frame_count = 0;
4190 Multi_create_mt_tried_login = 0;
4192 // remove campaign flags
4193 Game_mode &= ~(GM_CAMPAIGN_MODE);
4195 // send any pilots as appropriate
4196 multi_data_send_my_junk();
4197 Multi_create_file_list = Multi_create_mission_list;
4199 Multi_create_campaign_count = 0;
4200 Multi_create_mission_count = 0;
4201 Multi_create_files_loaded = 0;
4204 void multi_create_game_do()
4208 const char *loading_str = XSTR("Loading", 1336);
4212 // set this if we want to show the pilot info popup
4213 Multi_create_should_show_popup = 0;
4215 // first thing is to load the files
4216 if ( !Multi_create_files_loaded ) {
4217 // if I am a client, send a list request to the server for the missions
4218 if ( MULTIPLAYER_CLIENT ) {
4219 send_mission_list_request( MISSION_LIST_REQUEST );
4223 loading_bitmap = bm_load(Multi_create_loading_fname[gr_screen.res]);
4225 // draw the background, etc
4227 GR_MAYBE_CLEAR_RES(Multi_create_bitmap);
4228 if(Multi_create_bitmap != -1){
4229 gr_set_bitmap(Multi_create_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4233 if ( loading_bitmap > -1 ){
4234 gr_set_bitmap(loading_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4236 gr_bitmap( Please_wait_coords[gr_screen.res][MC_X_COORD], Please_wait_coords[gr_screen.res][MC_Y_COORD] );
4238 // draw "Loading" on it
4240 gr_set_color_fast(&Color_normal);
4242 gr_get_string_size(&str_w, &str_h, loading_str);
4243 gr_string((gr_screen.max_w - str_w) / 2, (gr_screen.max_h - str_h) / 2, loading_str);
4249 multi_create_list_load_missions();
4250 multi_create_list_load_campaigns();
4252 // if this is a tracker game, validate missions
4253 if(MULTI_IS_TRACKER_GAME){
4254 multi_update_valid_missions();
4257 // update the file list
4258 multi_create_setup_list_data(MULTI_CREATE_SHOW_MISSIONS);
4261 // don't bother setting netgame state if ont the server
4262 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4263 Netgame.game_state = NETGAME_STATE_FORMING;
4264 send_netgame_update_packet();
4267 // if we're on the standalone we have to tell him that we're now in the host setup screen
4268 Net_player->state = NETPLAYER_STATE_HOST_SETUP;
4269 send_netplayer_update_packet();
4271 Multi_create_files_loaded = 1;
4274 int k = chatbox_process();
4275 k = Multi_create_window.process(k,0);
4278 // same as the cancel button
4280 if(help_overlay_active(MULTI_CREATE_OVERLAY)){
4281 help_overlay_set_state(MULTI_CREATE_OVERLAY,0);
4283 gamesnd_play_iface(SND_USER_SELECT);
4284 multi_quit_game(PROMPT_HOST);
4289 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
4290 help_overlay_set_state(MULTI_CREATE_OVERLAY, 0);
4293 // process any button clicks
4294 multi_create_check_buttons();
4296 // do any network related stuff
4297 multi_create_do_netstuff();
4299 // draw the background, etc
4301 GR_MAYBE_CLEAR_RES(Multi_create_bitmap);
4302 if(Multi_create_bitmap != -1){
4303 gr_set_bitmap(Multi_create_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4307 // if we're not in team vs. team mode, don't draw the team buttons
4308 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
4309 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.hide();
4310 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.hide();
4311 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.disable();
4312 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.disable();
4314 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.enable();
4315 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.enable();
4316 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.unhide();
4317 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.unhide();
4320 // draw the window itself
4321 Multi_create_window.draw();
4323 gr_set_color_fast(&Color_normal);
4326 // draw Create Game text
4327 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));
4329 // draw players text
4330 gr_string(Mc_players_text[gr_screen.res][MC_X_COORD], Mc_players_text[gr_screen.res][MC_Y_COORD], XSTR("Players", 1269));
4332 // draw players text
4333 gr_string(Mc_team_text[gr_screen.res][MC_X_COORD], Mc_team_text[gr_screen.res][MC_Y_COORD], XSTR("Team", 1258));
4336 // process and display the player list
4337 // NOTE : this must be done before the buttons are checked to insure that a player hasn't dropped
4338 multi_create_plist_process();
4339 if(Netgame.type_flags & NG_TYPE_TEAM){
4340 multi_create_plist_blit_team();
4342 multi_create_plist_blit_normal();
4345 // process and display the game/campaign list
4346 multi_create_list_do();
4348 // draw the correct mission filter button
4349 multi_create_draw_filter_buttons();
4351 // display any text in the info area
4352 multi_common_render_text();
4354 // display any pending notification messages
4355 multi_common_notify_do();
4357 // force the correct mission/campaign button to light up
4358 if( Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ){
4359 Multi_create_buttons[gr_screen.res][MC_MISSION_FILTER].button.draw_forced(2);
4361 Multi_create_buttons[gr_screen.res][MC_CAMPAIGN_FILTER].button.draw_forced(2);
4364 // force draw the closed button if it is toggled on
4365 if(Netgame.flags & NG_FLAG_TEMP_CLOSED){
4366 Multi_create_buttons[gr_screen.res][MC_CLOSE].button.draw_forced(2);
4369 // process and show the chatbox thingie
4373 Multi_create_window.draw_tooltip();
4375 // display the voice status indicator
4376 multi_common_voice_display_status();
4378 // blit the help overlay if necessary
4379 help_overlay_maybe_blit(MULTI_CREATE_OVERLAY);
4382 if(MULTI_IS_TRACKER_GAME){
4383 if(Netgame.type_flags & NG_TYPE_SW){
4384 gr_set_color_fast(&Color_bright);
4386 gr_set_color_fast(&Color_normal);
4388 gr_string(Multi_create_sw_checkbox_text[gr_screen.res][0], Multi_create_sw_checkbox_text[gr_screen.res][1], "SquadWar");
4394 // if we're supposed to show the pilot info popup, do it now
4395 if(Multi_create_should_show_popup){
4396 // get the player index and address of the player item the mouse is currently over
4397 if(Multi_create_plist_select_flag){
4398 player_index = find_player_id(Multi_create_plist_select_id);
4399 if(player_index != -1){
4400 multi_pinfo_popup(&Net_players[player_index]);
4405 // increment the frame count
4406 Multi_create_frame_count++;
4409 void multi_create_game_close()
4411 // unload any bitmaps
4412 if(!bm_unload(Multi_create_bitmap)){
4413 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_create_bitmap_fname[gr_screen.res]));
4416 // unload the help overlay
4417 help_overlay_unload(MULTI_CREATE_OVERLAY);
4419 // destroy the chatbox
4422 // destroy the UI_WINDOW
4423 Multi_create_window.destroy();
4426 void multi_create_check_buttons()
4429 for(idx=0;idx<MULTI_CREATE_NUM_BUTTONS;idx++){
4430 // we only really need to check for one button pressed at a time, so we can break after
4432 if(Multi_create_buttons[gr_screen.res][idx].button.pressed()){
4433 multi_create_button_pressed(idx);
4438 // if the squad war checkbox was clicked
4439 if(Multi_create_sw_checkbox.changed()){
4440 multi_create_sw_clicked();
4444 void multi_create_button_pressed(int n)
4450 gamesnd_play_iface(SND_USER_SELECT);
4451 multi_quit_game(PROMPT_HOST);
4454 // if valid commit conditions have not been met
4455 if(!multi_create_ok_to_commit()){
4460 multi_create_accept_hit();
4465 if(!help_overlay_active(MULTI_CREATE_OVERLAY)){
4466 help_overlay_set_state(MULTI_CREATE_OVERLAY,1);
4468 help_overlay_set_state(MULTI_CREATE_OVERLAY,0);
4472 // scroll the info text box up
4473 case MC_SCROLL_INFO_UP:
4474 multi_common_scroll_text_up();
4477 // scroll the info text box down
4478 case MC_SCROLL_INFO_DOWN:
4479 multi_common_scroll_text_down();
4482 // scroll the player list up
4483 case MC_SCROLL_PLAYERS_UP:
4484 multi_create_plist_scroll_up();
4487 // scroll the player list down
4488 case MC_SCROLL_PLAYERS_DOWN:
4489 multi_create_plist_scroll_down();
4492 // scroll the game/campaign list up
4493 case MC_SCROLL_LIST_UP:
4494 multi_create_list_scroll_up();
4496 Multi_create_slider.forceUp(); // move slider up
4500 // scroll the game/campaign list down
4501 case MC_SCROLL_LIST_DOWN:
4502 multi_create_list_scroll_down();
4504 Multi_create_slider.forceDown(); // move slider down
4508 // go to the options screen
4510 gamesnd_play_iface(SND_USER_SELECT);
4511 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
4514 // show all missions
4516 if(Multi_create_filter != MISSION_TYPE_MULTI){
4517 gamesnd_play_iface(SND_USER_SELECT);
4518 Multi_create_filter = MISSION_TYPE_MULTI;
4519 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4521 gamesnd_play_iface(SND_GENERAL_FAIL);
4525 // show cooperative missions
4527 if(Multi_create_filter != MISSION_TYPE_MULTI_COOP){
4528 gamesnd_play_iface(SND_USER_SELECT);
4529 Multi_create_filter = MISSION_TYPE_MULTI_COOP;
4530 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4532 gamesnd_play_iface(SND_GENERAL_FAIL);
4536 // show team vs. team missions
4538 if(Multi_create_filter != MISSION_TYPE_MULTI_TEAMS){
4539 gamesnd_play_iface(SND_USER_SELECT);
4540 Multi_create_filter = MISSION_TYPE_MULTI_TEAMS;
4541 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4543 gamesnd_play_iface(SND_GENERAL_FAIL);
4547 // show dogfight missions
4549 case MC_SHOW_DOGFIGHT:
4550 if (Multi_create_filter != MISSION_TYPE_MULTI_DOGFIGHT){
4551 gamesnd_play_iface(SND_USER_SELECT);
4552 Multi_create_filter = MISSION_TYPE_MULTI_DOGFIGHT;
4553 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4555 gamesnd_play_iface(SND_GENERAL_FAIL);
4560 // toggle temporary netgame closed on/off
4562 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4563 Netgame.flags ^= NG_FLAG_TEMP_CLOSED;
4565 Netgame.options.flags |= MLO_FLAG_TEMP_CLOSED;
4566 multi_options_update_netgame();
4568 gamesnd_play_iface(SND_USER_SELECT);
4571 // kick the currently selected player (if possible)
4573 // lookup the player at the specified index
4574 if(Multi_create_plist_select_flag){
4575 idx = find_player_id(Multi_create_plist_select_id);
4576 // kick him - but don't ban him
4578 multi_kick_player(idx,0);
4583 // switch to individual mission mode and load in a list
4584 case MC_MISSION_FILTER:
4585 if(Multi_create_list_mode != MULTI_CREATE_SHOW_MISSIONS){
4586 Netgame.campaign_mode = MP_SINGLE;
4588 gamesnd_play_iface(SND_USER_SELECT);
4590 // update the file list
4591 multi_create_setup_list_data(MULTI_CREATE_SHOW_MISSIONS);
4593 gamesnd_play_iface(SND_GENERAL_FAIL);
4597 // switch to campaign mode and load in a list
4598 case MC_CAMPAIGN_FILTER:
4599 // switch off squad war
4600 Multi_create_sw_checkbox.set_state(0);
4601 Netgame.type_flags = NG_TYPE_COOP;
4603 if(Multi_create_list_mode != MULTI_CREATE_SHOW_CAMPAIGNS){
4604 Netgame.campaign_mode = MP_CAMPAIGN;
4606 gamesnd_play_iface(SND_USER_SELECT);
4608 // update the file list
4609 multi_create_setup_list_data(MULTI_CREATE_SHOW_CAMPAIGNS);
4611 gamesnd_play_iface(SND_GENERAL_FAIL);
4615 // attempt to set the selected player's team
4617 multi_create_set_selected_team(0);
4618 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4619 multi_team_send_update();
4623 // attempt to set the selected player's team
4625 multi_create_set_selected_team(1);
4626 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4627 multi_team_send_update();
4631 // popup the pilot info dialog for the currently selected pilot (will occur at the end of the frame)
4633 Multi_create_should_show_popup = 1;
4636 // go to the host options screen
4637 case MC_HOST_OPTIONS:
4638 gamesnd_play_iface(SND_USER_SELECT);
4639 gameseq_post_event(GS_EVENT_MULTI_HOST_OPTIONS);
4642 // refresh PXO file list
4643 case MC_PXO_REFRESH:
4644 if(!MULTI_IS_TRACKER_GAME){
4647 multi_create_refresh_pxo();
4651 gamesnd_play_iface(SND_GENERAL_FAIL);
4652 multi_common_add_notify(XSTR("Not implemented yet!",760));
4657 // do stuff like pinging servers, sending out requests, etc
4658 void multi_create_do_netstuff()
4662 // if not on a standalone
4663 void multi_create_init_as_server()
4665 // set me up as the host and master
4666 Net_player->flags |= (NETINFO_FLAG_AM_MASTER | NETINFO_FLAG_GAME_HOST);
4669 // if on a standalone
4670 void multi_create_init_as_client()
4672 Net_player->flags |= NETINFO_FLAG_GAME_HOST;
4675 // scroll up through the player list
4676 void multi_create_plist_scroll_up()
4678 gamesnd_play_iface(SND_GENERAL_FAIL);
4681 // scroll down through the player list
4682 void multi_create_plist_scroll_down()
4684 gamesnd_play_iface(SND_GENERAL_FAIL);
4687 void multi_create_plist_process()
4689 int test_count,idx,player_index;
4691 // first determine if there are 0 players in the game. This should never happen since the host is _always_ in the game
4693 for(idx=0;idx<MAX_PLAYERS;idx++){
4694 // count anyone except the standalone server (if applicable)
4695 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
4699 if(test_count <= 0){
4703 // if we had a selected item but that player has left, select myself instead
4704 if(Multi_create_plist_select_flag){
4705 player_index = find_player_id(Multi_create_plist_select_id);
4706 if(player_index == -1){
4707 Multi_create_plist_select_id = Net_player->player_id;
4710 Multi_create_plist_select_flag = 1;
4711 Multi_create_plist_select_id = Net_player->player_id;
4714 // if the player has clicked somewhere in the player list area
4715 if(Multi_create_player_select_button.pressed()){
4718 // get the player index and address of the player item the mouse is currently over
4719 player_id = multi_create_get_mouse_id();
4720 player_index = find_player_id(player_id);
4721 if(player_index != -1){
4722 Multi_create_plist_select_flag = 1;
4723 Multi_create_plist_select_id = player_id;
4728 void multi_create_plist_blit_normal()
4731 char str[CALLSIGN_LEN+5];
4732 int y_start = Mc_players_coords[gr_screen.res][MC_Y_COORD];
4735 // display all the players
4736 for(idx=0;idx<MAX_PLAYERS;idx++){
4737 // count anyone except the standalone server (if applicable)
4738 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
4742 // highlight him if he's the host
4743 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4744 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4745 gr_set_color_fast(&Color_text_active_hi);
4747 gr_set_color_fast(&Color_bright);
4750 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4751 gr_set_color_fast(&Color_text_active);
4753 gr_set_color_fast(&Color_text_normal);
4757 // optionally draw his CD status
4758 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4759 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4760 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4762 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4765 // make sure the string will fit, then display it
4766 SDL_strlcpy(str, Net_players[idx].player->callsign, SDL_arraysize(str));
4767 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4768 SDL_strlcat(str, XSTR("(O)",787), SDL_arraysize(str)); // [[ Observer ]]
4770 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4771 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4778 void multi_create_plist_blit_team()
4781 char str[CALLSIGN_LEN+1];
4782 int y_start = Mc_players_coords[gr_screen.res][MC_Y_COORD];
4785 // display all the red players first
4786 for(idx=0;idx<MAX_PLAYERS;idx++){
4787 // count anyone except the standalone server (if applicable)
4788 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
4789 // reset total offset
4792 // highlight him if he's the host
4793 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4794 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4795 gr_set_color_fast(&Color_text_active_hi);
4797 // be sure to blit the correct team button
4798 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.draw_forced(2);
4800 gr_set_color_fast(&Color_bright);
4803 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4804 gr_set_color_fast(&Color_text_active);
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_text_normal);
4813 // optionally draw his CD status
4814 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4815 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4816 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4818 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4821 // blit the red team indicator
4822 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
4823 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
4824 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT], 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-2);
4827 total_offset += Multi_common_icon_dims[MICON_TEAM0_SELECT][0] + 1;
4830 if(Multi_common_icons[MICON_TEAM0] != -1){
4831 gr_set_bitmap(Multi_common_icons[MICON_TEAM0], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4832 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4834 total_offset += Multi_common_icon_dims[MICON_TEAM0][0] + 1;
4838 // make sure the string will fit
4839 SDL_strlcpy(str, Net_players[idx].player->callsign, SDL_arraysize(str));
4840 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4841 SDL_strlcat(str, XSTR("(O)",787), SDL_arraysize(str));
4843 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4845 // display him in the correct half of the list depending on his team
4846 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4851 // display all the green players next
4852 for(idx=0;idx<MAX_PLAYERS;idx++){
4853 // count anyone except the standalone server (if applicable)
4854 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
4855 // reset total offset
4858 // highlight him if he's the host
4859 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4860 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4861 gr_set_color_fast(&Color_text_active_hi);
4863 // be sure to blit the correct team button
4864 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.draw_forced(2);
4866 gr_set_color_fast(&Color_bright);
4869 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4870 gr_set_color_fast(&Color_text_active);
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_text_normal);
4879 // optionally draw his CD status
4880 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4881 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4882 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4884 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4887 // blit the red team indicator
4888 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
4889 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
4890 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT], 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-2);
4893 total_offset += Multi_common_icon_dims[MICON_TEAM1_SELECT][0] + 1;
4896 if(Multi_common_icons[MICON_TEAM1] != -1){
4897 gr_set_bitmap(Multi_common_icons[MICON_TEAM1], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4898 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4900 total_offset += Multi_common_icon_dims[MICON_TEAM1][0] + 1;
4904 // make sure the string will fit
4905 SDL_strlcpy(str, Net_players[idx].player->callsign, SDL_arraysize(str));
4906 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4907 SDL_strlcat(str, XSTR("(O)",787), SDL_arraysize(str));
4909 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4911 // display him in the correct half of the list depending on his team
4912 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4918 void multi_create_list_scroll_up()
4920 if(Multi_create_list_start > 0){
4921 Multi_create_list_start--;
4923 gamesnd_play_iface(SND_SCROLL);
4925 gamesnd_play_iface(SND_GENERAL_FAIL);
4929 void multi_create_list_scroll_down()
4931 if((Multi_create_list_count - Multi_create_list_start) > Multi_create_list_max_display[gr_screen.res]){
4932 Multi_create_list_start++;
4934 gamesnd_play_iface(SND_SCROLL);
4936 gamesnd_play_iface(SND_GENERAL_FAIL);
4940 // gets a list of multiplayer misisons
4941 void multi_create_list_load_missions()
4943 char *fname, mission_name[NAME_LENGTH+1];
4947 SDL_snprintf(wild_card, SDL_arraysize(wild_card), "*%s", FS_MISSION_FILE_EXT);
4948 file_count = cf_get_file_list_preallocated(MULTI_CREATE_MAX_LIST_ITEMS, Multi_create_files_array, NULL, CF_TYPE_MISSIONS, wild_card);
4949 Multi_create_mission_count = 0;
4951 // maybe create a standalone dialog
4952 if(Game_mode & GM_STANDALONE_SERVER){
4953 std_create_gen_dialog("Loading missions");
4954 std_gen_set_text("Mission:", 1);
4957 for(idx = 0; idx < file_count; idx++){
4958 int flags,max_players;
4962 fname = Multi_create_files_array[idx];
4964 // tack on any necessary file extension
4965 filename = cf_add_ext( fname, FS_MISSION_FILE_EXT );
4967 // for multiplayer beta builds, only accept builtin missions
4968 #if defined(MULTIPLAYER_BETA_BUILD) || defined(FS2_DEMO)
4969 if(game_find_builtin_mission(filename) == NULL){
4972 #elif defined(PD_BUILD)
4973 if((game_find_builtin_mission(filename) == NULL) && !strstr(filename, "peterdrake")){
4978 if(Game_mode & GM_STANDALONE_SERVER){
4979 std_gen_set_text(filename, 2);
4982 flags = mission_parse_is_multi(filename, mission_name);
4984 // if the mission is a multiplayer mission, then add it to the mission list
4986 max_players = mission_parse_get_multi_mission_info( filename );
4987 m_respawn = The_mission.num_respawns;
4989 if ( Multi_create_mission_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
4990 multi_create_info *mcip;
4992 mcip = &Multi_create_mission_list[Multi_create_mission_count];
4993 SDL_strlcpy(mcip->filename, filename, SDL_arraysize(mcip->filename));
4994 SDL_strlcpy(mcip->name, mission_name, SDL_arraysize(mcip->name));
4995 mcip->flags = flags;
4996 mcip->respawn = m_respawn;
4997 mcip->max_players = (ubyte)max_players;
4999 // get any additional information for possibly builtin missions
5000 fs_builtin_mission *fb = game_find_builtin_mission(filename);
5004 Multi_create_mission_count++;
5010 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);
5013 // maybe create a standalone dialog
5014 if(Game_mode & GM_STANDALONE_SERVER){
5015 std_destroy_gen_dialog();
5019 void multi_create_list_load_campaigns()
5022 int idx, file_count;
5023 int campaign_type,max_players;
5027 // maybe create a standalone dialog
5028 if(Game_mode & GM_STANDALONE_SERVER){
5029 std_create_gen_dialog("Loading campaigns");
5030 std_gen_set_text("Campaign:", 1);
5033 Multi_create_campaign_count = 0;
5034 SDL_snprintf(wild_card, SDL_arraysize(wild_card), "*%s", FS_CAMPAIGN_FILE_EXT);
5035 file_count = cf_get_file_list_preallocated(MULTI_CREATE_MAX_LIST_ITEMS, Multi_create_files_array, NULL, CF_TYPE_MISSIONS, wild_card);
5036 for(idx = 0; idx < file_count; idx++){
5038 char *filename, name[NAME_LENGTH];
5040 fname = Multi_create_files_array[idx];
5042 // tack on any necessary file extension
5043 filename = cf_add_ext( fname, FS_CAMPAIGN_FILE_EXT );
5045 // for multiplayer beta builds, only accept builtin missions
5046 #if defined(MULTIPLAYER_BETA_BUILD) || defined(FS2_DEMO)
5047 if(game_find_builtin_mission(filename) == NULL){
5050 #elif defined(PD_BUILD)
5051 if((game_find_builtin_mission(filename) == NULL) && !strstr(filename, "peterdrake")){
5056 if(Game_mode & GM_STANDALONE_SERVER){
5057 std_gen_set_text(filename, 2);
5060 // if the campaign is a multiplayer campaign, then add the data to the campaign list items
5061 flags = mission_campaign_parse_is_multi( filename, name, SDL_arraysize(name) );
5062 if( flags != CAMPAIGN_TYPE_SINGLE && mission_campaign_get_info(filename,title,&campaign_type,&max_players)) {
5063 if ( Multi_create_campaign_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
5064 multi_create_info *mcip;
5066 mcip = &Multi_create_campaign_list[Multi_create_campaign_count];
5067 SDL_strlcpy(mcip->filename, filename, SDL_arraysize(mcip->filename));
5068 SDL_strlcpy(mcip->name, name, SDL_arraysize(mcip->name));
5070 // setup various flags
5071 if ( flags == CAMPAIGN_TYPE_MULTI_COOP ){
5072 mcip->flags = MISSION_TYPE_MULTI_COOP | MISSION_TYPE_MULTI;
5073 } else if ( flags == CAMPAIGN_TYPE_MULTI_TEAMS ) {
5074 mcip->flags = MISSION_TYPE_MULTI_TEAMS | MISSION_TYPE_MULTI;
5076 Int3(); // bogus campaign multi type -- find allender
5079 // 0 respawns for campaign files (should be contained within the mission files themselves)
5082 // 0 max players for campaign files
5083 mcip->max_players = (unsigned char)max_players;
5085 // get any additional information for possibly builtin missions
5086 fs_builtin_mission *fb = game_find_builtin_mission(filename);
5090 Multi_create_campaign_count++;
5095 // maybe create a standalone dialog
5096 if(Game_mode & GM_STANDALONE_SERVER){
5097 std_destroy_gen_dialog();
5101 void multi_create_list_do()
5104 int start_index,stop_index;
5105 char selected_name[255];
5107 // bail early if there aren't any selectable items
5108 if(Multi_create_list_count == 0){
5112 // first check to see if the user has clicked on an item
5113 if(Multi_create_list_select_button.pressed()){
5115 Multi_create_list_select_button.get_mouse_pos(NULL,&y);
5118 // make sure we are selectedin valid indices
5119 if((item < Multi_create_list_max_display[gr_screen.res]) && (item >= 0)){
5120 item += Multi_create_list_start;
5122 if(item < Multi_create_list_count){
5123 multi_create_list_select_item(item);
5124 gamesnd_play_iface(SND_IFACE_MOUSE_CLICK);
5129 // bail early if we don't have a start position
5130 if(Multi_create_list_start == -1){
5134 // display the list of individual campaigns/missions
5136 int y_start = Mc_list_coords[gr_screen.res][MC_Y_COORD];
5138 start_index = multi_create_select_to_index(Multi_create_list_start);
5139 stop_index = Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ? Multi_create_mission_count : Multi_create_campaign_count;
5140 for(idx=start_index; idx<stop_index; idx++){
5141 // see if we should drop out
5142 if(count == Multi_create_list_max_display[gr_screen.res]){
5146 // see if we should filter out this mission
5147 if ( !(Multi_create_file_list[idx].flags & Multi_create_filter) ){
5151 // highlight the selected item
5152 multi_create_select_to_filename(Multi_create_list_select, selected_name, SDL_arraysize(selected_name));
5153 if(!strcmp(selected_name,Multi_create_file_list[idx].filename)){
5154 gr_set_color_fast(&Color_text_selected);
5156 gr_set_color_fast(&Color_text_normal);
5159 // draw the type icon
5160 multi_create_list_blit_icons(idx, y_start);
5162 // force fit the mission name string
5163 SDL_strlcpy(selected_name, Multi_create_file_list[idx].name, SDL_arraysize(selected_name));
5164 gr_force_fit_string(selected_name,255,Mc_column1_w[gr_screen.res]);
5165 gr_string(Mc_mission_name_x[gr_screen.res],y_start,selected_name);
5167 // draw the max players if in mission mode
5168 SDL_snprintf(selected_name,SDL_arraysize(selected_name),"%d",(int)Multi_create_file_list[idx].max_players);
5169 gr_string(Mc_mission_count_x[gr_screen.res],y_start,selected_name);
5171 // force fit the mission filename string
5172 SDL_strlcpy(selected_name, Multi_create_file_list[idx].filename, SDL_arraysize(selected_name));
5173 gr_force_fit_string(selected_name,255,Mc_column3_w[gr_screen.res]);
5174 gr_string(Mc_mission_fname_x[gr_screen.res],y_start,selected_name);
5181 // takes care of stuff like changing indices around and setting up the netgame structure
5182 void multi_create_list_select_item(int n)
5184 int abs_index,campaign_type,max_players;
5185 char title[NAME_LENGTH+1];
5186 netgame_info ng_temp;
5189 char *campaign_desc;
5191 // if not on the standalone server
5192 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5195 // on the standalone
5197 memset(&ng_temp,0,sizeof(netgame_info));
5201 if ( n != Multi_create_list_select ) {
5202 // check to see if this is a valid index, and bail if it is not
5203 abs_index = multi_create_select_to_index(n);
5204 if(abs_index == -1){
5208 Multi_create_list_select = n;
5210 // set the mission name
5211 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5212 multi_create_select_to_filename(n, ng->mission_name, SDL_arraysize(ng->mission_name));
5214 multi_create_select_to_filename(n, ng->campaign_name, SDL_arraysize(ng->campaign_name));
5217 // make sure the netgame type is properly set
5218 int old_type = Netgame.type_flags;
5219 abs_index = multi_create_select_to_index(n);
5220 if(abs_index != -1){
5221 if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_TEAMS){
5222 // if we're in squad war mode, leave it as squad war
5223 if(old_type & NG_TYPE_SW){
5224 ng->type_flags = NG_TYPE_SW;
5226 ng->type_flags = NG_TYPE_TVT;
5228 } else if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_COOP){
5229 ng->type_flags = NG_TYPE_COOP;
5230 } else if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_DOGFIGHT){
5231 ng->type_flags = NG_TYPE_DOGFIGHT;
5235 // if we're no longer in a TvT game, just uncheck the squadwar checkbox
5236 if(!(ng->type_flags & NG_TYPE_TEAM)){
5237 Multi_create_sw_checkbox.set_state(0);
5240 // if we switched from something else to team vs. team mode, do some special processing
5241 if((ng->type_flags & NG_TYPE_TEAM) && (ng->type_flags != old_type) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
5245 switch(Multi_create_list_mode){
5246 case MULTI_CREATE_SHOW_MISSIONS:
5247 // don't forget to update the info box window thingie
5248 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5249 ship_init(); // mwa -- 10/15/97. Call this function to reset number of ships in mission
5250 ng->max_players = mission_parse_get_multi_mission_info( ng->mission_name );
5252 SDL_assert(ng->max_players > 0);
5253 SDL_strlcpy(ng->title, The_mission.name, SDL_arraysize(ng->title));
5255 // set the information area text
5256 multi_common_set_text(The_mission.mission_desc);
5258 // if we're on the standalone, send a request for the description
5260 send_netgame_descript_packet(&Netgame.server_addr,0);
5261 multi_common_set_text("");
5264 // set the respawns as appropriate
5265 if(Netgame.options.respawn <= Multi_create_file_list[abs_index].respawn){
5266 ng->respawn = Netgame.options.respawn;
5267 nprintf(("Network","Using netgame options for respawn count (%d %d)\n",Netgame.options.respawn,Multi_create_file_list[abs_index].respawn));
5269 ng->respawn = Multi_create_file_list[abs_index].respawn;
5270 nprintf(("Network","Using mission settings for respawn count (%d %d)\n",Netgame.options.respawn,Multi_create_file_list[abs_index].respawn));
5273 case MULTI_CREATE_SHOW_CAMPAIGNS:
5274 // if not on the standalone server
5275 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5276 // get the campaign info
5277 memset(title,0,NAME_LENGTH+1);
5278 if(!mission_campaign_get_info(ng->campaign_name,title,&campaign_type,&max_players, &campaign_desc)) {
5279 memset(ng->campaign_name,0,NAME_LENGTH+1);
5280 ng->max_players = 0;
5282 // if we successfully got the # of players
5284 memset(ng->title,0,NAME_LENGTH+1);
5285 SDL_strlcpy(ng->title, title, SDL_arraysize(ng->title));
5286 ng->max_players = max_players;
5289 nprintf(("Network","MC MAX PLAYERS : %d\n",ng->max_players));
5291 // set the information area text
5292 // multi_common_set_text(ng->title);
5293 multi_common_set_text(campaign_desc);
5295 // if on the standalone server, send a request for the description
5297 // no descriptions currently kept for campaigns
5300 // netgame respawns are always 0 for campaigns (until the first mission is loaded)
5305 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5307 send_netgame_update_packet();
5309 // update all machines about stuff like respawns, etc.
5310 multi_options_update_netgame();
5312 multi_options_update_mission(ng, Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ? 1 : 0);
5317 void multi_create_list_blit_icons(int list_index, int y_start)
5319 multi_create_info *mcip;
5320 fs_builtin_mission *fb;
5323 // get a pointer to the list item
5324 max_index = Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ? Multi_create_mission_count - 1 : Multi_create_campaign_count - 1;
5325 if((list_index < 0) || (list_index > max_index)){
5328 mcip = &Multi_create_file_list[list_index];
5330 // blit the multiplayer type icons
5331 if(mcip->flags & MISSION_TYPE_MULTI_COOP){
5332 if(Multi_common_icons[MICON_COOP] >= 0){
5333 gr_set_bitmap(Multi_common_icons[MICON_COOP], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5334 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5336 } else if(mcip->flags & MISSION_TYPE_MULTI_TEAMS){
5337 if(Multi_common_icons[MICON_TVT] >= 0){
5338 gr_set_bitmap(Multi_common_icons[MICON_TVT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5339 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5341 } else if(mcip->flags & MISSION_TYPE_MULTI_DOGFIGHT){
5342 if(Multi_common_icons[MICON_DOGFIGHT] >= 0){
5343 gr_set_bitmap(Multi_common_icons[MICON_DOGFIGHT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5344 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5348 // if its a valid mission, blit the valid mission icon
5349 if(MULTI_IS_TRACKER_GAME && (mcip->valid_status == MVALID_STATUS_VALID)){
5350 if(Multi_common_icons[MICON_VALID] >= 0){
5351 gr_set_bitmap(Multi_common_icons[MICON_VALID], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5352 gr_bitmap(Mc_icon_valid_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_valid_coords[gr_screen.res][MC_Y_COORD]);
5356 // now see if its a builtin mission
5357 fb = game_find_builtin_mission(mcip->filename);
5358 // if the mission is from volition, blit the volition icon
5359 if((fb != NULL) && (fb->flags & FSB_FROM_VOLITION)){
5360 if(Multi_common_icons[MICON_VOLITION] >= 0){
5361 gr_set_bitmap(Multi_common_icons[MICON_VOLITION], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5362 gr_bitmap(Mc_icon_volition_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_volition_coords[gr_screen.res][MC_Y_COORD]);
5367 // check for mdisk mission
5368 fb = game_find_builtin_mission(mcip->filename);
5369 if((fb != NULL) && (fb->flags & FSB_FROM_MDISK)){
5370 if(Multi_common_icons[MICON_MDISK] >= 0){
5371 gr_set_bitmap(Multi_common_icons[MICON_MDISK], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5372 gr_bitmap(Mc_icon_silent_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_silent_coords[gr_screen.res][MC_Y_COORD]);
5378 void multi_create_accept_hit()
5380 char selected_name[255];
5381 int start_campaign = 0;
5383 // make sure all players have finished joining
5384 if(!multi_netplayer_state_check(NETPLAYER_STATE_JOINED,1)){
5385 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("Please wait until all clients have finished joining",788));
5386 gamesnd_play_iface(SND_GENERAL_FAIL);
5389 gamesnd_play_iface(SND_COMMIT_PRESSED);
5392 // do single mission stuff
5393 switch(Multi_create_list_mode){
5394 case MULTI_CREATE_SHOW_MISSIONS:
5395 if(Multi_create_list_select != -1){
5396 // set the netgame mode
5397 Netgame.campaign_mode = MP_SINGLE;
5399 // setup various filenames and mission names
5400 multi_create_select_to_filename(Multi_create_list_select, selected_name, SDL_arraysize(selected_name));
5401 SDL_strlcpy( Game_current_mission_filename, selected_name, MAX_FILENAME_LEN );
5402 SDL_strlcpy( Netgame.mission_name, selected_name, MAX_FILENAME_LEN );
5405 ml_printf(NOX("Starting single mission %s, with %d players"), Game_current_mission_filename, multi_num_players());
5407 multi_common_add_notify(XSTR("No mission selected!",789));
5412 case MULTI_CREATE_SHOW_CAMPAIGNS:
5413 // do campaign related stuff
5414 if(Multi_create_list_select != -1){
5415 // set the netgame mode
5416 Netgame.campaign_mode = MP_CAMPAIGN;
5418 // start a campaign instead of a single mission
5419 multi_create_select_to_filename(Multi_create_list_select, selected_name, SDL_arraysize(selected_name));
5420 multi_campaign_start(selected_name);
5424 ml_printf(NOX("Starting campaign %s, with %d players"), selected_name, multi_num_players());
5426 multi_common_add_notify(XSTR("No campaign selected!",790));
5432 // if this is a team vs team situation, lock the players send a final team update
5433 if((Netgame.type_flags & NG_TYPE_TEAM) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
5434 multi_team_host_lock_all();
5435 multi_team_send_update();
5438 // if not on the standalone, move to the mission sync state which will take care of everything
5439 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5440 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
5441 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
5443 // otherwise tell the standalone to do so
5445 // when the standalone receives this, he'll do the mission syncing himself
5446 send_mission_sync_packet(MULTI_SYNC_PRE_BRIEFING,start_campaign);
5450 void multi_create_draw_filter_buttons()
5452 // highlight the correct filter button
5453 if ( Multi_create_filter == MISSION_TYPE_MULTI ){
5454 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL].button.draw_forced(2);
5455 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_COOP ) {
5456 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 1].button.draw_forced(2);
5457 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_TEAMS ) {
5458 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 2].button.draw_forced(2);
5459 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_DOGFIGHT ){
5460 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 3].button.draw_forced(2);
5466 void multi_create_set_selected_team(int team)
5470 // if we don't currently have a player selected, don't do anything
5471 if(!Multi_create_plist_select_flag){
5472 gamesnd_play_iface(SND_GENERAL_FAIL);
5475 gamesnd_play_iface(SND_USER_SELECT);
5477 // otherwise attempt to set the team for this guy
5478 player_index = find_player_id(Multi_create_plist_select_id);
5479 if(player_index != -1){
5480 multi_team_set_team(&Net_players[player_index],team);
5484 void multi_create_handle_join(net_player *pl)
5486 // for now just play a bloop sound
5487 gamesnd_play_iface(SND_ICON_DROP_ON_WING);
5490 // fill in net address of player the mouse is over, return player index (or -1 if none)
5491 short multi_create_get_mouse_id()
5493 // determine where he clicked (y pixel value)
5495 Multi_create_player_select_button.get_mouse_pos(NULL,&y);
5497 // select things a little differently if we're in team vs. team or non-team vs. team mode
5499 if(Netgame.type_flags & NG_TYPE_TEAM){
5500 int player_index = -1;
5502 // look through all of team red first
5503 for(idx=0;idx<MAX_PLAYERS;idx++){
5504 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
5507 // if this is the _nth_ guy
5515 // if we still haven't found him yet, look through the green team
5516 if(player_index == -1){
5517 for(idx=0;idx<MAX_PLAYERS;idx++){
5518 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
5520 // if this is the _nth_ guy
5529 if(player_index != -1){
5530 return Net_players[player_index].player_id;
5533 // select the nth active player if possible, disregarding the standalone server
5534 for(idx=0;idx<MAX_PLAYERS;idx++){
5535 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
5538 // if this is the _nth_ guy
5540 return Net_players[idx].player_id;
5549 void multi_create_select_to_filename(int select_index, char *filename, const int max_filelen)
5553 // look through the mission list
5554 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5555 for(idx=0;idx<Multi_create_mission_count;idx++){
5556 if(Multi_create_file_list[idx].flags & Multi_create_filter){
5560 // if we found the item
5561 if(select_index < 0){
5562 SDL_strlcpy(filename, Multi_create_file_list[idx].filename, max_filelen);
5567 // look through the campaign list
5568 else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
5569 for(idx=0;idx<Multi_create_campaign_count;idx++){
5572 // if we found the item
5573 if(select_index < 0){
5574 SDL_strlcpy(filename, Multi_create_file_list[idx].filename, max_filelen);
5580 SDL_strlcpy(filename, "", max_filelen);
5583 int multi_create_select_to_index(int select_index)
5586 int lookup_index = 0;
5588 // look through the mission list
5589 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5590 for(idx=0;idx<Multi_create_mission_count;idx++){
5591 if(Multi_create_file_list[idx].flags & Multi_create_filter){
5595 // if we found the item
5596 if(select_index < lookup_index){
5601 // look through the campaign list
5602 else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
5603 for(idx=0;idx<Multi_create_campaign_count;idx++){
5606 // if we found the item
5607 if(select_index < 0){
5616 int multi_create_ok_to_commit()
5618 int player_count, observer_count, idx;
5619 int notify_of_hacked_ships_tbl = 0;
5620 int notify_of_hacked_weapons_tbl = 0;
5621 char err_string[255];
5625 // make sure we have a valid mission selected
5626 if(Multi_create_list_select < 0){
5630 // if this is not a valid mission, let the player know
5631 abs_index = multi_create_select_to_index(Multi_create_list_select);
5636 // if we're playing with a hacked ships.tbl (on PXO)
5637 notify_of_hacked_ships_tbl = 0;
5638 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5639 if(!Game_ships_tbl_valid){
5640 notify_of_hacked_ships_tbl = 1;
5643 if(Netgame.flags & NG_FLAG_HACKED_SHIPS_TBL){
5644 notify_of_hacked_ships_tbl = 1;
5647 if(!MULTI_IS_TRACKER_GAME){
5648 notify_of_hacked_ships_tbl = 0;
5650 if(notify_of_hacked_ships_tbl){
5651 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){
5656 // if we're playing with a hacked weapons.tbl (on PXO)
5657 notify_of_hacked_weapons_tbl = 0;
5658 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5659 if(!Game_weapons_tbl_valid){
5660 notify_of_hacked_weapons_tbl = 1;
5663 if(Netgame.flags & NG_FLAG_HACKED_WEAPONS_TBL){
5664 notify_of_hacked_weapons_tbl = 1;
5667 if(!MULTI_IS_TRACKER_GAME){
5668 notify_of_hacked_weapons_tbl = 0;
5670 if(notify_of_hacked_weapons_tbl){
5671 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){
5676 // if any of the players have hacked data
5678 for(idx=0; idx<MAX_PLAYERS; idx++){
5679 // look for hacked players
5680 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAXOR)){
5684 // message everyone - haha
5685 if(Net_players[idx].player != NULL){
5686 SDL_snprintf(err_string, SDL_arraysize(err_string), "%s %s", Net_players[idx].player->callsign, XSTR("has hacked tables/data", 1271));
5688 SDL_snprintf(err_string, SDL_arraysize(err_string), "somebody %s", XSTR("has hacked tables/data", 1271));
5690 send_game_chat_packet(Net_player, err_string, MULTI_MSG_ALL, NULL, NULL, 1);
5693 // if we found a hacked set of data
5696 if(MULTI_IS_TRACKER_GAME){
5697 // don't allow squad war matches to continue
5698 if(Netgame.type_flags & NG_TYPE_SW){
5700 // if this is squad war, don't allow it to continue
5701 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));
5706 // otherwise, warn the players that stats will not saved
5708 // if this is squad war, don't allow it to continue
5709 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){
5714 // non-pxo, just give a notice
5716 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){
5722 // check to see that we don't have too many observers
5723 observer_count = multi_num_observers();
5724 if(observer_count > Netgame.options.max_observers){
5725 // print up the error string
5726 SDL_snprintf(err_string,SDL_arraysize(err_string),XSTR("There are too many observers in the game\n\nMax : %d\nCurrently %d\n\nPlease dump a few",791),Netgame.options.max_observers,observer_count);
5728 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, err_string);
5732 // check to see that we have a valid # of players for the the # of ships in the game
5733 player_count = multi_num_players();
5734 if(player_count > Netgame.max_players){
5735 // print up the error string
5736 SDL_snprintf(err_string,SDL_arraysize(err_string),XSTR("There are too many players in the game\n\nMax : %d\nCurrently %d\n\nPlease dump a few", 792), Netgame.max_players,player_count);
5738 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, err_string);
5742 // check to see if teams are assigned properly in a team vs. team situation
5743 if(Netgame.type_flags & NG_TYPE_TEAM){
5744 if(!multi_team_ok_to_commit()){
5745 gamesnd_play_iface(SND_GENERAL_FAIL);
5746 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("Teams and/or team captains are not assigned properly", 793));
5752 if(!multi_create_verify_cds()){
5753 gamesnd_play_iface(SND_GENERAL_FAIL);
5755 #ifdef MULTIPLAYER_BETA_BUILD
5756 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, "You need 1 CD for every player!");
5758 #ifdef DVD_MESSAGE_HACK
5759 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You need 1 DVD for every 4 players!", 794));
5761 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You need 1 CD for every 4 players!", 794));
5767 // if we're playing on the tracker
5768 if(MULTI_IS_TRACKER_GAME){
5769 #ifdef PXO_CHECK_VALID_MISSIONS
5770 if((Multi_create_file_list == Multi_create_mission_list) && (Multi_create_file_list[abs_index].valid_status != MVALID_STATUS_VALID)){
5771 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){
5778 if(!(Netgame.type_flags & NG_TYPE_SW)){
5779 // if he is playing by himself, tell him stats will not be accepted
5780 if(multi_num_players() == 1){
5781 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){
5794 int multi_create_verify_cds()
5796 int player_count = multi_num_players();
5800 // count how many cds we have
5802 for(idx=0;idx<MAX_PLAYERS;idx++){
5803 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAS_CD)){
5808 // for the beta, everyone must have a CD
5809 #ifdef MULTIPLAYER_BETA_BUILD
5810 if(multi_cd_count < player_count){
5814 // determine if we have enough
5815 float ratio = (float)player_count / (float)multi_cd_count;
5816 // greater than a 4 to 1 ratio
5822 // we meet the conditions
5826 // returns an index into Multi_create_mission_list
5827 int multi_create_lookup_mission(char *fname)
5831 for(idx=0; idx<Multi_create_mission_count; idx++){
5832 if(!SDL_strcasecmp(fname, Multi_create_mission_list[idx].filename)){
5837 // couldn't find the mission
5841 // returns an index into Multi_create_campaign_list
5842 int multi_create_lookup_campaign(char *fname)
5846 for(idx=0; idx<Multi_create_campaign_count; idx++){
5847 if(!SDL_strcasecmp(fname, Multi_create_campaign_list[idx].filename)){
5852 // couldn't find the campaign
5856 void multi_create_refresh_pxo()
5858 // delete mvalid.cfg if it exists
5859 cf_delete(MULTI_VALID_MISSION_FILE, CF_TYPE_DATA);
5861 // refresh missions from the tracker
5862 multi_update_valid_missions();
5865 void multi_create_sw_clicked()
5867 netgame_info ng_temp;
5870 int file_index = multi_create_select_to_index(Multi_create_list_select);
5872 // either a temporary netgame or the real one
5873 if(MULTIPLAYER_MASTER){
5880 // maybe switch squad war off
5881 if(!Multi_create_sw_checkbox.checked()){
5882 // if the mission selected is a coop mission, go back to coop mode
5883 SDL_assert(file_index != -1);
5884 if(file_index == -1){
5885 ng->type_flags = NG_TYPE_COOP;
5887 if(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_TEAMS){
5888 ng->type_flags = NG_TYPE_TVT;
5889 } else if(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_DOGFIGHT){
5890 ng->type_flags = NG_TYPE_DOGFIGHT;
5892 ng->type_flags = NG_TYPE_COOP;
5895 // switch squad war on
5897 SDL_assert(file_index != -1);
5898 if((file_index == -1) || !(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_TEAMS)){
5899 Multi_create_sw_checkbox.set_state(0);
5901 // at this point we know its safe to switch squad war on
5902 ng->type_flags = NG_TYPE_SW;
5907 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5909 send_netgame_update_packet();
5911 // update all machines about stuff like respawns, etc.
5912 multi_options_update_netgame();
5914 // on the standalone
5916 // standalone will take care of polling the usertracker
5917 multi_options_update_mission(ng, Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ? 1 : 0);
5922 // -------------------------------------------------------------------------------------------------------------
5924 // MULTIPLAYER HOST OPTIONS SCREEN
5927 #define MULTI_HO_NUM_BUTTONS 12
5928 #define MULTI_HO_NUM_RADIO_BUTTONS 10
5932 #define MULTI_HO_PALETTE "InterfacePalette"
5934 static const char *Multi_ho_bitmap_fname[GR_NUM_RESOLUTIONS] = {
5935 "MultiHost", // GR_640
5936 "2_MultiHost" // GR_1024
5939 static const char *Multi_ho_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
5940 "MultiHost-M", // GR_640
5941 "2_MultiHost-M" // GR_1024
5945 UI_WINDOW Multi_ho_window; // the window object for the join screen
5946 UI_INPUTBOX Multi_ho_respawns; // the # of respawns allowed in the game
5947 UI_INPUTBOX Multi_ho_time_limit; // mission time limit
5948 UI_INPUTBOX Multi_ho_voice_wait; // wait time between tokens
5949 UI_INPUTBOX Multi_ho_kill_limit; // kill limit in a furball mission
5950 UI_INPUTBOX Multi_ho_obs; // # of observers we'll allow
5951 int Multi_ho_bitmap; // the background bitmap
5953 // constants for coordinate lookup
5954 #define MULTI_HO_X_COORD 0
5955 #define MULTI_HO_Y_COORD 1
5956 #define MULTI_HO_W_COORD 2
5957 #define MULTI_HO_H_COORD 3
5958 #define MULTI_HO_TEXT_X_COORD 4
5959 #define MULTI_HO_TEXT_Y_COORD 5
5962 #define MULTI_HO_MSG_RANK 0 // highest ranking players can do messaging
5963 #define MULTI_HO_MSG_LEADER 1 // wing/team leaders can do messaging
5964 #define MULTI_HO_MSG_ANY 2 // any player can do messaging
5965 #define MULTI_HO_MSG_HOST 3 // only the host can do messaging
5966 #define MULTI_HO_END_RANK 4 // highest rank can and host can end mission
5967 #define MULTI_HO_END_LEADER 5 // wing/team leaders and host can end the mission
5968 #define MULTI_HO_END_ANY 6 // any player can end the mission
5969 #define MULTI_HO_END_HOST 7 // only host can end the mission
5970 #define MULTI_HO_VOICE_ON 8 // voice toggled on
5971 #define MULTI_HO_VOICE_OFF 9 // voice toggled off
5972 #define MULTI_HO_HOST_MODIFIES 10 // only the host or team captains can modify ships/weapons in briefing
5973 #define MULTI_HO_ACCEPT 11 // accept button
5975 ui_button_info Multi_ho_buttons[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_BUTTONS] = {
5978 // who is allowed to message
5979 ui_button_info("MH_00", 13, 157, -1, -1, 0), // highest rank
5980 ui_button_info("MH_01", 13, 179, -1, -1, 1), // team/wing leader
5981 ui_button_info("MH_02", 13, 200, -1, -1, 2), // any
5982 ui_button_info("MH_03", 13, 223, -1, -1, 3), // host
5984 // who is allowed to end the mission
5985 ui_button_info("MH_09", 13, 273, -1, -1, 9), // highest rank
5986 ui_button_info("MH_10", 13, 295, -1, -1, 10), // team/wing leader
5987 ui_button_info("MH_11", 13, 317, -1, -1, 11), // any
5988 ui_button_info("MH_12", 13, 339, -1, -1, 12), // host
5990 // voice on/off button
5991 ui_button_info("MH_14", 396, 156, -1, -1, 14),
5992 ui_button_info("MH_15", 453, 156, -1, -1, 15),
5994 // host modifies ships
5995 ui_button_info("MH_19", 215, 410, -1, -1, 19),
5998 ui_button_info("MH_18", 560, 411, -1, -1, 18),
6000 // who is allowed to message
6001 ui_button_info("MH_00", 3, 160, 46, 166, 0), // highest rank
6002 ui_button_info("MH_01", 3, 179, 46, 185, 1), // team/wing leader
6003 ui_button_info("MH_02", 3, 196, 46, 203, 2), // any
6004 ui_button_info("MH_03", 3, 214, 46, 220, 3), // host
6006 // who is allowed to end the mission
6007 ui_button_info("MH_04", 3, 257, 46, 265, 4), // highest rank
6008 ui_button_info("MH_05", 3, 276, 46, 283, 5), // team/wing leader
6009 ui_button_info("MH_06", 3, 294, 46, 300, 6), // any
6010 ui_button_info("MH_07", 3, 311, 46, 317, 7), // host
6012 // voice on/off button
6013 ui_button_info("MH_09", 542, 158, 545, 185, 9),
6014 ui_button_info("MH_10", 598, 158, 604, 185, 10),
6016 // host modifies ships
6017 ui_button_info("MH_13", 542, 377, 437, 363, 13),
6020 ui_button_info("MH_14", 572, 428, 580, 414, 14),
6024 // who is allowed to message
6025 ui_button_info("2_MH_00", 5, 256, 73, 269, 0), // highest rank
6026 ui_button_info("2_MH_01", 5, 286, 73, 297, 1), // team/wing leader
6027 ui_button_info("2_MH_02", 5, 314, 73, 325, 2), // any
6028 ui_button_info("2_MH_03", 5, 341, 73, 352, 3), // host
6030 // who is allowed to end the mission
6031 ui_button_info("2_MH_04", 5, 412, 73, 425, 4), // highest rank
6032 ui_button_info("2_MH_05", 5, 442, 73, 452, 5), // team/wing leader
6033 ui_button_info("2_MH_06", 5, 470, 73, 480, 6), // any
6034 ui_button_info("2_MH_07", 5, 497, 73, 508, 7), // host
6036 // voice on/off button
6037 ui_button_info("2_MH_09", 867, 253, 872, 296, 9),
6038 ui_button_info("2_MH_10", 957, 253, 966, 296, 10),
6040 // host modifies ships
6041 ui_button_info("2_MH_13", 867, 603, 784, 581, 13),
6044 ui_button_info("2_MH_14", 916, 685, 925, 665, 14),
6047 UI_XSTR Multi_ho_text[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_BUTTONS] = {
6049 // not needed for FS1
6051 {"Highest rank", 1280, 46, 166, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_RANK].button},
6052 {"Team / wing-leader", 1281, 46, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_LEADER].button},
6053 {"Any", 1282, 46, 203, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_ANY].button},
6054 {"Host", 1283, 46, 220, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_HOST].button},
6055 {"Highest rank", 1280, 46, 265, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_RANK].button},
6056 {"Team / wing-leader", 1281, 46, 283, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_LEADER].button},
6057 {"Any", 1282, 46, 300, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_ANY].button},
6058 {"Host", 1283, 46, 317, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_HOST].button},
6059 {"On", 1285, 545, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_VOICE_ON].button},
6060 {"Off", 1286, 604, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_VOICE_OFF].button},
6061 {"Host modifies ships", 1287, 437, 363, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_HOST_MODIFIES].button},
6062 {"Exit", 1417, 572, 418, UI_XSTR_COLOR_PINK, -1, &Multi_ho_buttons[0][MULTI_HO_ACCEPT].button},
6066 // not needed for FS1
6068 {"Highest rank", 1280, 62, 269, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_RANK].button},
6069 {"Team / wing-leader", 1281, 62, 297, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_LEADER].button},
6070 {"Any", 1282, 62, 325, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_ANY].button},
6071 {"Host", 1283, 62, 352, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_HOST].button},
6072 {"Highest rank", 1280, 62, 425, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_RANK].button},
6073 {"Team / wing-leader", 1281, 62, 452, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_LEADER].button},
6074 {"Any", 1282, 62, 480, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_ANY].button},
6075 {"Host", 1283, 62, 508, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_HOST].button},
6076 {"On", 1285, 877, 294, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_VOICE_ON].button},
6077 {"Off", 1286, 967, 293, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_VOICE_OFF].button},
6078 {"Host modifies ships", 1287, 869, 589, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_HOST_MODIFIES].button},
6079 {"Exit", 1417, 953, 672, UI_XSTR_COLOR_PINK, -1, &Multi_ho_buttons[1][MULTI_HO_ACCEPT].button},
6084 // radio button controls
6085 #define MULTI_HO_NUM_RADIO_GROUPS 3
6086 #define MULTI_HO_MSG_GROUP 0 // group dealing with squadmate messaging
6087 #define MULTI_HO_END_GROUP 1 // group dealing with ending the mission
6088 #define MULTI_HO_VOICE_GROUP 2 // group dealing with voice stuff
6089 int Multi_ho_radio_groups[MULTI_HO_NUM_RADIO_GROUPS] = { // currently selected button in the radio button group
6092 int Multi_ho_radio_info[MULTI_HO_NUM_RADIO_BUTTONS][3] = { // info related to each of the radio buttons themselves
6093 // { group #, value, button id# }
6094 {0, 0, 0}, // highest ranking players can do messaging
6095 {0, 1, 1}, // wing/team leaders can do messaging
6096 {0, 2, 2}, // any player can do messaging
6097 {0, 3, 3}, // only host can do messaging
6098 {1, 0, 4}, // highest rank and host can end the mission
6099 {1, 1, 5}, // team/wing leader can end the mission
6100 {1, 2, 6}, // any player can end the mission
6101 {1, 3, 7}, // only the host can end the mission
6102 {2, 0, 8}, // voice toggled on
6103 {2, 1, 9} // voice toggled off
6107 #define MULTI_HO_NUM_SLIDERS 3
6108 #define MULTI_HO_SLIDER_VOICE_QOS 0 // voice quality of sound
6109 #define MULTI_HO_SLIDER_VOICE_DUR 1 // max duration of voice recording
6110 #define MULTI_HO_SLIDER_SKILL 2 // skill level
6112 const char *filename;
6117 UI_DOT_SLIDER_NEW slider; // because we have a class inside this struct, we need the constructor below..
6119 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){}
6121 ho_sliders Multi_ho_sliders[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_SLIDERS] = {
6124 ho_sliders("MH_16", 396, 210, -1, -1, 16, 19, 10), // voice qos
6125 ho_sliders("MH_17", 396, 263, -1, -1, 17, 19, 10), // voice duration
6126 ho_sliders("MH_13", 10, 387, -1, -1, 13, 36, 5), // skill level
6128 ho_sliders("MH_11", 428, 214, 437, 199, 11, 19, 10), // voice qos
6129 ho_sliders("MH_12", 428, 261, 437, 246, 12, 19, 10), // voice duration
6130 ho_sliders("MH_08", 237, 454, 230, 411, 8, 36, 5), // skill level
6134 ho_sliders("2_MH_11", 684, 343, 690, 323, 11, 32, 10), // voice qos
6135 ho_sliders("2_MH_12", 685, 418, 837, 468, 12, 32, 10), // voice duration
6136 ho_sliders("2_MH_08", 379, 727, 369, 663, 8, 60, 5), // skill level
6140 int Multi_ho_mission_respawn;
6142 int Multi_ho_host_modifies;
6144 // whether or not any of the inputboxes on this screen had focus last frame
6145 int Multi_ho_lastframe_input = 0;
6147 // game information text areas
6151 #define MULTI_HO_NUM_TITLES 14
6153 UI_XSTR Multi_ho_titles[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_TITLES] = {
6155 { "AI Orders", 1289, 32, 144, UI_XSTR_COLOR_GREEN, -1, NULL },
6156 { "End Mission", 1290, 32, 242, UI_XSTR_COLOR_GREEN, -1, NULL },
6157 { "Time Limit", 1291, 32, 347, UI_XSTR_COLOR_GREEN, -1, NULL },
6158 { "Min", 1292, 74, 362, UI_XSTR_COLOR_GREEN, -1, NULL },
6159 { "Respawn Limit", 1288, 32, 378, UI_XSTR_COLOR_GREEN, -1, NULL },
6160 { "Kill Limit", 1293, 32, 409, UI_XSTR_COLOR_GREEN, -1, NULL },
6161 { "Observers", 1294, 32, 441, UI_XSTR_COLOR_GREEN, -1, NULL },
6162 { "Skill Level", 1284, 230, 411, UI_XSTR_COLOR_GREEN, -1, NULL },
6163 { "Voice Transmission", 1295, 437, 144, UI_XSTR_COLOR_GREEN, -1, NULL },
6164 { "Voice Quality", 1296, 437, 199, UI_XSTR_COLOR_GREEN, -1, NULL },
6165 { "Message Duration", 1297, 437, 246, UI_XSTR_COLOR_GREEN, -1, NULL },
6166 { "sec", 1522, 523, 292, UI_XSTR_COLOR_GREEN, -1, NULL },
6167 { "sec", 1523, 523, 332, UI_XSTR_COLOR_GREEN, -1, NULL },
6168 { "Voice Wait", 1298, 437, 313, UI_XSTR_COLOR_GREEN, -1, NULL },
6171 { "AI Orders", 1289, 48, 238, UI_XSTR_COLOR_GREEN, -1, NULL },
6172 { "End Mission", 1290, 48, 394, UI_XSTR_COLOR_GREEN, -1, NULL },
6173 { "Time Limit", 1291, 50, 568, UI_XSTR_COLOR_GREEN, -1, NULL },
6174 { "Min", 1292, 119, 581, UI_XSTR_COLOR_GREEN, -1, NULL },
6175 { "Respawn Limit", 1288, 50, 618, UI_XSTR_COLOR_GREEN, -1, NULL },
6176 { "Kill Limit", 1293, 50, 668, UI_XSTR_COLOR_GREEN, -1, NULL },
6177 { "Observers", 1294, 50, 718, UI_XSTR_COLOR_GREEN, -1, NULL },
6178 { "Skill Level", 1284, 398, 670, UI_XSTR_COLOR_GREEN, -1, NULL },
6179 { "Voice Transmission", 1295, 869, 239, UI_XSTR_COLOR_GREEN, -1, NULL },
6180 { "Voice Quality", 1296, 690, 331, UI_XSTR_COLOR_GREEN, -1, NULL },
6181 { "Message Duration", 1297, 690, 405, UI_XSTR_COLOR_GREEN, -1, NULL },
6182 { "sec", 1522, 837, 467, UI_XSTR_COLOR_GREEN, -1, NULL },
6183 { "sec", 1523, 837, 534, UI_XSTR_COLOR_GREEN, -1, NULL },
6184 { "Voice Wait", 1298, 742, 510, UI_XSTR_COLOR_GREEN, -1, NULL },
6189 // mission time limit input box
6190 int Ho_time_coords[GR_NUM_RESOLUTIONS][4] = {
6203 // furball kill limit input box
6204 int Ho_kill_coords[GR_NUM_RESOLUTIONS][4] = {
6217 // voice recording duration text display area
6218 int Ho_vd_coords[GR_NUM_RESOLUTIONS][4] = {
6231 // voice token wait input box
6232 int Ho_vw_coords[GR_NUM_RESOLUTIONS][6] = {
6245 // observer count input box
6246 int Ho_obs_coords[GR_NUM_RESOLUTIONS][4] = {
6259 // skill text description area
6260 int Ho_st_coords[GR_NUM_RESOLUTIONS][4] = {
6273 // respawn input box
6274 int Ho_rsp_coords[GR_NUM_RESOLUTIONS][6] = {
6287 // respawn max text area
6288 int Ho_max_rsp_coords[GR_NUM_RESOLUTIONS][2] = {
6301 // maximum values for various input boxes (to notify user of overruns)
6302 #define MULTI_HO_MAX_TIME_LIMIT 500
6303 #define MULTI_HO_MAX_TOKEN_WAIT 5
6304 #define MULTI_HO_MAX_KILL_LIMIT 9999
6305 #define MULTI_HO_MAX_OBS 4
6307 // LOCAL function definitions
6308 void multi_ho_check_buttons();
6309 void multi_ho_button_pressed(int n);
6310 void multi_ho_draw_radio_groups();
6311 void multi_ho_accept_hit();
6312 void multi_ho_get_options();
6313 void multi_ho_apply_options();
6314 void multi_ho_display_record_time();
6315 int multi_ho_check_values();
6316 void multi_ho_check_focus();
6317 void multi_ho_blit_max_respawns();
6318 void multi_ho_display_skill_level();
6320 void multi_host_options_init()
6324 // create the interface window
6325 Multi_ho_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
6326 Multi_ho_window.set_mask_bmap(Multi_ho_bitmap_mask_fname[gr_screen.res]);
6328 // load the background bitmap
6329 Multi_ho_bitmap = bm_load(Multi_ho_bitmap_fname[gr_screen.res]);
6330 if(Multi_ho_bitmap < 0){
6331 // we failed to load the bitmap - this is very bad
6335 // initialize the common notification messaging
6336 multi_common_notify_init();
6338 // use the common interface palette
6339 multi_common_set_palette();
6341 // create the interface buttons
6342 for(idx=0;idx<MULTI_HO_NUM_BUTTONS;idx++){
6343 // create the object
6344 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);
6346 // set the sound to play when highlighted
6347 Multi_ho_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
6349 // set the ani for the button
6350 Multi_ho_buttons[gr_screen.res][idx].button.set_bmaps(Multi_ho_buttons[gr_screen.res][idx].filename);
6352 // set the hotspot, ignoring the skill level button
6353 Multi_ho_buttons[gr_screen.res][idx].button.link_hotspot(Multi_ho_buttons[gr_screen.res][idx].hotspot);
6357 Multi_ho_window.add_XSTR(&Multi_ho_text[gr_screen.res][idx]);
6363 for(idx=0; idx<MULTI_HO_NUM_TITLES; idx++){
6364 Multi_ho_window.add_XSTR(&Multi_ho_titles[gr_screen.res][idx]);
6368 // create the interface sliders
6369 for(idx=0; idx<MULTI_HO_NUM_SLIDERS; idx++){
6370 // create the object
6371 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);
6374 // create the respawn count input box
6375 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);
6376 // if we're in campaign mode, disable it
6377 if(Netgame.campaign_mode == MP_CAMPAIGN){
6378 Multi_ho_respawns.set_text(XSTR("NA",795)); // [[ Not applicable ]]
6379 Multi_ho_respawns.disable();
6381 Multi_ho_respawns.set_valid_chars("0123456789");
6384 // create the time limit input box
6385 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);
6386 Multi_ho_time_limit.set_valid_chars("-0123456789");
6388 // create the voice token wait input box
6389 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);
6390 Multi_ho_voice_wait.set_valid_chars("01243456789");
6392 // create the furball kill limit input box
6393 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);
6394 Multi_ho_kill_limit.set_valid_chars("0123456789");
6396 // create the observer limit input box
6397 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);
6398 Multi_ho_obs.set_valid_chars("01234");
6400 // load in the current netgame defaults
6401 multi_ho_get_options();
6403 // whether or not any of the inputboxes on this screen had focus last frame
6404 Multi_ho_lastframe_input = 0;
6406 // get the # of respawns for the currently selected mission (if any)
6407 if(Multi_create_list_select != -1){
6408 int abs_index = multi_create_select_to_index(Multi_create_list_select);
6410 // if he has a valid mission selected
6412 Multi_ho_mission_respawn = (int)Multi_create_file_list[abs_index].respawn;
6414 Multi_ho_mission_respawn = -1;
6417 Multi_ho_mission_respawn = -1;
6421 void multi_ho_update_sliders()
6423 // game skill slider
6424 if (Game_skill_level != Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos) {
6425 if ( !(Netgame.type_flags & NG_TYPE_TEAM) ){
6426 Game_skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6427 gamesnd_play_iface(SND_USER_SELECT);
6429 Game_skill_level = NUM_SKILL_LEVELS / 2;
6433 // get the voice qos options
6434 if (Netgame.options.voice_qos != (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1)) {
6435 Netgame.options.voice_qos = (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1);
6436 gamesnd_play_iface(SND_USER_SELECT);
6439 // get the voice duration options
6440 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)) {
6441 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);
6442 gamesnd_play_iface(SND_USER_SELECT);
6447 void multi_host_options_do()
6452 k = Multi_ho_window.process();
6455 // process any keypresses
6458 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
6461 case KEY_CTRLED + SDLK_RETURN :
6462 gamesnd_play_iface(SND_COMMIT_PRESSED);
6463 multi_ho_accept_hit();
6467 // process any button clicks
6468 multi_ho_check_buttons();
6470 // update the sliders
6471 multi_ho_update_sliders();
6473 // make sure that the chatbox inputbox and any inputbox on this screen are mutually exclusive in terms of focus
6474 multi_ho_check_focus();
6476 // draw the background, etc
6478 GR_MAYBE_CLEAR_RES(Multi_ho_bitmap);
6479 if(Multi_ho_bitmap != -1){
6480 gr_set_bitmap(Multi_ho_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
6483 Multi_ho_window.draw();
6485 // draw all the radio buttons properly
6486 multi_ho_draw_radio_groups();
6488 // display any pending notification messages
6489 multi_common_notify_do();
6491 // display the voice record time settings
6492 multi_ho_display_record_time();
6494 // maybe display the max # of respawns next to the respawn input box
6495 multi_ho_blit_max_respawns();
6497 // blit the proper skill level
6498 multi_ho_display_skill_level();
6500 // blit the "host modifies button"
6501 if(Multi_ho_host_modifies){
6502 Multi_ho_buttons[gr_screen.res][MULTI_HO_HOST_MODIFIES].button.draw_forced(2);
6505 // process and show the chatbox thingie
6509 Multi_ho_window.draw_tooltip();
6511 // display the voice status indicator
6512 multi_common_voice_display_status();
6518 void multi_host_options_close()
6520 // unload any bitmaps
6521 if(!bm_unload(Multi_ho_bitmap)){
6522 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_ho_bitmap_fname[gr_screen.res]));
6525 // destroy the UI_WINDOW
6526 Multi_ho_window.destroy();
6529 void multi_ho_check_buttons()
6532 for(idx=0;idx<MULTI_HO_NUM_BUTTONS;idx++){
6533 // we only really need to check for one button pressed at a time, so we can break after
6535 if(Multi_ho_buttons[gr_screen.res][idx].button.pressed()){
6536 multi_ho_button_pressed(idx);
6542 void multi_ho_button_pressed(int n)
6544 int radio_index,idx;
6545 int x_pixel,y_pixel;
6547 // get the pixel position of the click
6548 Multi_ho_buttons[gr_screen.res][n].button.get_mouse_pos(&x_pixel,&y_pixel);
6551 // clicked on the accept button
6552 case MULTI_HO_ACCEPT:
6553 gamesnd_play_iface(SND_COMMIT_PRESSED);
6554 multi_ho_accept_hit();
6557 // clicked on the host/captains only modify button
6558 case MULTI_HO_HOST_MODIFIES:
6559 // toggle it on or off
6560 Multi_ho_host_modifies = !Multi_ho_host_modifies;
6561 gamesnd_play_iface(SND_USER_SELECT);
6565 // look through the radio buttons and see which one this corresponds to
6567 for(idx=0;idx<MULTI_HO_NUM_RADIO_BUTTONS;idx++){
6568 if(Multi_ho_radio_info[idx][2] == n){
6573 SDL_assert(radio_index != -1);
6575 // check to see if a radio button was pressed
6576 if(radio_index < MULTI_HO_NUM_RADIO_BUTTONS){
6577 // see if this value is already picked for this radio group
6578 if(Multi_ho_radio_groups[Multi_ho_radio_info[radio_index][0]] != Multi_ho_radio_info[radio_index][1]){
6579 gamesnd_play_iface(SND_USER_SELECT);
6580 Multi_ho_radio_groups[Multi_ho_radio_info[radio_index][0]] = Multi_ho_radio_info[radio_index][1];
6582 gamesnd_play_iface(SND_GENERAL_FAIL);
6587 void multi_ho_draw_radio_groups()
6591 // go through each item and draw it if it is the selected button in its respective group
6592 for(idx=0;idx<MULTI_HO_NUM_RADIO_BUTTONS;idx++){
6593 /// if this button is the currently selected one in its group
6594 if(Multi_ho_radio_info[idx][1] == Multi_ho_radio_groups[Multi_ho_radio_info[idx][0]]){
6595 Multi_ho_buttons[gr_screen.res][Multi_ho_radio_info[idx][2]].button.draw_forced(2);
6600 void multi_ho_accept_hit()
6604 // check the values in the input boxes
6605 if(!multi_ho_check_values()){
6609 // zero out the netgame flags
6612 // set default options
6613 Netgame.options.flags = (MSO_FLAG_INGAME_XFER | MSO_FLAG_ACCEPT_PIX);
6615 // set the squadmate messaging flags
6616 switch(Multi_ho_radio_groups[MULTI_HO_MSG_GROUP]){
6618 Netgame.options.squad_set = MSO_SQUAD_RANK;
6621 Netgame.options.squad_set = MSO_SQUAD_LEADER;
6624 Netgame.options.squad_set = MSO_SQUAD_ANY;
6627 Netgame.options.squad_set = MSO_SQUAD_HOST;
6633 // set the end mission flags
6634 switch(Multi_ho_radio_groups[MULTI_HO_END_GROUP]){
6636 Netgame.options.endgame_set = MSO_END_RANK;
6639 Netgame.options.endgame_set = MSO_END_LEADER;
6642 Netgame.options.endgame_set = MSO_END_ANY;
6645 Netgame.options.endgame_set = MSO_END_HOST;
6651 // set the voice toggle
6652 switch(Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP]){
6654 Netgame.options.flags &= ~(MSO_FLAG_NO_VOICE);
6657 Netgame.options.flags |= MSO_FLAG_NO_VOICE;
6663 // get the voice qos options
6664 Netgame.options.voice_qos = (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1);
6666 // get the voice duration options
6667 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);
6669 // set the skill level. If in team vs. team mode, preserve the old setting before saving
6670 // the pilot file. I'll bet that this doesn't work though because the pilot file gets
6671 // written in a bunch of locations....sigh.
6672 if ( !(Netgame.type_flags & NG_TYPE_TEAM) ){
6673 Game_skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6675 Game_skill_level = NUM_SKILL_LEVELS / 2;
6678 // set the netgame respawn count
6679 // maybe warn the user that respawns will not be used for a campaign mission
6680 if(Netgame.campaign_mode == MP_SINGLE){
6681 Multi_ho_respawns.get_text(resp_str);
6682 uint temp_respawn = (uint)atoi(resp_str);
6683 // if he currently has no mission selected, let the user set any # of respawns
6684 if((int)temp_respawn > Multi_ho_mission_respawn){
6685 if(Multi_ho_mission_respawn == -1){
6686 Netgame.respawn = temp_respawn;
6687 Netgame.options.respawn = temp_respawn;
6689 // this should have been taken care of by the interface code
6694 Netgame.options.respawn = temp_respawn;
6695 Netgame.respawn = temp_respawn;
6699 // get the mission time limit
6700 Multi_ho_time_limit.get_text(resp_str);
6701 int temp_time = atoi(resp_str);
6703 Netgame.options.mission_time_limit = fl2f(-1.0f);
6704 } else if(temp_time > MULTI_HO_MAX_TIME_LIMIT){
6707 Netgame.options.mission_time_limit = fl2f(60.0f * (float)temp_time);
6710 // get observer count options
6711 Multi_ho_obs.get_text(resp_str);
6712 int temp_obs = atoi(resp_str);
6713 if(temp_obs > MULTI_HO_MAX_OBS){
6716 Netgame.options.max_observers = (ubyte)temp_obs;
6718 // get the furball kill limit
6719 Multi_ho_kill_limit.get_text(resp_str);
6720 int temp_kills = atoi(resp_str);
6721 if(temp_kills > MULTI_HO_MAX_KILL_LIMIT){
6724 Netgame.options.kill_limit = temp_kills;
6726 // get the token wait limit
6727 Multi_ho_voice_wait.get_text(resp_str);
6728 int temp_wait = atoi(resp_str);
6729 if(temp_wait > MULTI_HO_MAX_TOKEN_WAIT){
6732 Netgame.options.voice_token_wait = (temp_wait * 1000);
6734 // set the netgame option
6735 Netgame.options.skill_level = (ubyte)Game_skill_level;
6737 // get whether we're in host/captains only modify mode
6738 Netgame.options.flags &= ~(MSO_FLAG_SS_LEADERS);
6739 if(Multi_ho_host_modifies){
6740 Netgame.options.flags |= MSO_FLAG_SS_LEADERS;
6743 // store these values locally
6744 memcpy(&Player->m_local_options,&Net_player->p_info.options,sizeof(multi_local_options));
6745 memcpy(&Player->m_server_options,&Netgame.options,sizeof(multi_server_options));
6746 write_pilot_file(Player);
6748 // apply any changes in settings (notify everyone of voice qos changes, etc)
6749 multi_ho_apply_options();
6751 // move back to the create game screen
6752 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
6755 void multi_ho_get_options()
6759 // set the squadmate messaging buttons
6760 switch(Netgame.options.squad_set){
6761 case MSO_SQUAD_RANK :
6762 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 0;
6764 case MSO_SQUAD_LEADER:
6765 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 1;
6768 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 2;
6770 case MSO_SQUAD_HOST:
6771 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 3;
6777 // set the mission end buttons
6778 switch(Netgame.options.endgame_set){
6780 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 0;
6782 case MSO_END_LEADER:
6783 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 1;
6786 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 2;
6789 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 3;
6795 // set the voice toggle buttons
6796 if(Netgame.options.flags & MSO_FLAG_NO_VOICE){
6797 Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP] = 1;
6799 Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP] = 0;
6802 // get the voice qos options
6803 SDL_assert((Netgame.options.voice_qos >= 1) && (Netgame.options.voice_qos <= 10));
6804 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos = (Netgame.options.voice_qos - 1);
6806 // get the voice duration options
6807 SDL_assert((Netgame.options.voice_record_time > 0) && (Netgame.options.voice_record_time <= MULTI_VOICE_MAX_TIME));
6808 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos = ((int)((float)Netgame.options.voice_record_time / 500.0f)) - 1;
6810 // get the current skill level
6811 SDL_assert((Game_skill_level >= 0) && (Game_skill_level < NUM_SKILL_LEVELS));
6812 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos = Game_skill_level;
6814 // get the # of observers
6815 memset(resp_str,0,10);
6816 SDL_snprintf(resp_str,SDL_arraysize(resp_str),"%d",Netgame.options.max_observers);
6817 Multi_ho_obs.set_text(resp_str);
6819 // set the respawn count
6820 if(Netgame.campaign_mode == MP_SINGLE){
6821 memset(resp_str,0,10);
6822 SDL_snprintf(resp_str,SDL_arraysize(resp_str),"%d",Netgame.respawn);
6823 Multi_ho_respawns.set_text(resp_str);
6826 // set the mission time limit
6827 memset(resp_str,0,10);
6828 float tl = f2fl(Netgame.options.mission_time_limit);
6829 SDL_snprintf(resp_str,SDL_arraysize(resp_str),"%d",(int)(tl / 60.0f));
6830 Multi_ho_time_limit.set_text(resp_str);
6832 // set the furball kill limit
6833 memset(resp_str,0,10);
6834 SDL_snprintf(resp_str,SDL_arraysize(resp_str),"%d",Netgame.options.kill_limit);
6835 Multi_ho_kill_limit.set_text(resp_str);
6837 // set the token wait time
6838 memset(resp_str,0,10);
6839 SDL_snprintf(resp_str,SDL_arraysize(resp_str),"%d",Netgame.options.voice_token_wait / 1000);
6840 Multi_ho_voice_wait.set_text(resp_str);
6842 // get whether we're in host/captains only modify mode
6843 if(Netgame.options.flags & MSO_FLAG_SS_LEADERS){
6844 Multi_ho_host_modifies = 1;
6846 Multi_ho_host_modifies = 0;
6850 void multi_ho_apply_options()
6852 // if the voice qos or duration has changed, apply the change
6853 multi_voice_maybe_update_vars(Netgame.options.voice_qos,Netgame.options.voice_record_time);
6855 // send an options update
6856 multi_options_update_netgame();
6859 // display the voice record time settings
6860 void multi_ho_display_record_time()
6863 int full_seconds, half_seconds;
6866 memset(time_str,0,30);
6869 full_seconds = (((Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 500) / 1000);
6871 // get the half-seconds
6872 half_seconds = ((((Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 500) % 1000) / 500) * 5;
6874 // format the string
6875 SDL_snprintf(time_str,SDL_arraysize(time_str),"%d.%d",full_seconds,half_seconds);
6876 gr_set_color_fast(&Color_bright);
6877 gr_string(Ho_vd_coords[gr_screen.res][MULTI_HO_X_COORD],Ho_vd_coords[gr_screen.res][MULTI_HO_Y_COORD],time_str);
6880 int multi_ho_check_values()
6884 memset(val_txt,0,255);
6886 // check against respawn settings
6887 if(Multi_ho_mission_respawn != -1){
6888 Multi_ho_respawns.get_text(val_txt);
6889 // if the value is invalid, let the user know
6890 if(atoi(val_txt) > Multi_ho_mission_respawn){
6891 memset(val_txt,0,255);
6892 SDL_snprintf(val_txt,SDL_arraysize(val_txt),XSTR("Warning\nRespawn count in greater than mission specified max (%d)",796),Multi_ho_mission_respawn);
6893 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6898 // check against mission time limit max
6899 Multi_ho_time_limit.get_text(val_txt);
6900 // if the value is invalid, force it to be valid
6901 if(atoi(val_txt) > MULTI_HO_MAX_TIME_LIMIT){
6902 memset(val_txt,0,255);
6903 SDL_snprintf(val_txt,SDL_arraysize(val_txt),XSTR("Warning\nMission time limit is greater than max allowed (%d)",797),MULTI_HO_MAX_TIME_LIMIT);
6904 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6908 // check against max observer limit
6909 Multi_ho_obs.get_text(val_txt);
6910 // if the value is invalid, force it to be valid
6911 if(atoi(val_txt) > MULTI_HO_MAX_OBS){
6912 memset(val_txt,0,255);
6913 SDL_snprintf(val_txt,SDL_arraysize(val_txt),XSTR("Warning\nObserver count is greater than max allowed (%d)",798),MULTI_HO_MAX_OBS);
6914 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6918 // check against furball kill limit
6919 Multi_ho_kill_limit.get_text(val_txt);
6920 // if the value is invalid, force it to be valid
6921 if(atoi(val_txt) > MULTI_HO_MAX_KILL_LIMIT){
6922 memset(val_txt,0,255);
6923 SDL_snprintf(val_txt,SDL_arraysize(val_txt),XSTR("Warning\nMission kill limit is greater than max allowed (%d)",799),MULTI_HO_MAX_KILL_LIMIT);
6924 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6928 // check against the token wait limit
6929 Multi_ho_voice_wait.get_text(val_txt);
6930 if(atoi(val_txt) > MULTI_HO_MAX_TOKEN_WAIT){
6931 memset(val_txt,0,255);
6932 SDL_snprintf(val_txt,SDL_arraysize(val_txt),XSTR("Warning\nvoice wait time is greater than max allowed (%d)",800),MULTI_HO_MAX_TOKEN_WAIT);
6933 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6937 // all values are valid
6941 void multi_ho_check_focus()
6943 // if an inputbox has been pressed (hit enter), lose its focus
6944 if (Multi_ho_respawns.pressed() || Multi_ho_time_limit.pressed() || Multi_ho_voice_wait.pressed() || Multi_ho_kill_limit.pressed() || Multi_ho_obs.pressed()) {
6945 Multi_ho_respawns.clear_focus();
6946 Multi_ho_time_limit.clear_focus();
6947 Multi_ho_voice_wait.clear_focus();
6948 Multi_ho_kill_limit.clear_focus();
6949 Multi_ho_obs.clear_focus();
6950 gamesnd_play_iface(SND_COMMIT_PRESSED);
6951 chatbox_set_focus();
6952 Multi_ho_lastframe_input = 0;
6954 } else if(!Multi_ho_lastframe_input) {
6955 // if we didn't have focus last frame
6956 if(Multi_ho_respawns.has_focus() || Multi_ho_time_limit.has_focus() || Multi_ho_kill_limit.has_focus() || Multi_ho_voice_wait.has_focus() ){
6957 chatbox_lose_focus();
6959 Multi_ho_lastframe_input = 1;
6962 // if we _did_ have focus last frame
6964 // if we no longer have focus on any of the input boxes, set the focus on the chatbox
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() && !chatbox_has_focus()){
6966 chatbox_set_focus();
6968 // if the chatbox now has focus, clear all focus from our inputboxes
6969 else if (chatbox_has_focus()) {
6970 Multi_ho_respawns.clear_focus();
6971 Multi_ho_time_limit.clear_focus();
6972 Multi_ho_kill_limit.clear_focus();
6973 Multi_ho_voice_wait.clear_focus();
6975 Multi_ho_lastframe_input = 0;
6980 void multi_ho_blit_max_respawns()
6984 // if we're in campaign mode, do nothing
6985 if(Netgame.campaign_mode == MP_CAMPAIGN){
6989 // otherwise blit the max as specified by the current mission file
6990 SDL_snprintf(string,SDL_arraysize(string),"(%d)",Multi_ho_mission_respawn);
6991 gr_set_color_fast(&Color_normal);
6992 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);
6995 void multi_ho_display_skill_level()
6997 int skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
7000 SDL_assert((skill_level >= 0) && (skill_level < NUM_SKILL_LEVELS));
7001 if((skill_level < 0) || (skill_level >= NUM_SKILL_LEVELS)){
7005 gr_set_color_fast(&Color_bright);
7006 gr_string(Ho_st_coords[gr_screen.res][0], Ho_st_coords[gr_screen.res][1], Skill_level_names(skill_level, 1));
7009 // -------------------------------------------------------------------------------------------------------------
7011 // MULTIPLAYER JOIN SCREEN
7014 #define MULTI_JW_NUM_BUTTONS 8
7018 #define MULTI_JW_PALETTE "InterfacePalette"
7020 static const char *Multi_jw_bitmap_fname[GR_NUM_RESOLUTIONS] = {
7021 "MultiJoinWait", // GR_640
7022 "2_MultiJoinWait" // GR_1024
7025 static const char *Multi_jw_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
7026 "MultiJoinWait-M", // GR_640
7027 "2_MultiJoinWait-M" // GR_1024
7033 #define MJW_SCROLL_PLAYERS_UP 0
7034 #define MJW_SCROLL_PLAYERS_DOWN 1
7037 #define MJW_PILOT_INFO 4
7038 #define MJW_SCROLL_INFO_UP 5
7039 #define MJW_SCROLL_INFO_DOWN 6
7040 #define MJW_CANCEL 7
7042 UI_WINDOW Multi_jw_window; // the window object for the join screen
7043 int Multi_jw_bitmap; // the background bitmap
7045 // constants for coordinate lookup
7046 #define MJW_X_COORD 0
7047 #define MJW_Y_COORD 1
7048 #define MJW_W_COORD 2
7049 #define MJW_H_COORD 3
7051 ui_button_info Multi_jw_buttons[GR_NUM_RESOLUTIONS][MULTI_JW_NUM_BUTTONS] = {
7054 ui_button_info("MJW_00", 0, 50, -1, -1, 0),
7055 ui_button_info("MJW_01", 0, 87, -1, -1, 1),
7056 ui_button_info("MJW_02", 20, 219, -1, -1, 2),
7057 ui_button_info("MJW_03", 73, 219, -1, -1, 3),
7058 ui_button_info("MJW_09", 131, 213, -1, -1, 9),
7059 ui_button_info("MJW_05", 0, 398, -1, -1, 5),
7060 ui_button_info("MJW_06", 0, 435, -1, -1, 6),
7061 ui_button_info("MJW_04", 559, 411, -1, -1, 4),
7063 ui_button_info("MJW_00", 1, 24, -1, -1, 0),
7064 ui_button_info("MJW_01", 1, 66, -1, -1, 1),
7065 ui_button_info("MJW_02", 30, 244, 20, 272, 2),
7066 ui_button_info("MJW_03", 84, 244, 73, 272, 3),
7067 ui_button_info("MJW_04", 139, 242, 134, 272, 4),
7068 ui_button_info("MJW_05", 1, 406, -1, -1, 5),
7069 ui_button_info("MJW_06", 1, 447, -1, -1, 6),
7070 ui_button_info("MJW_07", 577, 428, 570, 414, 7),
7074 ui_button_info("2_MJW_00", 2, 38, -1, -1, 0),
7075 ui_button_info("2_MJW_01", 2, 106, -1, -1, 1),
7076 ui_button_info("2_MJW_02", 48, 390, 47, 435, 2),
7077 ui_button_info("2_MJW_03", 134, 390, 133, 435, 3),
7078 ui_button_info("2_MJW_04", 223, 388, 225, 435, 4),
7079 ui_button_info("2_MJW_05", 2, 649, -1, -1, 5),
7080 ui_button_info("2_MJW_06", 2, 715, -1, -1, 6),
7081 ui_button_info("2_MJW_07", 923, 685, 931, 667, 7),
7086 #define MULTI_JW_NUM_TEXT 7
7088 UI_XSTR Multi_jw_text[GR_NUM_RESOLUTIONS][MULTI_JW_NUM_TEXT] = {
7090 { "Team 1", 1308, 20, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_TEAM0].button },
7091 { "Team 2", 1309, 73, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_TEAM1].button },
7092 { "Pilot", 1310, 134, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_PILOT_INFO].button },
7093 { "Info", 1311, 134, 283, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_PILOT_INFO].button },
7094 { "Cancel", 387, 570, 414, UI_XSTR_COLOR_PINK, -1, &Multi_jw_buttons[0][MJW_CANCEL].button },
7095 { "Players", 1269, 38, 8, UI_XSTR_COLOR_GREEN, -1, NULL },
7096 { "Choose Team", 1312, 27, 231, UI_XSTR_COLOR_GREEN, -1, NULL },
7099 { "Team 1", 1308, 47, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_TEAM0].button },
7100 { "Team 2", 1309, 133, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_TEAM1].button },
7101 { "Pilot", 1310, 225, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_PILOT_INFO].button },
7102 { "Info", 1311, 225, 446, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_PILOT_INFO].button },
7103 { "Cancel", 387, 931, 667, UI_XSTR_COLOR_PINK, -1, &Multi_jw_buttons[1][MJW_CANCEL].button },
7104 { "Players", 1269, 165, 12, UI_XSTR_COLOR_GREEN, -1, NULL },
7105 { "Choose Team", 1312, 45, 373, UI_XSTR_COLOR_GREEN, -1, NULL },
7110 int Mjw_players_coords[GR_NUM_RESOLUTIONS][4] = {
7123 int Mjw_mission_name_coords[GR_NUM_RESOLUTIONS][2] = {
7132 // squad war checkbox
7133 UI_CHECKBOX Multi_jw_sw_checkbox;
7134 const char *Multi_jw_sw_checkbox_fname[GR_NUM_RESOLUTIONS] = {
7138 int Multi_jw_sw_checkbox_coords[GR_NUM_RESOLUTIONS][2] = {
7146 int Multi_jw_sw_checkbox_text[GR_NUM_RESOLUTIONS][2] = {
7156 // player list control thingie defs
7157 #define MULTI_JW_PLIST_MAX_DISPLAY 19
7158 int Multi_jw_plist_select_flag; // indicates whether we currently have a selected player
7159 short Multi_jw_plist_select_id; // id of the current selected player
7160 UI_BUTTON Multi_jw_plist_select_button; // for selecting a player
7162 int Multi_jw_should_show_popup = 0;
7164 // LOCAL function definitions
7165 void multi_jw_check_buttons();
7166 void multi_jw_button_pressed(int n);
7167 void multi_jw_do_netstuff();
7168 void multi_jw_scroll_players_up();
7169 void multi_jw_scroll_players_down();
7170 void multi_jw_plist_process();
7171 void multi_jw_plist_blit_normal();
7172 void multi_jw_plist_blit_team();
7173 short multi_jw_get_mouse_id();
7175 void multi_game_client_setup_init()
7179 // create the interface window
7180 Multi_jw_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
7181 Multi_jw_window.set_mask_bmap(Multi_jw_bitmap_mask_fname[gr_screen.res]);
7183 // load the background bitmap
7184 Multi_jw_bitmap = bm_load(Multi_jw_bitmap_fname[gr_screen.res]);
7185 if(Multi_jw_bitmap < 0){
7186 // we failed to load the bitmap - this is very bad
7190 // initialize the player list data
7191 Multi_jw_plist_select_flag = 0;
7192 Multi_jw_plist_select_id = -1;
7194 // kill any old instances of the chatbox and create a new one
7196 chatbox_create(CHATBOX_FLAG_BIG | CHATBOX_FLAG_DRAW_BOX | CHATBOX_FLAG_BUTTONS);
7198 // initialize the common notification messaging
7199 multi_common_notify_init();
7201 // initialize the common mission info display area.
7202 multi_common_set_text("");
7204 // use the common interface palette
7205 multi_common_set_palette();
7207 // create the interface buttons
7208 for(idx=0; idx<MULTI_JW_NUM_BUTTONS; idx++){
7209 // create the object
7210 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);
7212 // set the sound to play when highlighted
7213 Multi_jw_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
7215 // set the ani for the button
7216 Multi_jw_buttons[gr_screen.res][idx].button.set_bmaps(Multi_jw_buttons[gr_screen.res][idx].filename);
7219 Multi_jw_buttons[gr_screen.res][idx].button.link_hotspot(Multi_jw_buttons[gr_screen.res][idx].hotspot);
7222 // if this is a PXO game, enable the squadwar checkbox
7223 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);
7224 Multi_jw_sw_checkbox.set_bmaps(Multi_jw_sw_checkbox_fname[gr_screen.res], 6, 0);
7225 Multi_jw_sw_checkbox.disable();
7226 if(!MULTI_IS_TRACKER_GAME){
7227 Multi_jw_sw_checkbox.hide();
7232 for(idx=0; idx<MULTI_JW_NUM_TEXT; idx++){
7233 Multi_jw_window.add_XSTR(&Multi_jw_text[gr_screen.res][idx]);
7237 // create the player select list button and hide it
7238 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);
7239 Multi_jw_plist_select_button.hide();
7242 Multi_jw_buttons[gr_screen.res][MJW_CANCEL].button.set_hotkey(SDLK_ESCAPE);
7244 // remove campaign flags
7245 Game_mode &= ~(GM_CAMPAIGN_MODE);
7247 // tell the server we have finished joining
7248 Net_player->state = NETPLAYER_STATE_JOINED;
7249 send_netplayer_update_packet();
7252 ml_printf(NOX("Joined netgame %s"), Netgame.name);
7254 // send any appropriate files
7255 multi_data_send_my_junk();
7258 void multi_game_client_setup_do_frame()
7261 int k = chatbox_process();
7262 char mission_text[255];
7263 k = Multi_jw_window.process(k,0);
7265 Multi_jw_should_show_popup = 0;
7267 // process any button clicks
7268 multi_jw_check_buttons();
7270 // do any network related stuff
7271 multi_jw_do_netstuff();
7273 // draw the background, etc
7275 GR_MAYBE_CLEAR_RES(Multi_jw_bitmap);
7276 if(Multi_jw_bitmap != -1){
7277 gr_set_bitmap(Multi_jw_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7281 // if we're not in team vs. team mode, don't draw the team buttons
7282 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
7283 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.hide();
7284 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.hide();
7285 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.disable();
7286 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.disable();
7288 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.enable();
7289 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.enable();
7290 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.unhide();
7291 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.unhide();
7294 if(MULTI_IS_TRACKER_GAME){
7295 // maybe check the squadwar button
7296 if(Netgame.type_flags & NG_TYPE_SW){
7297 Multi_jw_sw_checkbox.set_state(1);
7298 gr_set_color_fast(&Color_bright);
7300 Multi_jw_sw_checkbox.set_state(0);
7301 gr_set_color_fast(&Color_normal);
7304 gr_string(Multi_jw_sw_checkbox_text[gr_screen.res][0], Multi_jw_sw_checkbox_text[gr_screen.res][1], "SquadWar");
7307 // draw the UI window
7308 Multi_jw_window.draw();
7310 // process and display the player list
7311 // NOTE : this must be done before the buttons are checked to insure that a player hasn't dropped
7312 multi_jw_plist_process();
7313 if(Netgame.type_flags & NG_TYPE_TEAM){
7314 multi_jw_plist_blit_team();
7316 multi_jw_plist_blit_normal();
7319 // display any text in the info area
7320 multi_common_render_text();
7322 // display any pending notification messages
7323 multi_common_notify_do();
7325 // blit the mission filename if possible
7326 if(Netgame.campaign_mode){
7327 if(strlen(Netgame.campaign_name) > 0){
7328 SDL_strlcpy(mission_text, Netgame.campaign_name, SDL_arraysize(mission_text));
7330 if(strlen(Netgame.title) > 0){
7331 SDL_strlcat(mission_text, ", ", SDL_arraysize(mission_text));
7332 SDL_strlcat(mission_text, Netgame.title, SDL_arraysize(mission_text));
7335 gr_set_color_fast(&Color_bright_white);
7336 gr_string(Mjw_mission_name_coords[gr_screen.res][MJW_X_COORD],Mjw_mission_name_coords[gr_screen.res][MJW_Y_COORD],mission_text);
7339 if(strlen(Netgame.mission_name) > 0){
7340 SDL_strlcpy(mission_text, Netgame.mission_name, SDL_arraysize(mission_text));
7342 if(strlen(Netgame.title) > 0){
7343 SDL_strlcat(mission_text, ", ", SDL_arraysize(mission_text));
7344 SDL_strlcat(mission_text, Netgame.title, SDL_arraysize(mission_text));
7347 gr_set_color_fast(&Color_bright_white);
7348 gr_string(Mjw_mission_name_coords[gr_screen.res][MJW_X_COORD],Mjw_mission_name_coords[gr_screen.res][MJW_Y_COORD],mission_text);
7352 // process and show the chatbox thingie
7356 Multi_jw_window.draw_tooltip();
7358 // display the voice status indicator
7359 multi_common_voice_display_status();
7364 // if we're supposed to be displaying a pilot info popup
7365 if(Multi_jw_should_show_popup){
7366 player_index = find_player_id(Multi_jw_plist_select_id);
7367 if(player_index != -1){
7368 multi_pinfo_popup(&Net_players[player_index]);
7373 void multi_game_client_setup_close()
7375 // unload any bitmaps
7376 if(!bm_unload(Multi_jw_bitmap)){
7377 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_jw_bitmap_fname[gr_screen.res]));
7380 // destroy the chatbox
7383 // destroy the UI_WINDOW
7384 Multi_jw_window.destroy();
7387 if(Netgame.game_state == NETGAME_STATE_MISSION_SYNC){
7388 gamesnd_play_iface(SND_COMMIT_PRESSED);
7393 void multi_jw_check_buttons()
7396 for(idx=0;idx<MULTI_JW_NUM_BUTTONS;idx++){
7397 // we only really need to check for one button pressed at a time, so we can break after
7399 if(Multi_jw_buttons[gr_screen.res][idx].button.pressed()){
7400 multi_jw_button_pressed(idx);
7406 void multi_jw_button_pressed(int n)
7410 gamesnd_play_iface(SND_USER_SELECT);
7411 multi_quit_game(PROMPT_CLIENT);
7413 case MJW_SCROLL_PLAYERS_UP:
7414 multi_jw_scroll_players_up();
7416 case MJW_SCROLL_PLAYERS_DOWN:
7417 multi_jw_scroll_players_down();
7419 case MJW_SCROLL_INFO_UP:
7420 multi_common_scroll_text_up();
7422 case MJW_SCROLL_INFO_DOWN:
7423 multi_common_scroll_text_down();
7426 // request to set myself to team 0
7428 gamesnd_play_iface(SND_USER_SELECT);
7429 multi_team_set_team(Net_player,0);
7432 // request to set myself to team 1
7434 gamesnd_play_iface(SND_USER_SELECT);
7435 multi_team_set_team(Net_player,1);
7439 case MJW_PILOT_INFO:
7440 Multi_jw_should_show_popup = 1;
7444 multi_common_add_notify(XSTR("Not implemented yet!",760));
7449 // do stuff like pinging servers, sending out requests, etc
7450 void multi_jw_do_netstuff()
7454 void multi_jw_scroll_players_up()
7456 gamesnd_play_iface(SND_GENERAL_FAIL);
7459 // scroll down through the player list
7460 void multi_jw_scroll_players_down()
7462 gamesnd_play_iface(SND_GENERAL_FAIL);
7465 void multi_jw_plist_process()
7467 int test_count,player_index,idx;
7469 // first determine if there are 0 players in the game. This should never happen since the host is _always_ in the game
7471 for(idx=0;idx<MAX_PLAYERS;idx++){
7472 // count anyone except the standalone server (if applicable)
7473 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7477 if(test_count <= 0){
7481 // if we had a selected item but that player has left, select myself instead
7482 if(Multi_jw_plist_select_flag){
7483 player_index = find_player_id(Multi_jw_plist_select_id);
7484 if(player_index == -1){
7485 Multi_jw_plist_select_id = Net_player->player_id;
7488 Multi_jw_plist_select_flag = 1;
7489 Multi_jw_plist_select_id = Net_player->player_id;
7492 // if the player has clicked somewhere in the player list area
7493 if(Multi_jw_plist_select_button.pressed()){
7497 player_id = multi_jw_get_mouse_id();
7498 player_index = find_player_id(player_id);
7499 if(player_index != -1){
7500 Multi_jw_plist_select_id = player_id;
7501 Multi_jw_plist_select_flag = 1;
7506 void multi_jw_plist_blit_normal()
7509 char str[CALLSIGN_LEN+1];
7510 int y_start = Mjw_players_coords[gr_screen.res][MJW_Y_COORD];
7513 // display all the players
7514 for(idx=0;idx<MAX_PLAYERS;idx++){
7515 // reset total offset
7518 // count anyone except the standalone server (if applicable)
7519 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7520 // highlight him if he's the host
7521 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7522 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7523 gr_set_color_fast(&Color_text_active_hi);
7525 gr_set_color_fast(&Color_bright);
7528 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7529 gr_set_color_fast(&Color_text_active);
7531 gr_set_color_fast(&Color_text_normal);
7535 // optionally draw his CD status
7536 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7537 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7538 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7540 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7543 // make sure the string will fit, then display it
7544 SDL_strlcpy(str, Net_players[idx].player->callsign, SDL_arraysize(str));
7545 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
7546 SDL_strlcat(str, "(0)", SDL_arraysize(str));
7548 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7549 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7556 void multi_jw_plist_blit_team()
7559 char str[CALLSIGN_LEN+1];
7560 int y_start = Mjw_players_coords[gr_screen.res][MJW_Y_COORD];
7563 // always blit the proper team button based on _my_ team status
7564 if(Net_player->p_info.team == 0){
7565 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.draw_forced(2);
7567 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.draw_forced(2);
7570 // display all the red players first
7571 for(idx=0;idx<MAX_PLAYERS;idx++){
7572 // reset total offset
7575 // count anyone except the standalone server (if applicable)
7576 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
7577 // highlight him if he's the host
7578 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7579 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7580 gr_set_color_fast(&Color_text_active_hi);
7582 gr_set_color_fast(&Color_bright);
7585 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7586 gr_set_color_fast(&Color_text_active);
7588 gr_set_color_fast(&Color_text_normal);
7592 // optionally draw his CD status
7593 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7594 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7595 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7597 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7600 // blit the red team indicator
7601 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
7602 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
7603 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT], 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-2);
7606 total_offset += Multi_common_icon_dims[MICON_TEAM0_SELECT][0] + 1;
7609 if(Multi_common_icons[MICON_TEAM0] != -1){
7610 gr_set_bitmap(Multi_common_icons[MICON_TEAM0], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7611 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7613 total_offset += Multi_common_icon_dims[MICON_TEAM0][0] + 1;
7617 // make sure the string will fit
7618 SDL_strlcpy(str, Net_players[idx].player->callsign, SDL_arraysize(str));
7619 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7621 // display him in the correct half of the list depending on his team
7622 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7627 // display all the green players next
7628 for(idx=0;idx<MAX_PLAYERS;idx++){
7629 // reset total offset
7632 // count anyone except the standalone server (if applicable)
7633 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
7634 // highlight him if he's the host
7635 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7636 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7637 gr_set_color_fast(&Color_text_active_hi);
7639 gr_set_color_fast(&Color_bright);
7642 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7643 gr_set_color_fast(&Color_text_active);
7645 gr_set_color_fast(&Color_text_normal);
7649 // optionally draw his CD status
7650 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7651 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7652 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7654 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7657 // blit the red team indicator
7658 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
7659 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
7660 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT], 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-2);
7663 total_offset += Multi_common_icon_dims[MICON_TEAM1_SELECT][0] + 1;
7666 if(Multi_common_icons[MICON_TEAM1] != -1){
7667 gr_set_bitmap(Multi_common_icons[MICON_TEAM1], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7668 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7670 total_offset += Multi_common_icon_dims[MICON_TEAM1][0] + 1;
7674 // make sure the string will fit
7675 SDL_strlcpy(str, Net_players[idx].player->callsign, SDL_arraysize(str));
7676 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
7677 SDL_strlcat(str, "(0)", SDL_arraysize(str));
7679 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7681 // display him in the correct half of the list depending on his team
7682 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7688 void multi_jw_handle_join(net_player *pl)
7690 // for now just play a bloop sound
7691 gamesnd_play_iface(SND_ICON_DROP_ON_WING);
7694 short multi_jw_get_mouse_id()
7696 // determine where he clicked (y pixel value)
7698 Multi_jw_plist_select_button.get_mouse_pos(NULL,&y);
7700 // select things a little differently if we're in team vs. team or non-team vs. team mode
7702 if(Netgame.type_flags & NG_TYPE_TEAM){
7703 int player_index = -1;
7705 // look through all of team red first
7706 for(idx=0;idx<MAX_PLAYERS;idx++){
7707 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
7710 // if this is the _nth_ guy
7718 // if we still haven't found him yet, look through the green team
7719 if(player_index == -1){
7720 for(idx=0;idx<MAX_PLAYERS;idx++){
7721 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
7723 // if this is the _nth_ guy
7731 if(player_index != -1){
7732 return Net_players[idx].player_id;
7735 // select the nth active player if possible, disregarding the standalone server
7736 for(idx=0;idx<MAX_PLAYERS;idx++){
7737 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7740 // if this is the _nth_ guy
7742 return Net_players[idx].player_id;
7753 // -------------------------------------------------------------------------------------------------------------
7755 // MULTIPLAYER WAIT/SYNCH SCREEN
7758 #define MULTI_SYNC_HOST_COUNT 4 // host uses 4 buttons (and sometimes 5)
7759 #define MULTI_SYNC_CLIENT_COUNT 3 // client only uses 3 buttons
7761 const char *Multi_sync_bitmap_fname[GR_NUM_RESOLUTIONS] = {
7762 "MultiSynch", // GR_640
7763 "2_MultiSynch" // GR_1024
7766 const char *Multi_sync_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
7767 "MultiSynch-M", // GR_640
7768 "2_MultiSynch-M" // GR_1024
7773 // constants for coordinate lookup
7774 #define MS_X_COORD 0
7775 #define MS_Y_COORD 1
7776 #define MS_W_COORD 2
7777 #define MS_H_COORD 3
7779 UI_WINDOW Multi_sync_window; // the window object for the join screen
7780 int Multi_sync_button_count; // the # of buttons to use for this instance of mission sync
7781 int Multi_sync_bitmap; // the background bitmap
7784 #define MULTI_SYNC_NUM_BUTTONS 5
7785 #define MS_SCROLL_INFO_UP 0
7786 #define MS_SCROLL_INFO_DOWN 1
7790 ui_button_info Multi_sync_buttons[GR_NUM_RESOLUTIONS][MULTI_SYNC_NUM_BUTTONS] = {
7793 ui_button_info("MS_00", 0, 396, -1, -1, 0),
7794 ui_button_info("MS_01", 0, 435, -1, -1, 1),
7795 ui_button_info("MS_02", 496, 411, -1, -1, 2),
7796 ui_button_info("MS_04", 436, 403, -1, -1, 4),
7797 ui_button_info("MS_03", 556, 398, -1, -1, 3),
7799 ui_button_info("MS_00", 1, 404, -1, -1, 0),
7800 ui_button_info("MS_01", 1, 446, -1, -1, 1),
7801 ui_button_info("MS_03", 518, 426, 519, 416, 3),
7802 ui_button_info("MS_02", 469, 426, 479, 416, 2),
7803 ui_button_info("MS_04", 571, 420, 577, 416, 4),
7807 ui_button_info("2_MS_00", 2, 647, -1, -1, 0),
7808 ui_button_info("2_MS_01", 2, 713, -1, -1, 1),
7809 ui_button_info("2_MS_03", 829, 682, 831, 667, 3),
7810 ui_button_info("2_MS_02", 751, 682, 766, 667, 2),
7811 ui_button_info("2_MS_04", 914, 672, 924, 667, 4),
7817 #define MULTI_SYNC_NUM_TEXT 5
7819 #define MST_LAUNCH 2
7820 UI_XSTR Multi_sync_text[GR_NUM_RESOLUTIONS][MULTI_SYNC_NUM_TEXT] = {
7822 { "Kick", 1266, 479, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_KICK].button },
7823 { "Cancel", 387, 519, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_CANCEL].button },
7824 { "Launch", 801, 577, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_LAUNCH].button },
7825 { "Players", 1269, 23, 133, UI_XSTR_COLOR_GREEN, -1, NULL },
7826 { "Status", 1304, 228, 133, UI_XSTR_COLOR_GREEN, -1, NULL }
7829 { "Kick", 1266, 766, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_KICK].button },
7830 { "Cancel", 387, 831, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_CANCEL].button },
7831 { "Launch", 801, 924, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_LAUNCH].button },
7832 { "Players", 1269, 38, 214, UI_XSTR_COLOR_GREEN, -1, NULL },
7833 { "Status", 1304, 366, 214, UI_XSTR_COLOR_GREEN, -1, NULL }
7839 int Ms_status_coords[GR_NUM_RESOLUTIONS][4] = {
7848 // player status coords
7849 int Ms_status2_coords[GR_NUM_RESOLUTIONS][4] = {
7858 int Ms_cd_icon_offset[GR_NUM_RESOLUTIONS] = {
7863 int Ms_team_icon_offset[GR_NUM_RESOLUTIONS] = {
7868 // player currently selected, index into Net_players[]
7869 int Multi_sync_player_select = -1;
7871 // player list control thingie defs
7872 #define MULTI_SYNC_PLIST_MAX_DISPLAY 15
7873 int Multi_sync_plist_start; // where to start displaying from
7874 int Multi_sync_plist_count; // how many we have
7876 // list select button
7877 UI_BUTTON Multi_sync_plist_button;
7879 int Multi_sync_mode = -1;
7881 #define MULTI_SYNC_COUNTDOWN_TIME 5 // in seconds
7882 float Multi_sync_countdown_timer;
7883 int Multi_sync_countdown = -1;
7885 int Multi_launch_button_created;
7888 // countdown animation timer
7889 const char* Multi_sync_countdown_fname[GR_NUM_RESOLUTIONS] = {
7891 "2_Count" // GR_1024
7894 int Multi_sync_countdown_coords[GR_NUM_RESOLUTIONS][2] = {
7905 anim *Multi_sync_countdown_anim = NULL;
7906 anim_instance *Multi_sync_countdown_instance = NULL;
7909 // PREBRIEFING STUFF
7910 // syncing flags used by the server
7911 int Mission_sync_flags = 0;
7912 #define MS_FLAG_SENT_FILESIG (1<<0) // sent filesig requests
7913 #define MS_FLAG_SENT_LOAD (1<<1) // sent load packets
7914 #define MS_FLAG_PUSHED_BRIEFING (1<<2) // pushed everyone else into the briefing
7915 #define MS_FLAG_POST_DATA (1<<3) // sent the post data block
7916 #define MS_FLAG_WSS_SLOTS (1<<4) // all players have received wss slot data
7917 #define MS_FLAG_PSETTINGS (1<<5) // send the player settings packet
7918 #define MS_FLAG_MT_STATS_START (1<<6) // server has started getting player stats from the tracker
7919 #define MS_FLAG_MT_STATS_DONE (1<<7) // server has finished getting player stats from the tracker (success or fail)
7920 #define MS_FLAG_TS_SLOTS (1<<8) // team/ship slots have been sent
7921 #define MS_FLAG_DATA_DONE (1<<9) // done transferring all necessary data
7922 #define MS_FLAG_CAMP_DONE (1<<10) // send campaign pool/goal/event stuff
7924 // POSTBRIEFING STUFF
7925 int Multi_state_timestamp;
7926 int Multi_sync_launch_pressed;
7928 // LOCAL function definitions
7929 void multi_sync_check_buttons();
7930 void multi_sync_button_pressed(int n);
7931 void multi_sync_scroll_info_up();
7932 void multi_sync_scroll_info_down();
7933 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
7934 void multi_sync_display_status(const char *status, int index); // display info on the right hand portion of the status window thingie
7935 void multi_sync_force_start_pre();
7936 void multi_sync_force_start_post();
7937 void multi_sync_launch();
7938 void multi_sync_create_launch_button();
7939 void multi_sync_blit_screen_all();
7940 void multi_sync_handle_plist();
7942 void multi_sync_common_init();
7943 void multi_sync_common_do();
7944 void multi_sync_common_close();
7946 void multi_sync_pre_init();
7947 void multi_sync_pre_do();
7948 void multi_sync_pre_close();
7950 void multi_sync_post_init();
7951 void multi_sync_post_do();
7952 void multi_sync_post_close();
7957 // perform the correct init functions
7958 void multi_sync_init()
7960 Multi_sync_countdown = -1;
7964 // reset all timestamp
7965 multi_reset_timestamps();
7967 extern time_t Player_multi_died_check;
7968 Player_multi_died_check = -1;
7970 if(!(Game_mode & GM_STANDALONE_SERVER)){
7971 multi_sync_common_init();
7974 switch(Multi_sync_mode){
7975 case MULTI_SYNC_PRE_BRIEFING:
7976 multi_sync_pre_init();
7978 case MULTI_SYNC_POST_BRIEFING:
7979 multi_sync_post_init();
7981 case MULTI_SYNC_INGAME:
7982 multi_ingame_sync_init();
7987 // perform the correct do frame functions
7988 void multi_sync_do()
7990 if(!(Game_mode & GM_STANDALONE_SERVER)){
7991 multi_sync_common_do();
7994 // if the netgame is ending, don't do any sync processing
7995 if(multi_endgame_ending()){
7999 // process appropriateliy
8000 switch(Multi_sync_mode){
8001 case MULTI_SYNC_PRE_BRIEFING:
8002 multi_sync_pre_do();
8004 case MULTI_SYNC_POST_BRIEFING:
8005 multi_sync_post_do();
8007 case MULTI_SYNC_INGAME:
8008 multi_ingame_sync_do();
8011 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8012 if(Multi_sync_bitmap != -1){
8013 gr_set_bitmap(Multi_sync_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8016 Multi_sync_window.draw();
8018 multi_sync_blit_screen_all();
8025 // perform the correct close functions
8026 void multi_sync_close()
8028 switch(Multi_sync_mode){
8029 case MULTI_SYNC_PRE_BRIEFING:
8030 multi_sync_pre_close();
8032 case MULTI_SYNC_POST_BRIEFING:
8033 multi_sync_post_close();
8035 case MULTI_SYNC_INGAME:
8036 multi_ingame_sync_close();
8040 if(!(Game_mode & GM_STANDALONE_SERVER)){
8041 multi_sync_common_close();
8045 const char *multi_sync_tooltip_handler(const char *str)
8047 if (!SDL_strcasecmp(str, NOX("@launch"))) {
8048 if (Multi_launch_button_created){
8049 return XSTR("Launch",801);
8056 void multi_sync_common_init()
8060 // create the interface window
8061 Multi_sync_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
8062 Multi_sync_window.set_mask_bmap(Multi_sync_bitmap_mask_fname[gr_screen.res]);
8063 Multi_sync_window.tooltip_handler = multi_sync_tooltip_handler;
8065 // load the background bitmap
8066 Multi_sync_bitmap = bm_load(Multi_sync_bitmap_fname[gr_screen.res]);
8067 if (Multi_sync_bitmap < 0) {
8068 // we failed to load the bitmap - this is very bad
8072 // initialize the player list data
8073 Multi_sync_plist_start = 0;
8074 Multi_sync_plist_count = 1; // we can pretty safely assume that there's one player in the game - me.
8076 Multi_launch_button_created = 0;
8078 // create the chatbox thingie (shouldn't be necesary to do this, but we'll put it in for good measure)
8081 // force the chatbox to be small
8082 chatbox_force_small();
8084 // initialize the common notification messaging
8085 multi_common_notify_init();
8087 // initialize the common mission info display area.
8088 multi_common_set_text("");
8090 // use the common interface palette
8091 multi_common_set_palette();
8093 // don't select any player yet.
8094 Multi_sync_player_select = -1;
8096 // determine how many of the 5 buttons to create
8097 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8098 Multi_sync_button_count = MULTI_SYNC_HOST_COUNT;
8100 Multi_sync_button_count = MULTI_SYNC_CLIENT_COUNT;
8102 // create the interface buttons
8103 for(idx=0; idx<Multi_sync_button_count; idx++){
8104 // create the object
8105 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);
8107 // set the sound to play when highlighted
8108 Multi_sync_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
8110 // set the ani for the button
8111 // this wierdness is necessary because cancel and kick buttons aren't drawn on the background bitmap,
8112 // so we have to load in frame 0, too (the file should exist)
8113 if ((idx == MS_CANCEL) || (idx == MS_KICK) || (idx == MS_LAUNCH)) {
8114 Multi_sync_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sync_buttons[gr_screen.res][idx].filename, 3, 0);
8116 Multi_sync_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sync_buttons[gr_screen.res][idx].filename);
8120 Multi_sync_buttons[gr_screen.res][idx].button.link_hotspot(Multi_sync_buttons[gr_screen.res][idx].hotspot);
8125 for(idx=0; idx<MULTI_SYNC_NUM_TEXT; idx++) {
8126 // don't create the "launch" button text just yet
8127 if(idx == MST_LAUNCH) {
8130 // multiplayer clients should ignore the kick button
8131 if(!MULTIPLAYER_MASTER && !MULTIPLAYER_HOST && (idx == MST_KICK)) {
8135 Multi_sync_window.add_XSTR(&Multi_sync_text[gr_screen.res][idx]);
8139 // create the player list select button and hide it
8140 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);
8141 Multi_sync_plist_button.hide();
8143 // set up hotkeys for certain common functions
8144 Multi_sync_buttons[gr_screen.res][MS_CANCEL].button.set_hotkey(SDLK_ESCAPE);
8147 void multi_sync_common_do()
8149 int k = chatbox_process();
8150 k = Multi_sync_window.process(k);
8152 // process the player list
8153 multi_sync_handle_plist();
8155 // process any button clicks
8156 multi_sync_check_buttons();
8158 // process any keypresses
8162 gamesnd_play_iface(SND_USER_SELECT);
8163 multi_quit_game(PROMPT_ALL);
8168 void multi_sync_common_close()
8170 // unload any bitmaps
8171 if(!bm_unload(Multi_sync_bitmap)){
8172 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_sync_bitmap_fname[gr_screen.res]));
8175 extern time_t Player_multi_died_check;
8176 Player_multi_died_check = -1;
8178 // destroy the UI_WINDOW
8179 Multi_sync_window.destroy();
8182 void multi_sync_blit_screen_all()
8189 // display any text in the info area
8190 multi_common_render_text();
8192 // display any pending notification messages
8193 multi_common_notify_do();
8195 // display any info about visible players
8197 for(idx=0;idx<MAX_PLAYERS;idx++){
8198 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
8199 // display his name and status
8200 multi_sync_display_name(Net_players[idx].player->callsign,count,idx);
8202 // get the player state
8203 state = Net_players[idx].state;
8205 // if we're ingame joining, show all other players except myself as "playing"
8206 if((Net_player != NULL) && (&Net_players[idx] != Net_player) && ((Multi_sync_mode == MULTI_SYNC_INGAME) || (Net_player->flags & NETINFO_FLAG_INGAME_JOIN)) ){
8207 state = NETPLAYER_STATE_IN_MISSION;
8211 case NETPLAYER_STATE_MISSION_LOADING:
8212 multi_sync_display_status(XSTR("Mission Loading",802),count);
8214 case NETPLAYER_STATE_INGAME_SHIP_SELECT: // I don't think its possible to see this state, but...
8215 multi_sync_display_status(XSTR("Ingame Ship Select",803),count);
8217 case NETPLAYER_STATE_DEBRIEF:
8218 multi_sync_display_status(XSTR("Debriefing",804),count);
8220 case NETPLAYER_STATE_MISSION_SYNC:
8221 multi_sync_display_status(XSTR("Mission Sync",805),count);
8223 case NETPLAYER_STATE_JOINING:
8224 multi_sync_display_status(XSTR("Joining",806),count);
8226 case NETPLAYER_STATE_JOINED:
8227 multi_sync_display_status(XSTR("Joined",807),count);
8229 case NETPLAYER_STATE_SLOT_ACK :
8230 multi_sync_display_status(XSTR("Slot Ack",808),count);
8232 case NETPLAYER_STATE_BRIEFING:
8233 multi_sync_display_status(XSTR("Briefing",765),count);
8235 case NETPLAYER_STATE_SHIP_SELECT:
8236 multi_sync_display_status(XSTR("Ship Select",809),count);
8238 case NETPLAYER_STATE_WEAPON_SELECT:
8239 multi_sync_display_status(XSTR("Weapon Select",810),count);
8241 case NETPLAYER_STATE_WAITING:
8242 multi_sync_display_status(XSTR("Waiting",811),count);
8244 case NETPLAYER_STATE_IN_MISSION:
8245 multi_sync_display_status(XSTR("In Mission",812),count);
8247 case NETPLAYER_STATE_MISSION_LOADED:
8248 multi_sync_display_status(XSTR("Mission Loaded",813),count);
8250 case NETPLAYER_STATE_DATA_LOAD:
8251 multi_sync_display_status(XSTR("Data loading",814),count);
8253 case NETPLAYER_STATE_SETTINGS_ACK:
8254 multi_sync_display_status(XSTR("Ready To Enter Mission",815),count);
8256 case NETPLAYER_STATE_INGAME_SHIPS:
8257 multi_sync_display_status(XSTR("Ingame Ships Packet Ack",816),count);
8259 case NETPLAYER_STATE_INGAME_WINGS:
8260 multi_sync_display_status(XSTR("Ingame Wings Packet Ack",817),count);
8262 case NETPLAYER_STATE_INGAME_RPTS:
8263 multi_sync_display_status(XSTR("Ingame Respawn Points Ack",818),count);
8265 case NETPLAYER_STATE_SLOTS_ACK:
8266 multi_sync_display_status(XSTR("Ingame Weapon Slots Ack",819),count);
8268 case NETPLAYER_STATE_POST_DATA_ACK:
8269 multi_sync_display_status(XSTR("Post Briefing Data Block Ack",820),count);
8271 case NETPLAYER_STATE_FLAG_ACK :
8272 multi_sync_display_status(XSTR("Flags Ack",821),count);
8274 case NETPLAYER_STATE_MT_STATS :
8275 multi_sync_display_status(XSTR("Parallax Online Stats Updating",822),count);
8277 case NETPLAYER_STATE_WSS_ACK :
8278 multi_sync_display_status(XSTR("Weapon Slots Ack",823),count);
8280 case NETPLAYER_STATE_HOST_SETUP :
8281 multi_sync_display_status(XSTR("Host setup",824),count);
8283 case NETPLAYER_STATE_DEBRIEF_ACCEPT:
8284 multi_sync_display_status(XSTR("Debrief accept",825),count);
8286 case NETPLAYER_STATE_DEBRIEF_REPLAY:
8287 multi_sync_display_status(XSTR("Debrief replay",826),count);
8289 case NETPLAYER_STATE_CPOOL_ACK:
8290 multi_sync_display_status(XSTR("Campaign ship/weapon ack",827),count);
8292 case NETPLAYER_STATE_MISSION_XFER :
8294 // server should display the pct completion of all clients
8295 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8296 if(Net_players[idx].s_info.xfer_handle != -1){
8297 pct_complete = multi_xfer_pct_complete(Net_players[idx].s_info.xfer_handle);
8299 // if we've got a valid xfer handle
8300 if((pct_complete >= 0.0) && (pct_complete <= 1.0)){
8301 SDL_snprintf(txt,SDL_arraysize(txt),XSTR("Mission file xfer %d%%",828),(int)(pct_complete * 100.0f));
8305 SDL_strlcpy(txt, XSTR("Mission file xfer", 829), SDL_arraysize(txt));
8308 SDL_strlcpy(txt, XSTR("Mission file xfer", 829), SDL_arraysize(txt));
8311 // clients should display only for themselves (which is the only thing they know)
8313 // if we've got a valid file xfer handle
8314 if((&Net_players[idx] == Net_player) && (Net_player->s_info.xfer_handle != -1)){
8315 pct_complete = multi_xfer_pct_complete(Net_player->s_info.xfer_handle);
8317 // if we've got a valid xfer handle
8318 if((pct_complete >= 0.0) && (pct_complete <= 1.0)){
8319 SDL_snprintf(txt,SDL_arraysize(txt),XSTR("Mission file xfer %d%%",828),(int)(pct_complete * 100.0f));
8323 SDL_strlcpy(txt, XSTR("Mission file xfer", 829), SDL_arraysize(txt));
8328 SDL_strlcpy(txt, XSTR("Mission file xfer", 829), SDL_arraysize(txt));
8333 multi_sync_display_status(txt,count);
8336 nprintf(("Network","Unhandled player state : %d !\n",Net_players[idx].state));
8343 // display the mission start countdown timer (if any)
8344 anim_render_all(GS_STATE_MULTI_MISSION_SYNC,flFrametime);
8346 // process and show the chatbox thingie
8350 Multi_sync_window.draw_tooltip();
8352 // display the voice status indicator
8353 multi_common_voice_display_status();
8356 void multi_sync_check_buttons()
8359 for(idx=0;idx<Multi_sync_button_count;idx++){
8360 // we only really need to check for one button pressed at a time, so we can break after
8362 if(Multi_sync_buttons[gr_screen.res][idx].button.pressed()){
8363 multi_sync_button_pressed(idx);
8369 void multi_sync_button_pressed(int n)
8374 gamesnd_play_iface(SND_USER_SELECT);
8375 multi_quit_game(PROMPT_ALL);
8378 // scroll the info box up
8379 case MS_SCROLL_INFO_UP:
8380 multi_common_scroll_text_up();
8383 // scroll the info box down
8384 case MS_SCROLL_INFO_DOWN:
8385 multi_common_scroll_text_down();
8390 // if we have a currently selected player, kick him
8391 if(Multi_sync_player_select >= 0){
8392 multi_kick_player(Multi_sync_player_select);
8396 // start the final launch countdown (post-sync only)
8398 multi_sync_start_countdown();
8401 // doesn't do anything
8407 void multi_sync_pre_init()
8411 Netgame.game_state = NETGAME_STATE_MISSION_SYNC;
8413 // if we're in teamplay mode, always force skill level to be medium
8414 if((Netgame.type_flags & NG_TYPE_TEAM) && (Net_player->flags & NETINFO_FLAG_GAME_HOST)){
8415 Netgame.options.skill_level = NUM_SKILL_LEVELS / 2;
8416 Game_skill_level = NUM_SKILL_LEVELS / 2;
8417 multi_options_update_netgame();
8420 // notify everyone of when we get here
8421 if(!(Game_mode & GM_STANDALONE_SERVER)){
8422 Net_player->state = NETPLAYER_STATE_MISSION_SYNC;
8423 send_netplayer_update_packet();
8426 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8428 ml_string(NOX("Server performing pre-briefing data sync"));
8430 if(!(Game_mode & GM_STANDALONE_SERVER)){
8431 multi_common_set_text(XSTR("Server performing sync\n",830),1);
8434 // maybe initialize tvt and squad war stuff
8435 if(Netgame.type_flags & NG_TYPE_TEAM){
8436 multi_team_level_init();
8439 // force everyone into this state
8440 send_netgame_update_packet();
8442 if(!(Game_mode & GM_STANDALONE_SERVER)){
8443 multi_common_add_text(XSTR("Send update packet\n",831),1);
8446 // setup some of my own data
8447 Net_player->flags |= NETINFO_FLAG_MISSION_OK;
8449 // do any output stuff
8450 if(Game_mode & GM_STANDALONE_SERVER){
8451 std_debug_set_standalone_state_string("Mission Sync");
8454 // do this here to insure we have the most up to date file checksum info
8455 multi_get_mission_checksum(Game_current_mission_filename);
8456 // parse_get_file_signature(Game_current_mission_filename);
8458 if(!(Game_mode & GM_STANDALONE_SERVER)){
8459 multi_common_add_text(XSTR("Got file signatures\n",832),1);
8462 if(!(Game_mode & GM_STANDALONE_SERVER)){
8463 multi_common_add_text(XSTR("Sending update state packet\n",833),1);
8467 // if we're not in team vs. team mode - set all player teams to be 0, and unset all captaincy bits
8468 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
8469 for(idx=0;idx<MAX_PLAYERS;idx++){
8470 Net_players[idx].p_info.team = 0;
8471 Net_players[idx].flags &= ~(NETINFO_FLAG_TEAM_CAPTAIN);
8475 // we aren't necessarily xferring the mission file yet
8476 SDL_assert(Net_player->s_info.xfer_handle == -1);
8478 // always call this for good measure
8479 multi_campaign_flush_data();
8481 Mission_sync_flags = 0;
8482 Multi_mission_loaded = 0;
8485 void multi_sync_pre_do()
8489 // If I'm the server, wait for everyone to arrive in this state, then begin transferring data, etc.
8490 // all servers (standalone or no, go through this)
8491 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8492 // wait for everyone to arrive, then request filesig from all of them
8493 if(multi_netplayer_state_check(NETPLAYER_STATE_MISSION_SYNC) && !(Mission_sync_flags & MS_FLAG_SENT_FILESIG) && !multi_endgame_ending()){
8494 send_file_sig_request(Netgame.mission_name);
8495 Mission_sync_flags |= MS_FLAG_SENT_FILESIG;
8497 if(!(Game_mode & GM_STANDALONE_SERVER)){
8498 multi_common_add_text(XSTR("Sent filesig request\n",834),1);
8502 // if we're waiting for players to receive files, then check on their status
8503 if((Mission_sync_flags & MS_FLAG_SENT_FILESIG) && !multi_netplayer_flag_check(NETINFO_FLAG_MISSION_OK) && !multi_endgame_ending()){
8504 for(idx=0;idx<MAX_PLAYERS;idx++){
8505 // if this player is in the process of xferring a file
8506 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].s_info.xfer_handle != -1)){
8507 switch(multi_xfer_get_status(Net_players[idx].s_info.xfer_handle)){
8508 // if it has successfully completed, set his ok flag
8509 case MULTI_XFER_SUCCESS :
8511 Net_players[idx].flags |= NETINFO_FLAG_MISSION_OK;
8513 // release the xfer instance handle
8514 multi_xfer_release_handle(Net_players[idx].s_info.xfer_handle);
8515 Net_players[idx].s_info.xfer_handle = -1;
8517 // if it has failed or timed-out, kick the player
8518 case MULTI_XFER_TIMEDOUT:
8519 case MULTI_XFER_FAIL:
8520 // release the xfer handle
8521 multi_xfer_release_handle(Net_players[idx].s_info.xfer_handle);
8522 Net_players[idx].s_info.xfer_handle = -1;
8525 multi_kick_player(idx, 0, KICK_REASON_BAD_XFER);
8532 // NOTE : this is now obsolete
8533 // once everyone is verified, do any data transfer necessary
8534 if(multi_netplayer_flag_check(NETINFO_FLAG_MISSION_OK) && !(Mission_sync_flags & MS_FLAG_DATA_DONE) && !multi_endgame_ending()){
8535 // do nothing for now
8536 Mission_sync_flags |= MS_FLAG_DATA_DONE;
8538 // send campaign pool data
8539 multi_campaign_send_pool_status();
8542 // wait for everyone to ack on campaign pool data (even in non-campaign situations)
8543 if((Mission_sync_flags & MS_FLAG_DATA_DONE) && !(Mission_sync_flags & MS_FLAG_CAMP_DONE) && !multi_endgame_ending()){
8544 // check to see if everyone has acked the campaign pool data
8545 if(multi_netplayer_state_check(NETPLAYER_STATE_CPOOL_ACK)){
8546 Mission_sync_flags |= MS_FLAG_CAMP_DONE;
8550 // once everyone is verified, tell them to load the mission
8551 // also make sure to load the mission myself _AFTER_ telling everyone to do so. This makes the whole process
8552 // move along faster
8553 if((Mission_sync_flags & MS_FLAG_CAMP_DONE) && !(Mission_sync_flags & MS_FLAG_SENT_LOAD) && !multi_endgame_ending()){
8554 send_netplayer_load_packet(NULL);
8555 Mission_sync_flags |= MS_FLAG_SENT_LOAD;
8557 if(!(Game_mode & GM_STANDALONE_SERVER)){
8558 multi_common_add_text(XSTR("Sent load packet\n",835),1);
8561 // load the mission myself, as soon as possible
8562 if(!Multi_mission_loaded){
8563 nprintf(("Network","Server loading mission..."));
8565 // update everyone about my status
8566 Net_player->state = NETPLAYER_STATE_MISSION_LOADING;
8567 send_netplayer_update_packet();
8569 game_start_mission();
8571 nprintf(("Network","Done\n"));
8572 Multi_mission_loaded = 1;
8574 // update everyone about my status
8575 Net_player->state = NETPLAYER_STATE_MISSION_LOADED;
8576 send_netplayer_update_packet();
8578 if(!(Game_mode & GM_STANDALONE_SERVER)){
8579 multi_common_add_text(XSTR("Loaded mission locally\n",836),1);
8584 // if everyone has loaded the mission, randomly assign players to ships
8585 if(multi_netplayer_state_check(NETPLAYER_STATE_MISSION_LOADED) && !(Mission_sync_flags & MS_FLAG_TS_SLOTS) && !multi_endgame_ending()){
8586 // call the team select function to assign players to their ships, wings, etc
8587 multi_ts_assign_players_all();
8588 send_netplayer_slot_packet();
8591 Mission_sync_flags |= MS_FLAG_TS_SLOTS;
8594 // if everyone has loaded the mission, move to the team select stage
8595 if(Sync_test && multi_netplayer_state_check(NETPLAYER_STATE_SLOT_ACK) && !(Mission_sync_flags & MS_FLAG_PUSHED_BRIEFING) && !multi_endgame_ending()){
8596 Netgame.game_state = NETGAME_STATE_BRIEFING;
8597 send_netgame_update_packet(); // this will push everyone into the next state
8599 // the standalone moves to his own wait state, whereas in the normal game mode, the server/host moves in to the
8600 // team select state
8601 if(Game_mode & GM_STANDALONE_SERVER){
8602 gameseq_post_event(GS_EVENT_MULTI_STD_WAIT);
8604 gameseq_post_event(GS_EVENT_START_GAME);
8607 Mission_sync_flags |= MS_FLAG_PUSHED_BRIEFING;
8609 if(!(Game_mode & GM_STANDALONE_SERVER)){
8610 multi_common_add_text(XSTR("Moving to team select\n",837),1);
8614 // clients should detect here if they are doing a file xfer and do error processing
8615 if((Net_player->state == NETPLAYER_STATE_MISSION_XFER) && (Net_player->s_info.xfer_handle != -1) && !multi_endgame_ending()){
8616 switch(multi_xfer_get_status(Net_player->s_info.xfer_handle)){
8617 // if it has successfully completed, set his ok flag
8618 case MULTI_XFER_SUCCESS :
8619 // release my xfer handle
8620 multi_xfer_release_handle(Net_player->s_info.xfer_handle);
8621 Net_player->s_info.xfer_handle = -1;
8624 // if it has failed or timed-out, kick the player
8625 case MULTI_XFER_TIMEDOUT:
8626 case MULTI_XFER_FAIL:
8627 // release my xfer handle
8628 multi_xfer_release_handle(Net_player->s_info.xfer_handle);
8629 Net_player->s_info.xfer_handle = -1;
8631 // leave the game qith an error code
8632 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_XFER_FAIL);
8639 if(!(Game_mode & GM_STANDALONE_SERVER)){
8641 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8642 if(Multi_sync_bitmap != -1){
8643 gr_set_bitmap(Multi_sync_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8646 Multi_sync_window.draw();
8648 multi_sync_blit_screen_all();
8654 void multi_sync_pre_close()
8656 // at this point, we should shut down any file xfers...
8657 if(Net_player->s_info.xfer_handle != -1){
8658 nprintf(("Network","WARNING - killing file xfer while leaving mission sync state!!!\n"));
8660 multi_xfer_abort(Net_player->s_info.xfer_handle);
8661 Net_player->s_info.xfer_handle = -1;
8665 void multi_sync_post_init()
8667 multi_reset_timestamps();
8669 Multi_state_timestamp = timestamp(0);
8672 ml_string(NOX("Performing post-briefing data sync"));
8674 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8675 multi_common_add_text(XSTR("Server performing sync\n",830),1);
8677 multi_common_add_text(XSTR("Client performing sync\n",838),1);
8680 // everyone should re-initialize these
8681 init_multiplayer_stats();
8683 // reset all sequencing info
8684 multi_oo_reset_sequencing();
8686 // if I am not the master of the game, then send the firing information for my ship
8688 if ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) ){
8689 send_firing_info_packet();
8692 // if I'm not a standalone server, load up the countdown stuff
8693 if(!(Game_mode & GM_STANDALONE_SERVER)){
8694 Multi_sync_countdown_anim = NULL;
8695 Multi_sync_countdown_instance = NULL;
8696 Multi_sync_countdown_anim = anim_load(Multi_sync_countdown_fname[gr_screen.res]);
8697 if(Multi_sync_countdown_anim == NULL){
8698 nprintf(("General","WARNING!, Could not load countdown animation %s!\n",Multi_sync_countdown_fname[gr_screen.res]));
8702 // create objects for all permanent observers
8703 multi_obs_level_init();
8705 // clear the game start countdown timer
8706 Multi_sync_countdown_timer = -1.0f;
8707 Multi_sync_countdown = -1;
8709 // if this is a team vs. team mission, mark all ship teams appropriately
8710 if(Netgame.type_flags & NG_TYPE_TEAM){
8711 multi_team_mark_all_ships();
8714 Mission_sync_flags = 0;
8715 Multi_sync_launch_pressed = 0;
8718 #define MULTI_POST_TIMESTAMP 7000
8720 extern int create_wings();
8722 void multi_sync_post_do()
8726 // only if the host is also the master should he be doing this (non-standalone situation)
8727 if ( Net_player->flags & NETINFO_FLAG_AM_MASTER ) {
8729 // once everyone gets to this screen, send them the ship classes of all ships.
8730 if(multi_netplayer_state_check(NETPLAYER_STATE_WAITING) && !(Mission_sync_flags & MS_FLAG_POST_DATA)) {
8731 // only the host should ever do this
8732 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8733 // at this point we want to delete all necessary ships, change all necessary ship classes, and set all weapons up
8734 multi_ts_create_wings();
8736 // update player ets settings
8737 for(idx=0;idx<MAX_PLAYERS;idx++){
8738 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].player->objnum != -1)){
8739 multi_server_update_player_weapons(&Net_players[idx],&Ships[Objects[Net_players[idx].player->objnum].instance]);
8744 // note that this is done a little differently for standalones and nonstandalones
8745 send_post_sync_data_packet();
8747 multi_common_add_text(XSTR("Sending post briefing block information\n",839),1);
8749 Mission_sync_flags |= MS_FLAG_POST_DATA;
8752 // send weapon slots data
8753 if(multi_netplayer_state_check(NETPLAYER_STATE_POST_DATA_ACK) && !(Mission_sync_flags & MS_FLAG_WSS_SLOTS)) {
8754 // note that this is done a little differently for standalones and nonstandalones
8755 if(Netgame.type_flags & NG_TYPE_TEAM){
8756 send_wss_slots_data_packet(0,0);
8757 send_wss_slots_data_packet(1,1);
8759 send_wss_slots_data_packet(0,1);
8762 multi_common_add_text(XSTR("Sending weapon slots information\n",840),1);
8764 Mission_sync_flags |= MS_FLAG_WSS_SLOTS;
8767 // once weapon information is received, send player settings info
8768 if ( multi_netplayer_state_check(NETPLAYER_STATE_WSS_ACK) && !(Mission_sync_flags & MS_FLAG_PSETTINGS)) {
8769 send_player_settings_packet();
8771 // server (specifically, the standalone), should set this here
8772 Net_player->state = NETPLAYER_STATE_SETTINGS_ACK;
8773 send_netplayer_update_packet();
8775 multi_common_add_text(XSTR("Sending player settings packets\n",841),1);
8777 Mission_sync_flags |= MS_FLAG_PSETTINGS;
8780 // check to see if the countdown timer has started and act appropriately
8781 if( Multi_sync_countdown_timer > -1.0f ) {
8783 // increment by frametime.
8784 Multi_sync_countdown_timer += flFrametime;
8786 // if the animation is not playing, start it
8787 if(!(Game_mode & GM_STANDALONE_SERVER) && (Multi_sync_countdown_instance == NULL) && (Multi_sync_countdown_anim != NULL)){
8788 anim_play_struct aps;
8790 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]);
8791 aps.screen_id = GS_STATE_MULTI_MISSION_SYNC;
8792 aps.framerate_independent = 1;
8794 Multi_sync_countdown_instance = anim_play(&aps);
8797 // if the next second has expired
8798 if( Multi_sync_countdown_timer >= 1.0f ) {
8800 Multi_sync_countdown--;
8801 Multi_sync_countdown_timer = 0.0f;
8803 // if the countdown has reached 0, launch the mission
8804 if(Multi_sync_countdown == 0){
8805 Multi_sync_countdown_timer = -1.0f;
8807 Multi_sync_launch_pressed = 0;
8808 multi_sync_launch();
8810 // otherwise send a countdown packet
8812 send_countdown_packet(Multi_sync_countdown);
8817 // jump into the mission myself
8818 if((Multi_sync_countdown == 0) && multi_netplayer_state_check(NETPLAYER_STATE_IN_MISSION)){
8819 if(!((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !Multi_sync_launch_pressed)){
8820 Net_player->state = NETPLAYER_STATE_IN_MISSION;
8821 Netgame.game_state = NETGAME_STATE_IN_MISSION;
8822 gameseq_post_event(GS_EVENT_ENTER_GAME);
8824 multi_common_add_text(XSTR("Moving into game\n",842),1);
8828 // maybe start the animation countdown
8829 if((Multi_sync_countdown >= 0) && (Multi_sync_countdown_instance == NULL) && (Multi_sync_countdown_anim != NULL)){
8830 anim_play_struct aps;
8832 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]);
8833 aps.screen_id = GS_STATE_MULTI_MISSION_SYNC;
8834 aps.framerate_independent = 1;
8836 Multi_sync_countdown_instance = anim_play(&aps);
8840 // host - specific stuff
8841 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8842 // create the launch button so the host can click
8843 if( Sync_test && multi_netplayer_state_check(NETPLAYER_STATE_SETTINGS_ACK) ){
8844 multi_sync_create_launch_button();
8849 if(!(Game_mode & GM_STANDALONE_SERVER)){
8851 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8852 if(Multi_sync_bitmap != -1){
8853 gr_set_bitmap(Multi_sync_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8856 Multi_sync_window.draw();
8858 multi_sync_blit_screen_all();
8864 void multi_sync_post_close()
8868 // if I'm not a standalone server, unload up the countdown stuff
8869 if(!(Game_mode & GM_STANDALONE_SERVER)){
8870 // release all rendering animation instances (should only be 1)
8871 anim_release_all_instances(GS_STATE_MULTI_MISSION_SYNC);
8872 Multi_sync_countdown_instance = NULL;
8874 // free up the countdown animation
8875 if(Multi_sync_countdown_anim != NULL){
8876 anim_free(Multi_sync_countdown_anim);
8877 Multi_sync_countdown_anim = NULL;
8881 // all players should reset sequencing
8882 for(idx=0;idx<MAX_PLAYERS;idx++){
8883 if(Net_player->flags & NETINFO_FLAG_CONNECTED){
8884 Net_players[idx].client_cinfo_seq = 0;
8885 Net_players[idx].client_server_seq = 0;
8889 // multiplayer dogfight
8890 multi_df_level_pre_enter();
8892 // clients should clear obj_pair array and add pair for themselves
8894 if ( MULTIPLAYER_CLIENT ) {
8896 obj_add_pairs( OBJ_INDEX(Player_obj) );
8901 void multi_sync_display_name(const char *name, int index, int np_index)
8903 char fit[CALLSIGN_LEN];
8905 // make sure the string actually fits
8906 SDL_strlcpy(fit, name, SDL_arraysize(fit));
8908 // if we're in team vs. team mode
8909 if(Netgame.type_flags & NG_TYPE_TEAM){
8910 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]);
8912 // if this is the currently selected player, draw him highlighted
8913 if(np_index == Multi_sync_player_select){
8914 gr_set_color_fast(&Color_text_selected);
8916 gr_set_color_fast(&Color_text_normal);
8920 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);
8922 // blit his team icon
8924 if(Net_players[np_index].p_info.team == 0){
8925 // blit the team captain icon
8926 if(Net_players[np_index].flags & NETINFO_FLAG_TEAM_CAPTAIN){
8927 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
8928 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8929 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);
8932 // normal team member icon
8934 if(Multi_common_icons[MICON_TEAM0] != -1){
8935 gr_set_bitmap(Multi_common_icons[MICON_TEAM0], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8936 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 else if(Net_players[np_index].p_info.team == 1){
8942 // blit the team captain icon
8943 if(Net_players[np_index].flags & NETINFO_FLAG_TEAM_CAPTAIN){
8944 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
8945 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8946 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);
8949 // normal team member icon
8951 if(Multi_common_icons[MICON_TEAM1] != -1){
8952 gr_set_bitmap(Multi_common_icons[MICON_TEAM1], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8953 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 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]);
8960 // if this is the currently selected player, draw him highlighted
8961 if(np_index == Multi_sync_player_select){
8962 gr_set_color_fast(&Color_text_selected);
8964 gr_set_color_fast(&Color_text_normal);
8968 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);
8971 // maybe blit his CD status icon
8972 if((Net_players[np_index].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
8973 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8974 gr_bitmap(Ms_status_coords[gr_screen.res][MS_X_COORD], Ms_status_coords[gr_screen.res][MS_Y_COORD] + (index * 10));
8978 void multi_sync_display_status(const char *status, int index)
8982 // make sure the string actually fits
8983 SDL_strlcpy(fit, status, SDL_arraysize(fit));
8984 gr_force_fit_string(fit, 250, Ms_status2_coords[gr_screen.res][MS_W_COORD] - 20);
8985 gr_set_color_fast(&Color_bright);
8986 gr_string(Ms_status2_coords[gr_screen.res][MS_X_COORD], Ms_status2_coords[gr_screen.res][MS_Y_COORD] + (index * 10), fit);
8989 void multi_sync_force_start_pre()
8992 int want_state = NETPLAYER_STATE_SLOT_ACK; // kick any players who are still in this state
8994 // go through the player list and boot anyone who isn't in the right state
8995 for(idx=0;idx<MAX_PLAYERS;idx++){
8996 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx]) && (Net_players[idx].state == want_state)){
8997 multi_kick_player(idx,0);
9002 void multi_sync_force_start_post()
9006 int num_kill_states;
9008 // determine the state we want all players in so that we can find those who are not in the state
9009 kill_state[0] = NETPLAYER_STATE_BRIEFING;
9010 kill_state[1] = NETPLAYER_STATE_SHIP_SELECT;
9011 kill_state[2] = NETPLAYER_STATE_WEAPON_SELECT;
9012 num_kill_states = 3;
9014 // go through the player list and boot anyone who isn't in the right state
9015 for(idx=0;idx<MAX_PLAYERS;idx++){
9016 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx])){
9017 // check against all kill state
9018 for(idx2 = 0;idx2<num_kill_states;idx2++){
9019 if(Net_players[idx].state == kill_state[idx2]){
9020 multi_kick_player(idx,0);
9028 void multi_sync_start_countdown()
9030 // don't allow repeat button presses
9031 if(Multi_sync_launch_pressed){
9035 Multi_sync_launch_pressed = 1;
9037 // if I'm the server, begin the countdown
9038 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
9039 gamesnd_play_iface(SND_COMMIT_PRESSED);
9040 Multi_sync_countdown_timer = 0.0f;
9041 Multi_sync_countdown = MULTI_SYNC_COUNTDOWN_TIME;
9043 // send an initial countdown value
9044 send_countdown_packet(Multi_sync_countdown);
9046 // otherwise send the "start countdown" packet to the standalone
9048 SDL_assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
9049 send_countdown_packet(-1);
9053 void multi_sync_launch()
9055 // don't allow repeat button presses
9056 if(Multi_sync_launch_pressed){
9060 Multi_sync_launch_pressed = 1;
9063 ml_printf(NOX("Entering mission %s"), Game_current_mission_filename);
9065 // tell everyone to jump into the mission
9066 send_jump_into_mission_packet();
9067 Multi_state_timestamp = timestamp(MULTI_POST_TIMESTAMP);
9069 // set the # of players at the start of the mission
9070 Multi_num_players_at_start = multi_num_players();
9071 nprintf(("Network","# of players at start of mission : %d\n", Multi_num_players_at_start));
9073 // initialize datarate limiting for all clients
9074 multi_oo_rate_init_all();
9076 multi_common_add_text(XSTR("Sending mission start packet\n",843),1);
9079 void multi_sync_create_launch_button()
9081 if (!Multi_launch_button_created) {
9082 // create the object
9083 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);
9085 // set the sound to play when highlighted
9086 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_highlight_action(common_play_highlight_sound);
9088 // set the ani for the button
9089 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_bmaps(Multi_sync_buttons[gr_screen.res][MS_LAUNCH].filename, 3, 0);
9092 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.link_hotspot(Multi_sync_buttons[gr_screen.res][MS_LAUNCH].hotspot);
9095 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_hotkey(KEY_CTRLED+SDLK_RETURN);
9098 // create the text for the button
9099 Multi_sync_window.add_XSTR(&Multi_sync_text[gr_screen.res][MST_LAUNCH]);
9102 // increment the button count so we start checking this one
9103 Multi_sync_button_count++;
9105 Multi_launch_button_created = 1;
9109 void multi_sync_handle_plist()
9115 // if we don't have a currently selected player, select one
9116 if((Multi_sync_player_select < 0) || !MULTI_CONNECTED(Net_players[Multi_sync_player_select])){
9117 for(idx=0;idx<MAX_PLAYERS;idx++){
9118 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
9119 Multi_sync_player_select = idx;
9125 // check for button list presses
9126 if(Multi_sync_plist_button.pressed()){
9127 // get the y mouse coords
9128 Multi_sync_plist_button.get_mouse_pos(NULL,&my);
9130 // get the index of the item selected
9131 select_index = my / 10;
9133 // if the index is greater than the current # connections, do nothing
9134 if(select_index > (multi_num_connections() - 1)){
9138 // translate into an absolute Net_players[] index (get the Nth net player)
9139 Multi_sync_player_select = -1;
9140 for(idx=0;idx<MAX_PLAYERS;idx++){
9141 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
9145 // if we've found the item we're looking for
9146 if(select_index < 0){
9147 Multi_sync_player_select = idx;
9152 // if for some bizarre reason, this is an invalid player, unselect him and wait for the next interation
9154 if((Multi_sync_player_select >= 0) && (!MULTI_CONNECTED(Net_players[Multi_sync_player_select]) || MULTI_STANDALONE(Net_players[Multi_sync_player_select])) ){
9155 Multi_sync_player_select = -1;
9161 // -------------------------------------------------------------------------------------------------------------
9163 // MULTIPLAYER DEBRIEF SCREEN
9166 // other relevant data
9167 int Multi_debrief_accept_hit;
9168 int Multi_debrief_replay_hit;
9170 // set if the server has left the game
9171 int Multi_debrief_server_left = 0;
9173 // if we've reported on TvT status all players are in the debrief
9174 int Multi_debrief_reported_tvt = 0;
9176 // whether stats are being accepted
9177 // -1 == no decision yet
9180 int Multi_debrief_stats_accept_code = -1;
9182 int Multi_debrief_server_framecount = 0;
9184 float Multi_debrief_time = 0.0f;
9185 float Multi_debrief_resend_time = 10.0f;
9187 void multi_debrief_init()
9191 Multi_debrief_time = 0.0f;
9192 Multi_debrief_resend_time = 10.0f;
9194 // do this to notify the standalone or the normal server that we're in the debrief state and ready to receive packets
9195 if (!(Net_player->flags & NETINFO_FLAG_AM_MASTER)) {
9196 Net_player->state = NETPLAYER_STATE_DEBRIEF;
9197 send_netplayer_update_packet();
9200 // unflag some stuff
9201 for(idx=0;idx<MAX_PLAYERS;idx++){
9202 if(MULTI_CONNECTED(Net_players[idx])){
9203 Net_players[idx].flags &= ~(NETINFO_FLAG_RESPAWNING | NETINFO_FLAG_LIMBO | NETINFO_FLAG_WARPING_OUT);
9207 // if text input mode is active, clear it
9208 multi_msg_text_flush();
9210 // the server has not left yet
9211 Multi_debrief_server_left = 0;
9213 // have not hit accept or replay yet
9214 Multi_debrief_accept_hit = 0;
9215 Multi_debrief_replay_hit = 0;
9217 // stats have not been accepted yet
9218 Multi_debrief_stats_accept_code = -1;
9220 // mark stats as not being store yet
9221 Netgame.flags &= ~(NG_FLAG_STORED_MT_STATS);
9223 // no report on TvT yet
9224 Multi_debrief_reported_tvt = 0;
9226 Multi_debrief_server_framecount = 0;
9229 void multi_debrief_do_frame()
9231 Multi_debrief_time += flFrametime;
9233 // set the netgame state to be debriefing when appropriate
9234 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)){
9235 Netgame.game_state = NETGAME_STATE_DEBRIEF;
9236 send_netgame_update_packet();
9239 // evaluate all server stuff
9240 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
9241 multi_debrief_server_process();
9245 void multi_debrief_close()
9247 if ( MULTIPLAYER_CLIENT && (Netgame.game_state == NETGAME_STATE_MISSION_SYNC) ){
9248 gamesnd_play_iface( SND_COMMIT_PRESSED );
9252 // handle optional mission loop
9253 void multi_maybe_set_mission_loop()
9255 int cur = Campaign.current_mission;
9256 if (Campaign.missions[cur].has_mission_loop) {
9257 SDL_assert(Campaign.loop_mission != CAMPAIGN_LOOP_MISSION_UNINITIALIZED);
9259 bool require_repeat_mission = (Campaign.current_mission == Campaign.next_mission);
9261 // check for (1) mission loop available, (2) dont have to repeat last mission
9262 if ( (Campaign.missions[cur].has_mission_loop && (Campaign.loop_mission != -1)) && !require_repeat_mission ) {
9265 debrief_assemble_optional_mission_popup_text(buffer, SDL_arraysize(buffer), Campaign.missions[cur].mission_loop_desc);
9267 int choice = popup(0 , 2, POPUP_NO, POPUP_YES, buffer);
9269 Campaign.loop_enabled = 1;
9270 Campaign.next_mission = Campaign.loop_mission;
9275 // handle all cases for when the accept key is hit in a multiplayer debriefing
9276 void multi_debrief_accept_hit()
9278 // if we already accepted, do nothing
9279 // but he may need to hit accept again after the server has left the game, so allow this
9280 if(Multi_debrief_accept_hit){
9284 // mark this so that we don't hit it again
9285 Multi_debrief_accept_hit = 1;
9287 gamesnd_play_iface(SND_COMMIT_PRESSED);
9289 // if the server has left the game, always just end the game.
9290 if(Multi_debrief_server_left){
9291 if(!multi_quit_game(PROMPT_ALL)){
9292 Multi_debrief_server_left = 1;
9293 Multi_debrief_accept_hit = 0;
9295 Multi_debrief_server_left = 0;
9298 // query the host and see if he wants to accept stats
9299 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
9300 // if we're on a tracker game, he gets no choice for storing stats
9301 if(MULTI_IS_TRACKER_GAME){
9302 multi_maybe_set_mission_loop();
9304 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));
9306 // evaluate the result
9311 Multi_debrief_accept_hit = 0;
9314 // set the accept code to be "not accepting"
9316 multi_debrief_stats_toss();
9317 multi_maybe_set_mission_loop();
9320 // accept the stats and continue
9322 multi_debrief_stats_accept();
9323 multi_maybe_set_mission_loop();
9329 // set my netplayer state to be "debrief_accept", and be done with it
9330 Net_player->state = NETPLAYER_STATE_DEBRIEF_ACCEPT;
9331 send_netplayer_update_packet();
9335 // handle all cases for when the escape key is hit in a multiplayer debriefing
9336 void multi_debrief_esc_hit()
9340 // if the server has left
9341 if(Multi_debrief_server_left){
9342 multi_quit_game(PROMPT_ALL);
9347 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
9348 // if the stats have already been accepted
9349 if((Multi_debrief_stats_accept_code != -1) || (MULTI_IS_TRACKER_GAME)){
9350 multi_quit_game(PROMPT_HOST);
9352 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));
9354 // evaluate the result
9361 // set the accept code to be "not accepting"
9363 multi_debrief_stats_toss();
9364 multi_quit_game(PROMPT_NONE);
9367 // accept the stats and continue
9369 multi_debrief_stats_accept();
9370 multi_quit_game(PROMPT_NONE);
9375 // if the stats haven't been accepted yet, or this is a tracker game
9376 if((Multi_debrief_stats_accept_code == -1) && !(MULTI_IS_TRACKER_GAME)){
9377 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));
9379 // evaluate the result
9381 multi_quit_game(PROMPT_NONE);
9384 // otherwise go through the normal endgame channels
9386 multi_quit_game(PROMPT_ALL);
9391 void multi_debrief_replay_hit()
9393 // only the host should ever get here
9394 SDL_assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
9396 // if the button was already pressed, do nothing
9397 if(Multi_debrief_accept_hit){
9401 // same as hittin the except button except no stats are kept
9402 Multi_debrief_accept_hit = 1;
9404 // mark myself as being in the replay state so we know what to do next
9405 Net_player->state = NETPLAYER_STATE_DEBRIEF_REPLAY;
9406 send_netplayer_update_packet();
9409 // call this when the server has left and we would otherwise be saying "contact lost with server
9410 void multi_debrief_server_left()
9413 Multi_debrief_server_left = 1;
9415 // undo any "accept" hit so that clients can hit accept again to leave
9416 Multi_debrief_accept_hit = 0;
9419 void multi_debrief_stats_accept()
9421 // don't do anything if we've already accepted
9422 if(Multi_debrief_stats_accept_code != -1){
9426 Multi_debrief_stats_accept_code = 1;
9428 // if we're the host, and we're on a standalone, tell the standalone to begin the stats storing process
9429 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) || (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
9430 // send a packet to the players telling them to store their stats
9431 send_store_stats_packet(1);
9434 // add a chat line saying "stats have been accepted"
9435 multi_display_chat_msg(XSTR("<stats have been accepted>",850),0,0);
9438 ml_string(NOX("Stats stored"));
9441 void multi_debrief_stats_toss()
9443 // don't do anything if we've already accepted
9444 if(Multi_debrief_stats_accept_code != -1){
9448 Multi_debrief_stats_accept_code = 0;
9450 // if we're the host, and we're on a standalone, tell everyone to "toss" stats
9451 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) || (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
9452 // send a packet to the players telling them to store their stats
9453 send_store_stats_packet(0);
9456 // add a chat line saying "stats have been accepted"
9457 multi_display_chat_msg(XSTR("<stats have been tossed>",851),0,0);
9460 ml_string(NOX("Stats tossed"));
9463 int multi_debrief_stats_accept_code()
9465 return Multi_debrief_stats_accept_code;
9468 void multi_debrief_server_process()
9471 int player_status,other_status;
9473 Multi_debrief_server_framecount++;
9475 // if we're > 10 seconds into the debrief and not everyone is here, try warping everyone out again
9476 if((Multi_debrief_time >= Multi_debrief_resend_time) && !multi_netplayer_state_check3(NETPLAYER_STATE_DEBRIEF, NETPLAYER_STATE_DEBRIEF_ACCEPT, NETPLAYER_STATE_DEBRIEF_REPLAY, 1)){
9477 // find all players who are not in the debrief state and hit them with the endgame packet
9478 for(idx=0; idx<MAX_PLAYERS; idx++){
9479 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)) ){
9480 send_endgame_packet(&Net_players[idx]);
9485 Multi_debrief_resend_time += 7.0f;
9488 // evaluate the status of all players in the game (0 == not ready, 1 == ready to continue, 2 == ready to replay)
9491 // check all players
9492 for(idx=0;idx<MAX_PLAYERS;idx++){
9493 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_HOST(Net_players[idx])){
9494 if(Net_players[idx].state != NETPLAYER_STATE_DEBRIEF_ACCEPT){
9501 // if we haven't already reported TvT results
9502 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)){
9503 multi_team_report();
9504 Multi_debrief_reported_tvt = 1;
9507 // if all other players are good to go, check the host
9509 // if he is ready to continue
9510 if(Netgame.host->state == NETPLAYER_STATE_DEBRIEF_ACCEPT){
9513 // if he wants to replay the mission
9514 else if(Netgame.host->state == NETPLAYER_STATE_DEBRIEF_REPLAY){
9517 // if he is not ready
9522 // if all players are _not_ good to go
9527 // if we're in the debriefing state in a campaign mode, process accordingly
9528 if(Netgame.campaign_mode == MP_CAMPAIGN){
9529 multi_campaign_do_debrief(player_status);
9531 // otherwise process as normal (looking for all players to be ready to go to the next mission
9533 if(player_status == 1){
9534 multi_flush_mission_stuff();
9536 // set the netgame state to be forming and continue
9537 Netgame.game_state = NETGAME_STATE_FORMING;
9538 send_netgame_update_packet();
9540 // move to the proper state
9541 if(Game_mode & GM_STANDALONE_SERVER){
9542 gameseq_post_event(GS_EVENT_STANDALONE_MAIN);
9544 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
9547 multi_reset_timestamps();
9548 } else if(player_status == 2){
9549 multi_flush_mission_stuff();
9551 // tell everyone to move into the pre-briefing sync state
9552 Netgame.game_state = NETGAME_STATE_MISSION_SYNC;
9553 send_netgame_update_packet();
9555 // move back to the mission sync screen for the same mission again
9556 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
9557 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
9559 multi_reset_timestamps();
9565 // -------------------------------------------------------------------------------------------------------------
9567 // MULTIPLAYER PASSWORD POPUP
9572 static const char *Multi_pwd_bitmap_fname[GR_NUM_RESOLUTIONS] = {
9573 "Password", // GR_640
9574 "2_Password" // GR_1024
9577 static const char *Multi_pwd_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
9578 "Password-M", // GR_640
9579 "2_Password-M" // GR_1024
9584 // constants for coordinate lookup
9585 #define MPWD_X_COORD 0
9586 #define MPWD_Y_COORD 1
9587 #define MPWD_W_COORD 2
9588 #define MPWD_H_COORD 3
9591 #define MULTI_PWD_NUM_BUTTONS 2
9592 #define MPWD_CANCEL 0
9593 #define MPWD_COMMIT 1
9595 // password area defs
9596 int Mpwd_coords[GR_NUM_RESOLUTIONS][4] = {
9609 UI_WINDOW Multi_pwd_window; // the window object for the join screen
9610 UI_INPUTBOX Multi_pwd_passwd; // for Netgame.passwd
9611 int Multi_pwd_bitmap; // the background bitmap
9612 int Multi_passwd_background = -1;
9613 int Multi_passwd_done = -1;
9614 int Multi_passwd_running = 0;
9617 ui_button_info Multi_pwd_buttons[GR_NUM_RESOLUTIONS][MULTI_PWD_NUM_BUTTONS] = {
9620 ui_button_info("PWB_00", 402, 134, -1, -1, 0),
9621 ui_button_info("PWB_01", 450, 134, -1, -1, 1),
9623 ui_button_info("PWB_00", 411, 151, 405, 141, 0),
9624 ui_button_info("PWB_01", 460, 151, 465, 141, 1),
9628 ui_button_info("2_PWB_00", 659, 242, 649, 225, 0),
9629 ui_button_info("2_PWB_01", 737, 242, 736, 225, 1),
9635 #define MULTI_PWD_NUM_TEXT 3
9637 UI_XSTR Multi_pwd_text[GR_NUM_RESOLUTIONS][MULTI_PWD_NUM_TEXT] = {
9639 { "Cancel", 387, 400, 141, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[0][MPWD_CANCEL].button},
9640 { "Commit", 1062, 455, 141, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[0][MPWD_COMMIT].button},
9641 { "Enter Password", 1332, 149, 92, UI_XSTR_COLOR_GREEN, -1, NULL},
9644 { "Cancel", 387, 649, 225, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[1][MPWD_CANCEL].button},
9645 { "Commit", 1062, 736, 225, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[1][MPWD_COMMIT].button},
9646 { "Enter Password", 1332, 239, 148, UI_XSTR_COLOR_GREEN, -1, NULL},
9651 // initialize all graphics, etc
9652 void multi_passwd_init()
9656 // store the background as it currently is
9657 Multi_passwd_background = gr_save_screen();
9659 // create the interface window
9660 Multi_pwd_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
9661 Multi_pwd_window.set_mask_bmap(Multi_pwd_bitmap_mask_fname[gr_screen.res]);
9663 // load the background bitmap
9664 Multi_pwd_bitmap = bm_load(Multi_pwd_bitmap_fname[gr_screen.res]);
9665 if(Multi_pwd_bitmap < 0){
9666 // we failed to load the bitmap - this is very bad
9670 // create the interface buttons
9671 for(idx=0; idx<MULTI_PWD_NUM_BUTTONS; idx++){
9672 // create the object
9673 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);
9675 // set the sound to play when highlighted
9676 Multi_pwd_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
9678 // set the ani for the button
9679 Multi_pwd_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pwd_buttons[gr_screen.res][idx].filename);
9682 Multi_pwd_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pwd_buttons[gr_screen.res][idx].hotspot);
9687 for(idx=0; idx<MULTI_PWD_NUM_TEXT; idx++){
9688 Multi_pwd_window.add_XSTR(&Multi_pwd_text[gr_screen.res][idx]);
9692 // create the password input box
9693 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);
9694 Multi_pwd_passwd.set_focus();
9696 // link the enter key to ACCEPT
9697 Multi_pwd_buttons[gr_screen.res][MPWD_COMMIT].button.set_hotkey(SDLK_RETURN);
9699 Multi_passwd_done = -1;
9700 Multi_passwd_running = 1;
9703 // close down all graphics, etc
9704 void multi_passwd_close()
9706 // unload any bitmaps
9707 bm_release(Multi_pwd_bitmap);
9709 // destroy the UI_WINDOW
9710 Multi_pwd_window.destroy();
9712 // free up the saved background screen
9713 if(Multi_passwd_background >= 0){
9714 gr_free_screen(Multi_passwd_background);
9715 Multi_passwd_background = -1;
9718 Multi_passwd_running = 0;
9721 // process any button pressed
9722 void multi_passwd_process_buttons()
9724 // if the accept button was pressed
9725 if(Multi_pwd_buttons[gr_screen.res][MPWD_COMMIT].button.pressed()){
9726 gamesnd_play_iface(SND_USER_SELECT);
9727 Multi_passwd_done = 1;
9730 // if the cancel button was pressed
9731 if(Multi_pwd_buttons[gr_screen.res][MPWD_CANCEL].button.pressed()){
9732 gamesnd_play_iface(SND_USER_SELECT);
9733 Multi_passwd_done = 0;
9737 // run the passwd popup
9738 void multi_passwd_do(char *passwd, const int max_passlen)
9742 while(Multi_passwd_done == -1){
9743 // set frametime and run background stuff
9744 game_set_frametime(-1);
9745 game_do_state_common(gameseq_get_state());
9747 k = Multi_pwd_window.process();
9749 // process any keypresses
9752 // set this to indicate the user has cancelled for one reason or another
9753 Multi_passwd_done = 0;
9757 // if the input box text has changed
9758 if(Multi_pwd_passwd.changed()){
9759 SDL_strlcpy(passwd, "", max_passlen);
9760 Multi_pwd_passwd.get_text(passwd);
9763 // process any button pressed
9764 multi_passwd_process_buttons();
9766 // draw the background, etc
9769 if(Multi_passwd_background >= 0){
9770 gr_restore_screen(Multi_passwd_background);
9772 gr_set_bitmap(Multi_pwd_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
9774 Multi_pwd_window.draw();
9781 // bring up the password string popup, fill in passwd (return 1 if accept was pressed, 0 if cancel was pressed)
9782 int multi_passwd_popup(char *passwd, const int max_plen)
9784 // if the popup is already running for some reason, don't do anything
9785 if(Multi_passwd_running){
9789 // initialize all graphics
9790 multi_passwd_init();
9793 multi_passwd_do(passwd, max_plen);
9795 // shut everything down
9796 multi_passwd_close();
9798 return Multi_passwd_done;