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