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