]> icculus.org git repositories - taylor/freespace2.git/blob - src/weapon/flak.cpp
The Great Newline Fix
[taylor/freespace2.git] / src / weapon / flak.cpp
1 /*
2  * $Logfile: /Freespace2/code/Weapon/Flak.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * flak functions
8  *
9  * $Log$
10  * Revision 1.2  2002/05/07 03:16:53  theoddone33
11  * The Great Newline Fix
12  *
13  * Revision 1.1.1.1  2002/05/03 03:28:11  root
14  * Initial import.
15  * 
16  * 
17  * 9     7/31/99 2:57p Dave
18  * Scaled flak aim and jitter by weapon subsystem strength.
19  * 
20  * 8     5/24/99 5:45p Dave
21  * Added detail levels to the nebula, with a decent speedup. Split nebula
22  * lightning into its own section.
23  * 
24  * $NoKeywords: $
25  */
26
27 #include "flak.h"
28 #include "vecmat.h"
29 #include "bmpman.h"
30 #include "particle.h"
31 #include "weapon.h"
32 #include "systemvars.h"
33 #include "multi.h"
34 #include "muzzleflash.h"
35
36 // --------------------------------------------------------------------------------------------------------------------------------------
37 // FLAK DEFINES/VARS
38 //
39
40 // temporary - max distance from target that a jittered flak aim direction can point at
41 #define FLAK_MAX_ERROR                                                                                  60.0f                                                   // aim at _most_ this far off of the predicted target position
42 float Flak_error = FLAK_MAX_ERROR;
43
44 // muzzle flash animation
45 #define MUZZLE_FLASH_FILE                                                                               "flk2"
46 #define MUZZLE_FLASH_RADIUS                                                                     15.0f
47 float Flak_muzzle_radius = MUZZLE_FLASH_RADIUS;
48 int Flak_muzzle_flash_ani = -1;
49
50 // muzzle flash limiting
51 #define FLAK_MUZZLE_MOD         3
52 int Flak_muzzle_mod = 0;
53
54 // flak ranging info
55 #define FLAK_RANGE_DEFAULT                                                                              65.0f                                                   // spherical radius around the predicted target position
56 float Flak_range = FLAK_RANGE_DEFAULT;
57
58 // flak info
59 #define MAX_FLAK_INFO                                                                                   350
60 typedef struct flak_info {      
61         vector start_pos;                                                       // initial pos
62         float range;                                                            // range at which we'll detonate (-1 if unused);
63 } flak_info;
64 flak_info Flak[MAX_FLAK_INFO];
65
66
67 // --------------------------------------------------------------------------------------------------------------------------------------
68 // FLAK FUNCTIONS
69 //
70
71 // initialize flak stuff for the level
72 void flak_level_init()
73 {
74         int num_frames;
75         int fps;
76         int idx;
77
78         // if the muzzle flash ani is not loaded, do so
79         if(Flak_muzzle_flash_ani == -1){
80                 Flak_muzzle_flash_ani = bm_load_animation(MUZZLE_FLASH_FILE, &num_frames, &fps, 1);
81         }
82
83         // zero out flak info
84         memset(Flak, 0, sizeof(flak_info) * MAX_FLAK_INFO);
85         for(idx=0; idx<MAX_FLAK_INFO; idx++){
86                 Flak[idx].range = -1.0f;
87         }
88 }
89
90 // close down flak stuff
91 void flak_level_close()
92 {
93         // zero out the ani (bitmap manager will take care of releasing it I think)
94         if(Flak_muzzle_flash_ani != -1){
95                 Flak_muzzle_flash_ani = -1;
96         }
97 }
98
99 // given a newly created weapon, turn it into a flak weapon
100 void flak_create(weapon *wp)
101 {
102         int idx;
103         int found;
104         
105         // make sure this is a valid flak weapon object
106         Assert(wp->objnum >= 0);
107         Assert(Objects[wp->objnum].type == OBJ_WEAPON);
108         Assert(wp->weapon_info_index >= 0);
109         Assert(Weapon_info[wp->weapon_info_index].wi_flags & WIF_FLAK);
110
111         // switch off rendering for the object
112         obj_set_flags(&Objects[wp->objnum], Objects[wp->objnum].flags & ~(OF_RENDERS));
113
114         // try and find a free flak index
115         found = 0;
116         for(idx=0; idx<MAX_FLAK_INFO; idx++){
117                 if(Flak[idx].range < 0.0f){
118                         found = 1;
119                         break;
120                 }
121         }
122
123         // if we found a free slot
124         if(found){
125                 Flak[idx].range = 0.0f;
126                 wp->flak_index = (short)idx;
127         } else {
128                 nprintf(("General", "Out of FLAK slots!\n"));
129         }
130 }
131
132 // free up a flak object
133 void flak_delete(int flak_index)
134 {
135         Assert((flak_index >= 0) && (flak_index < MAX_FLAK_INFO));
136         memset(&Flak[flak_index], 0, sizeof(flak_info));
137         Flak[flak_index].range = -1;
138 }
139
140 // given a just fired flak shell, pick a detonating distance for it
141 void flak_pick_range(object *objp, vector *predicted_target_pos, float weapon_subsys_strength)
142 {
143         float final_range;
144         vector temp;
145         
146         // make sure this flak object is valid
147         Assert(objp->type == OBJ_WEAPON);
148         Assert(objp->instance >= 0);
149         Assert(Weapons[objp->instance].weapon_info_index >= 0);
150         Assert(Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags & WIF_FLAK);     
151         
152         // if the flak index is invalid, do nothing - if this fails the flak simply becomes a non-rendering bullet
153         if(Weapons[objp->instance].flak_index < 0){
154                 return;
155         }
156
157         // get the range to the target
158         vm_vec_sub(&temp, &objp->pos, predicted_target_pos);
159         final_range = vm_vec_mag(&temp);
160
161         // add in some randomness
162         final_range += (Flak_range + (Flak_range * 0.65f * (1.0f - weapon_subsys_strength))) * frand_range(-1.0f, 1.0f);        
163
164         // make sure we're firing at least 10 meters away
165         if(final_range < 10.0f){
166                 final_range = 10.0f;
167         }
168
169         // set the range
170         flak_set_range(objp, &objp->pos, final_range);
171 }
172
173 // add some jitter to a flak gun's aiming direction, take into account range to target so that we're never _too_ far off
174 // assumes dir is normalized
175 void flak_jitter_aim(vector *dir, float dist_to_target, float weapon_subsys_strength)
176 {                       
177         vector rand_twist_pre, rand_twist_post;         
178         matrix temp;
179         vector final_aim;
180         float error_val;
181         
182         // get the matrix needed to rotate the base direction to the actual direction           
183         vm_vector_2_matrix(&temp, dir, NULL, NULL);
184
185         // error value  
186         error_val = Flak_error + (Flak_error * 0.65f * (1.0f - weapon_subsys_strength));
187         
188         // scale the rvec by some random value and make it the "pre-twist" value
189         float rand_dist = frand_range(0.0f, error_val);
190         // no jitter - so do nothing
191         if(rand_dist <= 0.0f){
192                 return;
193         }
194         vm_vec_copy_scale(&rand_twist_pre, &temp.rvec, rand_dist);
195
196         // now rotate the twist vector around the x axis (the base aim axis) at a random angle
197         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);
198
199         // add the resulting vector to the base aim vector and normalize
200         final_aim = *dir;
201         vm_vec_scale(&final_aim, dist_to_target);
202         vm_vec_add(dir, &final_aim, &rand_twist_post);
203         vm_vec_normalize(dir);
204 }
205
206 // create a muzzle flash from a flak gun based upon firing position and weapon type
207 void flak_muzzle_flash(vector *pos, vector *dir, int turret_weapon_class)
208 {
209         // sanity
210         Assert((turret_weapon_class >= 0) && (turret_weapon_class < Num_weapon_types));
211         if((turret_weapon_class < 0) || (turret_weapon_class >= Num_weapon_types)){
212                 return;
213         }
214         Assert(Weapon_info[turret_weapon_class].wi_flags & WIF_FLAK);
215         if(!(Weapon_info[turret_weapon_class].wi_flags & WIF_FLAK)){
216                 return;
217         }
218         Assert(Weapon_info[turret_weapon_class].wi_flags & WIF_MFLASH);
219         if(!(Weapon_info[turret_weapon_class].wi_flags & WIF_MFLASH)){
220                 return;
221         }
222         Assert(Weapon_info[turret_weapon_class].muzzle_flash >= 0);
223         if(Weapon_info[turret_weapon_class].muzzle_flash < 0){
224                 return;
225         }
226
227         // maybe skip this flash
228         if(!(Flak_muzzle_mod % FLAK_MUZZLE_MOD)){
229                 // call the muzzle flash code
230                 mflash_create(pos, dir, Weapon_info[turret_weapon_class].muzzle_flash);
231         }
232
233         Flak_muzzle_mod++;
234         if(Flak_muzzle_mod >= 10000){
235                 Flak_muzzle_mod = 0;
236         }       
237 }
238
239 // maybe detonate a flak shell early/late (call from weapon_process_pre(...))
240 void flak_maybe_detonate(object *objp)
241 {                       
242         vector temp;    
243
244         // multiplayer clients should never detonate flak early
245         // if(MULTIPLAYER_CLIENT){
246                 // return;
247         // }
248
249         // if the shell has gone past its range, blow it up
250         vm_vec_sub(&temp, &objp->pos, &Flak[Weapons[objp->instance].flak_index].start_pos);
251         if(vm_vec_mag(&temp) >= Flak[Weapons[objp->instance].flak_index].range){
252                 weapon_detonate(objp);          
253         }
254 }
255
256 // given a just fired flak shell, pick a detonating distance for it
257 void flak_set_range(object *objp, vector *start_pos, float range)
258 {
259         Assert(objp->type == OBJ_WEAPON);
260         Assert(objp->instance >= 0);    
261         Assert(Weapons[objp->instance].flak_index >= 0);
262
263         // setup the flak info
264         Flak[Weapons[objp->instance].flak_index].range = range;
265         Flak[Weapons[objp->instance].flak_index].start_pos = *start_pos;
266 }
267
268 // get the current range for the flak object
269 float flak_get_range(object *objp)
270 {
271         Assert(objp->type == OBJ_WEAPON);
272         Assert(objp->instance >= 0);    
273         Assert(Weapons[objp->instance].flak_index >= 0);
274         
275         return Flak[Weapons[objp->instance].flak_index].range;
276 }
277
278 DCF(flak, "show flak dcf commands")
279 {
280         dc_printf("flak_err <float>      : set the radius of error for flak targeting\n");      
281         dc_printf("flak_range <float>           : set the radius of error for detonation of a flak shell\n");
282         dc_printf("flak_rad <float>      : set the radius for the muzzle flash on a flak gun\n");
283 }
284
285 DCF(flak_err, "set the radius of error for flak targeting")
286 {
287         dc_get_arg(ARG_FLOAT);
288         if(Dc_arg_type & ARG_FLOAT){             
289                 Flak_error = Dc_arg_float;
290         }
291 }
292
293 DCF(flak_range, "set the radius of error for detonation of a flak shell")
294 {
295         dc_get_arg(ARG_FLOAT);
296         if(Dc_arg_type & ARG_FLOAT){             
297                 Flak_range = Dc_arg_float;
298         }
299 }
300
301 DCF(flak_rad, "set the radius of flak gun muzzle flash")
302 {
303         dc_get_arg(ARG_FLOAT);
304         if(Dc_arg_type & ARG_FLOAT){             
305                 Flak_muzzle_radius = Dc_arg_float;
306         }
307 }