]> icculus.org git repositories - taylor/freespace2.git/blob - src/hud/hudshield.cpp
added copyright header
[taylor/freespace2.git] / src / hud / hudshield.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/Hud/HUDshield.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  * C file for the display and management of the HUD shield
16  *
17  * $Log$
18  * Revision 1.3  2002/06/09 04:41:21  relnev
19  * added copyright header
20  *
21  * Revision 1.2  2002/05/07 03:16:45  theoddone33
22  * The Great Newline Fix
23  *
24  * Revision 1.1.1.1  2002/05/03 03:28:09  root
25  * Initial import.
26  *
27  * 
28  * 12    8/27/99 10:36a Dave
29  * Impose a 2% penalty for hitting the shield balance key.
30  * 
31  * 11    8/23/99 11:34a Dave
32  * Fixed shield intensity rendering problems.
33  * 
34  * 10    8/01/99 12:39p Dave
35  * Added HUD contrast control key (for nebula).
36  * 
37  * 9     7/22/99 4:00p Dave
38  * Fixed beam weapon muzzle glow rendering. Externalized hud shield info.
39  * 
40  * 8     6/10/99 3:43p Dave
41  * Do a better job of syncing text colors to HUD gauges.
42  * 
43  * 7     1/07/99 9:06a Jasen
44  * coords...
45  * 
46  * 6     12/30/98 8:57a Jasen
47  * updated coords for hi res
48  * 
49  * 5     12/28/98 3:17p Dave
50  * Support for multiple hud bitmap filenames for hi-res mode.
51  * 
52  * 4     12/21/98 5:02p Dave
53  * Modified all hud elements to be multi-resolution friendly.
54  * 
55  * 3     12/14/98 1:15p Jasen
56  * added new HUD shield gauges
57  * 
58  * 2     10/07/98 10:53a Dave
59  * Initial checkin.
60  * 
61  * 1     10/07/98 10:49a Dave
62  * 
63  * 35    9/19/98 3:11p Adam
64  * Added new hardcoded values for Hud_shield_filenames
65  * 
66  * 34    8/25/98 1:48p Dave
67  * First rev of EMP effect. Player side stuff basically done. Next comes
68  * AI code.
69  * 
70  * 33    5/17/98 3:32p Lawrance
71  * Make shield gauge more readable when flashing
72  * 
73  * 32    4/25/98 5:39p Dave
74  * Removed an unneeded assert.
75  * 
76  * 31    4/25/98 3:56p Mike
77  * Make player's shield icon flash when Fred tells it to.
78  * 
79  * 30    4/25/98 2:00p Dave
80  * Installed a bunch of multiplayer context help screens. Reworked ingame
81  * join ship select screen. Fix places where network timestamps get hosed.
82  * 
83  * 29    4/21/98 12:19a Allender
84  * only play equalize shield sound if player equalizes shields
85  * 
86  * 28    3/26/98 5:26p John
87  * added new paging code. nonfunctional.
88  * 
89  * 27    3/21/98 3:35p Lawrance
90  * Tweak position of numeric integrity for target
91  * 
92  * 26    3/14/98 4:59p Lawrance
93  * Flash shield/ship icons when ships are hit
94  * 
95  * 25    3/02/98 5:42p John
96  * Removed WinAVI stuff from Freespace.  Made all HUD gauges wriggle from
97  * afterburner.  Made gr_set_clip work good with negative x &y.  Made
98  * model_caching be on by default.  Made each cached model have it's own
99  * bitmap id.  Made asteroids not rotate when model_caching is on.  
100  * 
101  * 24    2/22/98 12:19p John
102  * Externalized some strings
103  * 
104  * 23    2/12/98 4:58p Lawrance
105  * Change to new flashing method.
106  * 
107  * 22    1/12/98 11:16p Lawrance
108  * Wonderful HUD config.
109  * 
110  * 21    1/08/98 4:36p Lawrance
111  * Fix bug in shield drawing code.
112  * 
113  * 20    1/05/98 9:38p Lawrance
114  * Implement flashing HUD gauges.
115  * 
116  * 19    1/02/98 9:10p Lawrance
117  * Big changes to how colors get set on the HUD.
118  * 
119  * 18    12/29/97 9:48p Mike
120  * Prevent indexing before array start when quadrant_num = -1.
121  * 
122  * 17    12/01/97 12:27a Lawrance
123  * redo default alpha color for HUD, make it easy to modify in the future
124  * 
125  * 16    11/18/97 5:58p Lawrance
126  * flash escort view info when that ship is taking hits
127  * 
128  * 15    11/18/97 1:21p Mitri
129  * ALAN: be sure to only draw shield icons for targets that are ships
130  * 
131  * 14    11/17/97 6:37p Lawrance
132  * new gauges: extended target view, new lock triangles, support ship view
133  * 
134  * 13    11/13/97 10:46p Lawrance
135  * implemented new escort view, damage view and weapons
136  * 
137  * 12    11/12/97 9:42a Lawrance
138  * show player ship integrity above shield icon
139  * 
140  * 11    11/11/97 5:06p Lawrance
141  * fix bug with flashing frequency of hull
142  * 
143  * 10    11/09/97 11:27p Lawrance
144  * move target shield icon closer to center
145  * 
146  * 9     11/09/97 4:39p Lawrance
147  * don't draw mini ship icon anymore
148  * 
149  * 8     11/08/97 11:08p Lawrance
150  * implement new "mini-shield" view that sits near bottom of reticle
151  * 
152  * 7     11/05/97 11:21p Lawrance
153  * implement dynamic alpha on the shields
154  * 
155  * 6     11/04/97 8:34p Lawrance
156  * fix warning: remove unused variable
157  * 
158  * 5     11/04/97 7:49p Lawrance
159  * integrating new HUD reticle and shield icons
160  * 
161  * 4     10/24/97 5:51p Lawrance
162  * don't show shield % if ship has no shields
163  * 
164  * 3     9/03/97 4:32p John
165  * changed bmpman to only accept ani and pcx's.  made passing .pcx or .ani
166  * to bm_load functions not needed.   Made bmpman keep track of palettes
167  * for bitmaps not mapped into game palettes.
168  * 
169  * 2     8/25/97 12:24a Lawrance
170  * implemented HUD shield management
171  * 
172  * 1     8/24/97 10:31p Lawrance
173  *
174  * $NoKeywords: $
175  */
176
177 #include "2d.h"
178 #include "object.h"
179 #include "hud.h"
180 #include "hudtarget.h"
181 #include "hudtargetbox.h"
182 #include "hudets.h"
183 #include "player.h"
184 #include "gamesnd.h"
185 #include "freespace.h"
186 #include "bmpman.h"
187 #include "timer.h"
188 #include "hudshield.h"
189 #include "hudescort.h"
190 #include "emp.h"
191 #include "multi.h"
192
193 #define NUM_SHIELD_LEVELS               8
194
195 #define SHIELD_TRANSFER_PERCENT 0.083f          // 1/12 total shield strength
196
197 #define SHIELD_HIT_DURATION_SHORT       300     // time a shield quadrant flashes after being hit
198 #define SHIELD_FLASH_INTERVAL_FAST      200     // time between shield quadrant flashes
199
200 // now read in from hud.tbl
201 #define MAX_SHIELD_ICONS                40
202 int Hud_shield_filename_count = 0;
203 char Hud_shield_filenames[MAX_SHIELD_ICONS][MAX_FILENAME_LEN];
204
205 char Shield_mini_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = {
206         "targhit1",
207         "targhit1"
208 };
209
210 hud_frames Shield_gauges[MAX_SHIELD_ICONS];
211
212 static int Player_shield_coords[GR_NUM_RESOLUTIONS][2] = 
213 {
214         { // GR_640
215                 396, 379
216         },
217         { // GR_1024
218                 634, 670
219         }
220 };
221
222 static int Target_shield_coords[GR_NUM_RESOLUTIONS][2] = 
223 {
224         { // GR_640
225                 142, 379
226         },
227         { // GR_1024
228                 292, 670
229         }
230 };
231
232 static int Hud_shield_inited = 0;
233
234 int Shield_mini_coords[GR_NUM_RESOLUTIONS][2] = {
235         { // GR_640
236                 305, 291
237         },
238         { // GR_1024
239                 497, 470
240         }
241 };
242
243 // draw on the mini shield icon what the ship integrity is
244 int Hud_mini_3digit[GR_NUM_RESOLUTIONS][3] = {
245         { // GR_640
246                 310, 298, 0
247         },
248         { // GR_1024
249                 502, 477, 0
250         }
251 };
252 int Hud_mini_2digit[GR_NUM_RESOLUTIONS][3] = {
253         { // GR_640
254                 213, 298, 2
255         },
256         { // GR_1024
257                 346, 477, 2
258         }
259 };
260 int Hud_mini_1digit[GR_NUM_RESOLUTIONS][3] = {
261         { // GR_640
262                 316, 298, 6
263         },
264         { // GR_1024
265                 511, 477, 6
266         }
267 };
268 int Hud_mini_base[GR_NUM_RESOLUTIONS][2] = {
269         { // GR_640
270                 310, 298
271         },
272         { // GR_1024
273                 502, 477
274         }
275 };
276         
277 int Shield_mini_loaded = 0;
278 hud_frames Shield_mini_gauge;
279
280 #define SHIELD_HIT_PLAYER               0
281 #define SHIELD_HIT_TARGET               1
282 static shield_hit_info  Shield_hit_data[2];
283
284 // translate between clockwise-from-top shield quadrant ordering to way quadrants are numbered in the game
285 ubyte Quadrant_xlate[4] = {1,0,2,3};
286
287 void hud_shield_game_init()
288 {
289         char name[MAX_FILENAME_LEN+1] = "";
290
291         // read in hud.tbl
292         read_file_text("hud.tbl");
293         reset_parse();
294
295         Hud_shield_filename_count = 0;
296         required_string("#Shield Icons Begin");
297         while(!optional_string("#End")){
298                 required_string("$Shield:");
299
300                 stuff_string(name, F_NAME, NULL);
301
302                 // maybe store
303                 Assert(Hud_shield_filename_count < MAX_SHIELD_ICONS);
304                 if(Hud_shield_filename_count < MAX_SHIELD_ICONS){
305                         strcpy(Hud_shield_filenames[Hud_shield_filename_count++], name);
306                 }
307         }
308 }
309
310 // called at the start of each level from HUD_init.  Use Hud_shield_init so we only init Shield_gauges[] once.
311 void hud_shield_level_init()
312 {
313         int i;  
314
315         hud_shield_hit_reset(1);        // reset for the player
316
317         if ( Hud_shield_inited ) {
318                 return;
319         }       
320
321         for ( i = 0; i < MAX_SHIELD_ICONS; i++ ) {
322                 Shield_gauges[i].first_frame = -1;
323                 Shield_gauges[i].num_frames  = 0;
324         }
325
326         Hud_shield_inited = 1;
327
328         if ( !Shield_mini_loaded ) {
329                 Shield_mini_gauge.first_frame = bm_load_animation(Shield_mini_fname[gr_screen.res], &Shield_mini_gauge.num_frames);
330                 if ( Shield_mini_gauge.first_frame == -1 ) {
331                         Warning(LOCATION, "Could not load in the HUD shield ani: Shield_mini_fname[gr_screen.res]\n");
332                         return;
333                 }
334                 Shield_mini_loaded = 1;
335         }
336 }
337
338 int hud_shield_maybe_flash(int gauge, int target_index, int shield_offset)
339 {
340         int                                     flashed = 0;
341         shield_hit_info *shi;
342
343         shi = &Shield_hit_data[target_index];
344
345         if ( !timestamp_elapsed(shi->shield_hit_timers[shield_offset]) ) {
346                 if ( timestamp_elapsed(shi->shield_hit_next_flash[shield_offset]) ) {
347                         shi->shield_hit_next_flash[shield_offset] = timestamp(SHIELD_FLASH_INTERVAL_FAST);
348                         shi->shield_show_bright ^= (1<<shield_offset);  // toggle between default and bright frames
349                 }
350
351                 if ( shi->shield_show_bright & (1<<shield_offset) ) {
352                         // hud_set_bright_color();
353                         hud_set_gauge_color(gauge, HUD_C_BRIGHT);
354                         flashed = 1;
355                 } else {
356                         hud_set_gauge_color(gauge, HUD_C_NORMAL);
357                         // hud_set_default_color();
358                 }
359         }
360
361         return flashed;
362 }
363
364 // ------------------------------------------------------------------
365 // hud_shield_show()
366 //
367 // Show the players shield strength and integrity
368 //
369 void hud_shield_show(object *objp)
370 {
371         float                   max_shield;
372         int                     hud_color_index, range;
373         int                     sx, sy, i;
374         ship                    *sp;
375         ship_info       *sip;
376         hud_frames      *sgp;
377
378         if ( objp->type != OBJ_SHIP )
379                 return;
380
381         sp = &Ships[objp->instance];
382         sip = &Ship_info[sp->ship_info_index];
383
384         if ( sip->shield_icon_index == 255 ) {
385                 return;
386         }
387
388         if (objp == Player_obj) {
389                 hud_set_gauge_color(HUD_PLAYER_SHIELD_ICON);
390         } else {
391                 hud_set_gauge_color(HUD_TARGET_SHIELD_ICON);
392         }
393
394         // load in shield frames if not already loaded
395         Assert(sip->shield_icon_index >= 0 && sip->shield_icon_index < Hud_shield_filename_count);
396         sgp = &Shield_gauges[sip->shield_icon_index];
397
398         if ( sgp->first_frame == -1 ) {
399                 sgp->first_frame = bm_load_animation(Hud_shield_filenames[sip->shield_icon_index], &sgp->num_frames);
400                 if ( sgp->first_frame == -1 ) {
401                         Warning(LOCATION, "Could not load in the HUD shield ani: %s\n", Hud_shield_filenames[sip->shield_icon_index]);
402                         return;
403                 }
404         }
405
406         if ( objp == Player_obj ) {
407                 sx = Player_shield_coords[gr_screen.res][0];
408                 sy = Player_shield_coords[gr_screen.res][1];
409         } else {
410                 sx = Target_shield_coords[gr_screen.res][0];
411                 sy = Target_shield_coords[gr_screen.res][1];
412         }
413
414         sx += fl2i(HUD_offset_x);
415         sy += fl2i(HUD_offset_y);
416
417         // draw the ship first
418         if ( objp == Player_obj ) {
419                 hud_shield_maybe_flash(HUD_PLAYER_SHIELD_ICON, SHIELD_HIT_PLAYER, HULL_HIT_OFFSET);
420         } else {
421                 hud_shield_maybe_flash(HUD_TARGET_SHIELD_ICON, SHIELD_HIT_TARGET, HULL_HIT_OFFSET);
422         }
423
424         GR_AABITMAP(sgp->first_frame, sx, sy);  
425
426         // draw the four quadrants
427         //
428         // Draw shield quadrants at one of NUM_SHIELD_LEVELS
429         max_shield = sip->shields/4.0f;
430
431         for ( i = 0; i < 4; i++ ) {
432
433                 if ( objp->flags & OF_NO_SHIELDS ) {
434                         break;
435                 }
436
437                 if ( objp->shields[Quadrant_xlate[i]] < 0.1f ) {
438                         continue;
439                 }
440
441                 range = max(HUD_COLOR_ALPHA_MAX, HUD_color_alpha + 4);
442                 hud_color_index = fl2i( (objp->shields[Quadrant_xlate[i]] / max_shield) * range + 0.5);
443                 Assert(hud_color_index >= 0 && hud_color_index <= range);
444
445                 if ( hud_color_index < 0 ) {
446                         hud_color_index = 0;
447                 }
448                 if ( hud_color_index >= HUD_NUM_COLOR_LEVELS ) {
449                         hud_color_index = HUD_NUM_COLOR_LEVELS - 1;
450                 }
451
452                 int flash=0;
453                 if ( objp == Player_obj ) {
454                         flash = hud_shield_maybe_flash(HUD_PLAYER_SHIELD_ICON, SHIELD_HIT_PLAYER, i);
455                 } else {
456                         flash = hud_shield_maybe_flash(HUD_TARGET_SHIELD_ICON, SHIELD_HIT_TARGET, i);
457                 }
458                                 
459                 if ( !flash ) {
460                         // gr_set_color_fast(&HUD_color_defaults[hud_color_index]);
461                         if ( objp == Player_obj ) {
462                                 hud_set_gauge_color(HUD_PLAYER_SHIELD_ICON, hud_color_index);
463                         } else {
464                                 hud_set_gauge_color(HUD_TARGET_SHIELD_ICON, hud_color_index);
465                         }
466
467                         GR_AABITMAP(sgp->first_frame+i+1, sx, sy);                      
468                 }
469         }
470
471         // hud_set_default_color();
472 }
473
474 // called at beginning of level to page in all ship icons
475 // used in this level
476 void hud_ship_icon_page_in(ship_info *sip)
477 {
478         hud_frames      *sgp;
479
480         if ( sip->shield_icon_index == 255 ) {
481                 return;
482         }
483
484         // load in shield frames if not already loaded
485         Assert(sip->shield_icon_index >= 0 && sip->shield_icon_index < Hud_shield_filename_count);
486         sgp = &Shield_gauges[sip->shield_icon_index];
487
488         if ( sgp->first_frame == -1 ) {
489                 sgp->first_frame = bm_load_animation(Hud_shield_filenames[sip->shield_icon_index], &sgp->num_frames);
490                 if ( sgp->first_frame == -1 ) {
491                         Warning(LOCATION, "Could not load in the HUD shield ani: %s\n", Hud_shield_filenames[sip->shield_icon_index]);
492                         return;
493                 }
494         }
495
496         int i;
497         for (i=0; i<sgp->num_frames; i++ )      {
498                 bm_page_in_aabitmap(sgp->first_frame+i);
499         }
500
501 }
502
503 // ------------------------------------------------------------------
504 // hud_shield_equalize()
505 //
506 // Equalize the four shield quadrants for an object
507 //
508 void hud_shield_equalize(object *objp, player *pl)
509 {
510         float   strength;
511         int idx;
512         int all_equal = 1;
513
514         Assert(objp != NULL);
515         if(objp == NULL){
516                 return;
517         }
518         Assert(pl != NULL);
519         if(pl == NULL){
520                 return;
521         }
522         Assert(objp->type == OBJ_SHIP);
523         if(objp->type != OBJ_SHIP){
524                 return;
525         }
526
527         // are all quadrants equal?
528         for(idx=0; idx<MAX_SHIELD_SECTIONS-1; idx++){
529                 if(objp->shields[idx] != objp->shields[idx+1]){
530                         all_equal = 0;
531                         break;
532                 }
533         }
534
535         // not all equal
536         if(!all_equal){
537                 strength = get_shield_strength(objp);
538                 if ( strength != 0 ) {
539                         // maybe impose a 2% penalty - server side and single player only
540                         if(!MULTIPLAYER_CLIENT &&  (pl->shield_penalty_stamp < 0) || timestamp_elapsed_safe(pl->shield_penalty_stamp, 1000) ){
541                                 strength *= 0.98f;
542
543                                 // reset the penalty timestamp
544                                 pl->shield_penalty_stamp = timestamp(1000);
545                         }
546                         
547                         set_shield_strength(objp, strength);                                    
548                 }
549         }
550
551         // beep
552         if ( objp == Player_obj ){
553                 snd_play( &Snds[SND_SHIELD_XFER_OK] );
554         }
555 }
556
557 // ------------------------------------------------------------------
558 // hud_augment_shield_quadrant()
559 //
560 // Transfer shield energy to a shield quadrant from the three other
561 //      quadrants.  Works by trying to transfer a fixed amount of shield
562 //      energy from the other three quadrants, taking the same percentage
563 // from each quadrant.
564 //
565 //      input:  objp                    =>              object to perform shield transfer on
566 //                              direction       =>              which quadrant to augment:
567 //                                                                              0 - right
568 //                                                                              1 - top
569 //                                                                              2 - bottom
570 //                                                                              3 - left
571 //
572 void hud_augment_shield_quadrant(object *objp, int direction)
573 {
574         float   full_shields, xfer_amount, energy_avail, percent_to_take, delta;
575         float   max_quadrant_val;
576         int     i;
577
578         Assert(direction >= 0 && direction < 4);
579         Assert(objp->type == OBJ_SHIP);
580         full_shields = Ship_info[Ships[objp->instance].ship_info_index].shields;
581         
582         xfer_amount = full_shields * SHIELD_TRANSFER_PERCENT;
583         max_quadrant_val = full_shields/4.0f;
584
585         if ( (objp->shields[direction] + xfer_amount) > max_quadrant_val )
586                 xfer_amount = max_quadrant_val - objp->shields[direction];
587
588         Assert(xfer_amount >= 0);
589         if ( xfer_amount == 0 ) {
590                 // TODO: provide a feedback sound
591                 return;
592         }
593         else {
594                 snd_play( &Snds[SND_SHIELD_XFER_OK] );
595         }
596
597         energy_avail = 0.0f;
598         for ( i = 0; i < MAX_SHIELD_SECTIONS; i++ ) {
599                 if ( i == direction )
600                         continue;
601                 energy_avail += objp->shields[i];
602         }
603
604         percent_to_take = xfer_amount/energy_avail;
605         if ( percent_to_take > 1.0f )
606                 percent_to_take = 1.0f;
607
608         for ( i = 0; i < MAX_SHIELD_SECTIONS; i++ ) {
609                 if ( i == direction )
610                         continue;
611                 delta = percent_to_take * objp->shields[i];
612                 objp->shields[i] -= delta;
613                 Assert(objp->shields[i] >= 0 );
614                 objp->shields[direction] += delta;
615                 if ( objp->shields[direction] > max_quadrant_val )
616                         objp->shields[direction] = max_quadrant_val;
617         }
618 }
619
620 // Try to find a match between filename and the names inside
621 // of Hud_shield_filenames.  This will provide us with an 
622 // association of ship class to shield icon information.
623 void hud_shield_assign_info(ship_info *sip, char *filename)
624 {
625         ubyte i;
626
627         for ( i = 0; i < Hud_shield_filename_count; i++ ) {
628                 if ( !stricmp(filename, Hud_shield_filenames[i]) ) {
629                         sip->shield_icon_index = i;
630                 }
631         }               
632 }
633
634 void hud_show_mini_ship_integrity(object *objp, int x_force, int y_force)
635 {
636         char    text_integrity[64];
637         int     numeric_integrity;
638         float p_target_integrity,initial_hull;
639         int     nx, ny;
640
641         initial_hull = Ship_info[Ships[objp->instance].ship_info_index].initial_hull_strength;
642         if (  initial_hull <= 0 ) {
643                 Int3(); // illegal initial hull strength
644                 p_target_integrity = 0.0f;
645         } else {
646                 p_target_integrity = objp->hull_strength / initial_hull;
647                 if (p_target_integrity < 0){
648                         p_target_integrity = 0.0f;
649                 }
650         }
651
652         numeric_integrity = fl2i(p_target_integrity*100 + 0.5f);
653         if(numeric_integrity > 100){
654                 numeric_integrity = 100;
655         }
656         // Assert(numeric_integrity <= 100);
657
658         // base coords
659         nx = (x_force == -1) ? Hud_mini_base[gr_screen.res][0] : x_force;
660         ny = (y_force == -1) ? Hud_mini_base[gr_screen.res][1] : y_force;
661
662         // 3 digit hull strength
663         if ( numeric_integrity == 100 ) {
664                 nx += Hud_mini_3digit[gr_screen.res][2];
665         } 
666         // 2 digit hull strength
667         else if ( numeric_integrity < 10 ) {
668                 nx += Hud_mini_1digit[gr_screen.res][2];                
669         }
670         // 1 digit hull strength
671         else {
672                 nx += Hud_mini_2digit[gr_screen.res][2];                
673         }       
674
675         if ( numeric_integrity == 0 ) {
676                 if ( p_target_integrity > 0 ) {
677                         numeric_integrity = 1;
678                 }
679         }
680
681         nx += fl2i( HUD_offset_x );
682         ny += fl2i( HUD_offset_y );
683
684         sprintf(text_integrity, "%d", numeric_integrity);
685         if ( numeric_integrity < 100 ) {
686                 hud_num_make_mono(text_integrity);
687         }       
688
689         gr_string(nx, ny, text_integrity);
690 }
691
692 // Draw the miniature shield icon that is drawn near the reticle
693 void hud_shield_show_mini(object *objp, int x_force, int y_force, int x_hull_offset, int y_hull_offset)
694 {
695         float                   max_shield;
696         int                     hud_color_index, range, frame_offset;
697         int                     sx, sy, i;
698         ship                    *sp;
699         ship_info       *sip;
700         shield_hit_info *shi;
701
702         shi = &Shield_hit_data[SHIELD_HIT_TARGET];
703
704         if ( objp->type != OBJ_SHIP ) {
705                 return;
706         }
707
708         sp = &Ships[objp->instance];
709         sip = &Ship_info[sp->ship_info_index];
710
711         hud_set_gauge_color(HUD_TARGET_MINI_ICON);
712
713         if (!Shield_mini_loaded)
714                 return;
715
716         sx = (x_force == -1) ? Shield_mini_coords[gr_screen.res][0]+fl2i(HUD_offset_x) : x_force;
717         sy = (y_force == -1) ? Shield_mini_coords[gr_screen.res][1]+fl2i(HUD_offset_y) : y_force;
718
719         // draw the ship first
720         hud_shield_maybe_flash(HUD_TARGET_MINI_ICON, SHIELD_HIT_TARGET, HULL_HIT_OFFSET);
721         hud_show_mini_ship_integrity(objp,x_force + x_hull_offset,y_force + y_hull_offset);
722
723         // draw the four quadrants
724         // Draw shield quadrants at one of NUM_SHIELD_LEVELS
725         max_shield = sip->shields/4.0f;
726
727         for ( i = 0; i < 4; i++ ) {
728
729                 if ( objp->flags & OF_NO_SHIELDS ) {
730                         break;
731                 }
732
733                 if ( objp->shields[Quadrant_xlate[i]] < 0.1f ) {
734                         continue;
735                 }
736
737                 if ( hud_shield_maybe_flash(HUD_TARGET_MINI_ICON, SHIELD_HIT_TARGET, i) ) {
738                         frame_offset = i+4;
739                 } else {
740                         frame_offset = i;
741                 }
742                                 
743                 range = HUD_color_alpha;
744                 hud_color_index = fl2i( (objp->shields[Quadrant_xlate[i]] / max_shield) * range + 0.5);
745                 Assert(hud_color_index >= 0 && hud_color_index <= range);
746         
747                 if ( hud_color_index < 0 ) {
748                         hud_color_index = 0;
749                 }
750                 if ( hud_color_index >= HUD_NUM_COLOR_LEVELS ) {
751                         hud_color_index = HUD_NUM_COLOR_LEVELS - 1;
752                 }
753
754                 if ( hud_gauge_maybe_flash(HUD_TARGET_MINI_ICON) == 1) {
755                         // hud_set_bright_color();
756                         hud_set_gauge_color(HUD_TARGET_MINI_ICON, HUD_C_BRIGHT);
757                 } else {
758                         // gr_set_color_fast(&HUD_color_defaults[hud_color_index]);
759                         hud_set_gauge_color(HUD_TARGET_MINI_ICON, hud_color_index);
760                 }                                        
761
762                 GR_AABITMAP(Shield_mini_gauge.first_frame + frame_offset, sx, sy);              
763         }
764         
765         // hud_set_default_color();
766 }
767
768 // reset the shield_hit_info data structure
769 void shield_info_reset(shield_hit_info *shi)
770 {
771         int i;
772
773         shi->shield_hit_status = 0;
774         shi->shield_show_bright = 0;
775         for ( i = 0; i < NUM_SHIELD_HIT_MEMBERS; i++ ) {
776                 shi->shield_hit_timers[i] = 1;
777                 shi->shield_hit_next_flash[i] = 1;
778         }
779 }
780
781 // reset the timers and hit flags for the shield gauges
782 //
783 // This needs to be called whenever the player selects a new target
784 //
785 // input:       player  =>      optional parameter (default value 0).  This is to indicate that player shield hit
786 //                                                              info should be reset.  This is normally not the case.
787 //                                                              is for the player's current target
788 void hud_shield_hit_reset(int player)
789 {
790         shield_hit_info *shi;
791
792         if (player) {
793                 shi = &Shield_hit_data[SHIELD_HIT_PLAYER];
794         } else {
795                 shi = &Shield_hit_data[SHIELD_HIT_TARGET];
796         }
797
798         shield_info_reset(shi);
799 }
800
801 // called once per frame to update the state of Shield_hit_status based on the Shield_hit_timers[]
802 void hud_shield_hit_update()
803 {
804         int i, j, limit;                
805
806         limit = 1;
807         if ( Player_ai->target_objnum >= 0 ) {
808                 limit = 2;
809         }
810
811         for ( i = 0; i < limit; i++ ) {
812                 for ( j = 0; j < NUM_SHIELD_HIT_MEMBERS; j++ ) {
813                         if ( timestamp_elapsed(Shield_hit_data[i].shield_hit_timers[j]) ) {
814                                 Shield_hit_data[i].shield_hit_status &= ~(1<<j);
815                                 Shield_hit_data[i].shield_show_bright &= ~(1<<j);
816                         }
817                 }
818         }
819 }
820
821 // called when a shield quadrant is struct, so we can update the timer that will draw the quadrant
822 // as flashing
823 //
824 // input:
825 //                              objp            =>      object pointer for ship that has been hit
826 //                              quadrant        => quadrant of shield getting hit (-1 if no shield is present)
827 void hud_shield_quadrant_hit(object *objp, int quadrant)
828 {
829         shield_hit_info *shi;
830         int                                     num;
831
832         if ( objp->type != OBJ_SHIP )
833                 return;
834
835         hud_escort_ship_hit(objp, quadrant);
836         hud_gauge_popup_start(HUD_TARGET_MINI_ICON);
837
838         if ( OBJ_INDEX(objp) == Player_ai->target_objnum ) {
839                 shi = &Shield_hit_data[SHIELD_HIT_TARGET];
840         } else if ( objp == Player_obj ) {
841                 shi = &Shield_hit_data[SHIELD_HIT_PLAYER];
842         } else {
843                 return;
844         }
845
846         if ( quadrant >= 0 ) {
847                 num = Quadrant_xlate[quadrant];
848                 shi->shield_hit_timers[num] = timestamp(300);
849         } else {
850                 shi->shield_hit_timers[HULL_HIT_OFFSET] = timestamp(SHIELD_HIT_DURATION_SHORT);
851                 hud_targetbox_start_flash(TBOX_FLASH_HULL);
852         }
853 }
854
855
856 void hudshield_page_in()
857 {
858         bm_page_in_aabitmap( Shield_mini_gauge.first_frame, Shield_mini_gauge.num_frames );
859 }
860