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