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