]> icculus.org git repositories - taylor/freespace2.git/blob - src/radar/radar.cpp
warning cleanup
[taylor/freespace2.git] / src / radar / radar.cpp
1 /*
2  * $Logfile: /Freespace2/code/Radar/Radar.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * C module containg functions to display and manage the radar
8  *
9  * $Log$
10  * Revision 1.3  2002/06/02 04:26:34  relnev
11  * warning cleanup
12  *
13  * Revision 1.2  2002/05/07 03:16:51  theoddone33
14  * The Great Newline Fix
15  *
16  * Revision 1.1.1.1  2002/05/03 03:28:10  root
17  * Initial import.
18  *
19  * 
20  * 14    8/23/99 8:38p Andsager
21  * Added see_all debug console command for turning radar range infinite in
22  * nebula (but not targeting).
23  * 
24  * 13    6/10/99 3:43p Dave
25  * Do a better job of syncing text colors to HUD gauges.
26  * 
27  * 12    6/07/99 4:21p Andsager
28  * Add HUD color for tagged object.  Apply to target and radar.
29  * 
30  * 11    6/02/99 12:52p Andsager
31  * Added team-wide ship visibility.  Implemented for player.
32  * 
33  * 10    1/25/99 5:03a Dave
34  * First run of stealth, AWACS and TAG missile support. New mission type
35  * :)
36  * 
37  * 9     1/12/99 5:45p Dave
38  * Moved weapon pipeline in multiplayer to almost exclusively client side.
39  * Very good results. Bandwidth goes down, playability goes up for crappy
40  * connections. Fixed object update problem for ship subsystems.
41  * 
42  * 8     12/30/98 9:34a Jasen
43  * updated coords for hi res
44  * 
45  * 7     12/29/98 7:29p Dave
46  * Added some missing hi-res hud coord globalizations.
47  * 
48  * 6     12/29/98 2:30p Jasen
49  * added some new coords for 1024 HUD stuff
50  * 
51  * 5     12/28/98 3:17p Dave
52  * Support for multiple hud bitmap filenames for hi-res mode.
53  * 
54  * 4     11/05/98 4:18p Dave
55  * First run nebula support. Beefed up localization a bit. Removed all
56  * conditional compiles for foreign versions. Modified mission file
57  * format.
58  * 
59  * 3     10/13/98 9:29a Dave
60  * Started neatening up freespace.h. Many variables renamed and
61  * reorganized. Added AlphaColors.[h,cpp]
62  * 
63  * 2     10/07/98 10:53a Dave
64  * Initial checkin.
65  * 
66  * 1     10/07/98 10:51a Dave
67  * 
68  * 90    8/28/98 3:29p Dave
69  * EMP effect done. AI effects may need some tweaking as required.
70  * 
71  * 89    8/25/98 1:48p Dave
72  * First rev of EMP effect. Player side stuff basically done. Next comes
73  * AI code.
74  * 
75  * 88    6/12/98 4:52p Hoffoss
76  * Added support for special characters in in forgeign languages.
77  * 
78  * 87    6/09/98 10:31a Hoffoss
79  * Created index numbers for all xstr() references.  Any new xstr() stuff
80  * added from here on out should be added to the end if the list.  The
81  * current list count can be found in FreeSpace.cpp (search for
82  * XSTR_SIZE).
83  * 
84  * 86    5/19/98 10:26a John
85  * Fixed bug with radar blips not drawing in hardware.
86  * 
87  * 85    5/19/98 9:12a John
88  * Made radar blips render as font characters 132 and 133.   Needs a new
89  * font01.vf in the data tree.
90  * 
91  * 84    5/08/98 11:22a Allender
92  * fix ingame join trouble.  Small messaging fix.  Enable collisions for
93  * friendlies again
94  * 
95  * 83    5/01/98 12:24p Jim
96  * don't process radar_plot_obj on the standalone server
97  * 
98  * 82    4/07/98 4:05p Lawrance
99  * Only show hostile bombs on radar.
100  * 
101  * 81    3/26/98 5:26p John
102  * added new paging code. nonfunctional.
103  * 
104  * 80    3/15/98 3:11p Lawrance
105  * Always draw target radar blip bright.
106  * 
107  * 79    3/11/98 5:33p Lawrance
108  * Support rendering and targeting of jump nodes
109  * 
110  * 78    3/03/98 8:12p Lawrance
111  * Draw cargo as gray dots
112  * 
113  * 77    2/22/98 4:30p John
114  * More string externalization classification
115  * 
116  * 76    2/22/98 2:48p John
117  * More String Externalization Classification
118  * 
119  * 75    2/21/98 3:26p Lawrance
120  * Improve how blips get drawn for ships immune to sensors.
121  * 
122  * 74    2/16/98 11:58p Lawrance
123  * Add support for SF_HIDDEN_FROM_SENSORS flag.
124  * 
125  * 73    2/13/98 4:08p Lawrance
126  * Use more accurate distance formula when plotting radar dots... fixes
127  * "dead zone" black spot.
128  * 
129  * 72    2/12/98 4:58p Lawrance
130  * Change to new flashing method.
131  * 
132  * 71    2/11/98 12:04a Lawrance
133  * Only show bombs on radar, change code to use much less data.
134  * 
135  * 70    2/10/98 11:46a Lawrance
136  * Ensure TEAM_TRAITOR views other TEAM_TRAITOR ships as hostile.
137  * 
138  *
139  * $NoKeywords: $
140  *
141  */
142
143
144 #include "pstypes.h"
145 #include "font.h"
146 #include "floating.h"
147 #include "2d.h"
148 #include "3d.h"
149 #include "vecmat.h"
150 #include "palman.h"
151 #include "bmpman.h"
152 #include "object.h"
153 #include "ship.h"
154 #include "player.h"
155 #include "weapon.h"
156 #include "timer.h"
157 #include "ai.h"
158 #include "key.h"
159 #include "hud.h"
160 #include "hudtarget.h"
161 #include "hudconfig.h"
162 #include "subsysdamage.h"
163 #include "gamesnd.h"
164 #include "radar.h"
165 #include "linklist.h"
166 #include "multi.h"
167 #include "emp.h"
168 #include "freespace.h"
169 #include "localize.h"
170 #include "awacs.h"
171
172 int Radar_radius[GR_NUM_RESOLUTIONS][2] = {
173         { // GR_640
174                 120, 100
175         },
176         { // GR_1024
177                 192, 160
178         }
179 };
180
181 float Radar_center[GR_NUM_RESOLUTIONS][2] = {
182         { // GR_640
183                 322.0f, 422.0f
184         },
185         { // GR_1024
186                 515.0f, 675.0f
187         }
188 };
189
190 int Radar_coords[GR_NUM_RESOLUTIONS][2] = {
191         { // GR_640
192                 257, 369
193         }, 
194         { // GR_1024
195                 411, 590
196         }
197 };
198 char Radar_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = {
199         "radar1",
200         "2_radar1"
201 };
202
203 int Radar_blip_radius_normal[GR_NUM_RESOLUTIONS] = {
204         2,
205         4
206 };
207 int Radar_blip_radius_target[GR_NUM_RESOLUTIONS] = {
208         5,
209         8
210 };
211
212 #define BLIP_MUTATE_TIME        100
213
214 float radx, rady;
215
216 #define MAX_RADAR_LEVELS        2               // bright and dim radar dots are allowed
217
218 #define BLIP_CURRENT_TARGET     (1<<0)
219 #define BLIP_DRAW_DIM                   (1<<1)  // object is farther than Radar_dim_range units away
220 #define BLIP_DRAW_DISTORTED     (1<<2)  // object is resistant to sensors, so draw distorted
221
222 typedef struct blip     {
223         blip    *prev, *next;
224         int     x, y, rad;
225         int     flags;  // BLIP_ flags defined above
226 } blip;
227
228 #define MAX_BLIPS 150
229 typedef struct rcol {
230         ubyte   r, g, b;
231 } rcol;
232
233 #define MAX_RADAR_COLORS                9
234
235 #define RCOL_HOSTILE                    0
236 #define RCOL_FRIENDLY                   1
237 #define RCOL_UNKNOWN                    2
238 #define RCOL_NEUTRAL                    3
239 #define RCOL_BOMB                               4
240 #define RCOL_NAVBUOYS                   5
241 #define  RCOL_WARPING_SHIP              6
242 #define RCOL_JUMP_NODE                  7
243 #define RCOL_TAGGED                             8
244
245 static float    Radar_dim_range;                                        // range at which we start dimming the radar blips
246 static int              Radar_calc_dim_dist_timer;              // timestamp at which we recalc Radar_dim_range
247
248 #define NUM_FLICKER_TIMERS      2
249 static int Radar_flicker_timer[NUM_FLICKER_TIMERS];                                     // timestamp used to flicker blips on and off
250 static int Radar_flicker_on[NUM_FLICKER_TIMERS];                                                // status of flickering
251
252 #define RADAR_BLIP_BRIGHT               0                               
253 #define RADAR_BLIP_DIM                  1
254
255 rcol Radar_color_rgb[MAX_RADAR_LEVELS][MAX_RADAR_COLORS] = {
256         {{ 0xff, 0x00, 0x00},           // hostile                      (red)
257         { 0x00, 0xff, 0x00},                    // friendly                     (green)
258         { 0xff, 0x00, 0xff},                    // unknown                      (purple)
259         { 0xff, 0x00, 0x00},                    //      neutral                 (red)
260         { 0x7f, 0x7f, 0x00},                    // homing missile (yellow)
261         { 0x7f, 0x7f, 0x7f},                    // navbuoy or cargo     (gray)
262         { 0x00, 0x00, 0xff},                    // warp ship            (blue)
263         { 0x7f, 0x7f, 0x7f},                    // jump node            (gray)
264         { 0xff, 0xff, 0x00}},           // tagged                       (yellow)
265
266         // 1/3 intensity of above colors
267         {{ 0x7f, 0x00, 0x00},           // hostile                      (red)
268         { 0x00, 0x7f, 0x00},                    // friendly                     (green)
269         { 0x7f, 0x00, 0x7f},                    // unknown                      (purple)
270         { 0x7f, 0x00, 0x00},                    //      neutral                 (red)
271         { 0x40, 0x40, 0x00},                    // homing missile (yellow)
272         { 0x40, 0x40, 0x40},                    // navbuoy or cargo     (gray)
273         { 0x00, 0x00, 0x7f},                    // warp ship            (blue)
274         { 0x40, 0x40, 0x40},                    // jump node            (gray)
275         { 0x7f, 0x7f, 0x00}},           // tagged                       (yellow)
276 };
277
278 color Radar_colors[MAX_RADAR_LEVELS][MAX_RADAR_COLORS];
279
280 blip    Blip_bright_list[MAX_RADAR_COLORS];             // linked list of bright blips
281 blip    Blip_dim_list[MAX_RADAR_COLORS];                        // linked list of dim blips
282 blip    Blips[MAX_BLIPS];                                                               // blips pool
283 int     N_blips;                                                                                        // next blip index to take from pool
284
285 float Radar_farthest_dist = 1000.0f;
286 static int Blip_mutate_id;
287
288 static int Radar_static_playing;                        // is static currently playing on the radar?
289 static int Radar_static_next;                           // next time to toggle static on radar
290 static int Radar_avail_prev_frame;              // was radar active last frame?
291 static int Radar_death_timer;                           // timestamp used to play static on radar
292 int Radar_static_looping;                                       // id for looping radar static sound
293
294 static hud_frames Radar_gauge;
295
296 int Radar_dist_coords[GR_NUM_RESOLUTIONS][RR_MAX_RANGES][2] = 
297 {
298         { // GR_640
299                 {367, 461},                                                             // short
300                 {364, 461},                                                             // long
301                 {368, 461}                                                              // infinity
302         },
303         { // GR_1024
304                 {595, 740},                                                             // short
305                 {592, 740},                                                             // long
306                 {596, 741}                                                              // infinity
307         }
308 };
309
310 // forward declarations
311 void draw_radar_blips(int desired_color, int is_dim, int distort=0);
312
313 void radar_init()
314 {
315         int i,j;
316
317         Radar_gauge.first_frame = bm_load_animation(Radar_fname[gr_screen.res], &Radar_gauge.num_frames);
318         if ( Radar_gauge.first_frame < 0 ) {
319                 Warning(LOCATION,"Cannot load hud ani: %s\n", Radar_fname[gr_screen.res]);
320         }
321
322         for (i=0; i<MAX_RADAR_LEVELS; i++ )     {
323                 for (j=0; j<MAX_RADAR_COLORS; j++ )     {
324                         gr_init_alphacolor( &Radar_colors[i][j], Radar_color_rgb[i][j].r, Radar_color_rgb[i][j].g, Radar_color_rgb[i][j].b, 255 );
325                 }
326         }
327
328         Blip_mutate_id  = 1;
329 }
330
331 // determine what color the object blip should be drawn as
332 int radar_blip_color(object *objp)
333 {
334         int     color = 0;
335         ship    *shipp = NULL;
336
337         switch(objp->type) {
338         case OBJ_SHIP:
339                 shipp = &Ships[objp->instance];
340                 if ( shipp->flags & SF_ARRIVING_STAGE_1 )       {
341                         color = RCOL_WARPING_SHIP;
342                 } else if ( ship_is_tagged(objp) ) {
343                         color = RCOL_TAGGED;
344                 } else if ( Ship_info[shipp->ship_info_index].flags & (SIF_NAVBUOY|SIF_CARGO) ) {
345                         color = RCOL_NAVBUOYS;
346                 } else {
347                         if ( (Player_ship->team == shipp->team) && (Player_ship->team != TEAM_TRAITOR) ) {
348                                 color = RCOL_FRIENDLY;
349                         } else {
350                                 switch (shipp->team) {
351                                 case TEAM_FRIENDLY:
352                                 case TEAM_HOSTILE:
353                                 case TEAM_TRAITOR:
354                                         color = RCOL_HOSTILE;
355                                         break;
356                                 case TEAM_NEUTRAL:
357                                         color = RCOL_NEUTRAL;
358                                         break;
359                                 case TEAM_UNKNOWN:
360                                         color = RCOL_UNKNOWN;
361                                         break;
362                                 default:
363                                         color = RCOL_HOSTILE;
364                                         Int3(); //      Bogus team id in shipp->team
365                                         break;
366                                 }
367                         }
368                 }
369                 break;
370         case OBJ_WEAPON:
371                 color = RCOL_BOMB;
372                 break;
373         case OBJ_JUMP_NODE:
374                 color = RCOL_JUMP_NODE;
375                 break;
376         default:
377                 Error(LOCATION, "Illegal ship type in radar.");
378                 break;
379         }
380
381         return color;
382 }
383
384 int See_all = FALSE;
385 DCF_BOOL(see_all, See_all)
386
387 void radar_plot_object( object *objp )  
388 {
389         vector  pos, tempv;
390         float           dist, rscale, zdist, max_radar_dist;
391         int             xpos, ypos, color=0;
392         vector  *world_pos = &objp->pos;        
393         float           awacs_level;
394
395         // don't process anything here.  Somehow, a jumpnode object caused this function
396         // to get entered on server side.
397         if( Game_mode & GM_STANDALONE_SERVER ){
398                 return;
399         }
400
401         // multiplayer clients ingame joining should skip this function
402         if ( MULTIPLAYER_CLIENT && (Net_player->flags & NETINFO_FLAG_INGAME_JOIN) ){
403                 return;
404         }
405
406         // get team-wide awacs level for the object if not ship
407         int ship_is_visible = 0;
408         if (objp->type == OBJ_SHIP) {
409                 if (Player_ship != NULL) {
410                         if (ship_is_visible_by_team(objp->instance, Player_ship->team)) {
411                                 ship_is_visible = 1;
412                         }
413                 }
414         }
415
416         // only check awacs level if ship is not visible by team
417         awacs_level = 1.5f;
418         if (Player_ship != NULL && !ship_is_visible) {
419                 awacs_level = awacs_get_level(objp, Player_ship);
420         }
421
422         // if the awacs level is unviewable - bail
423         if(awacs_level < 0.0f && !See_all){
424                 return;
425         }
426
427         // Apply object type filters    
428         switch ( objp->type ) {
429         case OBJ_SHIP:
430                 // Place to cull ships, such as NavBuoys                
431                 break;
432                 
433         case OBJ_JUMP_NODE:
434                 // filter jump nodes here if required
435                 break;
436
437         case OBJ_WEAPON: {
438                 // if not a bomb, return
439                 if ( !(Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags & WIF_BOMB) ) {
440                         return;
441                 }
442
443                 // if bomb is on same team as player, return
444                 if ( (obj_team(objp) == Player_ship->team) && (Player_ship->team != TEAM_TRAITOR) ) {
445                         return;
446                 }
447                 break;
448         }
449
450         default:
451                 return;                 // if any other kind of object, don't want to show on radar
452                 break;
453         } // end switch
454
455         
456         // JAS -- new way of getting the rotated point that doesn't require this to be
457         // in a g3_start_frame/end_frame block.
458         vm_vec_sub(&tempv,world_pos,&Player_obj->pos);
459         vm_vec_rotate( &pos, &tempv, &Player_obj->orient );
460
461         // Apply range filter
462         dist = vm_vec_dist(world_pos, &Player_obj->pos);
463         max_radar_dist = Radar_ranges[HUD_config.rp_dist];
464         if ( dist > max_radar_dist ){
465                 return;
466         }
467
468         if ( dist < pos.z ) {
469                 rscale = 0.0f;
470         } else {
471                 rscale = (float) acos( pos.z/dist ) / 3.14159f;         //2.0f;  
472         }
473
474         zdist = fl_sqrt( (pos.x*pos.x)+(pos.y*pos.y) );
475
476         float new_x_dist, clipped_x_dist;
477         float new_y_dist, clipped_y_dist;
478
479         if (zdist < 0.01f ) {
480                 new_x_dist = 0.0f;
481                 new_y_dist = 0.0f;
482         }
483         else {
484                 new_x_dist = (pos.x/zdist) * rscale * radx;
485                 new_y_dist = (pos.y/zdist) * rscale * rady;
486
487                 // force new_x_dist and new_y_dist to be inside the radar
488
489                 float hypotenuse;
490                 float max_radius;
491
492                 hypotenuse = (float)_hypot(new_x_dist, new_y_dist);
493                 max_radius = i2fl(Radar_radius[gr_screen.res][0] - 5);
494
495                 if (hypotenuse >= (max_radius) ) {
496                         clipped_x_dist = max_radius * (new_x_dist / hypotenuse);
497                         clipped_y_dist = max_radius * (new_y_dist / hypotenuse);
498                         new_x_dist = clipped_x_dist;
499                         new_y_dist = clipped_y_dist;
500                 }
501         }
502
503         xpos = fl2i( Radar_center[gr_screen.res][0] + new_x_dist );
504         ypos = fl2i( Radar_center[gr_screen.res][1] - new_y_dist );
505
506         color = radar_blip_color(objp);
507
508         // Determine the distance at which we will dim the radar blip
509         if ( timestamp_elapsed(Radar_calc_dim_dist_timer) ) {
510                 Radar_calc_dim_dist_timer=timestamp(1000);
511                 Radar_dim_range = player_farthest_weapon_range();
512                 if ( Radar_dim_range <= 0 ) {
513                         Radar_dim_range=1500.0f;
514                 }
515         }
516
517         blip    *b;
518         int blip_dim=0;
519
520         if ( dist > Radar_dim_range ) {
521                 blip_dim=1;
522         }
523
524         if ( N_blips >= MAX_BLIPS ) {
525                 // out of blips, don't plot
526                 Int3();
527                 return;
528         }
529
530         b = &Blips[N_blips];
531         b->flags=0;
532
533         // flag the blip as a current target if it is
534         if (OBJ_INDEX(objp) == Player_ai->target_objnum)        {
535                 b->flags |= BLIP_CURRENT_TARGET;
536                 blip_dim = 0;
537         }
538
539         if ( blip_dim ) {
540                 list_append( &Blip_dim_list[color], b );
541         } else {
542                 list_append( &Blip_bright_list[color], b );
543         }
544
545         b->x = xpos;
546         b->y = ypos;
547
548         // see if blip should be drawn distorted
549         if (objp->type == OBJ_SHIP) {
550                 // ships specifically hidden from sensors
551                 if ( Ships[objp->instance].flags & SF_HIDDEN_FROM_SENSORS ) {
552                         b->flags |= BLIP_DRAW_DISTORTED;
553                 }
554
555                 // determine if its AWACS distorted
556                 if ( awacs_level < 1.0f ){
557                         b->flags |= BLIP_DRAW_DISTORTED;
558                 }
559         }                               
560
561         N_blips++;
562 }
563
564 // set N_blips for each color/brightness level to zero
565 void radar_null_nblips()
566 {
567         int i;
568
569         N_blips=0;
570
571         for (i=0; i<MAX_RADAR_COLORS; i++) {
572                 list_init(&Blip_bright_list[i]);
573                 list_init(&Blip_dim_list[i]);
574         }
575 }
576
577 // radar_mission_init() is called at the start of each mission.  
578 void radar_mission_init()
579 {
580         int i;
581
582         Blip_mutate_id                          = 1;
583         Radar_death_timer                       = 0;
584         Radar_static_playing            = 0;
585         Radar_static_next                       = 0;
586         Radar_avail_prev_frame  = 1;
587         Radar_calc_dim_dist_timer = timestamp(0);
588
589         for ( i=0; i<NUM_FLICKER_TIMERS; i++ ) {
590                 Radar_flicker_timer[i]=timestamp(0);
591                 Radar_flicker_on[i]=0;
592         }
593 }
594
595 #define SMALL_BLIP_CHAR (Lcl_special_chars + 5)
596 #define LARGE_BLIP_CHAR (Lcl_special_chars + 6)
597
598 int Small_blip_offset_x = 0;
599 int Small_blip_offset_y = 0;
600 int Large_blip_offset_x = 0;
601 int Large_blip_offset_y = 0;
602
603 char Small_blip_string[2];
604 char Large_blip_string[2];
605
606 void radar_frame_init()
607 {
608         radar_null_nblips();
609         radx = i2fl(Radar_radius[gr_screen.res][0])/2.0f;
610         rady = i2fl(Radar_radius[gr_screen.res][1])/2.0f;
611
612         int w,h;
613         gr_set_font(FONT1);
614
615         Small_blip_string[0] = ubyte(SMALL_BLIP_CHAR);
616         Small_blip_string[1] = 0;
617         gr_get_string_size( &w, &h, Small_blip_string );
618         Small_blip_offset_x = -w/2;
619         Small_blip_offset_y = -h/2;
620
621         Large_blip_string[0] = ubyte(LARGE_BLIP_CHAR);
622         Large_blip_string[1] = 0;
623         gr_get_string_size( &w, &h, Large_blip_string );
624         Large_blip_offset_x = -w/2;
625         Large_blip_offset_y = -h/2;
626 }
627
628 void radar_draw_circle( int x, int y, int rad )
629 {
630         if ( rad == Radar_blip_radius_target[gr_screen.res] )   {
631                 gr_string( Large_blip_offset_x+x, Large_blip_offset_y+y, Large_blip_string );
632         } else {
633                 // rad = RADAR_BLIP_RADIUS_NORMAL;
634                 gr_string( Small_blip_offset_x+x, Small_blip_offset_y+y, Small_blip_string );
635         }
636 }
637
638 // radar is damaged, so make blips dance around
639 void radar_blip_draw_distorted(blip *b)
640 {
641         int xdiff, ydiff;
642         float scale;
643         xdiff = -10 + rand()%20;
644         ydiff = -10 + rand()%20;
645
646         // maybe scale the effect if EMP is active
647         if(emp_active_local()){
648                 scale = emp_current_intensity();
649
650                 xdiff = (int)((float)xdiff * scale);
651                 ydiff = (int)((float)ydiff * scale);
652         }
653
654         radar_draw_circle( b->x+xdiff, b->y+ydiff, b->rad ); 
655 }
656
657 // blip is for a target immune to sensors, so cause to flicker in/out with mild distortion
658 void radar_blip_draw_flicker(blip *b)
659 {
660         int xdiff=0, ydiff=0, flicker_index;
661
662         if ( (b-Blips) & 1 ) {
663                 flicker_index=0;
664         } else {
665                 flicker_index=1;
666         }
667
668         if ( timestamp_elapsed(Radar_flicker_timer[flicker_index]) ) {
669                 Radar_flicker_timer[flicker_index] = timestamp_rand(50,1000);
670                 Radar_flicker_on[flicker_index] ^= 1;
671         }
672
673         if ( !Radar_flicker_on[flicker_index] ) {
674                 return;
675         }
676
677         if ( rand() & 1 ) {
678                 xdiff = -2 + rand()%4;
679                 ydiff = -2 + rand()%4;
680         }
681
682         radar_draw_circle( b->x+xdiff, b->y+ydiff, b->rad ); 
683 }
684
685 // Draw all the active radar blips
686 void draw_radar_blips(int rcol, int is_dim, int distort)
687 {
688         blip    *b=NULL;
689         blip    *blip_head=NULL;
690
691         // Need to set font.
692         gr_set_font(FONT1);
693
694         if ( is_dim ) {
695                 blip_head = &Blip_dim_list[rcol];
696         } else {
697                 blip_head = &Blip_bright_list[rcol];
698         }
699
700         for ( b = GET_FIRST(blip_head); b !=END_OF_LIST(blip_head); b = GET_NEXT(b) )   {
701
702                 Assert((rcol >= 0) && (rcol < MAX_RADAR_COLORS));
703
704                 if ( is_dim ) {
705                         gr_set_color_fast( &Radar_colors[RADAR_BLIP_DIM][rcol] );
706                 } else {
707                         gr_set_color_fast( &Radar_colors[RADAR_BLIP_BRIGHT][rcol] );
708                 }
709
710                 if (b->flags & BLIP_CURRENT_TARGET) {
711                         // draw cool blip to indicate current target
712                         b->rad = Radar_blip_radius_target[gr_screen.res];                               
713                 } else {
714                         b->rad = Radar_blip_radius_normal[gr_screen.res];
715                 }
716
717                 if ( distort ) { 
718                         radar_blip_draw_distorted(b);
719                 } else if ( b->flags & BLIP_DRAW_DISTORTED ) {
720                         radar_blip_draw_flicker(b);
721                 } else{
722                         radar_draw_circle( b->x, b->y, b->rad );
723                 }
724         }
725 }
726
727 // Draw the radar blips
728 // input:       distorted       =>              0 (default) to draw normal, 1 to draw distorted 
729 void radar_draw_blips_sorted(int distort)
730 {
731         // draw dim blips first
732         draw_radar_blips(RCOL_JUMP_NODE, 1, distort);
733         draw_radar_blips(RCOL_WARPING_SHIP, 1, distort);
734         draw_radar_blips(RCOL_NAVBUOYS, 1, distort);
735         draw_radar_blips(RCOL_FRIENDLY, 1, distort);
736         draw_radar_blips(RCOL_UNKNOWN, 1, distort);
737         draw_radar_blips(RCOL_BOMB, 1, distort);
738         draw_radar_blips(RCOL_NEUTRAL, 1, distort);
739         draw_radar_blips(RCOL_HOSTILE, 1, distort);
740         draw_radar_blips(RCOL_TAGGED, 1, distort);
741
742         // draw bright blips
743         draw_radar_blips(RCOL_JUMP_NODE, 0, distort);
744         draw_radar_blips(RCOL_WARPING_SHIP, 0, distort);
745         draw_radar_blips(RCOL_NAVBUOYS, 0, distort);
746         draw_radar_blips(RCOL_FRIENDLY, 0, distort);
747         draw_radar_blips(RCOL_UNKNOWN, 0, distort);
748         draw_radar_blips(RCOL_BOMB, 0, distort);
749         draw_radar_blips(RCOL_NEUTRAL, 0, distort);
750         draw_radar_blips(RCOL_HOSTILE, 0, distort);
751         draw_radar_blips(RCOL_TAGGED, 0, distort);
752 }
753
754 void radar_draw_range()
755 {
756         char buf[32];
757
758         // hud_set_bright_color();
759         hud_set_gauge_color(HUD_RADAR, HUD_C_BRIGHT);
760
761         switch ( HUD_config.rp_dist ) {
762
763         case RR_SHORT:
764                 gr_printf(Radar_dist_coords[gr_screen.res][RR_SHORT][0], Radar_dist_coords[gr_screen.res][RR_SHORT][1], XSTR( "2k", 467));
765                 break;
766
767         case RR_LONG:
768                 gr_printf(Radar_dist_coords[gr_screen.res][RR_LONG][0], Radar_dist_coords[gr_screen.res][RR_LONG][1], XSTR( "10k", 468));
769                 break;
770
771         case RR_INFINITY:
772                 sprintf(buf, NOX("%c"), Lcl_special_chars);
773                 gr_printf(Radar_dist_coords[gr_screen.res][RR_INFINITY][0], Radar_dist_coords[gr_screen.res][RR_INFINITY][1], buf);
774                 break;
775
776         default:
777                 Int3(); // can't happen (get Alan if it does)
778                 break;
779         }
780
781         hud_set_default_color();
782 }
783
784 void radar_frame_render(float frametime)
785 {
786         float   sensors_str;
787         int ok_to_blit_radar;
788
789         ok_to_blit_radar = 1;
790
791         sensors_str = ship_get_subsystem_strength( Player_ship, SUBSYSTEM_SENSORS );
792
793         if ( ship_subsys_disrupted(Player_ship, SUBSYSTEM_SENSORS) ) {
794                 sensors_str = MIN_SENSOR_STR_TO_RADAR-1;
795         }
796
797         // note that on lowest skill level, there is no radar effects due to sensors damage
798         if ( (Game_skill_level == 0) || (sensors_str > SENSOR_STR_RADAR_NO_EFFECTS) ) {
799                 Radar_static_playing = 0;
800                 Radar_static_next = 0;
801                 Radar_death_timer = 0;
802                 Radar_avail_prev_frame = 1;
803         } else if ( sensors_str < MIN_SENSOR_STR_TO_RADAR ) {
804                 if ( Radar_avail_prev_frame ) {
805                         Radar_death_timer = timestamp(2000);
806                         Radar_static_next = 1;
807                 }
808                 Radar_avail_prev_frame = 0;
809         } else {
810                 Radar_death_timer = 0;
811                 if ( Radar_static_next == 0 )
812                         Radar_static_next = 1;
813         }
814
815         if ( timestamp_elapsed(Radar_death_timer) ) {
816                 ok_to_blit_radar = 0;
817         }
818
819         hud_set_gauge_color(HUD_RADAR);
820         radar_blit_gauge();
821         radar_draw_range();
822
823         if ( timestamp_elapsed(Radar_static_next) ) {
824                 Radar_static_playing ^= 1;
825                 Radar_static_next = timestamp_rand(50, 750);
826         }
827
828         // if the emp effect is active, always draw the radar wackily
829         if(emp_active_local()){
830                 Radar_static_playing = 1;
831         }
832
833         if ( ok_to_blit_radar ) {
834                 if ( Radar_static_playing ) {
835                         radar_draw_blips_sorted(1);     // passing 1 means to draw distorted
836                         if ( Radar_static_looping == -1 ) {
837                                 Radar_static_looping = snd_play_looping(&Snds[SND_STATIC]);
838                         }
839                 } else {
840                         radar_draw_blips_sorted();
841                         if ( Radar_static_looping != -1 ) {
842                                 snd_stop(Radar_static_looping);
843                                 Radar_static_looping = -1;
844                         }
845                 }
846         } else {
847                 if ( Radar_static_looping != -1 ) {
848                         snd_stop(Radar_static_looping);
849                         Radar_static_looping = -1;
850                 }
851         }
852 }
853
854 void radar_blit_gauge()
855 {
856         gr_set_bitmap(Radar_gauge.first_frame+1);
857         gr_aabitmap( Radar_coords[gr_screen.res][0], Radar_coords[gr_screen.res][1] );
858
859
860 void radar_page_in()
861 {
862         bm_page_in_aabitmap( Radar_gauge.first_frame, Radar_gauge.num_frames );
863 }
864