2 * $Logfile: /Freespace2/code/Weapon/Emp.cpp $
7 * Header file for managing corkscrew missiles
10 * Revision 1.1 2002/05/03 03:28:11 root
14 * 9 7/24/99 1:54p Dave
15 * Hud text flash gauge. Reworked dead popup to use 4 buttons in red-alert
18 * 8 7/02/99 4:31p Dave
19 * Much more sophisticated lightning support.
21 * 7 4/23/99 12:01p Johnson
24 * 6 2/08/99 9:34a Dave
25 * Put in a little insurance code to make sure EMP effect truly dies when
26 * a player dies (to prevent wacky dead cam action).
28 * 5 1/08/99 2:08p Dave
29 * Fixed software rendering for pofview. Super early support for AWACS and
32 * 4 11/05/98 5:55p Dave
33 * Big pass at reducing #includes
35 * 3 10/13/98 9:29a Dave
36 * Started neatening up freespace.h. Many variables renamed and
37 * reorganized. Added AlphaColors.[h,cpp]
39 * 2 10/07/98 10:54a Dave
42 * 1 10/07/98 10:51a Dave
44 * 3 8/28/98 3:29p Dave
45 * EMP effect done. AI effects may need some tweaking as required.
47 * 2 8/25/98 1:49p Dave
48 * First rev of EMP effect. Player side stuff basically done. Next comes
51 * 1 8/24/98 9:29a Dave
59 #include "multimsgs.h"
60 #include "multiutil.h"
61 #include "systemvars.h"
62 #include "freespace.h"
65 #include "hudtarget.h"
66 #include "hudgauges.h"
67 #include "missiongoals.h"
70 // ----------------------------------------------------------------------------------------------------
71 // EMP EFFECT DEFINES/VARS
74 // intensity of the playing effect
75 float Emp_intensity = -1.0f; // current intensity of the EMP effect (normalized to EMP_INTENSITY_MAX)
76 float Emp_decr = 0.0f; // how much to decrement the effect per second
78 // timestamp until we should randomly choose another target
79 int Emp_wacky_target_timestamp = -1;
81 // max time we'll disrupt turrets on big ships
82 #define MAX_TURRET_DISRUPT_TIME 7500
84 // conventient for determining if EMP is active
85 #define EMP_ACTIVE_LOCAL() (Emp_intensity > 0.0f)
87 // for keeping track of messed up text
88 #define EMP_WACKY_TEXT_LEN 256
89 typedef struct wacky_text {
90 char str[EMP_WACKY_TEXT_LEN];
93 wacky_text Emp_wacky_text[NUM_TEXT_STAMPS];
95 // for randomly inserting characters
96 #define NUM_RANDOM_CHARS 51
97 char Emp_random_char[NUM_RANDOM_CHARS] =
98 { 'a', 'b', 'c', 'd', 'e', 'f', 'g', '4', 'h', '8', '_', '$', ')', '-', '~', 'u', 'q',
99 '.', 'x', 'h', '&', '%', '*', '1', '3', 't', 'h', 'o', 'p', '@', 'h', 'i','v', '+', '=',
100 '|', '{', '}', ':', ';', '^', 'l', 'z', 'u', 'v', '<', '>', '?', '5', '8' };
102 // EMP EFFECTS ON PLAYERS -----
103 // 1.) Lose target lock if any, along with ability to lock on
104 // 2.) Display EMP-BLAST icon or something (maybe flash it)
105 // 3.) at wacky intervals, target random ships (which he cannot lock on)
106 // 4.) Randomly flicker HUD gauges
107 // 5.) Randomly swap/mess-up HUD text
109 // EMP EFFECTS ON SHIPS -------
110 // 1.) Lightning effect proportional to the emp effect
112 // ----------------------------------------------------------------------------------------------------
113 // EMP EFFECT FUNCTIONS
116 // maybe reformat a string
117 void emp_maybe_reformat_text(char *text, int max_len, int gauge_id);
119 // randomize the chars in a string
120 void emp_randomize_chars(char *str);
123 // initialize the EMP effect for the mission
124 void emp_level_init()
129 Emp_intensity = 0.0f;
130 Emp_wacky_target_timestamp = -1;
132 for(idx=0; idx<NUM_TEXT_STAMPS; idx++){
133 memset(Emp_wacky_text[idx].str, 0, EMP_WACKY_TEXT_LEN);
134 Emp_wacky_text[idx].stamp = -1;
138 // apply the EMP effect to all relevant ships
139 void emp_apply(vector *pos, float inner_radius, float outer_radius, float emp_intensity, float emp_time)
141 float actual_intensity, actual_time;
149 weapon_info *wip_target;
151 // all machines check to see if the blast hit a bomb. if so, shut it down (can't move anymore)
152 for( mo = GET_FIRST(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
153 target = &Objects[mo->objnum];
154 if(target->type != OBJ_WEAPON){
158 Assert(target->instance >= 0);
159 if(target->instance < 0){
162 Assert(Weapons[target->instance].weapon_info_index >= 0);
163 if(Weapons[target->instance].weapon_info_index < 0){
167 // if we have a bomb weapon
168 wip_target = &Weapon_info[Weapons[target->instance].weapon_info_index];
169 if(wip_target->wi_flags & WIF_BOMB){
170 // get the distance between the detonation and the target object
171 vm_vec_sub(&dist, &target->pos, pos);
172 dist_mag = vm_vec_mag(&dist);
174 // if the bomb was within 1/4 of the outer radius, castrate it
175 if(dist_mag <= (outer_radius * 0.25f)){
176 // memset(&target->phys_info, 0, sizeof(physics_info));
177 Weapons[target->instance].weapon_flags |= WF_DEAD_IN_WATER;
178 mprintf(("EMP killing bomb\n"));
183 // if I'm only a client in a multiplayer game, do nothing
184 if(MULTIPLAYER_CLIENT){
188 // See if there are any friendly ships present, if so return without preventing msg
189 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
190 target = &Objects[so->objnum];
191 if(target->type != OBJ_SHIP){
195 Assert(Objects[so->objnum].instance >= 0);
196 if(Objects[so->objnum].instance < 0){
199 Assert(Ships[Objects[so->objnum].instance].ship_info_index >= 0);
200 if(Ships[Objects[so->objnum].instance].ship_info_index < 0){
204 // if the ship is a cruiser or cap ship, only apply the EMP effect to turrets
205 if(Ship_info[Ships[target->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)){
206 // void ship_subsys_set_disrupted(ship_subsys *ss, int time)
207 moveup = &Ships[target->instance].subsys_list;
208 if(moveup->next != NULL){
209 moveup = moveup->next;
211 while(moveup != &Ships[target->instance].subsys_list){
212 // if this is a turret, disrupt it
213 if((moveup->system_info != NULL) && (moveup->system_info->type == SUBSYSTEM_TURRET)){
216 // get the distance to the subsys
217 vm_vec_unrotate(&actual_pos, &moveup->system_info->pnt, &target->orient);
218 vm_vec_add2(&actual_pos, &target->pos);
219 vm_vec_sub(&dist, &actual_pos, pos);
220 dist_mag = vm_vec_mag(&dist);
222 // if for some reason, the object was outside the blast, radius
223 if(dist_mag > outer_radius){
225 moveup = moveup->next;
229 // compute a scale factor for the emp effect
231 if(dist_mag >= inner_radius){
232 scale_factor = 1.0f - (dist_mag / outer_radius);
235 // disrupt the turret
236 ship_subsys_set_disrupted(moveup, (int)(MAX_TURRET_DISRUPT_TIME * scale_factor));
238 mprintf(("EMP disrupting subsys %s on ship %s (%f, %f)\n", moveup->system_info->name, Ships[Objects[so->objnum].instance].ship_name, scale_factor, MAX_TURRET_DISRUPT_TIME * scale_factor));
242 moveup = moveup->next;
245 // otherwise coat the whole ship with the effect. mmmmmmmmm.
247 // get the distance between the detonation and the target object
248 vm_vec_sub(&dist, &target->pos, pos);
249 dist_mag = vm_vec_mag(&dist);
251 // if for some reason, the object was outside the blast, radius
252 if(dist_mag > outer_radius){
256 // compute a scale factor for the emp effect
258 if(dist_mag >= inner_radius){
259 scale_factor = 1.0f - (dist_mag / outer_radius);
260 actual_intensity = emp_intensity * scale_factor;
261 actual_time = emp_time * scale_factor;
264 // calculate actual EMP effect values
265 actual_intensity = emp_intensity * scale_factor;
266 actual_time = emp_time * scale_factor;
267 mprintf(("EMP effect s : %f, i : %f, t : %f\n", scale_factor, actual_intensity, actual_time));
269 // if this effect happened to be on me, start it now
270 if((target == Player_obj) && !(Game_mode & GM_STANDALONE_SERVER)){
271 emp_start_local(actual_intensity, actual_time);
274 // if this is a multiplayer game, notify other players of the effect
275 if(Game_mode & GM_MULTIPLAYER){
276 Assert(MULTIPLAYER_MASTER);
277 send_emp_effect(target->net_signature, actual_intensity, actual_time);
280 // now be sure to start the emp effect for the ship itself
281 emp_start_ship(target, actual_intensity, actual_time);
286 // start the emp effect for the passed ship (setup lightning arcs, timestamp, etc)
287 // NOTE : if this ship is also me, I should call emp_start_local() as well
288 void emp_start_ship(object *ship_obj, float intensity, float time)
292 float start_intensity;
294 // make sure this is a ship
295 Assert(ship_obj->type == OBJ_SHIP);
296 Assert(ship_obj->instance >= 0);
297 shipp = &Ships[ship_obj->instance];
299 // determining pre-existing EMP intensity (if any)
300 start_intensity = shipp->emp_intensity < 0.0f ? 0.0f : shipp->emp_intensity;
302 // setup values (capping them if necessary) (make sure that we un-normalize start_intensity)
303 if(intensity + (start_intensity * EMP_INTENSITY_MAX) >= EMP_INTENSITY_MAX){
304 intensity = EMP_INTENSITY_MAX - 1.0f;
306 intensity += (start_intensity * EMP_INTENSITY_MAX);
308 intensity /= EMP_INTENSITY_MAX;
310 if(time >= EMP_TIME_MAX){
311 time = EMP_TIME_MAX - 0.1f;
313 shipp->emp_intensity = intensity;
314 shipp->emp_decr = intensity / time;
316 // multiplayer clients should bail now
317 if(MULTIPLAYER_CLIENT){
321 // do any initial AI effects
322 Assert(shipp->ai_index >= 0);
323 aip = &Ai_info[shipp->ai_index];
325 // lose his current target
326 set_target_objnum(aip, -1);
327 set_targeted_subsys(aip, NULL, -1);
330 // process a ship for this frame
332 void emp_process_ship(ship *shipp)
337 Assert(shipp != NULL);
341 Assert(shipp->objnum >= 0);
342 if(shipp->objnum < 0){
345 objp = &Objects[shipp->objnum];
347 // if the emp intensity is < 0, there is no effect
348 if(shipp->emp_intensity < 0.0f){
349 shipp->emp_intensity = -1.0f;
354 // reduce the emp effect
355 shipp->emp_intensity -= shipp->emp_decr * flFrametime;
357 // multiplayer clients should bail here
358 if(MULTIPLAYER_CLIENT){
362 // if this is a player ship, don't do anything wacky
363 if(objp->flags & OF_PLAYER_SHIP){
367 // lose lock time, etc, etc.
368 Assert(shipp->ai_index >= 0);
369 aip = &Ai_info[shipp->ai_index];
370 aip->aspect_locked_time = 0.0f; // hasn't gotten aspect lock at all
371 aip->current_target_is_locked = 0; // isn't locked on his current target
372 aip->ai_flags &= ~AIF_SEEK_LOCK;
373 aip->nearest_locked_object = -1; // nothing near me, so I won't launch countermeasures
375 // if he's not a fighter or bomber, bail now
376 if(!(Ship_info[shipp->ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER))){
380 // pick targets randomly and wackily so that the ship flies crazily :)
381 if(((int)f2fl(Missiontime) + (int)(EMP_INTENSITY_MAX * shipp->emp_intensity)) % mod_val == 0){
382 int team_lookup = TEAM_FRIENDLY;
386 team_lookup = TEAM_FRIENDLY;
388 case TEAM_NEUTRAL: case TEAM_FRIENDLY:
389 team_lookup = TEAM_HOSTILE;
392 ship_lookup = ship_get_random_team_ship(team_lookup);
394 // if we got a valid ship object to target
395 if((ship_lookup >= 0) && (Ships[ship_lookup].objnum >= 0)){
396 if(shipp->team == TEAM_HOSTILE){
397 mprintf(("EMP random target select\n"));
401 ai_attack_object(objp, &Objects[Ships[ship_lookup].objnum], 89, NULL);
406 // start the emp effect for MYSELF (intensity == arbitrary intensity variable, time == time the effect will last)
407 // NOTE : time should be in seconds
408 void emp_start_local(float intensity, float time)
411 float start_intensity;
413 // determine pre-existing EMP intensity (if any)
414 start_intensity = Emp_intensity < 0.0f ? 0.0f : Emp_intensity;
416 // cap all values (make sure that we un-normalize start_intensity)
417 if(intensity + (start_intensity * EMP_INTENSITY_MAX) >= EMP_INTENSITY_MAX){
418 intensity = EMP_INTENSITY_MAX - 1.0f;
420 intensity += (start_intensity * EMP_INTENSITY_MAX);
422 if(time >= EMP_TIME_MAX){
423 time = EMP_TIME_MAX - 0.1f;
427 Emp_intensity = intensity / EMP_INTENSITY_MAX;
429 // lose my current target if any
430 if(Player_ai != NULL){
431 Player_ai->target_objnum = -1;
433 // lose any lock we have or are getting
436 // reset HUD gauge text wackiness stuff
437 for(idx=0; idx<NUM_TEXT_STAMPS; idx++){
438 memset(Emp_wacky_text[idx].str, 0, 256);
439 Emp_wacky_text[idx].stamp = -1;
442 // start the emp icon flashing
443 hud_start_text_flash(NOX("Emp"), 5000);
445 // determine how much we have to decrement the effect per second
446 Emp_decr = Emp_intensity / time;
449 game_flash( 1.0f, 1.0f, 0.5f );
452 // stop the emp effect cold
453 void emp_stop_local()
455 // kill off various EMP stuff
456 Emp_intensity = -1.0f;
457 Emp_wacky_target_timestamp = -1;
460 // if the EMP effect is active
461 int emp_active_local()
463 return EMP_ACTIVE_LOCAL();
466 // process some stuff every frame (before frame is rendered)
467 void emp_process_local()
469 if(!emp_active_local()){
473 // decrement the intensity a bit
474 Emp_intensity -= (flFrametime * Emp_decr);
476 // see if we should choose a random target
477 if((Emp_wacky_target_timestamp == -1) || timestamp_elapsed(Emp_wacky_target_timestamp)){
478 // choose a target (if not the "first" time)
479 if(Emp_wacky_target_timestamp != -1){
480 hud_target_random_ship();
483 // reset the timestamp
484 Emp_wacky_target_timestamp = timestamp((int)frand_range(100.0f, 750.0f * (1.0f - Emp_intensity)));
488 // randomly say yes or no to a gauge, if emp is not active, always say yes
489 int emp_should_blit_gauge()
491 // if the EMP effect is not active, always blit
492 if(!emp_active_local()){
496 // otherwise, randomly say no
497 return frand_range(0.0f, 1.0f) > Emp_intensity;
501 void emp_hud_string(int x, int y, int gauge_id, char *str)
508 // if the emp effect is not active, don't even bother messing with the text
509 if(emp_active_local()){
510 emp_maybe_reformat_text(tmp, 256, gauge_id);
513 emp_hud_jitter(&x, &y);
516 // print the string out
517 gr_string(x, y, tmp);
521 void emp_hud_printf(int x, int y, int gauge_id, char *format, ...)
527 va_start(args, format);
528 vsprintf(tmp, format, args);
531 // if the emp effect is not active, don't even bother messing with the text
532 if(emp_active_local()){
533 emp_maybe_reformat_text(tmp, 256, gauge_id);
536 emp_hud_jitter(&x, &y);
539 // print the string out
540 gr_string(x, y, tmp);
543 // maybe reformat a string
544 void emp_maybe_reformat_text(char *text, int max_len, int gauge_id)
548 // if the EMP effect is not active, never reformat it
549 if(!emp_active_local()){
553 // randomly _don't_ apply text craziness
554 if(frand_range(0.0f, 1.0f) > Emp_intensity){
558 // if the gauge is EG_NULL, empty the string
559 if(gauge_id == EG_NULL){
564 // if this gauge has not been wacked out, or if the timestamp has expired, we
565 // neeed to wack it out again
566 Assert((gauge_id >= EG_NULL) && (gauge_id < NUM_TEXT_STAMPS));
567 wt = &Emp_wacky_text[gauge_id];
568 if((wt->stamp == -1) || timestamp_elapsed(wt->stamp)){
569 // reformat specific gauges differently
572 case EG_WEAPON_TITLE: case EG_WEAPON_P1: case EG_WEAPON_P2: case EG_WEAPON_P3: case EG_WEAPON_S1: case EG_WEAPON_S2:
574 wep_index = (int)frand_range(0.0f, (float)(MAX_WEAPON_TYPES - 1));
575 strcpy(wt->str, Weapon_info[ wep_index >= MAX_WEAPON_TYPES ? 0 : wep_index ].name);
579 case EG_ESCORT1: case EG_ESCORT2: case EG_ESCORT3:
580 // choose a random ship
582 shipnum = ship_get_random_ship();
584 strcpy(wt->str, Ships[shipnum].ship_name);
593 // directives themselves
594 case EG_OBJ1: case EG_OBJ2: case EG_OBJ3: case EG_OBJ4: case EG_OBJ5:
595 strcpy(wt->str, text);
596 emp_randomize_chars(wt->str);
600 case EG_TBOX_EXTRA1: case EG_TBOX_EXTRA2: case EG_TBOX_EXTRA3: case EG_TBOX_CLASS:
601 case EG_TBOX_DIST: case EG_TBOX_CARGO: case EG_TBOX_HULL: case EG_TBOX_NAME: case EG_TBOX_INTEG:
602 strcpy(wt->str, text);
603 emp_randomize_chars(wt->str);
607 case EG_SQ1: case EG_SQ2: case EG_SQ3: case EG_SQ4: case EG_SQ5: case EG_SQ6: case EG_SQ7:
608 case EG_SQ8: case EG_SQ9: case EG_SQ10:
609 strcpy(wt->str, text);
610 emp_randomize_chars(wt->str);
618 // recalculate the timestamp
619 wt->stamp = timestamp((int)frand_range(100.0f, 750.0f * (1.0f - Emp_intensity)));
622 strcpy(text, wt->str);
624 // otherwise, use what we calculated last time
626 strcpy(text, wt->str);
630 // randomize the chars in a string
631 void emp_randomize_chars(char *str)
636 // shuffle chars around
637 for(idx=0; idx<(int)(strlen(str)-1); idx++){
638 if(frand_range(0.0f, 1.0f) < Emp_intensity){
639 char_index = Emp_random_char[(int)frand_range(0.0f, (float)(NUM_RANDOM_CHARS - 1))];
640 str[idx] = Emp_random_char[char_index];
645 // throw some jitter into HUD x and y coords
646 void emp_hud_jitter(int *x, int *y)
648 // if the emp effect is not active, don't jitter anything
649 if(!emp_active_local()){
654 *x += (int)frand_range(-8.0f * Emp_intensity, 8.0f * Emp_intensity);
655 *y += (int)frand_range(-8.0f * Emp_intensity, 8.0f * Emp_intensity);
658 // current intensity of the EMP effect (0.0 - 1.0)
659 float emp_current_intensity()
661 return Emp_intensity;
664 DCF(zap, "zap a ship with an EMP effect")
668 dc_get_arg(ARG_STRING);
669 if(Dc_arg_type & ARG_STRING){
670 shipnum = ship_name_lookup(Dc_arg, 1);
673 emp_start_ship(&Objects[Ships[shipnum].objnum], 500.0f, 10.0f);