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/Flak.cpp $
18 * Revision 1.4 2002/06/17 06:33:11 relnev
19 * ryan's struct patch for gcc 2.95
21 * Revision 1.3 2002/06/09 04:41:29 relnev
22 * added copyright header
24 * Revision 1.2 2002/05/07 03:16:53 theoddone33
25 * The Great Newline Fix
27 * Revision 1.1.1.1 2002/05/03 03:28:11 root
31 * 9 7/31/99 2:57p Dave
32 * Scaled flak aim and jitter by weapon subsystem strength.
34 * 8 5/24/99 5:45p Dave
35 * Added detail levels to the nebula, with a decent speedup. Split nebula
36 * lightning into its own section.
46 #include "systemvars.h"
48 #include "muzzleflash.h"
50 // --------------------------------------------------------------------------------------------------------------------------------------
54 // temporary - max distance from target that a jittered flak aim direction can point at
55 #define FLAK_MAX_ERROR 60.0f // aim at _most_ this far off of the predicted target position
56 float Flak_error = FLAK_MAX_ERROR;
58 // muzzle flash animation
59 #define MUZZLE_FLASH_FILE "flk2"
60 #define MUZZLE_FLASH_RADIUS 15.0f
61 float Flak_muzzle_radius = MUZZLE_FLASH_RADIUS;
62 int Flak_muzzle_flash_ani = -1;
64 // muzzle flash limiting
65 #define FLAK_MUZZLE_MOD 3
66 int Flak_muzzle_mod = 0;
69 #define FLAK_RANGE_DEFAULT 65.0f // spherical radius around the predicted target position
70 float Flak_range = FLAK_RANGE_DEFAULT;
73 #define MAX_FLAK_INFO 350
74 typedef struct flak_info {
75 vector start_pos; // initial pos
76 float range; // range at which we'll detonate (-1 if unused);
78 flak_info Flak[MAX_FLAK_INFO];
81 // --------------------------------------------------------------------------------------------------------------------------------------
85 // initialize flak stuff for the level
86 void flak_level_init()
92 // if the muzzle flash ani is not loaded, do so
93 if(Flak_muzzle_flash_ani == -1){
94 Flak_muzzle_flash_ani = bm_load_animation(MUZZLE_FLASH_FILE, &num_frames, &fps, 1);
98 memset(Flak, 0, sizeof(flak_info) * MAX_FLAK_INFO);
99 for(idx=0; idx<MAX_FLAK_INFO; idx++){
100 Flak[idx].range = -1.0f;
104 // close down flak stuff
105 void flak_level_close()
107 // zero out the ani (bitmap manager will take care of releasing it I think)
108 if(Flak_muzzle_flash_ani != -1){
109 Flak_muzzle_flash_ani = -1;
113 // given a newly created weapon, turn it into a flak weapon
114 void flak_create(weapon *wp)
119 // make sure this is a valid flak weapon object
120 SDL_assert(wp->objnum >= 0);
121 SDL_assert(Objects[wp->objnum].type == OBJ_WEAPON);
122 SDL_assert(wp->weapon_info_index >= 0);
123 SDL_assert(Weapon_info[wp->weapon_info_index].wi_flags & WIF_FLAK);
125 // switch off rendering for the object
126 obj_set_flags(&Objects[wp->objnum], Objects[wp->objnum].flags & ~(OF_RENDERS));
128 // try and find a free flak index
130 for(idx=0; idx<MAX_FLAK_INFO; idx++){
131 if(Flak[idx].range < 0.0f){
137 // if we found a free slot
139 Flak[idx].range = 0.0f;
140 wp->flak_index = (short)idx;
142 nprintf(("General", "Out of FLAK slots!\n"));
146 // free up a flak object
147 void flak_delete(int flak_index)
149 SDL_assert((flak_index >= 0) && (flak_index < MAX_FLAK_INFO));
150 memset(&Flak[flak_index], 0, sizeof(flak_info));
151 Flak[flak_index].range = -1;
154 // given a just fired flak shell, pick a detonating distance for it
155 void flak_pick_range(object *objp, vector *predicted_target_pos, float weapon_subsys_strength)
160 // make sure this flak object is valid
161 SDL_assert(objp->type == OBJ_WEAPON);
162 SDL_assert(objp->instance >= 0);
163 SDL_assert(Weapons[objp->instance].weapon_info_index >= 0);
164 SDL_assert(Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags & WIF_FLAK);
166 // if the flak index is invalid, do nothing - if this fails the flak simply becomes a non-rendering bullet
167 if(Weapons[objp->instance].flak_index < 0){
171 // get the range to the target
172 vm_vec_sub(&temp, &objp->pos, predicted_target_pos);
173 final_range = vm_vec_mag(&temp);
175 // add in some randomness
176 final_range += (Flak_range + (Flak_range * 0.65f * (1.0f - weapon_subsys_strength))) * frand_range(-1.0f, 1.0f);
178 // make sure we're firing at least 10 meters away
179 if(final_range < 10.0f){
184 flak_set_range(objp, &objp->pos, final_range);
187 // add some jitter to a flak gun's aiming direction, take into account range to target so that we're never _too_ far off
188 // assumes dir is normalized
189 void flak_jitter_aim(vector *dir, float dist_to_target, float weapon_subsys_strength)
191 vector rand_twist_pre, rand_twist_post;
196 // get the matrix needed to rotate the base direction to the actual direction
197 vm_vector_2_matrix(&temp, dir, NULL, NULL);
200 error_val = Flak_error + (Flak_error * 0.65f * (1.0f - weapon_subsys_strength));
202 // scale the rvec by some random value and make it the "pre-twist" value
203 float rand_dist = frand_range(0.0f, error_val);
204 // no jitter - so do nothing
205 if(rand_dist <= 0.0f){
208 vm_vec_copy_scale(&rand_twist_pre, &temp.v.rvec, rand_dist);
210 // now rotate the twist vector around the x axis (the base aim axis) at a random angle
211 vm_rot_point_around_line(&rand_twist_post, &rand_twist_pre, fl_radian(359.0f * frand_range(0.0f, 1.0f)), &vmd_zero_vector, dir);
213 // add the resulting vector to the base aim vector and normalize
215 vm_vec_scale(&final_aim, dist_to_target);
216 vm_vec_add(dir, &final_aim, &rand_twist_post);
217 vm_vec_normalize(dir);
220 // create a muzzle flash from a flak gun based upon firing position and weapon type
221 void flak_muzzle_flash(vector *pos, vector *dir, int turret_weapon_class)
224 SDL_assert((turret_weapon_class >= 0) && (turret_weapon_class < Num_weapon_types));
225 if((turret_weapon_class < 0) || (turret_weapon_class >= Num_weapon_types)){
228 SDL_assert(Weapon_info[turret_weapon_class].wi_flags & WIF_FLAK);
229 if(!(Weapon_info[turret_weapon_class].wi_flags & WIF_FLAK)){
232 SDL_assert(Weapon_info[turret_weapon_class].wi_flags & WIF_MFLASH);
233 if(!(Weapon_info[turret_weapon_class].wi_flags & WIF_MFLASH)){
236 SDL_assert(Weapon_info[turret_weapon_class].muzzle_flash >= 0);
237 if(Weapon_info[turret_weapon_class].muzzle_flash < 0){
241 // maybe skip this flash
242 if(!(Flak_muzzle_mod % FLAK_MUZZLE_MOD)){
243 // call the muzzle flash code
244 mflash_create(pos, dir, Weapon_info[turret_weapon_class].muzzle_flash);
248 if(Flak_muzzle_mod >= 10000){
253 // maybe detonate a flak shell early/late (call from weapon_process_pre(...))
254 void flak_maybe_detonate(object *objp)
258 // multiplayer clients should never detonate flak early
259 // if(MULTIPLAYER_CLIENT){
263 // if the shell has gone past its range, blow it up
264 vm_vec_sub(&temp, &objp->pos, &Flak[Weapons[objp->instance].flak_index].start_pos);
265 if(vm_vec_mag(&temp) >= Flak[Weapons[objp->instance].flak_index].range){
266 weapon_detonate(objp);
270 // given a just fired flak shell, pick a detonating distance for it
271 void flak_set_range(object *objp, vector *start_pos, float range)
273 SDL_assert(objp->type == OBJ_WEAPON);
274 SDL_assert(objp->instance >= 0);
275 SDL_assert(Weapons[objp->instance].flak_index >= 0);
277 // setup the flak info
278 Flak[Weapons[objp->instance].flak_index].range = range;
279 Flak[Weapons[objp->instance].flak_index].start_pos = *start_pos;
282 // get the current range for the flak object
283 float flak_get_range(object *objp)
285 SDL_assert(objp->type == OBJ_WEAPON);
286 SDL_assert(objp->instance >= 0);
287 SDL_assert(Weapons[objp->instance].flak_index >= 0);
289 return Flak[Weapons[objp->instance].flak_index].range;
292 DCF(flak, "show flak dcf commands")
294 dc_printf("flak_err <float> : set the radius of error for flak targeting\n");
295 dc_printf("flak_range <float> : set the radius of error for detonation of a flak shell\n");
296 dc_printf("flak_rad <float> : set the radius for the muzzle flash on a flak gun\n");
299 DCF(flak_err, "set the radius of error for flak targeting")
301 dc_get_arg(ARG_FLOAT);
302 if(Dc_arg_type & ARG_FLOAT){
303 Flak_error = Dc_arg_float;
307 DCF(flak_range, "set the radius of error for detonation of a flak shell")
309 dc_get_arg(ARG_FLOAT);
310 if(Dc_arg_type & ARG_FLOAT){
311 Flak_range = Dc_arg_float;
315 DCF(flak_rad, "set the radius of flak gun muzzle flash")
317 dc_get_arg(ARG_FLOAT);
318 if(Dc_arg_type & ARG_FLOAT){
319 Flak_muzzle_radius = Dc_arg_float;