2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on
10 * $Logfile: /Freespace2/code/Weapon/Emp.cpp $
15 * Header file for managing corkscrew missiles
18 * Revision 1.3 2002/06/09 04:41:29 relnev
19 * added copyright header
21 * Revision 1.2 2002/05/07 03:16:53 theoddone33
22 * The Great Newline Fix
24 * Revision 1.1.1.1 2002/05/03 03:28:11 root
28 * 9 7/24/99 1:54p Dave
29 * Hud text flash gauge. Reworked dead popup to use 4 buttons in red-alert
32 * 8 7/02/99 4:31p Dave
33 * Much more sophisticated lightning support.
35 * 7 4/23/99 12:01p Johnson
38 * 6 2/08/99 9:34a Dave
39 * Put in a little insurance code to make sure EMP effect truly dies when
40 * a player dies (to prevent wacky dead cam action).
42 * 5 1/08/99 2:08p Dave
43 * Fixed software rendering for pofview. Super early support for AWACS and
46 * 4 11/05/98 5:55p Dave
47 * Big pass at reducing #includes
49 * 3 10/13/98 9:29a Dave
50 * Started neatening up freespace.h. Many variables renamed and
51 * reorganized. Added AlphaColors.[h,cpp]
53 * 2 10/07/98 10:54a Dave
56 * 1 10/07/98 10:51a Dave
58 * 3 8/28/98 3:29p Dave
59 * EMP effect done. AI effects may need some tweaking as required.
61 * 2 8/25/98 1:49p Dave
62 * First rev of EMP effect. Player side stuff basically done. Next comes
65 * 1 8/24/98 9:29a Dave
73 #include "multimsgs.h"
74 #include "multiutil.h"
75 #include "systemvars.h"
76 #include "freespace.h"
79 #include "hudtarget.h"
80 #include "hudgauges.h"
81 #include "missiongoals.h"
84 // ----------------------------------------------------------------------------------------------------
85 // EMP EFFECT DEFINES/VARS
88 // intensity of the playing effect
89 float Emp_intensity = -1.0f; // current intensity of the EMP effect (normalized to EMP_INTENSITY_MAX)
90 float Emp_decr = 0.0f; // how much to decrement the effect per second
92 // timestamp until we should randomly choose another target
93 int Emp_wacky_target_timestamp = -1;
95 // max time we'll disrupt turrets on big ships
96 #define MAX_TURRET_DISRUPT_TIME 7500
98 // conventient for determining if EMP is active
99 #define EMP_ACTIVE_LOCAL() (Emp_intensity > 0.0f)
101 // for keeping track of messed up text
102 #define EMP_WACKY_TEXT_LEN 256
103 typedef struct wacky_text {
104 char str[EMP_WACKY_TEXT_LEN];
107 wacky_text Emp_wacky_text[NUM_TEXT_STAMPS];
109 // for randomly inserting characters
110 #define NUM_RANDOM_CHARS 51
111 const char Emp_random_char[NUM_RANDOM_CHARS] =
112 { 'a', 'b', 'c', 'd', 'e', 'f', 'g', '4', 'h', '8', '_', '$', ')', '-', '~', 'u', 'q',
113 '.', 'x', 'h', '&', '%', '*', '1', '3', 't', 'h', 'o', 'p', '@', 'h', 'i','v', '+', '=',
114 '|', '{', '}', ':', ';', '^', 'l', 'z', 'u', 'v', '<', '>', '?', '5', '8' };
116 // EMP EFFECTS ON PLAYERS -----
117 // 1.) Lose target lock if any, along with ability to lock on
118 // 2.) Display EMP-BLAST icon or something (maybe flash it)
119 // 3.) at wacky intervals, target random ships (which he cannot lock on)
120 // 4.) Randomly flicker HUD gauges
121 // 5.) Randomly swap/mess-up HUD text
123 // EMP EFFECTS ON SHIPS -------
124 // 1.) Lightning effect proportional to the emp effect
126 // ----------------------------------------------------------------------------------------------------
127 // EMP EFFECT FUNCTIONS
130 // maybe reformat a string
131 void emp_maybe_reformat_text(char *text, const int max_len, int gauge_id);
133 // randomize the chars in a string
134 void emp_randomize_chars(char *str);
137 // initialize the EMP effect for the mission
138 void emp_level_init()
143 Emp_intensity = 0.0f;
144 Emp_wacky_target_timestamp = -1;
146 for(idx=0; idx<NUM_TEXT_STAMPS; idx++){
147 memset(Emp_wacky_text[idx].str, 0, EMP_WACKY_TEXT_LEN);
148 Emp_wacky_text[idx].stamp = -1;
152 // apply the EMP effect to all relevant ships
153 void emp_apply(vector *pos, float inner_radius, float outer_radius, float emp_intensity, float emp_time)
155 float actual_intensity, actual_time;
163 weapon_info *wip_target;
165 // all machines check to see if the blast hit a bomb. if so, shut it down (can't move anymore)
166 for( mo = GET_FIRST(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
167 target = &Objects[mo->objnum];
168 if(target->type != OBJ_WEAPON){
172 SDL_assert(target->instance >= 0);
173 if(target->instance < 0){
176 SDL_assert(Weapons[target->instance].weapon_info_index >= 0);
177 if(Weapons[target->instance].weapon_info_index < 0){
181 // if we have a bomb weapon
182 wip_target = &Weapon_info[Weapons[target->instance].weapon_info_index];
183 if(wip_target->wi_flags & WIF_BOMB){
184 // get the distance between the detonation and the target object
185 vm_vec_sub(&dist, &target->pos, pos);
186 dist_mag = vm_vec_mag(&dist);
188 // if the bomb was within 1/4 of the outer radius, castrate it
189 if(dist_mag <= (outer_radius * 0.25f)){
190 // memset(&target->phys_info, 0, sizeof(physics_info));
191 Weapons[target->instance].weapon_flags |= WF_DEAD_IN_WATER;
192 mprintf(("EMP killing bomb\n"));
197 // if I'm only a client in a multiplayer game, do nothing
198 if(MULTIPLAYER_CLIENT){
202 // See if there are any friendly ships present, if so return without preventing msg
203 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
204 target = &Objects[so->objnum];
205 if(target->type != OBJ_SHIP){
209 SDL_assert(Objects[so->objnum].instance >= 0);
210 if(Objects[so->objnum].instance < 0){
213 SDL_assert(Ships[Objects[so->objnum].instance].ship_info_index >= 0);
214 if(Ships[Objects[so->objnum].instance].ship_info_index < 0){
218 // if the ship is a cruiser or cap ship, only apply the EMP effect to turrets
219 if(Ship_info[Ships[target->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)){
220 // void ship_subsys_set_disrupted(ship_subsys *ss, int time)
221 moveup = &Ships[target->instance].subsys_list;
222 if(moveup->next != NULL){
223 moveup = moveup->next;
225 while(moveup != &Ships[target->instance].subsys_list){
226 // if this is a turret, disrupt it
227 if((moveup->system_info != NULL) && (moveup->system_info->type == SUBSYSTEM_TURRET)){
230 // get the distance to the subsys
231 vm_vec_unrotate(&actual_pos, &moveup->system_info->pnt, &target->orient);
232 vm_vec_add2(&actual_pos, &target->pos);
233 vm_vec_sub(&dist, &actual_pos, pos);
234 dist_mag = vm_vec_mag(&dist);
236 // if for some reason, the object was outside the blast, radius
237 if(dist_mag > outer_radius){
239 moveup = moveup->next;
243 // compute a scale factor for the emp effect
245 if(dist_mag >= inner_radius){
246 scale_factor = 1.0f - (dist_mag / outer_radius);
249 // disrupt the turret
250 ship_subsys_set_disrupted(moveup, (int)(MAX_TURRET_DISRUPT_TIME * scale_factor));
252 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));
256 moveup = moveup->next;
259 // otherwise coat the whole ship with the effect. mmmmmmmmm.
261 // get the distance between the detonation and the target object
262 vm_vec_sub(&dist, &target->pos, pos);
263 dist_mag = vm_vec_mag(&dist);
265 // if for some reason, the object was outside the blast, radius
266 if(dist_mag > outer_radius){
270 // compute a scale factor for the emp effect
272 if(dist_mag >= inner_radius){
273 scale_factor = 1.0f - (dist_mag / outer_radius);
274 actual_intensity = emp_intensity * scale_factor;
275 actual_time = emp_time * scale_factor;
278 // calculate actual EMP effect values
279 actual_intensity = emp_intensity * scale_factor;
280 actual_time = emp_time * scale_factor;
281 mprintf(("EMP effect s : %f, i : %f, t : %f\n", scale_factor, actual_intensity, actual_time));
283 // if this effect happened to be on me, start it now
284 if((target == Player_obj) && !(Game_mode & GM_STANDALONE_SERVER)){
285 emp_start_local(actual_intensity, actual_time);
288 // if this is a multiplayer game, notify other players of the effect
289 if(Game_mode & GM_MULTIPLAYER){
290 SDL_assert(MULTIPLAYER_MASTER);
291 send_emp_effect(target->net_signature, actual_intensity, actual_time);
294 // now be sure to start the emp effect for the ship itself
295 emp_start_ship(target, actual_intensity, actual_time);
300 // start the emp effect for the passed ship (setup lightning arcs, timestamp, etc)
301 // NOTE : if this ship is also me, I should call emp_start_local() as well
302 void emp_start_ship(object *ship_obj, float intensity, float time)
306 float start_intensity;
308 // make sure this is a ship
309 SDL_assert(ship_obj->type == OBJ_SHIP);
310 SDL_assert(ship_obj->instance >= 0);
311 shipp = &Ships[ship_obj->instance];
313 // determining pre-existing EMP intensity (if any)
314 start_intensity = shipp->emp_intensity < 0.0f ? 0.0f : shipp->emp_intensity;
316 // setup values (capping them if necessary) (make sure that we un-normalize start_intensity)
317 if(intensity + (start_intensity * EMP_INTENSITY_MAX) >= EMP_INTENSITY_MAX){
318 intensity = EMP_INTENSITY_MAX - 1.0f;
320 intensity += (start_intensity * EMP_INTENSITY_MAX);
322 intensity /= EMP_INTENSITY_MAX;
324 if(time >= EMP_TIME_MAX){
325 time = EMP_TIME_MAX - 0.1f;
327 shipp->emp_intensity = intensity;
328 shipp->emp_decr = intensity / time;
330 // multiplayer clients should bail now
331 if(MULTIPLAYER_CLIENT){
335 // do any initial AI effects
336 SDL_assert(shipp->ai_index >= 0);
337 aip = &Ai_info[shipp->ai_index];
339 // lose his current target
340 set_target_objnum(aip, -1);
341 set_targeted_subsys(aip, NULL, -1);
344 // process a ship for this frame
346 void emp_process_ship(ship *shipp)
351 SDL_assert(shipp != NULL);
355 SDL_assert(shipp->objnum >= 0);
356 if(shipp->objnum < 0){
359 objp = &Objects[shipp->objnum];
361 // if the emp intensity is < 0, there is no effect
362 if(shipp->emp_intensity < 0.0f){
363 shipp->emp_intensity = -1.0f;
368 // reduce the emp effect
369 shipp->emp_intensity -= shipp->emp_decr * flFrametime;
371 // multiplayer clients should bail here
372 if(MULTIPLAYER_CLIENT){
376 // if this is a player ship, don't do anything wacky
377 if(objp->flags & OF_PLAYER_SHIP){
381 // lose lock time, etc, etc.
382 SDL_assert(shipp->ai_index >= 0);
383 aip = &Ai_info[shipp->ai_index];
384 aip->aspect_locked_time = 0.0f; // hasn't gotten aspect lock at all
385 aip->current_target_is_locked = 0; // isn't locked on his current target
386 aip->ai_flags &= ~AIF_SEEK_LOCK;
387 aip->nearest_locked_object = -1; // nothing near me, so I won't launch countermeasures
389 // if he's not a fighter or bomber, bail now
390 if(!(Ship_info[shipp->ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER))){
394 // pick targets randomly and wackily so that the ship flies crazily :)
395 if(((int)f2fl(Missiontime) + (int)(EMP_INTENSITY_MAX * shipp->emp_intensity)) % mod_val == 0){
396 int team_lookup = TEAM_FRIENDLY;
400 team_lookup = TEAM_FRIENDLY;
402 case TEAM_NEUTRAL: case TEAM_FRIENDLY:
403 team_lookup = TEAM_HOSTILE;
406 ship_lookup = ship_get_random_team_ship(team_lookup);
408 // if we got a valid ship object to target
409 if((ship_lookup >= 0) && (Ships[ship_lookup].objnum >= 0)){
410 if(shipp->team == TEAM_HOSTILE){
411 mprintf(("EMP random target select\n"));
415 ai_attack_object(objp, &Objects[Ships[ship_lookup].objnum], 89, NULL);
420 // start the emp effect for MYSELF (intensity == arbitrary intensity variable, time == time the effect will last)
421 // NOTE : time should be in seconds
422 void emp_start_local(float intensity, float time)
425 float start_intensity;
427 // determine pre-existing EMP intensity (if any)
428 start_intensity = Emp_intensity < 0.0f ? 0.0f : Emp_intensity;
430 // cap all values (make sure that we un-normalize start_intensity)
431 if(intensity + (start_intensity * EMP_INTENSITY_MAX) >= EMP_INTENSITY_MAX){
432 intensity = EMP_INTENSITY_MAX - 1.0f;
434 intensity += (start_intensity * EMP_INTENSITY_MAX);
436 if(time >= EMP_TIME_MAX){
437 time = EMP_TIME_MAX - 0.1f;
441 Emp_intensity = intensity / EMP_INTENSITY_MAX;
443 // lose my current target if any
444 if(Player_ai != NULL){
445 Player_ai->target_objnum = -1;
447 // lose any lock we have or are getting
450 // reset HUD gauge text wackiness stuff
451 for(idx=0; idx<NUM_TEXT_STAMPS; idx++){
452 memset(Emp_wacky_text[idx].str, 0, 256);
453 Emp_wacky_text[idx].stamp = -1;
456 // start the emp icon flashing
457 hud_start_text_flash(NOX("Emp"), 5000);
459 // determine how much we have to decrement the effect per second
460 Emp_decr = Emp_intensity / time;
463 game_flash( 1.0f, 1.0f, 0.5f );
466 // stop the emp effect cold
467 void emp_stop_local()
469 // kill off various EMP stuff
470 Emp_intensity = -1.0f;
471 Emp_wacky_target_timestamp = -1;
474 // if the EMP effect is active
475 int emp_active_local()
477 return EMP_ACTIVE_LOCAL();
480 // process some stuff every frame (before frame is rendered)
481 void emp_process_local()
483 if(!emp_active_local()){
487 // decrement the intensity a bit
488 Emp_intensity -= (flFrametime * Emp_decr);
490 // see if we should choose a random target
491 if((Emp_wacky_target_timestamp == -1) || timestamp_elapsed(Emp_wacky_target_timestamp)){
492 // choose a target (if not the "first" time)
493 if(Emp_wacky_target_timestamp != -1){
494 hud_target_random_ship();
497 // reset the timestamp
498 Emp_wacky_target_timestamp = timestamp((int)frand_range(100.0f, 750.0f * (1.0f - Emp_intensity)));
502 // randomly say yes or no to a gauge, if emp is not active, always say yes
503 int emp_should_blit_gauge()
505 // if the EMP effect is not active, always blit
506 if(!emp_active_local()){
510 // otherwise, randomly say no
511 return frand_range(0.0f, 1.0f) > Emp_intensity;
515 void emp_hud_string(int x, int y, int gauge_id, const char *str)
520 SDL_strlcpy(tmp, str, sizeof(tmp));
522 // if the emp effect is not active, don't even bother messing with the text
523 if(emp_active_local()){
524 emp_maybe_reformat_text(tmp, 256, gauge_id);
527 emp_hud_jitter(&x, &y);
530 // print the string out
531 gr_string(x, y, tmp);
535 void emp_hud_printf(int x, int y, int gauge_id, const char *format, ...)
541 va_start(args, format);
542 SDL_vsnprintf(tmp, sizeof(tmp), format, args);
545 // if the emp effect is not active, don't even bother messing with the text
546 if(emp_active_local()){
547 emp_maybe_reformat_text(tmp, 256, gauge_id);
550 emp_hud_jitter(&x, &y);
553 // print the string out
554 gr_string(x, y, tmp);
557 // maybe reformat a string
558 void emp_maybe_reformat_text(char *text, const int max_len, int gauge_id)
562 // if the EMP effect is not active, never reformat it
563 if(!emp_active_local()){
567 // randomly _don't_ apply text craziness
568 if(frand_range(0.0f, 1.0f) > Emp_intensity){
572 // if the gauge is EG_NULL, empty the string
573 if(gauge_id == EG_NULL){
574 SDL_strlcpy(text, "", max_len);
578 // if this gauge has not been wacked out, or if the timestamp has expired, we
579 // neeed to wack it out again
580 SDL_assert((gauge_id >= EG_NULL) && (gauge_id < NUM_TEXT_STAMPS));
581 wt = &Emp_wacky_text[gauge_id];
582 if((wt->stamp == -1) || timestamp_elapsed(wt->stamp)){
583 // reformat specific gauges differently
586 case EG_WEAPON_TITLE: case EG_WEAPON_P1: case EG_WEAPON_P2: case EG_WEAPON_P3: case EG_WEAPON_S1: case EG_WEAPON_S2:
588 wep_index = (int)frand_range(0.0f, (float)(MAX_WEAPON_TYPES - 1));
589 SDL_strlcpy(wt->str, Weapon_info[ wep_index >= MAX_WEAPON_TYPES ? 0 : wep_index ].name, sizeof(wt->str));
593 case EG_ESCORT1: case EG_ESCORT2: case EG_ESCORT3:
594 // choose a random ship
596 shipnum = ship_get_random_ship();
598 SDL_strlcpy(wt->str, Ships[shipnum].ship_name, sizeof(wt->str));
604 SDL_strlcpy(wt->str, "", sizeof(wt->str));
607 // directives themselves
608 case EG_OBJ1: case EG_OBJ2: case EG_OBJ3: case EG_OBJ4: case EG_OBJ5:
609 SDL_strlcpy(wt->str, text, sizeof(wt->str));
610 emp_randomize_chars(wt->str);
614 case EG_TBOX_EXTRA1: case EG_TBOX_EXTRA2: case EG_TBOX_EXTRA3: case EG_TBOX_CLASS:
615 case EG_TBOX_DIST: case EG_TBOX_CARGO: case EG_TBOX_HULL: case EG_TBOX_NAME: case EG_TBOX_INTEG:
616 SDL_strlcpy(wt->str, text, sizeof(wt->str));
617 emp_randomize_chars(wt->str);
621 case EG_SQ1: case EG_SQ2: case EG_SQ3: case EG_SQ4: case EG_SQ5: case EG_SQ6: case EG_SQ7:
622 case EG_SQ8: case EG_SQ9: case EG_SQ10:
623 SDL_strlcpy(wt->str, text, sizeof(wt->str));
624 emp_randomize_chars(wt->str);
632 // recalculate the timestamp
633 wt->stamp = timestamp((int)frand_range(100.0f, 750.0f * (1.0f - Emp_intensity)));
636 SDL_strlcpy(text, wt->str, max_len);
638 // otherwise, use what we calculated last time
640 SDL_strlcpy(text, wt->str, max_len);
644 // randomize the chars in a string
645 void emp_randomize_chars(char *str)
650 // shuffle chars around
651 for(idx=0; idx<(int)(strlen(str)-1); idx++){
652 if(frand_range(0.0f, 1.0f) < Emp_intensity){
653 char_index = Emp_random_char[(int)frand_range(0.0f, (float)(NUM_RANDOM_CHARS - 1))];
654 str[idx] = Emp_random_char[char_index];
659 // throw some jitter into HUD x and y coords
660 void emp_hud_jitter(int *x, int *y)
662 // if the emp effect is not active, don't jitter anything
663 if(!emp_active_local()){
668 *x += (int)frand_range(-8.0f * Emp_intensity, 8.0f * Emp_intensity);
669 *y += (int)frand_range(-8.0f * Emp_intensity, 8.0f * Emp_intensity);
672 // current intensity of the EMP effect (0.0 - 1.0)
673 float emp_current_intensity()
675 return Emp_intensity;
678 DCF(zap, "zap a ship with an EMP effect")
682 dc_get_arg(ARG_STRING);
683 if(Dc_arg_type & ARG_STRING){
684 shipnum = ship_name_lookup(Dc_arg, 1);
687 emp_start_ship(&Objects[Ships[shipnum].objnum], 500.0f, 10.0f);