2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
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
10 * $Logfile: /Freespace2/code/Mission/MissionLog.cpp $
15 * File to deal with Mission logs
18 * Revision 1.3 2002/06/09 04:41:22 relnev
19 * added copyright header
21 * Revision 1.2 2002/05/07 03:16:46 theoddone33
22 * The Great Newline Fix
24 * Revision 1.1.1.1 2002/05/03 03:28:09 root
28 * 10 11/01/99 2:13p Jefff
29 * some changes in how objective events are displayed in german
31 * 9 10/13/99 3:22p Jefff
32 * fixed unnumbered XSTRs
34 * 8 9/08/99 4:05p Dave
35 * Fixed string ID on self-destruxt message.
37 * 7 9/06/99 8:18p Andsager
38 * Make destroyed weapon subsystems display as turrets in event log.
40 * 6 8/26/99 8:51p Dave
41 * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes.
43 * 5 8/22/99 5:53p Dave
44 * Scoring fixes. Added self destruct key. Put callsigns in the logfile
45 * instead of ship designations for multiplayer players.
47 * 4 2/26/99 6:01p Andsager
48 * Add sexp has-been-tagged-delay and cap-subsys-cargo-known-delay
50 * 3 10/13/98 9:28a Dave
51 * Started neatening up freespace.h. Many variables renamed and
52 * reorganized. Added AlphaColors.[h,cpp]
54 * 2 10/07/98 10:53a Dave
57 * 1 10/07/98 10:49a Dave
59 * 57 6/09/98 10:31a Hoffoss
60 * Created index numbers for all xstr() references. Any new xstr() stuff
61 * added from here on out should be added to the end if the list. The
62 * current list count can be found in FreeSpace.cpp (search for
66 * AL: fix bogus null pointer test in message_log_add_seg()
68 * 55 4/28/98 4:45p Allender
69 * fix mission log problems on clients
71 * 54 4/23/98 10:06a Allender
72 * don't use the word "player" in event log for rearm event. Send
73 * shipname instead (players only)
75 * 53 3/28/98 1:56p Allender
76 * clarify the destroyed mission log entry.
78 * 52 3/13/98 2:49p Allender
79 * put fix into debug code so that it doesn't trip when no ships have
80 * departed (i.e. wing was waxes before they actulaly left)
82 * 51 3/12/98 5:52p Hoffoss
83 * Moved stuff over so icons don't overlap frame.
85 * 50 3/09/98 6:22p Hoffoss
86 * Fixed text overrun problems in event log display.
88 * 49 2/25/98 4:44p Allender
89 * fix up mission log and wing/ship departed problems (hopefully). There
90 * was problem where wing was marked departed although all ships were
91 * destroyed (I think).
93 * 48 2/25/98 11:07a Allender
94 * added debug code to help trap problems with wing departure and ship
97 * 47 2/23/98 8:55a John
98 * More string externalization
100 * 46 2/22/98 4:30p John
101 * More string externalization classification
103 * 45 2/10/98 12:47p Allender
104 * clear log memeory before mission start to prevent entries from previous
105 * mission from being used incorrectly
107 * 44 2/05/98 10:33a Hoffoss
108 * Changed refernce go "goal" to "objective" instead.
110 * 43 1/30/98 11:16a Comet
111 * DOH! fix bug checking for exited ships
113 * 42 1/08/98 1:59p Allender
114 * fixed problem with some subsystems not getting recognized on
115 * is-subsystem-destroyed sexpressions
117 * 41 1/07/98 4:41p Allender
118 * minor modification to special messages. Fixed cargo_revealed problem
119 * for multiplayer and problem with is-cargo-known sexpression
121 * 40 11/21/97 10:31a Allender
122 * minor cosmetic changes
124 * 39 11/20/97 3:52p Hoffoss
125 * Fixed timestamp color in mission log scrollback.
127 * 38 11/19/97 2:30p Allender
128 * hide waypoints done log entries
130 * 37 11/13/97 4:25p Allender
131 * hide certain mission log entries
133 * 36 11/13/97 4:05p Hoffoss
134 * Added hiding code for mission log entries.
136 * 35 11/06/97 5:42p Hoffoss
137 * Added support for fixed size timstamp rendering.
139 * 34 11/03/97 10:12p Hoffoss
140 * Finished up work on the hud message/mission log scrollback screen.
142 * 33 10/09/97 5:21p Allender
143 * try to check for bogus secondary names on ship destroy.
145 * 32 10/07/97 3:09p Allender
148 * 31 9/26/97 3:47p Allender
149 * add code to keep track of ships which have left mission (departed or
150 * destroyed). Log who killed who in mission log
152 * 30 9/10/97 3:30p Allender
153 * ignore waypoints_done entry when determining teams for log actions
155 * 29 9/10/97 8:53a Allender
156 * made the mission log prettier. Added team info to log entries -- meant
157 * reordering some code
159 * 28 8/31/97 6:38p Lawrance
160 * pass in frametime to do_frame loop
162 * 27 8/20/97 3:53p Allender
163 * minor fix to message log display
165 * 26 8/07/97 11:37a Allender
166 * made mission log cull non-essential entries when log starts getting
167 * full. More drastic action is taken as the log gets more full.
169 * 25 7/24/97 4:13p Mike
170 * Comment out check in mission_log_add_entry that prevented logging
171 * events when player is dead.
173 * 24 7/14/97 11:46a Lawrance
174 * allow log entries when in the navmap
176 * 23 7/10/97 1:40p Allender
177 * make dock/undock log entries work with either name (docker/dockee)
180 * 22 7/07/97 9:23a Allender
181 * added code for valid/invalid goals. Invalid goals are eval'ed,
182 * counted, or otherwise used
184 * 21 7/01/97 2:52p Allender
185 * added packets for mission log stuff and for pregame chat stuff
187 * 20 7/01/97 1:23p Allender
195 #include "gamesequence.h"
196 #include "freespace.h"
197 #include "missionlog.h"
201 #include "missiongoals.h"
203 #include "multimsgs.h"
204 #include "multiutil.h"
205 #include "alphacolors.h"
206 #include "localize.h"
208 #define MAX_LOG_ENTRIES 700
209 #define MAX_LOG_LINES 1000
211 // used for high water mark for culling out log entries
212 #define LOG_CULL_MARK ((int)(MAX_LOG_ENTRIES * 0.95f))
213 #define LOG_CULL_DOORDIE_MARK ((int)(MAX_LOG_ENTRIES * 0.99f))
214 #define LOG_LAST_DITCH_CULL_NUM ((int)(MAX_LOG_ENTRIES * 0.20f))
215 #define LOG_HALFWAY_REPORT_NUM ((int)(MAX_LOG_ENTRIES * 0.50f))
217 #define ML_FLAG_PRIMARY 1
218 #define ML_FLAG_SECONDARY 2
220 #define EMPTY_LOG_NAME ""
222 // defines for X position offsets of different items for mission log
227 #define LOG_COLOR_NORMAL 0
228 #define LOG_COLOR_BRIGHT 1
229 #define LOG_COLOR_FRIENDLY 2
230 #define LOG_COLOR_HOSTILE 3
231 #define LOG_COLOR_OTHER 4
233 // defines for log flags
234 #define LOG_FLAG_GOAL_FAILED (1<<0)
235 #define LOG_FLAG_GOAL_TRUE (1<<1)
237 typedef struct log_text_seg {
238 log_text_seg *next; // linked list
239 char *text; // the text
240 int color; // color text should be displayed in
241 int x; // x offset to display text at
242 int flags; // used to possibly print special characters when displaying the log
246 static int X, P_width;
248 // Log_lines is used for scrollback display purposes.
249 static log_text_seg *Log_lines[MAX_LOG_LINES];
250 static int Log_line_timestamps[MAX_LOG_LINES];
252 log_entry log_entries[MAX_LOG_ENTRIES]; // static array because John says....
255 void mission_log_init()
259 // zero out all the memory so we don't get bogus information when playing across missions!
260 memset( log_entries, 0, sizeof(log_entries) );
263 // returns the number of entries in the mission log
264 int mission_log_query_scrollback_size()
269 // function to clean up the mission log removing obsolete entries. Entries might get marked obsolete
270 // in several ways -- having to recycle entries, a ship's subsystem destroyed entries when a ship is
271 // fully destroyed, etc.
272 void mission_log_cull_obsolete_entries()
276 nprintf(("missionlog", "culling obsolete entries. starting last entry %d.\n", last_entry));
277 // find the first obsolete entry
278 for (i = 0; i < last_entry; i++ )
279 if ( log_entries[i].flags & MLF_OBSOLETE )
282 // nothing to do if next if statement is true
283 if ( i == last_entry )
286 // compact the log array, removing the obsolete entries.
287 index = i; // index is the first obsolete entry
289 // 'index' should always point to the next element in the list
290 // which is getting compacted. 'i' points to the next array
291 // element to be replaced.
293 // get to the next non-obsolete entry. The obsolete entry must not be essential either!
294 while ( (log_entries[index].flags & MLF_OBSOLETE) && !(log_entries[index].flags & MLF_ESSENTIAL) ) {
299 log_entries[i++] = log_entries[index++];
300 } while ( i < last_entry );
303 nprintf(("missionlog", "Ending entry: %d.\n", last_entry));
307 // function to mark entries as obsolete. Passed is the type of entry that is getting added
308 // to the log. Some entries might get marked obsolete as a result of this type
309 void mission_log_obsolete_entries(int type, const char *pname)
312 log_entry *entry = NULL;
314 // before adding this entry, check to see if the entry type is a ship destroyed entry.
315 // If so, we can remove any subsystem destroyed entries from the log for this ship.
316 if ( type == LOG_SHIP_DESTROYED ) {
317 for (i = 0; i < last_entry; i++) {
318 entry = &log_entries[i];
320 // check to see if the type is a subsystem destroyed entry, and that it belongs to the
321 // ship passed into this routine. If it matches, mark as obsolete. We'll clean up
322 // the log when it starts to get full
323 if ( !SDL_strcasecmp( pname, entry->pname ) ) {
324 if ( (entry->type == LOG_SHIP_SUBSYS_DESTROYED) || (entry->type == LOG_SHIP_DISARMED) || (entry->type == LOG_SHIP_DISABLED) )
325 entry->flags |= MLF_OBSOLETE;
330 // check to see if we are getting to about 80% of our log capacity. If so, cull the log.
331 if ( last_entry > LOG_CULL_MARK ) {
332 mission_log_cull_obsolete_entries();
334 // if we culled the entries, and we are still low on space, we need to take more drastic measures.
335 // these include removing all non-essential entries from the log. These entries are entries
336 // which has not been asked for by mission_log_get_time
337 if ( last_entry > LOG_CULL_MARK ) {
338 nprintf(("missionlog", "marking the first %d non-essential log entries as obsolete\n", LOG_LAST_DITCH_CULL_NUM));
339 for (i = 0; i < LOG_LAST_DITCH_CULL_NUM; i++ ) {
340 entry = &log_entries[i];
341 if ( !(entry->flags & MLF_ESSENTIAL) ){
342 entry->flags |= MLF_OBSOLETE;
346 // cull the obsolete entries again
347 mission_log_cull_obsolete_entries();
349 // if we get to this point, and there are no entries left -- we are in big trouble. We will simply
350 // mark the first 20% of the log as obsolete and compress. Don't do this unless we are *really*
352 if ( last_entry > LOG_CULL_DOORDIE_MARK ) {
353 nprintf(("missionlog", "removing the first %d entries in the mission log!!!!\n", LOG_LAST_DITCH_CULL_NUM));
354 for (i = 0; i < LOG_LAST_DITCH_CULL_NUM; i++ ){
355 entry->flags |= MLF_OBSOLETE;
358 mission_log_cull_obsolete_entries();
364 // assigns flag values to an entry based on the team value passed in
365 void mission_log_flag_team( log_entry *entry, int which_entry, int team )
367 if ( which_entry == ML_FLAG_PRIMARY ) {
368 if ( team == TEAM_FRIENDLY )
369 entry->flags |= MLF_PRIMARY_FRIENDLY;
371 entry->flags |= MLF_PRIMARY_HOSTILE;
373 } else if ( which_entry == ML_FLAG_SECONDARY ) {
374 if ( team == TEAM_FRIENDLY )
375 entry->flags |= MLF_SECONDARY_FRIENDLY;
377 entry->flags |= MLF_SECONDARY_HOSTILE;
380 Int3(); // get allender -- impossible type
383 // following function adds an entry into the mission log.
384 // pass a type and a string which indicates the object
385 // that this event is for. Don't add entries with this function for multiplayer
386 void mission_log_add_entry(int type, const char *pname, const char *sname, int info_index)
391 // multiplayer clients don't use this function to add log entries -- they will get
392 // all their info from the host
393 if ( (Game_mode & GM_MULTIPLAYER) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER) ){
397 last_entry_save = last_entry;
399 // mark any entries as obsolete. Part of the pruning is done based on the type (and name) passed
401 mission_log_obsolete_entries(type, pname);
403 entry = &log_entries[last_entry];
405 if ( last_entry == MAX_LOG_ENTRIES ){
411 SDL_assert (strlen(pname) < NAME_LENGTH);
412 SDL_strlcpy(entry->pname, pname, sizeof(entry->pname));
414 SDL_strlcpy( entry->pname, EMPTY_LOG_NAME, sizeof(entry->pname) );
417 SDL_assert (strlen(sname) < NAME_LENGTH);
418 SDL_strlcpy(entry->sname, sname, sizeof(entry->sname));
420 SDL_strlcpy( entry->sname, EMPTY_LOG_NAME, sizeof(entry->sname) );
422 entry->index = info_index;
425 // determine the contents of the flags member based on the type of entry we added. We need to store things
426 // like team for the primary and (possibly) secondary object for this entry.
430 case LOG_SHIP_DESTROYED:
431 case LOG_SHIP_ARRIVE:
432 case LOG_SHIP_DEPART:
434 case LOG_SHIP_SUBSYS_DESTROYED:
435 case LOG_SHIP_UNDOCK:
436 case LOG_SHIP_DISABLED:
437 case LOG_SHIP_DISARMED:
438 case LOG_SELF_DESTRUCT:
439 // multiplayer. callsign is passed in for ship destroyed and self destruct
440 if((Game_mode & GM_MULTIPLAYER) && (multi_find_player_by_callsign(pname) >= 0)){
441 int np_index = multi_find_player_by_callsign(pname);
442 index = multi_get_player_ship( np_index );
444 index = ship_name_lookup( pname );
447 SDL_assert ( index != -1 );
449 mission_log_flag_team( entry, ML_FLAG_PRIMARY, TEAM_FRIENDLY );
451 mission_log_flag_team( entry, ML_FLAG_PRIMARY, Ships[index].team );
454 // some of the entries have a secondary component. Figure out what is up with them.
455 if ( (type == LOG_SHIP_DOCK) || (type == LOG_SHIP_UNDOCK)) {
457 index = ship_name_lookup( sname );
458 SDL_assert( index != -1 );
459 mission_log_flag_team( entry, ML_FLAG_SECONDARY, Ships[index].team );
461 } else if ( type == LOG_SHIP_DESTROYED ) {
465 // multiplayer, player name will possibly be sent in
466 if((Game_mode & GM_MULTIPLAYER) && (multi_find_player_by_callsign(sname) >= 0)){
467 // get the player's ship
468 int np_index = multi_find_player_by_callsign(sname);
469 int np_ship = multi_get_player_ship(np_index);
472 team = Ships[Objects[Net_players[np_index].player->objnum].instance].team;
476 team = TEAM_FRIENDLY;
479 index = ship_name_lookup( sname );
480 // no ship, then it probably exited -- check the exited
482 index = ship_find_exited_ship_by_name( sname );
484 // Int3(); // get allender. name of object who killed ship appears to be bogus!!!
487 team = Ships_exited[index].team;
489 team = Ships[index].team;
493 mission_log_flag_team( entry, ML_FLAG_SECONDARY, team );
495 nprintf(("missionlog", "No secondary name for ship destroyed log entry!\n"));
497 } else if ( (type == LOG_SHIP_SUBSYS_DESTROYED) && (Ship_info[Ships[index].ship_info_index].flags & SIF_SMALL_SHIP) ) {
498 // make subsystem destroyed entries for small ships hidden
499 entry->flags |= MLF_HIDDEN;
500 } else if ( (type == LOG_SHIP_ARRIVE) && (Ships[index].wingnum != -1 ) ) {
501 // arrival of ships in wings don't display
502 entry->flags |= MLF_HIDDEN;
506 case LOG_WING_DESTROYED:
507 case LOG_WING_DEPART:
508 case LOG_WING_ARRIVE:
509 index = wing_name_lookup( pname, 1 );
510 SDL_assert( index != -1 );
511 SDL_assert( info_index != -1 ); // this is the team value
513 // get the team value for this wing. Departed or destroyed wings will pass the team
514 // value in info_index parameter. For arriving wings, get the team value from the
515 // first ship in the list
516 if ( type == LOG_WING_ARRIVE ) {
517 si = Wings[index].ship_index[0];
518 SDL_assert( si != -1 );
519 mission_log_flag_team( entry, ML_FLAG_PRIMARY, Ships[si].team );
521 mission_log_flag_team( entry, ML_FLAG_PRIMARY, info_index );
525 // MWA 2/25/98. debug code to try to find any ships in this wing that have departed.
526 // scan through all log entries and find at least one ship_depart entry for a ship
527 // that was in this wing.
528 if ( type == LOG_WING_DEPART ) {
531 // if all were destroyed, then don't do this debug code.
532 if ( Wings[index].total_destroyed == Wings[index].total_arrived_count ){
536 for ( i = 0; i < last_entry; i++ ) {
537 if ( log_entries[i].type != LOG_SHIP_DEPART ){
540 if( log_entries[i].index == index ){
544 if ( i == last_entry ){
545 Int3(); // get Allender -- cannot find any departed ships from wing that supposedly departed.
552 // don't display waypoint done entries
553 case LOG_WAYPOINTS_DONE:
554 entry->flags |= MLF_HIDDEN;
561 entry->timestamp = Missiontime;
563 // if in multiplayer and I am the master, send this log entry to everyone
564 if ( (Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_AM_MASTER) ){
565 send_mission_log_packet( last_entry );
571 if ( !(last_entry % 10) ) {
572 if ( (last_entry > LOG_HALFWAY_REPORT_NUM) && (last_entry > last_entry_save) ){
573 nprintf(("missionlog", "new highwater point reached for mission log (%d entries).\n", last_entry));
580 // function, used in multiplayer only, which adds an entry sent by the host of the game, into
581 // the mission log. The index of the log entry is passed as one of the parameters in addition to
582 // the normal parameters used for adding an entry to the log
583 void mission_log_add_entry_multi( int type, const char *pname, const char *sname, int index, fix timestamp, int flags )
587 // we'd better be in multiplayer and not the master of the game
588 SDL_assert ( Game_mode & GM_MULTIPLAYER );
589 SDL_assert ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) );
591 // mark any entries as obsolete. Part of the pruning is done based on the type (and name) passed
593 mission_log_obsolete_entries(type, pname);
595 entry = &log_entries[last_entry];
597 if ( last_entry == MAX_LOG_ENTRIES ){
605 SDL_assert (strlen(pname) < NAME_LENGTH);
606 SDL_strlcpy(entry->pname, pname, sizeof(entry->pname));
609 SDL_assert (strlen(sname) < NAME_LENGTH);
610 SDL_strlcpy(entry->sname, sname, sizeof(entry->sname));
612 entry->index = index;
614 entry->flags = flags;
615 entry->timestamp = timestamp;
618 // function to determine is the given event has taken place count number of times.
620 int mission_log_get_time_indexed( int type, const char *pname, const char *sname, int count, fix *time)
625 entry = &log_entries[0];
626 for (i = 0; i < last_entry; i++) {
628 if ( entry->type == type ) {
629 // if we are looking for a dock/undock entry, then we don't care about the order in which the names
630 // were passed into this function. Count the entry as found if either name matches both in the other
632 if ( (type == LOG_SHIP_DOCK) || (type == LOG_SHIP_UNDOCK) ) {
633 SDL_assert ( sname );
634 if ( (!SDL_strcasecmp(entry->pname, pname) && !SDL_strcasecmp(entry->sname, sname)) || (!SDL_strcasecmp(entry->pname, sname) && !SDL_strcasecmp(entry->sname, pname)) )
637 // for non dock/undock goals, then the names are important!
638 if ( SDL_strcasecmp(entry->pname, pname) )
640 if ( !sname || !SDL_strcasecmp(sname, entry->sname) )
647 entry->flags |= MLF_ESSENTIAL; // since the goal code asked for this entry, mark it as essential
649 *time = entry->timestamp;
662 // this function determines if the given type of event on the specified
663 // object has taken place yet. If not, it returns 0. If it has, the
664 // timestamp that the event happened is returned in the time parameter
665 int mission_log_get_time( int type, const char *pname, const char *sname, fix *time )
667 return mission_log_get_time_indexed( type, pname, sname, 1, time );
670 void message_log_add_seg(int n, int x, int color, const char *text, int flags = 0)
672 log_text_seg *seg, **parent;
674 if ((n < 0) || (n >= MAX_LOG_LINES))
677 parent = &Log_lines[n];
679 parent = &((*parent)->next);
681 seg = (log_text_seg *) malloc(sizeof(log_text_seg));
683 seg->text = strdup(text);
691 void message_log_add_segs(const char *text, int color, int flags = 0)
693 char *log_text = NULL, *log_text_ptr = NULL;
698 mprintf(("Why are you passing a NULL pointer to message_log_add_segs?\n"));
702 log_text = strdup(text);
703 log_text_ptr = log_text;
711 while (is_white_space(*log_text_ptr))
715 if ( !log_text_ptr[0] ) {
722 ptr = split_str_once(log_text_ptr, P_width - X);
724 if (ptr != log_text_ptr)
725 message_log_add_seg(Num_log_lines, X, color, log_text_ptr, flags);
728 gr_get_string_size(&w, NULL, log_text_ptr);
741 void message_log_remove_segs(int n)
743 log_text_seg *ptr, *ptr2;
745 if ((n < 0) || (n >= MAX_LOG_LINES))
758 // pw = total pixel width
759 void message_log_init_scrollback(int pw)
763 int i, c, kill, type;
766 mission_log_cull_obsolete_entries(); // compact array so we don't have gaps
768 // initialize the log lines data
770 for (i=0; i<MAX_LOG_LINES; i++) {
772 Log_line_timestamps[i] = 0;
775 for (i=0; i<last_entry; i++) {
776 entry = &log_entries[i];
778 if (entry->flags & MLF_HIDDEN)
781 // track time of event (normal timestamp milliseconds format)
782 Log_line_timestamps[Num_log_lines] = (int) ( f2fl(entry->timestamp) * 1000.0f );
784 // Generate subject ship text for entry
785 if ( entry->flags & MLF_PRIMARY_FRIENDLY ){
786 c = LOG_COLOR_FRIENDLY;
787 } else if ( entry->flags & MLF_PRIMARY_HOSTILE ){
788 c = LOG_COLOR_HOSTILE;
789 } else if ( (entry->type == LOG_GOAL_SATISFIED) || (entry->type == LOG_GOAL_FAILED) ){
790 c = LOG_COLOR_BRIGHT;
795 if ( (Lcl_gr) && ((entry->type == LOG_GOAL_FAILED) || (entry->type == LOG_GOAL_SATISFIED)) ) {
796 // in german goal events, just say "objective" instead of objective name
797 // this is cuz we cant translate objective names
798 message_log_add_seg(Num_log_lines, OBJECT_X, c, "Einsatzziel");
800 message_log_add_seg(Num_log_lines, OBJECT_X, c, entry->pname);
803 // now on to the actual message itself
806 if ( entry->flags & MLF_SECONDARY_FRIENDLY ){
807 c = LOG_COLOR_FRIENDLY;
808 } else if ( entry->flags & MLF_SECONDARY_HOSTILE ){
809 c = LOG_COLOR_HOSTILE;
811 c = LOG_COLOR_NORMAL;
814 switch (entry->type) {
815 case LOG_SHIP_DESTROYED:
816 message_log_add_segs(XSTR( "Destroyed", 404), LOG_COLOR_NORMAL);
817 if (strlen(entry->sname)) {
818 message_log_add_segs(XSTR( " Kill: ", 405), LOG_COLOR_NORMAL);
819 message_log_add_segs(entry->sname, c);
820 if (entry->index >= 0) {
821 SDL_snprintf(text, sizeof(text), NOX(" (%d%%)"), entry->index);
822 message_log_add_segs(text, LOG_COLOR_BRIGHT);
827 case LOG_SELF_DESTRUCT:
828 message_log_add_segs(XSTR( "Self Destructed", 1476), LOG_COLOR_NORMAL);
831 case LOG_WING_DESTROYED:
832 message_log_add_segs(XSTR( "Destroyed", 404), LOG_COLOR_NORMAL);
835 case LOG_SHIP_ARRIVE:
836 message_log_add_segs(XSTR( "Arrived", 406), LOG_COLOR_NORMAL);
839 case LOG_WING_ARRIVE:
840 if (entry->index > 1){
841 SDL_snprintf(text, sizeof(text), XSTR( "Arrived (wave %d)", 407), entry->index);
843 SDL_strlcpy(text, XSTR( "Arrived", 406), sizeof(text));
845 message_log_add_segs(text, LOG_COLOR_NORMAL);
848 case LOG_SHIP_DEPART:
849 message_log_add_segs(XSTR( "Departed", 408), LOG_COLOR_NORMAL);
852 case LOG_WING_DEPART:
853 message_log_add_segs(XSTR( "Departed", 408), LOG_COLOR_NORMAL);
857 message_log_add_segs(XSTR( "docked with ", 409), LOG_COLOR_NORMAL);
858 message_log_add_segs(entry->sname, c);
861 case LOG_SHIP_SUBSYS_DESTROYED: {
862 int si_index, model_index;
864 si_index = (int)((entry->index >> 16) & 0xffff);
865 model_index = (int)(entry->index & 0xffff);
867 message_log_add_segs(XSTR( "Subsystem ", 410), LOG_COLOR_NORMAL);
868 //message_log_add_segs(entry->sname, LOG_COLOR_BRIGHT);
869 const char *subsys_name = Ship_info[si_index].subsystems[model_index].name;
870 if (Ship_info[si_index].subsystems[model_index].type == SUBSYSTEM_TURRET) {
871 subsys_name = XSTR("Turret", 1487);
873 message_log_add_segs(subsys_name, LOG_COLOR_BRIGHT);
874 message_log_add_segs(XSTR( " destroyed", 411), LOG_COLOR_NORMAL);
878 case LOG_SHIP_UNDOCK:
879 message_log_add_segs(XSTR( "Undocked with ", 412), LOG_COLOR_NORMAL);
880 message_log_add_segs(entry->sname, c);
883 case LOG_SHIP_DISABLED:
884 message_log_add_segs(XSTR( "Disabled", 413), LOG_COLOR_NORMAL);
887 case LOG_SHIP_DISARMED:
888 message_log_add_segs(XSTR( "Disarmed", 414), LOG_COLOR_NORMAL);
891 case LOG_PLAYER_REARM:
892 message_log_add_segs(XSTR( " called for rearm", 415), LOG_COLOR_NORMAL);
895 case LOG_PLAYER_REARM_ABORT:
896 message_log_add_segs(XSTR( " aborted rearm", 416), LOG_COLOR_NORMAL);
899 case LOG_PLAYER_REINFORCEMENT:
900 message_log_add_segs(XSTR( "Called in as reinforcement", 417), LOG_COLOR_NORMAL);
903 case LOG_CARGO_REVEALED:
904 SDL_assert( entry->index != -1 );
905 message_log_add_segs(XSTR( "Cargo revealed: ", 418), LOG_COLOR_NORMAL);
906 message_log_add_segs( Cargo_names[entry->index], LOG_COLOR_BRIGHT );
909 case LOG_CAP_SUBSYS_CARGO_REVEALED:
910 SDL_assert( entry->index != -1 );
911 message_log_add_segs(entry->sname, LOG_COLOR_NORMAL);
912 message_log_add_segs(XSTR( " subsystem cargo revealed: ", 1488), LOG_COLOR_NORMAL);
913 message_log_add_segs( Cargo_names[entry->index], LOG_COLOR_BRIGHT );
917 case LOG_GOAL_SATISFIED:
918 case LOG_GOAL_FAILED: {
919 type = Mission_goals[entry->index].type & GOAL_TYPE_MASK;
921 // don't display failed bonus goals
922 if ( (type == BONUS_GOAL) && (entry->type == LOG_GOAL_FAILED) ) {
924 break; // don't display this line
927 SDL_snprintf( text, sizeof(text), XSTR( "%s objective ", 419), Goal_type_text(type) );
928 if ( entry->type == LOG_GOAL_SATISFIED )
929 SDL_strlcat(text, XSTR( "satisfied.", 420), sizeof(text));
931 SDL_strlcat(text, XSTR( "failed.", 421), sizeof(text));
933 message_log_add_segs(text, LOG_COLOR_BRIGHT, (entry->type == LOG_GOAL_SATISFIED?LOG_FLAG_GOAL_TRUE:LOG_FLAG_GOAL_FAILED) );
935 } // matches case statement!
939 message_log_remove_segs(Num_log_lines);
942 if (Num_log_lines < MAX_LOG_LINES)
948 void message_log_shutdown_scrollback()
952 for (i=0; i<MAX_LOG_LINES; i++)
953 message_log_remove_segs(i);
958 // message_log_scrollback displays the contents of the mesasge log currently much like the HUD
959 // message scrollback system. I'm sure this system will be overhauled.
960 void mission_log_scrollback(int line, int list_x, int list_y, int list_w, int list_h)
964 int font_h = gr_get_font_height();
968 while (y + font_h <= list_h) {
969 if (line >= Num_log_lines)
972 if (Log_line_timestamps[line]) {
973 gr_set_color_fast(&Color_text_normal);
974 gr_print_timestamp(list_x + TIME_X, list_y + y, Log_line_timestamps[line]);
977 seg = Log_lines[line];
979 switch (seg->color) {
980 case LOG_COLOR_BRIGHT:
981 gr_set_color_fast(&Color_bright);
984 case LOG_COLOR_FRIENDLY:
985 gr_set_color_fast(&Color_bright_green);
988 case LOG_COLOR_HOSTILE:
989 gr_set_color_fast(&Color_bright_red);
992 case LOG_COLOR_OTHER:
993 gr_set_color_fast(&Color_normal);
997 gr_set_color_fast(&Color_text_normal);
1001 SDL_strlcpy(buf, seg->text, sizeof(buf));
1002 if (seg->x < ACTION_X)
1003 gr_force_fit_string(buf, 256, ACTION_X - OBJECT_X - 8);
1005 gr_force_fit_string(buf, 256, list_w - seg->x);
1007 gr_string(list_x + seg->x, list_y + y, buf);
1009 // possibly "print" some symbols for interesting log entries
1010 if ( (seg->flags & LOG_FLAG_GOAL_TRUE) || (seg->flags & LOG_FLAG_GOAL_FAILED) ) {
1013 if ( seg->flags & LOG_FLAG_GOAL_FAILED )
1014 gr_set_color_fast(&Color_bright_red);
1016 gr_set_color_fast(&Color_bright_green);
1018 i = list_y + y + font_h / 2 - 1;
1019 gr_circle(list_x + TIME_X - 6, i, 5);
1021 gr_set_color_fast(&Color_bright);
1022 gr_line(list_x + TIME_X - 10, i, list_x + TIME_X - 8, i);
1023 gr_line(list_x + TIME_X - 6, i - 4, list_x + TIME_X - 6, i - 2);
1024 gr_line(list_x + TIME_X - 4, i, list_x + TIME_X - 2, i);
1025 gr_line(list_x + TIME_X - 6, i + 2, list_x + TIME_X - 6, i + 4);