]> icculus.org git repositories - taylor/freespace2.git/blob - src/mission/missionmessage.cpp
The Great Newline Fix
[taylor/freespace2.git] / src / mission / missionmessage.cpp
1 /*
2  * $Logfile: /Freespace2/code/Mission/MissionMessage.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * Controls messaging to player during the mission
8  *
9  * $Log$
10  * Revision 1.2  2002/05/07 03:16:46  theoddone33
11  * The Great Newline Fix
12  *
13  * Revision 1.1.1.1  2002/05/03 03:28:10  root
14  * Initial import.
15  *
16  * 
17  * 32    9/12/99 8:09p Dave
18  * Fixed problem where skip-training button would cause mission messages
19  * not to get paged out for the current mission.
20  * 
21  * 31    9/01/99 2:52p Andsager
22  * Add new heads to FRED and some debug code for playing heads
23  * 
24  * 30    8/28/99 7:29p Dave
25  * Fixed wingmen persona messaging. Make sure locked turrets don't count
26  * towards the # attacking a player.
27  * 
28  * 29    8/26/99 8:51p Dave
29  * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes.
30  * 
31  * 28    8/23/99 5:04p Jefff
32  * Added new mission flag to disable built-in messages from playing.
33  * Added fred support as well.
34  * 
35  * 27    8/19/99 10:12a Alanl
36  * preload mission-specific messages on machines greater than 48MB
37  * 
38  * 26    8/18/99 12:09p Andsager
39  * Add debug if message has no anim for message.  Make messages come from
40  * wing leader.
41  * 
42  * 25    7/31/99 2:30p Dave
43  * Added nifty mission message debug viewing keys.
44  * 
45  * 24    7/24/99 2:19p Dave
46  * Fixed broken build.
47  * 
48  * 23    7/23/99 5:44p Andsager
49  * make personas consistently choose same ship
50  * 
51  * 22    7/15/99 9:20a Andsager
52  * FS2_DEMO initial checkin
53  * 
54  * 21    7/14/99 4:27p Andsager
55  * Added multiple message debug check
56  * 
57  * 20    7/06/99 10:41a Andsager
58  * Add AWACS need help messages
59  * 
60  * 19    7/02/99 11:16a Andsager
61  * Removed mult message debug check.
62  * 
63  * 18    7/02/99 11:13a Andsager
64  * max debug version
65  * 
66  * 17    6/16/99 10:20a Dave
67  * Added send-message-list sexpression.
68  * 
69  * 16    6/14/99 5:53p Dave
70  * Removed duplicate message check temporarily.
71  * 
72  * 15    6/10/99 3:43p Dave
73  * Do a better job of syncing text colors to HUD gauges.
74  * 
75  * 14    6/09/99 2:56p Andsager
76  * Check all messages for repeat.  Allow multiple versions of same message
77  * if queued > 20 apart.
78  * 
79  * 13    6/07/99 11:33a Anoop
80  * Get rid of erroneous Int3() in multiple message check.
81  * 
82  * 12    6/07/99 10:31a Andsager
83  * Get rid of false multiplayer multiple messages catch.
84  * 
85  * 11    6/03/99 2:56p Andsager
86  * DOH!!
87  * 
88  * 10    6/03/99 2:44p Andsager
89  * Fix stupid bug in debug code.
90  * 
91  * 9     6/03/99 2:08p Andsager
92  * Put in debug code to find multiple mission messages.
93  * 
94  * 8     3/29/99 6:17p Dave
95  * More work on demo system. Got just about everything in except for
96  * blowing ships up, secondary weapons and player death/warpout.
97  * 
98  * 7     1/28/99 12:19a Dave
99  * Fixed a dumb debug build unhandled exception.
100  * 
101  * 6     1/07/99 10:08a Jasen
102  * coords
103  * 
104  * 5     1/07/99 9:24a Dave
105  * Put in hi-res coord support for head anim.
106  * 
107  * 4     11/05/98 4:18p Dave
108  * First run nebula support. Beefed up localization a bit. Removed all
109  * conditional compiles for foreign versions. Modified mission file
110  * format.
111  * 
112  * 3     10/23/98 3:51p Dave
113  * Full support for tstrings.tbl and foreign languages. All that remains
114  * is to make it active in Fred.
115  * 
116  * 2     10/07/98 10:53a Dave
117  * Initial checkin.
118  * 
119  * 1     10/07/98 10:49a Dave
120  * 
121  * 127   8/25/98 1:48p Dave
122  * First rev of EMP effect. Player side stuff basically done. Next comes
123  * AI code.
124  * 
125  * 126   6/01/98 11:43a John
126  * JAS & MK:  Classified all strings for localization.
127  * 
128  * 125   5/24/98 12:55a Mike
129  * Fix bug with scream from Installation.  Should also fix bug with double
130  * screams from some ships.
131  * 
132  * 124   5/18/98 6:06p Lawrance
133  * Don't play messages or auto-target on first frame
134  * 
135  * 123   5/15/98 8:36p Lawrance
136  * Add 'target ship that last sent transmission' target key
137  * 
138  * 122   5/09/98 10:00p Allender
139  * make vasudan persona for support use terran support persona
140  * 
141  * 121   5/08/98 11:21a Allender
142  * fix ingame join trouble.  Small messaging fix.  Enable collisions for
143  * friendlies again
144  * 
145  * 120   5/06/98 12:19p Lawrance
146  * Fix typo for 'Stray Warning Final'
147  * 
148  * 119   5/05/98 9:12p Allender
149  * fix large problem introduced last checkin when changiing Assert to if
150  * 
151  * 118   5/05/98 4:12p Chad
152  * changed Assert info if statement when removing messages from queue when
153  * too old
154  * 
155  * 117   5/01/98 12:34p John
156  * Added code to force FreeSpace to run in the same dir as exe and made
157  * all the parse error messages a little nicer.
158  * 
159  * 116   4/27/98 9:00p Allender
160  * mission specific messages from #<someone> are now sourced to terran
161  * command
162  * 
163  * 115   4/26/98 11:35a Allender
164  * make traitor message play by iteself in all cases
165  * 
166  * 114   4/25/98 11:49p Lawrance
167  * Add Terran Command stray messages
168  * 
169  * 113   4/22/98 9:17a Allender
170  * be sure that builtin command messages play with the correct hud source.
171  * Also be sure that messages which get converted to Terran command to the
172  * same
173  * 
174  * 112   4/20/98 1:30a Lawrance
175  * Don't load head animations if talking head gauge is disabled.
176  * 
177  * 111   4/17/98 11:03a Allender
178  * some rearm message being played too often and sent with incorrect
179  * 'who-from'
180  * 
181  * 110   4/13/98 5:06p Lawrance
182  * Cut off talking head about 250ms before wave ends
183  * 
184  * 109   4/10/98 9:14a Lawrance
185  * fix up persona code for the demo
186  * 
187  * 108   4/09/98 2:15p Allender
188  * fixed compiler warnings
189  * 
190  * 107   4/09/98 12:36p Allender
191  * don't allow the same ship to have messages overlapping.  Put in code to
192  * check for ship's existence (wingman only) before actually playing
193  * message
194  * 
195  * 106   4/09/98 12:32a Lawrance
196  * Fix bugs related to multiple screams from same ship, builtin messages
197  * playing after screams, or praising while severly damaged.
198  * 
199  * 105   4/08/98 3:45p Allender
200  * mission message overhaul.  Make message from any wingman mean any
201  * wingman with that persona.  Terran command wave and ani's for dead
202  * ships now play correctly.
203  * 
204  * 104   4/07/98 8:09p Lawrance
205  * don't play talking heads in the demo
206  * 
207  * 103   4/07/98 5:30p Lawrance
208  * Player can't send/receive messages when comm is destroyed.  Garble
209  * messages when comm is damaged.
210  * 
211  * 102   4/07/98 5:26p Allender
212  * low priority mission specific messages won't interrupt anything.
213  * 
214  * 101   4/07/98 10:51a Allender
215  * remove any allied from message senders.  Make heads for mission
216  * specific messages play appropriately
217  * 
218  * 100   4/07/98 12:04a Mike
219  * New system for instructor chastising player if he fires at instructor.
220  * 
221  * 99    4/03/98 11:39a Lawrance
222  * only allow 1 wingman persona in demo
223  * 
224  * 98    4/02/98 1:09p Allender
225  * don't process messages before player "enters" mission (i.e. due to
226  * player entry delay)
227  * 
228  * 97    4/02/98 10:06a Allender
229  * wing arrival message for delta and epsilon wings
230  * 
231  * 96    4/01/98 10:47p Lawrance
232  * Supporting builtin messages for rearm and repair requests
233  * 
234  * 95    3/25/98 8:43p Hoffoss
235  * Changed anim_play() to not be so damn complex when you try and call it.
236  * 
237  * 94    3/24/98 12:46p Allender
238  * save shipnum before killing currently playing message in preparation
239  * for playing death scream.
240  * 
241  * 93    3/22/98 3:54p Andsager
242  * AL: Prevent -1 index into Ships[] array when playing a scream
243  * 
244  * 92    3/18/98 10:20p Allender
245  * force wingman scream when he's talking and then dies
246  * 
247  * 91    3/18/98 12:03p John
248  * Marked all the new strings as externalized or not.
249  * 
250  * 90    3/17/98 4:01p Hoffoss
251  * Added HUD_SOURCE_TERRAN_CMD and changed code to utilize it when a
252  * message is being sent from Terran Command.
253  * 
254  * 89    3/05/98 10:18p Lawrance
255  * Play voice cue sound when there is no voice file present
256  * 
257  * 88    3/02/98 5:42p John
258  * Removed WinAVI stuff from Freespace.  Made all HUD gauges wriggle from
259  * afterburner.  Made gr_set_clip work good with negative x &y.  Made
260  * model_caching be on by default.  Made each cached model have it's own
261  * bitmap id.  Made asteroids not rotate when model_caching is on.  
262  * 
263  * 87    3/02/98 9:34a Allender
264  * don't allow mission specific messages to timeout.  Assert when trying
265  * to remove a mission specific messages from the queue.  Print out in the
266  * log file if the voice didn't play.
267  * 
268  * 86    2/23/98 8:45a John
269  * Externalized Strings
270  * 
271  * 85    2/20/98 8:33p Lawrance
272  * Add the 'All Alone' message
273  * 
274  * 84    2/16/98 2:20p Allender
275  * make death scream kill any other messages from that ship
276  * 
277  * 83    2/12/98 4:58p Lawrance
278  * Add support for 'All Clear' radio message
279  * 
280  * 82    2/11/98 9:44p Allender
281  * rearm repair code fixes.  hud support view shows abort status.  New
282  * support ship killed message.  More network stats
283  * 
284  * 81    2/04/98 10:44p Allender
285  * mark personas as not used between missions.   Don't ever randomly
286  * choose a Vasudan persona
287  * 
288  * 80    1/29/98 11:38a Allender
289  * support for Vasudan personas
290  * 
291  * 79    1/25/98 10:04p Lawrance
292  * Fix nasty divide-by-zero bug in message_calc_anim_start_frame().
293  * 
294  * 78    1/24/98 4:46p Lawrance
295  * add in support for new voice messasges
296  * 
297  * 77    1/22/98 5:13p Lawrance
298  * pick useful starting frame when playing animation
299  * 
300  * 76    1/21/98 7:20p Lawrance
301  * Make subsystem locking only work with line-of-sight, cleaned up locking
302  * code, moved globals to player struct.
303  * 
304  * 75    1/21/98 11:54a Duncan
305  * Commended out assert for the moment to allow Fred to run.  Mark can fix
306  * this properly later, since he understands it.
307  * 
308  * 74    1/21/98 10:33a Allender
309  * fixed up messaging code to play a random head when playing a builtin
310  * message
311  * 
312  * 73    1/20/98 6:21p Lawrance
313  * Stop animation from playing when voice clip ends early.
314  * 
315  * 72    1/20/98 3:43p Allender
316  * don't queue messages when player becomes traitor
317  * 
318  * 71    1/20/98 12:52p Lawrance
319  * Draw talking head as alpha-color bitmap, black out region behind
320  * animation.
321  * 
322  * 70    1/20/98 10:20a Lawrance
323  * Draw head animation as alpha-colored.
324  * 
325  * 69    1/18/98 9:51p Lawrance
326  * Add support for 'Player Died' messages.
327  * 
328  * 68    1/14/98 9:49p Allender
329  * removed 3'oclock and 9'oclock messages
330  * 
331  * 67    1/13/98 3:11p Allender
332  * new messages for disable/disarm
333  * 
334  * 66    1/12/98 11:16p Lawrance
335  * Wonderful HUD config.
336  * 
337  * 65    1/07/98 4:41p Allender
338  * minor modification to special messages.  Fixed cargo_revealed problem
339  * for multiplayer and problem with is-cargo-known sexpression
340  * 
341  * 64    12/15/97 12:14p Allender
342  * implemented overlapping messages
343  * 
344  * 63    12/12/97 4:58p Allender
345  * make messages interruptable.  Put in code to support multiple messages
346  * at once, although this feature not fully implemented yet.
347  * 
348  * 62    12/04/97 9:37p Dave
349  * Fixed a bunch of multiplayer messaging bugs.
350  * 
351  * 61    12/02/97 2:37p Allender
352  * added asserts to be sure that an actual ship (i.e. not cargo or
353  * otherwise) is the ship sending a message to the player
354  *
355  * $NoKeywords: $
356  */
357
358 #include "linklist.h"
359 #include "missionmessage.h"
360 #include "missiontraining.h"
361 #include "hudmessage.h"
362 #include "hudtarget.h"
363 #include "sexp.h"
364 #include "timer.h"
365 #include "parselo.h"
366 #include "gamesnd.h"
367 #include "sound.h"
368 #include "freespace.h"
369 #include "multi.h"
370 #include "multimsgs.h"
371 #include "gamesequence.h"
372 #include "animplay.h"
373 #include "controlsconfig.h"
374 #include "audiostr.h"
375 #include "hudsquadmsg.h"
376 #include "multiutil.h"
377 #include "hud.h"
378 #include "subsysdamage.h"
379 #include "emp.h"
380 #include "localize.h"
381 #include "demo.h"
382 #include "hudconfig.h"
383
384 // here is a text list of the builtin message names.  These names are used to match against
385 // names read in for builtin message radio bits to see what message to play.  These are
386 // generic names, meaning that there will be the same message type for a number of different
387 // personas
388 char *Builtin_message_types[MAX_BUILTIN_MESSAGE_TYPES] =
389 {
390 //XSTR:OFF
391         "Arrive Enemy",
392         "Attack Target",
393         "Beta Arrived",
394         "Check 6",
395         "Engage",
396         "Gamma Arrived",
397         "Help",
398         "Praise",
399         "Backup",
400         "Ignore Target",
401         "No",
402         "Oops 1",
403         "Permission",                   // AL: no code support yet
404         "Stray",                                        // DA: no code support
405         "Depart",
406         "yes",
407         "Rearm on Way",
408         "On way",
409         "Rearm warping in",
410         "No Target",
411         "Docking Start",                // AL: no message seems to exist for this
412         "Repair Done",
413         "Repair Aborted",
414         "Traitor",
415         "Rearm",
416         "Disable Target",
417         "Disarm Target",
418         "Player Dead",
419         "Death",
420         "Support Killed",
421         "All Clear",                    // DA: no code support
422         "All Alone",
423         "Repair",
424         "Delta Arrived",
425         "Epsilon Arrived",
426         "Instructor Hit",
427         "Instructor Attack",
428         "Stray Warning",
429         "Stray Warning Final",
430         "AWACS at 75",
431         "AWACS at 25"
432 //XSTR:ON
433 };
434
435 MMessage Messages[MAX_MISSION_MESSAGES];
436 int Message_times[MAX_MISSION_MESSAGES];
437
438 int Num_messages, Num_message_avis, Num_message_waves;
439 int Num_builtin_messages, Num_builtin_avis, Num_builtin_waves;
440
441 int Message_debug_index = -1;
442
443 message_extra Message_avis[MAX_MESSAGE_AVIS];
444 message_extra Message_waves[MAX_MESSAGE_WAVES];
445
446 #define MAX_PLAYING_MESSAGES            2
447
448 #ifdef FS2_DEMO
449         #define MAX_WINGMAN_HEADS                       1
450         #define MAX_COMMAND_HEADS                       1
451 #else
452 #define MAX_WINGMAN_HEADS                       2
453 #define MAX_COMMAND_HEADS                       3
454 #endif
455
456 //XSTR:OFF
457 #define HEAD_PREFIX_STRING                      "head-"
458 #define COMMAND_HEAD_PREFIX             "head-cm1"
459 #define COMMAND_WAVE_PREFIX             "TC_"
460 #define SUPPORT_NAME                                    "Support"
461 //XSTR:ON
462
463 // variables to keep track of messages that are currently playing
464 int Num_messages_playing;                                               // number of is a message currently playing?
465
466 typedef struct pmessage {
467         anim_instance *anim;            // handle of anim currently playing
468         int wave;                                       // handle of wave currently playing
469         int id;                                         // id of message currently playing
470         int priority;                           // priority of message currently playing
471         int shipnum;                            // shipnum of ship sending this message,  -1 if from Terran command
472         int builtin_type;                       // if a builtin message, type of the message
473 } pmessage;
474
475 LOCAL pmessage Playing_messages[MAX_PLAYING_MESSAGES];
476
477 int Message_shipnum;                                            // ship number of who is sending message to player -- used outside this module
478
479 // variables to control message queuing.  All new messages to the player are queued.  The array
480 // will be ordered by priority, then time submitted.
481
482 #define MQF_CONVERT_TO_COMMAND          (1<<0)                  // convert this queued message to terran command
483 #define MQF_CHECK_ALIVE                                 (1<<1)                  // check for the existence of who_from before sending
484
485 typedef struct message_q {
486         fix     time_added;                                     // time at which this entry was added
487         int     window_timestamp;                       // timestamp which will tell us how long we have to play the message
488         int     priority;                                       // priority of the message
489         int     message_num;                            // index into the Messages[] array
490         char    who_from[NAME_LENGTH];  // who this message is from
491         int     source;                                         // who the source of the message is (HUD_SOURCE_* type)
492         int     builtin_type;                           // type of builtin message (-1 if mission message)
493         int     flags;                                          // should this message entry be converted to Terran Command head/wave file
494         int     min_delay_stamp;                        // minimum delay before this message will start playing
495         int     group;                                          // message is part of a group, don't time it out
496 } message_q;
497
498 #define MAX_MESSAGE_Q                           30
499 #define MAX_MESSAGE_LIFE                        F1_0*30         // After being queued for 30 seconds, don't play it
500 #define DEFAULT_MESSAGE_LENGTH  3000                    // default number of milliseconds to display message indicator on hud
501 message_q       MessageQ[MAX_MESSAGE_Q];
502 int MessageQ_num;                       // keeps track of number of entries on the queue.
503
504 #define MESSAGE_IMMEDIATE_TIMESTAMP             1000            // immediate messages must play within 1 second
505 #define MESSAGE_SOON_TIMESTAMP                  5000            // "soon" messages must play within 5 seconds
506 #define MESSAGE_ANYTIME_TIMESTAMP               -1                      // anytime timestamps are invalid
507
508 // Persona information
509 int Num_personas;
510 Persona Personas[MAX_PERSONAS];
511
512 char *Persona_type_names[MAX_PERSONA_TYPES] = 
513 {
514 //XSTR:OFF
515         "wingman",
516         "support",
517         "large", 
518         "command",
519 //XSTR:ON
520 };
521
522 int Command_persona;
523
524 ///////////////////////////////////////////////////////////////////
525 // used to distort incoming messages when comms are damaged
526 ///////////////////////////////////////////////////////////////////
527 static int Message_wave_muted;
528 static int Message_wave_duration;
529 static int Next_mute_time;
530
531 #define MAX_DISTORT_PATTERNS    2
532 #define MAX_DISTORT_LEVELS              6
533 static float Distort_patterns[MAX_DISTORT_PATTERNS][MAX_DISTORT_LEVELS] = 
534 {
535         {0.20f, 0.20f, 0.20f, 0.20f, 0.20f, 0.20f},
536         {0.10f, 0.20f, 0.25f, 0.25f, 0.05f, 0.15f}
537 };
538
539 static int Distort_num;         // which distort pattern is being used
540 static int Distort_next;        // which section of distort pattern is next
541
542 int Head_coords[GR_NUM_RESOLUTIONS][2] = {
543         { // GR_640
544                 7, 45
545         },
546         { // GR_1024
547                 7, 66
548         }
549 };
550
551 // forward declaration
552 void message_maybe_distort_text(char *text);
553
554 // following functions to parse messages.tbl -- code pretty much ripped from weapon/ship table parsing code
555
556 // functions to deal with parsing personas.  Personas are just a list of names that give someone
557 // sending a message an identity which spans the life of the mission
558 void persona_parse()
559 {
560         int i;
561         char type[NAME_LENGTH];
562
563         Assert ( Num_personas < MAX_PERSONAS );
564
565         Personas[Num_personas].flags = 0;
566         required_string("$Persona:");
567         stuff_string(Personas[Num_personas].name, F_NAME, NULL);
568
569         // get the type name and set the appropriate flag
570         required_string("$Type:");
571         stuff_string( type, F_NAME, NULL );
572         for ( i = 0; i < MAX_PERSONA_TYPES; i++ ) {
573                 if ( !stricmp( type, Persona_type_names[i]) ) {
574
575                         Personas[Num_personas].flags |= (1<<i);
576
577                         // save the Terran Command persona in a global
578                         if ( Personas[Num_personas].flags & PERSONA_FLAG_COMMAND ) {
579 //                              Assert ( Command_persona == -1 );
580                                 Command_persona = Num_personas;
581                         }
582
583                         break;
584                 }
585         }
586
587         if ( optional_string("+Vasudan") )
588                 Personas[Num_personas].flags |= PERSONA_FLAG_VASUDAN;
589
590         if ( i == MAX_PERSONA_TYPES )
591                 Error(LOCATION, "Unknown persona type in messages.tbl -- %s\n", type );
592
593
594         Num_personas++;
595 }
596
597 // two functions to add avi/wave names into a table
598 int add_avi( char *avi_name )
599 {
600         int i;
601
602         Assert ( Num_message_avis < MAX_MESSAGE_AVIS );
603         Assert (strlen(avi_name) < MAX_FILENAME_LEN );
604
605         // check to see if there is an existing avi being used here
606         for ( i = 0; i < Num_message_avis; i++ ) {
607                 if ( !stricmp(Message_avis[i].name, avi_name) )
608                         return i;
609         }
610
611         // would have returned if a slot existed.
612         strcpy( Message_avis[Num_message_avis].name, avi_name );
613         Message_avis[Num_message_avis].num = -1;
614         Num_message_avis++;
615         return (Num_message_avis - 1);
616 }
617
618 int add_wave( char *wave_name )
619 {
620         int i;
621
622         Assert ( Num_message_waves < MAX_MESSAGE_WAVES );
623         Assert (strlen(wave_name) < MAX_FILENAME_LEN );
624
625         // check to see if there is an existing wave being used here
626         for ( i = 0; i < Num_message_waves; i++ ) {
627                 if ( !stricmp(Message_waves[i].name, wave_name) )
628                         return i;
629         }
630
631         strcpy( Message_waves[Num_message_waves].name, wave_name );
632         Message_waves[Num_message_waves].num = -1;
633         Num_message_waves++;
634         return (Num_message_waves - 1);
635 }
636
637 // parses an individual message
638 void message_parse( )
639 {
640         MissionMessage *msgp;
641         char persona_name[NAME_LENGTH];
642
643         Assert ( Num_messages < MAX_MISSION_MESSAGES );
644         msgp = &Messages[Num_messages];
645
646         required_string("$Name:");
647         stuff_string(msgp->name, F_NAME, NULL);
648
649         // team
650         msgp->multi_team = -1;
651         if(optional_string("$Team:")){
652                 int mt;
653                 stuff_int(&mt);
654
655                 // keep it real
656                 if((mt < 0) || (mt >= 2)){
657                         mt = -1;
658                 }
659
660                 // only bother with filters if multiplayer and TvT
661                 if(Fred_running || ((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM)) ){
662                         msgp->multi_team = mt;
663                 }
664         }
665
666         // backwards compatibility for old fred missions - all new ones should use $MessageNew
667         if(optional_string("$Message:")){
668                 stuff_string(msgp->message, F_MESSAGE, NULL);
669         } else {
670                 required_string("$MessageNew:");
671                 stuff_string(msgp->message, F_MULTITEXT, NULL);
672         }
673
674         msgp->persona_index = -1;
675         if ( optional_string("+Persona:") ) {
676                 stuff_string(persona_name, F_NAME, NULL);
677                 msgp->persona_index = message_persona_name_lookup( persona_name );
678         }
679
680         if ( !Fred_running)
681                 msgp->avi_info.index = -1;
682         else
683                 msgp->avi_info.name = NULL;
684
685         if ( optional_string("+AVI Name:") ) {
686                 char avi_name[MAX_FILENAME_LEN];
687
688                 stuff_string(avi_name, F_NAME, NULL);
689                 if ( !Fred_running ) {
690                         msgp->avi_info.index = add_avi(avi_name);
691                 } else {
692                         msgp->avi_info.name = strdup(avi_name);
693                 }
694         }
695
696         if ( !Fred_running )
697                 msgp->wave_info.index = -1;
698         else
699                 msgp->wave_info.name = NULL;
700
701         if ( optional_string("+Wave Name:") ) {
702                 char wave_name[MAX_FILENAME_LEN];
703
704                 stuff_string(wave_name, F_NAME, NULL);
705                 if ( !Fred_running ) {
706                         msgp->wave_info.index = add_wave(wave_name);
707                 } else {
708                         msgp->wave_info.name = strdup(wave_name);
709                 }
710         }
711
712         Num_messages++;
713 }
714
715 void parse_msgtbl()
716 {
717         // open localization
718         lcl_ext_open();
719
720         read_file_text("messages.tbl");
721         reset_parse();
722         Num_messages = 0;
723         Num_personas = 0;
724
725         required_string("#Personas");
726         while ( required_string_either("#Messages", "$Persona:")){
727                 persona_parse();
728         }
729
730         required_string("#Messages");
731         while (required_string_either("#End", "$Name:")){
732                 message_parse();
733         }
734
735         required_string("#End");
736
737         // save the number of builting message things -- make initing between missions easier
738         Num_builtin_messages = Num_messages;
739         Num_builtin_avis = Num_message_avis;
740         Num_builtin_waves = Num_message_waves;
741
742         // close localization
743         lcl_ext_close();
744 }
745
746 // this is called at the start of each level
747 void messages_init()
748 {
749         int rval, i;
750         static int table_read = 0;
751
752         if ( !table_read ) {
753                 Command_persona = -1;
754                 if ((rval = setjmp(parse_abort)) != 0) {
755                         Error(LOCATION, "Error parsing '%s'\r\nError code = %i.\r\n", "messages.tbl", rval);
756
757                 } else {                        
758                         parse_msgtbl();
759                         table_read = 1;
760                 }
761         }
762
763         // reset the number of messages that we have for this mission
764         Num_messages = Num_builtin_messages;
765         Message_debug_index = Num_builtin_messages - 1;
766         Num_message_avis = Num_builtin_avis;
767         Num_message_waves = Num_builtin_waves;
768
769         // initialize the stuff for the linked lists of messages
770         MessageQ_num = 0;
771         for (i = 0; i < MAX_MESSAGE_Q; i++) {
772                 MessageQ[i].priority = -1;
773                 MessageQ[i].time_added = -1;
774                 MessageQ[i].message_num = -1;
775                 MessageQ[i].builtin_type = -1;
776                 MessageQ[i].min_delay_stamp = -1;
777                 MessageQ[i].group = 0;
778         }
779         
780         // this forces a reload of the AVI's and waves for builtin messages.  Needed because the flic and
781         // sound system also get reset between missions!
782         for (i = 0; i < Num_builtin_avis; i++ ) {
783                 Message_avis[i].anim_data = NULL;
784         }
785
786         for (i = 0; i < Num_builtin_waves; i++ ){
787                 Message_waves[i].num = -1;
788         }
789
790         Message_shipnum = -1;
791         Num_messages_playing = 0;
792         for ( i = 0; i < MAX_PLAYING_MESSAGES; i++ ) {
793                 Playing_messages[i].anim = NULL;
794                 Playing_messages[i].wave = -1;
795                 Playing_messages[i].id = -1;
796                 Playing_messages[i].priority = -1;
797                 Playing_messages[i].shipnum = -1;
798                 Playing_messages[i].builtin_type = -1;
799         }
800
801         // reinitialize the personas.  mark them all as not used
802         for ( i = 0; i < Num_personas; i++ ){
803                 Personas[i].flags &= ~PERSONA_FLAG_USED;
804         }
805
806         Message_wave_muted = 0;
807         Next_mute_time = 1;
808
809         memset(Message_times, 0, sizeof(int)*MAX_MISSION_MESSAGES);
810 }
811
812 // called to do cleanup when leaving a mission
813 void message_mission_shutdown()
814 {
815         int i;
816
817         mprintf(("Unloading in mission messages\n"));
818
819         training_mission_shutdown();
820
821         // remove the wave sounds from memory
822         for (i = 0; i < Num_message_waves; i++ ) {
823                 if ( Message_waves[i].num != -1 ){
824                         snd_unload( Message_waves[i].num );
825                 }
826         }
827
828 }
829
830 // functions to deal with queuing messages to the message system.
831
832 //      Compare function for system qsort() for sorting message queue entries based on priority.
833 //      Return values set to sort array in _decreasing_ order.  If priorities equal, sort based
834 // on time added into queue
835 int message_queue_priority_compare(const void *a, const void *b)
836 {
837         message_q *ma, *mb;
838
839         ma = (message_q *) a;
840         mb = (message_q *) b;
841
842         if (ma->priority > mb->priority) {
843                 return -1;
844         } else if (ma->priority < mb->priority) {
845                 return 1;
846         } else if (ma->time_added < mb->time_added) {
847                 return -1;
848         } else if (ma->time_added > mb->time_added) {
849                 return 1;
850         } else {
851                 return 0;
852         }
853 }
854
855 // function to kill all currently playing messages.  kill_all parameter tells us to
856 // kill only the animations that are playing, or wave files too
857 void message_kill_all( int kill_all )
858 {
859         int i;
860
861         Assert( Num_messages_playing );
862
863         // kill sounds for all voices currently playing
864         for ( i = 0; i < Num_messages_playing; i++ ) {
865                 if ( (Playing_messages[i].anim != NULL) && anim_playing(Playing_messages[i].anim) ) {
866                         anim_stop_playing( Playing_messages[i].anim );
867                         Playing_messages[i].anim=NULL;
868                 }
869
870                 if ( kill_all ) {
871                         if ( (Playing_messages[i].wave != -1 ) && snd_is_playing(Playing_messages[i].wave) ){
872                                 snd_stop( Playing_messages[i].wave );
873                         }
874
875                         Playing_messages[i].shipnum = -1;
876                 }
877         }
878
879         if ( kill_all ) {
880                 Num_messages_playing = 0;
881         }
882 }
883
884 // function to kill nth playing message
885 void message_kill_playing( int message_num )
886 {
887         Assert( message_num < Num_messages_playing );
888
889         if ( (Playing_messages[message_num].anim != NULL) && anim_playing(Playing_messages[message_num].anim) ) {
890                 anim_stop_playing( Playing_messages[message_num].anim );
891                 Playing_messages[message_num].anim=NULL;
892         }
893         if ( (Playing_messages[message_num].wave != -1 ) && snd_is_playing(Playing_messages[message_num].wave) )
894                 snd_stop( Playing_messages[message_num].wave );
895
896         Playing_messages[message_num].shipnum = -1;
897 }
898
899
900 // returns true if all messages currently playing are builtin messages
901 int message_playing_builtin()
902 {
903         int i;
904
905         for ( i = 0; i < Num_messages_playing; i++ ) {
906                 if ( Playing_messages[i].id >= Num_builtin_messages ){
907                         break;
908                 }
909         }
910
911         // if we got through the list without breaking, all playing messages are builtin messages
912         if ( i == Num_messages_playing ){
913                 return 1;
914         } else {
915                 return 0;
916         }
917 }
918
919 // returns true in any playing message is of the specific builtin type
920 int message_playing_specific_builtin( int builtin_type )
921 {
922         int i;
923
924         for (i = 0; i < Num_messages_playing; i++ ) {
925                 if ( (Playing_messages[i].id < Num_builtin_messages) && (Playing_messages[i].builtin_type == builtin_type) ){
926                         return 1;
927                 }
928         }
929
930         return 0;
931 }
932
933 // returns true if all messages current playing are unique messages
934 int message_playing_unique()
935 {
936         int i;
937
938         for ( i = 0; i < Num_messages_playing; i++ ) {
939                 if ( Playing_messages[i].id < Num_builtin_messages ){
940                         break;
941                 }
942         }
943
944         // if we got through the list without breaking, all playing messages are builtin messages
945         if ( i == Num_messages_playing ){
946                 return 1;
947         } else {
948                 return 0;
949         }
950 }
951
952
953 // returns the highest priority of the currently playing messages
954 #define MESSAGE_GET_HIGHEST             1
955 #define MESSAGE_GET_LOWEST                      2
956 int message_get_priority(int which)
957 {
958         int i;
959         int priority;
960
961         if ( which == MESSAGE_GET_HIGHEST ){
962                 priority = MESSAGE_PRIORITY_LOW;
963         } else {
964                 priority = MESSAGE_PRIORITY_HIGH;
965         }
966
967         for ( i = 0; i < Num_messages_playing; i++ ) {
968                 if ( (which == MESSAGE_GET_HIGHEST) && (Playing_messages[i].priority > priority) ){
969                         priority = Playing_messages[i].priority;
970                 } else if ( (which == MESSAGE_GET_LOWEST) && (Playing_messages[i].priority < priority) ){
971                         priority = Playing_messages[i].priority;
972                 }
973         }
974
975         return priority;
976 }
977
978
979 // removes current message from the queue
980 void message_remove_from_queue(message_q *q)
981 {       
982         // quick out if nothing to do.
983         if ( MessageQ_num <= 0 ) {
984                 return;
985         }       
986
987         MessageQ_num--;
988         q->priority = -1;
989         q->time_added = -1;
990         q->message_num = -1;
991         q->builtin_type = -1;
992         q->min_delay_stamp = -1;
993         q->group = 0;   
994
995         if ( MessageQ_num > 0 ) {
996                 qsort(MessageQ, MAX_MESSAGE_Q, sizeof(message_q), message_queue_priority_compare);
997         }
998 }
999
1000 // Load in the sound data for a message.
1001 //
1002 // index - index into the Message_waves[] array
1003 //
1004 void message_load_wave(int index, const char *filename)
1005 {
1006         if (index == -1) {
1007                 Int3();
1008                 return;
1009         }
1010
1011         if ( Message_waves[index].num >= 0) {
1012                 return;
1013         }
1014
1015         game_snd tmp_gs;
1016         memset(&tmp_gs, 0, sizeof(game_snd));
1017         strcpy( tmp_gs.filename, filename );
1018         Message_waves[index].num = snd_load( &tmp_gs );
1019         if ( Message_waves[index].num == -1 ) {
1020                 nprintf (("messaging", "Cannot load message wave: %s.  Will not play\n", Message_waves[index].name ));
1021         }
1022 }
1023
1024 // Play wave file associated with message
1025 // input: m             =>              pointer to message description
1026 //
1027 // note: changes Messave_wave_duration, Playing_messages[].wave, and Message_waves[].num
1028 void message_play_wave( message_q *q )
1029 {
1030         int index;
1031         MissionMessage *m;
1032         char filename[MAX_FILENAME_LEN];
1033
1034         // check for multiple messages playing.  don't check builtin messages.
1035         if (q->message_num >= Num_builtin_messages) {
1036                 if ( (f2fl(Missiontime - Message_times[q->message_num]) < 10) && (f2fl(Missiontime) > 10) ) {
1037                         // Int3();  // Get Andsager
1038                 }
1039                 Message_times[q->message_num] = Missiontime;
1040         }
1041
1042         m = &Messages[q->message_num];
1043
1044         if ( m->wave_info.index != -1 ) {
1045                 index = m->wave_info.index;
1046
1047                 // sanity check
1048                 Assert( index != -1 );
1049                 if ( index == -1 ){
1050                         return;
1051                 }
1052
1053                 // if we need to bash the wave name because of "conversion" to terran command, do it here
1054                 strcpy( filename, Message_waves[index].name );
1055                 if ( q->flags & MQF_CONVERT_TO_COMMAND ) {
1056                         char *p, new_filename[MAX_FILENAME_LEN];
1057
1058                         Message_waves[index].num = -1;                                  // forces us to reload the message
1059
1060                         // bash the filename here. Look for "[1-6]_" at the front of the message.  If found, then
1061                         // convert to TC_*
1062                         p = strchr(filename, '_' );
1063                         if ( p == NULL ) {
1064                                 mprintf(("Cannot convert %s to terran command wave -- find Sandeep or Allender\n", Message_waves[index].name));
1065                                 return;
1066                         }
1067
1068                         // prepend the command name, and then the rest of the filename.
1069                         p++;
1070                         strcpy( new_filename, COMMAND_WAVE_PREFIX );
1071                         strcat( new_filename, p );
1072                         strcpy( filename, new_filename );
1073                 }
1074
1075                 // load the sound file into memory
1076                 message_load_wave(index, filename);
1077                 if ( Message_waves[index].num == -1 ) {
1078                         m->wave_info.index = -1;
1079                 }
1080
1081                 if ( m->wave_info.index >= 0 ) {
1082                         // this call relies on the fact that snd_play returns -1 if the sound cannot be played
1083                         Message_wave_duration = snd_get_duration(Message_waves[index].num);
1084                         Playing_messages[Num_messages_playing].wave = snd_play_raw( Message_waves[index].num, 0.0f );
1085                 }
1086         }
1087 }
1088
1089 // Determine the starting frame for the animation
1090 // input:       time    =>              time of voice clip, in ms
1091 //                              ani     =>              pointer to anim data
1092 //                              reverse =>      flag to indicate that the start should be time ms from the end (used for death screams)
1093 int message_calc_anim_start_frame(int time, anim *ani, int reverse)
1094 {
1095         float   wave_time, anim_time;
1096         int     start_frame;
1097
1098         start_frame=0;
1099
1100         // If no voice clip exists, start from beginning of anim
1101         if ( time <= 0 ) {
1102                 return start_frame;
1103         }
1104
1105         // convert time to seconds
1106         wave_time = time/1000.0f;
1107         anim_time = ani->time;
1108
1109         // If voice clip is longer than anim, start from beginning of anim
1110         if ( wave_time >= (anim_time) ) {
1111                 return start_frame;
1112         }
1113
1114         if ( reverse ) {
1115                 start_frame = (ani->total_frames-1) - fl2i(ani->fps * wave_time + 0.5f);
1116         } else {
1117                 int num_frames_extra;
1118                 num_frames_extra = fl2i(ani->fps * (anim_time - wave_time) + 0.5f);
1119                 if ( num_frames_extra > 0 ) {
1120                         start_frame=rand()%num_frames_extra;
1121                 }
1122         }
1123
1124         if ( start_frame < 0 ) {
1125                 Int3();
1126                 start_frame=0;
1127         }
1128
1129         return start_frame;
1130 }
1131
1132 // Play animation associated with message
1133 // input:       m               =>              pointer to message description
1134 //                              q               =>              message queue data
1135 //
1136 // note: changes Messave_wave_duration, Playing_messages[].wave, and Message_waves[].num
1137 void message_play_anim( message_q *q )
1138 {
1139         message_extra   *anim_info;
1140         int                             is_death_scream=0, persona_index=-1, rand_index=-1;
1141         char                            ani_name[MAX_FILENAME_LEN], *p;
1142         MissionMessage  *m;
1143
1144         m = &Messages[q->message_num];
1145
1146         // check to see if the avi_index is valid -- try and load/play the avi if so.
1147         if ( m->avi_info.index < 0 ) {
1148                 return;
1149         }
1150
1151         anim_info = &Message_avis[m->avi_info.index];
1152
1153         // get the filename.  Strip off the extension since we won't need it anyway
1154         strcpy(ani_name, anim_info->name);
1155         p = strchr(ani_name, '.');                      // gets us to the extension
1156         if ( p ) {
1157                 *p = '\0';
1158         }
1159
1160         // builtin messages are given a base ani which we should add a suffix on before trying
1161         // to load the animation.  See if this message is a builtin message which has a persona
1162         // attached to it.  Deal with munging the name
1163
1164         // support ships use a wingman head.
1165         // terran command uses it's own set of heads.
1166         int subhead_selected = FALSE;
1167         if ( (q->message_num < Num_builtin_messages) || !(_strnicmp(HEAD_PREFIX_STRING, ani_name, strlen(HEAD_PREFIX_STRING)-1)) ) {
1168                 persona_index = m->persona_index;
1169                 
1170                 // if this ani should be converted to a terran command, set the persona to the command persona
1171                 // so the correct head plays.
1172                 if ( q->flags & MQF_CONVERT_TO_COMMAND ) {
1173                         persona_index = Command_persona;
1174                         strcpy( ani_name, COMMAND_HEAD_PREFIX );
1175                 }
1176
1177                 if ( Personas[persona_index].flags & (PERSONA_FLAG_WINGMAN | PERSONA_FLAG_SUPPORT) ) {
1178                         // get a random head -- it's one of two.
1179                         if ( q->builtin_type == MESSAGE_WINGMAN_SCREAM ) {
1180                                 rand_index=2;   // 3rd version is always death animation
1181                                 is_death_scream=1;
1182                         } else {
1183                                 rand_index = (Missiontime % MAX_WINGMAN_HEADS);
1184                         }
1185                         sprintf(ani_name, "%s%c", ani_name, 'a'+rand_index);
1186                         subhead_selected = TRUE;
1187                 } else if ( Personas[persona_index].flags & (PERSONA_FLAG_COMMAND | PERSONA_FLAG_LARGE) ) {
1188                         // get a random head -- it's one of two.
1189                         rand_index = (Missiontime % MAX_COMMAND_HEADS);
1190                         sprintf(ani_name, "%s%c", ani_name, 'a'+rand_index);
1191                         subhead_selected = TRUE;
1192                 }
1193                 if (!subhead_selected) {
1194                         // choose between a and b
1195                         rand_index = (Missiontime % MAX_WINGMAN_HEADS);
1196                         sprintf(ani_name, "%s%c", ani_name, 'a'+rand_index);
1197                         mprintf(("message '%s' with invalid head.  Fix by assigning persona to the message.\n", m->name));
1198                 }
1199                 nprintf(("Messaging", "playing head %s for %s\n", ani_name, q->who_from));
1200         }
1201
1202         // check to see if the avi has been loaded.  If not, then load the AVI.  On an error loading
1203         // the avi, set the top level index to -1 to avoid multiple tries at loading the flick.
1204         if ( hud_gauge_active(HUD_TALKING_HEAD) ) {
1205                 anim_info->anim_data = anim_load( ani_name, 0 );
1206         } else {
1207                 return;
1208         }
1209
1210         if ( anim_info->anim_data == NULL ) {
1211                 nprintf (("messaging", "Cannot load message avi %s.  Will not play.\n", ani_name));
1212                 m->avi_info.index = -1;                 // if cannot load the avi -- set this index to -1 to avoid trying to load multiple times
1213         }
1214
1215         if ( m->avi_info.index >= 0 ) {
1216                 // This call relies on the fact that AVI_play will return -1 if the AVI cannot be played
1217                 // if any messages are already playing, kill off any head anims that are currently playing.  We will
1218                 // only play a head anim of the newest messages being played
1219                 if ( Num_messages_playing > 0 ) {
1220                         nprintf(("messaging", "killing off any currently playing head animations\n"));
1221                         message_kill_all( 0 );
1222                 }
1223
1224                 if ( hud_gauge_active(HUD_TALKING_HEAD) ) {
1225                         int anim_start_frame;
1226                         anim_play_struct aps;
1227
1228                         // figure out anim start frame
1229                         anim_start_frame = message_calc_anim_start_frame(Message_wave_duration, anim_info->anim_data, is_death_scream);
1230                         anim_play_init(&aps, anim_info->anim_data, Head_coords[gr_screen.res][0], Head_coords[gr_screen.res][1]);
1231                         aps.start_at = anim_start_frame;
1232                         
1233                         // aps.color = &HUD_color_defaults[HUD_color_alpha];
1234                         aps.color = &HUD_config.clr[HUD_TALKING_HEAD];
1235
1236                         Playing_messages[Num_messages_playing].anim = anim_play(&aps);
1237                 }
1238         }
1239 }
1240
1241 // process the message queue -- called once a frame
1242 void message_queue_process()
1243 {       
1244         char    buf[4096];
1245         message_q *q;
1246         int i;
1247         MissionMessage *m;
1248
1249         // Don't play messages until first frame has been rendered
1250         if ( Framecount < 2 ) {
1251                 return;
1252         }
1253
1254         // determine if all playing messages (if any) are done playing.  If any are done, remove their
1255         // entries collapsing the Playing_messages array if necessary
1256         if ( Num_messages_playing > 0 ) {
1257
1258                 // for each message playing, determine if it is done.
1259                 i = 0;
1260                 while ( i < Num_messages_playing ) {
1261                         int ani_done, wave_done, j;
1262
1263                         ani_done = 1;
1264                         if ( (Playing_messages[i].anim != NULL) && anim_playing(Playing_messages[i].anim) )
1265                                 ani_done = 0;
1266
1267                         wave_done = 1;
1268
1269 //                      if ( (Playing_messages[i].wave != -1) && snd_is_playing(Playing_messages[i].wave) )
1270                         if ( (Playing_messages[i].wave != -1) && (snd_time_remaining(Playing_messages[i].wave) > 250) )
1271                                 wave_done = 0;
1272
1273                         // AL 1-20-98: If voice message is done, kill the animation early
1274                         if ( (Playing_messages[i].wave != -1) && wave_done ) {
1275                                 if ( !ani_done ) {
1276                                         anim_stop_playing( Playing_messages[i].anim );
1277                                 }
1278                         }
1279
1280                         // see if the ship sending this message is dying.  If do, kill wave and anim
1281                         if ( Playing_messages[i].shipnum != -1 ) {
1282                                 if ( (Ships[Playing_messages[i].shipnum].flags & SF_DYING) && (Playing_messages[i].builtin_type != MESSAGE_WINGMAN_SCREAM) ) {
1283                                         int shipnum;
1284
1285                                         shipnum = Playing_messages[i].shipnum;
1286                                         message_kill_playing( i );
1287                                         // force this guy to scream
1288                                         // AL 22-2-98: Ensure don't use -1 to index into ships array.  Mark, something is incorrect 
1289                                         //             here, since message_kill_playing() seems to always set Playing_messages[i].shipnum to -1
1290                                         // MWA 3/24/98 -- save shipnum before killing message
1291                                         // 
1292                                         Assert( shipnum >= 0 );
1293                                         if ( !(Ships[shipnum].flags & SF_SHIP_HAS_SCREAMED) ) {
1294                                                 ship_scream( &Ships[shipnum] );
1295                                         }
1296                                         continue;                                                       // this should keep us in the while() loop with same value of i.                                                                                                                
1297                                 }                                                                                       // we should enter the next 'if' statement during next pass
1298                         }
1299
1300                         // if both ani and wave are done, mark internal variable so we can do next message on queue, and
1301                         // global variable to clear voice brackets on hud
1302                         if ( wave_done && ani_done ) {
1303                                 nprintf(("messaging", "Message %d is done playing\n", i));
1304                                 Message_shipnum = -1;
1305                                 Num_messages_playing--;
1306                                 if ( Num_messages_playing == 0 )
1307                                         break;
1308
1309                                 // there is still another message playing.  Collapse the playing_message array
1310                                 nprintf(("messaging", "Collapsing playing message stack\n"));
1311                                 for ( j = i+1; j < Num_messages_playing + 1; j++ ) {
1312                                         Playing_messages[j-1] = Playing_messages[j];
1313                                 }
1314                         } else {
1315                                 // messages is not done playing -- move to next message
1316                                 i++;
1317                         }
1318                 }
1319         }
1320
1321         // preprocess message queue and remove anything on the queue that is too old.  If next message on
1322         // the queue can be played, then break out of the loop.  Otherwise, loop until nothing on the queue
1323         while ( MessageQ_num > 0 ) {
1324                 q = &MessageQ[0];               
1325                 if ( timestamp_valid(q->window_timestamp) && timestamp_elapsed(q->window_timestamp) && !q->group) {
1326                         // remove message from queue and see if more to remove
1327                         nprintf(("messaging", "Message %s didn't play because it didn't fit into time window.\n", Messages[q->message_num].name));
1328                         if ( q->message_num < Num_builtin_messages ){                   // we should only ever remove builtin messages this way
1329                                 message_remove_from_queue(q);
1330                         } else {
1331                                 break;
1332                         }
1333                 } else {
1334                         break;
1335                 }
1336         }
1337
1338         // no need to process anything if there isn't anything on the queue
1339         if ( MessageQ_num <= 0 ){
1340                 return;
1341         }
1342
1343         // get a pointer to an item on the queue
1344         int found = -1;
1345         int idx = 0;
1346         while((found == -1) && (idx < MessageQ_num)){
1347                 // if this guy has no min delay timestamp, or it has expired, select him
1348                 if((MessageQ[idx].min_delay_stamp == -1) || timestamp_elapsed(MessageQ[idx].min_delay_stamp)){
1349                         found = idx;
1350                         break;
1351                 }
1352
1353                 // next
1354                 idx++;
1355         }
1356
1357         // if we didn't find anything, bail
1358         if(found == -1){
1359                 return;
1360         }
1361         // if this is not the first item on the queue, make it the first item
1362         if(found != 0){
1363                 message_q temp;
1364
1365                 // store the entry
1366                 memcpy(&temp, &MessageQ[found], sizeof(message_q));
1367
1368                 // move all other entries up
1369                 for(idx=found; idx>0; idx--){
1370                         memcpy(&MessageQ[idx], &MessageQ[idx-1], sizeof(message_q));
1371                 }
1372
1373                 // plop the entry down as being first
1374                 memcpy(&MessageQ[0], &temp, sizeof(message_q));
1375         }
1376
1377         q = &MessageQ[0];
1378         Assert ( q->message_num != -1 );
1379         Assert ( q->priority != -1 );
1380         Assert ( q->time_added != -1 );
1381
1382         if ( Num_messages_playing ) {
1383                 // peek at the first message on the queue to see if it should interrupt, or overlap a currently
1384                 // playing message.  Mission specific messages will always interrupt builtin messages.  They
1385                 // will never interrupt other mission specific messages.
1386                 //
1387                 //  Builtin message might interrupt other builtin messages, or overlap them, all depending on
1388                 // message priority.
1389
1390                 if ( q->builtin_type == MESSAGE_HAMMER_SWINE ) {
1391                         message_kill_all(1);
1392                 } else if ( message_playing_specific_builtin(MESSAGE_HAMMER_SWINE) ) {
1393                         MessageQ_num = 0;
1394                         return;
1395                 } else if ( message_playing_builtin() && ( q->message_num >= Num_builtin_messages) && (q->priority > MESSAGE_PRIORITY_LOW) ) {
1396                         // builtin is playing and we have a unique message to play.  Kill currently playing message
1397                         // so unique can play uninterrupted.  Only unique messages higher than low priority will interrupt
1398                         // other messages.
1399                         message_kill_all(1);
1400                         nprintf(("messaging", "Killing all currently playing messages to play unique message\n"));
1401                 } else if ( message_playing_builtin() && (q->message_num < Num_builtin_messages) ) {
1402                         // when a builtin message is queued, we might either overlap or interrupt the currently
1403                         // playing message.
1404                         //
1405                         // we have to check for num_messages_playing (again), since code for death scream might
1406                         // kill all messages.
1407                         if ( Num_messages_playing ) {
1408                                 if ( message_get_priority(MESSAGE_GET_HIGHEST) < q->priority ) {
1409                                         // lower priority message playing -- kill it.
1410                                         message_kill_all(1);
1411                                         nprintf(("messaging", "Killing all currently playing messages to play high priority builtin\n"));
1412                                 } else if ( message_get_priority(MESSAGE_GET_LOWEST) > q->priority ) {
1413                                         // queued message is a lower priority, so wait it out
1414                                         return;
1415                                 } else {
1416                                         // if we get here, then queued messages is a builtin message with the same priority
1417                                         // as the currently playing messages.  This state will cause messages to overlap.
1418                                         nprintf(("messaging", "playing builtin message (overlap) because priorities match\n"));
1419                                 }
1420                         }
1421                 } else if ( message_playing_unique() && (q->message_num < Num_builtin_messages) ) {
1422                         // code messages can kill any low priority mission specific messages
1423                         if ( Num_messages_playing ) {
1424                                 if ( message_get_priority(MESSAGE_GET_HIGHEST) == MESSAGE_PRIORITY_LOW ) {
1425                                         message_kill_all(1);
1426                                         nprintf(("messaging", "Killing low priority unique messages to play code message\n"));
1427                                 } else {
1428                                         return;                 // do nothing.
1429                                 }
1430                         }
1431                 } else {
1432                         return;
1433                 }
1434         }
1435
1436         // if we are playing the maximum number of voices, then return.  Make the check here since the above
1437         // code might kill of currently playing messages
1438         if ( Num_messages_playing == MAX_PLAYING_MESSAGES )
1439                 return;
1440
1441         Message_shipnum = ship_name_lookup( q->who_from );
1442
1443         // see if we need to check if sending ship is alive
1444         if ( q->flags & MQF_CHECK_ALIVE ) {
1445                 if ( Message_shipnum == -1 ) {
1446                         goto all_done;
1447                 }
1448         }
1449
1450         // if this is a ship, then don't play anything if this ship is already talking
1451         if ( Message_shipnum != -1 ) {
1452                 for ( i = 0; i < Num_messages_playing; i++ ) {
1453                         if ( (Playing_messages[i].shipnum != -1) && (Playing_messages[i].shipnum == Message_shipnum) ){
1454                                 return;
1455                         }
1456                 }
1457         }
1458
1459         // set up module globals for this message
1460         m = &Messages[q->message_num];
1461         Playing_messages[Num_messages_playing].anim = NULL;
1462         Playing_messages[Num_messages_playing].wave  = -1;
1463         Playing_messages[Num_messages_playing].id  = q->message_num;
1464         Playing_messages[Num_messages_playing].priority = q->priority;
1465         Playing_messages[Num_messages_playing].shipnum = Message_shipnum;
1466         Playing_messages[Num_messages_playing].builtin_type = q->builtin_type;
1467
1468         Message_wave_duration = 0;
1469
1470         // translate tokens in message to the real things
1471         message_translate_tokens(buf, m->message);
1472
1473         // AL: added 07/14/97.. only play avi/sound if in gameplay
1474         if ( gameseq_get_state() != GS_STATE_GAME_PLAY )
1475                 goto all_done;
1476
1477         // AL 4-7-98: Can't receive messages if comm is destroyed
1478         if ( hud_communications_state(Player_ship) == COMM_DESTROYED ) {
1479                 goto all_done;
1480         }
1481
1482         //      Don't play death scream unless a small ship.
1483         if ( q->builtin_type == MESSAGE_WINGMAN_SCREAM ) {
1484                 int t = Ship_info[Ships[Message_shipnum].ship_info_index].flags;
1485                 int t2 = SIF_SMALL_SHIP;
1486                 int t3 = t & t2;
1487                 if (!t3) {
1488                         goto all_done;
1489                 }
1490         }
1491
1492         // play wave first, since need to know duration for picking anim start frame
1493         message_play_wave(q);
1494
1495         // play animation for head
1496         #ifndef DEMO // do we want this for FS2_DEMO
1497                 message_play_anim(q);
1498         #endif
1499         
1500         // distort the message if comms system is damaged
1501         message_maybe_distort_text(buf);
1502
1503 #ifndef NDEBUG
1504         // debug only -- if the message is a builtin message, put in parens whether or not the voice played
1505         if ( Playing_messages[Num_messages_playing].wave == -1 ) {
1506                 strcat( buf, NOX("..(no wavefile for voice)"));
1507                 snd_play(&Snds[SND_CUE_VOICE]);
1508         }
1509 #endif
1510
1511         HUD_sourced_printf( q->source, NOX("%s: %s"), q->who_from, buf );
1512
1513         if ( Message_shipnum >= 0 ) {
1514                 hud_target_last_transmit_add(Message_shipnum);
1515         }
1516
1517 all_done:
1518         Num_messages_playing++;
1519         message_remove_from_queue( q );
1520 }
1521
1522 // queues up a message to display to the player
1523 void message_queue_message( int message_num, int priority, int timing, char *who_from, int source, int group, int delay, int builtin_type )
1524 {
1525         int i, m_persona;
1526
1527         if ( message_num < 0 ) return;
1528
1529         // some messages can get queued quickly.  Try to filter out certain types of messages before
1530         // they get queued if there are other messages of the same type already queued
1531         if ( (builtin_type == MESSAGE_REARM_ON_WAY) || (builtin_type == MESSAGE_OOPS) ) {
1532                 // if it is already playing, then don't play it
1533                 if ( message_playing_specific_builtin(builtin_type) ) 
1534                         return;
1535
1536                 for ( i = 0; i < MessageQ_num; i++ ) {
1537                         // if one of these messages is already queued, the don't play
1538                         if ( (MessageQ[i].message_num == message_num) && (MessageQ[i].builtin_type == builtin_type) )
1539                                 return;
1540
1541                 }
1542         }
1543
1544         // check to be sure that we haven't reached our max limit on these messages yet.
1545         if ( MessageQ_num == MAX_MESSAGE_Q ) {
1546                 Int3();                                                                                 
1547                 return;
1548         }
1549
1550         // if player is a traitor, no messages for him!!!
1551         if ( Player_ship->team == TEAM_TRAITOR ) {
1552                 return;
1553         }
1554
1555         m_persona = Messages[message_num].persona_index;
1556
1557         // put the message into a slot
1558         i = MessageQ_num;
1559         MessageQ[i].time_added = Missiontime;
1560         MessageQ[i].priority = priority;
1561         MessageQ[i].message_num = message_num;
1562         MessageQ[i].source = source;
1563         MessageQ[i].builtin_type = builtin_type;
1564         MessageQ[i].min_delay_stamp = timestamp(delay);
1565         MessageQ[i].group = group;
1566         strcpy(MessageQ[i].who_from, who_from);
1567
1568         // SPECIAL HACK -- if the who_from is terran command, and there is a wingman persona attached
1569         // to this message, then set a bit to tell the wave/anim playing code to play the command version
1570         // of the wave and head
1571         MessageQ[i].flags = 0;
1572         if ( !stricmp(who_from, TERRAN_COMMAND) && (m_persona != -1) && (Personas[m_persona].flags & PERSONA_FLAG_WINGMAN) ) {
1573                 MessageQ[i].flags |= MQF_CONVERT_TO_COMMAND;
1574                 MessageQ[i].source = HUD_SOURCE_TERRAN_CMD;
1575         }
1576
1577         if ( (m_persona != -1) && (Personas[m_persona].flags & PERSONA_FLAG_WINGMAN) ) {
1578                 if ( !strstr(who_from, ".wav") ) {
1579                         MessageQ[i].flags |= MQF_CHECK_ALIVE;
1580                 }
1581         }
1582
1583         // set the timestamp of when to play this message based on the 'timing' value
1584         if ( timing == MESSAGE_TIME_IMMEDIATE )
1585                 MessageQ[i].window_timestamp = timestamp(MESSAGE_IMMEDIATE_TIMESTAMP);
1586         else if ( timing == MESSAGE_TIME_SOON )
1587                 MessageQ[i].window_timestamp = timestamp(MESSAGE_SOON_TIMESTAMP);
1588         else
1589                 MessageQ[i].window_timestamp = timestamp(MESSAGE_ANYTIME_TIMESTAMP);            // make invalid
1590
1591         MessageQ_num++;
1592         qsort(MessageQ, MAX_MESSAGE_Q, sizeof(message_q), message_queue_priority_compare);
1593
1594         // Try to start it!
1595         // MWA -- called every frame from game loop
1596         //message_queue_process();
1597 }
1598
1599 // function to return the persona index of the given ship.  If it isn't assigned, it will be
1600 // in this function.  persona_type could be a wingman, Terran Command, or other generic ship
1601 // type personas.  ship is the ship we should assign a persona to
1602 int message_get_persona( ship *shipp )
1603 {
1604         int i, ship_type, slist[MAX_PERSONAS], count;
1605
1606         if ( shipp != NULL ) {
1607                 // see if this ship has a persona
1608                 if ( shipp->persona_index != -1 )
1609                         return shipp->persona_index;
1610
1611                 // get the type of ship (i.e. support, fighter/bomber, etc)
1612                 ship_type = Ship_info[shipp->ship_info_index].flags;
1613
1614                 // shorcut for Vasudan personas.  All vasudan fighters/bombers use the same persona.  All Vasudan
1615                 // large ships will use the same persona
1616                 if ( Ship_info[shipp->ship_info_index].species == SPECIES_VASUDAN ) {
1617                         int persona_needed;
1618
1619                         if ( ship_type & (SIF_FIGHTER|SIF_BOMBER) ) {
1620                                 persona_needed = PERSONA_FLAG_WINGMAN;
1621                         } else if ( ship_type & SIF_SUPPORT ) {
1622                                 persona_needed = PERSONA_FLAG_SUPPORT;
1623                         } else {
1624                                 persona_needed = PERSONA_FLAG_LARGE;
1625                         }
1626
1627                         // iternate through the persona list finding the one that we need
1628                         for ( i = 0; i < Num_personas; i++ ) {
1629                                 if ( (Personas[i].flags & persona_needed) && (Personas[i].flags & PERSONA_FLAG_VASUDAN) ) {
1630                                         nprintf(("messaging", "assigning vasudan persona %s to %s\n", Personas[i].name, shipp->ship_name));
1631                                         return i;
1632                                 }
1633                         }
1634
1635                         // make support personas use the terran persona by not returning here when looking for 
1636                         // Vasudan persona
1637                         if ( persona_needed != PERSONA_FLAG_SUPPORT )
1638                                 return -1;                      // shouldn't get here eventually, but return -1 for now to deal with missing persona
1639                 }
1640
1641                 // iterate through the persona list looking for one not used.  Look at the type of persona
1642                 // and try to determine appropriate personas to use.
1643                 for ( i = 0; i < Num_personas; i++ ) {
1644
1645                         // if this is a vasudan persona -- skip it
1646                         if ( Personas[i].flags & PERSONA_FLAG_VASUDAN )
1647                                 continue;
1648
1649                         // check the ship types, and don't try to assign those which don't type match
1650                         if ( (ship_type & SIF_SUPPORT) && !(Personas[i].flags & PERSONA_FLAG_SUPPORT) )
1651                                 continue;
1652                         else if ( (ship_type & (SIF_FIGHTER|SIF_BOMBER)) && !(Personas[i].flags & PERSONA_FLAG_WINGMAN) )
1653                                 continue;
1654                         else if ( !(ship_type & (SIF_FIGHTER|SIF_BOMBER|SIF_SUPPORT)) && !(Personas[i].flags & PERSONA_FLAG_LARGE) )
1655                                 continue;
1656
1657                         if ( !(Personas[i].flags & PERSONA_FLAG_USED) ) {
1658                                 nprintf(("messaging", "assigning persona %s to %s\n", Personas[i].name, shipp->ship_name));
1659                                 Personas[i].flags |= PERSONA_FLAG_USED;
1660                                 return i;
1661                         }
1662                 }
1663
1664                 // grab a random one, and reuse it (staying within type specifications)
1665                 count = 0;
1666                 for ( i = 0; i < Num_personas; i++ ) {
1667
1668                         // see if ship meets our criterea
1669                         if ( (ship_type & SIF_SUPPORT) && !(Personas[i].flags & PERSONA_FLAG_SUPPORT) )
1670                                 continue;
1671                         else if ( (ship_type & (SIF_FIGHTER|SIF_BOMBER)) && !(Personas[i].flags & PERSONA_FLAG_WINGMAN) )
1672                                 continue;
1673                         else if ( !(ship_type & (SIF_FIGHTER|SIF_BOMBER|SIF_SUPPORT)) && !(Personas[i].flags & PERSONA_FLAG_LARGE) )
1674                                 continue;
1675                         else if ( Personas[i].flags & PERSONA_FLAG_VASUDAN )            // don't use any vasudan persona
1676                                 continue;
1677
1678                         slist[count] = i;
1679                         count++;
1680                 }
1681
1682                 // couldn't find appropriate persona type
1683                 if ( count == 0 )
1684                         return -1;
1685
1686                 // now get a random one from the list
1687                 i = (rand() % count);
1688                 i = slist[i];
1689                         
1690                 nprintf(("messaging", "Couldn't find a new persona for ship %s, reusing persona %s\n", shipp->ship_name, Personas[i].name));
1691
1692                 return i;
1693         }
1694
1695         // for now -- we don't support other types of personas (non-wingman personas)
1696         Int3();
1697         return 0;
1698 }
1699
1700 // given a message id#, should it be filtered for me?
1701 int message_filter_multi(int id)
1702 {
1703         // not multiplayer
1704         if(!(Game_mode & GM_MULTIPLAYER)){
1705                 return 0;
1706         }
1707
1708         // bogus
1709         if((id < 0) || (id >= Num_messages)){
1710                 mprintf(("Filtering bogus mission message!\n"));
1711                 return 1;
1712         }
1713
1714         // builtin messages
1715         if(id < Num_builtin_messages){
1716         }
1717         // mission-specific messages
1718         else {
1719                 // not team filtered
1720                 if(Messages[id].multi_team < 0){
1721                         return 0;
1722                 }
1723
1724                 // not TvT
1725                 if(!(Netgame.type_flags & NG_TYPE_TEAM)){
1726                         return 0;
1727                 }
1728
1729                 // is this for my team?
1730                 if((Net_player != NULL) && (Net_player->p_info.team != Messages[id].multi_team)){
1731                         mprintf(("Filtering team-based mission message!\n"));
1732                         return 1;
1733                 }
1734         }               
1735         
1736         return 0;
1737 }
1738
1739 // send_unique_to_player sends a mission unique (specific) message to the player (possibly a multiplayer
1740 // person).  These messages are *not* the builtin messages
1741 void message_send_unique_to_player( char *id, void *data, int m_source, int priority, int group, int delay )
1742 {
1743         int i, source;
1744         char *who_from;
1745
1746         source = 0;
1747         who_from = NULL;
1748         for (i=0; i<Num_messages; i++) {
1749                 // find the message
1750                 if ( !stricmp(id, Messages[i].name) ) {
1751
1752                         // if the ship pointer and special_who are both NULL then this is from generic "Terran Command"
1753                         // if the ship is NULL and special_who is not NULL, then this is from special_who
1754                         // otherwise, message is from ship.
1755                         if ( m_source == MESSAGE_SOURCE_COMMAND ) {
1756                                 who_from = TERRAN_COMMAND;
1757                                 source = HUD_SOURCE_TERRAN_CMD;
1758                         } else if ( m_source == MESSAGE_SOURCE_SPECIAL ) {
1759                                 who_from = (char *)data;
1760                                 source = HUD_SOURCE_TERRAN_CMD;
1761                         } else if ( m_source == MESSAGE_SOURCE_WINGMAN ) {
1762                                 int m_persona, ship_index;
1763
1764                                 // find a wingman with the same persona as this message.  If the message's persona doesn't
1765                                 // exist, we will use Terran command
1766                                 m_persona = Messages[i].persona_index;
1767                                 if ( m_persona == -1 ) {
1768                                         mprintf(("Warning:  Message %d has no persona assigned.\n", i));
1769                                 }
1770
1771                                 // get a ship                                           
1772                                 ship_index = ship_get_random_player_wing_ship( SHIP_GET_NO_PLAYERS, 0.0f, m_persona, 1, Messages[i].multi_team);
1773
1774                                 // if the ship_index is -1, then make the message come from Terran command
1775                                 if ( ship_index == -1 ) {
1776                                         who_from = TERRAN_COMMAND;
1777                                         source = HUD_SOURCE_TERRAN_CMD;
1778                                 } else {
1779                                         who_from = Ships[ship_index].ship_name;
1780                                         source = HUD_get_team_source(Ships[ship_index].team);
1781                                 }
1782
1783                         } else if ( m_source == MESSAGE_SOURCE_SHIP ) {
1784                                 ship *shipp;
1785
1786                                 shipp = (ship *)data;
1787                                 who_from = shipp->ship_name;
1788                                 source = HUD_get_team_source(shipp->team);
1789
1790                                 // be sure that this ship can actually send a message!!! (i.e. not-not-flyable -- get it!)
1791                                 Assert( !(Ship_info[shipp->ship_info_index].flags & SIF_NOT_FLYABLE) );         // get allender or alan
1792                         }
1793
1794                         // not multiplayer or this message is for me, then queue it
1795                         // if ( !(Game_mode & GM_MULTIPLAYER) || ((multi_target == -1) || (multi_target == MY_NET_PLAYER_NUM)) ){
1796
1797                         // maybe filter it out altogether
1798                         if(!message_filter_multi(i)){
1799                                 message_queue_message( i, priority, MESSAGE_TIME_ANYTIME, who_from, source, group, delay );
1800                         }
1801
1802                         // record to the demo if necessary
1803                         if(Game_mode & GM_DEMO_RECORD){
1804                                 demo_POST_unique_message(id, who_from, m_source, priority);
1805                         }
1806                         // }
1807
1808                         // send a message packet to a player if destined for everyone or only a specific person
1809                         if ( MULTIPLAYER_MASTER ){
1810                                 send_mission_message_packet( i, who_from, priority, MESSAGE_TIME_SOON, source, -1, -1, -1);
1811                         }                       
1812
1813                         return;         // all done with displaying             
1814                 }
1815         }
1816         nprintf (("messaging", "Couldn't find message id %s to send to player!\n", id ));
1817 }
1818
1819 // send builtin_to_player sends a message (from messages.tbl) to the player.  These messages are
1820 // the generic infomrational type messages.  The have priorities like misison specific messages,
1821 // and use a timing to tell how long we should wait before playing this message
1822 void message_send_builtin_to_player( int type, ship *shipp, int priority, int timing, int group, int delay, int multi_target, int multi_team_filter )
1823 {
1824         int i, persona_index;
1825         int source;     
1826
1827         // if we aren't showing builtin msgs, bail
1828         if (The_mission.flags & MISSION_FLAG_NO_BUILTIN_MSGS) {
1829                 return;
1830         }
1831
1832         // see if there is a persona assigned to this ship.  If not, then try to assign one!!!
1833         if ( shipp ) {
1834                 if ( shipp->persona_index == -1 ){
1835                         shipp->persona_index = message_get_persona( shipp );
1836                 }
1837
1838                 persona_index = shipp->persona_index;
1839                 if ( persona_index == -1 ) {
1840                         nprintf(("messaging", "Couldn't find persona for %s\n", shipp->ship_name ));
1841                 }               
1842
1843                 // be sure that this ship can actually send a message!!! (i.e. not-not-flyable -- get it!)
1844                 Assert( !(Ship_info[shipp->ship_info_index].flags & SIF_NOT_FLYABLE) );         // get allender or alan
1845         } else {
1846                 persona_index = Command_persona;                                // use the terran command persona
1847         }
1848
1849         // try to find a builtin message with the given type for the given persona
1850         // make a loop out of this routne since we may try to play a message in the wrong
1851         // persona if we can't find the right message for the given persona
1852         do {
1853                 for ( i = 0; i < Num_builtin_messages; i++ ) {
1854                         char *name, *who_from;
1855
1856                         name = Builtin_message_types[type];
1857
1858                         // see if the have the type of message
1859                         if ( stricmp(Messages[i].name, name) ){
1860                                 continue;
1861                         }
1862
1863                         // must have the correct persona.  persona_index of -1 means find the first
1864                         // message possibly of the correct type
1865                         if ( (persona_index != -1 ) && (Messages[i].persona_index != persona_index) ){
1866                                 continue;
1867                         }
1868
1869                         // get who this message is from -- kind of a hack since we assume Terran Command in the
1870                         // absense of a ship.  This will be fixed later
1871                         if ( shipp ) {
1872                                 source = HUD_get_team_source( shipp->team );
1873                                 who_from = shipp->ship_name;
1874                         } else {
1875                                 source = HUD_SOURCE_TERRAN_CMD;
1876                                 who_from = TERRAN_COMMAND;
1877                         }
1878
1879                         // maybe change the who from here for special rearm cases (always seems like that is the case :-) )
1880                         if ( !stricmp(who_from, TERRAN_COMMAND) && (type == MESSAGE_REARM_ON_WAY) ){
1881                                 who_from = SUPPORT_NAME;
1882                         }
1883
1884                         // determine what we should actually do with this dang message.  In multiplayer, we must
1885                         // deal with the fact that this message might not get played on my machine if I am a server
1886
1887                         // not multiplayer or this message is for me, then queue it
1888                         if ( !(Game_mode & GM_MULTIPLAYER) || ((multi_target == -1) || (multi_target == MY_NET_PLAYER_NUM)) ){
1889
1890                                 // if this filter matches mine
1891                                 if( (multi_team_filter < 0) || !(Netgame.type_flags & NG_TYPE_TEAM) || ((Net_player != NULL) && (Net_player->p_info.team == multi_team_filter)) ){
1892                                         message_queue_message( i, priority, timing, who_from, source, group, delay, type );
1893
1894                                         // post a builtin message
1895                                         if(Game_mode & GM_DEMO_RECORD){
1896                                                 demo_POST_builtin_message(type, shipp, priority, timing);
1897                                         }
1898                                 }
1899                         }
1900
1901                         // send a message packet to a player if destined for everyone or only a specific person
1902                         if ( MULTIPLAYER_MASTER ) {
1903                                 // only send a message if it is of a particular type
1904                                 if(multi_target == -1){
1905                                         if(multi_message_should_broadcast(type)){                               
1906                                                 send_mission_message_packet( i, who_from, priority, timing, source, type, -1, multi_team_filter );
1907                                         }
1908                                 } else {
1909                                         send_mission_message_packet( i, who_from, priority, timing, source, type, multi_target, multi_team_filter );
1910                                 }
1911                         }
1912
1913                         return;         // all done with displaying
1914                 }
1915
1916                 if ( persona_index >= 0 ) {
1917                         nprintf(("messaging", "Couldn't find builtin message %s for persona %d\n", Builtin_message_types[type], persona_index ));
1918                         nprintf(("messaging", "looking for message for any persona\n"));
1919                         persona_index = -1;
1920                 } else {
1921                         persona_index = -999;           // used here and the next line only -- hard code bad, but I'm lazy
1922                 }
1923         } while ( persona_index != -999 );
1924 }
1925
1926 // message_is_playing()
1927 //
1928 // Return the Message_playing flag.  Message_playing is local to MissionMessage.cpp, but
1929 // this info is needed by code in HUDsquadmsg.cpp
1930 //
1931 int message_is_playing()
1932 {
1933         return Num_messages_playing?1:0;
1934 }
1935
1936 // Functions below pertain only to personas!!!!
1937
1938 // given a character string, try to find the persona index
1939 int message_persona_name_lookup( char *name )
1940 {
1941         int i;
1942
1943         for (i = 0; i < Num_personas; i++ ) {
1944                 if ( !stricmp(Personas[i].name, name) )
1945                         return i;
1946         }
1947
1948         return -1;
1949 }
1950
1951
1952 // Blank out portions of the audio playback for the sound identified by Message_wave
1953 // This works by using the same Distort_pattern[][] that was used to distort the associated text
1954 void message_maybe_distort()
1955 {
1956         int i;
1957         int was_muted;
1958
1959         if ( Num_messages_playing == 0 )
1960                 return;
1961         
1962         for ( i = 0; i < Num_messages_playing; i++ ) {
1963                 if ( !snd_is_playing(Playing_messages[i].wave) )
1964                         return;
1965         }
1966
1967         // distort the number of voices currently playing
1968         for ( i = 0; i < Num_messages_playing; i++ ) {
1969                 Assert(Playing_messages[i].wave >= 0 );
1970
1971                 was_muted = 0;
1972
1973                 // added check to see if EMP effect was active
1974                 // 8/24/98 - DB
1975                 if ( (hud_communications_state(Player_ship) != COMM_OK) || emp_active_local() ) {
1976                         was_muted = Message_wave_muted;
1977                         if ( timestamp_elapsed(Next_mute_time) ) {
1978                                 Next_mute_time = fl2i(Distort_patterns[Distort_num][Distort_next++] * Message_wave_duration);
1979                                 if ( Distort_next >= MAX_DISTORT_LEVELS )
1980                                         Distort_next = 0;
1981
1982                                 Message_wave_muted ^= 1;
1983                         }
1984                 
1985                         if ( Message_wave_muted ) {
1986                                 if ( !was_muted )
1987                                         snd_set_volume(Playing_messages[i].wave, 0.0f);
1988                         } else {
1989                                 if ( was_muted )
1990                                         snd_set_volume(Playing_messages[i].wave, Master_sound_volume);
1991                         }
1992                 }
1993         }
1994 }
1995
1996
1997 // if the player communications systems are heavily damaged, distort incoming messages.
1998 //
1999 // first case: Message_wave_duration == 0 (this occurs when there is no associated voice playback)
2000 //                                      Blank out random runs of characters in the message
2001 //
2002 // second case: Message_wave_duration > 0 (occurs when voice playback accompainies message)
2003 //                                       Blank out portions of the sound based on Distort_num, this this is that same
2004 //                                       data that will be used to blank out portions of the audio playback
2005 //
2006 void message_maybe_distort_text(char *text)
2007 {
2008         int i, j, len, run, curr_offset, voice_duration, next_distort;
2009
2010         if ( (hud_communications_state(Player_ship) == COMM_OK) && !emp_active_local() ) { 
2011                 return;
2012         }
2013
2014         len = strlen(text);
2015         if ( Message_wave_duration == 0 ) {
2016                 next_distort = 5+myrand()%5;
2017                 for ( i = 0; i < len; i++ ) {
2018                         if ( i == next_distort ) {
2019                                 run = 3+myrand()%5;
2020                                 if ( i+run > len )
2021                                         run = len-i;
2022                                 for ( j = 0; j < run; j++) {
2023                                         text[i++] = '-';
2024                                         if ( i >= len )
2025                                                 break;
2026                                 }
2027                                 next_distort = i + (5+myrand()%5);
2028                         }
2029                 }
2030                 return;
2031         }
2032
2033         voice_duration = Message_wave_duration;
2034
2035         // distort text
2036         Distort_num = myrand()%MAX_DISTORT_PATTERNS;
2037         Distort_next = 0;
2038         curr_offset = 0;
2039         while (voice_duration > 0) {
2040                 run = fl2i(Distort_patterns[Distort_num][Distort_next] * len);
2041                 if (Distort_next & 1) {
2042                         for ( i = curr_offset; i < min(len, curr_offset+run); i++ ) {
2043                                 if ( text[i] != ' ' ) 
2044                                         text[i] = '-';
2045                         }
2046                         curr_offset = i;
2047                         if ( i >= len )
2048                                 break;
2049                 } else {
2050                         curr_offset += run;
2051                 }
2052
2053                 voice_duration -= fl2i(Distort_patterns[Distort_num][Distort_next]*Message_wave_duration);
2054                 Distort_next++;
2055                 if ( Distort_next >= MAX_DISTORT_LEVELS )
2056                         Distort_next = 0;
2057         };
2058         
2059         Distort_next = 0;
2060 }
2061
2062 // return 1 if a talking head animation is playing, otherwise return 0
2063 int message_anim_is_playing()
2064 {
2065         int i;
2066
2067         for (i = 0; i < Num_messages_playing; i++ ) {
2068                 if ( (Playing_messages[i].anim != NULL) && anim_playing(Playing_messages[i].anim) )
2069                         return 1;
2070         }
2071
2072         return 0;
2073 }
2074
2075 // Load mission messages (this is called by the level paging code when running with low memory)
2076 void message_pagein_mission_messages()
2077 {
2078         int i;
2079         
2080         mprintf(("Paging in mission messages\n"));
2081
2082         if (Num_messages <= Num_builtin_messages) {
2083                 return;
2084         }
2085
2086         char *sound_filename;
2087
2088         for (i=Num_builtin_messages; i<Num_messages; i++) {
2089                 if (Messages[i].wave_info.index != -1) {
2090                         sound_filename = Message_waves[Messages[i].wave_info.index].name;
2091                         message_load_wave(Messages[i].wave_info.index, sound_filename);
2092                 }
2093         }
2094 }
2095