]> icculus.org git repositories - taylor/freespace2.git/blob - src/hud/hudets.cpp
Initial revision
[taylor/freespace2.git] / src / hud / hudets.cpp
1 /*
2  * $Logfile: /Freespace2/code/Hud/HUDets.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * C file that contains code to manage and display the Energy Transfer System (ETS)
8  *
9  * $Log$
10  * Revision 1.1  2002/05/03 03:28:09  root
11  * Initial revision
12  *
13  * 
14  * 14    10/28/99 11:17p Jefff
15  * nudged German "A" on ETS
16  * 
17  * 13    10/26/99 11:02p Jefff
18  * changed german ets labels
19  * 
20  * 12    9/05/99 11:23p Jimb
21  * changed weapon and shield recharge rates for skill levels
22  * 
23  * 11    9/03/99 2:28p Mikek
24  * Slightly increase rate of weapon recharge at Medium and Hard.  Slightly
25  * decrease at Easy.
26  * 
27  * 10    8/01/99 12:39p Dave
28  * Added HUD contrast control key (for nebula).
29  * 
30  * 9     6/10/99 3:43p Dave
31  * Do a better job of syncing text colors to HUD gauges.
32  * 
33  * 8     2/23/99 8:11p Dave
34  * Tidied up dogfight mode. Fixed TvT ship type problems for alpha wing.
35  * Small pass over todolist items.
36  * 
37  * 7     1/07/99 9:06a Jasen
38  * coords updated
39  * 
40  * 6     12/28/98 3:17p Dave
41  * Support for multiple hud bitmap filenames for hi-res mode.
42  * 
43  * 5     12/21/98 5:02p Dave
44  * Modified all hud elements to be multi-resolution friendly.
45  * 
46  * 4     11/05/98 4:18p Dave
47  * First run nebula support. Beefed up localization a bit. Removed all
48  * conditional compiles for foreign versions. Modified mission file
49  * format.
50  * 
51  * 3     10/13/98 9:28a Dave
52  * Started neatening up freespace.h. Many variables renamed and
53  * reorganized. Added AlphaColors.[h,cpp]
54  * 
55  * 2     10/07/98 10:53a Dave
56  * Initial checkin.
57  * 
58  * 1     10/07/98 10:49a Dave
59  * 
60  * 60    8/28/98 3:28p Dave
61  * EMP effect done. AI effects may need some tweaking as required.
62  * 
63  * 59    8/25/98 1:48p Dave
64  * First rev of EMP effect. Player side stuff basically done. Next comes
65  * AI code.
66  * 
67  * 58    6/19/98 3:49p Lawrance
68  * localization tweaks
69  * 
70  * 57    4/07/98 1:53p Lawrance
71  * Make energy system matter more.
72  * 
73  * 56    3/26/98 5:26p John
74  * added new paging code. nonfunctional.
75  * 
76  * 55    2/23/98 6:49p Lawrance
77  * Use gr_aabitmap_ex() instead of clipping regions
78  * 
79  * 54    2/22/98 4:17p John
80  * More string externalization classification... 190 left to go!
81  * 
82  * 53    2/12/98 4:58p Lawrance
83  * Change to new flashing method.
84  * 
85  * 52    1/19/98 11:37p Lawrance
86  * Fixing Optimization build warnings
87  * 
88  * 51    1/05/98 9:38p Lawrance
89  * Implement flashing HUD gauges.
90  * 
91  * 50    1/02/98 9:10p Lawrance
92  * Big changes to how colors get set on the HUD.
93  * 
94  * 49    12/30/97 4:28p Lawrance
95  * remove .ani extensions from filenames
96  * 
97  * 48    12/01/97 12:27a Lawrance
98  * redo default alpha color for HUD, make it easy to modify in the future
99  * 
100  * 47    11/15/97 6:10p Lawrance
101  * make ship speed less dependant on engine damage
102  * 
103  * 46    11/11/97 12:58a Lawrance
104  * deal with situation where ship has no shields
105  * 
106  * 45    11/10/97 2:58p Lawrance
107  * fix bug that was preventing engine damage from affecting AI ships
108  * 
109  * 44    11/09/97 3:25p Lawrance
110  * increase default alpha color
111  * 
112  * 43    11/06/97 10:32a Lawrance
113  * brighten up energy management bars
114  * 
115  * 42    11/05/97 11:19p Lawrance
116  * implement new ETS gauge
117  * 
118  * 41    11/05/97 4:04p Lawrance
119  * engine speed not affected on Trainee ONLY for player ship
120  * 
121  * 40    10/28/97 3:35p Lawrance
122  * subsystems will not be affected when playing on lowest skill level
123  *
124  * $NoKeywords: $
125  */
126
127
128 #include "hudets.h"
129 #include "hud.h"
130 #include "ship.h"
131 #include "freespace.h"
132 #include "player.h"
133 #include "2d.h"
134 #include "timer.h"
135 #include "sound.h"
136 #include "gamesnd.h"
137 #include "bmpman.h"
138 #include "emp.h"
139 #include "localize.h"
140         
141 #define ENERGY_DIVERT_DELTA                             0.2f    // percentage of energy transferred in a shield->weapon or weapon->shield energy transfer
142 #define INTIAL_SHIELD_RECHARGE_INDEX    4               // default shield charge rate (index in Energy_levels[])
143 #define INTIAL_WEAPON_RECHARGE_INDEX    4               // default weapon charge rate (index in Energy_levels[])
144 #define INTIAL_ENGINE_RECHARGE_INDEX    4               // default engine charge rate (index in Energy_levels[])
145
146 #define MAX_SHIELD_REGEN_PER_SECOND             0.02f   //      max percent/100 of shield energy regenerated per second
147 #define MAX_WEAPON_REGEN_PER_SECOND             0.04f   // max percent/100 of weapon energy regenerated per second
148
149 #define NUM_ENERGY_LEVELS       13              
150 #define MAX_ENERGY_INDEX        (NUM_ENERGY_LEVELS - 1)
151 float Energy_levels[NUM_ENERGY_LEVELS] = {0.0f,  0.0833f, 0.167f, 0.25f, 0.333f, 0.417f, 0.5f, 0.583f, 0.667f, 0.75f, 0.833f, 0.9167f, 1.0f};
152
153 #define AI_MODIFY_ETS_INTERVAL 500      // time between ets modifications for ai's (in milliseconds)
154
155 int Weapon_energy_cheat = 0;
156
157 // skill level scaling of the amount of energy that is allocated to the weapons and 
158 // shields
159 float   Skill_level_weapon_energy_scale[NUM_SKILL_LEVELS] = {10.0f, 4.0f, 2.5f, 2.0f, 1.5f};
160 float Skill_level_shield_energy_scale[NUM_SKILL_LEVELS] = {4.0f, 2.0f, 1.5f, 1.25f, 1.0f};
161
162 #define ZERO_INDEX                      0
163 #define ONE_THIRD_INDEX         4
164 #define ONE_HALF_INDEX          6
165 #define ALL_INDEX                               12
166
167 #define HAS_ENGINES                     (1<<0)
168 #define HAS_SHIELDS                     (1<<1)
169 #define HAS_WEAPONS                     (1<<2)
170
171 int ETS_bar_h[GR_NUM_RESOLUTIONS] = {
172         41,
173         41
174 };
175
176 typedef struct ets_gauge_info
177 {
178         char    letter;
179         int     letter_coords[2];
180         int     top_coords[2];
181         int     bottom_coords[2];
182 } ets_gauge_info;
183
184 ets_gauge_info Ets_gauge_info_german[GR_NUM_RESOLUTIONS][3] =
185 {
186         { // GR_640
187                 'G', {525,422}, {523,380}, {523,430},
188                 'S', {542,422}, {540,380}, {540,430},
189                 'A', {559,422}, {557,380}, {557,430}
190         },
191         { // GR_1024
192                 'G', {882,690}, {880,648}, {880,698},
193                 'S', {900,690}, {898,648}, {898,698},
194                 'A', {917,690}, {916,648}, {916,698}
195         }
196 };
197 ets_gauge_info Ets_gauge_info_french[GR_NUM_RESOLUTIONS][3] =
198 {
199         { // GR_640
200                 'C', {525,422}, {523,380}, {523,430},
201                 'B', {542,422}, {540,380}, {540,430},
202                 'M', {560,422}, {557,380}, {557,430}
203         }, 
204         { // GR_1024
205                 'C', {882,690}, {880,648}, {880,698},
206                 'B', {900,690}, {898,648}, {898,698},
207                 'M', {918,690}, {916,648}, {916,698}
208         },
209 };
210 ets_gauge_info Ets_gauge_info_english[GR_NUM_RESOLUTIONS][3] =
211 {
212         { // GR_640
213                 'G', {525,422}, {523,380}, {523,430},
214                 'S', {542,422}, {540,380}, {540,430},
215                 'E', {560,422}, {557,380}, {557,430}
216         },
217         { // GR_1024
218                 'G', {882,690}, {880,648}, {880,698},
219                 'S', {900,690}, {898,648}, {898,698},
220                 'E', {918,690}, {916,648}, {916,698}
221         }
222 };
223 ets_gauge_info *Ets_gauge_info = NULL;
224
225 char Ets_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = {
226         "energy1",
227         "energy1"
228 };
229
230 hud_frames Ets_gauge;
231
232 static int Hud_ets_inited = 0;
233
234 void hud_init_ets()
235 {
236         if ( Hud_ets_inited )
237                 return;
238
239         Ets_gauge.first_frame = bm_load_animation(Ets_fname[gr_screen.res], &Ets_gauge.num_frames);
240         if ( Ets_gauge.first_frame < 0 ) {
241                 Warning(LOCATION,"Cannot load hud ani: Ets_fname[gr_screen.res]\n");
242         }
243
244         if(Lcl_gr){
245                 Ets_gauge_info = Ets_gauge_info_german[gr_screen.res];
246         } else if(Lcl_fr){
247                 Ets_gauge_info = Ets_gauge_info_french[gr_screen.res];
248         } else {
249                 Ets_gauge_info = Ets_gauge_info_english[gr_screen.res];
250         }
251         
252         Hud_ets_inited = 1;
253 }
254
255 // -------------------------------------------------------------------------------------------------
256 // ets_init_ship() is called by a ship when it is created (effectively, for every ship at the start
257 // of a mission).  This will set the default charge rates for the different systems and initalialize
258 // the weapon energy reserve.
259 //
260 void ets_init_ship(object* obj)
261 {
262         ship* sp;
263
264         // fred should bail here
265         if(Fred_running){
266                 return;
267         }
268
269         Assert(obj->type == OBJ_SHIP);
270         sp = &Ships[obj->instance];
271         
272         sp->weapon_energy = Ship_info[sp->ship_info_index].max_weapon_reserve;
273         sp->next_manage_ets = timestamp(AI_MODIFY_ETS_INTERVAL);
274         set_default_recharge_rates(obj);
275 }
276
277 // -------------------------------------------------------------------------------------------------
278 // update_ets() is called once per frame for every OBJ_SHIP in the game.  The amount of energy
279 // to send to the weapons and shields is calculated, and the top ship speed is calculated.  The
280 // amount of time elapsed from the previous call is passed in as the parameter fl_frametime.
281 //
282 // parameters:   obj          ==> object that is updating their energy system
283 //               fl_frametime ==> game frametime (in seconds)
284 //
285 void update_ets(object* objp, float fl_frametime)
286 {
287         float max_new_shield_energy, max_new_weapon_energy, _ss;
288
289         if ( fl_frametime <= 0 ){
290                 return;
291         }
292
293         ship* ship_p = &Ships[objp->instance];
294         ship_info* sinfo_p = &Ship_info[ship_p->ship_info_index];
295
296         if ( ship_p->flags & SF_DYING ){
297                 return;
298         }
299
300         if ( sinfo_p->power_output == 0 ){
301                 return;
302         }
303
304 //      new_energy = fl_frametime * sinfo_p->power_output;
305
306         // update weapon energy
307         max_new_weapon_energy = fl_frametime * MAX_WEAPON_REGEN_PER_SECOND * sinfo_p->max_weapon_reserve;
308         if ( objp->flags & OF_PLAYER_SHIP ) {
309                 ship_p->weapon_energy += Energy_levels[ship_p->weapon_recharge_index] * max_new_weapon_energy * Skill_level_weapon_energy_scale[Game_skill_level];
310         } else {
311                 ship_p->weapon_energy += Energy_levels[ship_p->weapon_recharge_index] * max_new_weapon_energy;
312         }
313
314         if ( ship_p->weapon_energy > sinfo_p->max_weapon_reserve ){
315                 ship_p->weapon_energy = sinfo_p->max_weapon_reserve;
316         }
317
318         float shield_delta;
319         max_new_shield_energy = fl_frametime * MAX_SHIELD_REGEN_PER_SECOND * sinfo_p->shields;
320         if ( objp->flags & OF_PLAYER_SHIP ) {
321                 shield_delta = Energy_levels[ship_p->shield_recharge_index] * max_new_shield_energy * Skill_level_shield_energy_scale[Game_skill_level];
322         } else {
323                 shield_delta = Energy_levels[ship_p->shield_recharge_index] * max_new_shield_energy;
324         }
325
326         add_shield_strength(objp, shield_delta);
327
328         if ( (_ss = get_shield_strength(objp)) > sinfo_p->shields ){
329                 for (int i=0; i<MAX_SHIELD_SECTIONS; i++){
330                         objp->shields[i] *= sinfo_p->shields/ _ss;
331                 }
332         }
333
334         // calculate the top speed of the ship based on the energy flow to engines
335         float y = Energy_levels[ship_p->engine_recharge_index];
336
337         // check for a shortcuts first before doing linear interpolation
338         if ( y == Energy_levels[INTIAL_ENGINE_RECHARGE_INDEX] ){
339                 ship_p->current_max_speed = sinfo_p->max_speed;
340         } else if ( y == 0.0f ){
341                 ship_p->current_max_speed = 0.5f * sinfo_p->max_speed;
342         } else if ( y == 1.0f ){
343                 ship_p->current_max_speed = sinfo_p->max_overclocked_speed;
344         } else {
345                 // do a linear interpolation to find the current max speed, using points (0,1/2 default_max_speed) (.333,default_max_speed)
346                 // x = x1 + (y-y1) * (x2-x1) / (y2-y1);
347                 if ( y < Energy_levels[INTIAL_ENGINE_RECHARGE_INDEX] ){
348                         ship_p->current_max_speed =  0.5f*sinfo_p->max_speed + (y  * (0.5f*sinfo_p->max_speed) ) / Energy_levels[INTIAL_ENGINE_RECHARGE_INDEX];
349                 } else {
350                         // do a linear interpolation to find the current max speed, using points (.333,default_max_speed) (1,max_overclock_speed)
351                         ship_p->current_max_speed = sinfo_p->max_speed + (y - Energy_levels[INTIAL_ENGINE_RECHARGE_INDEX]) * (sinfo_p->max_overclocked_speed - sinfo_p->max_speed) / (1.0f - Energy_levels[INTIAL_ENGINE_RECHARGE_INDEX]);
352                 }
353         }
354
355         // AL 11-15-97: Rules for engine strength affecting max speed:
356         //                                              1. if strength >= 0.5 no affect 
357         //                                              2. if strength < 0.5 then max_speed = sqrt(strength)
358         //                                       
359         //                                       This will translate to 71% max speed at 50% engines, and 31% max speed at 10% engines
360         //
361         float strength = ship_get_subsystem_strength(ship_p, SUBSYSTEM_ENGINE);
362
363         // don't let engine strength affect max speed when playing on lowest skill level
364         if ( (objp != Player_obj) || (Game_skill_level > 0) ) {
365                 if ( strength < 0.5 ) {
366                         ship_p->current_max_speed *= fl_sqrt(strength);
367                 }
368         }
369
370         if ( timestamp_elapsed(ship_p->next_manage_ets) ) {
371                 if ( !(objp->flags & OF_PLAYER_SHIP) ) {
372                         ai_manage_ets(objp);
373                         ship_p->next_manage_ets = timestamp(AI_MODIFY_ETS_INTERVAL);
374                 }
375                 else {
376                         if ( Weapon_energy_cheat ){
377                                 ship_p->weapon_energy = sinfo_p->max_weapon_reserve;
378                         }
379                 }
380         }
381 }
382
383
384 // -------------------------------------------------------------------------------------------------
385 // ai_manage_ets() will determine if a ship should modify it's energy transfer percentages, or 
386 // transfer energy from shields->weapons or from weapons->shields
387 //
388
389 // minimum level rule constants
390 #define SHIELDS_MIN_LEVEL_PERCENT       0.3f
391 #define WEAPONS_MIN_LEVEL_PERCENT       0.3f
392
393 // maximum level rule constants
394 #define SHIELDS_MAX_LEVEL_PERCENT       0.8f
395 #define WEAPONS_MAX_LEVEL_PERCENT       0.8f
396
397 // emergency rule constants
398 #define SHIELDS_EMERG_LEVEL_PERCENT     0.10f
399 #define WEAPONS_EMERG_LEVEL_PERCENT     0.05f
400
401 // need this, or ai's tend to totally eliminate engine power!
402 #define MIN_ENGINE_RECHARGE_INDEX       3
403
404 #define DEFAULT_CHARGE_INDEX                    4
405 #define NORMAL_TOLERANCE_PERCENT                .10f
406
407 void ai_manage_ets(object* obj)
408 {
409         ship* ship_p = &Ships[obj->instance];
410         ship_info* ship_info_p = &Ship_info[ship_p->ship_info_index];
411
412         if ( ship_info_p->power_output == 0 )
413                 return;
414
415         if (ship_p->flags & SF_DYING)
416                 return;
417
418         // check if any of the three systems are not being used.  If so, don't allow energy management.
419         if ( !ship_info_p->shields || !ship_info_p->max_speed || !ship_info_p->max_weapon_reserve)
420                 return;
421
422         float shield_left_percent = get_shield_strength(obj)/ship_info_p->shields;
423         float weapon_left_percent = ship_p->weapon_energy/ship_info_p->max_weapon_reserve;
424
425         // maximum level check
426         //      MK, changed these, might as well let them go up to 100% if nothing else needs the recharge ability.
427         if ( weapon_left_percent == 1.0f) {
428                 decrease_recharge_rate(obj, WEAPONS);
429         }
430
431         if (!(obj->flags & OF_NO_SHIELDS) && (shield_left_percent == 1.0f)) {
432                 decrease_recharge_rate(obj, SHIELDS);
433         }
434
435         // minimum check
436
437         if (!(obj->flags & OF_NO_SHIELDS) && (shield_left_percent < SHIELDS_MIN_LEVEL_PERCENT)) {
438                 if ( weapon_left_percent > WEAPONS_MIN_LEVEL_PERCENT )
439                         increase_recharge_rate(obj, SHIELDS);
440         }
441
442         if ( weapon_left_percent < WEAPONS_MIN_LEVEL_PERCENT ) {
443                 increase_recharge_rate(obj, WEAPONS);
444         }
445
446         if ( ship_p->engine_recharge_index < MIN_ENGINE_RECHARGE_INDEX ) {
447                 increase_recharge_rate(obj, ENGINES);
448         }
449
450         // emergency check
451         if (!(obj->flags & OF_NO_SHIELDS)) {
452                 if ( shield_left_percent < SHIELDS_EMERG_LEVEL_PERCENT ) {
453                         if (ship_p->target_shields_delta == 0.0f)
454                                 transfer_energy_to_shields(obj);
455                 } else if ( weapon_left_percent < WEAPONS_EMERG_LEVEL_PERCENT ) {
456                         if ( shield_left_percent > SHIELDS_MIN_LEVEL_PERCENT || weapon_left_percent <= 0.01 )   // dampen ai enthusiasm for sucking energy to weapons
457                                 transfer_energy_to_weapons(obj);
458                 }
459
460         
461                 // check for return to normal values
462                 if ( fl_abs( shield_left_percent - 0.5f ) < NORMAL_TOLERANCE_PERCENT ) {
463                         if ( ship_p->shield_recharge_index > DEFAULT_CHARGE_INDEX )
464                                 decrease_recharge_rate(obj, SHIELDS);
465                         else if ( ship_p->shield_recharge_index < DEFAULT_CHARGE_INDEX )
466                                 increase_recharge_rate(obj, SHIELDS);
467                 }
468         }
469
470
471         if ( fl_abs( weapon_left_percent - 0.5f ) < NORMAL_TOLERANCE_PERCENT ) {
472                 if ( ship_p->weapon_recharge_index > DEFAULT_CHARGE_INDEX )
473                         decrease_recharge_rate(obj, WEAPONS);
474                 else if ( ship_p->weapon_recharge_index < DEFAULT_CHARGE_INDEX )
475                         increase_recharge_rate(obj, WEAPONS);
476         }
477 }
478
479 // -------------------------------------------------------------------------------------------------
480 // hud_show_ets() will display the charge rates for the three systems, and the reserve
481 // energy for shields and weapons.  hud_show_ets() is called once per frame.
482 //
483 void hud_show_ets()
484 {
485         int i, j, index, y_start, y_end, clip_h, w, h, x, y;
486
487         ship* ship_p = &Ships[Player_obj->instance];    
488
489         if ( Ets_gauge.first_frame < 0 ) {
490                 return;
491         }
492
493         hud_set_gauge_color(HUD_ETS_GAUGE);
494
495         // draw the letters for the gauges first, before any clipping occurs
496         i = 0;
497         for ( j = 0; j < 3; j++ ) {
498                 if ( j == 1 && Player_obj->flags & OF_NO_SHIELDS ) {
499                         continue;
500                 }
501                 Assert(Ets_gauge_info != NULL);
502                 gr_printf(Ets_gauge_info[i].letter_coords[0], Ets_gauge_info[i].letter_coords[1], NOX("%c"), Ets_gauge_info[j].letter); 
503                 i++;
504         }
505
506         // draw the three energy gauges
507         i = 0;
508         index = 0;
509         for ( j = 0; j < 3; j++ ) {
510                 switch (j) {
511                 case 0:
512                         index = ship_p->weapon_recharge_index;
513                         break;
514                 case 1:
515                         index = ship_p->shield_recharge_index;
516                         if ( Player_obj->flags & OF_NO_SHIELDS ) {
517                                 continue;
518                         }
519                         break;
520                 case 2:
521                         index = ship_p->engine_recharge_index;
522                         break;
523                 }
524
525                 clip_h = fl2i( (1 - Energy_levels[index]) * ETS_bar_h[gr_screen.res] );
526
527                 bm_get_info(Ets_gauge.first_frame,&w,&h);
528
529                 if ( index < NUM_ENERGY_LEVELS-1 ) {
530                         // some portion of dark needs to be drawn
531
532                         hud_set_gauge_color(HUD_ETS_GAUGE);
533
534                         // draw the top portion
535
536                         Assert(Ets_gauge_info != NULL);
537                         x = Ets_gauge_info[i].top_coords[0];
538                         y = Ets_gauge_info[i].top_coords[1];
539                         
540                         GR_AABITMAP_EX(Ets_gauge.first_frame,x,y,w,clip_h,0,0);                 
541
542                         // draw the bottom portion
543                         Assert(Ets_gauge_info != NULL);
544                         x = Ets_gauge_info[i].bottom_coords[0];
545                         y = Ets_gauge_info[i].bottom_coords[1];
546
547                         y_start = y + (ETS_bar_h[gr_screen.res] - clip_h);
548                         y_end = y + ETS_bar_h[gr_screen.res];
549                         
550                         GR_AABITMAP_EX(Ets_gauge.first_frame, x, y_start, w, y_end-y_start, 0, ETS_bar_h[gr_screen.res]-clip_h);                        
551                 }
552
553                 if ( index > 0 ) {
554                         if ( hud_gauge_maybe_flash(HUD_ETS_GAUGE) == 1 ) {
555                                 hud_set_gauge_color(HUD_ETS_GAUGE, HUD_C_DIM);
556                                 // hud_set_dim_color();
557                         } else {
558                                 hud_set_gauge_color(HUD_ETS_GAUGE, HUD_C_BRIGHT);
559                                 // hud_set_bright_color();
560                         }
561                         // some portion of recharge needs to be drawn
562
563                         // draw the top portion
564                         Assert(Ets_gauge_info != NULL);
565                         x = Ets_gauge_info[i].top_coords[0];
566                         y = Ets_gauge_info[i].top_coords[1];
567
568                         y_start = y + clip_h;
569                         y_end = y + ETS_bar_h[gr_screen.res];
570                         
571                         GR_AABITMAP_EX(Ets_gauge.first_frame+1, x, y_start, w, y_end-y_start, 0, clip_h);                       
572
573                         // draw the bottom portion
574                         Assert(Ets_gauge_info != NULL);
575                         x = Ets_gauge_info[i].bottom_coords[0];
576                         y = Ets_gauge_info[i].bottom_coords[1];
577                         
578                         GR_AABITMAP_EX(Ets_gauge.first_frame+2, x,y,w,ETS_bar_h[gr_screen.res]-clip_h,0,0);                     
579                 }
580                 i++;
581         }
582
583         // hud_set_default_color();
584 }
585
586 // -------------------------------------------------------------------------------------------------
587 // set_default_recharge_rates() will set the charge levels for the weapons, shields and
588 // engines to their default levels
589 void set_default_recharge_rates(object* obj)
590 {
591         int ship_properties;
592
593         ship* ship_p = &Ships[obj->instance];
594         ship_info* ship_info_p = &Ship_info[ship_p->ship_info_index];
595
596         if ( ship_info_p->power_output == 0 )
597                 return;
598
599         ship_properties = 0;    
600         if ( ship_info_p->max_weapon_reserve )
601                 ship_properties |= HAS_WEAPONS;
602         
603         if (!(obj->flags & OF_NO_SHIELDS))
604                 ship_properties |= HAS_SHIELDS;
605
606         if ( ship_info_p->max_speed )
607                 ship_properties |= HAS_ENGINES;
608
609         // the default charge rate depends on what systems are on each ship
610         switch ( ship_properties ) {
611                 case HAS_ENGINES | HAS_WEAPONS | HAS_SHIELDS:
612                         ship_p->shield_recharge_index = INTIAL_SHIELD_RECHARGE_INDEX;
613                         ship_p->weapon_recharge_index = INTIAL_WEAPON_RECHARGE_INDEX;
614                         ship_p->engine_recharge_index = INTIAL_ENGINE_RECHARGE_INDEX;
615                         break;
616
617                 case HAS_ENGINES | HAS_SHIELDS:
618                         ship_p->shield_recharge_index = ONE_HALF_INDEX;
619                         ship_p->weapon_recharge_index = ZERO_INDEX;
620                         ship_p->engine_recharge_index = ONE_HALF_INDEX;
621                         break;
622
623                 case HAS_WEAPONS | HAS_SHIELDS:
624                         ship_p->shield_recharge_index = ONE_HALF_INDEX;
625                         ship_p->weapon_recharge_index = ONE_HALF_INDEX;
626                         ship_p->engine_recharge_index = ZERO_INDEX;
627                         break;
628
629                 case HAS_ENGINES | HAS_WEAPONS:
630                         ship_p->shield_recharge_index = ZERO_INDEX;
631                         ship_p->weapon_recharge_index = ONE_HALF_INDEX;
632                         ship_p->engine_recharge_index = ONE_HALF_INDEX;
633                         break;
634
635                 case HAS_SHIELDS:
636                         ship_p->shield_recharge_index = ALL_INDEX;
637                         ship_p->weapon_recharge_index = ZERO_INDEX;
638                         ship_p->engine_recharge_index = ZERO_INDEX;
639                         break;
640
641                 case HAS_ENGINES:
642                         ship_p->shield_recharge_index = ZERO_INDEX;
643                         ship_p->weapon_recharge_index = ZERO_INDEX;
644                         ship_p->engine_recharge_index = ALL_INDEX;
645                         break;
646
647                 case HAS_WEAPONS:
648                         ship_p->shield_recharge_index = ZERO_INDEX;
649                         ship_p->weapon_recharge_index = ALL_INDEX;
650                         ship_p->engine_recharge_index = ZERO_INDEX;
651                         break;
652
653                 default:
654                         Int3(); // if no systems, power output should be zero, and this funtion shouldn't be called
655                         break;
656         } // end switch
657 }
658
659 // -------------------------------------------------------------------------------------------------
660 // increase_recharge_rate() will increase the energy flow to the specified system (one of
661 // WEAPONS, SHIELDS or ENGINES).  The increase in energy will result in a decrease to
662 // the other two systems.
663 void increase_recharge_rate(object* obj, SYSTEM_TYPE ship_system) 
664 {
665         int     *gain_index=NULL, *lose_index1=NULL, *lose_index2=NULL, *tmp=NULL;
666         int     count=0;
667         ship    *ship_p = &Ships[obj->instance];
668
669         switch ( ship_system ) {
670                 case WEAPONS:
671                         gain_index = &ship_p->weapon_recharge_index;
672                         lose_index1 = &ship_p->engine_recharge_index;
673                         if ( obj->flags & OF_NO_SHIELDS ) {
674                                 lose_index2 = NULL;
675                         } else {
676                                 lose_index2 = &ship_p->shield_recharge_index;
677                         }
678                         break;
679
680                 case SHIELDS:
681                         if ( obj->flags & OF_NO_SHIELDS ) {
682                                 return;
683                         }
684                         gain_index = &ship_p->shield_recharge_index;
685                         lose_index1 = &ship_p->weapon_recharge_index;
686                         lose_index2 = &ship_p->engine_recharge_index;
687                         break;
688
689                 case ENGINES:
690                         gain_index = &ship_p->engine_recharge_index;
691                         lose_index1 = &ship_p->weapon_recharge_index;
692                         if ( obj->flags & OF_NO_SHIELDS ) {
693                                 lose_index2 = NULL;
694                         } else {
695                                 lose_index2 = &ship_p->shield_recharge_index;
696                         }
697                         break;
698
699         } // end switch
700
701         // already full, nothing to do 
702         count = MAX_ENERGY_INDEX - *gain_index;
703         if ( count > 2 ) 
704                 count = 2;
705
706         if ( count <= 0 ) {
707                 if ( obj == Player_obj ) {
708                         snd_play( &Snds[SND_ENERGY_TRANS_FAIL], 0.0f );
709                 }
710                 return;
711         }
712
713         *gain_index += count;
714
715         // ensure that the highest lose index takes the first decrease
716         if ( lose_index1 && lose_index2 ) {
717                 if ( *lose_index1 < *lose_index2 ) {
718                         tmp = lose_index1;
719                         lose_index1 = lose_index2;
720                         lose_index2 = tmp;
721                 }
722         }
723
724         int sanity = 0;
725         while(count > 0) {
726                 if ( lose_index1 && *lose_index1 > 0 ) {
727                         *lose_index1 -= 1;
728                         count--;
729                 }
730
731                 if ( count <= 0 ) 
732                         break;
733
734                 if ( lose_index2 && *lose_index2 > 0 ) {
735                         *lose_index2 -= 1;
736                         count--;
737                 }
738
739                 if ( sanity++ > 10 ) {
740                         Int3(); // get Alan
741                         break;
742                 }
743         }
744
745         if ( obj == Player_obj )
746                 snd_play( &Snds[SND_ENERGY_TRANS], 0.0f );
747 }
748
749 // -------------------------------------------------------------------------------------------------
750 // decrease_recharge_rate() will decrease the energy flow to the specified system (one of
751 // WEAPONS, SHIELDS or ENGINES).  The decrease in energy will result in an increase to
752 // the other two systems.
753 void decrease_recharge_rate(object* obj, SYSTEM_TYPE ship_system) 
754 {
755         int     *lose_index=NULL, *gain_index1=NULL, *gain_index2=NULL, *tmp=NULL;
756         int     count;
757         ship    *ship_p = &Ships[obj->instance];
758
759         switch ( ship_system ) {
760                 case WEAPONS:
761                         lose_index = &ship_p->weapon_recharge_index;
762
763                         if ( obj->flags & OF_NO_SHIELDS ) {
764                                 gain_index1 = NULL;
765                         } else {
766                                 gain_index1 = &ship_p->shield_recharge_index;
767                         }
768
769                         gain_index2 = &ship_p->engine_recharge_index;
770                         break;
771
772                 case SHIELDS:
773                         if ( obj->flags & OF_NO_SHIELDS ) {
774                                 return;
775                         }
776                         lose_index = &ship_p->shield_recharge_index;
777                         gain_index1 = &ship_p->weapon_recharge_index;
778                         gain_index2 = &ship_p->engine_recharge_index;
779                         break;
780
781                 case ENGINES:
782                         lose_index = &ship_p->engine_recharge_index;
783
784                         if ( obj->flags & OF_NO_SHIELDS ) {
785                                 gain_index1 = NULL;
786                         } else {
787                                 gain_index1 = &ship_p->shield_recharge_index;
788                         }
789
790                         gain_index2 = &ship_p->weapon_recharge_index;
791                         break;
792         } // end switch
793
794         // check how much there is to lose
795         count = min(2, *lose_index);
796         if ( count <= 0 ) {
797                 if ( obj == Player_obj ) {
798                         snd_play( &Snds[SND_ENERGY_TRANS_FAIL], 0.0f );
799                 }
800                 return;
801         }
802
803         *lose_index -= count;
804
805         // make sure that the gain starts with the system which needs it most
806         if ( gain_index1 && gain_index2 ) {
807                 if ( *gain_index1 > *gain_index2 ) {
808                         tmp = gain_index1;
809                         gain_index1 = gain_index2;
810                         gain_index2 = tmp;
811                 }
812         }
813
814         int sanity=0;
815         while(count > 0) {
816                 if ( gain_index1 && *gain_index1 < MAX_ENERGY_INDEX ) {
817                         *gain_index1 += 1;
818                         count--;
819                 }
820
821                 if ( count <= 0 ) 
822                         break;
823
824                 if ( gain_index2 && *gain_index2 < MAX_ENERGY_INDEX ) {
825                         *gain_index2 += 1;
826                         count--;
827                 }
828
829                 if ( sanity++ > 10 ) {
830                         Int3(); // get Alan
831                         break;
832                 }
833         }
834
835         if ( obj == Player_obj )
836                 snd_play( &Snds[SND_ENERGY_TRANS], 0.0f );
837 }
838
839 void transfer_energy_weapon_common(object *objp, float from_field, float to_field, float *from_delta, float *to_delta, float max, float scale)
840 {
841         float   delta;
842
843         delta = from_field * ENERGY_DIVERT_DELTA * scale;
844
845         if (to_field + *to_delta + delta > max)
846                 delta = max - to_field - *to_delta;
847
848         if ( delta > 0 ) {
849                 if ( objp == Player_obj )
850                         snd_play( &Snds[SND_ENERGY_TRANS], 0.0f );
851
852                 if (delta > from_field)
853                         delta = from_field;
854
855                 *to_delta += delta;
856                 *from_delta -= delta;
857         } else
858                 if ( objp == Player_obj )
859                         snd_play( &Snds[SND_ENERGY_TRANS_FAIL], 0.0f );
860 }
861
862 // -------------------------------------------------------------------------------------------------
863 // transfer_energy_to_shields() will transfer ENERGY_DIVERT_DELTA percent of weapon energy
864 // to shield energy.
865 void transfer_energy_to_shields(object* obj)
866 {
867         ship*                   ship_p = &Ships[obj->instance];
868         ship_info*      sinfo_p = &Ship_info[ship_p->ship_info_index];
869
870         if (ship_p->flags & SF_DYING)
871                 return;
872
873         if ( obj->flags & OF_NO_SHIELDS ) {
874                 return;
875         }
876
877         transfer_energy_weapon_common(obj, ship_p->weapon_energy, get_shield_strength(obj), &ship_p->target_weapon_energy_delta, &ship_p->target_shields_delta, sinfo_p->shields, 0.5f);
878 }
879
880 // -------------------------------------------------------------------------------------------------
881 // transfer_energy_to_weapons() will transfer ENERGY_DIVERT_DELTA percent of shield energy
882 // to weapon energy.
883 void transfer_energy_to_weapons(object* obj)
884 {
885         ship*                   ship_p = &Ships[obj->instance];
886         ship_info*      sinfo_p = &Ship_info[ship_p->ship_info_index];
887
888         if (ship_p->flags & SF_DYING)
889                 return;
890
891         transfer_energy_weapon_common(obj, get_shield_strength(obj), ship_p->weapon_energy, &ship_p->target_shields_delta, &ship_p->target_weapon_energy_delta, sinfo_p->max_weapon_reserve, 1.0f);
892 }
893
894 void hudets_page_in()
895 {
896         bm_page_in_aabitmap( Ets_gauge.first_frame, Ets_gauge.num_frames );
897 }