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