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