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