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 gameseq_post_event(GS_EVENT_MAIN_MENU);
1498 gamesnd_play_iface(SND_USER_SELECT);
1502 // page up the game list
1504 multi_join_list_page_up();
1506 Multi_join_slider.force_currentItem(Multi_join_list_start);
1511 multi_pinfo_popup(Net_player);
1514 // page down the game list
1516 multi_join_list_page_down();
1518 Multi_join_slider.force_currentItem(Multi_join_list_start);
1522 // send out a ping-all
1524 multi_join_ping_all();
1525 Multi_join_ping_stamp = timestamp(MULTI_JOIN_PING_TIME);
1528 // shortcut to start a game
1530 multi_join_create_game();
1533 // scroll the game list up
1535 multi_join_list_scroll_up();
1537 Multi_join_slider.force_currentItem(Multi_join_list_start);
1541 // scroll the game list down
1543 multi_join_list_scroll_down();
1545 Multi_join_slider.force_currentItem(Multi_join_list_start);
1550 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
1551 help_overlay_set_state(MULTI_JOIN_OVERLAY, 0);
1554 // do any network related stuff
1555 multi_join_do_netstuff();
1557 // process any button clicks
1558 multi_join_check_buttons();
1560 // process any list selection stuff
1561 multi_join_process_select();
1563 // draw the background, etc
1565 GR_MAYBE_CLEAR_RES(Multi_join_bitmap);
1566 if(Multi_join_bitmap != -1){
1567 gr_set_bitmap(Multi_join_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1570 Multi_join_window.draw();
1572 // display the active games
1573 multi_join_display_games();
1575 // display any text in the info area
1576 multi_common_render_text();
1578 // display any pending notification messages
1579 multi_common_notify_do();
1581 // blit the CD icon and any PXO filter stuff
1582 multi_join_blit_top_stuff();
1584 // draw the help overlay
1585 help_overlay_maybe_blit(MULTI_JOIN_OVERLAY);
1590 // if we are supposed to be sending a join request
1591 if(Multi_join_should_send != -1){
1592 multi_join_send_join_request(Multi_join_should_send);
1594 Multi_join_should_send = -1;
1596 // increment the frame count
1597 Multi_join_frame_count++;
1600 void multi_join_game_close()
1602 // unload any bitmaps
1603 if(!bm_unload(Multi_join_bitmap)){
1604 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_join_bitmap_fname[gr_screen.res]));
1607 // unload the help overlay
1608 help_overlay_unload(MULTI_JOIN_OVERLAY);
1610 // free up the active game list
1611 multi_free_active_games();
1613 // destroy the UI_WINDOW
1614 Multi_join_window.destroy();
1617 common_free_interface_palette();
1621 void multi_join_check_buttons()
1624 for(idx=0;idx<MULTI_JOIN_NUM_BUTTONS;idx++){
1625 // we only really need to check for one button pressed at a time, so we can break after
1627 if(Multi_join_buttons[gr_screen.res][idx].button.pressed()){
1628 multi_join_button_pressed(idx);
1634 void multi_join_button_pressed(int n)
1638 // if we're player PXO, go back there
1639 gameseq_post_event(GS_EVENT_MAIN_MENU);
1640 gamesnd_play_iface(SND_USER_SELECT);
1643 if(Active_game_count <= 0){
1644 multi_common_add_notify(XSTR("No games found!",757));
1645 gamesnd_play_iface(SND_GENERAL_FAIL);
1646 } else if(Multi_join_list_selected == -1){
1647 multi_common_add_notify(XSTR("No game selected!",758));
1648 gamesnd_play_iface(SND_GENERAL_FAIL);
1649 } else if((Multi_join_sent_stamp != -1) && !timestamp_elapsed(Multi_join_sent_stamp)){
1650 multi_common_add_notify(XSTR("Still waiting on previous join request!",759));
1651 gamesnd_play_iface(SND_GENERAL_FAIL);
1653 // otherwise, if he's already played PXO games, warn him
1655 if(Player->flags & PLAYER_FLAGS_HAS_PLAYED_PXO){
1656 if(!multi_join_warn_pxo()){
1662 // send the join request here
1663 SDL_assert(Multi_join_selected_item != NULL);
1665 // send a join request packet
1666 Multi_join_should_send = 0;
1668 gamesnd_play_iface(SND_COMMIT_PRESSED);
1674 if(!help_overlay_active(MULTI_JOIN_OVERLAY)){
1675 help_overlay_set_state(MULTI_JOIN_OVERLAY,1);
1677 help_overlay_set_state(MULTI_JOIN_OVERLAY,0);
1681 // scroll the game list up
1683 multi_join_list_scroll_up();
1685 Multi_join_slider.force_currentItem(Multi_join_list_start);
1689 // scroll the game list down
1690 case MJ_SCROLL_DOWN:
1691 multi_join_list_scroll_down();
1693 Multi_join_slider.force_currentItem(Multi_join_list_start);
1697 // scroll the info text box up
1698 case MJ_SCROLL_INFO_UP:
1699 multi_common_scroll_text_up();
1702 // scroll the info text box down
1703 case MJ_SCROLL_INFO_DOWN:
1704 multi_common_scroll_text_down();
1707 // go to the options screen
1709 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
1712 // go to the start game screen
1714 multi_join_create_game();
1717 // refresh the game/server list
1719 gamesnd_play_iface(SND_USER_SELECT);
1720 broadcast_game_query();
1723 // join a game as an observer
1724 case MJ_JOIN_OBSERVER:
1725 if(Active_game_count <= 0){
1726 multi_common_add_notify(XSTR("No games found!",757));
1727 gamesnd_play_iface(SND_GENERAL_FAIL);
1728 } else if(Multi_join_list_selected == -1){
1729 multi_common_add_notify(XSTR("No game selected!",758));
1730 gamesnd_play_iface(SND_GENERAL_FAIL);
1731 } else if((Multi_join_sent_stamp != -1) && !timestamp_elapsed(Multi_join_sent_stamp)){
1732 multi_common_add_notify(XSTR("Still waiting on previous join request!",759));
1733 gamesnd_play_iface(SND_GENERAL_FAIL);
1735 // send the join request here
1736 SDL_assert(Multi_join_selected_item != NULL);
1738 Multi_join_should_send = 1;
1740 gamesnd_play_iface(SND_COMMIT_PRESSED);
1745 multi_common_add_notify(XSTR("Not implemented yet!",760));
1746 gamesnd_play_iface(SND_GENERAL_FAIL);
1751 // display all relevant info for active games
1752 void multi_join_display_games()
1754 active_game *moveup = Multi_join_list_start_item;
1758 int y_start = Mj_list_y[gr_screen.res];
1763 // blit the game status (including text and type icon)
1764 multi_join_blit_game_status(moveup,y_start);
1766 // get the connection type
1767 con_type = (moveup->flags & AG_FLAG_CONNECTION_SPEED_MASK) >> AG_FLAG_CONNECTION_BIT;
1768 if((con_type > 4) || (con_type < 0)){
1772 // display the connection speed
1774 SDL_strlcpy(str, Multi_join_speed_labels[con_type], SDL_arraysize(str));
1775 gr_set_color_fast(Multi_join_speed_colors[con_type]);
1776 gr_string(Mj_speed_coords[gr_screen.res][MJ_X_COORD], y_start, str);
1778 // we'll want to have different colors for highlighted items, etc.
1779 if(moveup == Multi_join_selected_item){
1780 gr_set_color_fast(&Color_text_selected);
1782 gr_set_color_fast(&Color_text_normal);
1785 // display the game name, adding appropriate status chars
1787 if(moveup->flags & AG_FLAG_STANDALONE){
1788 SDL_strlcat(str, MJ_CHAR_STANDALONE, SDL_arraysize(str));
1790 if(moveup->flags & AG_FLAG_CAMPAIGN){
1791 SDL_strlcat(str, MJ_CHAR_CAMPAIGN, SDL_arraysize(str));
1794 // tack on the actual server name
1795 SDL_strlcat(str, " ", SDL_arraysize(str));
1796 SDL_strlcat(str, moveup->name, SDL_arraysize(str));
1797 if(strlen(moveup->mission_name) > 0){
1798 SDL_strlcat(str, " / ", SDL_arraysize(str));
1799 SDL_strlcat(str, moveup->mission_name, SDL_arraysize(str));
1802 // make sure the string fits in the display area and draw it
1803 gr_force_fit_string(str,200,Mj_game_name_coords[gr_screen.res][MJ_W_COORD]);
1804 gr_string(Mj_game_name_coords[gr_screen.res][MJ_X_COORD],y_start,str);
1806 // display the ping time
1807 if(moveup->ping.ping_avg > 0){
1808 if(moveup->ping.ping_avg > 1000){
1809 gr_set_color_fast(&Color_bright_red);
1810 SDL_strlcpy(str, XSTR("> 1 sec",761), SDL_arraysize(str));
1812 // set the appropriate ping time color indicator
1813 if(moveup->ping.ping_avg > MJ_PING_YELLOW){
1814 gr_set_color_fast(&Color_bright_red);
1815 } else if(moveup->ping.ping_avg > MJ_PING_YELLOW){
1816 gr_set_color_fast(&Color_bright_yellow);
1818 gr_set_color_fast(&Color_bright_green);
1821 SDL_snprintf(str, SDL_arraysize(str), "%d%s", moveup->ping.ping_avg, XSTR(" ms",762));
1824 gr_string(Mj_ping_coords[gr_screen.res][MJ_X_COORD],y_start,str);
1827 // display the number of players (be sure to center it)
1828 if(moveup == Multi_join_selected_item){
1829 gr_set_color_fast(&Color_text_selected);
1831 gr_set_color_fast(&Color_text_normal);
1833 SDL_snprintf(str, SDL_arraysize(str), "%d", moveup->num_players);
1834 gr_get_string_size(&w,&h,str);
1835 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);
1839 moveup = moveup->next;
1840 } while((moveup != Active_game_head) && (count < Mj_max_game_items[gr_screen.res]));
1842 // if there are no items on the list, display this info
1844 gr_set_color_fast(&Color_bright);
1845 gr_string(Mj_game_name_coords[gr_screen.res][MJ_X_COORD] - 30,y_start,XSTR("<No game servers found>",763));
1849 void multi_join_blit_game_status(active_game *game, int y)
1852 char status_text[25];
1854 // blit the proper icon
1856 switch( game->flags & AG_FLAG_TYPE_MASK ){
1859 if(Multi_common_icons[MICON_COOP] != -1){
1860 gr_set_bitmap(Multi_common_icons[MICON_COOP], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1865 // team vs. team game
1867 if(Multi_common_icons[MICON_TVT] != -1){
1868 gr_set_bitmap(Multi_common_icons[MICON_TVT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1875 case AG_FLAG_DOGFIGHT:
1876 if(Multi_common_icons[MICON_DOGFIGHT] != -1){
1877 gr_set_bitmap(Multi_common_icons[MICON_DOGFIGHT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
1883 // if we're supposed to draw a bitmap
1885 gr_bitmap(Mj_game_icon_coords[gr_screen.res][MJ_X_COORD],y-1);
1888 // blit the proper status text
1889 memset(status_text,0,25);
1891 switch( game->flags & AG_FLAG_STATE_MASK ){
1892 case AG_FLAG_FORMING:
1893 gr_set_color_fast(&Color_bright_green);
1894 SDL_strlcpy(status_text, XSTR("Forming", 764), SDL_arraysize(status_text));
1896 case AG_FLAG_BRIEFING:
1897 gr_set_color_fast(&Color_bright_red);
1898 SDL_strlcpy(status_text, XSTR("Briefing", 765), SDL_arraysize(status_text));
1900 case AG_FLAG_DEBRIEF:
1901 gr_set_color_fast(&Color_bright_red);
1902 SDL_strlcpy(status_text, XSTR("Debrief", 766), SDL_arraysize(status_text));
1905 gr_set_color_fast(&Color_bright_red);
1906 SDL_strlcpy(status_text, XSTR("Paused", 767), SDL_arraysize(status_text));
1908 case AG_FLAG_IN_MISSION:
1909 gr_set_color_fast(&Color_bright_red);
1910 SDL_strlcpy(status_text, XSTR("Playing", 768), SDL_arraysize(status_text));
1913 gr_set_color_fast(&Color_bright);
1914 SDL_strlcpy(status_text, XSTR("Unknown", 769), SDL_arraysize(status_text));
1917 gr_get_string_size(&str_w,NULL,status_text);
1918 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);
1921 // load in a list of active games from our tcp.cfg file
1922 void multi_join_load_tcp_addrs()
1924 char line[MAX_IP_STRING];
1929 // attempt to open the ip list file
1930 file = cfopen(IP_CONFIG_FNAME,"rt",CFILE_NORMAL,CF_TYPE_DATA);
1932 nprintf(("Network","Error loading tcp.cfg file!\n"));
1936 // free up any existing server list
1937 multi_free_server_list();
1939 // read in all the strings in the file
1940 while(!cfeof(file)){
1942 cfgets(line,MAX_IP_STRING,file);
1944 // strip off any newline character
1945 if(line[strlen(line) - 1] == '\n'){
1946 line[strlen(line) - 1] = '\0';
1949 // empty lines don't get processed
1950 if( (line[0] == '\0') || (line[0] == '\n') ){
1954 if ( !psnet_is_valid_ip_string(line) ) {
1955 nprintf(("Network","Invalid ip string (%s)\n",line));
1957 // copy the server ip address
1958 memset(&addr,0,sizeof(net_addr));
1959 addr.type = NET_TCP;
1960 psnet_string_to_addr(&addr, line, SDL_arraysize(line));
1961 if ( addr.port == 0 ){
1962 addr.port = DEFAULT_GAME_PORT;
1965 // create a new server item on the list
1966 item = multi_new_server_item();
1968 memcpy(&item->server_addr,&addr,sizeof(net_addr));
1976 // do stuff like pinging servers, sending out requests, etc
1977 void multi_join_do_netstuff()
1979 // handle game query stuff
1980 if(Multi_join_glr_stamp == -1){
1981 broadcast_game_query();
1983 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
1984 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME_LOCAL);
1986 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME);
1989 // otherwise send out game query and restamp
1990 else if(timestamp_elapsed(Multi_join_glr_stamp)){
1991 broadcast_game_query();
1993 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
1994 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME_LOCAL);
1996 Multi_join_glr_stamp = timestamp(MULTI_JOIN_REFRESH_TIME);
2000 // check to see if we've been accepted. If so, put up message saying so
2001 if ( Net_player->flags & (NETINFO_FLAG_ACCEPT_INGAME|NETINFO_FLAG_ACCEPT_CLIENT|NETINFO_FLAG_ACCEPT_HOST|NETINFO_FLAG_ACCEPT_OBSERVER) ) {
2002 multi_common_add_notify(XSTR("Accepted. Waiting for player data.",770));
2005 // check to see if any join packets we have sent have timed out
2006 if((Multi_join_sent_stamp != -1) && (timestamp_elapsed(Multi_join_sent_stamp))){
2007 Multi_join_sent_stamp = -1;
2008 multi_common_add_notify(XSTR("Join request timed out!",771));
2011 // check to see if we should be pinging everyone
2012 if((Multi_join_ping_stamp == -1) || (timestamp_elapsed(Multi_join_ping_stamp))){
2013 multi_join_ping_all();
2014 Multi_join_ping_stamp = timestamp(MULTI_JOIN_PING_TIME);
2018 multi_join_cull_timeouts();
2021 // evaluate a returned pong.
2022 void multi_join_eval_pong(net_addr *addr, fix pong_time)
2025 active_game *moveup = Active_game_head;
2030 if(psnet_same(&moveup->server_addr,addr)){
2032 multi_ping_eval_pong(&moveup->ping);
2036 moveup = moveup->next;
2038 } while(moveup != Active_game_head);
2041 // update the game's ping
2043 if(found && (moveup->ping_end > moveup->ping_start)){
2044 moveup->ping_time = f2fl(moveup->ping_end - moveup->ping_start);
2045 moveup->ping_start = -1;
2046 moveup->ping_end = -1;
2051 // ping all the server on the list
2052 void multi_join_ping_all()
2054 active_game *moveup = Active_game_head;
2059 moveup->ping_start = timer_get_fixed_seconds();
2060 moveup->ping_end = -1;
2061 send_ping(&moveup->server_addr);
2063 multi_ping_send(&moveup->server_addr,&moveup->ping);
2065 moveup = moveup->next;
2066 } while(moveup != Active_game_head);
2070 void multi_join_process_select()
2072 // if we don't have anything selected and there are items on the list - select the first one
2073 if((Multi_join_list_selected == -1) && (Active_game_count > 0)){
2074 Multi_join_list_selected = 0;
2075 Multi_join_selected_item = multi_join_get_game(0);
2076 MJ_LIST_START_SET(0);
2077 Multi_join_list_start_item = Multi_join_selected_item;
2079 // send a mission description request to this guy
2080 send_netgame_descript_packet(&Multi_join_selected_item->server_addr,0);
2081 multi_common_set_text("");
2083 // I sure hope this doesn't happen
2084 SDL_assert(Multi_join_selected_item != NULL);
2087 // otherwise see if he's clicked on an item
2088 else if(Multi_join_select_button.pressed() && (Active_game_count > 0)){
2090 Multi_join_select_button.get_mouse_pos(NULL,&y);
2092 if(item + Multi_join_list_start < Active_game_count){
2093 gamesnd_play_iface(SND_IFACE_MOUSE_CLICK);
2095 Multi_join_list_selected = item + Multi_join_list_start;
2096 Multi_join_selected_item = multi_join_get_game(Multi_join_list_selected);
2098 // I sure hope this doesn't happen
2099 SDL_assert(Multi_join_selected_item != NULL);
2101 // send a mission description request to this guy
2102 send_netgame_descript_packet(&Multi_join_selected_item->server_addr,0);
2103 multi_common_set_text("");
2107 // if he's double clicked, then select it and accept
2108 if(Multi_join_select_button.double_clicked()){
2110 Multi_join_select_button.get_mouse_pos(NULL,&y);
2112 if(item == Multi_join_list_selected){
2113 multi_join_button_pressed(MJ_ACCEPT);
2118 // return game n (0 based index)
2119 active_game *multi_join_get_game(int n)
2121 active_game *moveup = Active_game_head;
2128 moveup = moveup->next;
2129 while((moveup != Active_game_head) && (count != n)){
2130 moveup = moveup->next;
2133 if(moveup == Active_game_head){
2134 nprintf(("Network","Warning, couldn't find game item %d!\n",n));
2144 // scroll through the game list
2145 void multi_join_list_scroll_up()
2147 // if we're not at the beginning of the list, scroll up
2148 if(Multi_join_list_start_item != Active_game_head){
2149 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2151 MJ_LIST_START_DEC();
2153 gamesnd_play_iface(SND_SCROLL);
2155 gamesnd_play_iface(SND_GENERAL_FAIL);
2159 // scroll through the game list
2160 void multi_join_list_scroll_down()
2162 if((Active_game_count - Multi_join_list_start) > Mj_max_game_items[gr_screen.res]){
2163 Multi_join_list_start_item = Multi_join_list_start_item->next;
2165 MJ_LIST_START_INC();
2167 gamesnd_play_iface(SND_SCROLL);
2169 gamesnd_play_iface(SND_GENERAL_FAIL);
2173 void multi_join_list_page_up()
2175 // in this case, just set us to the beginning of the list
2176 if((Multi_join_list_start - Mj_max_game_items[gr_screen.res]) < 0){
2177 Multi_join_list_start_item = Active_game_head;
2179 MJ_LIST_START_SET(0);
2181 gamesnd_play_iface(SND_SCROLL);
2183 // otherwise page the whole thing up
2185 for(idx=0; idx<Mj_max_game_items[gr_screen.res]; idx++){
2186 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2188 MJ_LIST_START_DEC();
2190 gamesnd_play_iface(SND_SCROLL);
2194 void multi_join_list_page_down()
2198 // page the whole thing down
2199 while((count < Mj_max_game_items[gr_screen.res]) && ((Active_game_count - Multi_join_list_start) > Mj_max_game_items[gr_screen.res])){
2200 Multi_join_list_start_item = Multi_join_list_start_item->next;
2201 MJ_LIST_START_INC();
2206 gamesnd_play_iface(SND_SCROLL);
2209 void multi_join_cull_timeouts()
2211 active_game *backup;
2213 active_game *moveup = Active_game_head;
2215 // traverse through the entire list if any items exist
2219 if((moveup->heard_from_timer != -1) && (timestamp_elapsed(moveup->heard_from_timer))){
2220 Active_game_count--;
2222 // if this is the head of the list
2223 if(moveup == Active_game_head){
2224 // if this is the _only_ item on the list
2225 if(moveup->next == Active_game_head){
2226 // handle any gui details related to deleting this item
2227 multi_join_handle_item_cull(Active_game_head, count);
2229 free(Active_game_head);
2230 Active_game_head = NULL;
2233 // if there are other items on the list
2235 // handle any gui details related to deleting this item
2236 multi_join_handle_item_cull(moveup, count);
2238 Active_game_head = moveup->next;
2239 Active_game_head->prev = moveup->prev;
2240 Active_game_head->prev->next = Active_game_head;
2242 moveup = Active_game_head;
2245 // if its somewhere else on the list
2247 // handle any gui details related to deleting this item
2248 multi_join_handle_item_cull(moveup, count);
2250 // if its the last item on the list
2251 moveup->next->prev = moveup->prev;
2252 moveup->prev->next = moveup->next;
2254 // if it was the last element on the list, return
2255 if(moveup->next == Active_game_head){
2259 backup = moveup->next;
2265 moveup = moveup->next;
2268 } while(moveup != Active_game_head);
2272 // deep magic begins here.
2273 void multi_join_handle_item_cull(active_game *item, int item_index)
2275 // if this is the only item on the list, unset everything
2276 if(item->next == item){
2277 Multi_join_list_selected = -1;
2278 Multi_join_selected_item = NULL;
2281 Multi_join_slider.set_numberItems(0);
2283 MJ_LIST_START_SET(-1);
2284 Multi_join_list_start_item = NULL;
2290 // see if we should be adjusting our currently selected item
2291 if(item_index <= Multi_join_list_selected){
2292 // the selected item is the head of the list
2293 if(Multi_join_selected_item == Active_game_head){
2294 // move the pointer up since this item is about to be destroyed
2295 Multi_join_selected_item = Multi_join_selected_item->next;
2297 // if this is the item being deleted, select the previous one
2298 if(item == Multi_join_selected_item){
2300 Multi_join_selected_item = Multi_join_selected_item->prev;
2302 // decrement the selected index by 1
2303 Multi_join_list_selected--;
2305 // now we know its a previous item, so our pointer stays the same but our index goes down by one, since there will be
2306 // 1 less item on the list
2308 // decrement the selected index by 1
2309 Multi_join_list_selected--;
2314 // see if we should be adjusting out current start position
2315 if(item_index <= Multi_join_list_start){
2316 // the start position is the head of the list
2317 if(Multi_join_list_start_item == Active_game_head){
2318 // move the pointer up since this item is about to be destroyed
2319 Multi_join_list_start_item = Multi_join_list_start_item->next;
2321 // if this is the item being deleted, select the previous one
2322 if(item == Multi_join_list_start_item){
2323 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2325 // decrement the starting index by 1
2326 MJ_LIST_START_DEC();
2328 // but decrement the starting index by 1
2329 MJ_LIST_START_DEC();
2334 // maybe go back up a bit so that we always have a full page of items
2335 if(Active_game_count > Mj_max_game_items[gr_screen.res]){
2336 while((Active_game_count - Multi_join_list_start) < Mj_max_game_items[gr_screen.res]){
2337 Multi_join_list_start_item = Multi_join_list_start_item->prev;
2338 MJ_LIST_START_DEC();
2342 // set slider location
2344 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);
2345 Multi_join_slider.force_currentItem(Multi_join_list_start);
2349 void multi_join_send_join_request(int as_observer)
2351 // don't do anything if we have no items selected
2352 if(Multi_join_selected_item == NULL){
2356 // 5/26/98 -- for team v team games, don't allow ingame joining :-(
2357 if ( (Multi_join_selected_item->flags & AG_FLAG_TEAMS) && (Multi_join_selected_item->flags & (AG_FLAG_PAUSE|AG_FLAG_IN_MISSION)) ) {
2358 popup(0, 1, POPUP_OK, XSTR("Joining ingame is currently not allowed for team vs. team games",772));
2362 memset(&Multi_join_request,0,sizeof(join_request));
2364 // if the netgame is in password mode, put up a request for the password
2365 if(Multi_join_selected_item->flags & AG_FLAG_PASSWD){
2366 if(!multi_passwd_popup(Multi_join_request.passwd, SDL_arraysize(Multi_join_request.passwd))){
2370 nprintf(("Password : %s\n",Multi_join_request.passwd));
2373 // fill out the join request struct
2374 SDL_strlcpy(Multi_join_request.callsign, Player->callsign, SDL_arraysize(Multi_join_request.callsign));
2375 if(strlen(Player->image_filename) > 0){
2376 SDL_strlcpy(Multi_join_request.image_filename, Player->image_filename, SDL_arraysize(Multi_join_request.image_filename));
2379 if(strlen(Player->squad_filename) > 0){
2380 SDL_strlcpy(Multi_join_request.squad_filename, Player->squad_filename, SDL_arraysize(Multi_join_request.squad_filename));
2384 // tracker id (if any)
2385 Multi_join_request.tracker_id = Multi_tracker_id;
2387 // player's rank (at least, what he wants you to _believe_)
2388 Multi_join_request.player_rank = (ubyte)Player->stats.rank;
2391 Multi_join_request.flags = 0;
2393 Multi_join_request.flags |= JOIN_FLAG_AS_OBSERVER;
2396 // if the player has hacked data
2397 if(game_hacked_data()){
2398 Multi_join_request.flags |= JOIN_FLAG_HAXOR;
2403 SDL_strlcpy(Multi_join_request.pxo_squad_name, Multi_tracker_squad_name, LOGIN_LEN);
2406 // version of this server
2407 Multi_join_request.version = MULTI_FS_SERVER_VERSION;
2409 // server compatible version
2410 Multi_join_request.comp_version = MULTI_FS_SERVER_COMPATIBLE_VERSION;
2412 // his local player options
2413 memcpy(&Multi_join_request.player_options,&Player->m_local_options,sizeof(multi_local_options));
2415 // set the server address for the netgame
2416 memcpy(&Netgame.server_addr,&Multi_join_selected_item->server_addr,sizeof(net_addr));
2418 // send a join request to the guy
2419 send_join_packet(&Multi_join_selected_item->server_addr,&Multi_join_request);
2422 Multi_join_sent_stamp = timestamp(MULTI_JOIN_SENT_WAIT);
2425 multi_common_add_notify(XSTR("Sending join request...",773));
2428 void multi_join_create_game()
2430 // maybe warn the player about possible crappy server conditions
2431 if(!multi_join_maybe_warn()){
2435 // make sure to flag ourself as being the master
2436 Net_player->flags |= (NETINFO_FLAG_AM_MASTER | NETINFO_FLAG_GAME_HOST);
2437 Net_player->state = NETPLAYER_STATE_HOST_SETUP;
2439 // if we're in PXO mode, mark it down in our player struct
2440 if(MULTI_IS_TRACKER_GAME){
2441 Player->flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
2442 Player->save_flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
2444 // otherwise, if he's already played PXO games, warn him
2447 if(Player->flags & PLAYER_FLAGS_HAS_PLAYED_PXO){
2448 if(!multi_join_warn_pxo()){
2455 gameseq_post_event(GS_EVENT_MULTI_START_GAME);
2456 gamesnd_play_iface(SND_USER_SELECT);
2459 void multi_join_reset_join_stamp()
2461 // unset the timestamp here so the user can immediately send another join request
2462 Multi_join_sent_stamp = -1;
2463 multi_common_add_notify("");
2466 void multi_join_blit_top_stuff()
2468 // blit the cd icon if he has one
2469 if(Multi_has_cd && (Multi_common_icons[MICON_CD] != -1)){
2472 bm_get_info(Multi_common_icons[MICON_CD], &cd_w, NULL, NULL, NULL, NULL);
2474 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
2475 gr_bitmap((gr_screen.max_w / 2) - (cd_w / 2), Mj_cd_coords[gr_screen.res]);
2479 #define CW_CODE_CANCEL 0 // cancel the action
2480 #define CW_CODE_OK 1 // continue anyway
2481 #define CW_CODE_INFO 2 // gimme some more information
2483 #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)
2484 #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)
2486 #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)
2487 #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)
2489 int multi_join_warn_update_low(int code)
2493 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),LOW_WARN_TEXT);
2496 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),LOW_INFO_TEXT);
2499 return CW_CODE_CANCEL;
2502 int multi_join_warn_update_medium(int code)
2506 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),MED_WARN_TEXT);
2509 return popup(0,3,XSTR("&Cancel",779),XSTR("&Continue",780),XSTR("&More info",781),MED_INFO_TEXT);
2512 return CW_CODE_CANCEL;
2515 int multi_join_maybe_warn()
2519 // if the player is set for low updates
2520 if(Player->m_local_options.obj_update_level == OBJ_UPDATE_LOW){
2523 code = multi_join_warn_update_low(code);
2524 } while((code != CW_CODE_CANCEL) && (code != CW_CODE_OK));
2529 // if the player is set for medium updates
2530 else if(Player->m_local_options.obj_update_level == OBJ_UPDATE_MEDIUM){
2533 code = multi_join_warn_update_medium(code);
2534 } while((code != CW_CODE_CANCEL) && (code != CW_CODE_OK));
2542 int multi_join_warn_pxo()
2544 // 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;
2548 void multi_join_blit_protocol()
2550 gr_set_color_fast(&Color_bright);
2552 switch(Socket_type){
2555 gr_string(5, 2, "TCP");
2564 // -------------------------------------------------------------------------------------------------
2566 // MULTIPLAYER START GAME screen
2571 #define MULTI_SG_PALETTE "InterfacePalette"
2573 static const char *Multi_sg_bitmap_fname[GR_NUM_RESOLUTIONS] = {
2574 "MultiStartGame", // GR_640
2575 "2_MultiStartGame" // GR_1024
2578 static const char *Multi_sg_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
2579 "MultiStartGame-M", // GR_640
2580 "2_MultiStartGame-M" // GR_1024
2585 int Multi_sg_rank_max_display[GR_NUM_RESOLUTIONS] = {
2590 // constants for coordinate look ups
2591 #define MSG_X_COORD 0
2592 #define MSG_Y_COORD 1
2593 #define MSG_W_COORD 2
2594 #define MSG_H_COORD 3
2598 // input password field
2599 int Msg_passwd_coords[GR_NUM_RESOLUTIONS][4] = {
2612 // input game title field
2613 int Msg_title_coords[GR_NUM_RESOLUTIONS][4] = {
2626 // rank selected field
2627 int Msg_rank_sel_coords[GR_NUM_RESOLUTIONS][4] = {
2641 int Msg_rank_list_coords[GR_NUM_RESOLUTIONS][4] = {
2657 #define MULTI_SG_NUM_BUTTONS 12
2658 #define MSG_OPEN_GAME 0
2659 #define MSG_CLOSED_GAME 1
2660 #define MSG_PASSWD_GAME 2
2661 #define MSG_RESTRICTED_GAME 3
2662 #define MSG_RANK_SET_GAME 4
2663 #define MSG_RANK_SCROLL_UP 5
2664 #define MSG_RANK_SCROLL_DOWN 6
2665 #define MSG_RANK_ABOVE 7
2666 #define MSG_RANK_BELOW 8
2668 #define MSG_OPTIONS 10
2669 #define MSG_ACCEPT 11
2671 #define MULTI_SG_NUM_BUTTONS 10
2672 #define MSG_OPEN_GAME 0
2673 //#define MSG_CLOSED_GAME 1
2674 //#define MSG_RESTRICTED_GAME 2
2675 #define MSG_PASSWD_GAME 1
2676 #define MSG_RANK_SET_GAME 2
2677 #define MSG_RANK_SCROLL_UP 3
2678 #define MSG_RANK_SCROLL_DOWN 4
2679 #define MSG_RANK_ABOVE 5
2680 #define MSG_RANK_BELOW 6
2682 #define MSG_OPTIONS 8
2683 #define MSG_ACCEPT 9
2686 UI_WINDOW Multi_sg_window; // the window object for the join screen
2687 UI_BUTTON Multi_sg_rank_button; // for selecting the rank marker
2688 UI_INPUTBOX Multi_sg_game_name; // for Netgame.name
2689 UI_INPUTBOX Multi_sg_game_passwd; // for Netgame.passwd
2690 int Multi_sg_bitmap; // the background bitmap
2692 ui_button_info Multi_sg_buttons[GR_NUM_RESOLUTIONS][MULTI_SG_NUM_BUTTONS] = {
2695 ui_button_info("MSG_00", 75, 111, -1, -1, 0),
2696 ui_button_info("MSG_01", 75, 139, -1, -1, 1),
2697 ui_button_info("MSG_02", 75, 164, -1, -1, 2),
2698 ui_button_info("MSG_03", 75, 199, -1, -1, 3),
2699 ui_button_info("MSG_04", 75, 243, -1, -1, 4),
2700 ui_button_info("MSG_05", 376, 231, -1, -1, 5),
2701 ui_button_info("MSG_06", 376, 258, -1, -1, 6),
2702 ui_button_info("MSG_07", 376, 291, -1, -1, 7),
2703 ui_button_info("MSG_08", 376, 320, -1, -1, 8),
2704 ui_button_info("MSG_09", 469, 427, -1, -1, 9),
2705 ui_button_info("MSG_10", 447, 452, -1, -1, 10),
2706 ui_button_info("MSG_11", 561, 411, -1, -1, 11),
2708 ui_button_info("MSG_00", 1, 184, 34, 191, 2), // open
2709 // ui_button_info("MSG_01", 1, 159, 34, 166, 1), // closed
2710 // ui_button_info("MSG_02", 1, 184, 34, 191, 2), // restricted
2711 ui_button_info("MSG_03", 1, 209, 34, 218, 3), // password
2712 ui_button_info("MSG_04", 1, 257, 34, 266, 4), // rank set
2713 ui_button_info("MSG_05", 1, 282, -1, -1, 5), // rank scroll up
2714 ui_button_info("MSG_06", 1, 307, -1, -1, 6), // rank scroll down
2715 ui_button_info("MSG_07", 177, 282, 210, 290, 7), // rank above
2716 ui_button_info("MSG_08", 177, 307, 210, 315, 8), // rank below
2717 ui_button_info("MSG_09", 536, 429, 500, 440, 9), // help
2718 ui_button_info("MSG_10", 536, 454, 479, 464, 10), // options
2719 ui_button_info("MSG_11", 576, 432, 571, 415, 11), // accept
2723 ui_button_info("2_MSG_00", 2, 295, 51, 307, 2), // open
2724 // ui_button_info("2_MSG_01", 2, 254, 51, 267, 1), // closed
2725 // ui_button_info("2_MSG_02", 2, 295, 51, 307, 2), // restricted
2726 ui_button_info("2_MSG_03", 2, 335, 51, 350, 3), // password
2727 ui_button_info("2_MSG_04", 2, 412, 51, 426, 4), // rank set
2728 ui_button_info("2_MSG_05", 2, 452, -1, -1, 5), // rank scroll up
2729 ui_button_info("2_MSG_06", 2, 492, -1, -1, 6), // rank scroll down
2730 ui_button_info("2_MSG_07", 284, 452, 335, 465, 7), // rank above
2731 ui_button_info("2_MSG_08", 284, 492, 335, 505, 8), // rank below
2732 ui_button_info("2_MSG_09", 858, 687, 817, 706, 9), // help
2733 ui_button_info("2_MSG_10", 858, 728, 797, 743, 10), // options
2734 ui_button_info("2_MSG_11", 921, 692, 921, 664, 11), // accept
2735 #ifdef MAKE_FS1 // filler for extra FS1 buttons
2736 ui_button_info("none", -1, -1, -1, -1, -1),
2737 ui_button_info("none", -1, -1, -1, -1, -1),
2743 #define MULTI_SG_NUM_TEXT 11
2745 UI_XSTR Multi_sg_text[GR_NUM_RESOLUTIONS][MULTI_SG_NUM_TEXT] = {
2747 {"Open", 1322, 34, 191, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_OPEN_GAME].button},
2748 // {"Closed", 1323, 34, 166, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_CLOSED_GAME].button},
2749 // {"Restricted", 1324, 34, 191, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RESTRICTED_GAME].button},
2750 {"Password Protected", 1325, 34, 218, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_PASSWD_GAME].button},
2751 {"Allow Rank", 1326, 34, 266, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_SET_GAME].button},
2752 {"Above", 1327, 210, 290, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_ABOVE].button},
2753 {"Below", 1328, 210, 315, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_RANK_BELOW].button},
2754 {"Help", 928, 500, 440, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_HELP].button},
2755 {"Options", 1036, 479, 464, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[0][MSG_OPTIONS].button},
2756 {"Accept", 1035, 571, 415, UI_XSTR_COLOR_PINK, -1, &Multi_sg_buttons[0][MSG_ACCEPT].button},
2757 {"Start Game", 1329, 26, 10, UI_XSTR_COLOR_GREEN, -1, NULL},
2758 {"Title", 1330, 26, 31, UI_XSTR_COLOR_GREEN, -1, NULL},
2759 {"Game Type", 1331, 12, 165, UI_XSTR_COLOR_GREEN, -1, NULL},
2762 {"Open", 1322, 51, 307, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_OPEN_GAME].button},
2763 // {"Closed", 1323, 51, 267, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_CLOSED_GAME].button},
2764 // {"Restricted", 1324, 51, 307, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RESTRICTED_GAME].button},
2765 {"Password Protected", 1325, 51, 350, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_PASSWD_GAME].button},
2766 {"Allow Rank", 1326, 51, 426, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_SET_GAME].button},
2767 {"Above", 1327, 335, 465, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_ABOVE].button},
2768 {"Below", 1328, 335, 505, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_RANK_BELOW].button},
2769 {"Help", 928, 817, 706, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_HELP].button},
2770 {"Options", 1036, 797, 743, UI_XSTR_COLOR_GREEN, -1, &Multi_sg_buttons[1][MSG_OPTIONS].button},
2771 {"Accept", 1035, 921, 664, UI_XSTR_COLOR_PINK, -1, &Multi_sg_buttons[1][MSG_ACCEPT].button},
2772 {"Start Game", 1329, 42, 22, UI_XSTR_COLOR_GREEN, -1, NULL},
2773 {"Title", 1330, 42, 50, UI_XSTR_COLOR_GREEN, -1, NULL},
2774 {"Game Type", 1331, 20, 264, UI_XSTR_COLOR_GREEN, -1, NULL},
2779 // starting index for displaying ranks
2780 int Multi_sg_rank_start;
2781 int Multi_sg_rank_select;
2783 // netgame pointer to indirect through
2784 netgame_info *Multi_sg_netgame;
2786 // hold temporary values in this structure when on a standalone server
2787 netgame_info Multi_sg_netgame_temp;
2789 // forward declarations
2790 void multi_sg_check_buttons();
2791 void multi_sg_button_pressed(int n);
2792 void multi_sg_init_gamenet();
2793 void multi_sg_draw_radio_buttons();
2794 void multi_sg_rank_scroll_up();
2795 void multi_sg_rank_scroll_down();
2796 void multi_sg_rank_display_stuff();
2797 void multi_sg_rank_process_select();
2798 void multi_sg_rank_build_name(char *in, char *out, const int max_outlen);
2799 void multi_sg_check_passwd();
2800 void multi_sg_check_name();
2801 void multi_sg_release_passwd();
2802 int multi_sg_rank_select_valid(int rank);
2803 void multi_sg_select_rank_default();
2805 // function which takes a rank name and returns the index. Useful for commandline options
2806 // for above and below rank. We return the index of the rank in the Ranks[] array. If
2807 // the rank isn't found, we return -1
2808 int multi_start_game_rank_from_name( char *rank ) {
2812 for ( i = 0; i <= MAX_FREESPACE1_RANK; i++ ) {
2814 for ( i = 0; i <= MAX_FREESPACE2_RANK; i++ ) {
2816 if ( !SDL_strcasecmp(Ranks[i].name, rank) ) {
2824 void multi_start_game_init()
2828 // initialize the gamenet
2829 multi_sg_init_gamenet();
2831 // create the interface window
2832 Multi_sg_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
2833 Multi_sg_window.set_mask_bmap(Multi_sg_bitmap_mask_fname[gr_screen.res]);
2835 // load the background bitmap
2836 Multi_sg_bitmap = bm_load(Multi_sg_bitmap_fname[gr_screen.res]);
2837 if(Multi_sg_bitmap < 0){
2838 // we failed to load the bitmap - this is very bad
2842 // initialize the common notification messaging
2843 multi_common_notify_init();
2845 // initialize the common text area
2846 multi_common_set_text("");
2848 // use the common interface palette
2849 multi_common_set_palette();
2851 // create the interface buttons
2852 for(idx=0; idx<MULTI_SG_NUM_BUTTONS; idx++){
2853 // create the object
2854 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);
2856 // set the sound to play when highlighted
2857 Multi_sg_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
2859 // set the ani for the button
2860 Multi_sg_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sg_buttons[gr_screen.res][idx].filename);
2863 Multi_sg_buttons[gr_screen.res][idx].button.link_hotspot(Multi_sg_buttons[gr_screen.res][idx].hotspot);
2868 for(idx=0; idx<MULTI_SG_NUM_TEXT; idx++){
2869 Multi_sg_window.add_XSTR(&Multi_sg_text[gr_screen.res][idx]);
2873 // load the help overlay
2874 help_overlay_load(MULTI_START_OVERLAY);
2875 help_overlay_set_state(MULTI_START_OVERLAY,0);
2877 // intiialize the rank selection items
2878 multi_sg_select_rank_default();
2879 Multi_sg_rank_start = Multi_sg_rank_select;
2881 // create the rank select button
2882 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);
2883 Multi_sg_rank_button.hide();
2885 // create the netgame name input box
2886 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);
2888 // create the netgame password input box, and disable it by default
2889 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);
2890 Multi_sg_game_passwd.hide();
2891 Multi_sg_game_passwd.disable();
2893 // set the netgame text to this gadget and make it have focus
2894 Multi_sg_game_name.set_text(Multi_sg_netgame->name);
2895 Multi_sg_game_name.set_focus();
2897 // if starting a netgame, set the name of the game and any other options that are appropriate
2898 if ( Cmdline_start_netgame ) {
2899 if ( Cmdline_game_name != NULL ) {
2900 SDL_strlcpy( Multi_sg_netgame->name, Cmdline_game_name, SDL_arraysize(Multi_sg_netgame->name) );
2901 Multi_sg_game_name.set_text(Multi_sg_netgame->name);
2904 // deal with the different game types -- only one should even be active, so we will just go down
2905 // the line. Last one wins.
2906 if ( Cmdline_closed_game ) {
2907 Multi_sg_netgame->mode = NG_MODE_CLOSED;
2908 } else if ( Cmdline_restricted_game ) {
2909 Multi_sg_netgame->mode = NG_MODE_RESTRICTED;
2910 } else if ( Cmdline_game_password != NULL ) {
2911 Multi_sg_netgame->mode = NG_MODE_PASSWORD;
2912 SDL_strlcpy(Multi_sg_netgame->passwd, Cmdline_game_password, SDL_arraysize(Multi_sg_netgame->passwd));
2913 Multi_sg_game_passwd.set_text(Multi_sg_netgame->passwd);
2916 // deal with rank above and rank below
2917 if ( (Cmdline_rank_above != NULL) || (Cmdline_rank_below != NULL) ) {
2921 if ( Cmdline_rank_above != NULL ) {
2922 rank_str = Cmdline_rank_above;
2924 rank_str = Cmdline_rank_below;
2927 // try and get the rank index from the name -- if found, then set the rank base
2928 // and the game type. apparently we only support either above or below, not both
2929 // together, so I make a random choice
2930 rank = multi_start_game_rank_from_name( rank_str );
2932 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
2934 // now an arbitrary decision
2935 if ( Cmdline_rank_above != NULL ) {
2936 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
2938 Multi_sg_netgame->mode = NG_MODE_RANK_BELOW;
2943 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
2947 void multi_start_game_do()
2949 // return here since we will be moving to the next stage anyway -- I don't want to see the backgrounds of
2950 // all the screens for < 1 second for every screen we automatically move to.
2951 if ( Cmdline_start_netgame ) {
2955 int k = Multi_sg_window.process();
2957 // process any keypresses
2960 if(help_overlay_active(MULTI_START_OVERLAY)){
2961 help_overlay_set_state(MULTI_START_OVERLAY,0);
2963 gamesnd_play_iface(SND_USER_SELECT);
2964 multi_quit_game(PROMPT_NONE);
2969 case SDLK_LCTRL + SDLK_RETURN :
2970 case SDLK_RCTRL + SDLK_RETURN :
2971 gamesnd_play_iface(SND_COMMIT_PRESSED);
2972 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
2976 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
2977 help_overlay_set_state(MULTI_START_OVERLAY, 0);
2980 // check to see if the user has selected a different rank
2981 multi_sg_rank_process_select();
2983 // check any button presses
2984 multi_sg_check_buttons();
2986 // check to see if any of the input boxes have changed, and update the appropriate Netgame fields if necessary
2987 multi_sg_check_passwd();
2988 multi_sg_check_name();
2990 // draw the background, etc
2992 GR_MAYBE_CLEAR_RES(Multi_sg_bitmap);
2993 if(Multi_sg_bitmap != -1){
2994 gr_set_bitmap(Multi_sg_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
2997 Multi_sg_window.draw();
2999 // display rank stuff
3000 multi_sg_rank_display_stuff();
3002 // display any pending notification messages
3003 multi_common_notify_do();
3005 // draw all radio button
3006 multi_sg_draw_radio_buttons();
3008 // draw the help overlay
3009 help_overlay_maybe_blit(MULTI_START_OVERLAY);
3015 void multi_start_game_close()
3017 // if i'm the host on a standalone server, send him my start game options (passwd, mode, etc)
3018 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
3019 multi_options_update_start_game(Multi_sg_netgame);
3022 // unload any bitmaps
3023 if(!bm_unload(Multi_sg_bitmap)){
3024 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_sg_bitmap_fname[gr_screen.res]));
3027 // unload the help overlay
3028 help_overlay_unload(MULTI_START_OVERLAY);
3030 // destroy the UI_WINDOW
3031 Multi_sg_window.destroy();
3034 void multi_sg_check_buttons()
3037 for(idx=0;idx<MULTI_SG_NUM_BUTTONS;idx++){
3038 // we only really need to check for one button pressed at a time, so we can break after
3040 if(Multi_sg_buttons[gr_screen.res][idx].button.pressed()){
3041 multi_sg_button_pressed(idx);
3047 void multi_sg_button_pressed(int n)
3050 // go to the options screen
3052 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
3057 if(!help_overlay_active(MULTI_START_OVERLAY)){
3058 help_overlay_set_state(MULTI_START_OVERLAY,1);
3060 help_overlay_set_state(MULTI_START_OVERLAY,0);
3064 // the open button was pressed
3066 // if the closed option is selected
3067 if(Multi_sg_netgame->mode != NG_MODE_OPEN){
3068 Multi_sg_netgame->mode = NG_MODE_OPEN;
3070 gamesnd_play_iface(SND_USER_SELECT);
3072 // release the password control if necessary
3073 multi_sg_release_passwd();
3075 // if its already selected
3077 gamesnd_play_iface(SND_GENERAL_FAIL);
3082 // the open button was pressed
3083 case MSG_CLOSED_GAME:
3084 // if the closed option is selected
3085 if(Multi_sg_netgame->mode != NG_MODE_CLOSED){
3086 Multi_sg_netgame->mode = NG_MODE_CLOSED;
3088 gamesnd_play_iface(SND_USER_SELECT);
3090 // release the password control if necessary
3091 multi_sg_release_passwd();
3093 // if its already selected
3095 gamesnd_play_iface(SND_GENERAL_FAIL);
3100 // toggle password protection
3101 case MSG_PASSWD_GAME:
3102 // if we selected it
3103 if(Multi_sg_netgame->mode != NG_MODE_PASSWORD){
3104 gamesnd_play_iface(SND_USER_SELECT);
3106 Multi_sg_game_passwd.enable();
3107 Multi_sg_game_passwd.unhide();
3108 Multi_sg_game_passwd.set_focus();
3110 Multi_sg_netgame->mode = NG_MODE_PASSWORD;
3112 // copy in the current network password
3113 Multi_sg_game_passwd.set_text(Multi_sg_netgame->passwd);
3115 gamesnd_play_iface(SND_GENERAL_FAIL);
3120 // toggle "restricted" on or off
3121 case MSG_RESTRICTED_GAME:
3122 if(Multi_sg_netgame->mode != NG_MODE_RESTRICTED){
3123 gamesnd_play_iface(SND_USER_SELECT);
3124 Multi_sg_netgame->mode = NG_MODE_RESTRICTED;
3126 // release the password control if necessary
3127 multi_sg_release_passwd();
3129 gamesnd_play_iface(SND_GENERAL_FAIL);
3134 // turn off all rank requirements
3135 case MSG_RANK_SET_GAME:
3136 // if either is set, then turn then both off
3137 if((Multi_sg_netgame->mode != NG_MODE_RANK_BELOW) && (Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE)){
3138 gamesnd_play_iface(SND_USER_SELECT);
3140 // set it to the default case if we're turning it off
3141 multi_sg_select_rank_default();
3142 Multi_sg_rank_start = Multi_sg_rank_select;
3144 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
3146 // release the password control if necessary
3147 multi_sg_release_passwd();
3149 gamesnd_play_iface(SND_GENERAL_FAIL);
3153 // rank above was pressed
3154 case MSG_RANK_ABOVE :
3155 if((Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE) || (Multi_sg_netgame->mode == NG_MODE_RANK_BELOW)){
3156 Multi_sg_netgame->mode = NG_MODE_RANK_ABOVE;
3158 // select the first item
3159 multi_sg_select_rank_default();
3160 Multi_sg_rank_start = Multi_sg_rank_select;
3163 gamesnd_play_iface(SND_USER_SELECT);
3165 gamesnd_play_iface(SND_GENERAL_FAIL);
3169 // rank below was pressed
3170 case MSG_RANK_BELOW :
3171 if((Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE) || (Multi_sg_netgame->mode == NG_MODE_RANK_BELOW)){
3172 Multi_sg_netgame->mode = NG_MODE_RANK_BELOW;
3174 // select the first item
3175 multi_sg_select_rank_default();
3176 Multi_sg_rank_start = Multi_sg_rank_select;
3179 gamesnd_play_iface(SND_USER_SELECT);
3181 gamesnd_play_iface(SND_GENERAL_FAIL);
3185 // scroll the rank list up
3186 case MSG_RANK_SCROLL_UP:
3187 multi_sg_rank_scroll_up();
3190 // scroll the rank list down
3191 case MSG_RANK_SCROLL_DOWN:
3192 multi_sg_rank_scroll_down();
3195 // move to the create game screen
3197 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
3198 gamesnd_play_iface(SND_COMMIT_PRESSED);
3202 gamesnd_play_iface(SND_GENERAL_FAIL);
3203 multi_common_add_notify(XSTR("Not implemented yet!",760));
3208 // NOTE : this is where all Netgame initialization should take place on the host
3209 void multi_sg_init_gamenet()
3211 char buf[128],out_name[128];
3213 net_player *server_save;
3215 // back this data up in case we are already connected to a standalone
3216 memcpy(&save,&Netgame.server_addr,sizeof(net_addr));
3217 server_save = Netgame.server;
3219 // remove campaign flags
3220 Game_mode &= ~(GM_CAMPAIGN_MODE);
3222 // clear out the Netgame structure and start filling in the values
3223 memset( &Netgame, 0, sizeof(Netgame) );
3224 memset( &Multi_sg_netgame_temp, 0, sizeof(netgame_info) );
3226 // if we're on the standalone, we're not the server, so we don't care about setting the netgame state
3227 if(Net_player->state != NETPLAYER_STATE_STD_HOST_SETUP){
3228 Netgame.game_state = NETGAME_STATE_HOST_SETUP;
3229 Multi_sg_netgame = &Netgame;
3232 ml_string(NOX("Starting netgame as Host/Server"));
3234 Multi_sg_netgame = &Multi_sg_netgame_temp;
3238 memcpy(&temp_addr.s_addr, &Netgame.server_addr, 4);
3239 char *server_addr = inet_ntoa(temp_addr);
3240 ml_printf(NOX("Starting netgame as Host on Standalone server : %s"), (server_addr == NULL) ? NOX("Unknown") : server_addr);
3243 Net_player->tracker_player_id = Multi_tracker_id;
3245 Multi_sg_netgame->security = (rand() % 32766) + 1; // get some random security number
3246 Multi_sg_netgame->mode = NG_MODE_OPEN;
3247 Multi_sg_netgame->rank_base = RANK_ENSIGN;
3248 if(Multi_sg_netgame->security < 16){
3249 Multi_sg_netgame->security += 16;
3252 // set the version_info field
3253 Multi_sg_netgame->version_info = NG_VERSION_ID;
3255 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
3256 Netgame.host = Net_player;
3259 // set the default netgame flags
3260 Multi_sg_netgame->flags = 0;
3262 // intialize endgame stuff
3263 multi_endgame_init();
3265 // load in my netgame options
3266 multi_options_netgame_load(&Netgame.options);
3268 // load my local netplayer options
3269 multi_options_local_load(&Net_player->p_info.options, Net_player);
3271 // setup the default game name, taking care of string length and player callsigns
3272 memset(out_name,0,128);
3274 pilot_format_callsign_personal(Player->callsign, out_name, SDL_arraysize(out_name));
3275 SDL_snprintf(buf, SDL_arraysize(buf), XSTR("%s game",782), out_name); // [[ %s will be a pilot's name ]]
3276 if ( strlen(buf) > MAX_GAMENAME_LEN ){
3277 SDL_strlcpy(buf, XSTR("Temporary name",783), SDL_arraysize(buf));
3279 SDL_strlcpy(Multi_sg_netgame->name, buf, SDL_arraysize(Multi_sg_netgame->name));
3281 // set the default qos and duration
3282 multi_voice_maybe_update_vars(Netgame.options.voice_qos,Netgame.options.voice_record_time);
3284 // make sure to set the server correctly (me or the standalone)
3285 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3286 memcpy(&Netgame.server_addr, &Psnet_my_addr,sizeof(net_addr));
3287 Netgame.server = Net_player;
3288 Net_player->player_id = multi_get_new_id();
3290 // setup debug flags
3291 Netgame.debug_flags = 0;
3293 if(!Cmdline_server_firing){
3294 Netgame.debug_flags |= NETD_FLAG_CLIENT_FIRING;
3296 if(!Cmdline_client_dodamage){
3297 Netgame.debug_flags |= NETD_FLAG_CLIENT_NODAMAGE;
3301 memcpy(&Netgame.server_addr,&save,sizeof(net_addr));
3302 Netgame.server = server_save;
3305 // if I have a cd or not
3307 Net_player->flags |= NETINFO_FLAG_HAS_CD;
3310 // if I have hacked data
3311 if(game_hacked_data()){
3312 Net_player->flags |= NETINFO_FLAG_HAXOR;
3315 // assign my player struct and other data
3316 Net_player->flags |= (NETINFO_FLAG_CONNECTED | NETINFO_FLAG_DO_NETWORKING);
3317 Net_player->s_info.voice_token_timestamp = -1;
3319 // if we're supposed to flush our cache directory, do so now
3320 if(Net_player->p_info.options.flags & MLO_FLAG_FLUSH_CACHE){
3321 multi_flush_multidata_cache();
3324 ml_string(NOX("Flushing multi-data cache"));
3330 void multi_sg_draw_radio_buttons()
3332 // draw the appropriate radio button
3333 switch(Multi_sg_netgame->mode){
3335 Multi_sg_buttons[gr_screen.res][MSG_OPEN_GAME].button.draw_forced(2);
3339 case NG_MODE_CLOSED:
3340 Multi_sg_buttons[gr_screen.res][MSG_CLOSED_GAME].button.draw_forced(2);
3344 case NG_MODE_PASSWORD:
3345 Multi_sg_buttons[gr_screen.res][MSG_PASSWD_GAME].button.draw_forced(2);
3349 case NG_MODE_RESTRICTED:
3350 Multi_sg_buttons[gr_screen.res][MSG_RESTRICTED_GAME].button.draw_forced(2);
3354 case NG_MODE_RANK_ABOVE:
3355 Multi_sg_buttons[gr_screen.res][MSG_RANK_SET_GAME].button.draw_forced(2);
3356 Multi_sg_buttons[gr_screen.res][MSG_RANK_ABOVE].button.draw_forced(2);
3358 case NG_MODE_RANK_BELOW:
3359 Multi_sg_buttons[gr_screen.res][MSG_RANK_SET_GAME].button.draw_forced(2);
3360 Multi_sg_buttons[gr_screen.res][MSG_RANK_BELOW].button.draw_forced(2);
3365 void multi_sg_rank_scroll_up()
3367 // if he doesn't have either of the rank flags set, then ignore this
3368 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3372 if(Multi_sg_rank_start > 0){
3373 Multi_sg_rank_start--;
3374 gamesnd_play_iface(SND_SCROLL);
3376 gamesnd_play_iface(SND_GENERAL_FAIL);
3380 void multi_sg_rank_scroll_down()
3382 // if he doesn't have either of the rank flags set, then ignore this
3383 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3387 if((NUM_RANKS - Multi_sg_rank_start) > Multi_sg_rank_max_display[gr_screen.res]){
3388 Multi_sg_rank_start++;
3389 gamesnd_play_iface(SND_SCROLL);
3391 gamesnd_play_iface(SND_GENERAL_FAIL);
3395 void multi_sg_rank_display_stuff()
3400 // if he doesn't have either of the rank flags set, then ignore this
3401 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3405 // display the list of ranks
3406 y = Msg_rank_list_coords[gr_screen.res][MSG_Y_COORD];
3407 idx = Multi_sg_rank_start;
3409 while((count < NUM_RANKS) && (count < Multi_sg_rank_max_display[gr_screen.res]) && (idx < NUM_RANKS)){
3410 // if its the selected item, then color it differently
3411 if(idx == Multi_sg_rank_select){
3412 gr_set_color_fast(&Color_text_selected);
3414 gr_set_color_fast(&Color_text_normal);
3418 multi_sg_rank_build_name(Ranks[idx].name, rank_name, sizeof(rank_name));
3419 gr_string(Msg_rank_list_coords[gr_screen.res][MSG_X_COORD],y,rank_name);
3427 // display the selected rank
3429 gr_set_color_fast(&Color_bright);
3430 multi_sg_rank_build_name(Ranks[Multi_sg_netgame->rank_base].name, rank_name, SDL_arraysize(rank_name));
3431 gr_string(Msg_rank_sel_coords[gr_screen.res][MSG_X_COORD],Msg_rank_sel_coords[gr_screen.res][MSG_Y_COORD],rank_name);
3435 void multi_sg_rank_process_select()
3439 // if he doesn't have either of the rank flags set, then ignore this
3440 if((Multi_sg_netgame->mode != NG_MODE_RANK_ABOVE) && (Multi_sg_netgame->mode != NG_MODE_RANK_BELOW)){
3444 // see if he's clicked on an item on the rank list
3445 if(Multi_sg_rank_button.pressed()){
3447 Multi_sg_rank_button.get_mouse_pos(NULL,&y);
3450 if(item + Multi_sg_rank_start < NUM_RANKS){
3451 // evaluate whether this rank is valid for the guy to pick
3452 if(multi_sg_rank_select_valid(item + Multi_sg_rank_start)){
3453 gamesnd_play_iface(SND_USER_SELECT);
3455 Multi_sg_rank_select = item + Multi_sg_rank_start;
3457 // set the Netgame rank
3458 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
3460 gamesnd_play_iface(SND_GENERAL_FAIL);
3462 memset(string,0,255);
3463 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);
3464 multi_common_add_notify(string);
3470 void multi_sg_rank_build_name(char *in, char *out, const int max_outlen)
3475 SDL_strlcpy(use, in, SDL_arraysize(use));
3476 first = strtok(use," ");
3478 // just copy the string
3480 SDL_strlcpy(out, in, max_outlen);
3483 // if the first part of the string is lieutenant, then abbreivate it and tack on the rest of the string
3484 if (SDL_strcasecmp(first,XSTR("lieutenant",785)) == 0) {
3485 first = strtok(NULL, NOX("\n"));
3487 // if he's not just a plain lieutenant
3489 SDL_snprintf(out, max_outlen, "%s%s", XSTR("Lt. ",786), first); // [[ lieutenant ]]
3491 // if he _is_ just a plain lieutenant
3493 SDL_strlcpy(out, in, max_outlen);
3496 SDL_strlcpy(out, in, max_outlen);
3500 void multi_sg_check_passwd()
3502 // check to see if the password input box has been pressed
3503 if(Multi_sg_game_passwd.changed()){
3504 Multi_sg_game_passwd.get_text(Multi_sg_netgame->passwd);
3508 void multi_sg_check_name()
3510 // check to see if the game name input box has been pressed
3511 if(Multi_sg_game_name.changed()){
3512 Multi_sg_game_name.get_text(Multi_sg_netgame->name);
3516 void multi_sg_release_passwd()
3518 // hide and disable the password input box
3519 Multi_sg_game_passwd.hide();
3520 Multi_sg_game_passwd.disable();
3522 // set the focus back to the name input box
3523 Multi_sg_game_name.set_focus();
3526 int multi_sg_rank_select_valid(int rank)
3529 if(Multi_sg_netgame->mode == NG_MODE_RANK_ABOVE){
3530 if(Net_player->player->stats.rank >= rank){
3536 if(Net_player->player->stats.rank <= rank){
3544 void multi_sg_select_rank_default()
3546 // pick our rank for now
3547 Multi_sg_rank_select = Net_player->player->stats.rank;
3549 // set the Netgame rank
3550 Multi_sg_netgame->rank_base = Multi_sg_rank_select;
3553 // -------------------------------------------------------------------------------------------------
3555 // MULTIPLAYER CREATE GAME screen
3560 const char *Multi_create_bitmap_fname[GR_NUM_RESOLUTIONS] = {
3561 "MultiCreate", // GR_640
3562 "2_MultiCreate" // GR_1024
3565 const char *Multi_create_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
3566 "MultiCreate-M", // GR_640
3567 "2_MultiCreate-M" // GR_1024
3570 const char *Multi_create_loading_fname[GR_NUM_RESOLUTIONS] = {
3571 "PleaseWait", // GR_640
3572 "2_PleaseWait" // GR_1024
3576 #define MULTI_CREATE_NUM_BUTTONS 23
3579 #define MC_SHOW_ALL 0
3580 #define MC_SHOW_COOP 1
3581 #define MC_SHOW_TEAM 2
3582 #define MC_SHOW_DOGFIGHT 3
3583 #define MC_PXO_REFRESH 4
3584 #define MC_PILOT_INFO 5
3585 #define MC_SCROLL_LIST_UP 6
3586 #define MC_SCROLL_LIST_DOWN 7
3587 #define MC_SCROLL_PLAYERS_UP 8
3588 #define MC_SCROLL_PLAYERS_DOWN 9
3589 #define MC_MISSION_FILTER 10
3590 #define MC_CAMPAIGN_FILTER 11
3591 #define MC_CANCEL 12
3596 #define MC_SCROLL_INFO_UP 17
3597 #define MC_SCROLL_INFO_DOWN 18
3598 #define MC_HOST_OPTIONS 19
3600 #define MC_OPTIONS 21
3601 #define MC_ACCEPT 22
3604 UI_WINDOW Multi_create_window; // the window object for the create screen
3605 UI_BUTTON Multi_create_player_select_button; // for selecting players
3606 UI_BUTTON Multi_create_list_select_button; // for selecting missions/campaigns
3607 int Multi_create_bitmap; // the background bitmap
3608 UI_SLIDER2 Multi_create_slider; // for create list
3610 // constants for coordinate look ups
3611 #define MC_X_COORD 0
3612 #define MC_Y_COORD 1
3613 #define MC_W_COORD 2
3614 #define MC_H_COORD 3
3616 ui_button_info Multi_create_buttons[GR_NUM_RESOLUTIONS][MULTI_CREATE_NUM_BUTTONS] = {
3619 ui_button_info("MC_18", 34, 131, -1, -1, 18), // all
3620 ui_button_info("MC_19", 72, 131, -1, -1, 19), // coop
3621 ui_button_info("MC_20", 120, 131, -1, -1, 20), // team
3622 // ui_button_info("MC_21", 166, 131, -1, -1, 21), // dogfight
3623 ui_button_info("none", -1, -1, -1, -1, -1), // dogfight (not used)
3624 ui_button_info("none", -1, -1, -1, -1, -1), // pxo?
3625 ui_button_info("MC_26", 540, 114, -1, -1, 26), // pilot info
3626 ui_button_info("MC_03", 0, 187, -1, -1, 2), // scroll list up
3627 ui_button_info("MC_02", 0, 227, -1, -1, 3), // scroll list down
3628 ui_button_info("MC_04", 611, 182, -1, -1, 4), // scroll players up
3629 ui_button_info("MC_05", 611, 221, -1, -1, 5), // scroll players down
3630 ui_button_info("MC_06", 18, 322, -1, -1, 6), // mission filter
3631 ui_button_info("MC_07", 18, 344, -1, -1, 7), // campaign filter
3632 ui_button_info("MC_10", 317, 339, -1, -1, 10), // cancel
3633 ui_button_info("MC_14", 464, 350, -1, -1, 14), // team 1
3634 ui_button_info("MC_15", 498, 350, -1, -1, 15), // team 2
3635 ui_button_info("MC_16", 527, 346, -1, -1, 16), // kick
3636 ui_button_info("MC_17", 572, 346, -1, -1, 17), // close
3637 ui_button_info("MC_08", 0, 398, -1, -1, 8), // scroll mission info up
3638 ui_button_info("MC_09", 0, 435, -1, -1, 9), // scroll mission info down
3639 ui_button_info("MC_27", 447, 402, -1, -1, 27), // host options
3640 ui_button_info("MC_11", 510, 428, -1, -1, 11), // help
3641 ui_button_info("MC_12", 510, 453, -1, -1, 12), // options
3642 ui_button_info("Mc_13", 562, 412, -1, -1, 13), // commit
3644 ui_button_info("MC_00", 32, 129, 36, 158, 0), // show all missions
3645 ui_button_info("MC_01", 76, 129, 71, 158, 1), // show coop missions
3646 ui_button_info("MC_02", 121, 129, 119, 158, 2), // show team missions
3647 ui_button_info("MC_03", 164, 129, 166, 158, 3), // show dogfight missions
3648 ui_button_info("MC_04", 399, 129, 229, 130, 4), // pxo mission refresh
3649 ui_button_info("MC_05", 567, 123, 467, 132, 5), // pilot info
3650 ui_button_info("MC_06", 1, 161, -1, -1, 6), // scroll mission info up
3651 ui_button_info("MC_08", 1, 304, -1, -1, 8), // scroll mission info down
3652 ui_button_info("MC_09", 613, 160, -1, -1, 9), // scroll players up
3653 ui_button_info("MC_10", 613, 202, -1, -1, 10), // scroll players down
3654 ui_button_info("MC_11", 22, 346, 27, 376, 11), // mission filter
3655 ui_button_info("MC_12", 104, 346, 110, 376, 12), // campaign filter
3656 ui_button_info("MC_13", 392, 341, 328, 364, 13), // cancel
3657 ui_button_info("MC_14", 472, 352, 482, 381, 14), // team 0
3658 ui_button_info("MC_15", 506, 352, 514, 381, 15), // team 1
3659 ui_button_info("MC_16", 539, 346, 539, 381, 16), // kick
3660 ui_button_info("MC_17", 589, 346, 582, 381, 17), // close
3661 ui_button_info("MC_18", 1, 406, -1, -1, 18), // scroll list up
3662 ui_button_info("MC_19", 1, 447, -1, -1, 19), // scroll list down
3663 ui_button_info("MC_20", 499, 434, 436, 423, 20), // host options
3664 ui_button_info("MC_21", 534, 426, -1, -1, 21), // help
3665 ui_button_info("MC_22", 534, 452, -1, -1, 22), // options
3666 ui_button_info("MC_23", 571, 426, 572, 413, 23), // commit
3670 ui_button_info("2_MC_00", 51, 207, 61, 253, 0), // show all missions
3671 ui_button_info("2_MC_01", 122, 207, 124, 253, 1), // show coop missions
3672 ui_button_info("2_MC_02", 193, 207, 194, 253, 2), // show team missions
3673 ui_button_info("2_MC_03", 263, 207, 261, 253, 3), // show dogfight missions
3674 ui_button_info("2_MC_04", 639, 207, 479, 218, 4), // pxo mission refresh
3675 ui_button_info("2_MC_05", 907, 197, 748, 216, 5), // pilot info
3676 ui_button_info("2_MC_06", 1, 258, -1, -1, 6), // scroll mission info up
3677 ui_button_info("2_MC_08", 1, 487, -1, -1, 8), // scroll mission info down
3678 ui_button_info("2_MC_09", 981, 256, -1, -1, 9), // scroll players up
3679 ui_button_info("2_MC_10", 981, 323, -1, -1, 10), // scroll players down
3680 ui_button_info("2_MC_11", 35, 554, 46, 601, 11), // mission filter
3681 ui_button_info("2_MC_12", 166, 554, 174, 601, 12), // campaign filter
3682 ui_button_info("2_MC_13", 628, 545, 559, 582, 13), // cancel
3683 ui_button_info("2_MC_14", 756, 564, 772, 610, 14), // team 0
3684 ui_button_info("2_MC_15", 810, 564, 826, 610, 15), // team 1
3685 ui_button_info("2_MC_16", 862, 554, 872, 610, 16), // kick
3686 ui_button_info("2_MC_17", 943, 554, 949, 610, 17), // close
3687 ui_button_info("2_MC_18", 1, 649, -1, -1, 18), // scroll list up
3688 ui_button_info("2_MC_19", 1, 716, -1, -1, 19), // scroll list down
3689 ui_button_info("2_MC_20", 798, 695, 726, 667, 20), // host options
3690 ui_button_info("2_MC_21", 854, 681, -1, -1, 21), // help
3691 ui_button_info("2_MC_22", 854, 724, -1, -1, 22), // options
3692 ui_button_info("2_MC_23", 914, 681, 932, 667, 23), // commit
3697 #define MULTI_CREATE_NUM_TEXT 0
3699 #define MULTI_CREATE_NUM_TEXT 15
3701 UI_XSTR Multi_create_text[GR_NUM_RESOLUTIONS][MULTI_CREATE_NUM_BUTTONS] = {
3703 // not needed for FS1
3705 {"All", 1256, 36, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_ALL].button},
3706 {"Coop", 1257, 71, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_COOP].button},
3707 {"Team", 1258, 119, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_TEAM].button},
3708 {"Dogfight", 1259, 166, 158, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_SHOW_DOGFIGHT].button},
3709 {"Refresh Missions", 1260, 229, 130, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_PXO_REFRESH].button},
3710 {"Pilot Info", 1261, 467, 132, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_PILOT_INFO].button},
3711 {"Missions", 1262, 27, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_MISSION_FILTER].button},
3712 {"Campaigns", 1263, 110, 376, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_CAMPAIGN_FILTER].button},
3713 {"Cancel", 387, 328, 364, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_CANCEL].button},
3714 {"1", 1264, 482, 381, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_TEAM0].button},
3715 {"2", 1265, 514, 381, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_TEAM1].button},
3716 {"Kick", 1266, 539, 381, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_KICK].button},
3717 {"Close", 1508, 582, 381, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_CLOSE].button},
3718 {"Host Options", 1267, 436, 423, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[0][MC_HOST_OPTIONS].button},
3719 {"Commit", 1062, 572, 413, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[0][MC_ACCEPT].button}
3723 // not needed for FS1
3725 {"All", 1256, 61, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_ALL].button},
3726 {"Coop", 1257, 124, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_COOP].button},
3727 {"Team", 1258, 194, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_TEAM].button},
3728 {"Dogfight", 1259, 261, 253, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_SHOW_DOGFIGHT].button},
3729 {"Refresh Missions", 1260, 501, 218, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_PXO_REFRESH].button},
3730 {"Pilot Info", 1261, 814, 216, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_PILOT_INFO].button},
3731 {"Missions", 1262, 46, 601, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_MISSION_FILTER].button},
3732 {"Campaigns", 1263, 174, 601, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_CAMPAIGN_FILTER].button},
3733 {"Cancel", 387, 559, 582, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_CANCEL].button},
3734 {"1", 1264, 772, 610, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_TEAM0].button},
3735 {"2", 1265, 826, 610, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_TEAM1].button},
3736 {"Kick", 1266, 872, 610, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_KICK].button},
3737 {"Close", 1508, 949, 610, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_CLOSE].button},
3738 {"Host Options", 1267, 755, 683, UI_XSTR_COLOR_GREEN, -1, &Multi_create_buttons[1][MC_HOST_OPTIONS].button},
3739 {"Commit", 1062, 932, 667, UI_XSTR_COLOR_PINK, -1, &Multi_create_buttons[1][MC_ACCEPT].button}
3744 // squad war checkbox
3745 UI_CHECKBOX Multi_create_sw_checkbox;
3746 const char *Multi_create_sw_checkbox_fname[GR_NUM_RESOLUTIONS] = {
3750 int Multi_create_sw_checkbox_coords[GR_NUM_RESOLUTIONS][2] = {
3758 int Multi_create_sw_checkbox_text[GR_NUM_RESOLUTIONS][2] = {
3767 // game information text areas
3768 int Mc_list_coords[GR_NUM_RESOLUTIONS][4] = {
3781 int Mc_players_coords[GR_NUM_RESOLUTIONS][4] = {
3794 int Mc_info_coords[GR_NUM_RESOLUTIONS][4] = {
3807 // mission icon stuff
3808 int Mc_icon_type_coords[GR_NUM_RESOLUTIONS][2] = {
3810 38, -2 // y is an offset
3813 61, -2 // y is an offset
3817 int Mc_icon_volition_coords[GR_NUM_RESOLUTIONS][2] = {
3819 61, -1 // y is an offset
3822 98, 1 // y is an offset
3826 int Mc_icon_silent_coords[GR_NUM_RESOLUTIONS][2] = {
3828 72, 0 // y is an offset
3831 115, 0 // y is an offset
3835 int Mc_icon_valid_coords[GR_NUM_RESOLUTIONS][2] = {
3837 91, 0 // y is an offset
3840 146, 0 // y is an offset
3844 // mission/campaign list column areas
3845 int Mc_column1_w[GR_NUM_RESOLUTIONS] = {
3850 int Mc_column2_w[GR_NUM_RESOLUTIONS] = {
3855 int Mc_column3_w[GR_NUM_RESOLUTIONS] = {
3860 int Mc_mission_name_x[GR_NUM_RESOLUTIONS] = {
3865 int Mc_mission_count_x[GR_NUM_RESOLUTIONS] = {
3870 int Mc_mission_fname_x[GR_NUM_RESOLUTIONS] = {
3875 int Mc_create_game_text[GR_NUM_RESOLUTIONS][2] = {
3876 {13, 116}, // GR_640
3877 {21, 186} // GR_1024
3880 int Mc_players_text[GR_NUM_RESOLUTIONS][2] = {
3881 {467, 150}, // GR_640
3882 {747, 240} // GR_1024
3885 int Mc_team_text[GR_NUM_RESOLUTIONS][2] = {
3886 {484, 342}, // GR_640
3887 {774, 547} // GR_1024
3890 int Mc_slider_coords[GR_NUM_RESOLUTIONS][4] = {
3892 3, 197, 13, 105 // GR_640
3895 5, 316, 20, 168 // GR_1024
3899 const char *Mc_slider_bitmap[GR_NUM_RESOLUTIONS] = {
3904 // player list control thingie defs
3905 #define MULTI_CREATE_PLIST_MAX_DISPLAY 20
3906 int Multi_create_plist_select_flag; // flag indicating if we have a play selected
3907 short Multi_create_plist_select_id; // the net address of the currently selected player (for lookup)
3909 // master tracker details
3910 int Multi_create_frame_count; // framecount
3911 int Multi_create_mt_tried_login; // attempted to login this server on the MT
3913 // mission filter settings
3914 int Multi_create_filter; // what mode we're in
3916 // game/campaign list control defs
3917 int Multi_create_list_max_display[GR_NUM_RESOLUTIONS] = {
3923 int Multi_create_list_count; // number of items in listbox
3924 int Multi_create_list_mode; // 0 == mission mode, 1 == campaign mode
3925 int Multi_create_list_start; // where to start displaying from
3926 int Multi_create_list_select; // which item is currently highlighted
3927 int Multi_create_files_loaded;
3929 char Multi_create_files_array[MULTI_CREATE_MAX_LIST_ITEMS][MAX_FILENAME_LEN];
3931 int Multi_create_mission_count; // how many we have
3932 int Multi_create_campaign_count;
3933 multi_create_info Multi_create_mission_list[MULTI_CREATE_MAX_LIST_ITEMS];
3934 multi_create_info Multi_create_campaign_list[MULTI_CREATE_MAX_LIST_ITEMS];
3936 // use a pointer for the file list. Will point to either the missions or the campaigns
3937 multi_create_info *Multi_create_file_list;
3939 // LOCAL function definitions
3940 void multi_create_check_buttons();
3941 void multi_create_button_pressed(int n);
3942 void multi_create_init_as_server();
3943 void multi_create_init_as_client();
3944 void multi_create_do_netstuff();
3945 void multi_create_plist_scroll_up();
3946 void multi_create_plist_scroll_down();
3947 void multi_create_plist_process();
3948 void multi_create_plist_blit_normal();
3949 void multi_create_plist_blit_team();
3950 void multi_create_list_scroll_up();
3951 void multi_create_list_scroll_down();
3952 void multi_create_list_do();
3953 void multi_create_list_select_item(int n);
3954 void multi_create_list_blit_icons(int list_index, int y_start);
3955 void multi_create_accept_hit();
3956 void multi_create_draw_filter_buttons();
3957 void multi_create_set_selected_team(int team);
3958 short multi_create_get_mouse_id();
3959 int multi_create_ok_to_commit();
3960 int multi_create_verify_cds();
3961 void multi_create_refresh_pxo();
3962 void multi_create_sw_clicked();
3964 // since we can selectively filter out mission/campaign types we always need to map a selected index (which is relative
3965 // to the displayed list), to an absolute index (which is relative to the total file list - some of which may filtered out)
3966 void multi_create_select_to_filename(int select_index, char *filename, const int max_filelen);
3967 int multi_create_select_to_index(int select_index);
3969 int Multi_create_should_show_popup = 0;
3972 // sorting function to sort mission lists.. Basic sorting on mission name
3973 int multi_create_sort_func(const void *a, const void *b)
3975 multi_create_info *m1, *m2;
3977 m1 = (multi_create_info *)a;
3978 m2 = (multi_create_info *)b;
3980 return ( strcmp(m1->name, m2->name) );
3983 void multi_create_setup_list_data(int mode)
3985 int idx,should_sort,switched_modes;
3987 // set the current mode
3990 if((Multi_create_list_mode != mode) && (mode != -1)){
3991 Multi_create_list_mode = mode;
3994 // set up the list pointers
3995 if ( Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ) {
3996 Multi_create_file_list = Multi_create_mission_list;
3997 } else if ( Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ) {
3998 Multi_create_file_list = Multi_create_campaign_list;
4004 // get the mission count based upon the filter selected
4005 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
4006 switch(Multi_create_filter){
4007 case MISSION_TYPE_MULTI:
4008 Multi_create_list_count = Multi_create_mission_count;
4011 Multi_create_list_count = 0;
4012 // find all missions which match
4013 for(idx=0;idx<Multi_create_mission_count;idx++){
4014 if(Multi_create_mission_list[idx].flags & Multi_create_filter){
4015 Multi_create_list_count++;
4019 // if we switched modes and we have more than 0 items, sort them
4020 if(switched_modes && (Multi_create_list_count > 0)){
4025 } else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
4026 switch(Multi_create_filter){
4027 case MISSION_TYPE_MULTI:
4028 Multi_create_list_count = Multi_create_campaign_count;
4031 Multi_create_list_count = 0;
4032 // find all missions which match
4033 for(idx=0;idx<Multi_create_campaign_count;idx++){
4034 if(Multi_create_campaign_list[idx].flags & Multi_create_filter){
4035 Multi_create_list_count++;
4039 // if we switched modes and we have more than 0 items, sort them
4040 if(switched_modes && (Multi_create_list_count > 0)){
4047 // reset the list start and selected indices
4048 Multi_create_list_start = 0;
4049 Multi_create_list_select = -1;
4050 multi_create_list_select_item(Multi_create_list_start);
4052 // sort the list of missions if necessary
4054 qsort(Multi_create_file_list, Multi_create_list_count, sizeof(multi_create_info), multi_create_sort_func);
4059 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);
4063 void multi_create_game_init()
4068 // now make sure to initialze various netgame stuff based upon whether we're on a standalone or not
4069 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4070 multi_create_init_as_server();
4072 multi_create_init_as_client();
4075 // initialize the player list data
4076 Multi_create_plist_select_flag = 0;
4077 Multi_create_plist_select_id = -1;
4079 // create the interface window
4080 Multi_create_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
4081 Multi_create_window.set_mask_bmap(Multi_create_bitmap_mask_fname[gr_screen.res]);
4083 // load the background bitmap
4084 Multi_create_bitmap = bm_load(Multi_create_bitmap_fname[gr_screen.res]);
4085 if(Multi_create_bitmap < 0){
4086 // we failed to load the bitmap - this is very bad
4090 // close any previous existing instances of the chatbox and create a new one
4094 // load the help overlay
4095 help_overlay_load(MULTI_CREATE_OVERLAY);
4096 help_overlay_set_state(MULTI_CREATE_OVERLAY, 0);
4098 // initialize the common notification messaging
4099 multi_common_notify_init();
4101 // use the common interface palette
4102 multi_common_set_palette();
4104 // create the interface buttons
4105 for(idx=0; idx<MULTI_CREATE_NUM_BUTTONS; idx++){
4106 b = &Multi_create_buttons[gr_screen.res][idx];
4108 // create the object
4109 b->button.create(&Multi_create_window, "", b->x, b->y, 1, 1, ((idx == MC_SCROLL_LIST_UP) || (idx == MC_SCROLL_LIST_DOWN)), 1);
4111 // set the sound to play when highlighted
4112 b->button.set_highlight_action(common_play_highlight_sound);
4114 // set the ani for the button
4115 b->button.set_bmaps(b->filename);
4118 b->button.link_hotspot(b->hotspot);
4120 // some special case stuff for the pxo refresh button
4121 if(idx == MC_PXO_REFRESH){
4122 // if not a PXO game, or if I'm not a server disable and hide the button
4123 if(!MULTI_IS_TRACKER_GAME || !MULTIPLAYER_MASTER){
4125 b->button.disable();
4131 for(idx=0; idx<MULTI_CREATE_NUM_TEXT; idx++){
4132 Multi_create_window.add_XSTR(&Multi_create_text[gr_screen.res][idx]);
4135 // if this is a PXO game, enable the squadwar checkbox
4136 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);
4137 Multi_create_sw_checkbox.set_bmaps(Multi_create_sw_checkbox_fname[gr_screen.res], 6, 0);
4138 if(!MULTI_IS_TRACKER_GAME){
4139 Multi_create_sw_checkbox.hide();
4140 Multi_create_sw_checkbox.disable();
4144 // disable squad war button in demo
4145 Multi_create_sw_checkbox.hide();
4146 Multi_create_sw_checkbox.disable();
4149 // initialize the mission type filtering mode
4150 Multi_create_filter = MISSION_TYPE_MULTI;
4152 // initialize the list mode, and load in a list
4153 memset(Multi_create_mission_list, 0, sizeof(multi_create_info) * MULTI_CREATE_MAX_LIST_ITEMS);
4154 memset(Multi_create_campaign_list, 0, sizeof(multi_create_info) * MULTI_CREATE_MAX_LIST_ITEMS);
4155 for(idx=0; idx<MULTI_CREATE_MAX_LIST_ITEMS; idx++){
4156 Multi_create_mission_list[idx].valid_status = MVALID_STATUS_UNKNOWN;
4157 Multi_create_campaign_list[idx].valid_status = MVALID_STATUS_UNKNOWN;
4159 Multi_create_list_mode = MULTI_CREATE_SHOW_MISSIONS;
4160 Multi_create_list_start = -1;
4161 Multi_create_list_select = -1;
4162 Multi_create_list_count = 0;
4165 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);
4168 // create the player list select button
4169 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);
4170 Multi_create_player_select_button.hide();
4172 // create the mission/campaign list select button
4173 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);
4174 Multi_create_list_select_button.hide();
4176 // set hotkeys for a couple of things.
4177 Multi_create_buttons[gr_screen.res][MC_ACCEPT].button.set_hotkey(KEY_CTRLED+SDLK_RETURN);
4179 // init some master tracker stuff
4180 Multi_create_frame_count = 0;
4181 Multi_create_mt_tried_login = 0;
4183 // remove campaign flags
4184 Game_mode &= ~(GM_CAMPAIGN_MODE);
4186 // send any pilots as appropriate
4187 multi_data_send_my_junk();
4188 Multi_create_file_list = Multi_create_mission_list;
4190 Multi_create_campaign_count = 0;
4191 Multi_create_mission_count = 0;
4192 Multi_create_files_loaded = 0;
4195 void multi_create_game_do()
4199 const char *loading_str = XSTR("Loading", 1336);
4203 // set this if we want to show the pilot info popup
4204 Multi_create_should_show_popup = 0;
4206 // first thing is to load the files
4207 if ( !Multi_create_files_loaded ) {
4208 // if I am a client, send a list request to the server for the missions
4209 if ( MULTIPLAYER_CLIENT ) {
4210 send_mission_list_request( MISSION_LIST_REQUEST );
4214 loading_bitmap = bm_load(Multi_create_loading_fname[gr_screen.res]);
4216 // draw the background, etc
4218 GR_MAYBE_CLEAR_RES(Multi_create_bitmap);
4219 if(Multi_create_bitmap != -1){
4220 gr_set_bitmap(Multi_create_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4224 if ( loading_bitmap > -1 ){
4225 gr_set_bitmap(loading_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4227 gr_bitmap( Please_wait_coords[gr_screen.res][MC_X_COORD], Please_wait_coords[gr_screen.res][MC_Y_COORD] );
4229 // draw "Loading" on it
4231 gr_set_color_fast(&Color_normal);
4233 gr_get_string_size(&str_w, &str_h, loading_str);
4234 gr_string((gr_screen.max_w - str_w) / 2, (gr_screen.max_h - str_h) / 2, loading_str);
4240 multi_create_list_load_missions();
4241 multi_create_list_load_campaigns();
4243 // if this is a tracker game, validate missions
4244 if(MULTI_IS_TRACKER_GAME){
4245 multi_update_valid_missions();
4248 // update the file list
4249 multi_create_setup_list_data(MULTI_CREATE_SHOW_MISSIONS);
4252 // don't bother setting netgame state if ont the server
4253 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4254 Netgame.game_state = NETGAME_STATE_FORMING;
4255 send_netgame_update_packet();
4258 // if we're on the standalone we have to tell him that we're now in the host setup screen
4259 Net_player->state = NETPLAYER_STATE_HOST_SETUP;
4260 send_netplayer_update_packet();
4262 Multi_create_files_loaded = 1;
4265 int k = chatbox_process();
4266 k = Multi_create_window.process(k,0);
4269 // same as the cancel button
4271 if(help_overlay_active(MULTI_CREATE_OVERLAY)){
4272 help_overlay_set_state(MULTI_CREATE_OVERLAY,0);
4274 gamesnd_play_iface(SND_USER_SELECT);
4275 multi_quit_game(PROMPT_HOST);
4280 if ( mouse_down(MOUSE_LEFT_BUTTON) ) {
4281 help_overlay_set_state(MULTI_CREATE_OVERLAY, 0);
4284 // process any button clicks
4285 multi_create_check_buttons();
4287 // do any network related stuff
4288 multi_create_do_netstuff();
4290 // draw the background, etc
4292 GR_MAYBE_CLEAR_RES(Multi_create_bitmap);
4293 if(Multi_create_bitmap != -1){
4294 gr_set_bitmap(Multi_create_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4298 // if we're not in team vs. team mode, don't draw the team buttons
4299 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
4300 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.hide();
4301 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.hide();
4302 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.disable();
4303 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.disable();
4305 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.enable();
4306 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.enable();
4307 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.unhide();
4308 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.unhide();
4311 // draw the window itself
4312 Multi_create_window.draw();
4314 gr_set_color_fast(&Color_normal);
4317 // draw Create Game text
4318 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));
4320 // draw players text
4321 gr_string(Mc_players_text[gr_screen.res][MC_X_COORD], Mc_players_text[gr_screen.res][MC_Y_COORD], XSTR("Players", 1269));
4323 // draw players text
4324 gr_string(Mc_team_text[gr_screen.res][MC_X_COORD], Mc_team_text[gr_screen.res][MC_Y_COORD], XSTR("Team", 1258));
4327 // process and display the player list
4328 // NOTE : this must be done before the buttons are checked to insure that a player hasn't dropped
4329 multi_create_plist_process();
4330 if(Netgame.type_flags & NG_TYPE_TEAM){
4331 multi_create_plist_blit_team();
4333 multi_create_plist_blit_normal();
4336 // process and display the game/campaign list
4337 multi_create_list_do();
4339 // draw the correct mission filter button
4340 multi_create_draw_filter_buttons();
4342 // display any text in the info area
4343 multi_common_render_text();
4345 // display any pending notification messages
4346 multi_common_notify_do();
4348 // force the correct mission/campaign button to light up
4349 if( Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ){
4350 Multi_create_buttons[gr_screen.res][MC_MISSION_FILTER].button.draw_forced(2);
4352 Multi_create_buttons[gr_screen.res][MC_CAMPAIGN_FILTER].button.draw_forced(2);
4355 // force draw the closed button if it is toggled on
4356 if(Netgame.flags & NG_FLAG_TEMP_CLOSED){
4357 Multi_create_buttons[gr_screen.res][MC_CLOSE].button.draw_forced(2);
4360 // process and show the chatbox thingie
4364 Multi_create_window.draw_tooltip();
4366 // display the voice status indicator
4367 multi_common_voice_display_status();
4369 // blit the help overlay if necessary
4370 help_overlay_maybe_blit(MULTI_CREATE_OVERLAY);
4373 if(MULTI_IS_TRACKER_GAME){
4374 if(Netgame.type_flags & NG_TYPE_SW){
4375 gr_set_color_fast(&Color_bright);
4377 gr_set_color_fast(&Color_normal);
4379 gr_string(Multi_create_sw_checkbox_text[gr_screen.res][0], Multi_create_sw_checkbox_text[gr_screen.res][1], "SquadWar");
4385 // if we're supposed to show the pilot info popup, do it now
4386 if(Multi_create_should_show_popup){
4387 // get the player index and address of the player item the mouse is currently over
4388 if(Multi_create_plist_select_flag){
4389 player_index = find_player_id(Multi_create_plist_select_id);
4390 if(player_index != -1){
4391 multi_pinfo_popup(&Net_players[player_index]);
4396 // increment the frame count
4397 Multi_create_frame_count++;
4400 void multi_create_game_close()
4402 // unload any bitmaps
4403 if(!bm_unload(Multi_create_bitmap)){
4404 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_create_bitmap_fname[gr_screen.res]));
4407 // unload the help overlay
4408 help_overlay_unload(MULTI_CREATE_OVERLAY);
4410 // destroy the chatbox
4413 // destroy the UI_WINDOW
4414 Multi_create_window.destroy();
4417 void multi_create_check_buttons()
4420 for(idx=0;idx<MULTI_CREATE_NUM_BUTTONS;idx++){
4421 // we only really need to check for one button pressed at a time, so we can break after
4423 if(Multi_create_buttons[gr_screen.res][idx].button.pressed()){
4424 multi_create_button_pressed(idx);
4429 // if the squad war checkbox was clicked
4430 if(Multi_create_sw_checkbox.changed()){
4431 multi_create_sw_clicked();
4435 void multi_create_button_pressed(int n)
4441 gamesnd_play_iface(SND_USER_SELECT);
4442 multi_quit_game(PROMPT_HOST);
4445 // if valid commit conditions have not been met
4446 if(!multi_create_ok_to_commit()){
4451 multi_create_accept_hit();
4456 if(!help_overlay_active(MULTI_CREATE_OVERLAY)){
4457 help_overlay_set_state(MULTI_CREATE_OVERLAY,1);
4459 help_overlay_set_state(MULTI_CREATE_OVERLAY,0);
4463 // scroll the info text box up
4464 case MC_SCROLL_INFO_UP:
4465 multi_common_scroll_text_up();
4468 // scroll the info text box down
4469 case MC_SCROLL_INFO_DOWN:
4470 multi_common_scroll_text_down();
4473 // scroll the player list up
4474 case MC_SCROLL_PLAYERS_UP:
4475 multi_create_plist_scroll_up();
4478 // scroll the player list down
4479 case MC_SCROLL_PLAYERS_DOWN:
4480 multi_create_plist_scroll_down();
4483 // scroll the game/campaign list up
4484 case MC_SCROLL_LIST_UP:
4485 multi_create_list_scroll_up();
4487 Multi_create_slider.forceUp(); // move slider up
4491 // scroll the game/campaign list down
4492 case MC_SCROLL_LIST_DOWN:
4493 multi_create_list_scroll_down();
4495 Multi_create_slider.forceDown(); // move slider down
4499 // go to the options screen
4501 gamesnd_play_iface(SND_USER_SELECT);
4502 gameseq_post_event(GS_EVENT_OPTIONS_MENU);
4505 // show all missions
4507 if(Multi_create_filter != MISSION_TYPE_MULTI){
4508 gamesnd_play_iface(SND_USER_SELECT);
4509 Multi_create_filter = MISSION_TYPE_MULTI;
4510 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4512 gamesnd_play_iface(SND_GENERAL_FAIL);
4516 // show cooperative missions
4518 if(Multi_create_filter != MISSION_TYPE_MULTI_COOP){
4519 gamesnd_play_iface(SND_USER_SELECT);
4520 Multi_create_filter = MISSION_TYPE_MULTI_COOP;
4521 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4523 gamesnd_play_iface(SND_GENERAL_FAIL);
4527 // show team vs. team missions
4529 if(Multi_create_filter != MISSION_TYPE_MULTI_TEAMS){
4530 gamesnd_play_iface(SND_USER_SELECT);
4531 Multi_create_filter = MISSION_TYPE_MULTI_TEAMS;
4532 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4534 gamesnd_play_iface(SND_GENERAL_FAIL);
4538 // show dogfight missions
4540 case MC_SHOW_DOGFIGHT:
4541 if (Multi_create_filter != MISSION_TYPE_MULTI_DOGFIGHT){
4542 gamesnd_play_iface(SND_USER_SELECT);
4543 Multi_create_filter = MISSION_TYPE_MULTI_DOGFIGHT;
4544 multi_create_setup_list_data(Multi_create_list_mode); // update the file list
4546 gamesnd_play_iface(SND_GENERAL_FAIL);
4551 // toggle temporary netgame closed on/off
4553 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4554 Netgame.flags ^= NG_FLAG_TEMP_CLOSED;
4556 Netgame.options.flags |= MLO_FLAG_TEMP_CLOSED;
4557 multi_options_update_netgame();
4559 gamesnd_play_iface(SND_USER_SELECT);
4562 // kick the currently selected player (if possible)
4564 // lookup the player at the specified index
4565 if(Multi_create_plist_select_flag){
4566 idx = find_player_id(Multi_create_plist_select_id);
4567 // kick him - but don't ban him
4569 multi_kick_player(idx,0);
4574 // switch to individual mission mode and load in a list
4575 case MC_MISSION_FILTER:
4576 if(Multi_create_list_mode != MULTI_CREATE_SHOW_MISSIONS){
4577 Netgame.campaign_mode = MP_SINGLE;
4579 gamesnd_play_iface(SND_USER_SELECT);
4581 // update the file list
4582 multi_create_setup_list_data(MULTI_CREATE_SHOW_MISSIONS);
4584 gamesnd_play_iface(SND_GENERAL_FAIL);
4588 // switch to campaign mode and load in a list
4589 case MC_CAMPAIGN_FILTER:
4590 // switch off squad war
4591 Multi_create_sw_checkbox.set_state(0);
4592 Netgame.type_flags = NG_TYPE_COOP;
4594 if(Multi_create_list_mode != MULTI_CREATE_SHOW_CAMPAIGNS){
4595 Netgame.campaign_mode = MP_CAMPAIGN;
4597 gamesnd_play_iface(SND_USER_SELECT);
4599 // update the file list
4600 multi_create_setup_list_data(MULTI_CREATE_SHOW_CAMPAIGNS);
4602 gamesnd_play_iface(SND_GENERAL_FAIL);
4606 // attempt to set the selected player's team
4608 multi_create_set_selected_team(0);
4609 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4610 multi_team_send_update();
4614 // attempt to set the selected player's team
4616 multi_create_set_selected_team(1);
4617 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4618 multi_team_send_update();
4622 // popup the pilot info dialog for the currently selected pilot (will occur at the end of the frame)
4624 Multi_create_should_show_popup = 1;
4627 // go to the host options screen
4628 case MC_HOST_OPTIONS:
4629 gamesnd_play_iface(SND_USER_SELECT);
4630 gameseq_post_event(GS_EVENT_MULTI_HOST_OPTIONS);
4633 // refresh PXO file list
4634 case MC_PXO_REFRESH:
4635 if(!MULTI_IS_TRACKER_GAME){
4638 multi_create_refresh_pxo();
4642 gamesnd_play_iface(SND_GENERAL_FAIL);
4643 multi_common_add_notify(XSTR("Not implemented yet!",760));
4648 // do stuff like pinging servers, sending out requests, etc
4649 void multi_create_do_netstuff()
4653 // if not on a standalone
4654 void multi_create_init_as_server()
4656 // set me up as the host and master
4657 Net_player->flags |= (NETINFO_FLAG_AM_MASTER | NETINFO_FLAG_GAME_HOST);
4660 // if on a standalone
4661 void multi_create_init_as_client()
4663 Net_player->flags |= NETINFO_FLAG_GAME_HOST;
4666 // scroll up through the player list
4667 void multi_create_plist_scroll_up()
4669 gamesnd_play_iface(SND_GENERAL_FAIL);
4672 // scroll down through the player list
4673 void multi_create_plist_scroll_down()
4675 gamesnd_play_iface(SND_GENERAL_FAIL);
4678 void multi_create_plist_process()
4680 int test_count,idx,player_index;
4682 // first determine if there are 0 players in the game. This should never happen since the host is _always_ in the game
4684 for(idx=0;idx<MAX_PLAYERS;idx++){
4685 // count anyone except the standalone server (if applicable)
4686 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
4690 if(test_count <= 0){
4694 // if we had a selected item but that player has left, select myself instead
4695 if(Multi_create_plist_select_flag){
4696 player_index = find_player_id(Multi_create_plist_select_id);
4697 if(player_index == -1){
4698 Multi_create_plist_select_id = Net_player->player_id;
4701 Multi_create_plist_select_flag = 1;
4702 Multi_create_plist_select_id = Net_player->player_id;
4705 // if the player has clicked somewhere in the player list area
4706 if(Multi_create_player_select_button.pressed()){
4709 // get the player index and address of the player item the mouse is currently over
4710 player_id = multi_create_get_mouse_id();
4711 player_index = find_player_id(player_id);
4712 if(player_index != -1){
4713 Multi_create_plist_select_flag = 1;
4714 Multi_create_plist_select_id = player_id;
4719 void multi_create_plist_blit_normal()
4722 char str[CALLSIGN_LEN+5];
4723 int y_start = Mc_players_coords[gr_screen.res][MC_Y_COORD];
4726 // display all the players
4727 for(idx=0;idx<MAX_PLAYERS;idx++){
4728 // count anyone except the standalone server (if applicable)
4729 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
4733 // highlight him if he's the host
4734 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4735 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4736 gr_set_color_fast(&Color_text_active_hi);
4738 gr_set_color_fast(&Color_bright);
4741 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4742 gr_set_color_fast(&Color_text_active);
4744 gr_set_color_fast(&Color_text_normal);
4748 // optionally draw his CD status
4749 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4750 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4751 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4753 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4756 // make sure the string will fit, then display it
4757 SDL_strlcpy(str, Net_players[idx].player->callsign, SDL_arraysize(str));
4758 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4759 SDL_strlcat(str, XSTR("(O)",787), SDL_arraysize(str)); // [[ Observer ]]
4761 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4762 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4769 void multi_create_plist_blit_team()
4772 char str[CALLSIGN_LEN+1];
4773 int y_start = Mc_players_coords[gr_screen.res][MC_Y_COORD];
4776 // display all the red players first
4777 for(idx=0;idx<MAX_PLAYERS;idx++){
4778 // count anyone except the standalone server (if applicable)
4779 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
4780 // reset total offset
4783 // highlight him if he's the host
4784 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4785 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4786 gr_set_color_fast(&Color_text_active_hi);
4788 // be sure to blit the correct team button
4789 Multi_create_buttons[gr_screen.res][MC_TEAM0].button.draw_forced(2);
4791 gr_set_color_fast(&Color_bright);
4794 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4795 gr_set_color_fast(&Color_text_active);
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_text_normal);
4804 // optionally draw his CD status
4805 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4806 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4807 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4809 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4812 // blit the red team indicator
4813 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
4814 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
4815 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT], 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-2);
4818 total_offset += Multi_common_icon_dims[MICON_TEAM0_SELECT][0] + 1;
4821 if(Multi_common_icons[MICON_TEAM0] != -1){
4822 gr_set_bitmap(Multi_common_icons[MICON_TEAM0], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4823 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4825 total_offset += Multi_common_icon_dims[MICON_TEAM0][0] + 1;
4829 // make sure the string will fit
4830 SDL_strlcpy(str, Net_players[idx].player->callsign, SDL_arraysize(str));
4831 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4832 SDL_strlcat(str, XSTR("(O)",787), SDL_arraysize(str));
4834 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4836 // display him in the correct half of the list depending on his team
4837 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4842 // display all the green players next
4843 for(idx=0;idx<MAX_PLAYERS;idx++){
4844 // count anyone except the standalone server (if applicable)
4845 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
4846 // reset total offset
4849 // highlight him if he's the host
4850 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
4851 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4852 gr_set_color_fast(&Color_text_active_hi);
4854 // be sure to blit the correct team button
4855 Multi_create_buttons[gr_screen.res][MC_TEAM1].button.draw_forced(2);
4857 gr_set_color_fast(&Color_bright);
4860 if(Multi_create_plist_select_id == Net_players[idx].player_id){
4861 gr_set_color_fast(&Color_text_active);
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_text_normal);
4870 // optionally draw his CD status
4871 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
4872 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4873 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start - 1);
4875 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
4878 // blit the red team indicator
4879 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
4880 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
4881 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT], 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-2);
4884 total_offset += Multi_common_icon_dims[MICON_TEAM1_SELECT][0] + 1;
4887 if(Multi_common_icons[MICON_TEAM1] != -1){
4888 gr_set_bitmap(Multi_common_icons[MICON_TEAM1], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
4889 gr_bitmap(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset, y_start-2);
4891 total_offset += Multi_common_icon_dims[MICON_TEAM1][0] + 1;
4895 // make sure the string will fit
4896 SDL_strlcpy(str, Net_players[idx].player->callsign, SDL_arraysize(str));
4897 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
4898 SDL_strlcat(str, XSTR("(O)",787), SDL_arraysize(str));
4900 gr_force_fit_string(str,CALLSIGN_LEN,Mc_players_coords[gr_screen.res][MC_W_COORD] - total_offset);
4902 // display him in the correct half of the list depending on his team
4903 gr_string(Mc_players_coords[gr_screen.res][MC_X_COORD] + total_offset,y_start,str);
4909 void multi_create_list_scroll_up()
4911 if(Multi_create_list_start > 0){
4912 Multi_create_list_start--;
4914 gamesnd_play_iface(SND_SCROLL);
4916 gamesnd_play_iface(SND_GENERAL_FAIL);
4920 void multi_create_list_scroll_down()
4922 if((Multi_create_list_count - Multi_create_list_start) > Multi_create_list_max_display[gr_screen.res]){
4923 Multi_create_list_start++;
4925 gamesnd_play_iface(SND_SCROLL);
4927 gamesnd_play_iface(SND_GENERAL_FAIL);
4931 // gets a list of multiplayer misisons
4932 void multi_create_list_load_missions()
4934 char *fname, mission_name[NAME_LENGTH+1];
4938 SDL_snprintf(wild_card, SDL_arraysize(wild_card), "*%s", FS_MISSION_FILE_EXT);
4939 file_count = cf_get_file_list_preallocated(MULTI_CREATE_MAX_LIST_ITEMS, Multi_create_files_array, NULL, CF_TYPE_MISSIONS, wild_card);
4940 Multi_create_mission_count = 0;
4942 // maybe create a standalone dialog
4943 if(Game_mode & GM_STANDALONE_SERVER){
4944 std_create_gen_dialog("Loading missions");
4945 std_gen_set_text("Mission:", 1);
4948 for(idx = 0; idx < file_count; idx++){
4949 int flags,max_players;
4953 fname = Multi_create_files_array[idx];
4955 // tack on any necessary file extension
4956 filename = cf_add_ext( fname, FS_MISSION_FILE_EXT );
4958 // for multiplayer beta builds, only accept builtin missions
4959 #if defined(MULTIPLAYER_BETA_BUILD) || defined(FS2_DEMO)
4960 if(game_find_builtin_mission(filename) == NULL){
4963 #elif defined(PD_BUILD)
4964 if((game_find_builtin_mission(filename) == NULL) && !strstr(filename, "peterdrake")){
4969 if(Game_mode & GM_STANDALONE_SERVER){
4970 std_gen_set_text(filename, 2);
4973 flags = mission_parse_is_multi(filename, mission_name);
4975 // if the mission is a multiplayer mission, then add it to the mission list
4977 max_players = mission_parse_get_multi_mission_info( filename );
4978 m_respawn = The_mission.num_respawns;
4980 if ( Multi_create_mission_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
4981 multi_create_info *mcip;
4983 mcip = &Multi_create_mission_list[Multi_create_mission_count];
4984 SDL_strlcpy(mcip->filename, filename, SDL_arraysize(mcip->filename));
4985 SDL_strlcpy(mcip->name, mission_name, SDL_arraysize(mcip->name));
4986 mcip->flags = flags;
4987 mcip->respawn = m_respawn;
4988 mcip->max_players = (ubyte)max_players;
4990 // get any additional information for possibly builtin missions
4991 fs_builtin_mission *fb = game_find_builtin_mission(filename);
4995 Multi_create_mission_count++;
5001 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);
5004 // maybe create a standalone dialog
5005 if(Game_mode & GM_STANDALONE_SERVER){
5006 std_destroy_gen_dialog();
5010 void multi_create_list_load_campaigns()
5013 int idx, file_count;
5014 int campaign_type,max_players;
5018 // maybe create a standalone dialog
5019 if(Game_mode & GM_STANDALONE_SERVER){
5020 std_create_gen_dialog("Loading campaigns");
5021 std_gen_set_text("Campaign:", 1);
5024 Multi_create_campaign_count = 0;
5025 SDL_snprintf(wild_card, SDL_arraysize(wild_card), "*%s", FS_CAMPAIGN_FILE_EXT);
5026 file_count = cf_get_file_list_preallocated(MULTI_CREATE_MAX_LIST_ITEMS, Multi_create_files_array, NULL, CF_TYPE_MISSIONS, wild_card);
5027 for(idx = 0; idx < file_count; idx++){
5029 char *filename, name[NAME_LENGTH];
5031 fname = Multi_create_files_array[idx];
5033 // tack on any necessary file extension
5034 filename = cf_add_ext( fname, FS_CAMPAIGN_FILE_EXT );
5036 // for multiplayer beta builds, only accept builtin missions
5037 #if defined(MULTIPLAYER_BETA_BUILD) || defined(FS2_DEMO)
5038 if(game_find_builtin_mission(filename) == NULL){
5041 #elif defined(PD_BUILD)
5042 if((game_find_builtin_mission(filename) == NULL) && !strstr(filename, "peterdrake")){
5047 if(Game_mode & GM_STANDALONE_SERVER){
5048 std_gen_set_text(filename, 2);
5051 // if the campaign is a multiplayer campaign, then add the data to the campaign list items
5052 flags = mission_campaign_parse_is_multi( filename, name, SDL_arraysize(name) );
5053 if( flags != CAMPAIGN_TYPE_SINGLE && mission_campaign_get_info(filename,title,&campaign_type,&max_players)) {
5054 if ( Multi_create_campaign_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
5055 multi_create_info *mcip;
5057 mcip = &Multi_create_campaign_list[Multi_create_campaign_count];
5058 SDL_strlcpy(mcip->filename, filename, SDL_arraysize(mcip->filename));
5059 SDL_strlcpy(mcip->name, name, SDL_arraysize(mcip->name));
5061 // setup various flags
5062 if ( flags == CAMPAIGN_TYPE_MULTI_COOP ){
5063 mcip->flags = MISSION_TYPE_MULTI_COOP | MISSION_TYPE_MULTI;
5064 } else if ( flags == CAMPAIGN_TYPE_MULTI_TEAMS ) {
5065 mcip->flags = MISSION_TYPE_MULTI_TEAMS | MISSION_TYPE_MULTI;
5067 Int3(); // bogus campaign multi type -- find allender
5070 // 0 respawns for campaign files (should be contained within the mission files themselves)
5073 // 0 max players for campaign files
5074 mcip->max_players = (unsigned char)max_players;
5076 // get any additional information for possibly builtin missions
5077 fs_builtin_mission *fb = game_find_builtin_mission(filename);
5081 Multi_create_campaign_count++;
5086 // maybe create a standalone dialog
5087 if(Game_mode & GM_STANDALONE_SERVER){
5088 std_destroy_gen_dialog();
5092 void multi_create_list_do()
5095 int start_index,stop_index;
5096 char selected_name[255];
5098 // bail early if there aren't any selectable items
5099 if(Multi_create_list_count == 0){
5103 // first check to see if the user has clicked on an item
5104 if(Multi_create_list_select_button.pressed()){
5106 Multi_create_list_select_button.get_mouse_pos(NULL,&y);
5109 // make sure we are selectedin valid indices
5110 if((item < Multi_create_list_max_display[gr_screen.res]) && (item >= 0)){
5111 item += Multi_create_list_start;
5113 if(item < Multi_create_list_count){
5114 multi_create_list_select_item(item);
5115 gamesnd_play_iface(SND_IFACE_MOUSE_CLICK);
5120 // bail early if we don't have a start position
5121 if(Multi_create_list_start == -1){
5125 // display the list of individual campaigns/missions
5127 int y_start = Mc_list_coords[gr_screen.res][MC_Y_COORD];
5129 start_index = multi_create_select_to_index(Multi_create_list_start);
5130 stop_index = Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ? Multi_create_mission_count : Multi_create_campaign_count;
5131 for(idx=start_index; idx<stop_index; idx++){
5132 // see if we should drop out
5133 if(count == Multi_create_list_max_display[gr_screen.res]){
5137 // see if we should filter out this mission
5138 if ( !(Multi_create_file_list[idx].flags & Multi_create_filter) ){
5142 // highlight the selected item
5143 multi_create_select_to_filename(Multi_create_list_select, selected_name, SDL_arraysize(selected_name));
5144 if(!strcmp(selected_name,Multi_create_file_list[idx].filename)){
5145 gr_set_color_fast(&Color_text_selected);
5147 gr_set_color_fast(&Color_text_normal);
5150 // draw the type icon
5151 multi_create_list_blit_icons(idx, y_start);
5153 // force fit the mission name string
5154 SDL_strlcpy(selected_name, Multi_create_file_list[idx].name, SDL_arraysize(selected_name));
5155 gr_force_fit_string(selected_name,255,Mc_column1_w[gr_screen.res]);
5156 gr_string(Mc_mission_name_x[gr_screen.res],y_start,selected_name);
5158 // draw the max players if in mission mode
5159 SDL_snprintf(selected_name,SDL_arraysize(selected_name),"%d",(int)Multi_create_file_list[idx].max_players);
5160 gr_string(Mc_mission_count_x[gr_screen.res],y_start,selected_name);
5162 // force fit the mission filename string
5163 SDL_strlcpy(selected_name, Multi_create_file_list[idx].filename, SDL_arraysize(selected_name));
5164 gr_force_fit_string(selected_name,255,Mc_column3_w[gr_screen.res]);
5165 gr_string(Mc_mission_fname_x[gr_screen.res],y_start,selected_name);
5172 // takes care of stuff like changing indices around and setting up the netgame structure
5173 void multi_create_list_select_item(int n)
5175 int abs_index,campaign_type,max_players;
5176 char title[NAME_LENGTH+1];
5177 netgame_info ng_temp;
5180 char *campaign_desc;
5182 // if not on the standalone server
5183 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5186 // on the standalone
5188 memset(&ng_temp,0,sizeof(netgame_info));
5192 if ( n != Multi_create_list_select ) {
5193 // check to see if this is a valid index, and bail if it is not
5194 abs_index = multi_create_select_to_index(n);
5195 if(abs_index == -1){
5199 Multi_create_list_select = n;
5201 // set the mission name
5202 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5203 multi_create_select_to_filename(n, ng->mission_name, SDL_arraysize(ng->mission_name));
5205 multi_create_select_to_filename(n, ng->campaign_name, SDL_arraysize(ng->campaign_name));
5208 // make sure the netgame type is properly set
5209 int old_type = Netgame.type_flags;
5210 abs_index = multi_create_select_to_index(n);
5211 if(abs_index != -1){
5212 if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_TEAMS){
5213 // if we're in squad war mode, leave it as squad war
5214 if(old_type & NG_TYPE_SW){
5215 ng->type_flags = NG_TYPE_SW;
5217 ng->type_flags = NG_TYPE_TVT;
5219 } else if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_COOP){
5220 ng->type_flags = NG_TYPE_COOP;
5221 } else if(Multi_create_file_list[abs_index].flags & MISSION_TYPE_MULTI_DOGFIGHT){
5222 ng->type_flags = NG_TYPE_DOGFIGHT;
5226 // if we're no longer in a TvT game, just uncheck the squadwar checkbox
5227 if(!(ng->type_flags & NG_TYPE_TEAM)){
5228 Multi_create_sw_checkbox.set_state(0);
5231 // if we switched from something else to team vs. team mode, do some special processing
5232 if((ng->type_flags & NG_TYPE_TEAM) && (ng->type_flags != old_type) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
5236 switch(Multi_create_list_mode){
5237 case MULTI_CREATE_SHOW_MISSIONS:
5238 // don't forget to update the info box window thingie
5239 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5240 ship_init(); // mwa -- 10/15/97. Call this function to reset number of ships in mission
5241 ng->max_players = mission_parse_get_multi_mission_info( ng->mission_name );
5243 SDL_assert(ng->max_players > 0);
5244 SDL_strlcpy(ng->title, The_mission.name, SDL_arraysize(ng->title));
5246 // set the information area text
5247 multi_common_set_text(The_mission.mission_desc);
5249 // if we're on the standalone, send a request for the description
5251 send_netgame_descript_packet(&Netgame.server_addr,0);
5252 multi_common_set_text("");
5255 // set the respawns as appropriate
5256 if(Netgame.options.respawn <= Multi_create_file_list[abs_index].respawn){
5257 ng->respawn = Netgame.options.respawn;
5258 nprintf(("Network","Using netgame options for respawn count (%d %d)\n",Netgame.options.respawn,Multi_create_file_list[abs_index].respawn));
5260 ng->respawn = Multi_create_file_list[abs_index].respawn;
5261 nprintf(("Network","Using mission settings for respawn count (%d %d)\n",Netgame.options.respawn,Multi_create_file_list[abs_index].respawn));
5264 case MULTI_CREATE_SHOW_CAMPAIGNS:
5265 // if not on the standalone server
5266 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5267 // get the campaign info
5268 memset(title,0,NAME_LENGTH+1);
5269 if(!mission_campaign_get_info(ng->campaign_name,title,&campaign_type,&max_players, &campaign_desc)) {
5270 memset(ng->campaign_name,0,NAME_LENGTH+1);
5271 ng->max_players = 0;
5273 // if we successfully got the # of players
5275 memset(ng->title,0,NAME_LENGTH+1);
5276 SDL_strlcpy(ng->title, title, SDL_arraysize(ng->title));
5277 ng->max_players = max_players;
5280 nprintf(("Network","MC MAX PLAYERS : %d\n",ng->max_players));
5282 // set the information area text
5283 // multi_common_set_text(ng->title);
5284 multi_common_set_text(campaign_desc);
5286 // if on the standalone server, send a request for the description
5288 // no descriptions currently kept for campaigns
5291 // netgame respawns are always 0 for campaigns (until the first mission is loaded)
5296 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5298 send_netgame_update_packet();
5300 // update all machines about stuff like respawns, etc.
5301 multi_options_update_netgame();
5303 multi_options_update_mission(ng, Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ? 1 : 0);
5308 void multi_create_list_blit_icons(int list_index, int y_start)
5310 multi_create_info *mcip;
5311 fs_builtin_mission *fb;
5314 // get a pointer to the list item
5315 max_index = Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS ? Multi_create_mission_count - 1 : Multi_create_campaign_count - 1;
5316 if((list_index < 0) || (list_index > max_index)){
5319 mcip = &Multi_create_file_list[list_index];
5321 // blit the multiplayer type icons
5322 if(mcip->flags & MISSION_TYPE_MULTI_COOP){
5323 if(Multi_common_icons[MICON_COOP] >= 0){
5324 gr_set_bitmap(Multi_common_icons[MICON_COOP], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5325 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5327 } else if(mcip->flags & MISSION_TYPE_MULTI_TEAMS){
5328 if(Multi_common_icons[MICON_TVT] >= 0){
5329 gr_set_bitmap(Multi_common_icons[MICON_TVT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5330 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5332 } else if(mcip->flags & MISSION_TYPE_MULTI_DOGFIGHT){
5333 if(Multi_common_icons[MICON_DOGFIGHT] >= 0){
5334 gr_set_bitmap(Multi_common_icons[MICON_DOGFIGHT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5335 gr_bitmap(Mc_icon_type_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_type_coords[gr_screen.res][MC_Y_COORD]);
5339 // if its a valid mission, blit the valid mission icon
5340 if(MULTI_IS_TRACKER_GAME && (mcip->valid_status == MVALID_STATUS_VALID)){
5341 if(Multi_common_icons[MICON_VALID] >= 0){
5342 gr_set_bitmap(Multi_common_icons[MICON_VALID], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5343 gr_bitmap(Mc_icon_valid_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_valid_coords[gr_screen.res][MC_Y_COORD]);
5347 // now see if its a builtin mission
5348 fb = game_find_builtin_mission(mcip->filename);
5349 // if the mission is from volition, blit the volition icon
5350 if((fb != NULL) && (fb->flags & FSB_FROM_VOLITION)){
5351 if(Multi_common_icons[MICON_VOLITION] >= 0){
5352 gr_set_bitmap(Multi_common_icons[MICON_VOLITION], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5353 gr_bitmap(Mc_icon_volition_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_volition_coords[gr_screen.res][MC_Y_COORD]);
5358 // check for mdisk mission
5359 fb = game_find_builtin_mission(mcip->filename);
5360 if((fb != NULL) && (fb->flags & FSB_FROM_MDISK)){
5361 if(Multi_common_icons[MICON_MDISK] >= 0){
5362 gr_set_bitmap(Multi_common_icons[MICON_MDISK], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
5363 gr_bitmap(Mc_icon_silent_coords[gr_screen.res][MC_X_COORD],y_start + Mc_icon_silent_coords[gr_screen.res][MC_Y_COORD]);
5369 void multi_create_accept_hit()
5371 char selected_name[255];
5372 int start_campaign = 0;
5374 // make sure all players have finished joining
5375 if(!multi_netplayer_state_check(NETPLAYER_STATE_JOINED,1)){
5376 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("Please wait until all clients have finished joining",788));
5377 gamesnd_play_iface(SND_GENERAL_FAIL);
5380 gamesnd_play_iface(SND_COMMIT_PRESSED);
5383 // do single mission stuff
5384 switch(Multi_create_list_mode){
5385 case MULTI_CREATE_SHOW_MISSIONS:
5386 if(Multi_create_list_select != -1){
5387 // set the netgame mode
5388 Netgame.campaign_mode = MP_SINGLE;
5390 // setup various filenames and mission names
5391 multi_create_select_to_filename(Multi_create_list_select, selected_name, SDL_arraysize(selected_name));
5392 SDL_strlcpy( Game_current_mission_filename, selected_name, MAX_FILENAME_LEN );
5393 SDL_strlcpy( Netgame.mission_name, selected_name, MAX_FILENAME_LEN );
5396 ml_printf(NOX("Starting single mission %s, with %d players"), Game_current_mission_filename, multi_num_players());
5398 multi_common_add_notify(XSTR("No mission selected!",789));
5403 case MULTI_CREATE_SHOW_CAMPAIGNS:
5404 // do campaign related stuff
5405 if(Multi_create_list_select != -1){
5406 // set the netgame mode
5407 Netgame.campaign_mode = MP_CAMPAIGN;
5409 // start a campaign instead of a single mission
5410 multi_create_select_to_filename(Multi_create_list_select, selected_name, SDL_arraysize(selected_name));
5411 multi_campaign_start(selected_name);
5415 ml_printf(NOX("Starting campaign %s, with %d players"), selected_name, multi_num_players());
5417 multi_common_add_notify(XSTR("No campaign selected!",790));
5423 // if this is a team vs team situation, lock the players send a final team update
5424 if((Netgame.type_flags & NG_TYPE_TEAM) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
5425 multi_team_host_lock_all();
5426 multi_team_send_update();
5429 // if not on the standalone, move to the mission sync state which will take care of everything
5430 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5431 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
5432 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
5434 // otherwise tell the standalone to do so
5436 // when the standalone receives this, he'll do the mission syncing himself
5437 send_mission_sync_packet(MULTI_SYNC_PRE_BRIEFING,start_campaign);
5441 void multi_create_draw_filter_buttons()
5443 // highlight the correct filter button
5444 if ( Multi_create_filter == MISSION_TYPE_MULTI ){
5445 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL].button.draw_forced(2);
5446 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_COOP ) {
5447 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 1].button.draw_forced(2);
5448 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_TEAMS ) {
5449 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 2].button.draw_forced(2);
5450 } else if ( Multi_create_filter == MISSION_TYPE_MULTI_DOGFIGHT ){
5451 Multi_create_buttons[gr_screen.res][MC_SHOW_ALL + 3].button.draw_forced(2);
5457 void multi_create_set_selected_team(int team)
5461 // if we don't currently have a player selected, don't do anything
5462 if(!Multi_create_plist_select_flag){
5463 gamesnd_play_iface(SND_GENERAL_FAIL);
5466 gamesnd_play_iface(SND_USER_SELECT);
5468 // otherwise attempt to set the team for this guy
5469 player_index = find_player_id(Multi_create_plist_select_id);
5470 if(player_index != -1){
5471 multi_team_set_team(&Net_players[player_index],team);
5475 void multi_create_handle_join(net_player *pl)
5477 // for now just play a bloop sound
5478 gamesnd_play_iface(SND_ICON_DROP_ON_WING);
5481 // fill in net address of player the mouse is over, return player index (or -1 if none)
5482 short multi_create_get_mouse_id()
5484 // determine where he clicked (y pixel value)
5486 Multi_create_player_select_button.get_mouse_pos(NULL,&y);
5488 // select things a little differently if we're in team vs. team or non-team vs. team mode
5490 if(Netgame.type_flags & NG_TYPE_TEAM){
5491 int player_index = -1;
5493 // look through all of team red first
5494 for(idx=0;idx<MAX_PLAYERS;idx++){
5495 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
5498 // if this is the _nth_ guy
5506 // if we still haven't found him yet, look through the green team
5507 if(player_index == -1){
5508 for(idx=0;idx<MAX_PLAYERS;idx++){
5509 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
5511 // if this is the _nth_ guy
5520 if(player_index != -1){
5521 return Net_players[player_index].player_id;
5524 // select the nth active player if possible, disregarding the standalone server
5525 for(idx=0;idx<MAX_PLAYERS;idx++){
5526 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
5529 // if this is the _nth_ guy
5531 return Net_players[idx].player_id;
5540 void multi_create_select_to_filename(int select_index, char *filename, const int max_filelen)
5544 // look through the mission list
5545 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5546 for(idx=0;idx<Multi_create_mission_count;idx++){
5547 if(Multi_create_file_list[idx].flags & Multi_create_filter){
5551 // if we found the item
5552 if(select_index < 0){
5553 SDL_strlcpy(filename, Multi_create_file_list[idx].filename, max_filelen);
5558 // look through the campaign list
5559 else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
5560 for(idx=0;idx<Multi_create_campaign_count;idx++){
5563 // if we found the item
5564 if(select_index < 0){
5565 SDL_strlcpy(filename, Multi_create_file_list[idx].filename, max_filelen);
5571 SDL_strlcpy(filename, "", max_filelen);
5574 int multi_create_select_to_index(int select_index)
5577 int lookup_index = 0;
5579 // look through the mission list
5580 if(Multi_create_list_mode == MULTI_CREATE_SHOW_MISSIONS){
5581 for(idx=0;idx<Multi_create_mission_count;idx++){
5582 if(Multi_create_file_list[idx].flags & Multi_create_filter){
5586 // if we found the item
5587 if(select_index < lookup_index){
5592 // look through the campaign list
5593 else if(Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS){
5594 for(idx=0;idx<Multi_create_campaign_count;idx++){
5597 // if we found the item
5598 if(select_index < 0){
5607 int multi_create_ok_to_commit()
5609 int player_count, observer_count, idx;
5610 int notify_of_hacked_ships_tbl = 0;
5611 int notify_of_hacked_weapons_tbl = 0;
5612 char err_string[255];
5616 // make sure we have a valid mission selected
5617 if(Multi_create_list_select < 0){
5621 // if this is not a valid mission, let the player know
5622 abs_index = multi_create_select_to_index(Multi_create_list_select);
5627 // if we're playing with a hacked ships.tbl (on PXO)
5628 notify_of_hacked_ships_tbl = 0;
5629 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5630 if(!Game_ships_tbl_valid){
5631 notify_of_hacked_ships_tbl = 1;
5634 if(Netgame.flags & NG_FLAG_HACKED_SHIPS_TBL){
5635 notify_of_hacked_ships_tbl = 1;
5638 if(!MULTI_IS_TRACKER_GAME){
5639 notify_of_hacked_ships_tbl = 0;
5641 if(notify_of_hacked_ships_tbl){
5642 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){
5647 // if we're playing with a hacked weapons.tbl (on PXO)
5648 notify_of_hacked_weapons_tbl = 0;
5649 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5650 if(!Game_weapons_tbl_valid){
5651 notify_of_hacked_weapons_tbl = 1;
5654 if(Netgame.flags & NG_FLAG_HACKED_WEAPONS_TBL){
5655 notify_of_hacked_weapons_tbl = 1;
5658 if(!MULTI_IS_TRACKER_GAME){
5659 notify_of_hacked_weapons_tbl = 0;
5661 if(notify_of_hacked_weapons_tbl){
5662 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){
5667 // if any of the players have hacked data
5669 for(idx=0; idx<MAX_PLAYERS; idx++){
5670 // look for hacked players
5671 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAXOR)){
5675 // message everyone - haha
5676 if(Net_players[idx].player != NULL){
5677 SDL_snprintf(err_string, SDL_arraysize(err_string), "%s %s", Net_players[idx].player->callsign, XSTR("has hacked tables/data", 1271));
5679 SDL_snprintf(err_string, SDL_arraysize(err_string), "somebody %s", XSTR("has hacked tables/data", 1271));
5681 send_game_chat_packet(Net_player, err_string, MULTI_MSG_ALL, NULL, NULL, 1);
5684 // if we found a hacked set of data
5687 if(MULTI_IS_TRACKER_GAME){
5688 // don't allow squad war matches to continue
5689 if(Netgame.type_flags & NG_TYPE_SW){
5691 // if this is squad war, don't allow it to continue
5692 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));
5697 // otherwise, warn the players that stats will not saved
5699 // if this is squad war, don't allow it to continue
5700 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){
5705 // non-pxo, just give a notice
5707 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){
5713 // check to see that we don't have too many observers
5714 observer_count = multi_num_observers();
5715 if(observer_count > Netgame.options.max_observers){
5716 // print up the error string
5717 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);
5719 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, err_string);
5723 // check to see that we have a valid # of players for the the # of ships in the game
5724 player_count = multi_num_players();
5725 if(player_count > Netgame.max_players){
5726 // print up the error string
5727 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);
5729 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, err_string);
5733 // check to see if teams are assigned properly in a team vs. team situation
5734 if(Netgame.type_flags & NG_TYPE_TEAM){
5735 if(!multi_team_ok_to_commit()){
5736 gamesnd_play_iface(SND_GENERAL_FAIL);
5737 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("Teams and/or team captains are not assigned properly", 793));
5743 if(!multi_create_verify_cds()){
5744 gamesnd_play_iface(SND_GENERAL_FAIL);
5746 #ifdef MULTIPLAYER_BETA_BUILD
5747 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, "You need 1 CD for every player!");
5749 #ifdef DVD_MESSAGE_HACK
5750 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You need 1 DVD for every 4 players!", 794));
5752 popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("You need 1 CD for every 4 players!", 794));
5758 // if we're playing on the tracker
5759 if(MULTI_IS_TRACKER_GAME){
5760 #ifdef PXO_CHECK_VALID_MISSIONS
5761 if((Multi_create_file_list == Multi_create_mission_list) && (Multi_create_file_list[abs_index].valid_status != MVALID_STATUS_VALID)){
5762 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){
5769 if(!(Netgame.type_flags & NG_TYPE_SW)){
5770 // if he is playing by himself, tell him stats will not be accepted
5771 if(multi_num_players() == 1){
5772 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){
5785 int multi_create_verify_cds()
5787 int player_count = multi_num_players();
5791 // count how many cds we have
5793 for(idx=0;idx<MAX_PLAYERS;idx++){
5794 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_HAS_CD)){
5799 // for the beta, everyone must have a CD
5800 #ifdef MULTIPLAYER_BETA_BUILD
5801 if(multi_cd_count < player_count){
5805 // determine if we have enough
5806 float ratio = (float)player_count / (float)multi_cd_count;
5807 // greater than a 4 to 1 ratio
5813 // we meet the conditions
5817 // returns an index into Multi_create_mission_list
5818 int multi_create_lookup_mission(char *fname)
5822 for(idx=0; idx<Multi_create_mission_count; idx++){
5823 if(!SDL_strcasecmp(fname, Multi_create_mission_list[idx].filename)){
5828 // couldn't find the mission
5832 // returns an index into Multi_create_campaign_list
5833 int multi_create_lookup_campaign(char *fname)
5837 for(idx=0; idx<Multi_create_campaign_count; idx++){
5838 if(!SDL_strcasecmp(fname, Multi_create_campaign_list[idx].filename)){
5843 // couldn't find the campaign
5847 void multi_create_refresh_pxo()
5849 // delete mvalid.cfg if it exists
5850 cf_delete(MULTI_VALID_MISSION_FILE, CF_TYPE_DATA);
5852 // refresh missions from the tracker
5853 multi_update_valid_missions();
5856 void multi_create_sw_clicked()
5858 netgame_info ng_temp;
5861 int file_index = multi_create_select_to_index(Multi_create_list_select);
5863 // either a temporary netgame or the real one
5864 if(MULTIPLAYER_MASTER){
5871 // maybe switch squad war off
5872 if(!Multi_create_sw_checkbox.checked()){
5873 // if the mission selected is a coop mission, go back to coop mode
5874 SDL_assert(file_index != -1);
5875 if(file_index == -1){
5876 ng->type_flags = NG_TYPE_COOP;
5878 if(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_TEAMS){
5879 ng->type_flags = NG_TYPE_TVT;
5880 } else if(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_DOGFIGHT){
5881 ng->type_flags = NG_TYPE_DOGFIGHT;
5883 ng->type_flags = NG_TYPE_COOP;
5886 // switch squad war on
5888 SDL_assert(file_index != -1);
5889 if((file_index == -1) || !(Multi_create_file_list[file_index].flags & MISSION_TYPE_MULTI_TEAMS)){
5890 Multi_create_sw_checkbox.set_state(0);
5892 // at this point we know its safe to switch squad war on
5893 ng->type_flags = NG_TYPE_SW;
5898 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5900 send_netgame_update_packet();
5902 // update all machines about stuff like respawns, etc.
5903 multi_options_update_netgame();
5905 // on the standalone
5907 // standalone will take care of polling the usertracker
5908 multi_options_update_mission(ng, Multi_create_list_mode == MULTI_CREATE_SHOW_CAMPAIGNS ? 1 : 0);
5913 // -------------------------------------------------------------------------------------------------------------
5915 // MULTIPLAYER HOST OPTIONS SCREEN
5918 #define MULTI_HO_NUM_BUTTONS 12
5919 #define MULTI_HO_NUM_RADIO_BUTTONS 10
5923 #define MULTI_HO_PALETTE "InterfacePalette"
5925 static const char *Multi_ho_bitmap_fname[GR_NUM_RESOLUTIONS] = {
5926 "MultiHost", // GR_640
5927 "2_MultiHost" // GR_1024
5930 static const char *Multi_ho_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
5931 "MultiHost-M", // GR_640
5932 "2_MultiHost-M" // GR_1024
5936 UI_WINDOW Multi_ho_window; // the window object for the join screen
5937 UI_INPUTBOX Multi_ho_respawns; // the # of respawns allowed in the game
5938 UI_INPUTBOX Multi_ho_time_limit; // mission time limit
5939 UI_INPUTBOX Multi_ho_voice_wait; // wait time between tokens
5940 UI_INPUTBOX Multi_ho_kill_limit; // kill limit in a furball mission
5941 UI_INPUTBOX Multi_ho_obs; // # of observers we'll allow
5942 int Multi_ho_bitmap; // the background bitmap
5944 // constants for coordinate lookup
5945 #define MULTI_HO_X_COORD 0
5946 #define MULTI_HO_Y_COORD 1
5947 #define MULTI_HO_W_COORD 2
5948 #define MULTI_HO_H_COORD 3
5949 #define MULTI_HO_TEXT_X_COORD 4
5950 #define MULTI_HO_TEXT_Y_COORD 5
5953 #define MULTI_HO_MSG_RANK 0 // highest ranking players can do messaging
5954 #define MULTI_HO_MSG_LEADER 1 // wing/team leaders can do messaging
5955 #define MULTI_HO_MSG_ANY 2 // any player can do messaging
5956 #define MULTI_HO_MSG_HOST 3 // only the host can do messaging
5957 #define MULTI_HO_END_RANK 4 // highest rank can and host can end mission
5958 #define MULTI_HO_END_LEADER 5 // wing/team leaders and host can end the mission
5959 #define MULTI_HO_END_ANY 6 // any player can end the mission
5960 #define MULTI_HO_END_HOST 7 // only host can end the mission
5961 #define MULTI_HO_VOICE_ON 8 // voice toggled on
5962 #define MULTI_HO_VOICE_OFF 9 // voice toggled off
5963 #define MULTI_HO_HOST_MODIFIES 10 // only the host or team captains can modify ships/weapons in briefing
5964 #define MULTI_HO_ACCEPT 11 // accept button
5966 ui_button_info Multi_ho_buttons[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_BUTTONS] = {
5969 // who is allowed to message
5970 ui_button_info("MH_00", 13, 157, -1, -1, 0), // highest rank
5971 ui_button_info("MH_01", 13, 179, -1, -1, 1), // team/wing leader
5972 ui_button_info("MH_02", 13, 200, -1, -1, 2), // any
5973 ui_button_info("MH_03", 13, 223, -1, -1, 3), // host
5975 // who is allowed to end the mission
5976 ui_button_info("MH_09", 13, 273, -1, -1, 9), // highest rank
5977 ui_button_info("MH_10", 13, 295, -1, -1, 10), // team/wing leader
5978 ui_button_info("MH_11", 13, 317, -1, -1, 11), // any
5979 ui_button_info("MH_12", 13, 339, -1, -1, 12), // host
5981 // voice on/off button
5982 ui_button_info("MH_14", 396, 156, -1, -1, 14),
5983 ui_button_info("MH_15", 453, 156, -1, -1, 15),
5985 // host modifies ships
5986 ui_button_info("MH_19", 215, 410, -1, -1, 19),
5989 ui_button_info("MH_18", 560, 411, -1, -1, 18),
5991 // who is allowed to message
5992 ui_button_info("MH_00", 3, 160, 46, 166, 0), // highest rank
5993 ui_button_info("MH_01", 3, 179, 46, 185, 1), // team/wing leader
5994 ui_button_info("MH_02", 3, 196, 46, 203, 2), // any
5995 ui_button_info("MH_03", 3, 214, 46, 220, 3), // host
5997 // who is allowed to end the mission
5998 ui_button_info("MH_04", 3, 257, 46, 265, 4), // highest rank
5999 ui_button_info("MH_05", 3, 276, 46, 283, 5), // team/wing leader
6000 ui_button_info("MH_06", 3, 294, 46, 300, 6), // any
6001 ui_button_info("MH_07", 3, 311, 46, 317, 7), // host
6003 // voice on/off button
6004 ui_button_info("MH_09", 542, 158, 545, 185, 9),
6005 ui_button_info("MH_10", 598, 158, 604, 185, 10),
6007 // host modifies ships
6008 ui_button_info("MH_13", 542, 377, 437, 363, 13),
6011 ui_button_info("MH_14", 572, 428, 580, 414, 14),
6015 // who is allowed to message
6016 ui_button_info("2_MH_00", 5, 256, 73, 269, 0), // highest rank
6017 ui_button_info("2_MH_01", 5, 286, 73, 297, 1), // team/wing leader
6018 ui_button_info("2_MH_02", 5, 314, 73, 325, 2), // any
6019 ui_button_info("2_MH_03", 5, 341, 73, 352, 3), // host
6021 // who is allowed to end the mission
6022 ui_button_info("2_MH_04", 5, 412, 73, 425, 4), // highest rank
6023 ui_button_info("2_MH_05", 5, 442, 73, 452, 5), // team/wing leader
6024 ui_button_info("2_MH_06", 5, 470, 73, 480, 6), // any
6025 ui_button_info("2_MH_07", 5, 497, 73, 508, 7), // host
6027 // voice on/off button
6028 ui_button_info("2_MH_09", 867, 253, 872, 296, 9),
6029 ui_button_info("2_MH_10", 957, 253, 966, 296, 10),
6031 // host modifies ships
6032 ui_button_info("2_MH_13", 867, 603, 784, 581, 13),
6035 ui_button_info("2_MH_14", 916, 685, 925, 665, 14),
6038 UI_XSTR Multi_ho_text[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_BUTTONS] = {
6040 // not needed for FS1
6042 {"Highest rank", 1280, 46, 166, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_RANK].button},
6043 {"Team / wing-leader", 1281, 46, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_LEADER].button},
6044 {"Any", 1282, 46, 203, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_ANY].button},
6045 {"Host", 1283, 46, 220, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_MSG_HOST].button},
6046 {"Highest rank", 1280, 46, 265, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_RANK].button},
6047 {"Team / wing-leader", 1281, 46, 283, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_LEADER].button},
6048 {"Any", 1282, 46, 300, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_ANY].button},
6049 {"Host", 1283, 46, 317, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_END_HOST].button},
6050 {"On", 1285, 545, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_VOICE_ON].button},
6051 {"Off", 1286, 604, 185, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_VOICE_OFF].button},
6052 {"Host modifies ships", 1287, 437, 363, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[0][MULTI_HO_HOST_MODIFIES].button},
6053 {"Exit", 1417, 572, 418, UI_XSTR_COLOR_PINK, -1, &Multi_ho_buttons[0][MULTI_HO_ACCEPT].button},
6057 // not needed for FS1
6059 {"Highest rank", 1280, 62, 269, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_RANK].button},
6060 {"Team / wing-leader", 1281, 62, 297, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_LEADER].button},
6061 {"Any", 1282, 62, 325, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_ANY].button},
6062 {"Host", 1283, 62, 352, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_MSG_HOST].button},
6063 {"Highest rank", 1280, 62, 425, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_RANK].button},
6064 {"Team / wing-leader", 1281, 62, 452, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_LEADER].button},
6065 {"Any", 1282, 62, 480, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_ANY].button},
6066 {"Host", 1283, 62, 508, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_END_HOST].button},
6067 {"On", 1285, 877, 294, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_VOICE_ON].button},
6068 {"Off", 1286, 967, 293, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_VOICE_OFF].button},
6069 {"Host modifies ships", 1287, 869, 589, UI_XSTR_COLOR_GREEN, -1, &Multi_ho_buttons[1][MULTI_HO_HOST_MODIFIES].button},
6070 {"Exit", 1417, 953, 672, UI_XSTR_COLOR_PINK, -1, &Multi_ho_buttons[1][MULTI_HO_ACCEPT].button},
6075 // radio button controls
6076 #define MULTI_HO_NUM_RADIO_GROUPS 3
6077 #define MULTI_HO_MSG_GROUP 0 // group dealing with squadmate messaging
6078 #define MULTI_HO_END_GROUP 1 // group dealing with ending the mission
6079 #define MULTI_HO_VOICE_GROUP 2 // group dealing with voice stuff
6080 int Multi_ho_radio_groups[MULTI_HO_NUM_RADIO_GROUPS] = { // currently selected button in the radio button group
6083 int Multi_ho_radio_info[MULTI_HO_NUM_RADIO_BUTTONS][3] = { // info related to each of the radio buttons themselves
6084 // { group #, value, button id# }
6085 {0, 0, 0}, // highest ranking players can do messaging
6086 {0, 1, 1}, // wing/team leaders can do messaging
6087 {0, 2, 2}, // any player can do messaging
6088 {0, 3, 3}, // only host can do messaging
6089 {1, 0, 4}, // highest rank and host can end the mission
6090 {1, 1, 5}, // team/wing leader can end the mission
6091 {1, 2, 6}, // any player can end the mission
6092 {1, 3, 7}, // only the host can end the mission
6093 {2, 0, 8}, // voice toggled on
6094 {2, 1, 9} // voice toggled off
6098 #define MULTI_HO_NUM_SLIDERS 3
6099 #define MULTI_HO_SLIDER_VOICE_QOS 0 // voice quality of sound
6100 #define MULTI_HO_SLIDER_VOICE_DUR 1 // max duration of voice recording
6101 #define MULTI_HO_SLIDER_SKILL 2 // skill level
6103 const char *filename;
6108 UI_DOT_SLIDER_NEW slider; // because we have a class inside this struct, we need the constructor below..
6110 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){}
6112 ho_sliders Multi_ho_sliders[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_SLIDERS] = {
6115 ho_sliders("MH_16", 396, 210, -1, -1, 16, 19, 10), // voice qos
6116 ho_sliders("MH_17", 396, 263, -1, -1, 17, 19, 10), // voice duration
6117 ho_sliders("MH_13", 10, 387, -1, -1, 13, 36, 5), // skill level
6119 ho_sliders("MH_11", 428, 214, 437, 199, 11, 19, 10), // voice qos
6120 ho_sliders("MH_12", 428, 261, 437, 246, 12, 19, 10), // voice duration
6121 ho_sliders("MH_08", 237, 454, 230, 411, 8, 36, 5), // skill level
6125 ho_sliders("2_MH_11", 684, 343, 690, 323, 11, 32, 10), // voice qos
6126 ho_sliders("2_MH_12", 685, 418, 837, 468, 12, 32, 10), // voice duration
6127 ho_sliders("2_MH_08", 379, 727, 369, 663, 8, 60, 5), // skill level
6131 int Multi_ho_mission_respawn;
6133 int Multi_ho_host_modifies;
6135 // whether or not any of the inputboxes on this screen had focus last frame
6136 int Multi_ho_lastframe_input = 0;
6138 // game information text areas
6142 #define MULTI_HO_NUM_TITLES 14
6144 UI_XSTR Multi_ho_titles[GR_NUM_RESOLUTIONS][MULTI_HO_NUM_TITLES] = {
6146 { "AI Orders", 1289, 32, 144, UI_XSTR_COLOR_GREEN, -1, NULL },
6147 { "End Mission", 1290, 32, 242, UI_XSTR_COLOR_GREEN, -1, NULL },
6148 { "Time Limit", 1291, 32, 347, UI_XSTR_COLOR_GREEN, -1, NULL },
6149 { "Min", 1292, 74, 362, UI_XSTR_COLOR_GREEN, -1, NULL },
6150 { "Respawn Limit", 1288, 32, 378, UI_XSTR_COLOR_GREEN, -1, NULL },
6151 { "Kill Limit", 1293, 32, 409, UI_XSTR_COLOR_GREEN, -1, NULL },
6152 { "Observers", 1294, 32, 441, UI_XSTR_COLOR_GREEN, -1, NULL },
6153 { "Skill Level", 1284, 230, 411, UI_XSTR_COLOR_GREEN, -1, NULL },
6154 { "Voice Transmission", 1295, 437, 144, UI_XSTR_COLOR_GREEN, -1, NULL },
6155 { "Voice Quality", 1296, 437, 199, UI_XSTR_COLOR_GREEN, -1, NULL },
6156 { "Message Duration", 1297, 437, 246, UI_XSTR_COLOR_GREEN, -1, NULL },
6157 { "sec", 1522, 523, 292, UI_XSTR_COLOR_GREEN, -1, NULL },
6158 { "sec", 1523, 523, 332, UI_XSTR_COLOR_GREEN, -1, NULL },
6159 { "Voice Wait", 1298, 437, 313, UI_XSTR_COLOR_GREEN, -1, NULL },
6162 { "AI Orders", 1289, 48, 238, UI_XSTR_COLOR_GREEN, -1, NULL },
6163 { "End Mission", 1290, 48, 394, UI_XSTR_COLOR_GREEN, -1, NULL },
6164 { "Time Limit", 1291, 50, 568, UI_XSTR_COLOR_GREEN, -1, NULL },
6165 { "Min", 1292, 119, 581, UI_XSTR_COLOR_GREEN, -1, NULL },
6166 { "Respawn Limit", 1288, 50, 618, UI_XSTR_COLOR_GREEN, -1, NULL },
6167 { "Kill Limit", 1293, 50, 668, UI_XSTR_COLOR_GREEN, -1, NULL },
6168 { "Observers", 1294, 50, 718, UI_XSTR_COLOR_GREEN, -1, NULL },
6169 { "Skill Level", 1284, 398, 670, UI_XSTR_COLOR_GREEN, -1, NULL },
6170 { "Voice Transmission", 1295, 869, 239, UI_XSTR_COLOR_GREEN, -1, NULL },
6171 { "Voice Quality", 1296, 690, 331, UI_XSTR_COLOR_GREEN, -1, NULL },
6172 { "Message Duration", 1297, 690, 405, UI_XSTR_COLOR_GREEN, -1, NULL },
6173 { "sec", 1522, 837, 467, UI_XSTR_COLOR_GREEN, -1, NULL },
6174 { "sec", 1523, 837, 534, UI_XSTR_COLOR_GREEN, -1, NULL },
6175 { "Voice Wait", 1298, 742, 510, UI_XSTR_COLOR_GREEN, -1, NULL },
6180 // mission time limit input box
6181 int Ho_time_coords[GR_NUM_RESOLUTIONS][4] = {
6194 // furball kill limit input box
6195 int Ho_kill_coords[GR_NUM_RESOLUTIONS][4] = {
6208 // voice recording duration text display area
6209 int Ho_vd_coords[GR_NUM_RESOLUTIONS][4] = {
6222 // voice token wait input box
6223 int Ho_vw_coords[GR_NUM_RESOLUTIONS][6] = {
6236 // observer count input box
6237 int Ho_obs_coords[GR_NUM_RESOLUTIONS][4] = {
6250 // skill text description area
6251 int Ho_st_coords[GR_NUM_RESOLUTIONS][4] = {
6264 // respawn input box
6265 int Ho_rsp_coords[GR_NUM_RESOLUTIONS][6] = {
6278 // respawn max text area
6279 int Ho_max_rsp_coords[GR_NUM_RESOLUTIONS][2] = {
6292 // maximum values for various input boxes (to notify user of overruns)
6293 #define MULTI_HO_MAX_TIME_LIMIT 500
6294 #define MULTI_HO_MAX_TOKEN_WAIT 5
6295 #define MULTI_HO_MAX_KILL_LIMIT 9999
6296 #define MULTI_HO_MAX_OBS 4
6298 // LOCAL function definitions
6299 void multi_ho_check_buttons();
6300 void multi_ho_button_pressed(int n);
6301 void multi_ho_draw_radio_groups();
6302 void multi_ho_accept_hit();
6303 void multi_ho_get_options();
6304 void multi_ho_apply_options();
6305 void multi_ho_display_record_time();
6306 int multi_ho_check_values();
6307 void multi_ho_check_focus();
6308 void multi_ho_blit_max_respawns();
6309 void multi_ho_display_skill_level();
6311 void multi_host_options_init()
6315 // create the interface window
6316 Multi_ho_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
6317 Multi_ho_window.set_mask_bmap(Multi_ho_bitmap_mask_fname[gr_screen.res]);
6319 // load the background bitmap
6320 Multi_ho_bitmap = bm_load(Multi_ho_bitmap_fname[gr_screen.res]);
6321 if(Multi_ho_bitmap < 0){
6322 // we failed to load the bitmap - this is very bad
6326 // initialize the common notification messaging
6327 multi_common_notify_init();
6329 // use the common interface palette
6330 multi_common_set_palette();
6332 // create the interface buttons
6333 for(idx=0;idx<MULTI_HO_NUM_BUTTONS;idx++){
6334 // create the object
6335 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);
6337 // set the sound to play when highlighted
6338 Multi_ho_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
6340 // set the ani for the button
6341 Multi_ho_buttons[gr_screen.res][idx].button.set_bmaps(Multi_ho_buttons[gr_screen.res][idx].filename);
6343 // set the hotspot, ignoring the skill level button
6344 Multi_ho_buttons[gr_screen.res][idx].button.link_hotspot(Multi_ho_buttons[gr_screen.res][idx].hotspot);
6348 Multi_ho_window.add_XSTR(&Multi_ho_text[gr_screen.res][idx]);
6354 for(idx=0; idx<MULTI_HO_NUM_TITLES; idx++){
6355 Multi_ho_window.add_XSTR(&Multi_ho_titles[gr_screen.res][idx]);
6359 // create the interface sliders
6360 for(idx=0; idx<MULTI_HO_NUM_SLIDERS; idx++){
6361 // create the object
6362 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);
6365 // create the respawn count input box
6366 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);
6367 // if we're in campaign mode, disable it
6368 if(Netgame.campaign_mode == MP_CAMPAIGN){
6369 Multi_ho_respawns.set_text(XSTR("NA",795)); // [[ Not applicable ]]
6370 Multi_ho_respawns.disable();
6372 Multi_ho_respawns.set_valid_chars("0123456789");
6375 // create the time limit input box
6376 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);
6377 Multi_ho_time_limit.set_valid_chars("-0123456789");
6379 // create the voice token wait input box
6380 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);
6381 Multi_ho_voice_wait.set_valid_chars("01243456789");
6383 // create the furball kill limit input box
6384 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);
6385 Multi_ho_kill_limit.set_valid_chars("0123456789");
6387 // create the observer limit input box
6388 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);
6389 Multi_ho_obs.set_valid_chars("01234");
6391 // load in the current netgame defaults
6392 multi_ho_get_options();
6394 // whether or not any of the inputboxes on this screen had focus last frame
6395 Multi_ho_lastframe_input = 0;
6397 // get the # of respawns for the currently selected mission (if any)
6398 if(Multi_create_list_select != -1){
6399 int abs_index = multi_create_select_to_index(Multi_create_list_select);
6401 // if he has a valid mission selected
6403 Multi_ho_mission_respawn = (int)Multi_create_file_list[abs_index].respawn;
6405 Multi_ho_mission_respawn = -1;
6408 Multi_ho_mission_respawn = -1;
6412 void multi_ho_update_sliders()
6414 // game skill slider
6415 if (Game_skill_level != Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos) {
6416 if ( !(Netgame.type_flags & NG_TYPE_TEAM) ){
6417 Game_skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6418 gamesnd_play_iface(SND_USER_SELECT);
6420 Game_skill_level = NUM_SKILL_LEVELS / 2;
6424 // get the voice qos options
6425 if (Netgame.options.voice_qos != (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1)) {
6426 Netgame.options.voice_qos = (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1);
6427 gamesnd_play_iface(SND_USER_SELECT);
6430 // get the voice duration options
6431 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)) {
6432 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);
6433 gamesnd_play_iface(SND_USER_SELECT);
6438 void multi_host_options_do()
6443 k = Multi_ho_window.process();
6446 // process any keypresses
6449 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
6452 case KEY_CTRLED + SDLK_RETURN :
6453 gamesnd_play_iface(SND_COMMIT_PRESSED);
6454 multi_ho_accept_hit();
6458 // process any button clicks
6459 multi_ho_check_buttons();
6461 // update the sliders
6462 multi_ho_update_sliders();
6464 // make sure that the chatbox inputbox and any inputbox on this screen are mutually exclusive in terms of focus
6465 multi_ho_check_focus();
6467 // draw the background, etc
6469 GR_MAYBE_CLEAR_RES(Multi_ho_bitmap);
6470 if(Multi_ho_bitmap != -1){
6471 gr_set_bitmap(Multi_ho_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
6474 Multi_ho_window.draw();
6476 // draw all the radio buttons properly
6477 multi_ho_draw_radio_groups();
6479 // display any pending notification messages
6480 multi_common_notify_do();
6482 // display the voice record time settings
6483 multi_ho_display_record_time();
6485 // maybe display the max # of respawns next to the respawn input box
6486 multi_ho_blit_max_respawns();
6488 // blit the proper skill level
6489 multi_ho_display_skill_level();
6491 // blit the "host modifies button"
6492 if(Multi_ho_host_modifies){
6493 Multi_ho_buttons[gr_screen.res][MULTI_HO_HOST_MODIFIES].button.draw_forced(2);
6496 // process and show the chatbox thingie
6500 Multi_ho_window.draw_tooltip();
6502 // display the voice status indicator
6503 multi_common_voice_display_status();
6509 void multi_host_options_close()
6511 // unload any bitmaps
6512 if(!bm_unload(Multi_ho_bitmap)){
6513 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_ho_bitmap_fname[gr_screen.res]));
6516 // destroy the UI_WINDOW
6517 Multi_ho_window.destroy();
6520 void multi_ho_check_buttons()
6523 for(idx=0;idx<MULTI_HO_NUM_BUTTONS;idx++){
6524 // we only really need to check for one button pressed at a time, so we can break after
6526 if(Multi_ho_buttons[gr_screen.res][idx].button.pressed()){
6527 multi_ho_button_pressed(idx);
6533 void multi_ho_button_pressed(int n)
6535 int radio_index,idx;
6536 int x_pixel,y_pixel;
6538 // get the pixel position of the click
6539 Multi_ho_buttons[gr_screen.res][n].button.get_mouse_pos(&x_pixel,&y_pixel);
6542 // clicked on the accept button
6543 case MULTI_HO_ACCEPT:
6544 gamesnd_play_iface(SND_COMMIT_PRESSED);
6545 multi_ho_accept_hit();
6548 // clicked on the host/captains only modify button
6549 case MULTI_HO_HOST_MODIFIES:
6550 // toggle it on or off
6551 Multi_ho_host_modifies = !Multi_ho_host_modifies;
6552 gamesnd_play_iface(SND_USER_SELECT);
6556 // look through the radio buttons and see which one this corresponds to
6558 for(idx=0;idx<MULTI_HO_NUM_RADIO_BUTTONS;idx++){
6559 if(Multi_ho_radio_info[idx][2] == n){
6564 SDL_assert(radio_index != -1);
6566 // check to see if a radio button was pressed
6567 if(radio_index < MULTI_HO_NUM_RADIO_BUTTONS){
6568 // see if this value is already picked for this radio group
6569 if(Multi_ho_radio_groups[Multi_ho_radio_info[radio_index][0]] != Multi_ho_radio_info[radio_index][1]){
6570 gamesnd_play_iface(SND_USER_SELECT);
6571 Multi_ho_radio_groups[Multi_ho_radio_info[radio_index][0]] = Multi_ho_radio_info[radio_index][1];
6573 gamesnd_play_iface(SND_GENERAL_FAIL);
6578 void multi_ho_draw_radio_groups()
6582 // go through each item and draw it if it is the selected button in its respective group
6583 for(idx=0;idx<MULTI_HO_NUM_RADIO_BUTTONS;idx++){
6584 /// if this button is the currently selected one in its group
6585 if(Multi_ho_radio_info[idx][1] == Multi_ho_radio_groups[Multi_ho_radio_info[idx][0]]){
6586 Multi_ho_buttons[gr_screen.res][Multi_ho_radio_info[idx][2]].button.draw_forced(2);
6591 void multi_ho_accept_hit()
6595 // check the values in the input boxes
6596 if(!multi_ho_check_values()){
6600 // zero out the netgame flags
6603 // set default options
6604 Netgame.options.flags = (MSO_FLAG_INGAME_XFER | MSO_FLAG_ACCEPT_PIX);
6606 // set the squadmate messaging flags
6607 switch(Multi_ho_radio_groups[MULTI_HO_MSG_GROUP]){
6609 Netgame.options.squad_set = MSO_SQUAD_RANK;
6612 Netgame.options.squad_set = MSO_SQUAD_LEADER;
6615 Netgame.options.squad_set = MSO_SQUAD_ANY;
6618 Netgame.options.squad_set = MSO_SQUAD_HOST;
6624 // set the end mission flags
6625 switch(Multi_ho_radio_groups[MULTI_HO_END_GROUP]){
6627 Netgame.options.endgame_set = MSO_END_RANK;
6630 Netgame.options.endgame_set = MSO_END_LEADER;
6633 Netgame.options.endgame_set = MSO_END_ANY;
6636 Netgame.options.endgame_set = MSO_END_HOST;
6642 // set the voice toggle
6643 switch(Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP]){
6645 Netgame.options.flags &= ~(MSO_FLAG_NO_VOICE);
6648 Netgame.options.flags |= MSO_FLAG_NO_VOICE;
6654 // get the voice qos options
6655 Netgame.options.voice_qos = (ubyte)(Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos + 1);
6657 // get the voice duration options
6658 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);
6660 // set the skill level. If in team vs. team mode, preserve the old setting before saving
6661 // the pilot file. I'll bet that this doesn't work though because the pilot file gets
6662 // written in a bunch of locations....sigh.
6663 if ( !(Netgame.type_flags & NG_TYPE_TEAM) ){
6664 Game_skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6666 Game_skill_level = NUM_SKILL_LEVELS / 2;
6669 // set the netgame respawn count
6670 // maybe warn the user that respawns will not be used for a campaign mission
6671 if(Netgame.campaign_mode == MP_SINGLE){
6672 Multi_ho_respawns.get_text(resp_str);
6673 uint temp_respawn = (uint)atoi(resp_str);
6674 // if he currently has no mission selected, let the user set any # of respawns
6675 if((int)temp_respawn > Multi_ho_mission_respawn){
6676 if(Multi_ho_mission_respawn == -1){
6677 Netgame.respawn = temp_respawn;
6678 Netgame.options.respawn = temp_respawn;
6680 // this should have been taken care of by the interface code
6685 Netgame.options.respawn = temp_respawn;
6686 Netgame.respawn = temp_respawn;
6690 // get the mission time limit
6691 Multi_ho_time_limit.get_text(resp_str);
6692 int temp_time = atoi(resp_str);
6694 Netgame.options.mission_time_limit = fl2f(-1.0f);
6695 } else if(temp_time > MULTI_HO_MAX_TIME_LIMIT){
6698 Netgame.options.mission_time_limit = fl2f(60.0f * (float)temp_time);
6701 // get observer count options
6702 Multi_ho_obs.get_text(resp_str);
6703 int temp_obs = atoi(resp_str);
6704 if(temp_obs > MULTI_HO_MAX_OBS){
6707 Netgame.options.max_observers = (ubyte)temp_obs;
6709 // get the furball kill limit
6710 Multi_ho_kill_limit.get_text(resp_str);
6711 int temp_kills = atoi(resp_str);
6712 if(temp_kills > MULTI_HO_MAX_KILL_LIMIT){
6715 Netgame.options.kill_limit = temp_kills;
6717 // get the token wait limit
6718 Multi_ho_voice_wait.get_text(resp_str);
6719 int temp_wait = atoi(resp_str);
6720 if(temp_wait > MULTI_HO_MAX_TOKEN_WAIT){
6723 Netgame.options.voice_token_wait = (temp_wait * 1000);
6725 // set the netgame option
6726 Netgame.options.skill_level = (ubyte)Game_skill_level;
6728 // get whether we're in host/captains only modify mode
6729 Netgame.options.flags &= ~(MSO_FLAG_SS_LEADERS);
6730 if(Multi_ho_host_modifies){
6731 Netgame.options.flags |= MSO_FLAG_SS_LEADERS;
6734 // store these values locally
6735 memcpy(&Player->m_local_options,&Net_player->p_info.options,sizeof(multi_local_options));
6736 memcpy(&Player->m_server_options,&Netgame.options,sizeof(multi_server_options));
6737 write_pilot_file(Player);
6739 // apply any changes in settings (notify everyone of voice qos changes, etc)
6740 multi_ho_apply_options();
6742 // move back to the create game screen
6743 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
6746 void multi_ho_get_options()
6750 // set the squadmate messaging buttons
6751 switch(Netgame.options.squad_set){
6752 case MSO_SQUAD_RANK :
6753 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 0;
6755 case MSO_SQUAD_LEADER:
6756 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 1;
6759 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 2;
6761 case MSO_SQUAD_HOST:
6762 Multi_ho_radio_groups[MULTI_HO_MSG_GROUP] = 3;
6768 // set the mission end buttons
6769 switch(Netgame.options.endgame_set){
6771 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 0;
6773 case MSO_END_LEADER:
6774 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 1;
6777 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 2;
6780 Multi_ho_radio_groups[MULTI_HO_END_GROUP] = 3;
6786 // set the voice toggle buttons
6787 if(Netgame.options.flags & MSO_FLAG_NO_VOICE){
6788 Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP] = 1;
6790 Multi_ho_radio_groups[MULTI_HO_VOICE_GROUP] = 0;
6793 // get the voice qos options
6794 SDL_assert((Netgame.options.voice_qos >= 1) && (Netgame.options.voice_qos <= 10));
6795 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_QOS].slider.pos = (Netgame.options.voice_qos - 1);
6797 // get the voice duration options
6798 SDL_assert((Netgame.options.voice_record_time > 0) && (Netgame.options.voice_record_time <= MULTI_VOICE_MAX_TIME));
6799 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos = ((int)((float)Netgame.options.voice_record_time / 500.0f)) - 1;
6801 // get the current skill level
6802 SDL_assert((Game_skill_level >= 0) && (Game_skill_level < NUM_SKILL_LEVELS));
6803 Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos = Game_skill_level;
6805 // get the # of observers
6806 memset(resp_str,0,10);
6807 SDL_snprintf(resp_str,SDL_arraysize(resp_str),"%d",Netgame.options.max_observers);
6808 Multi_ho_obs.set_text(resp_str);
6810 // set the respawn count
6811 if(Netgame.campaign_mode == MP_SINGLE){
6812 memset(resp_str,0,10);
6813 SDL_snprintf(resp_str,SDL_arraysize(resp_str),"%d",Netgame.respawn);
6814 Multi_ho_respawns.set_text(resp_str);
6817 // set the mission time limit
6818 memset(resp_str,0,10);
6819 float tl = f2fl(Netgame.options.mission_time_limit);
6820 SDL_snprintf(resp_str,SDL_arraysize(resp_str),"%d",(int)(tl / 60.0f));
6821 Multi_ho_time_limit.set_text(resp_str);
6823 // set the furball kill limit
6824 memset(resp_str,0,10);
6825 SDL_snprintf(resp_str,SDL_arraysize(resp_str),"%d",Netgame.options.kill_limit);
6826 Multi_ho_kill_limit.set_text(resp_str);
6828 // set the token wait time
6829 memset(resp_str,0,10);
6830 SDL_snprintf(resp_str,SDL_arraysize(resp_str),"%d",Netgame.options.voice_token_wait / 1000);
6831 Multi_ho_voice_wait.set_text(resp_str);
6833 // get whether we're in host/captains only modify mode
6834 if(Netgame.options.flags & MSO_FLAG_SS_LEADERS){
6835 Multi_ho_host_modifies = 1;
6837 Multi_ho_host_modifies = 0;
6841 void multi_ho_apply_options()
6843 // if the voice qos or duration has changed, apply the change
6844 multi_voice_maybe_update_vars(Netgame.options.voice_qos,Netgame.options.voice_record_time);
6846 // send an options update
6847 multi_options_update_netgame();
6850 // display the voice record time settings
6851 void multi_ho_display_record_time()
6854 int full_seconds, half_seconds;
6857 memset(time_str,0,30);
6860 full_seconds = (((Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 500) / 1000);
6862 // get the half-seconds
6863 half_seconds = ((((Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_VOICE_DUR].slider.pos + 1) * 500) % 1000) / 500) * 5;
6865 // format the string
6866 SDL_snprintf(time_str,SDL_arraysize(time_str),"%d.%d",full_seconds,half_seconds);
6867 gr_set_color_fast(&Color_bright);
6868 gr_string(Ho_vd_coords[gr_screen.res][MULTI_HO_X_COORD],Ho_vd_coords[gr_screen.res][MULTI_HO_Y_COORD],time_str);
6871 int multi_ho_check_values()
6875 memset(val_txt,0,255);
6877 // check against respawn settings
6878 if(Multi_ho_mission_respawn != -1){
6879 Multi_ho_respawns.get_text(val_txt);
6880 // if the value is invalid, let the user know
6881 if(atoi(val_txt) > Multi_ho_mission_respawn){
6882 memset(val_txt,0,255);
6883 SDL_snprintf(val_txt,SDL_arraysize(val_txt),XSTR("Warning\nRespawn count in greater than mission specified max (%d)",796),Multi_ho_mission_respawn);
6884 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6889 // check against mission time limit max
6890 Multi_ho_time_limit.get_text(val_txt);
6891 // if the value is invalid, force it to be valid
6892 if(atoi(val_txt) > MULTI_HO_MAX_TIME_LIMIT){
6893 memset(val_txt,0,255);
6894 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);
6895 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6899 // check against max observer limit
6900 Multi_ho_obs.get_text(val_txt);
6901 // if the value is invalid, force it to be valid
6902 if(atoi(val_txt) > MULTI_HO_MAX_OBS){
6903 memset(val_txt,0,255);
6904 SDL_snprintf(val_txt,SDL_arraysize(val_txt),XSTR("Warning\nObserver count is greater than max allowed (%d)",798),MULTI_HO_MAX_OBS);
6905 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6909 // check against furball kill limit
6910 Multi_ho_kill_limit.get_text(val_txt);
6911 // if the value is invalid, force it to be valid
6912 if(atoi(val_txt) > MULTI_HO_MAX_KILL_LIMIT){
6913 memset(val_txt,0,255);
6914 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);
6915 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6919 // check against the token wait limit
6920 Multi_ho_voice_wait.get_text(val_txt);
6921 if(atoi(val_txt) > MULTI_HO_MAX_TOKEN_WAIT){
6922 memset(val_txt,0,255);
6923 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);
6924 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,val_txt);
6928 // all values are valid
6932 void multi_ho_check_focus()
6934 // if an inputbox has been pressed (hit enter), lose its focus
6935 if (Multi_ho_respawns.pressed() || Multi_ho_time_limit.pressed() || Multi_ho_voice_wait.pressed() || Multi_ho_kill_limit.pressed() || Multi_ho_obs.pressed()) {
6936 Multi_ho_respawns.clear_focus();
6937 Multi_ho_time_limit.clear_focus();
6938 Multi_ho_voice_wait.clear_focus();
6939 Multi_ho_kill_limit.clear_focus();
6940 Multi_ho_obs.clear_focus();
6941 gamesnd_play_iface(SND_COMMIT_PRESSED);
6942 chatbox_set_focus();
6943 Multi_ho_lastframe_input = 0;
6945 } else if(!Multi_ho_lastframe_input) {
6946 // if we didn't have focus last frame
6947 if(Multi_ho_respawns.has_focus() || Multi_ho_time_limit.has_focus() || Multi_ho_kill_limit.has_focus() || Multi_ho_voice_wait.has_focus() ){
6948 chatbox_lose_focus();
6950 Multi_ho_lastframe_input = 1;
6953 // if we _did_ have focus last frame
6955 // if we no longer have focus on any of the input boxes, set the focus on the chatbox
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() && !chatbox_has_focus()){
6957 chatbox_set_focus();
6959 // if the chatbox now has focus, clear all focus from our inputboxes
6960 else if (chatbox_has_focus()) {
6961 Multi_ho_respawns.clear_focus();
6962 Multi_ho_time_limit.clear_focus();
6963 Multi_ho_kill_limit.clear_focus();
6964 Multi_ho_voice_wait.clear_focus();
6966 Multi_ho_lastframe_input = 0;
6971 void multi_ho_blit_max_respawns()
6975 // if we're in campaign mode, do nothing
6976 if(Netgame.campaign_mode == MP_CAMPAIGN){
6980 // otherwise blit the max as specified by the current mission file
6981 SDL_snprintf(string,SDL_arraysize(string),"(%d)",Multi_ho_mission_respawn);
6982 gr_set_color_fast(&Color_normal);
6983 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);
6986 void multi_ho_display_skill_level()
6988 int skill_level = Multi_ho_sliders[gr_screen.res][MULTI_HO_SLIDER_SKILL].slider.pos;
6991 SDL_assert((skill_level >= 0) && (skill_level < NUM_SKILL_LEVELS));
6992 if((skill_level < 0) || (skill_level >= NUM_SKILL_LEVELS)){
6996 gr_set_color_fast(&Color_bright);
6997 gr_string(Ho_st_coords[gr_screen.res][0], Ho_st_coords[gr_screen.res][1], Skill_level_names(skill_level, 1));
7000 // -------------------------------------------------------------------------------------------------------------
7002 // MULTIPLAYER JOIN SCREEN
7005 #define MULTI_JW_NUM_BUTTONS 8
7009 #define MULTI_JW_PALETTE "InterfacePalette"
7011 static const char *Multi_jw_bitmap_fname[GR_NUM_RESOLUTIONS] = {
7012 "MultiJoinWait", // GR_640
7013 "2_MultiJoinWait" // GR_1024
7016 static const char *Multi_jw_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
7017 "MultiJoinWait-M", // GR_640
7018 "2_MultiJoinWait-M" // GR_1024
7024 #define MJW_SCROLL_PLAYERS_UP 0
7025 #define MJW_SCROLL_PLAYERS_DOWN 1
7028 #define MJW_PILOT_INFO 4
7029 #define MJW_SCROLL_INFO_UP 5
7030 #define MJW_SCROLL_INFO_DOWN 6
7031 #define MJW_CANCEL 7
7033 UI_WINDOW Multi_jw_window; // the window object for the join screen
7034 int Multi_jw_bitmap; // the background bitmap
7036 // constants for coordinate lookup
7037 #define MJW_X_COORD 0
7038 #define MJW_Y_COORD 1
7039 #define MJW_W_COORD 2
7040 #define MJW_H_COORD 3
7042 ui_button_info Multi_jw_buttons[GR_NUM_RESOLUTIONS][MULTI_JW_NUM_BUTTONS] = {
7045 ui_button_info("MJW_00", 0, 50, -1, -1, 0),
7046 ui_button_info("MJW_01", 0, 87, -1, -1, 1),
7047 ui_button_info("MJW_02", 20, 219, -1, -1, 2),
7048 ui_button_info("MJW_03", 73, 219, -1, -1, 3),
7049 ui_button_info("MJW_09", 131, 213, -1, -1, 9),
7050 ui_button_info("MJW_05", 0, 398, -1, -1, 5),
7051 ui_button_info("MJW_06", 0, 435, -1, -1, 6),
7052 ui_button_info("MJW_04", 559, 411, -1, -1, 4),
7054 ui_button_info("MJW_00", 1, 24, -1, -1, 0),
7055 ui_button_info("MJW_01", 1, 66, -1, -1, 1),
7056 ui_button_info("MJW_02", 30, 244, 20, 272, 2),
7057 ui_button_info("MJW_03", 84, 244, 73, 272, 3),
7058 ui_button_info("MJW_04", 139, 242, 134, 272, 4),
7059 ui_button_info("MJW_05", 1, 406, -1, -1, 5),
7060 ui_button_info("MJW_06", 1, 447, -1, -1, 6),
7061 ui_button_info("MJW_07", 577, 428, 570, 414, 7),
7065 ui_button_info("2_MJW_00", 2, 38, -1, -1, 0),
7066 ui_button_info("2_MJW_01", 2, 106, -1, -1, 1),
7067 ui_button_info("2_MJW_02", 48, 390, 47, 435, 2),
7068 ui_button_info("2_MJW_03", 134, 390, 133, 435, 3),
7069 ui_button_info("2_MJW_04", 223, 388, 225, 435, 4),
7070 ui_button_info("2_MJW_05", 2, 649, -1, -1, 5),
7071 ui_button_info("2_MJW_06", 2, 715, -1, -1, 6),
7072 ui_button_info("2_MJW_07", 923, 685, 931, 667, 7),
7077 #define MULTI_JW_NUM_TEXT 7
7079 UI_XSTR Multi_jw_text[GR_NUM_RESOLUTIONS][MULTI_JW_NUM_TEXT] = {
7081 { "Team 1", 1308, 20, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_TEAM0].button },
7082 { "Team 2", 1309, 73, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_TEAM1].button },
7083 { "Pilot", 1310, 134, 272, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_PILOT_INFO].button },
7084 { "Info", 1311, 134, 283, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[0][MJW_PILOT_INFO].button },
7085 { "Cancel", 387, 570, 414, UI_XSTR_COLOR_PINK, -1, &Multi_jw_buttons[0][MJW_CANCEL].button },
7086 { "Players", 1269, 38, 8, UI_XSTR_COLOR_GREEN, -1, NULL },
7087 { "Choose Team", 1312, 27, 231, UI_XSTR_COLOR_GREEN, -1, NULL },
7090 { "Team 1", 1308, 47, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_TEAM0].button },
7091 { "Team 2", 1309, 133, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_TEAM1].button },
7092 { "Pilot", 1310, 225, 435, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_PILOT_INFO].button },
7093 { "Info", 1311, 225, 446, UI_XSTR_COLOR_GREEN, -1, &Multi_jw_buttons[1][MJW_PILOT_INFO].button },
7094 { "Cancel", 387, 931, 667, UI_XSTR_COLOR_PINK, -1, &Multi_jw_buttons[1][MJW_CANCEL].button },
7095 { "Players", 1269, 165, 12, UI_XSTR_COLOR_GREEN, -1, NULL },
7096 { "Choose Team", 1312, 45, 373, UI_XSTR_COLOR_GREEN, -1, NULL },
7101 int Mjw_players_coords[GR_NUM_RESOLUTIONS][4] = {
7114 int Mjw_mission_name_coords[GR_NUM_RESOLUTIONS][2] = {
7123 // squad war checkbox
7124 UI_CHECKBOX Multi_jw_sw_checkbox;
7125 const char *Multi_jw_sw_checkbox_fname[GR_NUM_RESOLUTIONS] = {
7129 int Multi_jw_sw_checkbox_coords[GR_NUM_RESOLUTIONS][2] = {
7137 int Multi_jw_sw_checkbox_text[GR_NUM_RESOLUTIONS][2] = {
7147 // player list control thingie defs
7148 #define MULTI_JW_PLIST_MAX_DISPLAY 19
7149 int Multi_jw_plist_select_flag; // indicates whether we currently have a selected player
7150 short Multi_jw_plist_select_id; // id of the current selected player
7151 UI_BUTTON Multi_jw_plist_select_button; // for selecting a player
7153 int Multi_jw_should_show_popup = 0;
7155 // LOCAL function definitions
7156 void multi_jw_check_buttons();
7157 void multi_jw_button_pressed(int n);
7158 void multi_jw_do_netstuff();
7159 void multi_jw_scroll_players_up();
7160 void multi_jw_scroll_players_down();
7161 void multi_jw_plist_process();
7162 void multi_jw_plist_blit_normal();
7163 void multi_jw_plist_blit_team();
7164 short multi_jw_get_mouse_id();
7166 void multi_game_client_setup_init()
7170 // create the interface window
7171 Multi_jw_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
7172 Multi_jw_window.set_mask_bmap(Multi_jw_bitmap_mask_fname[gr_screen.res]);
7174 // load the background bitmap
7175 Multi_jw_bitmap = bm_load(Multi_jw_bitmap_fname[gr_screen.res]);
7176 if(Multi_jw_bitmap < 0){
7177 // we failed to load the bitmap - this is very bad
7181 // initialize the player list data
7182 Multi_jw_plist_select_flag = 0;
7183 Multi_jw_plist_select_id = -1;
7185 // kill any old instances of the chatbox and create a new one
7187 chatbox_create(CHATBOX_FLAG_BIG | CHATBOX_FLAG_DRAW_BOX | CHATBOX_FLAG_BUTTONS);
7189 // initialize the common notification messaging
7190 multi_common_notify_init();
7192 // initialize the common mission info display area.
7193 multi_common_set_text("");
7195 // use the common interface palette
7196 multi_common_set_palette();
7198 // create the interface buttons
7199 for(idx=0; idx<MULTI_JW_NUM_BUTTONS; idx++){
7200 // create the object
7201 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);
7203 // set the sound to play when highlighted
7204 Multi_jw_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
7206 // set the ani for the button
7207 Multi_jw_buttons[gr_screen.res][idx].button.set_bmaps(Multi_jw_buttons[gr_screen.res][idx].filename);
7210 Multi_jw_buttons[gr_screen.res][idx].button.link_hotspot(Multi_jw_buttons[gr_screen.res][idx].hotspot);
7213 // if this is a PXO game, enable the squadwar checkbox
7214 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);
7215 Multi_jw_sw_checkbox.set_bmaps(Multi_jw_sw_checkbox_fname[gr_screen.res], 6, 0);
7216 Multi_jw_sw_checkbox.disable();
7217 if(!MULTI_IS_TRACKER_GAME){
7218 Multi_jw_sw_checkbox.hide();
7223 for(idx=0; idx<MULTI_JW_NUM_TEXT; idx++){
7224 Multi_jw_window.add_XSTR(&Multi_jw_text[gr_screen.res][idx]);
7228 // create the player select list button and hide it
7229 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);
7230 Multi_jw_plist_select_button.hide();
7233 Multi_jw_buttons[gr_screen.res][MJW_CANCEL].button.set_hotkey(SDLK_ESCAPE);
7235 // remove campaign flags
7236 Game_mode &= ~(GM_CAMPAIGN_MODE);
7238 // tell the server we have finished joining
7239 Net_player->state = NETPLAYER_STATE_JOINED;
7240 send_netplayer_update_packet();
7243 ml_printf(NOX("Joined netgame %s"), Netgame.name);
7245 // send any appropriate files
7246 multi_data_send_my_junk();
7249 void multi_game_client_setup_do_frame()
7252 int k = chatbox_process();
7253 char mission_text[255];
7254 k = Multi_jw_window.process(k,0);
7256 Multi_jw_should_show_popup = 0;
7258 // process any button clicks
7259 multi_jw_check_buttons();
7261 // do any network related stuff
7262 multi_jw_do_netstuff();
7264 // draw the background, etc
7266 GR_MAYBE_CLEAR_RES(Multi_jw_bitmap);
7267 if(Multi_jw_bitmap != -1){
7268 gr_set_bitmap(Multi_jw_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7272 // if we're not in team vs. team mode, don't draw the team buttons
7273 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
7274 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.hide();
7275 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.hide();
7276 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.disable();
7277 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.disable();
7279 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.enable();
7280 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.enable();
7281 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.unhide();
7282 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.unhide();
7285 if(MULTI_IS_TRACKER_GAME){
7286 // maybe check the squadwar button
7287 if(Netgame.type_flags & NG_TYPE_SW){
7288 Multi_jw_sw_checkbox.set_state(1);
7289 gr_set_color_fast(&Color_bright);
7291 Multi_jw_sw_checkbox.set_state(0);
7292 gr_set_color_fast(&Color_normal);
7295 gr_string(Multi_jw_sw_checkbox_text[gr_screen.res][0], Multi_jw_sw_checkbox_text[gr_screen.res][1], "SquadWar");
7298 // draw the UI window
7299 Multi_jw_window.draw();
7301 // process and display the player list
7302 // NOTE : this must be done before the buttons are checked to insure that a player hasn't dropped
7303 multi_jw_plist_process();
7304 if(Netgame.type_flags & NG_TYPE_TEAM){
7305 multi_jw_plist_blit_team();
7307 multi_jw_plist_blit_normal();
7310 // display any text in the info area
7311 multi_common_render_text();
7313 // display any pending notification messages
7314 multi_common_notify_do();
7316 // blit the mission filename if possible
7317 if(Netgame.campaign_mode){
7318 if(strlen(Netgame.campaign_name) > 0){
7319 SDL_strlcpy(mission_text, Netgame.campaign_name, SDL_arraysize(mission_text));
7321 if(strlen(Netgame.title) > 0){
7322 SDL_strlcat(mission_text, ", ", SDL_arraysize(mission_text));
7323 SDL_strlcat(mission_text, Netgame.title, SDL_arraysize(mission_text));
7326 gr_set_color_fast(&Color_bright_white);
7327 gr_string(Mjw_mission_name_coords[gr_screen.res][MJW_X_COORD],Mjw_mission_name_coords[gr_screen.res][MJW_Y_COORD],mission_text);
7330 if(strlen(Netgame.mission_name) > 0){
7331 SDL_strlcpy(mission_text, Netgame.mission_name, SDL_arraysize(mission_text));
7333 if(strlen(Netgame.title) > 0){
7334 SDL_strlcat(mission_text, ", ", SDL_arraysize(mission_text));
7335 SDL_strlcat(mission_text, Netgame.title, SDL_arraysize(mission_text));
7338 gr_set_color_fast(&Color_bright_white);
7339 gr_string(Mjw_mission_name_coords[gr_screen.res][MJW_X_COORD],Mjw_mission_name_coords[gr_screen.res][MJW_Y_COORD],mission_text);
7343 // process and show the chatbox thingie
7347 Multi_jw_window.draw_tooltip();
7349 // display the voice status indicator
7350 multi_common_voice_display_status();
7355 // if we're supposed to be displaying a pilot info popup
7356 if(Multi_jw_should_show_popup){
7357 player_index = find_player_id(Multi_jw_plist_select_id);
7358 if(player_index != -1){
7359 multi_pinfo_popup(&Net_players[player_index]);
7364 void multi_game_client_setup_close()
7366 // unload any bitmaps
7367 if(!bm_unload(Multi_jw_bitmap)){
7368 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_jw_bitmap_fname[gr_screen.res]));
7371 // destroy the chatbox
7374 // destroy the UI_WINDOW
7375 Multi_jw_window.destroy();
7378 if(Netgame.game_state == NETGAME_STATE_MISSION_SYNC){
7379 gamesnd_play_iface(SND_COMMIT_PRESSED);
7384 void multi_jw_check_buttons()
7387 for(idx=0;idx<MULTI_JW_NUM_BUTTONS;idx++){
7388 // we only really need to check for one button pressed at a time, so we can break after
7390 if(Multi_jw_buttons[gr_screen.res][idx].button.pressed()){
7391 multi_jw_button_pressed(idx);
7397 void multi_jw_button_pressed(int n)
7401 gamesnd_play_iface(SND_USER_SELECT);
7402 multi_quit_game(PROMPT_CLIENT);
7404 case MJW_SCROLL_PLAYERS_UP:
7405 multi_jw_scroll_players_up();
7407 case MJW_SCROLL_PLAYERS_DOWN:
7408 multi_jw_scroll_players_down();
7410 case MJW_SCROLL_INFO_UP:
7411 multi_common_scroll_text_up();
7413 case MJW_SCROLL_INFO_DOWN:
7414 multi_common_scroll_text_down();
7417 // request to set myself to team 0
7419 gamesnd_play_iface(SND_USER_SELECT);
7420 multi_team_set_team(Net_player,0);
7423 // request to set myself to team 1
7425 gamesnd_play_iface(SND_USER_SELECT);
7426 multi_team_set_team(Net_player,1);
7430 case MJW_PILOT_INFO:
7431 Multi_jw_should_show_popup = 1;
7435 multi_common_add_notify(XSTR("Not implemented yet!",760));
7440 // do stuff like pinging servers, sending out requests, etc
7441 void multi_jw_do_netstuff()
7445 void multi_jw_scroll_players_up()
7447 gamesnd_play_iface(SND_GENERAL_FAIL);
7450 // scroll down through the player list
7451 void multi_jw_scroll_players_down()
7453 gamesnd_play_iface(SND_GENERAL_FAIL);
7456 void multi_jw_plist_process()
7458 int test_count,player_index,idx;
7460 // first determine if there are 0 players in the game. This should never happen since the host is _always_ in the game
7462 for(idx=0;idx<MAX_PLAYERS;idx++){
7463 // count anyone except the standalone server (if applicable)
7464 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7468 if(test_count <= 0){
7472 // if we had a selected item but that player has left, select myself instead
7473 if(Multi_jw_plist_select_flag){
7474 player_index = find_player_id(Multi_jw_plist_select_id);
7475 if(player_index == -1){
7476 Multi_jw_plist_select_id = Net_player->player_id;
7479 Multi_jw_plist_select_flag = 1;
7480 Multi_jw_plist_select_id = Net_player->player_id;
7483 // if the player has clicked somewhere in the player list area
7484 if(Multi_jw_plist_select_button.pressed()){
7488 player_id = multi_jw_get_mouse_id();
7489 player_index = find_player_id(player_id);
7490 if(player_index != -1){
7491 Multi_jw_plist_select_id = player_id;
7492 Multi_jw_plist_select_flag = 1;
7497 void multi_jw_plist_blit_normal()
7500 char str[CALLSIGN_LEN+1];
7501 int y_start = Mjw_players_coords[gr_screen.res][MJW_Y_COORD];
7504 // display all the players
7505 for(idx=0;idx<MAX_PLAYERS;idx++){
7506 // reset total offset
7509 // count anyone except the standalone server (if applicable)
7510 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7511 // highlight him if he's the host
7512 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7513 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7514 gr_set_color_fast(&Color_text_active_hi);
7516 gr_set_color_fast(&Color_bright);
7519 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7520 gr_set_color_fast(&Color_text_active);
7522 gr_set_color_fast(&Color_text_normal);
7526 // optionally draw his CD status
7527 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7528 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7529 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7531 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7534 // make sure the string will fit, then display it
7535 SDL_strlcpy(str, Net_players[idx].player->callsign, SDL_arraysize(str));
7536 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
7537 SDL_strlcat(str, "(0)", SDL_arraysize(str));
7539 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7540 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7547 void multi_jw_plist_blit_team()
7550 char str[CALLSIGN_LEN+1];
7551 int y_start = Mjw_players_coords[gr_screen.res][MJW_Y_COORD];
7554 // always blit the proper team button based on _my_ team status
7555 if(Net_player->p_info.team == 0){
7556 Multi_jw_buttons[gr_screen.res][MJW_TEAM0].button.draw_forced(2);
7558 Multi_jw_buttons[gr_screen.res][MJW_TEAM1].button.draw_forced(2);
7561 // display all the red players first
7562 for(idx=0;idx<MAX_PLAYERS;idx++){
7563 // reset total offset
7566 // count anyone except the standalone server (if applicable)
7567 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
7568 // highlight him if he's the host
7569 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7570 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7571 gr_set_color_fast(&Color_text_active_hi);
7573 gr_set_color_fast(&Color_bright);
7576 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7577 gr_set_color_fast(&Color_text_active);
7579 gr_set_color_fast(&Color_text_normal);
7583 // optionally draw his CD status
7584 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7585 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7586 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7588 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7591 // blit the red team indicator
7592 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
7593 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
7594 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT], 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-2);
7597 total_offset += Multi_common_icon_dims[MICON_TEAM0_SELECT][0] + 1;
7600 if(Multi_common_icons[MICON_TEAM0] != -1){
7601 gr_set_bitmap(Multi_common_icons[MICON_TEAM0], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7602 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7604 total_offset += Multi_common_icon_dims[MICON_TEAM0][0] + 1;
7608 // make sure the string will fit
7609 SDL_strlcpy(str, Net_players[idx].player->callsign, SDL_arraysize(str));
7610 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7612 // display him in the correct half of the list depending on his team
7613 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7618 // display all the green players next
7619 for(idx=0;idx<MAX_PLAYERS;idx++){
7620 // reset total offset
7623 // count anyone except the standalone server (if applicable)
7624 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
7625 // highlight him if he's the host
7626 if(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST){
7627 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7628 gr_set_color_fast(&Color_text_active_hi);
7630 gr_set_color_fast(&Color_bright);
7633 if(Multi_jw_plist_select_id == Net_players[idx].player_id){
7634 gr_set_color_fast(&Color_text_active);
7636 gr_set_color_fast(&Color_text_normal);
7640 // optionally draw his CD status
7641 if((Net_players[idx].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
7642 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7643 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start - 1);
7645 total_offset += Multi_common_icon_dims[MICON_CD][0] + 1;
7648 // blit the red team indicator
7649 if(Net_players[idx].flags & NETINFO_FLAG_TEAM_CAPTAIN){
7650 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
7651 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7652 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7654 total_offset += Multi_common_icon_dims[MICON_TEAM1_SELECT][0] + 1;
7657 if(Multi_common_icons[MICON_TEAM1] != -1){
7658 gr_set_bitmap(Multi_common_icons[MICON_TEAM1], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
7659 gr_bitmap(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start-2);
7661 total_offset += Multi_common_icon_dims[MICON_TEAM1][0] + 1;
7665 // make sure the string will fit
7666 SDL_strlcpy(str, Net_players[idx].player->callsign, SDL_arraysize(str));
7667 if(Net_players[idx].flags & NETINFO_FLAG_OBSERVER){
7668 SDL_strlcat(str, "(0)", SDL_arraysize(str));
7670 gr_force_fit_string(str,CALLSIGN_LEN,Mjw_players_coords[gr_screen.res][MJW_W_COORD] - total_offset);
7672 // display him in the correct half of the list depending on his team
7673 gr_string(Mjw_players_coords[gr_screen.res][MJW_X_COORD] + total_offset,y_start,str);
7679 void multi_jw_handle_join(net_player *pl)
7681 // for now just play a bloop sound
7682 gamesnd_play_iface(SND_ICON_DROP_ON_WING);
7685 short multi_jw_get_mouse_id()
7687 // determine where he clicked (y pixel value)
7689 Multi_jw_plist_select_button.get_mouse_pos(NULL,&y);
7691 // select things a little differently if we're in team vs. team or non-team vs. team mode
7693 if(Netgame.type_flags & NG_TYPE_TEAM){
7694 int player_index = -1;
7696 // look through all of team red first
7697 for(idx=0;idx<MAX_PLAYERS;idx++){
7698 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 0)){
7701 // if this is the _nth_ guy
7709 // if we still haven't found him yet, look through the green team
7710 if(player_index == -1){
7711 for(idx=0;idx<MAX_PLAYERS;idx++){
7712 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_players[idx].p_info.team == 1)){
7714 // if this is the _nth_ guy
7722 if(player_index != -1){
7723 return Net_players[idx].player_id;
7726 // select the nth active player if possible, disregarding the standalone server
7727 for(idx=0;idx<MAX_PLAYERS;idx++){
7728 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
7731 // if this is the _nth_ guy
7733 return Net_players[idx].player_id;
7744 // -------------------------------------------------------------------------------------------------------------
7746 // MULTIPLAYER WAIT/SYNCH SCREEN
7749 #define MULTI_SYNC_HOST_COUNT 4 // host uses 4 buttons (and sometimes 5)
7750 #define MULTI_SYNC_CLIENT_COUNT 3 // client only uses 3 buttons
7752 const char *Multi_sync_bitmap_fname[GR_NUM_RESOLUTIONS] = {
7753 "MultiSynch", // GR_640
7754 "2_MultiSynch" // GR_1024
7757 const char *Multi_sync_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
7758 "MultiSynch-M", // GR_640
7759 "2_MultiSynch-M" // GR_1024
7764 // constants for coordinate lookup
7765 #define MS_X_COORD 0
7766 #define MS_Y_COORD 1
7767 #define MS_W_COORD 2
7768 #define MS_H_COORD 3
7770 UI_WINDOW Multi_sync_window; // the window object for the join screen
7771 int Multi_sync_button_count; // the # of buttons to use for this instance of mission sync
7772 int Multi_sync_bitmap; // the background bitmap
7775 #define MULTI_SYNC_NUM_BUTTONS 5
7776 #define MS_SCROLL_INFO_UP 0
7777 #define MS_SCROLL_INFO_DOWN 1
7781 ui_button_info Multi_sync_buttons[GR_NUM_RESOLUTIONS][MULTI_SYNC_NUM_BUTTONS] = {
7784 ui_button_info("MS_00", 0, 396, -1, -1, 0),
7785 ui_button_info("MS_01", 0, 435, -1, -1, 1),
7786 ui_button_info("MS_02", 496, 411, -1, -1, 2),
7787 ui_button_info("MS_04", 436, 403, -1, -1, 4),
7788 ui_button_info("MS_03", 556, 398, -1, -1, 3),
7790 ui_button_info("MS_00", 1, 404, -1, -1, 0),
7791 ui_button_info("MS_01", 1, 446, -1, -1, 1),
7792 ui_button_info("MS_03", 518, 426, 519, 416, 3),
7793 ui_button_info("MS_02", 469, 426, 479, 416, 2),
7794 ui_button_info("MS_04", 571, 420, 577, 416, 4),
7798 ui_button_info("2_MS_00", 2, 647, -1, -1, 0),
7799 ui_button_info("2_MS_01", 2, 713, -1, -1, 1),
7800 ui_button_info("2_MS_03", 829, 682, 831, 667, 3),
7801 ui_button_info("2_MS_02", 751, 682, 766, 667, 2),
7802 ui_button_info("2_MS_04", 914, 672, 924, 667, 4),
7808 #define MULTI_SYNC_NUM_TEXT 5
7810 #define MST_LAUNCH 2
7811 UI_XSTR Multi_sync_text[GR_NUM_RESOLUTIONS][MULTI_SYNC_NUM_TEXT] = {
7813 { "Kick", 1266, 479, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_KICK].button },
7814 { "Cancel", 387, 519, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_CANCEL].button },
7815 { "Launch", 801, 577, 416, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[0][MS_LAUNCH].button },
7816 { "Players", 1269, 23, 133, UI_XSTR_COLOR_GREEN, -1, NULL },
7817 { "Status", 1304, 228, 133, UI_XSTR_COLOR_GREEN, -1, NULL }
7820 { "Kick", 1266, 766, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_KICK].button },
7821 { "Cancel", 387, 831, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_CANCEL].button },
7822 { "Launch", 801, 924, 667, UI_XSTR_COLOR_PINK, -1, &Multi_sync_buttons[1][MS_LAUNCH].button },
7823 { "Players", 1269, 38, 214, UI_XSTR_COLOR_GREEN, -1, NULL },
7824 { "Status", 1304, 366, 214, UI_XSTR_COLOR_GREEN, -1, NULL }
7830 int Ms_status_coords[GR_NUM_RESOLUTIONS][4] = {
7839 // player status coords
7840 int Ms_status2_coords[GR_NUM_RESOLUTIONS][4] = {
7849 int Ms_cd_icon_offset[GR_NUM_RESOLUTIONS] = {
7854 int Ms_team_icon_offset[GR_NUM_RESOLUTIONS] = {
7859 // player currently selected, index into Net_players[]
7860 int Multi_sync_player_select = -1;
7862 // player list control thingie defs
7863 #define MULTI_SYNC_PLIST_MAX_DISPLAY 15
7864 int Multi_sync_plist_start; // where to start displaying from
7865 int Multi_sync_plist_count; // how many we have
7867 // list select button
7868 UI_BUTTON Multi_sync_plist_button;
7870 int Multi_sync_mode = -1;
7872 #define MULTI_SYNC_COUNTDOWN_TIME 5 // in seconds
7873 float Multi_sync_countdown_timer;
7874 int Multi_sync_countdown = -1;
7876 int Multi_launch_button_created;
7879 // countdown animation timer
7880 const char* Multi_sync_countdown_fname[GR_NUM_RESOLUTIONS] = {
7882 "2_Count" // GR_1024
7885 int Multi_sync_countdown_coords[GR_NUM_RESOLUTIONS][2] = {
7896 anim *Multi_sync_countdown_anim = NULL;
7897 anim_instance *Multi_sync_countdown_instance = NULL;
7900 // PREBRIEFING STUFF
7901 // syncing flags used by the server
7902 int Mission_sync_flags = 0;
7903 #define MS_FLAG_SENT_FILESIG (1<<0) // sent filesig requests
7904 #define MS_FLAG_SENT_LOAD (1<<1) // sent load packets
7905 #define MS_FLAG_PUSHED_BRIEFING (1<<2) // pushed everyone else into the briefing
7906 #define MS_FLAG_POST_DATA (1<<3) // sent the post data block
7907 #define MS_FLAG_WSS_SLOTS (1<<4) // all players have received wss slot data
7908 #define MS_FLAG_PSETTINGS (1<<5) // send the player settings packet
7909 #define MS_FLAG_MT_STATS_START (1<<6) // server has started getting player stats from the tracker
7910 #define MS_FLAG_MT_STATS_DONE (1<<7) // server has finished getting player stats from the tracker (success or fail)
7911 #define MS_FLAG_TS_SLOTS (1<<8) // team/ship slots have been sent
7912 #define MS_FLAG_DATA_DONE (1<<9) // done transferring all necessary data
7913 #define MS_FLAG_CAMP_DONE (1<<10) // send campaign pool/goal/event stuff
7915 // POSTBRIEFING STUFF
7916 int Multi_state_timestamp;
7917 int Multi_sync_launch_pressed;
7919 // LOCAL function definitions
7920 void multi_sync_check_buttons();
7921 void multi_sync_button_pressed(int n);
7922 void multi_sync_scroll_info_up();
7923 void multi_sync_scroll_info_down();
7924 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
7925 void multi_sync_display_status(const char *status, int index); // display info on the right hand portion of the status window thingie
7926 void multi_sync_force_start_pre();
7927 void multi_sync_force_start_post();
7928 void multi_sync_launch();
7929 void multi_sync_create_launch_button();
7930 void multi_sync_blit_screen_all();
7931 void multi_sync_handle_plist();
7933 void multi_sync_common_init();
7934 void multi_sync_common_do();
7935 void multi_sync_common_close();
7937 void multi_sync_pre_init();
7938 void multi_sync_pre_do();
7939 void multi_sync_pre_close();
7941 void multi_sync_post_init();
7942 void multi_sync_post_do();
7943 void multi_sync_post_close();
7948 // perform the correct init functions
7949 void multi_sync_init()
7951 Multi_sync_countdown = -1;
7955 // reset all timestamp
7956 multi_reset_timestamps();
7958 extern time_t Player_multi_died_check;
7959 Player_multi_died_check = -1;
7961 if(!(Game_mode & GM_STANDALONE_SERVER)){
7962 multi_sync_common_init();
7965 switch(Multi_sync_mode){
7966 case MULTI_SYNC_PRE_BRIEFING:
7967 multi_sync_pre_init();
7969 case MULTI_SYNC_POST_BRIEFING:
7970 multi_sync_post_init();
7972 case MULTI_SYNC_INGAME:
7973 multi_ingame_sync_init();
7978 // perform the correct do frame functions
7979 void multi_sync_do()
7981 if(!(Game_mode & GM_STANDALONE_SERVER)){
7982 multi_sync_common_do();
7985 // if the netgame is ending, don't do any sync processing
7986 if(multi_endgame_ending()){
7990 // process appropriateliy
7991 switch(Multi_sync_mode){
7992 case MULTI_SYNC_PRE_BRIEFING:
7993 multi_sync_pre_do();
7995 case MULTI_SYNC_POST_BRIEFING:
7996 multi_sync_post_do();
7998 case MULTI_SYNC_INGAME:
7999 multi_ingame_sync_do();
8002 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8003 if(Multi_sync_bitmap != -1){
8004 gr_set_bitmap(Multi_sync_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8007 Multi_sync_window.draw();
8009 multi_sync_blit_screen_all();
8016 // perform the correct close functions
8017 void multi_sync_close()
8019 switch(Multi_sync_mode){
8020 case MULTI_SYNC_PRE_BRIEFING:
8021 multi_sync_pre_close();
8023 case MULTI_SYNC_POST_BRIEFING:
8024 multi_sync_post_close();
8026 case MULTI_SYNC_INGAME:
8027 multi_ingame_sync_close();
8031 if(!(Game_mode & GM_STANDALONE_SERVER)){
8032 multi_sync_common_close();
8036 const char *multi_sync_tooltip_handler(const char *str)
8038 if (!SDL_strcasecmp(str, NOX("@launch"))) {
8039 if (Multi_launch_button_created){
8040 return XSTR("Launch",801);
8047 void multi_sync_common_init()
8051 // create the interface window
8052 Multi_sync_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
8053 Multi_sync_window.set_mask_bmap(Multi_sync_bitmap_mask_fname[gr_screen.res]);
8054 Multi_sync_window.tooltip_handler = multi_sync_tooltip_handler;
8056 // load the background bitmap
8057 Multi_sync_bitmap = bm_load(Multi_sync_bitmap_fname[gr_screen.res]);
8058 if (Multi_sync_bitmap < 0) {
8059 // we failed to load the bitmap - this is very bad
8063 // initialize the player list data
8064 Multi_sync_plist_start = 0;
8065 Multi_sync_plist_count = 1; // we can pretty safely assume that there's one player in the game - me.
8067 Multi_launch_button_created = 0;
8069 // create the chatbox thingie (shouldn't be necesary to do this, but we'll put it in for good measure)
8072 // force the chatbox to be small
8073 chatbox_force_small();
8075 // initialize the common notification messaging
8076 multi_common_notify_init();
8078 // initialize the common mission info display area.
8079 multi_common_set_text("");
8081 // use the common interface palette
8082 multi_common_set_palette();
8084 // don't select any player yet.
8085 Multi_sync_player_select = -1;
8087 // determine how many of the 5 buttons to create
8088 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8089 Multi_sync_button_count = MULTI_SYNC_HOST_COUNT;
8091 Multi_sync_button_count = MULTI_SYNC_CLIENT_COUNT;
8093 // create the interface buttons
8094 for(idx=0; idx<Multi_sync_button_count; idx++){
8095 // create the object
8096 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);
8098 // set the sound to play when highlighted
8099 Multi_sync_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
8101 // set the ani for the button
8102 // this wierdness is necessary because cancel and kick buttons aren't drawn on the background bitmap,
8103 // so we have to load in frame 0, too (the file should exist)
8104 if ((idx == MS_CANCEL) || (idx == MS_KICK) || (idx == MS_LAUNCH)) {
8105 Multi_sync_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sync_buttons[gr_screen.res][idx].filename, 3, 0);
8107 Multi_sync_buttons[gr_screen.res][idx].button.set_bmaps(Multi_sync_buttons[gr_screen.res][idx].filename);
8111 Multi_sync_buttons[gr_screen.res][idx].button.link_hotspot(Multi_sync_buttons[gr_screen.res][idx].hotspot);
8116 for(idx=0; idx<MULTI_SYNC_NUM_TEXT; idx++) {
8117 // don't create the "launch" button text just yet
8118 if(idx == MST_LAUNCH) {
8121 // multiplayer clients should ignore the kick button
8122 if(!MULTIPLAYER_MASTER && !MULTIPLAYER_HOST && (idx == MST_KICK)) {
8126 Multi_sync_window.add_XSTR(&Multi_sync_text[gr_screen.res][idx]);
8130 // create the player list select button and hide it
8131 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);
8132 Multi_sync_plist_button.hide();
8134 // set up hotkeys for certain common functions
8135 Multi_sync_buttons[gr_screen.res][MS_CANCEL].button.set_hotkey(SDLK_ESCAPE);
8138 void multi_sync_common_do()
8140 int k = chatbox_process();
8141 k = Multi_sync_window.process(k);
8143 // process the player list
8144 multi_sync_handle_plist();
8146 // process any button clicks
8147 multi_sync_check_buttons();
8149 // process any keypresses
8153 gamesnd_play_iface(SND_USER_SELECT);
8154 multi_quit_game(PROMPT_ALL);
8159 void multi_sync_common_close()
8161 // unload any bitmaps
8162 if(!bm_unload(Multi_sync_bitmap)){
8163 nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_sync_bitmap_fname[gr_screen.res]));
8166 extern time_t Player_multi_died_check;
8167 Player_multi_died_check = -1;
8169 // destroy the UI_WINDOW
8170 Multi_sync_window.destroy();
8173 void multi_sync_blit_screen_all()
8180 // display any text in the info area
8181 multi_common_render_text();
8183 // display any pending notification messages
8184 multi_common_notify_do();
8186 // display any info about visible players
8188 for(idx=0;idx<MAX_PLAYERS;idx++){
8189 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
8190 // display his name and status
8191 multi_sync_display_name(Net_players[idx].player->callsign,count,idx);
8193 // get the player state
8194 state = Net_players[idx].state;
8196 // if we're ingame joining, show all other players except myself as "playing"
8197 if((Net_player != NULL) && (&Net_players[idx] != Net_player) && ((Multi_sync_mode == MULTI_SYNC_INGAME) || (Net_player->flags & NETINFO_FLAG_INGAME_JOIN)) ){
8198 state = NETPLAYER_STATE_IN_MISSION;
8202 case NETPLAYER_STATE_MISSION_LOADING:
8203 multi_sync_display_status(XSTR("Mission Loading",802),count);
8205 case NETPLAYER_STATE_INGAME_SHIP_SELECT: // I don't think its possible to see this state, but...
8206 multi_sync_display_status(XSTR("Ingame Ship Select",803),count);
8208 case NETPLAYER_STATE_DEBRIEF:
8209 multi_sync_display_status(XSTR("Debriefing",804),count);
8211 case NETPLAYER_STATE_MISSION_SYNC:
8212 multi_sync_display_status(XSTR("Mission Sync",805),count);
8214 case NETPLAYER_STATE_JOINING:
8215 multi_sync_display_status(XSTR("Joining",806),count);
8217 case NETPLAYER_STATE_JOINED:
8218 multi_sync_display_status(XSTR("Joined",807),count);
8220 case NETPLAYER_STATE_SLOT_ACK :
8221 multi_sync_display_status(XSTR("Slot Ack",808),count);
8223 case NETPLAYER_STATE_BRIEFING:
8224 multi_sync_display_status(XSTR("Briefing",765),count);
8226 case NETPLAYER_STATE_SHIP_SELECT:
8227 multi_sync_display_status(XSTR("Ship Select",809),count);
8229 case NETPLAYER_STATE_WEAPON_SELECT:
8230 multi_sync_display_status(XSTR("Weapon Select",810),count);
8232 case NETPLAYER_STATE_WAITING:
8233 multi_sync_display_status(XSTR("Waiting",811),count);
8235 case NETPLAYER_STATE_IN_MISSION:
8236 multi_sync_display_status(XSTR("In Mission",812),count);
8238 case NETPLAYER_STATE_MISSION_LOADED:
8239 multi_sync_display_status(XSTR("Mission Loaded",813),count);
8241 case NETPLAYER_STATE_DATA_LOAD:
8242 multi_sync_display_status(XSTR("Data loading",814),count);
8244 case NETPLAYER_STATE_SETTINGS_ACK:
8245 multi_sync_display_status(XSTR("Ready To Enter Mission",815),count);
8247 case NETPLAYER_STATE_INGAME_SHIPS:
8248 multi_sync_display_status(XSTR("Ingame Ships Packet Ack",816),count);
8250 case NETPLAYER_STATE_INGAME_WINGS:
8251 multi_sync_display_status(XSTR("Ingame Wings Packet Ack",817),count);
8253 case NETPLAYER_STATE_INGAME_RPTS:
8254 multi_sync_display_status(XSTR("Ingame Respawn Points Ack",818),count);
8256 case NETPLAYER_STATE_SLOTS_ACK:
8257 multi_sync_display_status(XSTR("Ingame Weapon Slots Ack",819),count);
8259 case NETPLAYER_STATE_POST_DATA_ACK:
8260 multi_sync_display_status(XSTR("Post Briefing Data Block Ack",820),count);
8262 case NETPLAYER_STATE_FLAG_ACK :
8263 multi_sync_display_status(XSTR("Flags Ack",821),count);
8265 case NETPLAYER_STATE_MT_STATS :
8266 multi_sync_display_status(XSTR("Parallax Online Stats Updating",822),count);
8268 case NETPLAYER_STATE_WSS_ACK :
8269 multi_sync_display_status(XSTR("Weapon Slots Ack",823),count);
8271 case NETPLAYER_STATE_HOST_SETUP :
8272 multi_sync_display_status(XSTR("Host setup",824),count);
8274 case NETPLAYER_STATE_DEBRIEF_ACCEPT:
8275 multi_sync_display_status(XSTR("Debrief accept",825),count);
8277 case NETPLAYER_STATE_DEBRIEF_REPLAY:
8278 multi_sync_display_status(XSTR("Debrief replay",826),count);
8280 case NETPLAYER_STATE_CPOOL_ACK:
8281 multi_sync_display_status(XSTR("Campaign ship/weapon ack",827),count);
8283 case NETPLAYER_STATE_MISSION_XFER :
8285 // server should display the pct completion of all clients
8286 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8287 if(Net_players[idx].s_info.xfer_handle != -1){
8288 pct_complete = multi_xfer_pct_complete(Net_players[idx].s_info.xfer_handle);
8290 // if we've got a valid xfer handle
8291 if((pct_complete >= 0.0) && (pct_complete <= 1.0)){
8292 SDL_snprintf(txt,SDL_arraysize(txt),XSTR("Mission file xfer %d%%",828),(int)(pct_complete * 100.0f));
8296 SDL_strlcpy(txt, XSTR("Mission file xfer", 829), SDL_arraysize(txt));
8299 SDL_strlcpy(txt, XSTR("Mission file xfer", 829), SDL_arraysize(txt));
8302 // clients should display only for themselves (which is the only thing they know)
8304 // if we've got a valid file xfer handle
8305 if((&Net_players[idx] == Net_player) && (Net_player->s_info.xfer_handle != -1)){
8306 pct_complete = multi_xfer_pct_complete(Net_player->s_info.xfer_handle);
8308 // if we've got a valid xfer handle
8309 if((pct_complete >= 0.0) && (pct_complete <= 1.0)){
8310 SDL_snprintf(txt,SDL_arraysize(txt),XSTR("Mission file xfer %d%%",828),(int)(pct_complete * 100.0f));
8314 SDL_strlcpy(txt, XSTR("Mission file xfer", 829), SDL_arraysize(txt));
8319 SDL_strlcpy(txt, XSTR("Mission file xfer", 829), SDL_arraysize(txt));
8324 multi_sync_display_status(txt,count);
8327 nprintf(("Network","Unhandled player state : %d !\n",Net_players[idx].state));
8334 // display the mission start countdown timer (if any)
8335 anim_render_all(GS_STATE_MULTI_MISSION_SYNC,flFrametime);
8337 // process and show the chatbox thingie
8341 Multi_sync_window.draw_tooltip();
8343 // display the voice status indicator
8344 multi_common_voice_display_status();
8347 void multi_sync_check_buttons()
8350 for(idx=0;idx<Multi_sync_button_count;idx++){
8351 // we only really need to check for one button pressed at a time, so we can break after
8353 if(Multi_sync_buttons[gr_screen.res][idx].button.pressed()){
8354 multi_sync_button_pressed(idx);
8360 void multi_sync_button_pressed(int n)
8365 gamesnd_play_iface(SND_USER_SELECT);
8366 multi_quit_game(PROMPT_ALL);
8369 // scroll the info box up
8370 case MS_SCROLL_INFO_UP:
8371 multi_common_scroll_text_up();
8374 // scroll the info box down
8375 case MS_SCROLL_INFO_DOWN:
8376 multi_common_scroll_text_down();
8381 // if we have a currently selected player, kick him
8382 if(Multi_sync_player_select >= 0){
8383 multi_kick_player(Multi_sync_player_select);
8387 // start the final launch countdown (post-sync only)
8389 multi_sync_start_countdown();
8392 // doesn't do anything
8398 void multi_sync_pre_init()
8402 Netgame.game_state = NETGAME_STATE_MISSION_SYNC;
8404 // if we're in teamplay mode, always force skill level to be medium
8405 if((Netgame.type_flags & NG_TYPE_TEAM) && (Net_player->flags & NETINFO_FLAG_GAME_HOST)){
8406 Netgame.options.skill_level = NUM_SKILL_LEVELS / 2;
8407 Game_skill_level = NUM_SKILL_LEVELS / 2;
8408 multi_options_update_netgame();
8411 // notify everyone of when we get here
8412 if(!(Game_mode & GM_STANDALONE_SERVER)){
8413 Net_player->state = NETPLAYER_STATE_MISSION_SYNC;
8414 send_netplayer_update_packet();
8417 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8419 ml_string(NOX("Server performing pre-briefing data sync"));
8421 if(!(Game_mode & GM_STANDALONE_SERVER)){
8422 multi_common_set_text(XSTR("Server performing sync\n",830),1);
8425 // maybe initialize tvt and squad war stuff
8426 if(Netgame.type_flags & NG_TYPE_TEAM){
8427 multi_team_level_init();
8430 // force everyone into this state
8431 send_netgame_update_packet();
8433 if(!(Game_mode & GM_STANDALONE_SERVER)){
8434 multi_common_add_text(XSTR("Send update packet\n",831),1);
8437 // setup some of my own data
8438 Net_player->flags |= NETINFO_FLAG_MISSION_OK;
8440 // do any output stuff
8441 if(Game_mode & GM_STANDALONE_SERVER){
8442 std_debug_set_standalone_state_string("Mission Sync");
8445 // do this here to insure we have the most up to date file checksum info
8446 multi_get_mission_checksum(Game_current_mission_filename);
8447 // parse_get_file_signature(Game_current_mission_filename);
8449 if(!(Game_mode & GM_STANDALONE_SERVER)){
8450 multi_common_add_text(XSTR("Got file signatures\n",832),1);
8453 if(!(Game_mode & GM_STANDALONE_SERVER)){
8454 multi_common_add_text(XSTR("Sending update state packet\n",833),1);
8458 // if we're not in team vs. team mode - set all player teams to be 0, and unset all captaincy bits
8459 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
8460 for(idx=0;idx<MAX_PLAYERS;idx++){
8461 Net_players[idx].p_info.team = 0;
8462 Net_players[idx].flags &= ~(NETINFO_FLAG_TEAM_CAPTAIN);
8466 // we aren't necessarily xferring the mission file yet
8467 SDL_assert(Net_player->s_info.xfer_handle == -1);
8469 // always call this for good measure
8470 multi_campaign_flush_data();
8472 Mission_sync_flags = 0;
8473 Multi_mission_loaded = 0;
8476 void multi_sync_pre_do()
8480 // If I'm the server, wait for everyone to arrive in this state, then begin transferring data, etc.
8481 // all servers (standalone or no, go through this)
8482 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8483 // wait for everyone to arrive, then request filesig from all of them
8484 if(multi_netplayer_state_check(NETPLAYER_STATE_MISSION_SYNC) && !(Mission_sync_flags & MS_FLAG_SENT_FILESIG) && !multi_endgame_ending()){
8485 send_file_sig_request(Netgame.mission_name);
8486 Mission_sync_flags |= MS_FLAG_SENT_FILESIG;
8488 if(!(Game_mode & GM_STANDALONE_SERVER)){
8489 multi_common_add_text(XSTR("Sent filesig request\n",834),1);
8493 // if we're waiting for players to receive files, then check on their status
8494 if((Mission_sync_flags & MS_FLAG_SENT_FILESIG) && !multi_netplayer_flag_check(NETINFO_FLAG_MISSION_OK) && !multi_endgame_ending()){
8495 for(idx=0;idx<MAX_PLAYERS;idx++){
8496 // if this player is in the process of xferring a file
8497 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].s_info.xfer_handle != -1)){
8498 switch(multi_xfer_get_status(Net_players[idx].s_info.xfer_handle)){
8499 // if it has successfully completed, set his ok flag
8500 case MULTI_XFER_SUCCESS :
8502 Net_players[idx].flags |= NETINFO_FLAG_MISSION_OK;
8504 // release the xfer instance handle
8505 multi_xfer_release_handle(Net_players[idx].s_info.xfer_handle);
8506 Net_players[idx].s_info.xfer_handle = -1;
8508 // if it has failed or timed-out, kick the player
8509 case MULTI_XFER_TIMEDOUT:
8510 case MULTI_XFER_FAIL:
8511 // release the xfer handle
8512 multi_xfer_release_handle(Net_players[idx].s_info.xfer_handle);
8513 Net_players[idx].s_info.xfer_handle = -1;
8516 multi_kick_player(idx, 0, KICK_REASON_BAD_XFER);
8523 // NOTE : this is now obsolete
8524 // once everyone is verified, do any data transfer necessary
8525 if(multi_netplayer_flag_check(NETINFO_FLAG_MISSION_OK) && !(Mission_sync_flags & MS_FLAG_DATA_DONE) && !multi_endgame_ending()){
8526 // do nothing for now
8527 Mission_sync_flags |= MS_FLAG_DATA_DONE;
8529 // send campaign pool data
8530 multi_campaign_send_pool_status();
8533 // wait for everyone to ack on campaign pool data (even in non-campaign situations)
8534 if((Mission_sync_flags & MS_FLAG_DATA_DONE) && !(Mission_sync_flags & MS_FLAG_CAMP_DONE) && !multi_endgame_ending()){
8535 // check to see if everyone has acked the campaign pool data
8536 if(multi_netplayer_state_check(NETPLAYER_STATE_CPOOL_ACK)){
8537 Mission_sync_flags |= MS_FLAG_CAMP_DONE;
8541 // once everyone is verified, tell them to load the mission
8542 // also make sure to load the mission myself _AFTER_ telling everyone to do so. This makes the whole process
8543 // move along faster
8544 if((Mission_sync_flags & MS_FLAG_CAMP_DONE) && !(Mission_sync_flags & MS_FLAG_SENT_LOAD) && !multi_endgame_ending()){
8545 send_netplayer_load_packet(NULL);
8546 Mission_sync_flags |= MS_FLAG_SENT_LOAD;
8548 if(!(Game_mode & GM_STANDALONE_SERVER)){
8549 multi_common_add_text(XSTR("Sent load packet\n",835),1);
8552 // load the mission myself, as soon as possible
8553 if(!Multi_mission_loaded){
8554 nprintf(("Network","Server loading mission..."));
8556 // update everyone about my status
8557 Net_player->state = NETPLAYER_STATE_MISSION_LOADING;
8558 send_netplayer_update_packet();
8560 game_start_mission();
8562 nprintf(("Network","Done\n"));
8563 Multi_mission_loaded = 1;
8565 // update everyone about my status
8566 Net_player->state = NETPLAYER_STATE_MISSION_LOADED;
8567 send_netplayer_update_packet();
8569 if(!(Game_mode & GM_STANDALONE_SERVER)){
8570 multi_common_add_text(XSTR("Loaded mission locally\n",836),1);
8575 // if everyone has loaded the mission, randomly assign players to ships
8576 if(multi_netplayer_state_check(NETPLAYER_STATE_MISSION_LOADED) && !(Mission_sync_flags & MS_FLAG_TS_SLOTS) && !multi_endgame_ending()){
8577 // call the team select function to assign players to their ships, wings, etc
8578 multi_ts_assign_players_all();
8579 send_netplayer_slot_packet();
8582 Mission_sync_flags |= MS_FLAG_TS_SLOTS;
8585 // if everyone has loaded the mission, move to the team select stage
8586 if(Sync_test && multi_netplayer_state_check(NETPLAYER_STATE_SLOT_ACK) && !(Mission_sync_flags & MS_FLAG_PUSHED_BRIEFING) && !multi_endgame_ending()){
8587 Netgame.game_state = NETGAME_STATE_BRIEFING;
8588 send_netgame_update_packet(); // this will push everyone into the next state
8590 // the standalone moves to his own wait state, whereas in the normal game mode, the server/host moves in to the
8591 // team select state
8592 if(Game_mode & GM_STANDALONE_SERVER){
8593 gameseq_post_event(GS_EVENT_MULTI_STD_WAIT);
8595 gameseq_post_event(GS_EVENT_START_GAME);
8598 Mission_sync_flags |= MS_FLAG_PUSHED_BRIEFING;
8600 if(!(Game_mode & GM_STANDALONE_SERVER)){
8601 multi_common_add_text(XSTR("Moving to team select\n",837),1);
8605 // clients should detect here if they are doing a file xfer and do error processing
8606 if((Net_player->state == NETPLAYER_STATE_MISSION_XFER) && (Net_player->s_info.xfer_handle != -1) && !multi_endgame_ending()){
8607 switch(multi_xfer_get_status(Net_player->s_info.xfer_handle)){
8608 // if it has successfully completed, set his ok flag
8609 case MULTI_XFER_SUCCESS :
8610 // release my xfer handle
8611 multi_xfer_release_handle(Net_player->s_info.xfer_handle);
8612 Net_player->s_info.xfer_handle = -1;
8615 // if it has failed or timed-out, kick the player
8616 case MULTI_XFER_TIMEDOUT:
8617 case MULTI_XFER_FAIL:
8618 // release my xfer handle
8619 multi_xfer_release_handle(Net_player->s_info.xfer_handle);
8620 Net_player->s_info.xfer_handle = -1;
8622 // leave the game qith an error code
8623 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_XFER_FAIL);
8630 if(!(Game_mode & GM_STANDALONE_SERVER)){
8632 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8633 if(Multi_sync_bitmap != -1){
8634 gr_set_bitmap(Multi_sync_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8637 Multi_sync_window.draw();
8639 multi_sync_blit_screen_all();
8645 void multi_sync_pre_close()
8647 // at this point, we should shut down any file xfers...
8648 if(Net_player->s_info.xfer_handle != -1){
8649 nprintf(("Network","WARNING - killing file xfer while leaving mission sync state!!!\n"));
8651 multi_xfer_abort(Net_player->s_info.xfer_handle);
8652 Net_player->s_info.xfer_handle = -1;
8656 void multi_sync_post_init()
8658 multi_reset_timestamps();
8660 Multi_state_timestamp = timestamp(0);
8663 ml_string(NOX("Performing post-briefing data sync"));
8665 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8666 multi_common_add_text(XSTR("Server performing sync\n",830),1);
8668 multi_common_add_text(XSTR("Client performing sync\n",838),1);
8671 // everyone should re-initialize these
8672 init_multiplayer_stats();
8674 // reset all sequencing info
8675 multi_oo_reset_sequencing();
8677 // if I am not the master of the game, then send the firing information for my ship
8679 if ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) ){
8680 send_firing_info_packet();
8683 // if I'm not a standalone server, load up the countdown stuff
8684 if(!(Game_mode & GM_STANDALONE_SERVER)){
8685 Multi_sync_countdown_anim = NULL;
8686 Multi_sync_countdown_instance = NULL;
8687 Multi_sync_countdown_anim = anim_load(Multi_sync_countdown_fname[gr_screen.res]);
8688 if(Multi_sync_countdown_anim == NULL){
8689 nprintf(("General","WARNING!, Could not load countdown animation %s!\n",Multi_sync_countdown_fname[gr_screen.res]));
8693 // create objects for all permanent observers
8694 multi_obs_level_init();
8696 // clear the game start countdown timer
8697 Multi_sync_countdown_timer = -1.0f;
8698 Multi_sync_countdown = -1;
8700 // if this is a team vs. team mission, mark all ship teams appropriately
8701 if(Netgame.type_flags & NG_TYPE_TEAM){
8702 multi_team_mark_all_ships();
8705 Mission_sync_flags = 0;
8706 Multi_sync_launch_pressed = 0;
8709 #define MULTI_POST_TIMESTAMP 7000
8711 extern int create_wings();
8713 void multi_sync_post_do()
8717 // only if the host is also the master should he be doing this (non-standalone situation)
8718 if ( Net_player->flags & NETINFO_FLAG_AM_MASTER ) {
8720 // once everyone gets to this screen, send them the ship classes of all ships.
8721 if(multi_netplayer_state_check(NETPLAYER_STATE_WAITING) && !(Mission_sync_flags & MS_FLAG_POST_DATA)) {
8722 // only the host should ever do this
8723 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8724 // at this point we want to delete all necessary ships, change all necessary ship classes, and set all weapons up
8725 multi_ts_create_wings();
8727 // update player ets settings
8728 for(idx=0;idx<MAX_PLAYERS;idx++){
8729 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].player->objnum != -1)){
8730 multi_server_update_player_weapons(&Net_players[idx],&Ships[Objects[Net_players[idx].player->objnum].instance]);
8735 // note that this is done a little differently for standalones and nonstandalones
8736 send_post_sync_data_packet();
8738 multi_common_add_text(XSTR("Sending post briefing block information\n",839),1);
8740 Mission_sync_flags |= MS_FLAG_POST_DATA;
8743 // send weapon slots data
8744 if(multi_netplayer_state_check(NETPLAYER_STATE_POST_DATA_ACK) && !(Mission_sync_flags & MS_FLAG_WSS_SLOTS)) {
8745 // note that this is done a little differently for standalones and nonstandalones
8746 if(Netgame.type_flags & NG_TYPE_TEAM){
8747 send_wss_slots_data_packet(0,0);
8748 send_wss_slots_data_packet(1,1);
8750 send_wss_slots_data_packet(0,1);
8753 multi_common_add_text(XSTR("Sending weapon slots information\n",840),1);
8755 Mission_sync_flags |= MS_FLAG_WSS_SLOTS;
8758 // once weapon information is received, send player settings info
8759 if ( multi_netplayer_state_check(NETPLAYER_STATE_WSS_ACK) && !(Mission_sync_flags & MS_FLAG_PSETTINGS)) {
8760 send_player_settings_packet();
8762 // server (specifically, the standalone), should set this here
8763 Net_player->state = NETPLAYER_STATE_SETTINGS_ACK;
8764 send_netplayer_update_packet();
8766 multi_common_add_text(XSTR("Sending player settings packets\n",841),1);
8768 Mission_sync_flags |= MS_FLAG_PSETTINGS;
8771 // check to see if the countdown timer has started and act appropriately
8772 if( Multi_sync_countdown_timer > -1.0f ) {
8774 // increment by frametime.
8775 Multi_sync_countdown_timer += flFrametime;
8777 // if the animation is not playing, start it
8778 if(!(Game_mode & GM_STANDALONE_SERVER) && (Multi_sync_countdown_instance == NULL) && (Multi_sync_countdown_anim != NULL)){
8779 anim_play_struct aps;
8781 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]);
8782 aps.screen_id = GS_STATE_MULTI_MISSION_SYNC;
8783 aps.framerate_independent = 1;
8785 Multi_sync_countdown_instance = anim_play(&aps);
8788 // if the next second has expired
8789 if( Multi_sync_countdown_timer >= 1.0f ) {
8791 Multi_sync_countdown--;
8792 Multi_sync_countdown_timer = 0.0f;
8794 // if the countdown has reached 0, launch the mission
8795 if(Multi_sync_countdown == 0){
8796 Multi_sync_countdown_timer = -1.0f;
8798 Multi_sync_launch_pressed = 0;
8799 multi_sync_launch();
8801 // otherwise send a countdown packet
8803 send_countdown_packet(Multi_sync_countdown);
8808 // jump into the mission myself
8809 if((Multi_sync_countdown == 0) && multi_netplayer_state_check(NETPLAYER_STATE_IN_MISSION)){
8810 if(!((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !Multi_sync_launch_pressed)){
8811 Net_player->state = NETPLAYER_STATE_IN_MISSION;
8812 Netgame.game_state = NETGAME_STATE_IN_MISSION;
8813 gameseq_post_event(GS_EVENT_ENTER_GAME);
8815 multi_common_add_text(XSTR("Moving into game\n",842),1);
8819 // maybe start the animation countdown
8820 if((Multi_sync_countdown >= 0) && (Multi_sync_countdown_instance == NULL) && (Multi_sync_countdown_anim != NULL)){
8821 anim_play_struct aps;
8823 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]);
8824 aps.screen_id = GS_STATE_MULTI_MISSION_SYNC;
8825 aps.framerate_independent = 1;
8827 Multi_sync_countdown_instance = anim_play(&aps);
8831 // host - specific stuff
8832 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
8833 // create the launch button so the host can click
8834 if( Sync_test && multi_netplayer_state_check(NETPLAYER_STATE_SETTINGS_ACK) ){
8835 multi_sync_create_launch_button();
8840 if(!(Game_mode & GM_STANDALONE_SERVER)){
8842 GR_MAYBE_CLEAR_RES(Multi_sync_bitmap);
8843 if(Multi_sync_bitmap != -1){
8844 gr_set_bitmap(Multi_sync_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8847 Multi_sync_window.draw();
8849 multi_sync_blit_screen_all();
8855 void multi_sync_post_close()
8859 // if I'm not a standalone server, unload up the countdown stuff
8860 if(!(Game_mode & GM_STANDALONE_SERVER)){
8861 // release all rendering animation instances (should only be 1)
8862 anim_release_all_instances(GS_STATE_MULTI_MISSION_SYNC);
8863 Multi_sync_countdown_instance = NULL;
8865 // free up the countdown animation
8866 if(Multi_sync_countdown_anim != NULL){
8867 anim_free(Multi_sync_countdown_anim);
8868 Multi_sync_countdown_anim = NULL;
8872 // all players should reset sequencing
8873 for(idx=0;idx<MAX_PLAYERS;idx++){
8874 if(Net_player->flags & NETINFO_FLAG_CONNECTED){
8875 Net_players[idx].client_cinfo_seq = 0;
8876 Net_players[idx].client_server_seq = 0;
8880 // multiplayer dogfight
8881 multi_df_level_pre_enter();
8883 // clients should clear obj_pair array and add pair for themselves
8885 if ( MULTIPLAYER_CLIENT ) {
8887 obj_add_pairs( OBJ_INDEX(Player_obj) );
8892 void multi_sync_display_name(const char *name, int index, int np_index)
8894 char fit[CALLSIGN_LEN];
8896 // make sure the string actually fits
8897 SDL_strlcpy(fit, name, SDL_arraysize(fit));
8899 // if we're in team vs. team mode
8900 if(Netgame.type_flags & NG_TYPE_TEAM){
8901 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]);
8903 // if this is the currently selected player, draw him highlighted
8904 if(np_index == Multi_sync_player_select){
8905 gr_set_color_fast(&Color_text_selected);
8907 gr_set_color_fast(&Color_text_normal);
8911 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);
8913 // blit his team icon
8915 if(Net_players[np_index].p_info.team == 0){
8916 // blit the team captain icon
8917 if(Net_players[np_index].flags & NETINFO_FLAG_TEAM_CAPTAIN){
8918 if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
8919 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8920 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);
8923 // normal team member icon
8925 if(Multi_common_icons[MICON_TEAM0] != -1){
8926 gr_set_bitmap(Multi_common_icons[MICON_TEAM0], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8927 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 else if(Net_players[np_index].p_info.team == 1){
8933 // blit the team captain icon
8934 if(Net_players[np_index].flags & NETINFO_FLAG_TEAM_CAPTAIN){
8935 if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
8936 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8937 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);
8940 // normal team member icon
8942 if(Multi_common_icons[MICON_TEAM1] != -1){
8943 gr_set_bitmap(Multi_common_icons[MICON_TEAM1], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8944 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 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]);
8951 // if this is the currently selected player, draw him highlighted
8952 if(np_index == Multi_sync_player_select){
8953 gr_set_color_fast(&Color_text_selected);
8955 gr_set_color_fast(&Color_text_normal);
8959 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);
8962 // maybe blit his CD status icon
8963 if((Net_players[np_index].flags & NETINFO_FLAG_HAS_CD) && (Multi_common_icons[MICON_CD] != -1)){
8964 gr_set_bitmap(Multi_common_icons[MICON_CD], GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
8965 gr_bitmap(Ms_status_coords[gr_screen.res][MS_X_COORD], Ms_status_coords[gr_screen.res][MS_Y_COORD] + (index * 10));
8969 void multi_sync_display_status(const char *status, int index)
8973 // make sure the string actually fits
8974 SDL_strlcpy(fit, status, SDL_arraysize(fit));
8975 gr_force_fit_string(fit, 250, Ms_status2_coords[gr_screen.res][MS_W_COORD] - 20);
8976 gr_set_color_fast(&Color_bright);
8977 gr_string(Ms_status2_coords[gr_screen.res][MS_X_COORD], Ms_status2_coords[gr_screen.res][MS_Y_COORD] + (index * 10), fit);
8980 void multi_sync_force_start_pre()
8983 int want_state = NETPLAYER_STATE_SLOT_ACK; // kick any players who are still in this state
8985 // go through the player list and boot anyone who isn't in the right state
8986 for(idx=0;idx<MAX_PLAYERS;idx++){
8987 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx]) && (Net_players[idx].state == want_state)){
8988 multi_kick_player(idx,0);
8993 void multi_sync_force_start_post()
8997 int num_kill_states;
8999 // determine the state we want all players in so that we can find those who are not in the state
9000 kill_state[0] = NETPLAYER_STATE_BRIEFING;
9001 kill_state[1] = NETPLAYER_STATE_SHIP_SELECT;
9002 kill_state[2] = NETPLAYER_STATE_WEAPON_SELECT;
9003 num_kill_states = 3;
9005 // go through the player list and boot anyone who isn't in the right state
9006 for(idx=0;idx<MAX_PLAYERS;idx++){
9007 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx])){
9008 // check against all kill state
9009 for(idx2 = 0;idx2<num_kill_states;idx2++){
9010 if(Net_players[idx].state == kill_state[idx2]){
9011 multi_kick_player(idx,0);
9019 void multi_sync_start_countdown()
9021 // don't allow repeat button presses
9022 if(Multi_sync_launch_pressed){
9026 Multi_sync_launch_pressed = 1;
9028 // if I'm the server, begin the countdown
9029 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
9030 gamesnd_play_iface(SND_COMMIT_PRESSED);
9031 Multi_sync_countdown_timer = 0.0f;
9032 Multi_sync_countdown = MULTI_SYNC_COUNTDOWN_TIME;
9034 // send an initial countdown value
9035 send_countdown_packet(Multi_sync_countdown);
9037 // otherwise send the "start countdown" packet to the standalone
9039 SDL_assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
9040 send_countdown_packet(-1);
9044 void multi_sync_launch()
9046 // don't allow repeat button presses
9047 if(Multi_sync_launch_pressed){
9051 Multi_sync_launch_pressed = 1;
9054 ml_printf(NOX("Entering mission %s"), Game_current_mission_filename);
9056 // tell everyone to jump into the mission
9057 send_jump_into_mission_packet();
9058 Multi_state_timestamp = timestamp(MULTI_POST_TIMESTAMP);
9060 // set the # of players at the start of the mission
9061 Multi_num_players_at_start = multi_num_players();
9062 nprintf(("Network","# of players at start of mission : %d\n", Multi_num_players_at_start));
9064 // initialize datarate limiting for all clients
9065 multi_oo_rate_init_all();
9067 multi_common_add_text(XSTR("Sending mission start packet\n",843),1);
9070 void multi_sync_create_launch_button()
9072 if (!Multi_launch_button_created) {
9073 // create the object
9074 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);
9076 // set the sound to play when highlighted
9077 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_highlight_action(common_play_highlight_sound);
9079 // set the ani for the button
9080 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_bmaps(Multi_sync_buttons[gr_screen.res][MS_LAUNCH].filename, 3, 0);
9083 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.link_hotspot(Multi_sync_buttons[gr_screen.res][MS_LAUNCH].hotspot);
9086 Multi_sync_buttons[gr_screen.res][MS_LAUNCH].button.set_hotkey(KEY_CTRLED+SDLK_RETURN);
9089 // create the text for the button
9090 Multi_sync_window.add_XSTR(&Multi_sync_text[gr_screen.res][MST_LAUNCH]);
9093 // increment the button count so we start checking this one
9094 Multi_sync_button_count++;
9096 Multi_launch_button_created = 1;
9100 void multi_sync_handle_plist()
9106 // if we don't have a currently selected player, select one
9107 if((Multi_sync_player_select < 0) || !MULTI_CONNECTED(Net_players[Multi_sync_player_select])){
9108 for(idx=0;idx<MAX_PLAYERS;idx++){
9109 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
9110 Multi_sync_player_select = idx;
9116 // check for button list presses
9117 if(Multi_sync_plist_button.pressed()){
9118 // get the y mouse coords
9119 Multi_sync_plist_button.get_mouse_pos(NULL,&my);
9121 // get the index of the item selected
9122 select_index = my / 10;
9124 // if the index is greater than the current # connections, do nothing
9125 if(select_index > (multi_num_connections() - 1)){
9129 // translate into an absolute Net_players[] index (get the Nth net player)
9130 Multi_sync_player_select = -1;
9131 for(idx=0;idx<MAX_PLAYERS;idx++){
9132 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
9136 // if we've found the item we're looking for
9137 if(select_index < 0){
9138 Multi_sync_player_select = idx;
9143 // if for some bizarre reason, this is an invalid player, unselect him and wait for the next interation
9145 if((Multi_sync_player_select >= 0) && (!MULTI_CONNECTED(Net_players[Multi_sync_player_select]) || MULTI_STANDALONE(Net_players[Multi_sync_player_select])) ){
9146 Multi_sync_player_select = -1;
9152 // -------------------------------------------------------------------------------------------------------------
9154 // MULTIPLAYER DEBRIEF SCREEN
9157 // other relevant data
9158 int Multi_debrief_accept_hit;
9159 int Multi_debrief_replay_hit;
9161 // set if the server has left the game
9162 int Multi_debrief_server_left = 0;
9164 // if we've reported on TvT status all players are in the debrief
9165 int Multi_debrief_reported_tvt = 0;
9167 // whether stats are being accepted
9168 // -1 == no decision yet
9171 int Multi_debrief_stats_accept_code = -1;
9173 int Multi_debrief_server_framecount = 0;
9175 float Multi_debrief_time = 0.0f;
9176 float Multi_debrief_resend_time = 10.0f;
9178 void multi_debrief_init()
9182 Multi_debrief_time = 0.0f;
9183 Multi_debrief_resend_time = 10.0f;
9185 // do this to notify the standalone or the normal server that we're in the debrief state and ready to receive packets
9186 if (!(Net_player->flags & NETINFO_FLAG_AM_MASTER)) {
9187 Net_player->state = NETPLAYER_STATE_DEBRIEF;
9188 send_netplayer_update_packet();
9191 // unflag some stuff
9192 for(idx=0;idx<MAX_PLAYERS;idx++){
9193 if(MULTI_CONNECTED(Net_players[idx])){
9194 Net_players[idx].flags &= ~(NETINFO_FLAG_RESPAWNING | NETINFO_FLAG_LIMBO | NETINFO_FLAG_WARPING_OUT);
9198 // if text input mode is active, clear it
9199 multi_msg_text_flush();
9201 // the server has not left yet
9202 Multi_debrief_server_left = 0;
9204 // have not hit accept or replay yet
9205 Multi_debrief_accept_hit = 0;
9206 Multi_debrief_replay_hit = 0;
9208 // stats have not been accepted yet
9209 Multi_debrief_stats_accept_code = -1;
9211 // mark stats as not being store yet
9212 Netgame.flags &= ~(NG_FLAG_STORED_MT_STATS);
9214 // no report on TvT yet
9215 Multi_debrief_reported_tvt = 0;
9217 Multi_debrief_server_framecount = 0;
9220 void multi_debrief_do_frame()
9222 Multi_debrief_time += flFrametime;
9224 // set the netgame state to be debriefing when appropriate
9225 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)){
9226 Netgame.game_state = NETGAME_STATE_DEBRIEF;
9227 send_netgame_update_packet();
9230 // evaluate all server stuff
9231 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
9232 multi_debrief_server_process();
9236 void multi_debrief_close()
9238 if ( MULTIPLAYER_CLIENT && (Netgame.game_state == NETGAME_STATE_MISSION_SYNC) ){
9239 gamesnd_play_iface( SND_COMMIT_PRESSED );
9243 // handle optional mission loop
9244 void multi_maybe_set_mission_loop()
9246 int cur = Campaign.current_mission;
9247 if (Campaign.missions[cur].has_mission_loop) {
9248 SDL_assert(Campaign.loop_mission != CAMPAIGN_LOOP_MISSION_UNINITIALIZED);
9250 bool require_repeat_mission = (Campaign.current_mission == Campaign.next_mission);
9252 // check for (1) mission loop available, (2) dont have to repeat last mission
9253 if ( (Campaign.missions[cur].has_mission_loop && (Campaign.loop_mission != -1)) && !require_repeat_mission ) {
9256 debrief_assemble_optional_mission_popup_text(buffer, SDL_arraysize(buffer), Campaign.missions[cur].mission_loop_desc);
9258 int choice = popup(0 , 2, POPUP_NO, POPUP_YES, buffer);
9260 Campaign.loop_enabled = 1;
9261 Campaign.next_mission = Campaign.loop_mission;
9266 // handle all cases for when the accept key is hit in a multiplayer debriefing
9267 void multi_debrief_accept_hit()
9269 // if we already accepted, do nothing
9270 // but he may need to hit accept again after the server has left the game, so allow this
9271 if(Multi_debrief_accept_hit){
9275 // mark this so that we don't hit it again
9276 Multi_debrief_accept_hit = 1;
9278 gamesnd_play_iface(SND_COMMIT_PRESSED);
9280 // if the server has left the game, always just end the game.
9281 if(Multi_debrief_server_left){
9282 if(!multi_quit_game(PROMPT_ALL)){
9283 Multi_debrief_server_left = 1;
9284 Multi_debrief_accept_hit = 0;
9286 Multi_debrief_server_left = 0;
9289 // query the host and see if he wants to accept stats
9290 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
9291 // if we're on a tracker game, he gets no choice for storing stats
9292 if(MULTI_IS_TRACKER_GAME){
9293 multi_maybe_set_mission_loop();
9295 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));
9297 // evaluate the result
9302 Multi_debrief_accept_hit = 0;
9305 // set the accept code to be "not accepting"
9307 multi_debrief_stats_toss();
9308 multi_maybe_set_mission_loop();
9311 // accept the stats and continue
9313 multi_debrief_stats_accept();
9314 multi_maybe_set_mission_loop();
9320 // set my netplayer state to be "debrief_accept", and be done with it
9321 Net_player->state = NETPLAYER_STATE_DEBRIEF_ACCEPT;
9322 send_netplayer_update_packet();
9326 // handle all cases for when the escape key is hit in a multiplayer debriefing
9327 void multi_debrief_esc_hit()
9331 // if the server has left
9332 if(Multi_debrief_server_left){
9333 multi_quit_game(PROMPT_ALL);
9338 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
9339 // if the stats have already been accepted
9340 if((Multi_debrief_stats_accept_code != -1) || (MULTI_IS_TRACKER_GAME)){
9341 multi_quit_game(PROMPT_HOST);
9343 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));
9345 // evaluate the result
9352 // set the accept code to be "not accepting"
9354 multi_debrief_stats_toss();
9355 multi_quit_game(PROMPT_NONE);
9358 // accept the stats and continue
9360 multi_debrief_stats_accept();
9361 multi_quit_game(PROMPT_NONE);
9366 // if the stats haven't been accepted yet, or this is a tracker game
9367 if((Multi_debrief_stats_accept_code == -1) && !(MULTI_IS_TRACKER_GAME)){
9368 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));
9370 // evaluate the result
9372 multi_quit_game(PROMPT_NONE);
9375 // otherwise go through the normal endgame channels
9377 multi_quit_game(PROMPT_ALL);
9382 void multi_debrief_replay_hit()
9384 // only the host should ever get here
9385 SDL_assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
9387 // if the button was already pressed, do nothing
9388 if(Multi_debrief_accept_hit){
9392 // same as hittin the except button except no stats are kept
9393 Multi_debrief_accept_hit = 1;
9395 // mark myself as being in the replay state so we know what to do next
9396 Net_player->state = NETPLAYER_STATE_DEBRIEF_REPLAY;
9397 send_netplayer_update_packet();
9400 // call this when the server has left and we would otherwise be saying "contact lost with server
9401 void multi_debrief_server_left()
9404 Multi_debrief_server_left = 1;
9406 // undo any "accept" hit so that clients can hit accept again to leave
9407 Multi_debrief_accept_hit = 0;
9410 void multi_debrief_stats_accept()
9412 // don't do anything if we've already accepted
9413 if(Multi_debrief_stats_accept_code != -1){
9417 Multi_debrief_stats_accept_code = 1;
9419 // if we're the host, and we're on a standalone, tell the standalone to begin the stats storing process
9420 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) || (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
9421 // send a packet to the players telling them to store their stats
9422 send_store_stats_packet(1);
9425 // add a chat line saying "stats have been accepted"
9426 multi_display_chat_msg(XSTR("<stats have been accepted>",850),0,0);
9429 ml_string(NOX("Stats stored"));
9432 void multi_debrief_stats_toss()
9434 // don't do anything if we've already accepted
9435 if(Multi_debrief_stats_accept_code != -1){
9439 Multi_debrief_stats_accept_code = 0;
9441 // if we're the host, and we're on a standalone, tell everyone to "toss" stats
9442 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) || (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
9443 // send a packet to the players telling them to store their stats
9444 send_store_stats_packet(0);
9447 // add a chat line saying "stats have been accepted"
9448 multi_display_chat_msg(XSTR("<stats have been tossed>",851),0,0);
9451 ml_string(NOX("Stats tossed"));
9454 int multi_debrief_stats_accept_code()
9456 return Multi_debrief_stats_accept_code;
9459 void multi_debrief_server_process()
9462 int player_status,other_status;
9464 Multi_debrief_server_framecount++;
9466 // if we're > 10 seconds into the debrief and not everyone is here, try warping everyone out again
9467 if((Multi_debrief_time >= Multi_debrief_resend_time) && !multi_netplayer_state_check3(NETPLAYER_STATE_DEBRIEF, NETPLAYER_STATE_DEBRIEF_ACCEPT, NETPLAYER_STATE_DEBRIEF_REPLAY, 1)){
9468 // find all players who are not in the debrief state and hit them with the endgame packet
9469 for(idx=0; idx<MAX_PLAYERS; idx++){
9470 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)) ){
9471 send_endgame_packet(&Net_players[idx]);
9476 Multi_debrief_resend_time += 7.0f;
9479 // evaluate the status of all players in the game (0 == not ready, 1 == ready to continue, 2 == ready to replay)
9482 // check all players
9483 for(idx=0;idx<MAX_PLAYERS;idx++){
9484 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_HOST(Net_players[idx])){
9485 if(Net_players[idx].state != NETPLAYER_STATE_DEBRIEF_ACCEPT){
9492 // if we haven't already reported TvT results
9493 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)){
9494 multi_team_report();
9495 Multi_debrief_reported_tvt = 1;
9498 // if all other players are good to go, check the host
9500 // if he is ready to continue
9501 if(Netgame.host->state == NETPLAYER_STATE_DEBRIEF_ACCEPT){
9504 // if he wants to replay the mission
9505 else if(Netgame.host->state == NETPLAYER_STATE_DEBRIEF_REPLAY){
9508 // if he is not ready
9513 // if all players are _not_ good to go
9518 // if we're in the debriefing state in a campaign mode, process accordingly
9519 if(Netgame.campaign_mode == MP_CAMPAIGN){
9520 multi_campaign_do_debrief(player_status);
9522 // otherwise process as normal (looking for all players to be ready to go to the next mission
9524 if(player_status == 1){
9525 multi_flush_mission_stuff();
9527 // set the netgame state to be forming and continue
9528 Netgame.game_state = NETGAME_STATE_FORMING;
9529 send_netgame_update_packet();
9531 // move to the proper state
9532 if(Game_mode & GM_STANDALONE_SERVER){
9533 gameseq_post_event(GS_EVENT_STANDALONE_MAIN);
9535 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
9538 multi_reset_timestamps();
9539 } else if(player_status == 2){
9540 multi_flush_mission_stuff();
9542 // tell everyone to move into the pre-briefing sync state
9543 Netgame.game_state = NETGAME_STATE_MISSION_SYNC;
9544 send_netgame_update_packet();
9546 // move back to the mission sync screen for the same mission again
9547 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
9548 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
9550 multi_reset_timestamps();
9556 // -------------------------------------------------------------------------------------------------------------
9558 // MULTIPLAYER PASSWORD POPUP
9563 static const char *Multi_pwd_bitmap_fname[GR_NUM_RESOLUTIONS] = {
9564 "Password", // GR_640
9565 "2_Password" // GR_1024
9568 static const char *Multi_pwd_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
9569 "Password-M", // GR_640
9570 "2_Password-M" // GR_1024
9575 // constants for coordinate lookup
9576 #define MPWD_X_COORD 0
9577 #define MPWD_Y_COORD 1
9578 #define MPWD_W_COORD 2
9579 #define MPWD_H_COORD 3
9582 #define MULTI_PWD_NUM_BUTTONS 2
9583 #define MPWD_CANCEL 0
9584 #define MPWD_COMMIT 1
9586 // password area defs
9587 int Mpwd_coords[GR_NUM_RESOLUTIONS][4] = {
9600 UI_WINDOW Multi_pwd_window; // the window object for the join screen
9601 UI_INPUTBOX Multi_pwd_passwd; // for Netgame.passwd
9602 int Multi_pwd_bitmap; // the background bitmap
9603 int Multi_passwd_background = -1;
9604 int Multi_passwd_done = -1;
9605 int Multi_passwd_running = 0;
9608 ui_button_info Multi_pwd_buttons[GR_NUM_RESOLUTIONS][MULTI_PWD_NUM_BUTTONS] = {
9611 ui_button_info("PWB_00", 402, 134, -1, -1, 0),
9612 ui_button_info("PWB_01", 450, 134, -1, -1, 1),
9614 ui_button_info("PWB_00", 411, 151, 405, 141, 0),
9615 ui_button_info("PWB_01", 460, 151, 465, 141, 1),
9619 ui_button_info("2_PWB_00", 659, 242, 649, 225, 0),
9620 ui_button_info("2_PWB_01", 737, 242, 736, 225, 1),
9626 #define MULTI_PWD_NUM_TEXT 3
9628 UI_XSTR Multi_pwd_text[GR_NUM_RESOLUTIONS][MULTI_PWD_NUM_TEXT] = {
9630 { "Cancel", 387, 400, 141, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[0][MPWD_CANCEL].button},
9631 { "Commit", 1062, 455, 141, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[0][MPWD_COMMIT].button},
9632 { "Enter Password", 1332, 149, 92, UI_XSTR_COLOR_GREEN, -1, NULL},
9635 { "Cancel", 387, 649, 225, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[1][MPWD_CANCEL].button},
9636 { "Commit", 1062, 736, 225, UI_XSTR_COLOR_GREEN, -1, &Multi_pwd_buttons[1][MPWD_COMMIT].button},
9637 { "Enter Password", 1332, 239, 148, UI_XSTR_COLOR_GREEN, -1, NULL},
9642 // initialize all graphics, etc
9643 void multi_passwd_init()
9647 // store the background as it currently is
9648 Multi_passwd_background = gr_save_screen();
9650 // create the interface window
9651 Multi_pwd_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
9652 Multi_pwd_window.set_mask_bmap(Multi_pwd_bitmap_mask_fname[gr_screen.res]);
9654 // load the background bitmap
9655 Multi_pwd_bitmap = bm_load(Multi_pwd_bitmap_fname[gr_screen.res]);
9656 if(Multi_pwd_bitmap < 0){
9657 // we failed to load the bitmap - this is very bad
9661 // create the interface buttons
9662 for(idx=0; idx<MULTI_PWD_NUM_BUTTONS; idx++){
9663 // create the object
9664 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);
9666 // set the sound to play when highlighted
9667 Multi_pwd_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
9669 // set the ani for the button
9670 Multi_pwd_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pwd_buttons[gr_screen.res][idx].filename);
9673 Multi_pwd_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pwd_buttons[gr_screen.res][idx].hotspot);
9678 for(idx=0; idx<MULTI_PWD_NUM_TEXT; idx++){
9679 Multi_pwd_window.add_XSTR(&Multi_pwd_text[gr_screen.res][idx]);
9683 // create the password input box
9684 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);
9685 Multi_pwd_passwd.set_focus();
9687 // link the enter key to ACCEPT
9688 Multi_pwd_buttons[gr_screen.res][MPWD_COMMIT].button.set_hotkey(SDLK_RETURN);
9690 Multi_passwd_done = -1;
9691 Multi_passwd_running = 1;
9694 // close down all graphics, etc
9695 void multi_passwd_close()
9697 // unload any bitmaps
9698 bm_release(Multi_pwd_bitmap);
9700 // destroy the UI_WINDOW
9701 Multi_pwd_window.destroy();
9703 // free up the saved background screen
9704 if(Multi_passwd_background >= 0){
9705 gr_free_screen(Multi_passwd_background);
9706 Multi_passwd_background = -1;
9709 Multi_passwd_running = 0;
9712 // process any button pressed
9713 void multi_passwd_process_buttons()
9715 // if the accept button was pressed
9716 if(Multi_pwd_buttons[gr_screen.res][MPWD_COMMIT].button.pressed()){
9717 gamesnd_play_iface(SND_USER_SELECT);
9718 Multi_passwd_done = 1;
9721 // if the cancel button was pressed
9722 if(Multi_pwd_buttons[gr_screen.res][MPWD_CANCEL].button.pressed()){
9723 gamesnd_play_iface(SND_USER_SELECT);
9724 Multi_passwd_done = 0;
9728 // run the passwd popup
9729 void multi_passwd_do(char *passwd, const int max_passlen)
9733 while(Multi_passwd_done == -1){
9734 // set frametime and run background stuff
9735 game_set_frametime(-1);
9736 game_do_state_common(gameseq_get_state());
9738 k = Multi_pwd_window.process();
9740 // process any keypresses
9743 // set this to indicate the user has cancelled for one reason or another
9744 Multi_passwd_done = 0;
9748 // if the input box text has changed
9749 if(Multi_pwd_passwd.changed()){
9750 SDL_strlcpy(passwd, "", max_passlen);
9751 Multi_pwd_passwd.get_text(passwd);
9754 // process any button pressed
9755 multi_passwd_process_buttons();
9757 // draw the background, etc
9760 if(Multi_passwd_background >= 0){
9761 gr_restore_screen(Multi_passwd_background);
9763 gr_set_bitmap(Multi_pwd_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
9765 Multi_pwd_window.draw();
9772 // bring up the password string popup, fill in passwd (return 1 if accept was pressed, 0 if cancel was pressed)
9773 int multi_passwd_popup(char *passwd, const int max_plen)
9775 // if the popup is already running for some reason, don't do anything
9776 if(Multi_passwd_running){
9780 // initialize all graphics
9781 multi_passwd_init();
9784 multi_passwd_do(passwd, max_plen);
9786 // shut everything down
9787 multi_passwd_close();
9789 return Multi_passwd_done;