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