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