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