]> icculus.org git repositories - taylor/freespace2.git/blob - src/missionui/chatbox.cpp
Initial revision
[taylor/freespace2.git] / src / missionui / chatbox.cpp
1 /*
2  * $Logfile: /Freespace2/code/MissionUI/Chatbox.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * C module to handle all code for multiplayer chat windows
8  *
9  * $Log$
10  * Revision 1.1  2002/05/03 03:28:10  root
11  * Initial revision
12  *
13  * 
14  * 12    8/05/99 4:05p Jefff
15  * fixed pause chatbox display coords
16  * 
17  * 11    7/29/99 10:47p Dave
18  * Standardized D3D fogging using vertex fog. Shook out Savage 4 bugs.
19  * 
20  * 10    5/22/99 5:35p Dave
21  * Debrief and chatbox screens. Fixed small hi-res HUD bug.
22  * 
23  * 9     2/24/99 2:25p Dave
24  * Fixed up chatbox bugs. Made squad war reporting better. Fixed a respawn
25  * bug for dogfight more.
26  * 
27  * 8     2/23/99 8:11p Dave
28  * Tidied up dogfight mode. Fixed TvT ship type problems for alpha wing.
29  * Small pass over todolist items.
30  * 
31  * 7     2/11/99 3:08p Dave
32  * PXO refresh button. Very preliminary squad war support.
33  * 
34  * 6     2/01/99 5:55p Dave
35  * Removed the idea of explicit bitmaps for buttons. Fixed text
36  * highlighting for disabled gadgets.
37  * 
38  * 5     1/29/99 2:08a Dave
39  * Fixed beam weapon collisions with players. Reduced size of scoring
40  * struct for multiplayer. Disabled PXO.
41  * 
42  * 4     1/28/99 7:09p Neilk
43  * Modified chatbox to use new interface graphics (only 640)
44  * 
45  * 3     10/13/98 9:28a Dave
46  * Started neatening up freespace.h. Many variables renamed and
47  * reorganized. Added AlphaColors.[h,cpp]
48  * 
49  * 2     10/07/98 10:53a Dave
50  * Initial checkin.
51  * 
52  * 1     10/07/98 10:49a Dave
53  * 
54  * 57    9/17/98 3:08p Dave
55  * PXO to non-pxo game warning popup. Player icon stuff in create and join
56  * game screens. Upped server count refresh time in PXO to 35 secs (from
57  * 20).
58  * 
59  * 56    9/11/98 5:08p Dave
60  * More tweaks to kick notification system.
61  * 
62  * 55    9/11/98 4:14p Dave
63  * Fixed file checksumming of < file_size. Put in more verbose kicking and
64  * PXO stats store reporting.
65  * 
66  * 54    6/09/98 5:15p Lawrance
67  * French/German localization
68  * 
69  * 53    6/09/98 10:31a Hoffoss
70  * Created index numbers for all xstr() references.  Any new xstr() stuff
71  * added from here on out should be added to the end if the list.  The
72  * current list count can be found in FreeSpace.cpp (search for
73  * XSTR_SIZE).
74  * 
75  * 52    6/01/98 11:43a John
76  * JAS & MK:  Classified all strings for localization.
77  * 
78  * 51    5/22/98 9:35p Dave
79  * Put in channel based support for PXO. Put in "shutdown" button for
80  * standalone. UI tweaks for TvT
81  * 
82  * 50    5/17/98 6:32p Dave
83  * Make sure clients/servers aren't kicked out of the debriefing when team
84  * captains leave a game. Fixed chatbox off-by-one error. Fixed image
85  * xfer/pilot info popup stuff.
86  * 
87  * 49    5/17/98 1:43a Dave
88  * Eradicated chatbox problems. Remove speed match for observers. Put in
89  * help screens for PXO. Fix messaging and end mission privelges. Fixed
90  * team select screen bugs. Misc UI fixes.
91  * 
92  * 48    5/15/98 9:52p Dave
93  * Added new stats for freespace. Put in artwork for viewing stats on PXO.
94  * 
95  * 47    5/15/98 5:15p Dave
96  * Fix a standalone resetting bug.Tweaked PXO interface. Display captaincy
97  * status for team vs. team. Put in asserts to check for invalid team vs.
98  * team situations.
99  * 
100  * 46    5/08/98 7:08p Dave
101  * Lots of UI tweaking.
102  * 
103  * 45    5/07/98 6:26p Dave
104  * Fix strange boundary conditions which arise when players die/respawn
105  * while the game is being ended. Spiff up the chatbox doskey thing a bit.
106  * 
107  * 44    5/04/98 10:39p Dave
108  * Put in endgame sequencing.  Need to check campaign situations.
109  * Realigned ship info on team select screen.
110  * 
111  * 43    5/02/98 5:38p Dave
112  * Put in new tracker API code. Put in ship information on mp team select
113  * screen. Make standalone server name permanent. Fixed standalone server
114  * text messages.
115  * 
116  * 42    4/29/98 6:00p Dave
117  * Fixed chatbox font colors. Made observer offscreen indicators work.
118  * Numerous small UI fixes. Fix rank limitations for mp games. 
119  * 
120  * 41    4/25/98 3:14p Dave
121  * Fixed chatbox clipping problem.
122  * 
123  * 40    4/16/98 6:34p Dave
124  * Fixed reversed team vs. team colors.
125  * 
126  * 39    4/14/98 5:06p Dave
127  * Don't load or send invalid pilot pics. Fixed chatbox graphic errors.
128  * Made chatbox display team icons in a team vs. team game. Fixed up pause
129  * and endgame sequencing issues.
130  * 
131  * 38    4/13/98 7:48p Dave
132  * Fixed chatbox overrun errors. Put in line recall function (4 deep).
133  * 
134  * 37    4/12/98 2:09p Dave
135  * Make main hall door text less stupid. Make sure inputbox focus in the
136  * multi host options screen is managed more intelligently.
137  * 
138  * 36    4/01/98 11:19p Dave
139  * Put in auto-loading of xferred pilot pic files. Grey out background
140  * behind pinfo popup. Put a chatbox message in when players are kicked.
141  * Moved mission title down in briefing. Other ui fixes.
142  * 
143  * 35    3/31/98 4:42p Allender
144  * mission objective support for team v. team mode.  Chatbox changes to
145  * make input box be correct length when typing
146  * 
147  * 34    3/29/98 1:24p Dave
148  * Make chatbox not clear between multiplayer screens. Select player ship
149  * as default in mp team select and weapons select screens. Made create
150  * game mission list use 2 fixed size columns.
151  * 
152  * 33    3/19/98 5:05p Dave
153  * Put in support for targeted multiplayer text and voice messaging (all,
154  * friendly, hostile, individual).
155  * 
156  * 32    3/18/98 12:03p John
157  * Marked all the new strings as externalized or not.
158  * 
159  * 31    3/17/98 12:30a Dave
160  * Put in hud support for rtvoice. Several ui interface changes.
161  * 
162  * 30    3/10/98 10:59p Dave
163  * Fixed single player pause screen. Put in temporary fix for multiplayer
164  * version. Fixed several chatbox display and text string bugs.
165  * 
166  * 29    2/27/98 9:41a Dave
167  * Made "up/down" arrows flip direction when toggling chatbox sizes.
168  * 
169  * 28    2/26/98 4:21p Dave
170  * More robust multiplayer voice.
171  * 
172  * 27    2/22/98 4:17p John
173  * More string externalization classification... 190 left to go!
174  * 
175  * 26    2/22/98 12:19p John
176  * Externalized some strings
177  * 
178  * 25    2/13/98 3:46p Dave
179  * Put in dynamic chatbox sizing. Made multiplayer file lookups use cfile
180  * functions.
181  * 
182  * 24    2/04/98 10:50p Allender
183  * zero out chat line before storing text
184  * 
185  * 23    1/29/98 5:23p Dave
186  * Made ingame join handle bad packets gracefully.
187  * 
188  * 22    1/23/98 5:43p Dave
189  * Finished bringing standalone up to speed. Coded in new host options
190  * screen.
191  * 
192  * 21    1/17/98 5:51p Dave
193  * Bug fixes for bugs generated by multiplayer testing.
194  * 
195  * 20    1/16/98 5:23p Allender
196  * more chatbox changes
197  * 
198  * 19    1/16/98 4:28p Allender
199  * automatically send line when close to end of chatbox.  
200  * 
201  * 18    1/16/98 2:34p Dave
202  * Made pause screen work properly (multiplayer). Changed how chat packets
203  * work.
204  * 
205  * 17    1/15/98 6:12p Dave
206  * Fixed weapons loadout bugs with multiplayer respawning. Added
207  * multiplayer start screen. Fixed a few chatbox bugs.
208  * 
209  * 16    1/15/98 5:10p Allender
210  * ton of interface changes.  chatbox in multiplayer now behaves
211  * differently than before.  It's always active in any screen that uses
212  * it.  Only non-printatble characters will get passed back out from
213  * chatbox
214  * 
215  * 15    1/12/98 5:17p Dave
216  * Put in a bunch of multiplayer sequencing code. Made weapon/ship select
217  * work through the standalone.
218  * 
219  * 14    1/07/98 5:20p Dave
220  * Put in support for multiplayer campaigns with the new interface
221  * screens.
222  * 
223  * 13    1/05/98 5:06p Dave
224  * Fixed a chat packet bug. Fixed a few state save/restore bugs. Updated a
225  * few things for multiplayer server transfer.
226  * 
227  * 12    12/29/97 3:15p Dave
228  * Put in auto-indenting for multiplayer chatbox. Tweaked some multiplayer
229  * interface code.
230  * 
231  * 11    12/22/97 9:13p Allender
232  * fixed up a few minor issues with chatbox
233  * 
234  * 10    12/19/97 7:08p Jasen
235  * Updated coords for new ChatBox (small)
236  * 
237  * 9     12/18/97 8:59p Dave
238  * Finished putting in basic support for weapon select and ship select in
239  * multiplayer.
240  * 
241  * 8     12/06/97 4:27p Dave
242  * Another load of interface and multiplayer bug fixes.
243  * 
244  * 7     12/03/97 4:16p Hoffoss
245  * Changed sound stuff used in interface screens for interface purposes.
246  * 
247  * 6     11/12/97 4:40p Dave
248  * Put in multiplayer campaign support parsing, loading and saving. Made
249  * command-line variables better named. Changed some things on the initial
250  * pilot select screen.
251  * 
252  * 5     10/24/97 10:59p Hoffoss
253  * Added in create pilot popup window and barracks screen.
254  * 
255  * 4     10/08/97 5:08p Lawrance
256  * use mask region for chat box inputbox, so getting focus is easier
257  * 
258  * 3     10/01/97 4:47p Lawrance
259  * move some #defines out of header file into .cpp file
260  * 
261  * 2     10/01/97 4:39p Lawrance
262  * move chat code into Chatbox.cpp, simplify interface
263  * 
264  * 1     10/01/97 10:54a Lawrance
265  *
266  * $NoKeywords: $
267  */
268
269 #include "ui.h"
270 #include "mouse.h"
271 #include "freespace.h"
272 #include "chatbox.h"
273 #include "missionscreencommon.h"
274 #include "multimsgs.h"
275 #include "gamesnd.h"
276 #include "bmpman.h"
277 #include "cmdline.h"
278 #include "key.h"
279 #include "multi.h"
280 #include "multiui.h"
281 #include "multi_pmsg.h"
282 #include "alphacolors.h"
283
284 ///////////////////////////////////////////////////////////
285 // Chat window UI 
286 ///////////////////////////////////////////////////////////
287
288 // a little extra spacing for the team vs. team icons
289 #define CHATBOX_TEAM_ICON_SPACE                 18
290
291 // SMALL CHATBOX ----------------------------------------------------------------------------------
292
293 // background bitmap
294 char* Chatbox_small_bitmap_fname[GR_NUM_RESOLUTIONS] = {
295         "Chatbox",              // GR_640
296         "2_Chatbox"             // GR_1024
297 };
298
299 // background mask
300 char* Chatbox_small_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
301         "Chatbox-m",    // GR_640
302         "2_Chatbox-m"   // GR_1024
303 };
304
305 // chatbox coords
306 int Chatbox_small_coords[GR_NUM_RESOLUTIONS][2] = {
307         { // GR_640
308                 192, 0
309         },
310         { // GR_1024
311                 308, 0                   // ?
312         }
313 };
314
315 // display area coods
316 int Chatbox_small_display_coords[GR_NUM_RESOLUTIONS][4] = {
317         {       // GR_640
318                 196 + CHATBOX_TEAM_ICON_SPACE, 13, 410 - CHATBOX_TEAM_ICON_SPACE, 74
319         },
320         {       // GR_1024
321                 315 + CHATBOX_TEAM_ICON_SPACE, 22, 654 - CHATBOX_TEAM_ICON_SPACE, 116
322         }
323 };
324
325 // input box coords
326 int Chatbox_small_input_coords[GR_NUM_RESOLUTIONS][4] = {
327         {       // GR_640
328                 204, 100, 371, 22
329         },
330         {       // GR_1024
331                 328, 163, 591, 34
332         }
333 };
334
335 // max # of lines
336 int Chatbox_small_max_lines[GR_NUM_RESOLUTIONS] = {
337         7,                              // GR_640
338         12                              // GR_1024
339 };
340
341 // BIG CHATBOX ----------------------------------------------------------------------------------
342
343 // background bitmap
344 char* Chatbox_big_bitmap_fname[GR_NUM_RESOLUTIONS] = {
345         "ChatboxBig",           // GR_640
346         "2_ChatboxBig"          // GR_1024
347 };
348
349 // mask
350 char* Chatbox_big_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
351         "Chatbox-m",            // GR_640
352         "2_Chatbox-m"                   // GR_1024
353 };
354
355 // chatbox coords
356 int Chatbox_big_coords[GR_NUM_RESOLUTIONS][2] = {
357         { // GR_640
358                 192, 0
359         },
360         { // GR_1024
361                 307, 0
362         }
363 };
364
365 // display area coords
366 int Chatbox_big_display_coords[GR_NUM_RESOLUTIONS][4] = {
367         {       // GR_640
368                 196 + CHATBOX_TEAM_ICON_SPACE, 13, 410 - CHATBOX_TEAM_ICON_SPACE, 326
369         },
370         {       // GR_1024
371                 315 + CHATBOX_TEAM_ICON_SPACE, 22, 654 - CHATBOX_TEAM_ICON_SPACE, 519
372         }
373 };
374
375 // input box coords
376 int Chatbox_big_input_coords[GR_NUM_RESOLUTIONS][4] = {
377         {       // GR_640
378                 204, 352, 371, 22
379         },
380         {       // GR_1024
381                 328, 565, 591, 34
382         }
383 };
384
385 // max # of lines
386 int Chatbox_big_max_lines[GR_NUM_RESOLUTIONS] = {
387         32,                     // GR_640
388         51                              // GR_1024
389 };
390
391 // PAUSED CHATBOX ----------------------------------------------------------------------------------
392
393 // mask
394 char* Chatbox_p_bitmap_fname[GR_NUM_RESOLUTIONS] = {
395         "MPPause",                      // GR_640
396         "2_MPPause"                     // GR_1024
397 };
398
399 // mask
400 char* Chatbox_p_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
401         "MPPause-m",            // GR_640
402         "2_MPPause-m"           // GR_1024
403 };
404
405 // chatbox coords
406 int Chatbox_p_coords[GR_NUM_RESOLUTIONS][2] = {
407         { // GR_640
408                 192, 0
409         },
410         { // GR_1024
411                 307, 0
412         }
413 };
414
415 // display area coords
416 int Chatbox_p_display_coords[GR_NUM_RESOLUTIONS][4] = {
417         {       // GR_640
418                 103 + CHATBOX_TEAM_ICON_SPACE, 149, 380 - CHATBOX_TEAM_ICON_SPACE, 143
419         },
420         {       // GR_1024
421                 165 + CHATBOX_TEAM_ICON_SPACE, 244, 654 - CHATBOX_TEAM_ICON_SPACE, 263
422         }
423 };
424
425 // input box coords
426 int Chatbox_p_input_coords[GR_NUM_RESOLUTIONS][4] = {
427         {       // GR_640
428                 106, 328, 379, 17
429         },
430         {       // GR_1024
431                 165, 525, 607, 27
432         }
433 };
434
435 // max # of lines
436 int Chatbox_p_max_lines[GR_NUM_RESOLUTIONS] = {
437         17,                     // GR_640
438         26                              // GR_1024
439 };
440
441 // defines for location and other info of chat box MULTI_PAUSED
442 /*
443 #define CHATBOX_MP_FNAME                                        NOX("MPPause")
444 #define CHATBOX_MP_MASK                                         NOX("MPPause-m")
445 #define CHATBOX_MP_X1                                           112
446 #define CHATBOX_MP_Y1                                           198
447 #define CHATBOX_MP_X2                                           477
448 #define CHATBOX_MP_Y2                                           308
449 #define CHATBOX_MP_ICON_X                                       (CHATBOX_MP_X1)
450 #define CHATBOX_MP_W                                                    (CHATBOX_MP_X2 - CHATBOX_MP_X1 + 1)
451 #define CHATBOX_MP_H                                                    (CHATBOX_MP_Y2 - CHATBOX_MP_Y1 + 1)
452 #define CHATBOX_MP_BEGIN_X                                      (CHATBOX_MP_X1 + 3 + CHATBOX_TEAM_ICON_SPACE)
453 #define CHATBOX_MP_BEGIN_Y                                      CHATBOX_MP_Y1
454 #define CHATBOX_MP_DISP_W                                       365
455 #define CHATBOX_MP_MAX_LINES                            11
456 #define CHATBOX_MP_INPUTBOX_X                           (CHATBOX_MP_X1 + 3)
457 #define CHATBOX_MP_INPUTBOX_W                           (CHATBOX_MP_W)
458 #define CHATBOX_MP_TEXTENTER_Y                  329
459 */
460
461 // CHATBOX ----------------------------------------------------------------------------------
462
463 // the settings being used for this instance
464 char Chatbox_mask[50];
465 int Chatbox_x1;
466 int Chatbox_y1;
467 int Chatbox_icon_x;
468 int Chatbox_w;
469 int Chatbox_h;
470 int Chatbox_begin_x;
471 int Chatbox_begin_y;
472 int Chatbox_disp_w;
473 int Chatbox_max_lines;
474 int Chatbox_inputbox_x;
475 int Chatbox_inputbox_w;
476 int Chatbox_textenter_y;
477 int Chat_scroll_up_coord[2];
478 int Chat_scroll_down_coord[2];
479
480 // how many pixels to indent succesive lines of text from a given player
481 #define CHAT_LINE_INDENT                                        20
482
483 // what chars other than letters and number's we'll toss
484 #define CHATBOX_INVALID_CHARS                           NOX("~`")                       // this is primarily so that we don't interfere with the voice recording keys
485
486 // common defines and data
487 #define CHATBOX_STRING_LEN                                      (CALLSIGN_LEN + CHATBOX_MAX_LEN + 32)
488 UI_WINDOW       Chat_window;
489 UI_INPUTBOX     Chat_inputbox;
490 UI_BUTTON Chat_enter_text;
491
492 // button controls
493 #define CHATBOX_NUM_BUTTONS                             3
494 #define CHATBOX_SCROLL_UP                                       0
495 #define CHATBOX_SCROLL_DOWN                             1
496 #define CHATBOX_TOGGLE_SIZE                             2                               // used for both big and small
497
498 // coordinate indicies
499 #define CHATBOX_X_COORD 0
500 #define CHATBOX_Y_COORD 1
501 #define CHATBOX_W_COORD 2
502 #define CHATBOX_H_COORD 3
503
504 // chatbox buttons
505 ui_button_info Chatbox_buttons[GR_NUM_RESOLUTIONS][CHATBOX_NUM_BUTTONS+1] = {
506         { // GR_640
507                 ui_button_info("CHB_00",        613,    3,              -1,     -1,     0),
508                 ui_button_info("CHB_01",        613,    41,     -1,     -1,     1),
509                 ui_button_info("CHB_02a",       607,    74,     -1,     -1,     2),
510                 ui_button_info("CHB_02b",       607,    74,     -1,     -1,     2),
511         },      
512         { // GR_1024
513                 ui_button_info("2_CHB_00",              981,    5,              -1,     -1,     0),
514                 ui_button_info("2_CHB_01",              981,    67,     -1,     -1,     1),
515                 ui_button_info("2_CHB_02a",     971,    119,    -1,     -1,     2),
516                 ui_button_info("2_CHB_02b",     971,    119,    -1,     -1,     2),
517         }
518 };
519
520 int Chatbox_mode_flags = 0;
521
522 int Chatbox_bitmap = -1;
523 int Chatbox_big_bitmap = -1;
524 int Chatbox_small_bitmap = -1;
525 int Chatbox_mp_bitmap = -1;
526 int Chatbox_created = 0;
527
528 ///////////////////////////////////////////////////////////
529 // Chat window text
530 ///////////////////////////////////////////////////////////
531 #define MAX_BRIEF_CHAT_LINES 60   // how many lines we can store in the scrollback buffer
532 #define BRIEF_DISPLAY_SPACING 2   // pixel spacing between chat lines
533
534 // the first byte of the text string will be the net player id of the 
535 char Brief_chat_lines[MAX_BRIEF_CHAT_LINES][CHATBOX_STRING_LEN];
536 int Brief_chat_indents[MAX_BRIEF_CHAT_LINES];
537
538 int Brief_chat_next_index[MAX_BRIEF_CHAT_LINES];
539 int Brief_chat_prev_index[MAX_BRIEF_CHAT_LINES];
540 int Num_brief_chat_lines;
541 int Brief_current_add_line;
542 int Brief_start_display_index;
543
544 // chatbox line recall data
545 #define CHATBOX_MAX_RECALL_LINES                        10
546 int Chatbox_recall_count = 0;
547 int Chatbox_recall_index = 0;
548 int Chatbox_recall_last = -1;
549 char Chatbox_recall_lines[CHATBOX_MAX_RECALL_LINES][CHATBOX_MAX_LEN+2];
550
551 ///////////////////////////////////////////////////////////
552 // forward declarations
553 ///////////////////////////////////////////////////////////
554 void chatbox_chat_init();
555 void chatbox_render_chat_lines();
556 void chatbox_set_mode(int mode_flags);
557 void chatbox_toggle_size();
558 void chatbox_toggle_size_adjust_lines();
559 void chat_autosplit_line(char *msg,char *remainder);
560 int chatbox_num_displayed_lines();
561 void chatbox_recall_add(char *string);
562 void chatbox_recall_up();
563 void chatbox_recall_down();
564
565 // set the chatbox mode without checking for any previous modes which may need to be handled specially
566 void chatbox_set_mode(int mode_flags)
567 {
568         int size;
569         
570         // set the stored mode
571         Chatbox_mode_flags = mode_flags;
572         
573         // small pregame chatbox
574         if(Chatbox_mode_flags & CHATBOX_FLAG_SMALL){
575                 size = 0;
576         }
577         // big pregame chatbox
578         else if(Chatbox_mode_flags & CHATBOX_FLAG_BIG){
579                 size = 1;
580         }
581         // multiplayer paused
582         else {
583                 size = 2;
584         }
585
586         // set up the display/init variables based upon what mode we chode
587         switch(size){
588         case 0:         
589                 strcpy(Chatbox_mask, Chatbox_small_bitmap_mask_fname[gr_screen.res]);
590                 Chatbox_x1 = Chatbox_small_coords[gr_screen.res][CHATBOX_X_COORD];
591                 Chatbox_y1 = Chatbox_small_coords[gr_screen.res][CHATBOX_Y_COORD];              
592                 Chatbox_icon_x = Chatbox_small_display_coords[gr_screen.res][CHATBOX_X_COORD] - CHATBOX_TEAM_ICON_SPACE;
593                 Chatbox_w = Chatbox_small_display_coords[gr_screen.res][CHATBOX_W_COORD];
594                 Chatbox_h = Chatbox_small_display_coords[gr_screen.res][CHATBOX_H_COORD];
595                 Chatbox_begin_x = Chatbox_small_display_coords[gr_screen.res][CHATBOX_X_COORD];
596                 Chatbox_begin_y = Chatbox_small_display_coords[gr_screen.res][CHATBOX_Y_COORD];
597                 Chatbox_disp_w = Chatbox_small_display_coords[gr_screen.res][CHATBOX_W_COORD];
598                 Chatbox_max_lines = Chatbox_small_max_lines[gr_screen.res];
599                 Chatbox_inputbox_x = Chatbox_small_input_coords[gr_screen.res][CHATBOX_X_COORD];
600                 Chatbox_inputbox_w = Chatbox_small_input_coords[gr_screen.res][CHATBOX_W_COORD];
601                 Chatbox_textenter_y = Chatbox_small_input_coords[gr_screen.res][CHATBOX_Y_COORD];               
602                 break;
603
604         case 1:         
605                 strcpy(Chatbox_mask, Chatbox_big_bitmap_mask_fname[gr_screen.res]);
606                 Chatbox_x1 = Chatbox_big_coords[gr_screen.res][CHATBOX_X_COORD];
607                 Chatbox_y1 = Chatbox_big_coords[gr_screen.res][CHATBOX_Y_COORD];                
608                 Chatbox_icon_x = Chatbox_big_display_coords[gr_screen.res][CHATBOX_X_COORD] - CHATBOX_TEAM_ICON_SPACE;
609                 Chatbox_w = Chatbox_big_display_coords[gr_screen.res][CHATBOX_W_COORD];
610                 Chatbox_h = Chatbox_big_display_coords[gr_screen.res][CHATBOX_H_COORD];
611                 Chatbox_begin_x = Chatbox_big_display_coords[gr_screen.res][CHATBOX_X_COORD];
612                 Chatbox_begin_y = Chatbox_big_display_coords[gr_screen.res][CHATBOX_Y_COORD];
613                 Chatbox_disp_w = Chatbox_big_display_coords[gr_screen.res][CHATBOX_W_COORD];
614                 Chatbox_max_lines = Chatbox_big_max_lines[gr_screen.res];
615                 Chatbox_inputbox_x = Chatbox_big_input_coords[gr_screen.res][CHATBOX_X_COORD];
616                 Chatbox_inputbox_w = Chatbox_big_input_coords[gr_screen.res][CHATBOX_W_COORD];
617                 Chatbox_textenter_y = Chatbox_big_input_coords[gr_screen.res][CHATBOX_Y_COORD];         
618                 break;
619
620         case 2:                         
621                 Chatbox_x1 = Chatbox_p_coords[gr_screen.res][CHATBOX_X_COORD];
622                 Chatbox_y1 = Chatbox_p_coords[gr_screen.res][CHATBOX_Y_COORD];          
623                 Chatbox_icon_x = Chatbox_p_display_coords[gr_screen.res][CHATBOX_X_COORD] - CHATBOX_TEAM_ICON_SPACE;
624                 Chatbox_w = Chatbox_p_display_coords[gr_screen.res][CHATBOX_W_COORD];
625                 Chatbox_h = Chatbox_p_display_coords[gr_screen.res][CHATBOX_H_COORD];
626                 Chatbox_begin_x = Chatbox_p_display_coords[gr_screen.res][CHATBOX_X_COORD];
627                 Chatbox_begin_y = Chatbox_p_display_coords[gr_screen.res][CHATBOX_Y_COORD];
628                 Chatbox_disp_w = Chatbox_p_display_coords[gr_screen.res][CHATBOX_W_COORD];
629                 Chatbox_max_lines = Chatbox_p_max_lines[gr_screen.res];
630                 Chatbox_inputbox_x = Chatbox_p_input_coords[gr_screen.res][CHATBOX_X_COORD];
631                 Chatbox_inputbox_w = Chatbox_p_input_coords[gr_screen.res][CHATBOX_W_COORD];
632                 Chatbox_textenter_y = Chatbox_p_input_coords[gr_screen.res][CHATBOX_Y_COORD];
633                 break;
634         }
635 }
636
637 // automatically split up any input text, send it, and leave the remainder 
638 void chatbox_autosplit_line()
639 {
640         char *remainder,msg[150];
641         int msg_pixel_width;
642         
643         // if the chat line is getting too long, fire off the message, putting the last
644         // word on the next input line.
645         memset(msg,0,150);
646         Chat_inputbox.get_text(msg);
647         remainder = "";
648         // determine if the width of the string in pixels is > than the inputbox width -- if so,
649         // then send the message
650         gr_get_string_size(&msg_pixel_width, NULL, msg);
651         // if ( msg_pixel_width >= (Chatbox_inputbox_w - Player->short_callsign_width) ) {
652         if ( msg_pixel_width >= (Chatbox_inputbox_w - 25)) {
653                 remainder = strrchr(msg, ' ');
654                 if ( remainder ) {
655                         *remainder = '\0';
656                         remainder++;
657                 } else {
658                         remainder = "";
659                 }       
660                 // if I'm the server, then broadcast the packet         
661                 chatbox_recall_add(msg);
662                 send_game_chat_packet(Net_player, msg, MULTI_MSG_ALL,NULL);
663                 chatbox_add_line(msg, MY_NET_PLAYER_NUM);
664
665                 // display any remainder of text on the next line
666                 Chat_inputbox.set_text(remainder);
667         } else if((Chat_inputbox.pressed() && (strlen(msg) > 0)) || (strlen(msg) >= CHATBOX_MAX_LEN)) { 
668                 // tack on the null terminator in the boundary case
669                 int x = strlen(msg);
670                 if(x >= CHATBOX_MAX_LEN){
671                         msg[CHATBOX_MAX_LEN-1] = '\0';
672                 }               
673                 // if I'm the server, then broadcast the packet         
674                 chatbox_recall_add(msg);
675                 send_game_chat_packet(Net_player, msg, MULTI_MSG_ALL,NULL);
676                 chatbox_add_line(msg, MY_NET_PLAYER_NUM);
677
678                 // display any remainder of text on the next line
679                 Chat_inputbox.set_text(remainder);              
680         }       
681 }
682
683 // initialize all chatbox details with the given mode flags
684 int chatbox_create(int mode_flags)
685 {
686         int idx;
687         
688         // don't do anything if the chatbox is already initialized
689         if (Chatbox_created){
690                 return -1;
691         }
692
693         // probably shouldn't be using the chatbox in single player mode
694         Assert(Game_mode & GM_MULTIPLAYER);
695
696         // setup all data to correspond to our mode flags
697         chatbox_set_mode(mode_flags);
698         
699         // initialize all low-level details related to chatting
700         chatbox_chat_init();            
701
702         // attempt to load in the chatbox background bitmap
703         if(Chatbox_mode_flags & CHATBOX_FLAG_DRAW_BOX){
704                 Chatbox_big_bitmap = bm_load(Chatbox_big_bitmap_fname[gr_screen.res]);
705                 Chatbox_small_bitmap = bm_load(Chatbox_small_bitmap_fname[gr_screen.res]);
706                 Chatbox_mp_bitmap = bm_load(Chatbox_p_bitmap_fname[gr_screen.res]);
707                 
708                 if(Chatbox_mode_flags & CHATBOX_FLAG_SMALL){
709                         Chatbox_bitmap = Chatbox_small_bitmap;
710                 } else if(Chatbox_mode_flags & CHATBOX_FLAG_BIG){
711                         Chatbox_bitmap = Chatbox_big_bitmap;
712                 } else {
713                         Chatbox_bitmap = Chatbox_mp_bitmap;
714                 }
715
716                 if((Chatbox_bitmap == -1) || (Chatbox_small_bitmap == -1) || (Chatbox_big_bitmap == -1) || (Chatbox_mp_bitmap == -1)){
717                         return -1;
718                 }
719         }
720         
721         // attempt to create the ui window for the chatbox and assign the mask
722         Chat_window.create( 0, 0, gr_screen.max_w, gr_screen.max_h, 0 );
723         Chat_window.set_mask_bmap(Chatbox_mask);        
724
725    // create the chat text enter input area     
726         Chat_inputbox.create( &Chat_window, Chatbox_inputbox_x, Chatbox_textenter_y, Chatbox_inputbox_w, CHATBOX_MAX_LEN, "", UI_INPUTBOX_FLAG_INVIS | UI_INPUTBOX_FLAG_ESC_CLR | UI_INPUTBOX_FLAG_KEYTHRU | UI_INPUTBOX_FLAG_EAT_USED, Chatbox_w, Color_netplayer[MY_NET_PLAYER_NUM]); 
727         Chat_inputbox.set_focus();
728         Chat_inputbox.set_invalid_chars(CHATBOX_INVALID_CHARS);
729
730         // if we're supposed to supply and check for out own buttons
731         if((Chatbox_mode_flags & CHATBOX_FLAG_BUTTONS) && (Chatbox_mode_flags & CHATBOX_FLAG_DRAW_BOX)){
732                 for(idx=0; idx<CHATBOX_NUM_BUTTONS; idx++){
733                         // create the button                    
734                         Chatbox_buttons[gr_screen.res][idx].button.create(&Chat_window, "", Chatbox_buttons[gr_screen.res][idx].x, Chatbox_buttons[gr_screen.res][idx].y, 60, 30, (idx == CHATBOX_TOGGLE_SIZE) ? 0 : 1);
735                         
736                         // set the highlight action
737                         Chatbox_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
738
739                         // set the bitmap
740                         Chatbox_buttons[gr_screen.res][idx].button.set_bmaps(Chatbox_buttons[gr_screen.res][idx].filename);
741
742                         // set the hotspot
743                         Chatbox_buttons[gr_screen.res][idx].button.link_hotspot(Chatbox_buttons[gr_screen.res][idx].hotspot);
744                 }
745                 
746                 // now create the toggle size button with the appropriate button
747                 if(Chatbox_mode_flags & CHATBOX_FLAG_SMALL){
748                         Chatbox_buttons[gr_screen.res][CHATBOX_TOGGLE_SIZE].button.set_bmaps(Chatbox_buttons[gr_screen.res][CHATBOX_TOGGLE_SIZE].filename);
749                 } else {
750                         Chatbox_buttons[gr_screen.res][CHATBOX_TOGGLE_SIZE].button.set_bmaps(Chatbox_buttons[gr_screen.res][CHATBOX_TOGGLE_SIZE+1].filename);
751                 }
752         }
753
754         // an invisible button that will set focus to input box when clicked on
755         Chat_enter_text.create( &Chat_window, "", 0, 0, 60, 30, 0);
756         Chat_enter_text.hide();                                 // button doesn't show up
757         Chat_enter_text.link_hotspot(50);       
758
759         Chatbox_created = 1;
760         return 0;
761 }
762
763 // process this frame for the chatbox
764 int chatbox_process(int key_in)
765 {       
766         int key_out;
767
768         key_out = key_in;
769
770         // if the chatbox hasn't explicitly been created, we can't do any processing
771         if(!Chatbox_created){
772                 return key_out;
773         }
774
775         // process the incoming key appropriately
776         if (key_in == -1) {
777                 key_out = Chat_window.process();
778         } else {
779                 key_out = Chat_window.process(key_in);
780         }
781
782         // look for special keypresses
783         switch(key_out){
784         // line recall up one
785         case KEY_UP:
786                 chatbox_recall_up();
787                 key_out = 0;
788                 break;
789         
790         // line recall down one
791         case KEY_DOWN:
792                 chatbox_recall_down();
793                 key_out = 0;
794                 break;
795         }
796
797         // if we're supposed to be checking our own scroll buttons
798         if((Chatbox_mode_flags & CHATBOX_FLAG_BUTTONS) && (Chatbox_mode_flags & CHATBOX_FLAG_DRAW_BOX)){
799                 if ( Chatbox_buttons[gr_screen.res][CHATBOX_SCROLL_UP].button.pressed() ) {
800                         chatbox_scroll_up();
801                 }
802
803                 if ( Chatbox_buttons[gr_screen.res][CHATBOX_SCROLL_DOWN].button.pressed() ) {
804                         chatbox_scroll_down();
805                 }
806
807                 if ( Chatbox_buttons[gr_screen.res][CHATBOX_TOGGLE_SIZE].button.pressed() ){
808                         chatbox_toggle_size();
809                 }
810         }
811
812         // check to see if the enter text button has been pressed
813         if ( Chat_enter_text.pressed() ) {
814                 Chat_inputbox.set_focus();
815         }
816
817         // check to see if the current input text needs to be split up and sent automaticall
818         chatbox_autosplit_line();       
819
820         return key_out;
821 }
822
823 void chatbox_close()
824 {
825         // destory the UI window
826         Chat_window.destroy();
827
828         // unload any bitmaps
829         if(Chatbox_small_bitmap != -1){
830                 bm_unload(Chatbox_small_bitmap);
831         }
832         if(Chatbox_big_bitmap != -1){
833                 bm_unload(Chatbox_big_bitmap);
834         }
835         if(Chatbox_mp_bitmap != -1){
836                 bm_unload(Chatbox_mp_bitmap);
837         }       
838
839         // clear all the text lines in the
840         chatbox_clear();
841         Chatbox_created = 0;
842 }
843
844 // shutdown all chatbox functionality
845 void chatbox_clear()
846 {
847         // only do this if we have valid data
848         if(Chatbox_created){
849                 // clear all chat data
850                 memset(Brief_chat_lines,0,(MAX_BRIEF_CHAT_LINES * (CHATBOX_MAX_LEN + 2)));              
851                 Brief_start_display_index=0;
852                 Num_brief_chat_lines=0;
853                 Brief_current_add_line=0;
854
855                 // clear all line recall data
856                 Chatbox_recall_count = 0;
857                 Chatbox_recall_index = 0;
858                 Chatbox_recall_last = -1;
859                 memset(Chatbox_recall_lines,0,CHATBOX_MAX_RECALL_LINES * (CHATBOX_MAX_LEN + 2));        
860         }
861 }
862
863 // render the chatbox for this frame
864 void chatbox_render()
865 {
866         if (!Chatbox_created){
867                 return;
868         }
869
870         // clear the multiplayer chat window
871         // gr_set_clip(Chatbox_x1, Chatbox_y1, Chatbox_w, Chatbox_h);
872         // gr_clear();
873         // gr_reset_clip();
874
875         // draw the background bitmap if we're supposed to
876         if ( (Chatbox_bitmap != -1) && (Chatbox_mode_flags & CHATBOX_FLAG_DRAW_BOX)) {
877                 gr_set_bitmap( Chatbox_bitmap );
878                 gr_bitmap(Chatbox_x1, Chatbox_y1);              
879         }
880
881         // render the chat lines
882         chatbox_render_chat_lines();
883
884         // render any UI window stuff
885         Chat_window.draw();
886 }
887
888 // try and scroll the chatbox up. return 0 or 1 on fail or success
889 int chatbox_scroll_up()
890 {       
891         if(Num_brief_chat_lines > Chatbox_max_lines){
892                 int prev = Brief_chat_prev_index[Brief_start_display_index];
893         
894                 // check to make sure we won't be going "up" above the "beginning" of the text array
895            if(Brief_chat_prev_index[Brief_current_add_line] != prev && !(Num_brief_chat_lines < MAX_BRIEF_CHAT_LINES && Brief_start_display_index==0)){
896                         Brief_start_display_index = prev;
897                         return 1;
898                 }
899                 
900         } 
901         return 0;
902 }
903
904 // try and scroll the chatbox down, return 0 or 1 on fail or success
905 int chatbox_scroll_down()
906 {
907         int idx;
908         int next;
909         
910    if(Num_brief_chat_lines > Chatbox_max_lines){
911                 // yuck. There's got to be a better way to do this.
912                 next = Brief_chat_next_index[Brief_start_display_index];
913                 for(idx = 1;idx <= Chatbox_max_lines; idx++){
914                         next = Brief_chat_next_index[next];
915                 }
916                         
917                 // check to make sure we won't be going "down" below the "bottom" of the text array
918                 if(Brief_chat_next_index[Brief_current_add_line] != next){
919                         Brief_start_display_index = Brief_chat_next_index[Brief_start_display_index];
920                         return 1;
921                 }
922         }
923         return 0;
924 }
925         
926 // quick explanation as to how the scrolling works :
927 // Brief_chat_next_index is an array A of size n, where A[i] = i+1 and A[n] = 0
928 // Brief_chat_prev_index is an array A of size n, where A[i] = i-1 and A[0] = n
929 // in other words, if you increment an integer i = A[i], you get the next index (or the prev)
930 // with wrapping. In this way, as chat lines are entered, they are continuously wrapped 
931 // around the Brief_chat_lines array so we can keep it at a fixed size. These arrays are used
932 // for both entering new chat strings as well as moving the Brief_start_display_index 
933 // integer, which is self-explanatory
934
935 void chatbox_chat_init()
936 {
937         int idx;        
938                 
939    chatbox_clear();
940
941         // setup the wraparound arrays
942         for(idx=0;idx<MAX_BRIEF_CHAT_LINES;idx++){
943       Brief_chat_next_index[idx] = idx+1;
944         }
945         Brief_chat_next_index[MAX_BRIEF_CHAT_LINES-1]=0;
946
947         for(idx=MAX_BRIEF_CHAT_LINES-1; idx > 0 ; idx--){
948                 Brief_chat_prev_index[idx] = idx - 1;
949         }
950         Brief_chat_prev_index[0] = MAX_BRIEF_CHAT_LINES-1;      
951
952         // initialize the line recall data
953         Chatbox_recall_count = 0;
954         Chatbox_recall_index = 0;
955         Chatbox_recall_last = -1;
956         memset(Chatbox_recall_lines,0,CHATBOX_MAX_RECALL_LINES * (CHATBOX_MAX_LEN + 2));        
957 }
958
959 // int Test_color = 0;
960 void chatbox_add_line(char *msg, int pid, int add_id)
961 {
962         int backup;
963         int     n_lines,idx;
964         int     n_chars[3];             
965         char    *p_str[3];                      // for the initial line (unindented)
966         char msg_extra[CHATBOX_STRING_LEN];
967
968         if(!Chatbox_created){
969                 return;
970         }
971
972         // maybe stick on who sent the message  
973         if(add_id){
974                 if(MULTI_STANDALONE(Net_players[pid])){
975                         sprintf(msg_extra, NOX("%s %s"), NOX("<SERVER>"), msg );
976                 } else {
977                         sprintf(msg_extra, NOX("%s: %s"), Net_players[pid].player->short_callsign, msg );
978                 }
979         } else {
980                 strcpy(msg_extra,msg);
981         }       
982         Assert(strlen(msg_extra) < (CHATBOX_STRING_LEN - 2));   
983
984         // split the text up into as many lines as necessary
985         n_lines = split_str(msg_extra, Chatbox_disp_w, n_chars, p_str, 3);
986         Assert(n_lines != -1);  
987
988         // setup the first line -- be sure to clear out the line
989         memset( Brief_chat_lines[Brief_current_add_line], 0, CHATBOX_STRING_LEN );
990
991         // add the player id #
992         Brief_chat_lines[Brief_current_add_line][0] = (char)(pid % 16); 
993         // Brief_chat_lines[Brief_current_add_line][0] = (char)Test_color;      
994         // Test_color = (Test_color == MAX_PLAYERS - 1) ? 0 : Test_color++;
995
996         // set the indent to 0
997         Brief_chat_indents[Brief_current_add_line] = 0;
998
999         // copy in the chars
1000         strncpy(&Brief_chat_lines[Brief_current_add_line][1],p_str[0],CHATBOX_STRING_LEN - 1);
1001         if(n_chars[0] >= CHATBOX_STRING_LEN){
1002                 Brief_chat_lines[Brief_current_add_line][CHATBOX_STRING_LEN - 1] = '\0';
1003         } else {
1004                 Brief_chat_lines[Brief_current_add_line][n_chars[0] + 1] = '\0';
1005         }
1006         
1007         // increment the total line count if we haven't reached the max already
1008         if(Num_brief_chat_lines<MAX_BRIEF_CHAT_LINES){
1009                 Num_brief_chat_lines++;
1010         }
1011         
1012         // get the index of the next string to add text to
1013         Brief_current_add_line = Brief_chat_next_index[Brief_current_add_line]; 
1014         
1015         // if we have more than 1 line, re-split everything so that the rest are indented
1016         if(n_lines > 1){
1017                 // split up the string after the first break-marker
1018                 n_lines = split_str(msg_extra + n_chars[0],Chatbox_disp_w - CHAT_LINE_INDENT,n_chars,p_str,3);
1019                 Assert(n_lines != -1);          
1020
1021                 // setup these remaining lines
1022                 for(idx=0;idx<n_lines;idx++){
1023                         // add the player id#
1024                         Brief_chat_lines[Brief_current_add_line][0] = (char)(pid % 16); 
1025
1026                         // add the proper indent
1027                         Brief_chat_indents[Brief_current_add_line] = CHAT_LINE_INDENT;
1028
1029                         // copy in the line text itself
1030                         strncpy(&Brief_chat_lines[Brief_current_add_line][1],p_str[idx],CHATBOX_STRING_LEN-1); 
1031                         if(n_chars[idx] >= CHATBOX_STRING_LEN){
1032                                 Brief_chat_lines[Brief_current_add_line][CHATBOX_STRING_LEN - 1] = '\0';
1033                         } else {
1034                                 Brief_chat_lines[Brief_current_add_line][n_chars[idx] + 1] = '\0';
1035                         }
1036                         
1037                         // increment the total line count if we haven't reached the max already
1038                         if(Num_brief_chat_lines<MAX_BRIEF_CHAT_LINES){
1039                                 Num_brief_chat_lines++;
1040                         }
1041                         
1042                         // get the index of the next line to add text to
1043                         Brief_current_add_line = Brief_chat_next_index[Brief_current_add_line];                         
1044                 }
1045         }
1046                         
1047         // COMMAND LINE OPTION
1048         if(Cmdline_multi_stream_chat_to_file && Multi_chat_stream!=NULL && strlen(msg)>0){ // stream to the file if we're supposed to
1049                 cfwrite_string(msg,Multi_chat_stream);
1050                 cfwrite_char('\n',Multi_chat_stream);
1051         }       
1052
1053         // if this line of text is from the player himself, automatically go to the bottom of
1054         // the chat list
1055         if(pid == MY_NET_PLAYER_NUM){
1056                 if(Num_brief_chat_lines > Chatbox_max_lines){
1057                         Brief_start_display_index = Brief_current_add_line;
1058                         for(backup = 1;backup <= Chatbox_max_lines;backup++){
1059                                 Brief_start_display_index = Brief_chat_prev_index[Brief_start_display_index];
1060                         }
1061                 }
1062         }
1063         // if we have enough lines of text to require scrolling, scroll down by one.
1064         else { 
1065                 chatbox_scroll_down();
1066         }       
1067 }
1068
1069 void chatbox_render_chat_lines()
1070 {
1071    int started_at,player_num,count,ly;  
1072         
1073         started_at = Brief_start_display_index;
1074         count = 0;      
1075         ly = Chatbox_begin_y;
1076         while((count < Chatbox_max_lines) && (count < Num_brief_chat_lines)){   
1077                 // determine what player this chat line came from, and set the appropriate text color
1078                 player_num = Brief_chat_lines[started_at][0];                   
1079                 
1080                 // print the line out
1081                 gr_set_color_fast(Color_netplayer[player_num]);
1082
1083                 // draw the first line (no indent)                                              
1084                 gr_string(Chatbox_begin_x + Brief_chat_indents[started_at],ly,&Brief_chat_lines[started_at][1]);                
1085
1086                 // if we're in a team vs. team game, blit the player color icon
1087                 if(Netgame.type_flags & NG_TYPE_TEAM){
1088                         switch(Net_players[player_num].p_info.team){
1089                         case 0 :
1090                                 // if he's a team captain
1091                                 if(Net_players[player_num].flags & NETINFO_FLAG_TEAM_CAPTAIN){
1092                                         if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){
1093                                                 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT]);
1094                                                 gr_bitmap(Chatbox_icon_x,ly-2);
1095                                         } 
1096                                 }
1097                                 // just you're average peon
1098                                 else {
1099                                         if(Multi_common_icons[MICON_TEAM0] != -1){
1100                                                 gr_set_bitmap(Multi_common_icons[MICON_TEAM0]);
1101                                                 gr_bitmap(Chatbox_icon_x,ly-2);
1102                                         }
1103                                 }
1104                                 break;
1105                         case 1 :
1106                                 // if he's a team captain
1107                                 if(Net_players[player_num].flags & NETINFO_FLAG_TEAM_CAPTAIN){
1108                                         if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){
1109                                                 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT]);
1110                                                 gr_bitmap(Chatbox_icon_x,ly-2);
1111                                         }
1112                                 }
1113                                 // just your average peon
1114                                 else {
1115                                         if(Multi_common_icons[MICON_TEAM1] != -1){
1116                                                 gr_set_bitmap(Multi_common_icons[MICON_TEAM1]);
1117                                                 gr_bitmap(Chatbox_icon_x,ly-2);
1118                                         }
1119                                 }
1120                                 break;
1121                         }
1122                 }
1123
1124                 // increment the count and line position
1125                 count++;                
1126                 ly += 10;
1127                 
1128                 // increment the started at index
1129                 started_at = Brief_chat_next_index[started_at];
1130         }               
1131 }
1132
1133 void chatbox_toggle_size()
1134 {
1135         // if we're in "small" mode
1136         if(Chatbox_mode_flags & CHATBOX_FLAG_SMALL){
1137                 chatbox_force_big();
1138                 
1139                 // play a sound
1140                 gamesnd_play_iface(SND_IFACE_MOUSE_CLICK);
1141         }
1142         // if we're in "big" mode
1143         else if(Chatbox_mode_flags & CHATBOX_FLAG_BIG){
1144                 chatbox_force_small();
1145                 
1146                 // play a sound
1147                 gamesnd_play_iface(SND_IFACE_MOUSE_CLICK);
1148         }
1149 }
1150
1151 void chatbox_toggle_size_adjust_lines()
1152 {
1153         int count_diff;
1154         
1155         // if the chatbox is now big, move the starting display index _up_ as far as possible
1156         if(Chatbox_mode_flags & CHATBOX_FLAG_BIG){                              
1157                 // if we've wrapped around      or we have more chatlines then we can display, move back as far as we can
1158                 if((Num_brief_chat_lines > MAX_BRIEF_CHAT_LINES) || (Num_brief_chat_lines > Chatbox_max_lines)){                        
1159                         count_diff = Chatbox_max_lines - chatbox_num_displayed_lines();         
1160                         while(count_diff > 0){
1161                                 Brief_start_display_index = Brief_chat_prev_index[Brief_start_display_index];
1162                                 count_diff--;
1163                         }
1164                 }
1165                 // otherwise start displaying from position 0
1166                 else {                                  
1167                         Brief_start_display_index = 0;                  
1168                 }                       
1169         }
1170         // if the chatbox is now small, move the starting display index down as far as we need
1171         else if(Chatbox_mode_flags & CHATBOX_FLAG_SMALL){
1172                 count_diff = chatbox_num_displayed_lines();
1173                 // if we were displaying more lines than we can now
1174                 if(count_diff > Chatbox_max_lines){
1175                         count_diff -= Chatbox_max_lines;
1176                         while(count_diff > 0){
1177                                 Brief_start_display_index = Brief_chat_next_index[Brief_start_display_index];
1178                                 count_diff--;
1179                         }
1180                 }
1181         }
1182 }
1183
1184 int chatbox_num_displayed_lines()
1185 {
1186         int idx = Brief_start_display_index;
1187         int count;
1188
1189         // count the lines up
1190         count = 0;
1191         while(idx != Brief_current_add_line){
1192                 idx = Brief_chat_next_index[idx];
1193                 count++;
1194         }
1195
1196         return count;
1197 }
1198
1199 // force the chatbox to go into small mode (if its in large mode) - will not wotk if in multi paused chatbox mode
1200 void chatbox_force_small()
1201 {
1202         int new_mode_flags;
1203
1204         // don't do anything unless we're currently in "big" mode
1205         if(!(Chatbox_mode_flags & CHATBOX_FLAG_BIG)){
1206                 return;
1207         }
1208
1209         new_mode_flags = Chatbox_mode_flags;
1210
1211         // switch to the appropriate mode
1212         new_mode_flags &= ~(CHATBOX_FLAG_SMALL | CHATBOX_FLAG_BIG);     
1213         new_mode_flags |= CHATBOX_FLAG_SMALL;
1214         Chatbox_bitmap = Chatbox_small_bitmap;
1215                 
1216         // flip the up/down arrow
1217         Chatbox_buttons[gr_screen.res][CHATBOX_TOGGLE_SIZE].button.set_bmaps(Chatbox_buttons[gr_screen.res][CHATBOX_TOGGLE_SIZE].filename);
1218         
1219         // call this to set everything up correctly
1220         chatbox_set_mode(new_mode_flags);               
1221         
1222         // change the location of the input box
1223         Chat_inputbox.update_dimensions(Chatbox_inputbox_x, Chatbox_textenter_y, Chatbox_inputbox_w,15);
1224         Chat_inputbox.set_focus();
1225
1226         // adjust what line we start displaying from based upon the new size of the window
1227         chatbox_toggle_size_adjust_lines();
1228 }
1229
1230 // force the chatbox to go into big mode (if its in small mode) - will not work if in multi paused chatbox mode
1231 void chatbox_force_big()
1232 {
1233         int new_mode_flags;
1234
1235         // don't do anything unless we're currently in "small" mode
1236         if(!(Chatbox_mode_flags & CHATBOX_FLAG_SMALL)){
1237                 return;
1238         }
1239
1240         new_mode_flags = Chatbox_mode_flags;    
1241
1242         // switch to the appropriate mode
1243         new_mode_flags &= ~(CHATBOX_FLAG_SMALL | CHATBOX_FLAG_BIG);     
1244         new_mode_flags |= CHATBOX_FLAG_BIG;
1245         Chatbox_bitmap = Chatbox_big_bitmap;
1246                 
1247         // flip the up/down arrow
1248         Chatbox_buttons[gr_screen.res][CHATBOX_TOGGLE_SIZE].button.set_bmaps(Chatbox_buttons[gr_screen.res][CHATBOX_TOGGLE_SIZE+1].filename);
1249         
1250         // call this to set everything up correctly
1251         chatbox_set_mode(new_mode_flags);               
1252         
1253         // change the location of the input box
1254         Chat_inputbox.update_dimensions(Chatbox_inputbox_x, Chatbox_textenter_y, Chatbox_inputbox_w,15);
1255         Chat_inputbox.set_focus();
1256
1257         // adjust what line we start displaying from based upon the new size of the window
1258         chatbox_toggle_size_adjust_lines();
1259 }
1260
1261 // "lose" the focus on the chatbox inputbox
1262 void chatbox_lose_focus()
1263 {
1264         if(!Chatbox_created){
1265                 return;
1266         }
1267
1268         // clear the focus on the inputbox
1269         Chat_inputbox.clear_focus();
1270 }
1271
1272 // return if the inputbox for the chatbox currently has focus
1273 int chatbox_has_focus()
1274 {
1275         return Chat_inputbox.has_focus();
1276 }
1277
1278 // grab the focus for the chatbox inputbox
1279 void chatbox_set_focus()
1280 {
1281         Chat_inputbox.set_focus();
1282 }
1283
1284 // return if the inputbox was pressed - "clicked on"
1285 int chatbox_pressed()
1286 {
1287         return Chat_inputbox.pressed();
1288 }
1289
1290 // add the string to the line recall list
1291 void chatbox_recall_add(char *string)
1292 {
1293         int idx;        
1294
1295         // aleays reset the recall index when adding
1296         Chatbox_recall_index = 0;
1297         Chatbox_recall_last = -1;
1298
1299         // if this string matches the last string we entered, don't add it again
1300         if(!strcmp(string,Chatbox_recall_lines[0])){
1301                 return;
1302         }
1303
1304         // move all items up (this works fine for small #'s of recall lines
1305         for(idx=CHATBOX_MAX_RECALL_LINES-1;idx > 0;idx--){
1306                 memcpy(&Chatbox_recall_lines[idx],&Chatbox_recall_lines[idx-1],CHATBOX_MAX_LEN+2);
1307         }
1308
1309         // copy the new item into spot 0
1310         strcpy(Chatbox_recall_lines[0],string);
1311
1312         // increment the recall count if necessary
1313         if(Chatbox_recall_count < CHATBOX_MAX_RECALL_LINES){
1314                 Chatbox_recall_count++;
1315         }       
1316 }
1317
1318 // user has pressed the "up" key
1319 void chatbox_recall_up()
1320 {
1321         // if we've got no recall lines, do nothing
1322         if(Chatbox_recall_count <= 0){
1323                 return;
1324         }
1325
1326         // if we can increment up
1327         if(Chatbox_recall_index < (Chatbox_recall_count - 1)){
1328                 // if this is the last line we recalled, pre-increment
1329                 if(Chatbox_recall_last == Chatbox_recall_index){
1330                         Chat_inputbox.set_text(Chatbox_recall_lines[++Chatbox_recall_index]);
1331                         Chatbox_recall_last = Chatbox_recall_index;
1332                 }
1333                 // otherwise, post increment
1334                 else {
1335                         Chat_inputbox.set_text(Chatbox_recall_lines[Chatbox_recall_index++]);
1336                         Chatbox_recall_last = Chatbox_recall_index - 1;
1337                 }
1338         } 
1339         // if we can't increment up
1340         else {
1341                 Chat_inputbox.set_text(Chatbox_recall_lines[Chatbox_recall_index]);
1342                 Chatbox_recall_last = Chatbox_recall_index;
1343         }       
1344 }
1345
1346 // user has pressed the "down" key
1347 void chatbox_recall_down()
1348 {       
1349         // if we've got no recall lines, do nothing
1350         if(Chatbox_recall_count <= 0){
1351                 return;
1352         }
1353
1354         // if we can decrement down
1355         if(Chatbox_recall_index > 0){
1356                 // if this is the last line we recalled, pre-decrement
1357                 if(Chatbox_recall_last == Chatbox_recall_index){
1358                         Chat_inputbox.set_text(Chatbox_recall_lines[--Chatbox_recall_index]);
1359                         Chatbox_recall_last = Chatbox_recall_index;
1360                 } 
1361                 // otherwise post,decrement
1362                 else {
1363                         Chat_inputbox.set_text(Chatbox_recall_lines[Chatbox_recall_index--]);
1364                         Chatbox_recall_last = Chatbox_recall_index + 1;
1365                 }
1366         } 
1367         // if we can't decrement down
1368         else {          
1369                 Chat_inputbox.set_text("");
1370                 Chatbox_recall_last = -1;
1371         }       
1372 }
1373
1374 // reset all timestamps associated with the chatbox
1375 void chatbox_reset_timestamps()
1376 {
1377         int idx;
1378
1379         // if there is no chatbox created, do nothing
1380         if(!Chatbox_created){
1381                 return;
1382         }
1383
1384         // otherwise clear all timestamps on all the buttons
1385         for(idx=0; idx<CHATBOX_NUM_BUTTONS+1; idx++){
1386                 Chatbox_buttons[gr_screen.res][idx].button.reset_timestamps();
1387         }
1388 }