]> icculus.org git repositories - taylor/freespace2.git/blob - src/weapon/corkscrew.cpp
Initial revision
[taylor/freespace2.git] / src / weapon / corkscrew.cpp
1 /*
2  * $Logfile: /Freespace2/code/Weapon/Corkscrew.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * C module for managing corkscrew missiles
8  * 
9  * $NoKeywords: $
10  */
11
12 #include "corkscrew.h"
13 #include "weapon.h"
14 #include "ship.h"
15 #include "timer.h"
16 #include "freespace.h"  // for Missiontime
17 #include "trails.h"
18
19 // corkscrew structure flags
20 #define CS_FLAG_USED                                            (1<<0)          // this structure is in use
21 #define CS_FLAG_COUNTER                                 (1<<1)          // counterrotate this guy
22
23 // corkscrew settings
24 int Corkscrew_missile_delay                     = 30;                   // delay between missile firings
25 int Corkscrew_num_missiles_fired                = 4;                    // # of missiles fire in one shot
26 float Corkscrew_radius                                  = 1.25f;                // radius of the corkscrew itself
27 float Corkscrew_twist                                   = 5.0f;         // in degrees/second
28 int Corkscrew_helix                                             = 1;                    // attempt to point the missile in the right direction
29 int Corkscrew_counterrotate                     = 1;                    // counterrotate every other missile
30 int Corkscrew_shrink                                            = 0;                    // shrink the radius of every successive missile
31 float Corkscrew_shrink_val                              = 0.3f;         // rate at which the radius shrinks
32 int Corkscrew_down_first                                = 1;                    // have the corkscrew go "down" first
33
34 // current counterrotation and radius shrink values
35 float Corkscrew_radius_cur = Corkscrew_radius;
36
37 typedef struct cscrew_info {            
38         int flags;                                                                                              // flags for the missile
39         
40         // info about the corkscrew effect for this missile
41         vector cen_p;                                                                                   // vector pointing to the "center" of the corkscrew     
42         float radius;                                                                                   // radius of the corkscrew
43         matrix real_orient;                                                                     // the orientation used when calling physics (bashed before rendering)
44         vector last_corkscrew_pos;                                                      // last position along the corkscrew
45 } cscrew_info;
46
47 #define MAX_CORKSCREW_MISSILES  100
48 cscrew_info     Corkscrew_missiles[MAX_CORKSCREW_MISSILES];
49
50 // ------------------------------------------------------------------
51 // cscrew_level_init()
52 //
53 // Called at the start of each new mission
54 //
55 void cscrew_level_init()
56 {
57         memset(Corkscrew_missiles, 0, sizeof(cscrew_info) * MAX_CORKSCREW_MISSILES);
58 }
59
60 // ------------------------------------------------------------------
61 // cscrew_maybe_fire_missile()
62 //
63 // Check if there are any swarm missiles to fire, and if enough time
64 // has elapsed since last one fired, go ahead and fire it.
65 //
66 // This is called once per ship frame in ship_move()
67 //
68 void cscrew_maybe_fire_missile(int shipnum)
69 {
70         ship                    *sp;
71         ship_weapon *swp;
72         int                     weapon_info_index;
73
74         Assert(shipnum >= 0 && shipnum < MAX_SHIPS );
75         sp = &Ships[shipnum];
76
77         // make sure we're supposed to be firing some missiles
78         if ( sp->num_corkscrew_to_fire <= 0 ){
79                 return;
80         }
81
82         // make sure we have a valid weapon band
83         swp = &sp->weapons;
84         if ( swp->current_secondary_bank == -1 ) {
85                 sp->num_corkscrew_to_fire = 0;
86                 return;
87         }
88
89         weapon_info_index = swp->secondary_bank_weapons[swp->current_secondary_bank];
90         Assert( weapon_info_index >= 0 && weapon_info_index < MAX_WEAPON_TYPES );
91
92         // if current secondary bank is not a corkscrew missile, return
93         if ( !(Weapon_info[weapon_info_index].wi_flags & WIF_CORKSCREW) ) {
94                 sp->num_corkscrew_to_fire = 0;
95                 return;
96         }
97
98         if ( timestamp_elapsed(sp->next_corkscrew_fire) ) {
99                 sp->next_corkscrew_fire = timestamp(Corkscrew_missile_delay);
100                 ship_fire_secondary( &Objects[sp->objnum], 1 );
101                 sp->num_corkscrew_to_fire--;
102         }
103 }
104
105 // ------------------------------------------------------------------
106 // cscrew_create()
107 //
108 //      Get a free corkscrew missile entry, and initialize the struct members
109 //
110 int cscrew_create(object *obj)
111 {
112         int                     i;
113         cscrew_info     *cscrewp = NULL;        
114         
115         for ( i = 0; i < MAX_CORKSCREW_MISSILES; i++ ) {
116                 cscrewp = &Corkscrew_missiles[i];
117                 if ( !(cscrewp->flags & CS_FLAG_USED) ) {
118                         break;          
119                 }
120         }
121
122         if ( i >= MAX_CORKSCREW_MISSILES ) {
123                 nprintf(("Warning","No more corkscrew missiles are available\n"));
124                 return -1;
125         }
126
127         // mark the guy as "used"
128         cscrewp->flags = CS_FLAG_USED;
129
130         // determine if he is counterrotating
131         if(Corkscrew_counterrotate){
132                 if(frand_range(0.0f, 1.0f) < 0.5f){
133                         cscrewp->flags |= CS_FLAG_COUNTER;
134                 }               
135         }
136
137         // get the "center" pointing vector
138         vector neg;
139         neg = obj->orient.uvec;
140         if(Corkscrew_down_first){
141                 vm_vec_negate(&neg);
142         }
143         vm_vec_scale_add2(&cscrewp->cen_p, &neg, Corkscrew_radius);     
144
145         // move the missile up so that the corkscrew point is at the muzzle of the gun
146         // vm_vec_scale_add2(&obj->pos, &obj->orient.uvec, Corkscrew_radius);
147
148         // store some initial helix params
149         cscrewp->real_orient = obj->orient;
150         cscrewp->last_corkscrew_pos = obj->pos;
151         
152         return i;
153 }
154
155 // ------------------------------------------------------------------
156 // cscrew_delete()
157 //
158 //
159 void cscrew_delete(int i)
160 {       
161         if ( !(Corkscrew_missiles[i].flags & CS_FLAG_USED) ) {
162                 Int3(); 
163         }
164
165         memset(&Corkscrew_missiles[i], 0, sizeof(cscrew_info));
166 }
167
168 // pre process the corkscrew weapon by putting him in the "center" of his corkscrew
169 void cscrew_process_pre(object *objp)
170 {               
171         cscrew_info *ci;
172
173         // check stuff
174         Assert(objp->type == OBJ_WEAPON);       
175         Assert(Weapons[objp->instance].cscrew_index >= 0);
176         Assert(Corkscrew_missiles[Weapons[objp->instance].cscrew_index].flags & CS_FLAG_USED);
177
178         ci = &Corkscrew_missiles[Weapons[objp->instance].cscrew_index];
179
180         // unrotate the missile itself
181         if(Corkscrew_helix){
182                 // restore the "real" matrix now
183                 objp->orient = ci->real_orient;         
184         }
185         // move the missile back to the center of the corkscrew 
186         vm_vec_add2(&objp->pos, &ci->cen_p);    
187 }
188
189 // post process the corkscrew weapon by putting him back to the right spot on his corkscrew
190 void cscrew_process_post(object *objp)
191 {       
192         vector cen, neg;
193         vector new_pt;  
194         weapon *wp;
195         weapon_info *wip;
196         cscrew_info *ci;
197         float twist_val;
198
199         // check stuff
200         Assert(objp->type == OBJ_WEAPON);       
201         Assert(Weapons[objp->instance].cscrew_index >= 0);
202         Assert(Corkscrew_missiles[Weapons[objp->instance].cscrew_index].flags & CS_FLAG_USED);
203
204         // get various useful pointers
205         wp = &Weapons[objp->instance];
206         wip = &Weapon_info[wp->weapon_info_index];
207         ci = &Corkscrew_missiles[wp->cscrew_index];
208
209         // move to the outside of the corkscrew                 
210         neg = ci->cen_p;
211         cen = objp->pos;        
212         vm_vec_negate(&neg);            
213         vm_vec_add2(&objp->pos, &neg);          
214
215         // determine what direction (clockwise or counterclockwise) the missile will spin       
216         twist_val = ci->flags & CS_FLAG_COUNTER ? -Corkscrew_twist : Corkscrew_twist;
217         twist_val *= flFrametime;       
218         
219         // rotate the missile position
220         vm_rot_point_around_line(&new_pt, &objp->pos, twist_val, &cen, &objp->orient.fvec);     
221         objp->pos = new_pt;
222
223         // rotate the missile itself
224         if(Corkscrew_helix){            
225                 vector dir;
226         
227                 // compute a "fake" orient and store the old one for safekeeping
228                 ci->real_orient = objp->orient;
229                 vm_vec_sub(&dir, &objp->pos, &ci->last_corkscrew_pos);
230                 vm_vec_normalize(&dir);
231                 vm_vector_2_matrix(&objp->orient, &dir, NULL, NULL);    
232                 
233                 // mark down this position so we can orient nicely _next_ frame
234                 ci->last_corkscrew_pos = objp->pos;
235         }       
236
237         // get the new center pointing vector
238         vm_vec_sub(&ci->cen_p, &cen, &objp->pos);
239
240         // do trail stuff here
241         if ( wp->trail_num > -1 )       {
242                 if (trail_stamp_elapsed(wp->trail_num)) {
243                         trail_add_segment( wp->trail_num, &objp->pos );
244                         trail_set_stamp(wp->trail_num);
245                 } else {
246                         trail_set_segment( wp->trail_num, &objp->pos );
247                 }
248         }       
249 }
250
251 // debug console functionality
252 void cscrew_display_dcf()
253 {
254         dc_printf("Corkscrew settings\n\n");
255         dc_printf("Delay (cscrew_delay) : %d\n",Corkscrew_missile_delay);
256         dc_printf("Count (cscrew_count) : %d\n",Corkscrew_num_missiles_fired);
257         dc_printf("Radius (cscrew_radius) :     %f\n",Corkscrew_radius);
258         dc_printf("Twist (cscrew_twist) : %f\n",Corkscrew_twist);       
259         if(Corkscrew_helix){
260                 dc_printf("Helix (cscrew_helix): ON\n");
261         } else {
262                 dc_printf("Helix (cscrew_helix): OFF\n");
263         }
264         if(Corkscrew_counterrotate){
265                 dc_printf("Counterrotate (cscrew_counter): ON\n");
266         } else {
267                 dc_printf("Counterrotate (cscrew_counter): OFF\n");
268         }
269         if(Corkscrew_shrink){
270                 dc_printf("Shrink (cscrew_shrink): ON\n");
271         } else {
272                 dc_printf("Shrink (cscrew_shrink): OFF\n");
273         }
274         dc_printf("Corkscrew shrink (cscrew_shrinkval): %f\n", Corkscrew_shrink_val);
275         if(Corkscrew_down_first){
276                 dc_printf("Corkscrew down first : ON\n");
277         } else {
278                 dc_printf("Corkscrew down first : OFF\n");
279         }
280 }
281
282 DCF(cscrew, "Listing of corkscrew missile debug console functions")
283 {
284         cscrew_display_dcf();
285 }
286
287 DCF(cscrew_delay, "Change the delay between corkscrew firing")
288 {       
289         dc_get_arg(ARG_INT);
290         if(Dc_arg_type & ARG_INT){
291                 Corkscrew_missile_delay = Dc_arg_int;           
292         }
293
294         cscrew_display_dcf();
295 }
296
297 DCF(cscrew_count, "Change the # of corkscrew missiles fired")
298 {       
299         dc_get_arg(ARG_INT);
300         if(Dc_arg_type & ARG_INT){
301                 Corkscrew_num_missiles_fired = Dc_arg_int;              
302         }
303
304         cscrew_display_dcf();
305 }
306
307 DCF(cscrew_radius, "Change the radius of corkscrew missiles")
308 {       
309         dc_get_arg(ARG_FLOAT);
310         if(Dc_arg_type & ARG_FLOAT){
311                 Corkscrew_radius = Dc_arg_float;
312         }
313
314         cscrew_display_dcf();
315 }
316
317 DCF(cscrew_twist, "Change the rate of the corkscrew twist")
318 {
319         dc_get_arg(ARG_FLOAT);
320         if(Dc_arg_type & ARG_FLOAT){
321                 Corkscrew_twist = Dc_arg_float;
322         }
323
324         cscrew_display_dcf();
325 }
326
327 DCF(cscrew_helix, "Attempt to orient missile nicely along the corkscrew")
328 {
329         Corkscrew_helix = !Corkscrew_helix;
330
331         cscrew_display_dcf();
332 }
333
334 DCF(cscrew_counter, "Counterrotate every other missile")
335 {
336         Corkscrew_counterrotate = !Corkscrew_counterrotate;
337
338         cscrew_display_dcf();
339 }
340
341 DCF(cscrew_shrink, "Shrink the radius of every other missile")
342 {
343         Corkscrew_shrink = !Corkscrew_shrink;
344
345         cscrew_display_dcf();
346 }
347
348 DCF(cscrew_shrinkval, "Change the rate at which the radii shrink")
349 {
350         dc_get_arg(ARG_FLOAT);
351         if(Dc_arg_type & ARG_FLOAT){
352                 Corkscrew_shrink_val = Dc_arg_float;
353         }
354
355         cscrew_display_dcf();
356 }
357
358 DCF(cscrew_down, "Cause the missile to spiral down first")
359 {
360         Corkscrew_down_first = !Corkscrew_down_first;
361
362         cscrew_display_dcf();
363 }