]> icculus.org git repositories - taylor/freespace2.git/blob - src/network/stand_gui.cpp
Initial revision
[taylor/freespace2.git] / src / network / stand_gui.cpp
1 /*
2  * $Logfile: /Freespace2/code/Network/stand_gui.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * $Log$
8  * Revision 1.1  2002/05/03 03:28:10  root
9  * Initial revision
10  *
11  * 
12  * 16    8/16/99 4:06p Dave
13  * Big honking checkin.
14  * 
15  * 15    8/11/99 5:54p Dave
16  * Fixed collision problem. Fixed standalone ghost problem.
17  * 
18  * 14    8/04/99 5:45p Dave
19  * Upped default standalone server framerate to 60.
20  * 
21  * 13    5/22/99 5:35p Dave
22  * Debrief and chatbox screens. Fixed small hi-res HUD bug.
23  * 
24  * 12    5/19/99 4:07p Dave
25  * Moved versioning code into a nice isolated common place. Fixed up
26  * updating code on the pxo screen. Fixed several stub problems.
27  * 
28  * 11    4/25/99 7:43p Dave
29  * Misc small bug fixes. Made sun draw properly.
30  * 
31  * 10    2/24/99 2:25p Dave
32  * Fixed up chatbox bugs. Made squad war reporting better. Fixed a respawn
33  * bug for dogfight more.
34  * 
35  * 9     2/18/99 11:46a Neilk
36  * hires interface coord support
37  * 
38  * 8     2/12/99 6:16p Dave
39  * Pre-mission Squad War code is 95% done.
40  * 
41  * 7     11/19/98 4:19p Dave
42  * Put IPX sockets back in psnet. Consolidated all multiplayer config
43  * files into one.
44  * 
45  * 6     11/17/98 11:12a Dave
46  * Removed player identification by address. Now assign explicit id #'s.
47  * 
48  * 5     11/05/98 5:55p Dave
49  * Big pass at reducing #includes
50  * 
51  * 4     10/09/98 2:57p Dave
52  * Starting splitting up OS stuff.
53  * 
54  * 3     10/08/98 4:29p Dave
55  * Removed reference to osdefs.h
56  * 
57  * 2     10/07/98 10:53a Dave
58  * Initial checkin.
59  * 
60  * 1     10/07/98 10:50a Dave
61  * 
62  * 63    9/17/98 11:56a Allender
63  * allow use of return key in edit box
64  * 
65  * 62    9/04/98 3:52p Dave
66  * Put in validated mission updating and application during stats
67  * updating.
68  * 
69  * 61    7/24/98 9:27a Dave
70  * Tidied up endgame sequencing by removing several old flags and
71  * standardizing _all_ endgame stuff with a single function call.
72  * 
73  * 60    7/10/98 5:04p Dave
74  * Fix connection speed bug on standalone server.
75  * 
76  * 59    6/18/98 4:46p Allender
77  * removed test code that I previously forgot to remove
78  * 
79  * 58    6/18/98 3:52p Allender
80  * make close button actually close down property sheet
81  * 
82  * 57    6/13/98 6:02p Hoffoss
83  * Externalized all new (or forgot to be added) strings to all the code.
84  * 
85  * 56    6/13/98 3:19p Hoffoss
86  * NOX()ed out a bunch of strings that shouldn't be translated.
87  * 
88  * 55    6/10/98 2:56p Dave
89  * Substantial changes to reduce bandwidth and latency problems.
90  * 
91  * 54    5/24/98 3:45a Dave
92  * Minor object update fixes. Justify channel information on PXO. Add a
93  * bunch of configuration stuff for the standalone.
94  * 
95  * 53    5/22/98 9:35p Dave
96  * Put in channel based support for PXO. Put in "shutdown" button for
97  * standalone. UI tweaks for TvT
98  * 
99  * 52    5/21/98 9:45p Dave
100  * Lengthened tracker polling times. Put in initial support for PXO
101  * servers with channel filters. Fixed several small UI bugs.
102  * 
103  * 51    5/18/98 9:15p Dave
104  * Put in network config file support.
105  * 
106  * 50    5/15/98 5:16p Dave
107  * Fix a standalone resetting bug.Tweaked PXO interface. Display captaincy
108  * status for team vs. team. Put in asserts to check for invalid team vs.
109  * team situations.
110  * 
111  * 49    5/15/98 3:36p John
112  * Fixed bug with new graphics window code and standalone server.  Made
113  * hwndApp not be a global anymore.
114  * 
115  * 48    5/10/98 7:06p Dave
116  * Fix endgame sequencing ESC key. Changed how host options warning popups
117  * are done. Fixed pause/message scrollback/options screen problems in mp.
118  * Make sure observer HUD doesn't try to lock weapons.
119  * 
120  * 47    5/09/98 7:16p Dave
121  * Put in CD checking. Put in standalone host password. Made pilot into
122  * popup scrollable.
123  * 
124  * 46    5/08/98 7:09p Dave
125  * Lots of UI tweaking.
126  * 
127  * 45    5/08/98 5:05p Dave
128  * Go to the join game screen when quitting multiplayer. Fixed mission
129  * text chat bugs. Put mission type symbols on the create game list.
130  * Started updating standalone gui controls.
131  * 
132  * 44    5/04/98 1:44p Dave
133  * Fixed up a standalone resetting problem. Fixed multiplayer stats
134  * collection for clients. Make sure all multiplayer ui screens have the
135  * correct palette at all times.
136  * 
137  * 43    5/02/98 5:38p Dave
138  * Put in new tracker API code. Put in ship information on mp team select
139  * screen. Make standalone server name permanent. Fixed standalone server
140  * text messages.
141  * 
142  * 42    5/02/98 1:45a Dave
143  * Make standalone goal tree not flicker.
144  * 
145  * 41    5/01/98 10:57a Jim
146  * from Dave:  fixed mission goal problems
147  * 
148  * 40    4/29/98 12:11a Dave
149  * Put in first rev of full API support for new master tracker.
150  * 
151  * 39    4/28/98 5:11p Dave
152  * Fixed multi_quit_game() client side sequencing problem. Turn off
153  * afterburners when ending multiplayer mission. Begin integration of mt
154  * API from Kevin Bentley.
155  * 
156  * 38    4/25/98 2:02p Dave
157  * Put in multiplayer context help screens. Reworked ingame join ship
158  * select screen. Fixed places where network timestamps get hosed.
159  * 
160  * 37    4/06/98 6:37p Dave
161  * Put in max_observers netgame server option. Make sure host is always
162  * defaulted to alpha 1 or zeta 1. Changed create game so that MAX_PLAYERS
163  * can always join but need to be kicked before commit can happen. Put in
164  * support for server ending a game and notifying clients of a special
165  * condition.
166  * 
167  * 36    3/31/98 5:18p John
168  * Removed demo/save/restore.  Made NDEBUG defined compile.  Removed a
169  * bunch of debug stuff out of player file.  Made model code be able to
170  * unload models and malloc out only however many models are needed.
171  *  
172  * 
173  * 35    3/24/98 5:00p Dave
174  * Fixed several ui bugs. Put in pre and post voice stream playback sound
175  * fx. Put in error specific popups for clients getting dropped from games
176  * through actions other than their own.
177  * 
178  * 34    3/19/98 5:05p Dave
179  * Put in support for targeted multiplayer text and voice messaging (all,
180  * friendly, hostile, individual).
181  * 
182  * 33    3/17/98 5:29p Dave
183  * Minor bug fixes in player select menu. Solidified mp joining process.
184  * Made furball mode support ingame joiners and dropped players correctly.
185  * 
186  * 32    3/15/98 4:17p Dave
187  * Fixed oberver hud problems. Put in handy netplayer macros. Reduced size
188  * of network orientation matrices.
189  * 
190  * 31    3/03/98 5:12p Dave
191  * 50% done with team vs. team interface issues. Added statskeeping to
192  * secondary weapon blasts. Numerous multiplayer ui bug fixes.
193  * 
194  * 30    2/12/98 4:41p Dave
195  * Seperated multiplayer kick functionality into its own module. Ui
196  * tweaking
197  * 
198  * 29    2/05/98 10:24a Hoffoss
199  * Changed "goal" text to "objective", which is the correct term nowadays.
200  * 
201  * 28    1/31/98 4:32p Dave
202  * Put in new support for VMT player validation, game logging in, and game
203  * logging out. Need to finish stats transfer.
204  * 
205  * 27    1/28/98 6:24p Dave
206  * Made standalone use ~8 megs less memory. Fixed multiplayer submenu
207  * sequencing problem.
208  * 
209  * 26    1/24/98 3:39p Dave
210  * Fixed numerous multiplayer bugs (last frame quit problem, weapon bank
211  * changing, deny packets). Add several controls to standalone server.
212  * 
213  * 25    1/20/98 2:23p Dave
214  * Removed optimized build warnings. 99% done with ingame join fixes.
215  * 
216  * 24    1/17/98 2:46a Dave
217  * Reworked multiplayer join/accept process. Ingame join still needs to be
218  * integrated.
219  * 
220  * 23    1/16/98 2:34p Dave
221  * Made pause screen work properly (multiplayer). Changed how chat packets
222  * work.
223  * 
224  * 22    1/13/98 5:37p Dave
225  * Reworked a lot of standalone interface code. Put in single and
226  * multiplayer popups for death sequence. Solidified multiplayer kick
227  * code.
228  * 
229  * 21    1/11/98 10:03p Allender
230  * removed <winsock.h> from headers which included it.  Made psnet_socket
231  * type which is defined just as SOCKET type is.
232  * 
233  * 20    1/05/98 5:07p Dave
234  * Fixed a chat packet bug. Fixed a few state save/restore bugs. Updated a
235  * few things for multiplayer server transfer.
236  * 
237  * 19    12/10/97 4:46p Dave
238  * Added in more detailed support for multiplayer packet lag/loss. Fixed
239  * some multiplayer stuff. Added some controls to the standalone.
240  * 
241  * 18    12/03/97 11:50p Dave
242  * Fixed a bunch of multiplayer bugs (standalone and non)
243  * 
244  * 17    12/03/97 11:59a Dave
245  * Dependant merge checkin
246  * 
247  * 16    12/02/97 10:05p Dave
248  * Began some large-scale multiplayer debugging work (mostly standalone)
249  * 
250  * 15    11/15/97 2:37p Dave
251  * More multiplayer campaign support.
252  * 
253  * 14    10/29/97 5:18p Dave
254  * More debugging of server transfer. Put in debrief/brief 
255  * transition for multiplayer (w/standalone)
256  * 
257  * 13    10/25/97 7:23p Dave
258  * Moved back to single set stats storing. Put in better respawning
259  * locations system.
260  * 
261  * 12    10/24/97 6:19p Dave
262  * More standalone testing/fixing. Added reliable endgame sequencing.
263  * Added reliable ingame joining. Added reliable stats transfer (endgame).
264  * Added support for dropping players in debriefing. Removed a lot of old
265  * unused code.
266  * 
267  * 11    10/21/97 5:21p Dave
268  * Fixed pregame mission load/file verify debacle. Added single vs.
269  * multiplayer stats system.
270  * 
271  * 10    10/14/97 5:38p Dave
272  * Player respawns 99.9% done. Only need to check cases for server/host
273  * getting killed.
274  * 
275  * 9     10/03/97 4:57p Dave
276  * Added functions for new text controls. Added some more reset controls.
277  * Put in checks for all-players-gone.
278  * 
279  * 8     9/17/97 9:09a Dave
280  * Observer mode works, put in standalone controls. Fixed up some stuff for
281  * ingame join broken by recent code checkins.
282  * 
283  * 7     8/29/97 5:03p Dave
284  * Added a ton of new gui controls/features.
285  * 
286  * 6     8/26/97 5:03p Dave
287  * Added bunch of informational controls. Standardized some functions for
288  * external use. Put in godview mode (conditionaled out though).
289  * 
290  * 5     8/23/97 11:31a Dave
291  * Put in new gui calls. Added a bunch of display controls.
292  * 
293  * 4     8/20/97 4:19p Dave
294  * Moved some functions around. Added the standalone state text box.
295  * 
296  * 3     8/18/97 11:46a Dave
297  * Moved definition of STANDALONE_FRAME_CAP tp multi.h
298  * 
299  * 2     8/11/97 4:52p Dave
300  * Spliced out standalone GUI stuff from OsApi and WinMain.cpp to its own
301  * module.
302  * 
303  * 1     8/11/97 4:21p Dave
304  * 
305  * $NoKeywords: $
306  */
307
308 #include <windows.h>
309 #include <windowsx.h>
310 #include <commctrl.h>
311 #include <stdio.h>
312 #include <io.h>
313 #include <fcntl.h>
314 #include <stdarg.h>
315
316 #include "vecmat.h"
317 #include "tmapper.h"
318 #include "2d.h"
319 #include "3d.h"
320 #include "model.h"
321 #include "bmpman.h"
322 #include "lighting.h"
323 #include "linklist.h"
324 #include "freespace.h"
325
326 #include "stand_gui.h"
327 #include "pstypes.h"
328 #include "osapi.h"
329 #include "key.h"
330 #include "palman.h"
331 #include "mouse.h"
332 #include "outwnd.h"
333 #include "2d.h"
334 #include "cfile.h"
335 #include "sound.h"
336 #include "freespaceresource.h"
337 #include "multi.h"
338 #include "multimsgs.h"
339 #include "multiutil.h"
340 #include "missiongoals.h"
341 #include "systemvars.h"
342 #include "cmdline.h"
343 #include "multi_kick.h"
344 #include "multi_pmsg.h"
345 #include "chatbox.h"
346 #include "multi_endgame.h"
347 #include "gamesequence.h"
348 #include "osregistry.h"
349 #include "timer.h"
350 #include "version.h"
351
352 HANDLE Standalone_thread;
353 DWORD Standalone_thread_id;
354 static HWND Standalone_hwnd = NULL;
355
356 // -----------------------------------------------------------------------------------------
357 // standalone global defs
358
359 #define MAX_STANDALONE_PAGES 5
360
361 #define STD_STATS_UPDATE_TIME                   500             // ms between updating player stats on the visible controls
362 #define STD_NG_UPDATE_TIME                              100             // ms between updating netgame information are controls
363
364 // coords for the "shutdown" button (client window coords)
365 static int Std_shutdown_coords[GR_NUM_RESOLUTIONS][2] = {
366         { 130, 450 },   // GR_640
367         { 130, 450 }    // GR_640
368 };
369
370 // you should reference Page_handles and Pages with these defines from now on
371 #define CONNECT_PAGE                    0
372 #define MULTIPLAYER_PAGE        1
373 #define PLAYER_INFO_PAGE   2
374 #define GODSTUFF_PAGE      3
375 #define DEBUG_PAGE         4
376
377 // standalone gui property sheet stuff
378 static HWND Psht;
379 static HWND Page_handles[MAX_STANDALONE_PAGES];
380 static PROPSHEETPAGE Pages[MAX_STANDALONE_PAGES];
381 static PROPSHEETHEADER Sheet;
382
383 // index into Page_handles[] representing the currently selected page
384 static int Active_standalone_page;
385
386 // timestamp for updating currently selected player stats on the player info page
387 int Standalone_stats_stamp;
388
389 // timestamp for updating the netgame information are text controls
390 int Standalone_ng_stamp;
391
392 // banned player callsigns
393 #define STANDALONE_MAX_BAN                      50
394 char Standalone_ban_list[STANDALONE_MAX_BAN][CALLSIGN_LEN+1];
395 int Standalone_ban_count = 0;
396
397 // ----------------------------------------------------------------------------------------
398 // mission validation dialog
399 //
400
401 static HWND Multi_gen_dialog = NULL;            // the dialog itself
402
403 // dialog proc for this dialog
404 BOOL CALLBACK std_gen_dialog_proc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
405 {
406         switch(uMsg){           
407         case WM_INITDIALOG:
408                 return TRUE;
409
410         // destory command
411         case WM_DESTROY:
412                 Multi_gen_dialog = NULL;
413                 break;
414         }
415         return FALSE;
416 }
417
418 // create the validate dialog 
419 void std_create_gen_dialog(char *title)
420 {
421         // if the dialog is already active, do nothing
422         if(Multi_gen_dialog != NULL){
423                 return;
424         }
425
426         // otherwise create the dialog
427         Multi_gen_dialog = CreateDialog(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_GEN), Psht, (DLGPROC)std_gen_dialog_proc);   
428         if(Multi_gen_dialog != NULL){
429                 SetWindowText(Multi_gen_dialog, title);         
430         }
431 }
432
433 // kill the validate dialog();
434 void std_destroy_gen_dialog()
435 {
436         // if the dialog is not active, do nothing
437         if(Multi_gen_dialog == NULL){
438                 return;
439         }
440
441         // kill it
442         DestroyWindow(Multi_gen_dialog);
443         Multi_gen_dialog = NULL;
444 }
445
446 // set the text in the filename of the validate dialog
447 // valid values for field_num == 0 .. 2
448 void std_gen_set_text(char *str, int field_num)
449 {
450         HWND ctrl;
451
452         // if the dialog is not active
453         if((Multi_gen_dialog == NULL) || (str == NULL) || (field_num < 0) || (field_num > 2)){
454                 return;
455         }
456
457         // otherwise set the text
458         ctrl = GetDlgItem(Multi_gen_dialog, (int)MAKEINTRESOURCE(IDC_FIELD1));
459         switch(field_num){
460         case 0:
461                 ctrl = GetDlgItem(Multi_gen_dialog,(int)MAKEINTRESOURCE(IDC_FIELD1));
462                 break;
463         case 1:
464                 ctrl = GetDlgItem(Multi_gen_dialog,(int)MAKEINTRESOURCE(IDC_FIELD2));
465                 break;
466         case 2:
467                 ctrl = GetDlgItem(Multi_gen_dialog,(int)MAKEINTRESOURCE(IDC_FIELD3));
468                 break;  
469         }
470         SetWindowText(ctrl, str);
471 }
472
473 // is the validate dialog active
474 int std_gen_is_active()
475 {
476         return Multi_gen_dialog != NULL;
477 }
478
479 // ----------------------------------------------------------------------------------------
480 // connection page/tab functions
481 //
482 static HWND Multi_std_name;                                     // standalone name text edit control
483 HWND    Multi_std_host_passwd;                                  // host password text control
484 int Multi_std_namechange_force;
485
486 // convert the index of an item in the list box into an index into the net players array
487 int std_connect_lindex_to_npindex(int index);
488
489 // set the text box indicating how many players are connected, returning the determined count
490 int std_connect_set_connect_count()
491 {
492         HWND ctrl;
493         char str[40];
494         char val[10];   
495         int idx,count;
496
497         // setup the text string
498         strcpy(str,XSTR("# Connections : ",911));
499
500         // determine how many players are actually connected
501         count = 0;
502         for(idx=0;idx<MAX_PLAYERS;idx++){
503                 if(MULTI_CONNECTED(Net_players[idx]) && (Net_player != &Net_players[idx])){
504                         count++;
505                 }
506         }
507         
508         // tack on the player count to the end of the string
509         sprintf(val,"%d",count);
510         strcat(str,val);
511
512         // set the text itself
513    ctrl = GetDlgItem(Page_handles[CONNECT_PAGE],(int)MAKEINTRESOURCE(IDC_CON_COUNT));
514    SetWindowText(ctrl,str);
515
516         // return the num of players found
517         return count;
518 }
519
520 // set the connect status (connected or not) of the game host
521 void std_connect_set_host_connect_status()
522 {
523         int idx,found;
524         HWND ctrl;
525
526         // first try and find the host
527         found = 0;
528         for(idx=0;idx<MAX_PLAYERS;idx++){
529                 if(MULTI_CONNECTED(Net_players[idx]) && MULTI_HOST(Net_players[idx])){
530                         found = 1;
531                         break;
532                 }
533         }
534
535         // get the control and set the status
536         ctrl = GetDlgItem(Page_handles[CONNECT_PAGE],(int)MAKEINTRESOURCE(IDC_HOST_IS));
537         if(found){
538                 SetWindowText(ctrl, XSTR("Host connected ? Yes",912));
539         } else {
540                 SetWindowText(ctrl, XSTR("Host connected ? No",913));
541         }
542 }
543
544 // add an ip string to the connect page listbox
545 void std_connect_add_ip_string(char *string)
546 {
547    HWND ctrl;
548         
549         // add the item
550         ctrl = GetDlgItem(Page_handles[CONNECT_PAGE], (int)MAKEINTRESOURCE(IDC_CONPING));
551         SendMessage(ctrl, LB_ADDSTRING, (WPARAM)0, (LPARAM)(LPCTSTR)string);
552 }
553
554 // remove an ip string from the connect page listbox
555 void std_connect_remove_ip_string(char *string)
556 {
557         HWND ctrl;      
558         int loc;
559         
560         // get the control handle
561         ctrl = GetDlgItem(Page_handles[CONNECT_PAGE], (int)MAKEINTRESOURCE(IDC_CONPING));
562
563         // NOTE the use of FINDSTRING and _not_ FINDSTRINGEXACT !!
564         // since we've appended the ping to the end of the string, we can only check the 
565         // "prefix" which is the net_players name
566         loc = SendMessage(ctrl, LB_FINDSTRING, (WPARAM)-1, (LPARAM)(LPCTSTR)string);
567
568         if(loc!=LB_ERR){
569                 SendMessage(ctrl, LB_DELETESTRING, (WPARAM)loc, (LPARAM)0);
570         }
571 }
572
573 // set an ip string on the connect page listbox
574 void std_connect_set_ip_string(char *lookup,char *string)
575 {
576         HWND ctrl;
577         int loc;
578
579         // get the control handle
580         ctrl = GetDlgItem(Page_handles[CONNECT_PAGE],(int)MAKEINTRESOURCE(IDC_CONPING));
581  
582         // NOTE the use of FINDSTRING and _not_ FINDSTRINGEXACT !!
583         // since we've appended the ping to the end of the string, we can only check the 
584         // "prefix" which is the net_players name
585         loc = SendMessage(ctrl,LB_FINDSTRING,(WPARAM)-1,(LPARAM)(LPCTSTR)lookup);
586
587         if(loc!=LB_ERR){
588                 SendMessage(ctrl,LB_DELETESTRING,(WPARAM)loc,(LPARAM)0);
589                 SendMessage(ctrl,LB_INSERTSTRING,(WPARAM)loc,(LPARAM)string);
590         }
591 }
592
593 void std_connect_kick_player()
594 {               
595         int player_num,sel;
596         HWND ctrl;      
597
598         // get the control handle
599         ctrl = GetDlgItem(Page_handles[CONNECT_PAGE],(int)MAKEINTRESOURCE(IDC_CONPING));
600
601         sel = SendMessage(ctrl,LB_GETCURSEL,(WPARAM)0,(LPARAM)0);
602         // attempt to get the player index
603         if(sel != CB_ERR){
604                 player_num = std_connect_lindex_to_npindex(sel);
605
606                 // if we found him, then kick the bastard
607                 if(player_num != -1){
608                         multi_kick_player(player_num,0);
609                 }
610         }
611 }
612
613 // update the ping for this particular player
614 void std_connect_update_ping(net_player *p)
615 {
616         char str[40],ping[10],sml_ping[10],lookup[50];
617
618         // as long as his ping is not -1, do an update
619         if(p->s_info.ping.ping_avg > -1){       
620                 // get the lookup string
621                 psnet_addr_to_string(lookup,&p->p_info.addr);
622                 
623                 // build the string to replace the ping with
624                 strcpy(str,lookup); 
625                 strcat(str,", "); 
626                 
627                 // chop it off at pings greater than 1 second
628                 if(p->s_info.ping.ping_avg > 1000){
629                         strcat(str,XSTR("> 1 sec",914));
630                         strcpy(sml_ping,XSTR("> 1 sec",914));
631                 }
632                 else {
633                         sprintf(ping,"%d",p->s_info.ping.ping_avg); 
634                         strcat(str,ping);
635                         strcat(str,XSTR(" ms",915));
636                         strcpy(sml_ping,ping); strcat(sml_ping,XSTR(" ms",915));
637                 }
638
639                 // set the string
640                 std_connect_set_ip_string(lookup,str);
641         }
642 }
643
644 // clear all the controls for this page
645 void std_connect_clear_controls()
646 {
647         HWND handle;
648
649         // set various connect counts
650         std_connect_set_connect_count();
651         std_connect_set_host_connect_status();
652
653         // reset the list of players and pings
654         handle = GetDlgItem(Page_handles[CONNECT_PAGE],(int)MAKEINTRESOURCE(IDC_CONPING));
655         SendMessage(handle,LB_RESETCONTENT,(WPARAM)0,(LPARAM)0);
656 }
657
658 // set the game name for the standalone. passing NULL uses the default
659 void std_connect_set_gamename(char *name)
660 {
661         char buf[MAX_GAMENAME_LEN+1];
662
663         // use the default name for now
664         if(name == NULL){
665                 // if a permanent name exists, use that instead of the default
666                 if(strlen(Multi_options_g.std_pname)){
667                         strcpy(Netgame.name, Multi_options_g.std_pname);
668                 } else {
669                         strcpy(Netgame.name,XSTR("Standalone Server",916));
670                 }
671         } else {
672                 strcpy(Netgame.name,name);
673         }
674
675         // update the text control
676         strcpy(buf,Netgame.name);
677         Multi_std_namechange_force = 0;
678         SetWindowText(Multi_std_name,buf);
679         Multi_std_namechange_force = 1;
680 }
681
682 // the user has changed the text in the server name text box. handle this
683 void std_connect_handle_name_change()
684 {
685         char buf[MAX_GAMENAME_LEN+2];
686         int max_len = MAX_GAMENAME_LEN+2;
687
688         if(Multi_std_namechange_force){
689                 memset(buf,0,MAX_GAMENAME_LEN+2);
690                 memcpy(&buf[0],&max_len,sizeof(int));
691
692                 // get the new text
693                 SendMessage(Multi_std_name,EM_GETLINE,(WPARAM)0,(LPARAM)(LPCSTR)buf);
694
695                 // just copy it over for now. we may want to process this more later on
696                 strcpy(Netgame.name,buf);
697
698                 // copy it to the permanent name
699                 strcpy(Multi_options_g.std_pname, buf);
700         }
701 }
702
703 // the user has changed the text in the host password text box
704 void std_connect_handle_passwd_change()
705 {
706         char buf[STD_PASSWD_LEN+2];
707         int max_len = STD_PASSWD_LEN+2;
708         
709         memset(buf,0,STD_PASSWD_LEN+2);
710         memcpy(&buf[0],&max_len,sizeof(int));
711
712         // get the new text
713         SendMessage(Multi_std_host_passwd,EM_GETLINE,(WPARAM)0,(LPARAM)(LPCSTR)buf);
714
715         // just copy it over for now. we may want to process this more later on
716         strcpy(Multi_options_g.std_passwd, buf);
717 }
718
719 // convert the index of an item in the list box into an index into the net players array
720 int std_connect_lindex_to_npindex(int index)
721 {
722         HWND ctrl;                      
723         char list_text[40];     
724         char addr_text[40];
725         int ret,idx;
726
727         // get the control handle
728         ctrl = GetDlgItem(Page_handles[CONNECT_PAGE],(int)MAKEINTRESOURCE(IDC_CONPING));
729
730         // get the string contained at a given index    
731         SendMessage(ctrl,LB_GETTEXT,(WPARAM)index,(LPARAM)(LPSTR)list_text);
732
733         // look through the net players array and compare address strings (yuck)
734         ret = -1;
735         for(idx=0;idx<MAX_PLAYERS;idx++){
736                 // only look at connected players
737                 if(MULTI_CONNECTED(Net_players[idx])){
738                         strcpy(addr_text,"");
739                         psnet_addr_to_string(addr_text,&Net_players[idx].p_info.addr);
740
741                         // if we found the match
742                         if((strlen(addr_text) != 0) && (strstr(list_text,addr_text) != NULL)){
743                                 ret = idx;
744                                 break;
745                         }
746                 }
747         }
748
749         return ret;
750 }
751
752 // message handler for the connect tab
753 BOOL CALLBACK connect_proc(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)
754 {
755    switch(uMsg){
756         // initialize the dialog
757         case WM_INITDIALOG:
758                 // setup the page handle array for this page
759                 Page_handles[CONNECT_PAGE] = hwndDlg;           
760                 
761                 // create the standalone name text box and limit its text length
762                 Multi_std_name = GetDlgItem(hwndDlg, (int)MAKEINTRESOURCE(IDC_STD_NAME));
763                 SendMessage(Multi_std_name, EM_SETLIMITTEXT, (WPARAM)MAX_GAMENAME_LEN-1, (LPARAM)0);
764                 Multi_std_namechange_force = 1;
765
766                 // create the standalone host password input box
767                 Multi_std_host_passwd = GetDlgItem(hwndDlg, (int)MAKEINTRESOURCE(IDC_STD_HOST_PASSWD));
768                 SendMessage(Multi_std_host_passwd, EM_SETLIMITTEXT, (WPARAM)STD_PASSWD_LEN, (LPARAM)0);
769                 memset(Multi_options_g.std_passwd, 0, STD_PASSWD_LEN+1);
770
771                 return 1;      
772
773         // process a command of some kind (usually button presses)
774         case WM_COMMAND:
775                 switch(HIWORD(wParam)){
776                 // a button press
777                 case BN_CLICKED :
778                         switch(LOWORD(wParam)){                 
779                         // the reset standalone button
780                         case IDC_RESET_MULTI : 
781                                 // multi_standalone_quit_game();
782                                 multi_quit_game(PROMPT_NONE);
783                                 break;
784                         
785                         // kick the currently selected player
786                         case IDC_KICK_BUTTON :                                                          
787                                 std_connect_kick_player();
788                                 break;
789                         
790                         // refresh file list (PXO)
791                         case IDC_PXO_REFRESH:                           
792                                 if(MULTI_IS_TRACKER_GAME){
793                                         // delete mvalid.cfg if it exists
794                                         cf_delete(MULTI_VALID_MISSION_FILE, CF_TYPE_DATA);
795
796                                         // refresh missions
797                                         multi_update_valid_missions();
798                                 }
799                                 break;
800                         }
801                         break;
802                 // an edit control text has been changed
803                 case EN_UPDATE :
804                         if((HWND)lParam == Multi_std_name){
805                                 // update the standalone name field in Netgame.name
806                                 std_connect_handle_name_change();
807                         } else if((HWND)lParam == Multi_std_host_passwd){
808                                 // update the standalone host passwd
809                                 std_connect_handle_passwd_change();
810                         }
811                         break;
812                 }
813                 break;
814
815         // a notification message
816         case WM_NOTIFY :
817                 // notification that this is the current selected page. set our own internal data vars
818                 if(((LPNMHDR)lParam)->code == PSN_SETACTIVE){
819                         Active_standalone_page = CONNECT_PAGE;
820                 } else if ( (((LPNMHDR)lParam)->code == PSN_APPLY) || (((LPNMHDR)lParam)->code == PSN_RESET) ) {
821                         PostMessage( Psht, WM_DESTROY, 0, 0 );
822                 }
823                 break;
824         
825         default :
826                 return 0;               
827         }
828         return 0;
829 }
830
831         
832 // ----------------------------------------------------------------------------------------
833 // multiplayer page/tab functions
834 //
835
836 static HWND                     Framecap_trackbar;                                      // trackbar for capping framerate
837 static HWND                Standalone_FPS;                                              // text control for displaying framerate
838 static HWND                     Standalone_mission_name;                        // text control for showing the current mission name
839 static HWND                     Standalone_missiontime;                         // text control for showing current mission time
840 static HIMAGELIST Goal_bitmaps;                                                 // bitmaps array for the goal tree control
841 static HWND                     Standalone_goals;                                               // goal tree control handle
842 static HTREEITEM        Goal_items[3];                                                  // primary, secondary, and bonus goal items
843 static HWND                     Std_ng_max_players;                                     // max players display text control
844 static HWND                     Std_ng_max_observers;                           // max observers display text control
845 static HWND                     Std_ng_security;                                                // netgame security display text control
846 static HWND                     Std_ng_respawns;                                                // netgame # respawns display text control
847
848 #define GOALVIEW_X 5                                                                                    // goal view control extents
849 #define GOALVIEW_Y 242                                                                          //
850 #define GOALVIEW_W 160                                                                          //
851 #define GOALVIEW_H 168                                                                          //
852
853 // handle the user sliding the framerate cap scrollbar around
854 void std_multi_handle_framecap_scroll(HWND ctrl);
855
856 // initialize the framerate cap slide control
857 void std_multi_init_framecap_slider(HWND hwndDlg);
858
859 // initialize all the controls for this page
860 void std_multi_init_multi_controls(HWND hwndDlg);
861
862 // return the handle to the item matching the given parameters
863 HTREEITEM std_multi_get_goal_item(char *goal_string,int type);
864
865 // set the mission time in seconds
866 void std_multi_set_standalone_missiontime(float mission_time)
867 {
868         char txt[20];
869         char time_txt[30];
870         fix m_time = fl2f(mission_time);
871
872         // format the time string and set the text
873         game_format_time(m_time,time_txt);      
874         sprintf(txt,"  :  %.1f", mission_time);
875         strcat(time_txt,txt);
876         SetWindowText(Standalone_missiontime,time_txt);
877 }
878
879 // set the mission name
880 void std_multi_set_standalone_mission_name(char *mission_name)
881 {
882         // set the text
883         SetWindowText(Standalone_mission_name,mission_name);
884 }
885
886 // initialize the goal tree for this mission 
887 void std_multi_setup_goal_tree()
888 {
889    TV_ITEM         new_item;
890         TV_INSERTSTRUCT tree_insert;
891         char goal_name[NAME_LENGTH+1];
892
893         // clear out the tree control
894         TreeView_DeleteAllItems(Standalone_goals);
895
896    // add the primary goal tag
897    new_item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
898         new_item.pszText = goal_name;
899         strcpy(new_item.pszText,XSTR("Primary Objectives",917));
900         new_item.iImage = 0;
901         new_item.iSelectedImage = 0;            
902         tree_insert.hParent      = NULL;
903         tree_insert.hInsertAfter = TVI_FIRST;
904         tree_insert.item = new_item;
905         Goal_items[0] = TreeView_InsertItem(Standalone_goals,&tree_insert);
906
907         // add the secondary goal tag
908         new_item.pszText = goal_name;
909         strcpy(new_item.pszText,XSTR("Secondary Objectives",918));
910         new_item.iImage = 0;
911         new_item.iSelectedImage = 0;    
912         tree_insert.hInsertAfter = TVI_LAST;
913         tree_insert.item = new_item;
914    Goal_items[1] = TreeView_InsertItem(Standalone_goals,&tree_insert);
915
916         // add the bonus goal tag
917         new_item.pszText = goal_name;
918         strcpy(new_item.pszText,XSTR("Bonus Objectives",919));
919         new_item.iImage = 0;
920         new_item.iSelectedImage = 0;                    
921         tree_insert.item = new_item;
922    Goal_items[2] = TreeView_InsertItem(Standalone_goals,&tree_insert);
923 }
924
925 // add all the goals from the current mission to the tree control
926 void std_multi_add_goals()
927 {
928         TV_ITEM new_item;
929         TV_INSERTSTRUCT tree_insert;
930         int idx,goal_flags,perm_goal_flags;             
931         char goal_name[NAME_LENGTH+1];
932
933         // setup data common for every item
934         new_item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE;    
935         tree_insert.hInsertAfter = TVI_LAST;    
936
937         perm_goal_flags = 0;
938         for(idx=0;idx<Num_goals;idx++){         
939                 // reset the goal flags
940                 goal_flags = 0;
941
942       switch(Mission_goals[idx].type & GOAL_TYPE_MASK){
943                 // primary goal
944                 case PRIMARY_GOAL :
945                         goal_flags |= (1<<1);           // (image index == 1, primary goal)                                             
946                         perm_goal_flags |= (1<<1);
947                         break;
948                 
949                 // a secondary goal
950                 case SECONDARY_GOAL :
951                         goal_flags |= (1<<2);           // (image index == 1, secondary goal)                   
952                         perm_goal_flags |= (1<<2);
953                         break;
954                 
955                 // a bonus  goal
956                 case BONUS_GOAL :
957                         goal_flags |= (1<<3);           // (image index == 1, bonus goal)                       
958                         perm_goal_flags |= (1<<3);
959                         break;
960                 
961                 default :
962                         goal_flags |= (1<<0);           // (image index == 3, no goal)
963                         break;
964                 }
965                 
966       // first select whether to insert under primary, secondary, or bonus tree roots
967                 tree_insert.hParent = Goal_items[Mission_goals[idx].type & GOAL_TYPE_MASK];  
968
969                 // set the goal name
970                 new_item.pszText = goal_name;
971                 strcpy(new_item.pszText,Mission_goals[idx].name);
972                 
973                 // set the correct image indices
974                 new_item.iImage = (goal_flags & (1<<0)) ? 3 : 0;
975                 new_item.iSelectedImage = (goal_flags & (1<<0)) ? 3 : 0;
976
977                 // insert the item
978                 tree_insert.item = new_item;
979                 TreeView_InsertItem(Standalone_goals,&tree_insert);
980         }       
981
982         // check to see if there are any of the three types of mission goals. If not, then 
983         // insert "none"
984         if(!(perm_goal_flags & (1<<1))){
985                 // insert the "none" item 
986                 tree_insert.hParent = Goal_items[0];
987                 new_item.pszText = goal_name;
988                 strcpy(new_item.pszText,XSTR("none",920));
989                 new_item.iImage = 3;
990                 new_item.iSelectedImage = 3;
991                 tree_insert.item = new_item;
992                 TreeView_InsertItem(Standalone_goals,&tree_insert);
993         }
994         if(!(perm_goal_flags & (1<<2))){
995                 // insert the "none" item
996                 tree_insert.hParent = Goal_items[1];
997                 new_item.pszText = goal_name;
998                 strcpy(new_item.pszText,XSTR("none",920));
999                 new_item.iImage = 3;
1000                 new_item.iSelectedImage = 3;
1001                 tree_insert.item = new_item;
1002                 TreeView_InsertItem(Standalone_goals,&tree_insert);
1003         }
1004         if(!(perm_goal_flags & (1<<3))){
1005                 // insert the "none" item
1006                 tree_insert.hParent = Goal_items[1];
1007                 new_item.pszText = goal_name;
1008                 strcpy(new_item.pszText,XSTR("none",920));
1009                 new_item.iImage = 3;
1010                 new_item.iSelectedImage = 3;
1011                 tree_insert.item = new_item;
1012                 TreeView_InsertItem(Standalone_goals,&tree_insert);
1013         }
1014
1015         // expand out all the tree roots so all goals are shown
1016         for(idx=0;idx<3;idx++){
1017                 TreeView_Expand(Standalone_goals,Goal_items[idx],TVE_EXPAND);
1018         }
1019 }
1020
1021 // update all the goals in the goal tree based upon the mission status
1022 void std_multi_update_goals()
1023 {
1024         HTREEITEM update_item;
1025         TV_ITEM setting,lookup;
1026         int idx,should_update;
1027
1028    setting.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE;
1029
1030         // go through all the goals
1031         for(idx=0;idx<Num_goals;idx++){
1032                 // get a handle to the tree item
1033                 update_item = NULL;
1034                 update_item = std_multi_get_goal_item(Mission_goals[idx].name,Mission_goals[idx].type & GOAL_TYPE_MASK);
1035
1036                 // continue if we didn't get a valid item
1037                 if(update_item == NULL){
1038                         continue;
1039                 }
1040                 
1041                 // get the tree item itself (as it currently stands)
1042                 lookup.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE;
1043                 lookup.hItem = update_item;
1044                 if(!TreeView_GetItem(Standalone_goals,&lookup)){
1045                         continue;
1046                 }
1047                 
1048                 should_update = 0;              
1049                 // determine what image to set for each one (failed, satisfied, incomplete, etc)
1050                 switch(Mission_goals[idx].satisfied){
1051                 case GOAL_FAILED :                      
1052                         // determine if we should update the item
1053                         if((lookup.iImage != 4) && (lookup.iSelectedImage != 4)){
1054                                 setting.iImage = 4;
1055                                 setting.iSelectedImage = 4;
1056
1057                                 should_update = 1;
1058                         }
1059                         break;
1060                 case GOAL_COMPLETE :
1061                         // determine if we should update the item
1062                         if((lookup.iImage != 2) && (lookup.iSelectedImage != 2)){                               
1063                                 setting.iImage = 2;
1064                                 setting.iSelectedImage = 2;
1065
1066                                 should_update = 1;
1067                         }
1068                         break;
1069
1070                 case GOAL_INCOMPLETE :
1071                         // determine if we should update the item
1072                         if((lookup.iImage != 1) && (lookup.iSelectedImage != 1)){                               
1073                                 setting.iImage = 1;
1074                                 setting.iSelectedImage = 1;
1075
1076                                 should_update = 1;
1077                         }
1078                         break;
1079                 }
1080                         
1081                 // set the actual image
1082                 if(should_update){
1083                         setting.hItem = update_item;
1084                         TreeView_SetItem(Standalone_goals,&setting);            
1085                 }
1086         }
1087 }
1088
1089 // set the framerate text box for this page
1090 void std_multi_set_framerate(float f)
1091 {
1092         char fr[10];
1093
1094         // set the window text
1095         sprintf(fr,"%.1f",f);
1096         SetWindowText(Standalone_FPS,fr);
1097 }
1098
1099 // clear all the controls for this page
1100 void std_multi_clear_controls()
1101 {
1102         // clear out the mission name text static
1103         SetWindowText(Standalone_mission_name,"");
1104
1105         // clear out the framerate text box
1106         SetWindowText(Standalone_FPS,"");
1107
1108         // clear out the misison time text box
1109         SetWindowText(Standalone_missiontime,"");               
1110
1111         // clear out the netgame max players text box
1112         SetWindowText(Std_ng_max_players,"");
1113
1114         // clear out the netgame max observer text box
1115         SetWindowText(Std_ng_max_observers,"");
1116
1117         // clear out the netgame security text box
1118         SetWindowText(Std_ng_security,"");
1119
1120         // clear out the netgame respawns # text box
1121         SetWindowText(Std_ng_respawns,"");
1122
1123         // clear the goal tree control
1124         std_multi_setup_goal_tree();
1125 }
1126
1127 // update the netgame information area controls with the current Netgame settings
1128 void std_multi_update_netgame_info_controls()
1129 {
1130         char buf[40];
1131
1132         // update the 
1133         
1134         // update the max players control
1135         sprintf(buf,"%d",Netgame.max_players);
1136         SetWindowText(Std_ng_max_players,buf);
1137
1138         // update the max observers control
1139         sprintf(buf,"%d",Netgame.options.max_observers);
1140         SetWindowText(Std_ng_max_observers,buf);
1141
1142         // update the netgame security control
1143         sprintf(buf,"%d",Netgame.security);
1144         SetWindowText(Std_ng_security,buf);
1145
1146         // update the netgame respawns # control
1147         sprintf(buf,"%d",Netgame.respawn);
1148         SetWindowText(Std_ng_respawns,buf);
1149 }
1150
1151 // handle the user sliding the framerate cap scrollbar around
1152 void std_multi_handle_framecap_scroll(HWND ctrl)
1153 {
1154    int pos;
1155         char pos_text[10];
1156    
1157         // determine where the slider now is
1158         pos = SendMessage(ctrl,TBM_GETPOS,(WPARAM)0,(LPARAM)0);
1159
1160         // update the text display 
1161         sprintf(pos_text,"%d",pos);
1162         SetWindowText(GetDlgItem(Page_handles[MULTIPLAYER_PAGE],(int)MAKEINTRESOURCE(IDC_FRAMECAP_STATIC)),pos_text);
1163         
1164         // set the framecap var
1165         Multi_options_g.std_framecap = pos;
1166 }
1167
1168 // initialize the framerate cap slide control
1169 void std_multi_init_framecap_slider(HWND hwndDlg)
1170 {
1171         WPARAM wp;
1172         LPARAM lp;
1173
1174    // create the trackbar object
1175         Framecap_trackbar = CreateWindowEx(0,TRACKBAR_CLASS,NULL,WS_CHILD | WS_VISIBLE,
1176                                               10,10,300,30,hwndDlg,NULL,GetModuleHandle(NULL),NULL);    
1177
1178         // set the range of the framerate cap
1179         wp = (WPARAM)(BOOL)TRUE;
1180         lp = (LPARAM)MAKELONG(1, 100);
1181    SendMessage(Framecap_trackbar,TBM_SETRANGE,wp,lp);
1182         
1183    // set the default framerate cap the be the standalone default
1184         wp = (WPARAM)(BOOL)TRUE;
1185         lp = (LPARAM)(LONG)30;
1186         SendMessage(Framecap_trackbar,TBM_SETPOS,wp,lp);
1187
1188    // call this to update the standalone framecap on this first run
1189         std_multi_handle_framecap_scroll(Framecap_trackbar);
1190 }
1191
1192 // initialize all the controls for this page
1193 void std_multi_init_multi_controls(HWND hwndDlg)
1194 {
1195    HBITMAP ref;
1196         COLORREF mask;
1197         
1198         // create the framecap slider
1199         std_multi_init_framecap_slider(hwndDlg);
1200
1201         // create the framerate display text box
1202         Standalone_FPS = GetDlgItem(hwndDlg,(int)MAKEINTRESOURCE(IDC_STANDALONE_FPS));
1203
1204         // create the missiontime text box
1205         Standalone_missiontime = GetDlgItem(hwndDlg,(int)MAKEINTRESOURCE(IDC_STANDALONE_MTIME));
1206
1207         // create the mission name text box
1208         Standalone_mission_name = GetDlgItem(hwndDlg,(int)MAKEINTRESOURCE(IDC_MISSION_NAME));
1209
1210         // create the netgame max players text box
1211         Std_ng_max_players = GetDlgItem(hwndDlg,(int)MAKEINTRESOURCE(IDC_NG_MAXPLAYERS));
1212
1213         // create the netgame max observers text box
1214         Std_ng_max_observers = GetDlgItem(hwndDlg,(int)MAKEINTRESOURCE(IDC_NG_MAXOBSERVERS));
1215
1216         // create the netgame security text box
1217         Std_ng_security = GetDlgItem(hwndDlg,(int)MAKEINTRESOURCE(IDC_NG_SECURITY));
1218
1219         // create the netgame respawns # text box
1220         Std_ng_respawns = GetDlgItem(hwndDlg,(int)MAKEINTRESOURCE(IDC_NG_RESPAWNS));
1221
1222    // load the goal tree-view bitmaps
1223         Goal_bitmaps = ImageList_Create(16,16,ILC_COLOR4 | ILC_MASK,5,0);
1224         
1225         mask = 0x00ff00ff;
1226         ref = LoadBitmap(GetModuleHandle(NULL),MAKEINTRESOURCE(IDB_GOAL_ORD));  
1227         ImageList_AddMasked(Goal_bitmaps,ref,mask);
1228                         
1229         ref = LoadBitmap(GetModuleHandle(NULL),MAKEINTRESOURCE(IDB_GOAL_INC));
1230         mask = 0x00ffffff;
1231         ImageList_AddMasked(Goal_bitmaps,ref,mask);
1232         
1233         ref = LoadBitmap(GetModuleHandle(NULL),MAKEINTRESOURCE(IDB_GOAL_COMP));
1234         mask = 0x00ffffff;
1235         ImageList_AddMasked(Goal_bitmaps,ref,mask);
1236         
1237         ref = LoadBitmap(GetModuleHandle(NULL),MAKEINTRESOURCE(IDB_GOAL_NONE));
1238         mask = 0x00ffffff;
1239         ImageList_AddMasked(Goal_bitmaps,ref,mask);
1240
1241         ref = LoadBitmap(GetModuleHandle(NULL),MAKEINTRESOURCE(IDB_GOAL_FAIL));
1242         mask = 0x00ffffff;
1243         ImageList_AddMasked(Goal_bitmaps,ref,mask);
1244         
1245    // create the tree view control and associate its image list
1246         Standalone_goals = CreateWindowEx(0, WC_TREEVIEW, XSTR("Tree View",921), 
1247         WS_VISIBLE | WS_CHILD | WS_BORDER | TVS_HASLINES, 
1248         GOALVIEW_X,GOALVIEW_Y,GOALVIEW_W,GOALVIEW_H,
1249         hwndDlg, NULL, GetModuleHandle(NULL), NULL); 
1250         TreeView_SetImageList(Standalone_goals,Goal_bitmaps,TVSIL_NORMAL);
1251 }
1252
1253 // return the handle to the item matching the given parameters
1254 HTREEITEM std_multi_get_goal_item(char *goal_string,int type)
1255 {
1256         HTREEITEM ret,moveup;
1257         TV_ITEM lookup;
1258         int done;
1259         char goal_name_text[NAME_LENGTH+1];
1260
1261         // look under the correct root item
1262         lookup.mask = TVIF_TEXT;
1263         lookup.pszText = goal_name_text;
1264         lookup.cchTextMax = NAME_LENGTH;
1265         strcpy(lookup.pszText,goal_string);
1266
1267         // search through all the items
1268         done=0;
1269         ret=NULL;
1270         moveup = TreeView_GetChild(Standalone_goals,Goal_items[type]);
1271         while(!done && moveup!=NULL){
1272                 lookup.hItem = moveup;
1273                 TreeView_GetItem(Standalone_goals,&lookup);
1274                 if(strcmp(lookup.pszText,goal_string)==0){
1275                         ret = moveup;
1276                         done=1;
1277                 }
1278                 if(!done){
1279                         moveup = TreeView_GetNextItem(Standalone_goals,moveup,TVGN_NEXT);
1280                 }
1281         }       
1282         return ret;
1283 }
1284
1285 // message handler for the multiplayer tab
1286 BOOL CALLBACK multi_proc(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)
1287 {
1288         switch(uMsg){
1289         // initialize the page
1290         case WM_INITDIALOG:
1291                 // set the page handle
1292                 Page_handles[MULTIPLAYER_PAGE] = hwndDlg;
1293
1294                 // initialize all the controls
1295                 std_multi_init_multi_controls(hwndDlg);
1296                 return 1;
1297                 break;
1298         
1299         // a scroll message from the framerate cap trackbar
1300         case WM_HSCROLL:
1301                 std_multi_handle_framecap_scroll((HWND)lParam);
1302                 return 1;
1303                 break;
1304
1305         // notification that this page has been set as active
1306         case WM_NOTIFY :
1307                 // setup our own internal vars
1308                 if(((LPNMHDR)lParam)->code == PSN_SETACTIVE){
1309                         Active_standalone_page = MULTIPLAYER_PAGE;
1310                 } else if ( (((LPNMHDR)lParam)->code == PSN_APPLY) || (((LPNMHDR)lParam)->code == PSN_RESET) ) {
1311                         // PostMessage( Psht, WM_DESTROY, 0, 0 );
1312                         gameseq_post_event(GS_EVENT_QUIT_GAME);
1313                 }
1314                 break;
1315
1316         default :
1317                 return 0;
1318                 break;
1319         }
1320         return 0;
1321 }
1322
1323
1324 // ---------------------------------------------------------------------------------------
1325 // player info page/tab functions
1326 //
1327
1328 #define MAX_PLAYER_STAT_FIELDS 11                                                       // the # of stats fields for a given set
1329 static HWND Player_name_list;                                                                   // the listbox control with player callsigns in it
1330 static HWND Player_ship_type;                                                                   // the current player's ship type
1331 static HWND Player_ping_time;                                                                   // the current player's ping time
1332 static HWND Player_stats[MAX_PLAYER_STAT_FIELDS];               // text boxes for player alltime statistics info
1333 static HWND Player_mstats[MAX_PLAYER_STAT_FIELDS];              // text boxes for player mission statistics info
1334
1335 // sprintf and set window text to the passed int
1336 #define STD_ADDSTRING(hwnd,val) { sprintf(txt,"%d",(int)val); SetWindowText(hwnd,txt); }
1337
1338 // intialize all the controls in the player info tab
1339 void std_pinfo_init_player_info_controls(HWND hwndDlg);
1340
1341 // returns true or false depending on whether the passed netplayer is the currently selected guy
1342 int std_pinfo_player_is_active(net_player *p);
1343
1344 // start displaying info for the passed player on this page
1345 void std_pinfo_display_player_info(net_player *p)
1346 {
1347         char txt[40];           
1348
1349         // set his ship type
1350         SetWindowText(Player_ship_type,Ship_info[p->p_info.ship_class].name);
1351
1352         // display his ping time
1353         std_pinfo_update_ping(p);
1354         
1355         // his alltime stats
1356         scoring_struct *ptr = &p->player->stats;
1357         STD_ADDSTRING(Player_stats[0],ptr->p_shots_fired);
1358    STD_ADDSTRING(Player_stats[1],ptr->p_shots_hit);
1359         STD_ADDSTRING(Player_stats[2],ptr->p_bonehead_hits);
1360         STD_ADDSTRING(Player_stats[3],
1361                 (int)((float)100.0*((float)ptr->p_shots_hit/(float)ptr->p_shots_fired)));
1362         STD_ADDSTRING(Player_stats[4],
1363                           (int)((float)100.0*((float)ptr->p_bonehead_hits/(float)ptr->p_shots_fired)));
1364         STD_ADDSTRING(Player_stats[5],ptr->s_shots_fired);
1365         STD_ADDSTRING(Player_stats[6],ptr->s_shots_hit);
1366         STD_ADDSTRING(Player_stats[7],ptr->s_bonehead_hits);
1367         STD_ADDSTRING(Player_stats[8],
1368                                          (int)((float)100.0*((float)ptr->s_shots_hit/(float)ptr->s_shots_fired)));
1369         STD_ADDSTRING(Player_stats[9],
1370                 (int)((float)100.0*((float)ptr->s_bonehead_hits/(float)ptr->s_shots_fired)));
1371         STD_ADDSTRING(Player_stats[10],ptr->assists);
1372
1373    // his stats for the current mission
1374         STD_ADDSTRING(Player_mstats[0],ptr->mp_shots_fired);
1375    STD_ADDSTRING(Player_mstats[1],ptr->mp_shots_hit);
1376         STD_ADDSTRING(Player_mstats[2],ptr->mp_bonehead_hits);
1377         STD_ADDSTRING(Player_mstats[3],
1378                 (int)((float)100.0*((float)ptr->mp_shots_hit/(float)ptr->mp_shots_fired)));
1379         STD_ADDSTRING(Player_mstats[4],
1380                           (int)((float)100.0*((float)ptr->mp_bonehead_hits/(float)ptr->mp_shots_fired)));
1381         STD_ADDSTRING(Player_mstats[5],ptr->ms_shots_fired);
1382         STD_ADDSTRING(Player_mstats[6],ptr->ms_shots_hit);
1383         STD_ADDSTRING(Player_mstats[7],ptr->ms_bonehead_hits);
1384         STD_ADDSTRING(Player_mstats[8],
1385                                          (int)((float)100.0*((float)ptr->ms_shots_hit/(float)ptr->ms_shots_fired)));
1386         STD_ADDSTRING(Player_mstats[9],
1387                 (int)((float)100.0*((float)ptr->ms_bonehead_hits/(float)ptr->ms_shots_fired))); 
1388         STD_ADDSTRING(Player_mstats[10],ptr->m_assists);
1389 }
1390
1391 // check to see if this player is the one being displayed, and if so, then update the display info
1392 // return 1 if the player was updated
1393 int std_pinfo_maybe_update_player_info(net_player *p)
1394 {       
1395         // only update if this is the currently active player
1396         if(std_pinfo_player_is_active(p)){      
1397                 std_pinfo_display_player_info(p);
1398                 return 1;
1399         }
1400         return 0;
1401 }
1402
1403 // add a player to the list on the player info page
1404 void std_pinfo_add_player_list_item(net_player *p)
1405 {       
1406    // add the item
1407         SendMessage(Player_name_list,CB_ADDSTRING,(WPARAM)0,(LPARAM)(LPCTSTR)p->player->callsign);      
1408
1409         // if this is the first item on the list, then select it and display it
1410         if(SendMessage(Player_name_list,CB_GETCOUNT,(WPARAM)0,(LPARAM)0) == 1){
1411                 // select the item
1412                 SendMessage(Player_name_list,CB_SETCURSEL,(WPARAM)0,(LPARAM)0);
1413
1414                 // display this players info
1415                 std_pinfo_display_player_info(p);
1416         }
1417 }
1418
1419 // remove a player from the list on the player info page
1420 void std_pinfo_remove_player_list_item(net_player *p)
1421 {
1422         int loc;
1423         
1424         // lookup thie player
1425         loc = SendMessage(Player_name_list,CB_FINDSTRINGEXACT,(WPARAM)-1,(LPARAM)(LPCTSTR)p->player->callsign);
1426
1427         // if we found the entry, then delete it
1428         if(loc!=CB_ERR){
1429                 SendMessage(Player_name_list,CB_DELETESTRING,(WPARAM)loc,(LPARAM)0);
1430         }
1431 }
1432
1433 // update the ping display for this player
1434 void std_pinfo_update_ping(net_player *p)
1435 {
1436         char sml_ping[30];
1437         
1438         // chop it off at pings greater than 1 second
1439         if(p->s_info.ping.ping_avg > 1000){             
1440                 strcpy(sml_ping,XSTR("> 1 sec",914));
1441         }
1442         // use the ping itself
1443         else {
1444                 sprintf(sml_ping,"%d",p->s_info.ping.ping_avg);                 
1445                 strcat(sml_ping,XSTR(" ms",915));
1446         }
1447         
1448         SetWindowText(Player_ping_time,sml_ping);
1449 }
1450
1451 // clear the player info page controls
1452 void std_pinfo_clear_controls()
1453 {
1454         int idx;
1455         
1456    // clear the player selection list
1457         SendMessage(Player_name_list,CB_RESETCONTENT,(WPARAM)0,(LPARAM)0);
1458         
1459         // clear out misc items 
1460         SetWindowText(Player_ship_type,"");
1461         SetWindowText(Player_ping_time,"");
1462
1463         // clear out the player stats
1464         for(idx=0;idx<MAX_PLAYER_STAT_FIELDS;idx++){
1465                 SetWindowText(Player_stats[idx],"");
1466                 SetWindowText(Player_mstats[idx],"");
1467         }
1468 }
1469
1470 // intialize all the controls in the player info tab
1471 void std_pinfo_init_player_info_controls(HWND hwndDlg)
1472 {       
1473         // create the player callsign listbox
1474         Player_name_list = GetDlgItem(Page_handles[PLAYER_INFO_PAGE],(int)MAKEINTRESOURCE(IDC_PLAYER_LIST));
1475
1476         // create the player ship type text box
1477         Player_ship_type = GetDlgItem(Page_handles[PLAYER_INFO_PAGE],(int)MAKEINTRESOURCE(IDC_PSHIP_TYPE));
1478
1479         // create the player ping time text box
1480         Player_ping_time = GetDlgItem(Page_handles[PLAYER_INFO_PAGE],(int)MAKEINTRESOURCE(IDC_PING_TIME));
1481
1482         // initialize the various and sundry statistics text controls (alltime)
1483    Player_stats[0] = GetDlgItem(Page_handles[PLAYER_INFO_PAGE],(int)MAKEINTRESOURCE(IDC_PSHOTS));
1484         Player_stats[1] = GetDlgItem(Page_handles[PLAYER_INFO_PAGE],(int)MAKEINTRESOURCE(IDC_PHITS));
1485         Player_stats[2] = GetDlgItem(Page_handles[PLAYER_INFO_PAGE],(int)MAKEINTRESOURCE(IDC_PBHHITS));
1486         Player_stats[3] = GetDlgItem(Page_handles[PLAYER_INFO_PAGE],(int)MAKEINTRESOURCE(IDC_PPCT));
1487         Player_stats[4] = GetDlgItem(Page_handles[PLAYER_INFO_PAGE],(int)MAKEINTRESOURCE(IDC_PBHPCT));
1488         Player_stats[5] = GetDlgItem(Page_handles[PLAYER_INFO_PAGE],(int)MAKEINTRESOURCE(IDC_SSHOTS));
1489         Player_stats[6] = GetDlgItem(Page_handles[PLAYER_INFO_PAGE],(int)MAKEINTRESOURCE(IDC_SECHITS));
1490         Player_stats[7] = GetDlgItem(Page_handles[PLAYER_INFO_PAGE],(int)MAKEINTRESOURCE(IDC_SBHHITS));
1491         Player_stats[8] = GetDlgItem(Page_handles[PLAYER_INFO_PAGE],(int)MAKEINTRESOURCE(IDC_SPCT));
1492         Player_stats[9] = GetDlgItem(Page_handles[PLAYER_INFO_PAGE],(int)MAKEINTRESOURCE(IDC_SBHPCT));
1493         Player_stats[10] = GetDlgItem(Page_handles[PLAYER_INFO_PAGE],(int)MAKEINTRESOURCE(IDC_ASSISTS));
1494
1495         // initialize the various and sundry statistics text controls (this mission)
1496         Player_mstats[0] = GetDlgItem(Page_handles[PLAYER_INFO_PAGE],(int)MAKEINTRESOURCE(IDC_MPSHOTS));
1497         Player_mstats[1] = GetDlgItem(Page_handles[PLAYER_INFO_PAGE],(int)MAKEINTRESOURCE(IDC_MPHITS));
1498         Player_mstats[2] = GetDlgItem(Page_handles[PLAYER_INFO_PAGE],(int)MAKEINTRESOURCE(IDC_MPBHHITS));
1499         Player_mstats[3] = GetDlgItem(Page_handles[PLAYER_INFO_PAGE],(int)MAKEINTRESOURCE(IDC_MPPCT));
1500         Player_mstats[4] = GetDlgItem(Page_handles[PLAYER_INFO_PAGE],(int)MAKEINTRESOURCE(IDC_MPBHPCT));
1501         Player_mstats[5] = GetDlgItem(Page_handles[PLAYER_INFO_PAGE],(int)MAKEINTRESOURCE(IDC_MSSHOTS));
1502         Player_mstats[6] = GetDlgItem(Page_handles[PLAYER_INFO_PAGE],(int)MAKEINTRESOURCE(IDC_MSECHITS));
1503         Player_mstats[7] = GetDlgItem(Page_handles[PLAYER_INFO_PAGE],(int)MAKEINTRESOURCE(IDC_MSBHHITS));
1504         Player_mstats[8] = GetDlgItem(Page_handles[PLAYER_INFO_PAGE],(int)MAKEINTRESOURCE(IDC_MSPCT));
1505         Player_mstats[9] = GetDlgItem(Page_handles[PLAYER_INFO_PAGE],(int)MAKEINTRESOURCE(IDC_MSBHPCT));
1506         Player_mstats[10] = GetDlgItem(Page_handles[PLAYER_INFO_PAGE],(int)MAKEINTRESOURCE(IDC_MASSISTS));
1507 }
1508
1509 // returns true or false depending on whether the passed netplayer is the currently selected guy
1510 int std_pinfo_player_is_active(net_player *p)
1511 {
1512         int sel;
1513         char player[40];
1514
1515         // get the index of the currently selected item
1516         sel = SendMessage(Player_name_list,CB_GETCURSEL,(WPARAM)0,(LPARAM)0);
1517
1518         // if we didn't find the item, return a 0 length string
1519         if(sel == LB_ERR){
1520                 return 0;
1521         }
1522
1523         // otherwise, get the callsign of the given player
1524         SendMessage(Player_name_list,CB_GETLBTEXT,(WPARAM)sel,(LPARAM)player);
1525         
1526         // if there is a valid player selected and he's the guy we want
1527         return ((strlen(player) != 0) && (strcmp(p->player->callsign,player) == 0)) ? 1 : 0;
1528 }
1529
1530 // message handler for the player info tab
1531 BOOL CALLBACK player_info_proc(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)
1532 {
1533         int val,player_num;     
1534         char callsign[40];
1535
1536         switch(uMsg){
1537         // initialize the dialog
1538         case WM_INITDIALOG:
1539                 // set the page handle
1540                 Page_handles[PLAYER_INFO_PAGE] = hwndDlg;
1541
1542                 // intialize all the control
1543       std_pinfo_init_player_info_controls(hwndDlg);
1544                 return 1;
1545                 break;
1546
1547         // a command message of some kind
1548         case WM_COMMAND:
1549                 switch(HIWORD(wParam)){
1550                 // a listbox selection change message
1551                 case CBN_SELCHANGE :
1552                         // get the newly selected item
1553                         val = SendMessage(Player_name_list,CB_GETCURSEL,(WPARAM)0,(LPARAM)0);
1554                         if(val!=CB_ERR){                
1555                                 // get the callsign
1556                                 if(SendMessage(Player_name_list,CB_GETLBTEXT,(WPARAM)val,(LPARAM)callsign) != CB_ERR){                          
1557                                         // lookup the player
1558                                         player_num = multi_find_player_by_callsign(callsign);
1559                                         
1560                                         // if we found him then display his info
1561                                         if(player_num != -1){
1562                                                 std_pinfo_display_player_info(&Net_players[player_num]);
1563                                         }
1564                                 }
1565                         }
1566                         break;
1567                 }
1568       break;
1569         
1570         // a notification message
1571         case WM_NOTIFY :
1572                 // set our page to be the active one
1573                 if(((LPNMHDR)lParam)->code == PSN_SETACTIVE){
1574                         Active_standalone_page = PLAYER_INFO_PAGE;
1575                 } else if ( (((LPNMHDR)lParam)->code == PSN_APPLY) || (((LPNMHDR)lParam)->code == PSN_RESET) ) {
1576                         PostMessage( Psht, WM_DESTROY, 0, 0 );
1577                 }
1578                 break;
1579
1580         default :
1581                 return 0;
1582                 break;
1583         }
1584         return 0;
1585 }
1586
1587
1588 // ---------------------------------------------------------------------------------------
1589 // player god stuff page/tab functions
1590 //
1591
1592 #define GODSTUFF_MAX_ITEMS                                      19                              // how many items we can fit on the chatbox at one time
1593
1594 static HWND God_player_list;                                                            // the listbox of player callsigns      
1595 static HWND Godstuff_fps;                                                                       // the framerate text box
1596 static HWND Godstuff_broadcast_text;                                    // the text input box for sending messages to players
1597 static HWND Godstuff_broadcast_button;                                  // the button to send the text messages
1598 static HWND Godstuff_player_messages;                                   // handle to the list box containing player chatter
1599
1600 // initialize all the controls in the godstuff tab
1601 void std_gs_init_godstuff_controls(HWND hwndDlg);
1602
1603 // add a player to the listbox on the godstuff page
1604 void std_gs_add_god_player(net_player *p)
1605 {
1606         // add the item
1607         SendMessage(God_player_list,CB_ADDSTRING,(WPARAM)0,(LPARAM)(LPCTSTR)p->player->callsign);       
1608
1609                 // if this is the first item on the list, then select it
1610         if(SendMessage(God_player_list,CB_GETCOUNT,(WPARAM)0,(LPARAM)0) == 1){
1611                 // select the item
1612                 SendMessage(God_player_list,CB_SETCURSEL,(WPARAM)0,(LPARAM)0);          
1613         }
1614 }
1615
1616 // remove a player from the listbox on the godstuff page
1617 void std_gs_remove_god_player(net_player *p)
1618 {
1619         int loc;
1620         
1621         // lookup the player
1622         loc = SendMessage(God_player_list,CB_FINDSTRINGEXACT,(WPARAM)-1,(LPARAM)(LPCTSTR)p->player->callsign);
1623
1624         // if we found him, them delete the item
1625         if(loc!=CB_ERR){
1626                 SendMessage(God_player_list,CB_DELETESTRING,(WPARAM)loc,(LPARAM)0);
1627         }
1628 }
1629
1630 // send a message as if the standalone were a player
1631 void std_gs_send_godstuff_message()
1632 {
1633         char txt[256];  
1634
1635         // get the text in the edit control
1636         memset(txt,0,256);
1637         txt[0]=120;     
1638         SendMessage(Godstuff_broadcast_text,EM_GETLINE,(WPARAM)0,(LPARAM)&txt[0]);
1639         
1640         // if the string is not zero length
1641         if(strlen(txt) > 0){            
1642                 // send a game chat packet
1643                 send_game_chat_packet(Net_player, txt, MULTI_MSG_ALL,NULL);             
1644
1645                 // add the text to our own control              
1646                 std_add_chat_text(txt, MY_NET_PLAYER_NUM,1);            
1647
1648                 // clear the text control
1649                 SetWindowText(Godstuff_broadcast_text, "");
1650         }
1651 }
1652
1653 // set the framerate text box for this page
1654 void std_gs_set_framerate(float f)
1655 {
1656         char fr[10];
1657
1658         // set the window text
1659         sprintf(fr,"%.1f",f);
1660         SetWindowText(Godstuff_fps,fr);
1661 }
1662
1663 // clear the godstuff page controlsv
1664 void std_gs_clear_controls()
1665 {               
1666         // clear the framerate area
1667         SetWindowText(Godstuff_fps, "0");
1668         
1669         // clear the text area 
1670         SetWindowText(Godstuff_broadcast_text,"");
1671
1672         // reset the combo box  
1673         SendMessage(God_player_list, CB_RESETCONTENT, (WPARAM)0, (LPARAM)0);
1674
1675         // clear the player chatter listbox
1676         SendMessage(Godstuff_player_messages, LB_RESETCONTENT, (WPARAM)0, (LPARAM)0);
1677 }
1678
1679 // initialize all the controls in the godstuff tab
1680 void std_gs_init_godstuff_controls(HWND hwndDlg)
1681 {
1682         // initialize the player listbox control   
1683         God_player_list = GetDlgItem(Page_handles[GODSTUFF_PAGE], (int)MAKEINTRESOURCE(IDC_PLAYER_GOD_LIST));
1684         
1685         // initialize the framerate text box
1686         Godstuff_fps = GetDlgItem(Page_handles[GODSTUFF_PAGE], (int)MAKEINTRESOURCE(IDC_GODSTUFF_FPS));
1687
1688         // initialize the messaging edit control
1689         Godstuff_broadcast_text = GetDlgItem(Page_handles[GODSTUFF_PAGE], (int)MAKEINTRESOURCE(IDC_GODSTUFF_BROADCAST));
1690         SendMessage(Godstuff_broadcast_text, EM_SETLIMITTEXT, (WPARAM)CHATBOX_MAX_LEN, (LPARAM)0);
1691         SendMessage(Godstuff_broadcast_text, EM_FMTLINES, (WPARAM)TRUE, (LPARAM)0);
1692
1693         // create the player chatter list box
1694         Godstuff_player_messages = GetDlgItem(Page_handles[GODSTUFF_PAGE], (int)MAKEINTRESOURCE(IDC_GOD_CHAT));                 
1695         
1696         // initialize the message broadcast button
1697         Godstuff_broadcast_button = GetDlgItem(Page_handles[GODSTUFF_PAGE], (int)MAKEINTRESOURCE(IDC_GODSTUFF_SENDMESS));
1698         // hide the button -- we can now process return key
1699         ShowWindow(Godstuff_broadcast_button, SW_HIDE);
1700
1701 }
1702
1703 // message handler for the godstuff tab
1704 BOOL CALLBACK godstuff_proc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
1705 {
1706         switch(uMsg){
1707         // initialize the dialog
1708         case WM_INITDIALOG:
1709                 // setup the page handle
1710                 Page_handles[GODSTUFF_PAGE] = hwndDlg;
1711
1712                 // initialize the controls for this page
1713       std_gs_init_godstuff_controls(hwndDlg);
1714                 return 1;
1715                 break;
1716
1717         // a notification message
1718         case WM_NOTIFY :
1719                 // set this page to be the currently active one
1720                 if(((LPNMHDR)lParam)->code == PSN_SETACTIVE){
1721                         Active_standalone_page = GODSTUFF_PAGE;
1722                 } else if ( (((LPNMHDR)lParam)->code == PSN_APPLY) || (((LPNMHDR)lParam)->code == PSN_RESET) ) {
1723                         PostMessage( Psht, WM_DESTROY, 0, 0 );
1724                 }
1725                 break;  
1726
1727
1728         // a command message of some kind
1729         case WM_COMMAND:                
1730                 switch(HIWORD(wParam)){
1731                 // a button click
1732                 case BN_CLICKED :
1733                         switch(LOWORD(wParam)){
1734                         // send the message to the player
1735                         case IDC_GODSTUFF_SENDMESS : 
1736                                 std_gs_send_godstuff_message();   
1737                                 break;
1738                         }
1739                         break;
1740
1741                 }
1742                 break;
1743
1744         default :
1745                 return 0;
1746                 break;
1747         }
1748         return 0;
1749 }
1750
1751
1752 // ---------------------------------------------------------------------------------------
1753 // debug page/tab functions
1754 //
1755
1756 static HWND Standalone_state_string;                                                            // the text control box
1757
1758 // initialize the controls for the debug page
1759 void std_debug_init_debug_controls(HWND hwndDlg);
1760
1761 // set the text on the standalones state indicator box
1762 void std_debug_set_standalone_state_string(char *str)
1763 {
1764    // set the text
1765         SetWindowText(Standalone_state_string,str);
1766 }
1767
1768 // clear the debug page controls
1769 void std_debug_clear_controls()
1770 {
1771         // set the current debug state
1772         std_debug_set_standalone_state_string("");
1773 }
1774
1775 // initialize the controls for the debug page
1776 void std_debug_init_debug_controls(HWND hwndDlg)
1777 {
1778         // create the state string text box
1779         Standalone_state_string = GetDlgItem(hwndDlg,(int)MAKEINTRESOURCE(IDC_STANDALONE_STATE));
1780         
1781         // standalone state indicator
1782         SetWindowText(Standalone_state_string,"");
1783 }
1784
1785 // message handler for the godstuff tab
1786 BOOL CALLBACK debug_proc(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)
1787 {
1788         switch(uMsg){
1789         // initialize the dialog
1790         case WM_INITDIALOG:
1791                 // setup the page handle
1792                 Page_handles[DEBUG_PAGE] = hwndDlg;
1793
1794                 // intialize the controls for this page
1795       std_debug_init_debug_controls(hwndDlg);
1796                 return 1;
1797                 break;
1798
1799         // a notification message
1800         case WM_NOTIFY :
1801                 // set the currently active page to this one
1802                 if(((LPNMHDR)lParam)->code == PSN_SETACTIVE){
1803                         Active_standalone_page = DEBUG_PAGE;
1804                 } else if ( (((LPNMHDR)lParam)->code == PSN_APPLY) || (((LPNMHDR)lParam)->code == PSN_RESET) ) {
1805                         PostMessage( Psht, WM_DESTROY, 0, 0 );
1806                 }
1807                 break;
1808
1809         default :
1810                 return 0;
1811                 break;
1812         }
1813         return 0;
1814 }
1815
1816
1817 // ---------------------------------------------------------------------------------------
1818 // general functions
1819 // 
1820
1821 // add a player and take care of updating all gui/data details
1822 void std_add_player(net_player *p)
1823 {       
1824         char ip_string[60];     
1825         
1826         // get his ip string and add it to the list
1827         psnet_addr_to_string(ip_string,&p->p_info.addr);
1828         std_connect_add_ip_string(ip_string);
1829
1830         // add to the player info player list box, and update his info
1831         std_pinfo_add_player_list_item(p);
1832         std_pinfo_maybe_update_player_info(p);
1833
1834         // add to the god stuff player list box
1835         std_gs_add_god_player(p);       
1836
1837         // check to see if this guy is the host.        
1838         std_connect_set_host_connect_status();  
1839
1840         // set the connection count
1841         std_connect_set_connect_count();
1842 }
1843
1844 // remove a player and take care of updateing all gui/data details
1845 int std_remove_player(net_player *p)
1846 {       
1847         int count;
1848         char ip_string[60];     
1849    
1850         // determine his ip string and remove it from the list
1851         psnet_addr_to_string(ip_string,&p->p_info.addr);
1852         std_connect_remove_ip_string(ip_string);
1853
1854         // remove from the player info player list box
1855         std_pinfo_remove_player_list_item(p);
1856
1857         // remove from the godstuff list box
1858         std_gs_remove_god_player(p);
1859
1860         // update the host connect count        
1861         std_connect_set_host_connect_status();  
1862
1863         // update the currently connected players
1864         count = std_connect_set_connect_count();
1865
1866         if(count == 0){
1867                 // multi_standalone_quit_game();      
1868                 multi_quit_game(PROMPT_NONE);
1869                 return 1;
1870         }
1871
1872         return 0;
1873 }
1874
1875 // set any relevant controls which display the framerate of the standalone
1876 void std_set_standalone_fps(float fps)
1877 {
1878         // set the framerate in the multiplayer dialog
1879         std_multi_set_framerate(fps);
1880
1881         // set the framerate in the godstuff dialog
1882         std_gs_set_framerate(fps);
1883 }
1884
1885 // update any relveant controls which display the ping for the given player
1886 void std_update_player_ping(net_player *p)
1887 {
1888         // update the ping on the connect page
1889         std_connect_update_ping(p);
1890
1891         // update the ping on the player info page
1892         std_pinfo_update_ping(p);       
1893 }
1894
1895 // reset everything in the standalone gui (all the controls)
1896 void std_reset_standalone_gui()
1897 {
1898         // clear the connect page controls
1899         std_connect_clear_controls();
1900
1901         // clear the multi page controls
1902         std_multi_clear_controls();
1903
1904         // clear the player info page controls
1905         std_pinfo_clear_controls();
1906
1907         // clear the godstuff page controls
1908         std_gs_clear_controls();
1909
1910         // clear the debug page controls
1911         std_debug_clear_controls();
1912
1913         // set all framerate displays to 0
1914         std_set_standalone_fps((float)0);
1915         
1916         // set the mission time
1917         std_multi_set_standalone_missiontime((float)0); 
1918
1919         // reset the stats update timestamp
1920         Standalone_stats_stamp = -1;
1921
1922         // reset the netgame info timestamp
1923         Standalone_ng_stamp = -1;       
1924 }
1925
1926 // do any gui related issues on the standalone (like periodically updating player stats, etc...)
1927 void std_do_gui_frame()
1928 {
1929         int idx;
1930         
1931         // check to see if the timestamp for updating player selected stats has popped
1932         if((Standalone_stats_stamp == -1) || timestamp_elapsed(Standalone_stats_stamp)){
1933                 // reset the timestamp
1934                 Standalone_stats_stamp = timestamp(STD_STATS_UPDATE_TIME);
1935
1936                 // update any player currently selected
1937                 // there's probably a nicer way to do this, but...
1938                 for(idx=0;idx<MAX_PLAYERS;idx++){
1939                         if(MULTI_CONNECTED(Net_players[idx]) && (Net_player != &Net_players[idx])){
1940                                 if(std_pinfo_maybe_update_player_info(&Net_players[idx])){
1941                                         break;
1942                                 }
1943                         }
1944                 }
1945         }
1946
1947         // check to see if the timestamp for updating the netgame information controls has popped
1948         if((Standalone_ng_stamp == -1) || timestamp_elapsed(Standalone_ng_stamp)){
1949                 // reset the timestamp
1950                 Standalone_ng_stamp = timestamp(STD_NG_UPDATE_TIME);
1951
1952                 // update the controls
1953                 std_multi_update_netgame_info_controls();
1954         }
1955 }
1956
1957 // notify the user that the standalone has failed to login to the tracker on startup
1958 void std_notify_tracker_login_fail()
1959 {
1960         MessageBox(Psht,XSTR("The standalone server has failed to log in to Parallax Online!",922),XSTR("VMT Warning!",923),MB_OK);
1961 }
1962
1963 // attempt to log the standalone into the tracker
1964 void std_tracker_login()
1965 {
1966 }
1967
1968 // reset all stand gui timestamps
1969 void std_reset_timestamps()
1970 {
1971         // reset the stats update stamp
1972         Standalone_stats_stamp = timestamp(STD_STATS_UPDATE_TIME);
1973
1974         // reset the netgame controls update timestamp
1975         Standalone_ng_stamp = timestamp(STD_NG_UPDATE_TIME);
1976 }
1977
1978 // add a line of text chat to the standalone
1979 void std_add_chat_text(char *text,int player_index,int add_id)
1980 {
1981         int num_items,ret_val;
1982         char format[512];
1983
1984         // invalid player ?
1985         if(player_index == -1){
1986                 return;
1987         }
1988
1989         // format the chat text nicely
1990         if(add_id){
1991                 if(MULTI_STANDALONE(Net_players[player_index])){
1992                         sprintf(format,XSTR("<SERVER> %s",924),text);
1993                 } else {
1994                         sprintf(format,"%s: %s",Net_players[player_index].player->callsign,text);
1995                 }
1996         } else {
1997                 strcpy(format,text);
1998         }
1999
2000         // insert the text string into the godstuff chat box and scroll it down to the bottom
2001         SendMessage(Godstuff_player_messages,LB_INSERTSTRING,(WPARAM)-1,(LPARAM)format);
2002         num_items = SendMessage(Godstuff_player_messages,LB_GETCOUNT,(WPARAM)0,(LPARAM)0);
2003         if(num_items > 19){
2004                 ret_val = SendMessage(Godstuff_player_messages,LB_SETTOPINDEX,(WPARAM)num_items - GODSTUFF_MAX_ITEMS,(LPARAM)0);                
2005         }
2006 }
2007
2008 // if the standalone is host password protected
2009 int std_is_host_passwd()
2010 {
2011         return (strlen(Multi_options_g.std_passwd) > 0) ? 1 : 0;
2012 }
2013
2014 // change the default property sheet interface into something more useful
2015 void std_mutate_sheet()
2016 {
2017         HWND ok_button = NULL;
2018         HWND cancel_button = NULL;
2019         HWND apply_button = NULL;
2020         HWND help_button = NULL;        
2021         char lookup[512];
2022         
2023         // get the buttons on the property sheet itself
2024         HWND child = GetWindow(Psht,GW_CHILD);
2025         while(child != NULL){
2026                 // get the text of the window
2027                 memset(lookup,0,512);
2028                 GetWindowText(child,lookup,511);
2029                 
2030                 // if its the OK button
2031                 if(!stricmp(lookup,XSTR("ok",925))){
2032                         ok_button = child;
2033                 } 
2034
2035                 // if its the cancel button
2036                 if(!stricmp(lookup,XSTR("cancel",926))){
2037                         cancel_button = child;
2038                 } 
2039
2040                 // if its the apply button
2041                 if(!stricmp(lookup,XSTR("&apply",927))){
2042                         apply_button = child;
2043                 } 
2044
2045                 // if its the help button
2046                 if(!stricmp(lookup,XSTR("help",928))){
2047                         help_button = child;
2048                 } 
2049
2050                 child = GetWindow(child,GW_HWNDNEXT);
2051         }
2052
2053         // kill em all
2054         if(apply_button != NULL){               
2055                 DestroyWindow(apply_button);
2056         }
2057         
2058         // rename the shutdown button and move it over a bit
2059         if(ok_button != NULL){                          
2060                 // set the text
2061                 SetWindowText(ok_button,XSTR("Shutdown",929));
2062
2063                 // move it
2064                 SetWindowPos(ok_button,
2065                         HWND_TOP,
2066                         Std_shutdown_coords[gr_screen.res][0],
2067                         Std_shutdown_coords[gr_screen.res][1],
2068                         0,0,
2069                         SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOZORDER
2070                         );
2071         }
2072
2073         // kill em all
2074         if(cancel_button != NULL){
2075                 DestroyWindow(cancel_button);
2076         }       
2077
2078         // kill em all
2079         if(help_button != NULL){
2080                 DestroyWindow(help_button);
2081         }       
2082
2083         // now we want to mess with the titlebar controls       
2084 }
2085
2086 // if the given callsign is banned from the server
2087 int std_player_is_banned(char *name)
2088 {
2089         int idx;
2090
2091         // go through the ban list
2092         for(idx=0;idx<Standalone_ban_count;idx++){
2093                 if(!stricmp(name,Standalone_ban_list[idx])){
2094                         return 1;
2095                 }
2096         }
2097
2098         // not banned
2099         return 0;
2100 }
2101
2102 // add a callsign to the ban list
2103 void std_add_ban(char *name)
2104 {
2105         // if we've reached the max bans
2106         if(Standalone_ban_count >= STANDALONE_MAX_BAN){
2107                 return;
2108         }
2109
2110         // otherwise add it
2111         memset(Standalone_ban_list[Standalone_ban_count],0,CALLSIGN_LEN+1);
2112         strcpy(Standalone_ban_list[Standalone_ban_count++],name);
2113 }
2114
2115
2116 // -------------------------------------------------------------------------------
2117 // property sheet/page creation and handling 
2118 //
2119
2120 void std_init_property_pages()
2121 {
2122         PROPSHEETPAGE *p;
2123         
2124         // connect tab
2125         p = &Pages[CONNECT_PAGE];
2126         p->dwSize = sizeof(PROPSHEETPAGE);
2127         p->dwFlags = PSP_DEFAULT;
2128         p->hInstance = GetModuleHandle(NULL);
2129         p->pszTemplate = MAKEINTRESOURCE(IDD_CONNECT);
2130         p->pszIcon = NULL;
2131         p->pfnDlgProc = (DLGPROC)connect_proc;
2132         p->pszTitle = XSTR("Connections",930);
2133         p->lParam = 0;
2134         p->pfnCallback = NULL;
2135
2136    // multiplayer tab
2137         p = &Pages[MULTIPLAYER_PAGE];
2138         p->dwSize = sizeof(PROPSHEETPAGE);
2139         p->dwFlags = PSP_DEFAULT;
2140         p->hInstance = GetModuleHandle(NULL);
2141         p->pszTemplate = MAKEINTRESOURCE(IDD_MULTI);
2142         p->pszIcon = NULL;
2143         p->pfnDlgProc = (DLGPROC)multi_proc;
2144         p->pszTitle = XSTR("Multi-Player",931);
2145         p->lParam = 0;
2146         p->pfnCallback = NULL;
2147
2148    // player tab
2149         p = &Pages[PLAYER_INFO_PAGE];
2150         p->dwSize = sizeof(PROPSHEETPAGE);
2151         p->dwFlags = PSP_DEFAULT;
2152         p->hInstance = GetModuleHandle(NULL);
2153         p->pszTemplate = MAKEINTRESOURCE(IDD_PLAYER_DIALOG);
2154         p->pszIcon = NULL;
2155         p->pfnDlgProc = (DLGPROC)player_info_proc;
2156         p->pszTitle = XSTR("Player info",932);
2157         p->lParam = 0;
2158         p->pfnCallback = NULL;
2159
2160    // godstuff tab
2161         p = &Pages[GODSTUFF_PAGE];
2162         p->dwSize = sizeof(PROPSHEETPAGE);
2163         p->dwFlags = PSP_DEFAULT;
2164         p->hInstance = GetModuleHandle(NULL);
2165         p->pszTemplate = MAKEINTRESOURCE(IDD_GODSTUFF);
2166         p->pszIcon = NULL;
2167         p->pfnDlgProc = (DLGPROC)godstuff_proc;
2168         p->pszTitle = XSTR("God Stuff",933);
2169         p->lParam = 0;
2170         p->pfnCallback = NULL;
2171
2172         // debug tab
2173         p = &Pages[DEBUG_PAGE];
2174         p->dwSize = sizeof(PROPSHEETPAGE);
2175         p->dwFlags = PSP_DEFAULT;
2176         p->hInstance = GetModuleHandle(NULL);
2177         p->pszTemplate = MAKEINTRESOURCE(IDD_DEBUG_DIALOG);
2178         p->pszIcon = NULL;
2179         p->pfnDlgProc = (DLGPROC)debug_proc;
2180         p->pszTitle = XSTR("Debug",934);
2181         p->lParam = 0;
2182         p->pfnCallback = NULL;
2183 }
2184
2185 // build a title string
2186 void std_build_title_string(char *str)
2187 {
2188         char part1[256];
2189         char cat[256];
2190         char temp[256];
2191
2192         // build the version #
2193         memset(part1, 0, 256);
2194         memset(cat, 0, 256);
2195         memset(temp, 0, 256);
2196
2197         sprintf(part1, "%s %d.", XSTR("Freespace Standalone",935), FS_VERSION_MAJOR);
2198         if(FS_VERSION_MINOR < 10){
2199                 strcpy(cat, "0");
2200                 strcat(cat, itoa(FS_VERSION_MINOR, temp, 10));
2201         } else {
2202                 sprintf(cat, "%d", FS_VERSION_MINOR);
2203         }
2204         strcat(part1, cat);
2205         strcat(part1, ".");
2206         if(FS_VERSION_BUILD < 10){
2207                 strcpy(cat, "0");
2208                 strcat(cat, itoa(FS_VERSION_BUILD, temp, 10));
2209         } else {
2210                 sprintf(cat, "%d", FS_VERSION_BUILD);
2211         }
2212         strcat(part1, cat);     
2213
2214         // first part
2215         strcpy(str, part1);
2216
2217 #ifdef STANDALONE_ONLY_BUILD
2218         sprintf(cat, "   %s %d", "Release", STANDALONE_ONLY_RELEASE_VERSION);
2219         strcat(str, cat);
2220 #endif
2221 }
2222
2223 // initialize the property sheet itself
2224 HWND std_init_property_sheet(HWND hwndDlg)
2225 {
2226         LONG styles;
2227
2228         // initialize the property pages
2229         std_init_property_pages();
2230         
2231         // create the property sheet
2232         Sheet.dwSize = sizeof(PROPSHEETHEADER);
2233         Sheet.dwFlags = PSH_PROPSHEETPAGE | PSH_MODELESS | PSH_NOAPPLYNOW;
2234         Sheet.hwndParent = hwndDlg;
2235         Sheet.hInstance = GetModuleHandle(NULL);
2236         Sheet.nPages = MAX_STANDALONE_PAGES;
2237
2238         // set the title bar appropriately
2239         char title_str[512];
2240         memset(title_str, 0, 512);
2241         std_build_title_string(title_str);
2242         Sheet.pszCaption = title_str;
2243
2244         Sheet.nStartPage = 0;
2245         Sheet.ppsp = &Pages[0];
2246         Sheet.pfnCallback = NULL;
2247         Psht = (HWND)PropertySheet(&Sheet);
2248
2249         // set the window style to include a minimize button
2250         //styles = GetWindowLong(Psht, GWL_STYLE );
2251         //if ( styles != 0 ) {
2252         //      SetWindowLong(Psht, GWL_STYLE, styles | WS_MINIMIZEBOX );
2253         //}
2254
2255         styles = GetWindowLong(Psht, GWL_EXSTYLE );
2256         if ( styles != 0 ) {
2257                 SetWindowLong( Psht, GWL_EXSTYLE, (styles & ~WS_EX_CONTEXTHELP) );
2258         }
2259
2260         memset(Multi_options_g.std_pname, 0, MAX_GAMENAME_LEN+1);
2261
2262         // change the default property sheet interface into something more useful
2263         std_mutate_sheet();
2264
2265         // return a handle to this property sheet
2266         return Psht;
2267 }
2268
2269 extern int Lighting_flag;
2270
2271 BOOL std_create_standalone_window()
2272 {               
2273         Standalone_hwnd = std_init_property_sheet(NULL);
2274
2275         // this is kind of a big-ass hack. But here's what it does. Property sheets only 
2276         // initialize their individual pages the first time (and ONLY the first time) they
2277         // are selected. So before any of this happens, their control handles are bogus. So,
2278         // by calling PropSheet_SetCurSel for all the pages, WM_INITDIALOG is sent to all of
2279         // them, and their controls become valid. However, its kind of silly because each 
2280         // page will blink into existence for a second. I can't see another way arount this
2281         // problem.
2282         int idx;
2283         for(idx=MAX_STANDALONE_PAGES-1;idx>=0;idx--){
2284            PropSheet_SetCurSel(Psht,(HPROPSHEETPAGE)&Pages[idx],idx);
2285         }
2286
2287 //   main_window_inited = 1;
2288
2289         // turn off lighting effects
2290         Lighting_flag = 0;
2291
2292         // turn off all sound and music
2293         Cmdline_freespace_no_sound = 1;
2294         Cmdline_freespace_no_music = 1;
2295
2296    // reset all standalone gui items
2297         std_reset_standalone_gui();
2298
2299         // initialize the debug outwindow
2300         #ifndef NDEBUG
2301                 outwnd_init();
2302         #endif
2303
2304         return TRUE;
2305 }
2306
2307 // just like the osapi version for the nonstandalone mode of Freespace
2308 DWORD standalone_process(WORD lparam)
2309 {
2310         MSG msg;
2311
2312         if ( !std_create_standalone_window() )
2313                 return 0;
2314
2315         while (1)       {       
2316 #if 0
2317            if(PropSheet_GetCurrentPageHwnd(Psht)==NULL){
2318                         mprintf(("prop sheet is destroyed -- exiting\n"));
2319                         DestroyWindow(Psht);
2320                         PostQuitMessage(0);
2321                         os_close();
2322                         std_deinit_standalone();
2323                         break;
2324                 }
2325 #endif
2326                 if (WaitMessage())      {
2327                         while(PeekMessage(&msg,0,0,0,PM_REMOVE))        {
2328
2329                                 // if the dialog should be destroyed, then exit.
2330                                 if ( msg.message == WM_DESTROY )        {
2331                                         DestroyWindow(Psht);
2332                                         PostQuitMessage(0);
2333                                         gameseq_post_event(GS_EVENT_QUIT_GAME);
2334                                         return 0;
2335                                 }
2336
2337                                 // see if the message is destined for the edit control, and what the message is.
2338                                 // intercept if a return
2339                                 if ( msg.hwnd == Godstuff_broadcast_text ) {
2340                                         int virt_key;
2341
2342                                         virt_key = (int)msg.wParam;
2343                                         if ( (msg.message == WM_KEYDOWN) && (virt_key == VK_RETURN) ) {
2344                                                 std_gs_send_godstuff_message();
2345                                                 continue;
2346                                         }
2347                                 }
2348
2349                                 TranslateMessage(&msg);
2350                                 DispatchMessage(&msg);
2351                         }
2352                 }
2353         }
2354         return 0;
2355 }
2356
2357 // called when freespace initialized
2358 void std_init_standalone()
2359 {
2360         // start the main thread
2361         Standalone_thread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)standalone_process, NULL, 0, &Standalone_thread_id );
2362
2363         os_init_registry_stuff(Osreg_company_name, Osreg_app_name,NULL);
2364         
2365         // set the close functions
2366         atexit(std_deinit_standalone);
2367 }
2368
2369 // called when freespace closes
2370 void std_deinit_standalone()
2371 {       
2372         if (Standalone_thread)  {
2373                 CloseHandle(Standalone_thread);
2374                 Standalone_thread = NULL;
2375         }
2376 }
2377