]> icculus.org git repositories - btb/d2x.git/blob - main/laser.c
use the orientation parameter of g3_draw_bitmap
[btb/d2x.git] / main / laser.c
1 /*
2 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
3 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
4 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
5 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
6 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
7 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
8 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
9 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
10 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
11 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
12 */
13
14 /*
15  *
16  * This will contain the laser code
17  *
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include <conf.h>
22 #endif
23
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <time.h>
27
28 #include "inferno.h"
29 #include "args.h"
30 #include "dxxerror.h"
31 #include "mono.h"
32 #include "key.h"
33 #include "texmap.h"
34 #include "timer.h"
35 #ifdef TACTILE
36 #include "tactile.h"
37 #endif
38
39
40 int Laser_rapid_fire = 0;
41
42
43 object *Guided_missile[MAX_PLAYERS]={NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
44 int Guided_missile_sig[MAX_PLAYERS]={-1,-1,-1,-1,-1,-1,-1,-1};
45
46 int find_homing_object_complete(vms_vector *curpos, object *tracker, int track_obj_type1, int track_obj_type2);
47
48 extern char Multi_is_guided;
49 extern char BounceCheat;
50                                                                                                 
51 extern void newdemo_record_guided_end(void);
52 extern void newdemo_record_guided_start(void);
53
54 int find_homing_object(vms_vector *curpos, object *tracker);
55
56 //---------------------------------------------------------------------------------
57 // Called by render code.... determines if the laser is from a robot or the
58 // player and calls the appropriate routine.
59
60 void Laser_render(object *obj)
61 {
62
63 //      Commented out by John (sort of, typed by Mike) on 6/8/94
64 #if 0
65         switch( obj->id )       {
66         case WEAPON_TYPE_WEAK_LASER:
67         case WEAPON_TYPE_STRONG_LASER:
68         case WEAPON_TYPE_CANNON_BALL:
69         case WEAPON_TYPE_MISSILE:
70                 break;
71         default:
72                 Error( "Invalid weapon type in Laser_render\n" );
73         }
74 #endif
75         
76         switch( Weapon_info[obj->id].render_type )      {
77         case WEAPON_RENDER_LASER:
78                 Int3(); // Not supported anymore!
79                 //Laser_draw_one( OBJECT_NUMBER(obj), Weapon_info[obj->id].bitmap );
80                 break;
81         case WEAPON_RENDER_BLOB:
82                 draw_object_blob(obj, Weapon_info[obj->id].bitmap  );
83                 break;
84         case WEAPON_RENDER_POLYMODEL:
85                 break;
86         case WEAPON_RENDER_VCLIP:
87                 Int3(); //      Oops, not supported, type added by mk on 09/09/94, but not for lasers...
88         default:
89                 Error( "Invalid weapon render type in Laser_render\n" );
90         }
91
92 }
93
94 //---------------------------------------------------------------------------------
95 // Draws a texture-mapped laser bolt
96
97 //void Laser_draw_one( int objnum, grs_bitmap * bmp )
98 //{
99 //      int t1, t2, t3;
100 //      g3s_point p1, p2;
101 //      object *obj;
102 //      vms_vector start_pos,end_pos;
103 //
104 //      obj = &Objects[objnum];
105 //
106 //      start_pos = obj->pos;
107 //      vm_vec_scale_add(&end_pos,&start_pos,&obj->orient.fvec,-Laser_length);
108 //
109 //      g3_rotate_point(&p1,&start_pos);
110 //      g3_rotate_point(&p2,&end_pos);
111 //
112 //      t1 = Lighting_on;
113 //      t2 = Interpolation_method;
114 //      t3 = Transparency_on;
115 //
116 //      Lighting_on  = 0;
117 //      //Interpolation_method = 3;     // Full perspective
118 //      Interpolation_method = 1;       // Linear
119 //      Transparency_on = 1;
120 //
121 //      //gr_setcolor( gr_getcolor(31,15,0));
122 //      //g3_draw_line_ptrs(p1,p2);
123 //      //g3_draw_rod(p1,0x2000,p2,0x2000);
124 //      //g3_draw_rod(p1,Laser_width,p2,Laser_width);
125 //      g3_draw_rod_tmap(bmp,&p2,Laser_width,&p1,Laser_width,0);
126 //      Lighting_on = t1;
127 //      Interpolation_method = t2;
128 //      Transparency_on = t3;
129 //
130 //}
131
132 //      Changed by MK on 09/07/94
133 //      I want you to be able to blow up your own bombs.
134 //      AND...Your proximity bombs can blow you up if they're 2.0 seconds or more old.
135 //      Changed by MK on 06/06/95: Now must be 4.0 seconds old.  Much valid Net-complaining.
136 int laser_are_related( int o1, int o2 )
137 {
138         if ( (o1<0) || (o2<0) ) 
139                 return 0;
140
141         // See if o2 is the parent of o1
142         if ( Objects[o1].type == OBJ_WEAPON  )
143                 if ( (Objects[o1].ctype.laser_info.parent_num==o2) && (Objects[o1].ctype.laser_info.parent_signature==Objects[o2].signature) )
144                 {
145                         //      o1 is a weapon, o2 is the parent of 1, so if o1 is PROXIMITY_BOMB and o2 is player, they are related only if o1 < 2.0 seconds old
146                         if ((Objects[o1].id == PHOENIX_ID && (GameTime > Objects[o1].ctype.laser_info.creation_time + F1_0/4)) || 
147                            (Objects[o1].id == GUIDEDMISS_ID && (GameTime > Objects[o1].ctype.laser_info.creation_time + F1_0*2)) || 
148                                 (((Objects[o1].id == PROXIMITY_ID) || (Objects[o1].id == SUPERPROX_ID)) && (GameTime > Objects[o1].ctype.laser_info.creation_time + F1_0*4))) {
149                                 return 0;
150                         } else
151                                 return 1;
152                 }
153
154         // See if o1 is the parent of o2
155         if ( Objects[o2].type == OBJ_WEAPON  )
156         {
157                 if ( (Objects[o2].ctype.laser_info.parent_num==o1) && (Objects[o2].ctype.laser_info.parent_signature==Objects[o1].signature) )
158                 {
159                         //      o2 is a weapon, o1 is the parent of 2, so if o2 is PROXIMITY_BOMB and o1 is player, they are related only if o1 < 2.0 seconds old
160                         if ((Objects[o2].id == PHOENIX_ID && (GameTime > Objects[o2].ctype.laser_info.creation_time + F1_0/4)) || 
161                            (Objects[o2].id == GUIDEDMISS_ID && (GameTime > Objects[o2].ctype.laser_info.creation_time + F1_0*2)) || 
162                                 (((Objects[o2].id == PROXIMITY_ID) || (Objects[o2].id == SUPERPROX_ID)) && (GameTime > Objects[o2].ctype.laser_info.creation_time + F1_0*4))) {
163                                 return 0;
164                         } else
165                                 return 1;
166                 }
167         }
168
169         // They must both be weapons
170         if ( Objects[o1].type != OBJ_WEAPON || Objects[o2].type != OBJ_WEAPON ) 
171                 return 0;
172
173         //      Here is the 09/07/94 change -- Siblings must be identical, others can hurt each other
174         // See if they're siblings...
175         //      MK: 06/08/95, Don't allow prox bombs to detonate for 3/4 second.  Else too likely to get toasted by your own bomb if hit by opponent.
176         if ( Objects[o1].ctype.laser_info.parent_signature==Objects[o2].ctype.laser_info.parent_signature )
177         {
178                 if (Objects[o1].id == PROXIMITY_ID  || Objects[o2].id == PROXIMITY_ID || Objects[o1].id == SUPERPROX_ID || Objects[o2].id == SUPERPROX_ID) {
179                         //      If neither is older than 1/2 second, then can't blow up!
180                         if ((GameTime > (Objects[o1].ctype.laser_info.creation_time + F1_0/2)) || (GameTime > (Objects[o2].ctype.laser_info.creation_time + F1_0/2)))
181                                 return 0;
182                         else
183                                 return 1;
184                 } else
185                         return 1;
186         }
187
188         //      Anything can cause a collision with a robot super prox mine.
189         if (Objects[o1].id == ROBOT_SUPERPROX_ID || Objects[o2].id == ROBOT_SUPERPROX_ID ||
190                  Objects[o1].id == PROXIMITY_ID || Objects[o2].id == PROXIMITY_ID ||
191                  Objects[o1].id == SUPERPROX_ID || Objects[o2].id == SUPERPROX_ID ||
192                  Objects[o1].id == PMINE_ID || Objects[o2].id == PMINE_ID)
193                 return 0;
194
195         return 1;
196 }
197
198 //--unused-- int Muzzle_scale=2;
199 int Laser_offset=0;
200
201 void do_muzzle_stuff(int segnum, vms_vector *pos)
202 {
203         Muzzle_data[Muzzle_queue_index].create_time = timer_get_fixed_seconds();
204         Muzzle_data[Muzzle_queue_index].segnum = segnum;
205         Muzzle_data[Muzzle_queue_index].pos = *pos;
206         Muzzle_queue_index++;
207         if (Muzzle_queue_index >= MUZZLE_QUEUE_MAX)
208                 Muzzle_queue_index = 0;
209 }
210
211 //creates a weapon object
212 int create_weapon_object(int weapon_type,int segnum,vms_vector *position)
213 {
214         int rtype=-1;
215         fix laser_radius = -1;
216         int objnum;
217         object *obj;
218
219         switch( Weapon_info[weapon_type].render_type )  {
220
221                 case WEAPON_RENDER_BLOB:
222                         rtype = RT_LASER;                       // Render as a laser even if blob (see render code above for explanation)
223                         laser_radius = Weapon_info[weapon_type].blob_size;
224                         break;
225                 case WEAPON_RENDER_POLYMODEL:
226                         laser_radius = 0;       //      Filled in below.
227                         rtype = RT_POLYOBJ;
228                         break;
229                 case WEAPON_RENDER_LASER:
230                         Int3();         // Not supported anymore
231                         break;
232                 case WEAPON_RENDER_NONE:
233                         rtype = RT_NONE;
234                         laser_radius = F1_0;
235                         break;
236                 case WEAPON_RENDER_VCLIP:
237                         rtype = RT_WEAPON_VCLIP;
238                         laser_radius = Weapon_info[weapon_type].blob_size;
239                         break;
240                 default:
241                         Error( "Invalid weapon render type in Laser_create_new\n" );
242         }
243
244         Assert(laser_radius != -1);
245         Assert(rtype != -1);
246
247         objnum = obj_create( OBJ_WEAPON, weapon_type, segnum, position, NULL, laser_radius, CT_WEAPON, MT_PHYSICS, rtype );
248         if (objnum == -1)
249                 return -1;
250         
251         obj = &Objects[objnum];
252
253         if (Weapon_info[weapon_type].render_type == WEAPON_RENDER_POLYMODEL) {
254                 obj->rtype.pobj_info.model_num = Weapon_info[obj->id].model_num;
255                 obj->size = fixdiv(Polygon_models[obj->rtype.pobj_info.model_num].rad,Weapon_info[obj->id].po_len_to_width_ratio);
256         }
257
258         obj->mtype.phys_info.mass = Weapon_info[weapon_type].mass;
259         obj->mtype.phys_info.drag = Weapon_info[weapon_type].drag;
260         vm_vec_zero(&obj->mtype.phys_info.thrust);
261
262         if (Weapon_info[weapon_type].bounce==1)
263                 obj->mtype.phys_info.flags |= PF_BOUNCE;
264
265         if (Weapon_info[weapon_type].bounce==2 || BounceCheat)
266                 obj->mtype.phys_info.flags |= PF_BOUNCE+PF_BOUNCES_TWICE;
267
268
269         return objnum;
270 }
271
272 extern int Doing_lighting_hack_flag;
273
274 //      -------------------------------------------------------------------------------------------------------------------------------
275 //      ***** HEY ARTISTS!! *****
276 //      Here are the constants you're looking for! --MK
277
278 //      Change the following constants to affect the look of the omega cannon.
279 //      Changing these constants will not affect the damage done.
280 //      WARNING: If you change DESIRED_OMEGA_DIST and MAX_OMEGA_BLOBS, you don't merely change the look of the cannon,
281 //      you change its range.  If you decrease DESIRED_OMEGA_DIST, you decrease how far the gun can fire.
282 #define MIN_OMEGA_BLOBS         3                               //      No matter how close the obstruction, at this many blobs created.
283 #define MIN_OMEGA_DIST                  (F1_0*3)                //      At least this distance between blobs, unless doing so would violate MIN_OMEGA_BLOBS
284 #define DESIRED_OMEGA_DIST      (F1_0*5)                //      This is the desired distance between blobs.  For distances > MIN_OMEGA_BLOBS*DESIRED_OMEGA_DIST, but not very large, this will apply.
285 #define MAX_OMEGA_BLOBS         16                              //      No matter how far away the obstruction, this is the maximum number of blobs.
286 #define MAX_OMEGA_DIST                  (MAX_OMEGA_BLOBS * DESIRED_OMEGA_DIST)          //      Maximum extent of lightning blobs.
287
288 //      Additionally, several constants which apply to homing objects in general control the behavior of the Omega Cannon.
289 //      They are defined in laser.h.  They are copied here for reference.  These values are valid on 1/10/96:
290 //      If you want the Omega Cannon view cone to be different than the Homing Missile viewcone, contact MK to make the change.
291 //      (Unless you are a programmer, in which case, do it yourself!)
292 #define OMEGA_MIN_TRACKABLE_DOT                 (15*F1_0/16)            //      Larger values mean narrower cone.  F1_0 means damn near impossible.  0 means 180 degree field of view.
293 #define OMEGA_MAX_TRACKABLE_DIST                MAX_OMEGA_DIST  //      An object must be at least this close to be tracked.
294
295 //      Note, you don't need to change these constants.  You can control damage and energy consumption by changing the
296 //      usual bitmaps.tbl parameters.
297 #define OMEGA_DAMAGE_SCALE                      32                              //      Controls how much damage is done.  This gets multiplied by FrameTime and then again by the damage specified in bitmaps.tbl in the $WEAPON line.
298 #define OMEGA_ENERGY_CONSUMPTION        16                              //      Controls how much energy is consumed.  This gets multiplied by FrameTime and then again by the energy parameter from bitmaps.tbl.
299 //      -------------------------------------------------------------------------------------------------------------------------------
300
301 void delete_old_omega_blobs(object *parent_objp)
302 {
303         int     i;
304         int     parent_num;
305         int     count = 0;
306
307         parent_num = parent_objp->ctype.laser_info.parent_num;
308
309         for (i=0; i<=Highest_object_index; i++)
310                 if (Objects[i].type == OBJ_WEAPON)
311                         if (Objects[i].id == OMEGA_ID)
312                                 if (Objects[i].ctype.laser_info.parent_num == parent_num) {
313                                         obj_delete(i);
314                                         count++;
315                                 }
316
317         mprintf((0, "%i Omega blobs deleted in frame %i\n", count, FrameCount));
318 }
319
320 // ---------------------------------------------------------------------------------
321 void create_omega_blobs(int firing_segnum, vms_vector *firing_pos, vms_vector *goal_pos, object *parent_objp)
322 {
323         int                     i, last_segnum, last_created_objnum = -1;
324         vms_vector      vec_to_goal;
325         fix                     dist_to_goal;
326         int                     num_omega_blobs;
327         fix                     omega_blob_dist;
328         vms_vector      omega_delta_vector;
329         vms_vector      blob_pos, perturb_vec;
330         fix                     perturb_array[MAX_OMEGA_BLOBS];
331
332         if (Game_mode & GM_MULTI)
333                 delete_old_omega_blobs(parent_objp);
334
335         vm_vec_sub(&vec_to_goal, goal_pos, firing_pos);
336
337         dist_to_goal = vm_vec_normalize_quick(&vec_to_goal);
338
339         if (dist_to_goal < MIN_OMEGA_BLOBS * MIN_OMEGA_DIST) {
340                 omega_blob_dist = MIN_OMEGA_DIST;
341                 num_omega_blobs = dist_to_goal/omega_blob_dist;
342                 if (num_omega_blobs == 0)
343                         num_omega_blobs = 1;
344         } else {
345                 omega_blob_dist = DESIRED_OMEGA_DIST;
346                 num_omega_blobs = dist_to_goal / omega_blob_dist;
347                 if (num_omega_blobs > MAX_OMEGA_BLOBS) {
348                         num_omega_blobs = MAX_OMEGA_BLOBS;
349                         omega_blob_dist = dist_to_goal / num_omega_blobs;
350                 } else if (num_omega_blobs < MIN_OMEGA_BLOBS) {
351                         num_omega_blobs = MIN_OMEGA_BLOBS;
352                         omega_blob_dist = dist_to_goal / num_omega_blobs;
353                 }
354         }
355
356         omega_delta_vector = vec_to_goal;
357         vm_vec_scale(&omega_delta_vector, omega_blob_dist);
358
359         //      Now, create all the blobs
360         blob_pos = *firing_pos;
361         last_segnum = firing_segnum;
362
363         //      If nearby, don't perturb vector.  If not nearby, start halfway out.
364         if (dist_to_goal < MIN_OMEGA_DIST*4) {
365                 for (i=0; i<num_omega_blobs; i++)
366                         perturb_array[i] = 0;
367         } else {
368                 vm_vec_scale_add2(&blob_pos, &omega_delta_vector, F1_0/2);      //      Put first blob half way out.
369                 for (i=0; i<num_omega_blobs/2; i++) {
370                         perturb_array[i] = F1_0*i + F1_0/4;
371                         perturb_array[num_omega_blobs-1-i] = F1_0*i;
372                 }
373         }
374
375         //      Create random perturbation vector, but favor _not_ going up in player's reference.
376         make_random_vector(&perturb_vec);
377         vm_vec_scale_add2(&perturb_vec, &parent_objp->orient.uvec, -F1_0/2);
378
379         Doing_lighting_hack_flag = 1;   //      Ugly, but prevents blobs which are probably outside the mine from killing framerate.
380
381         for (i=0; i<num_omega_blobs; i++) {
382                 vms_vector      temp_pos;
383                 int                     blob_objnum, segnum;
384
385                 //      This will put the last blob right at the destination object, causing damage.
386                 if (i == num_omega_blobs-1)
387                         vm_vec_scale_add2(&blob_pos, &omega_delta_vector, 15*F1_0/32);  //      Move last blob another (almost) half section
388
389                 //      Every so often, re-perturb blobs
390                 if ((i % 4) == 3) {
391                         vms_vector      temp_vec;
392
393                         make_random_vector(&temp_vec);
394                         vm_vec_scale_add2(&perturb_vec, &temp_vec, F1_0/4);
395                 }
396
397                 vm_vec_scale_add(&temp_pos, &blob_pos, &perturb_vec, perturb_array[i]);
398
399                 segnum = find_point_seg(&temp_pos, last_segnum);
400                 if (segnum != -1) {
401                         object          *objp;
402
403                         last_segnum = segnum;
404                         blob_objnum = obj_create(OBJ_WEAPON, OMEGA_ID, segnum, &temp_pos, NULL, 0, CT_WEAPON, MT_PHYSICS, RT_WEAPON_VCLIP );
405                         if (blob_objnum == -1)
406                                 break;
407
408                         last_created_objnum = blob_objnum;
409
410                         objp = &Objects[blob_objnum];
411
412                         objp->lifeleft = ONE_FRAME_TIME;
413                         objp->mtype.phys_info.velocity = vec_to_goal;
414
415                         //      Only make the last one move fast, else multiple blobs might collide with target.
416                         vm_vec_scale(&objp->mtype.phys_info.velocity, F1_0*4);
417
418                         objp->size = Weapon_info[objp->id].blob_size;
419
420                         objp->shields = fixmul(OMEGA_DAMAGE_SCALE*FrameTime, Weapon_info[objp->id].strength[Difficulty_level]);
421         
422                         objp->ctype.laser_info.parent_type                      = parent_objp->type;
423                         objp->ctype.laser_info.parent_signature = parent_objp->signature;
424                         objp->ctype.laser_info.parent_num       = OBJECT_NUMBER(parent_objp);
425                         objp->movement_type = MT_NONE;  //      Only last one moves, that will get bashed below.
426
427                 }
428
429                 vm_vec_add2(&blob_pos, &omega_delta_vector);
430
431         }
432
433         //      Make last one move faster, but it's already moving at speed = F1_0*4.
434         if (last_created_objnum != -1) {
435                 vm_vec_scale(&Objects[last_created_objnum].mtype.phys_info.velocity, Weapon_info[OMEGA_ID].speed[Difficulty_level]/4);
436                 Objects[last_created_objnum].movement_type = MT_PHYSICS;
437         }
438
439         Doing_lighting_hack_flag = 0;
440 }
441
442 #define MIN_OMEGA_CHARGE        (MAX_OMEGA_CHARGE/8)
443 #define OMEGA_CHARGE_SCALE      4                       //      FrameTime / OMEGA_CHARGE_SCALE added to Omega_charge every frame.
444 fix     Omega_charge = MAX_OMEGA_CHARGE;
445
446 #define OMEGA_CHARGE_SCALE      4
447
448 int     Last_omega_fire_frame=0;
449
450 // ---------------------------------------------------------------------------------
451 //      Call this every frame to recharge the Omega Cannon.
452 void omega_charge_frame(void)
453 {
454         fix     delta_charge, old_omega_charge;
455
456         if (Omega_charge == MAX_OMEGA_CHARGE)
457                 return;
458
459         if (!(player_has_weapon(OMEGA_INDEX, 0) & HAS_WEAPON_FLAG))
460                 return;
461
462         if (Player_is_dead)
463                 return;
464
465         if ((Primary_weapon == OMEGA_INDEX) && (Omega_charge == 0) && (Players[Player_num].energy == 0)) {
466                 Primary_weapon--;
467                 auto_select_weapon(0);
468         }
469
470         //      Don't charge while firing.
471         if ((Last_omega_fire_frame == FrameCount) || (Last_omega_fire_frame == FrameCount-1))
472                 return;
473
474         if (Players[Player_num].energy) {
475                 fix     energy_used;
476
477                 old_omega_charge = Omega_charge;
478                 Omega_charge += FrameTime/OMEGA_CHARGE_SCALE;
479                 if (Omega_charge > MAX_OMEGA_CHARGE)
480                         Omega_charge = MAX_OMEGA_CHARGE;
481
482                 delta_charge = Omega_charge - old_omega_charge;
483
484                 energy_used = fixmul(F1_0*190/17, delta_charge);
485                 if (Difficulty_level < 2)
486                         energy_used = fixmul(energy_used, i2f(Difficulty_level+2)/4);
487
488                 Players[Player_num].energy -= energy_used;
489                 if (Players[Player_num].energy < 0)
490                         Players[Player_num].energy = 0;
491         }
492
493
494 }
495
496 // -- fix       Last_omega_muzzle_flash_time;
497
498 // ---------------------------------------------------------------------------------
499 //      *objp is the object firing the omega cannon
500 //      *pos is the location from which the omega bolt starts
501 void do_omega_stuff(object *parent_objp, vms_vector *firing_pos, object *weapon_objp)
502 {
503         int                     lock_objnum, firing_segnum;
504         vms_vector      goal_pos;
505         int                     pnum = parent_objp->id;
506
507         if (pnum == Player_num) {
508                 //      If charge >= min, or (some charge and zero energy), allow to fire.
509                 if (!((Omega_charge >= MIN_OMEGA_CHARGE) || (Omega_charge && !Players[pnum].energy))) {
510                         obj_delete(OBJECT_NUMBER(weapon_objp));
511                         return;
512                 }
513
514                 Omega_charge -= FrameTime;
515                 if (Omega_charge < 0)
516                         Omega_charge = 0;
517
518                 //      Ensure that the lightning cannon can be fired next frame.
519                 Next_laser_fire_time = GameTime+1;
520
521                 Last_omega_fire_frame = FrameCount;
522         }
523
524         weapon_objp->ctype.laser_info.parent_type = OBJ_PLAYER;
525         weapon_objp->ctype.laser_info.parent_num = Players[pnum].objnum;
526         weapon_objp->ctype.laser_info.parent_signature = Objects[Players[pnum].objnum].signature;
527
528         lock_objnum = find_homing_object(firing_pos, weapon_objp);
529
530         firing_segnum = find_point_seg(firing_pos, parent_objp->segnum);
531
532         //      Play sound.
533         if ( parent_objp == Viewer )
534                 digi_play_sample( Weapon_info[weapon_objp->id].flash_sound, F1_0 );
535         else
536                 digi_link_sound_to_pos( Weapon_info[weapon_objp->id].flash_sound, weapon_objp->segnum, 0, &weapon_objp->pos, 0, F1_0 );
537
538         // -- if ((Last_omega_muzzle_flash_time + F1_0/4 < GameTime) || (Last_omega_muzzle_flash_time > GameTime)) {
539         // --   do_muzzle_stuff(firing_segnum, firing_pos);
540         // --   Last_omega_muzzle_flash_time = GameTime;
541         // -- }
542
543         //      Delete the original object.  Its only purpose in life was to determine which object to home in on.
544         obj_delete(OBJECT_NUMBER(weapon_objp));
545
546         //      If couldn't lock on anything, fire straight ahead.
547         if (lock_objnum == -1) {
548                 fvi_query       fq;
549                 fvi_info                hit_data;
550                 int                     fate;
551                 vms_vector      perturb_vec, perturbed_fvec;
552
553                 make_random_vector(&perturb_vec);
554                 vm_vec_scale_add(&perturbed_fvec, &parent_objp->orient.fvec, &perturb_vec, F1_0/16);
555
556                 vm_vec_scale_add(&goal_pos, firing_pos, &perturbed_fvec, MAX_OMEGA_DIST);
557                 fq.startseg = firing_segnum;
558                 if (fq.startseg == -1) {
559                         mprintf((1, "Trying to fire Omega Cannon, but gun is outside mine.  Aborting!\n"));
560                         return;
561                 }
562                 fq.p0                                           = firing_pos;
563                 fq.p1                                           = &goal_pos;
564                 fq.rad                                  = 0;
565                 fq.thisobjnum       = OBJECT_NUMBER(parent_objp);
566                 fq.ignore_obj_list      = NULL;
567                 fq.flags                                        = FQ_IGNORE_POWERUPS | FQ_TRANSPOINT | FQ_CHECK_OBJS;           //what about trans walls???
568
569                 fate = find_vector_intersection(&fq, &hit_data);
570                 if (fate != HIT_NONE) {
571                         Assert(hit_data.hit_seg != -1);         //      How can this be?  We went from inside the mine to outside without hitting anything?
572                         goal_pos = hit_data.hit_pnt;
573                 }
574         } else
575                 goal_pos = Objects[lock_objnum].pos;
576
577         //      This is where we create a pile of omega blobs!
578         create_omega_blobs(firing_segnum, firing_pos, &goal_pos, parent_objp);
579
580 }
581
582 // ---------------------------------------------------------------------------------
583 // Initializes a laser after Fire is pressed 
584 //      Returns object number.
585 int Laser_create_new( vms_vector * direction, vms_vector * position, int segnum, int parent, int weapon_type, int make_sound )
586 {
587         int objnum;
588         object *obj;
589         fix parent_speed, weapon_speed;
590         fix volume;
591         fix laser_length=0;
592
593         Assert( weapon_type < N_weapon_types );
594
595         if ( (weapon_type<0) || (weapon_type>=N_weapon_types) )
596                 weapon_type = 0;
597
598         //      Don't let homing blobs make muzzle flash.
599         if (Objects[parent].type == OBJ_ROBOT)
600                 do_muzzle_stuff(segnum, position);
601
602         objnum = create_weapon_object(weapon_type,segnum,position);
603
604         if ( objnum < 0 ) {
605                 mprintf((1, "Can't create laser - Out of objects!\n" ));
606                 return -1;
607         }
608
609         obj = &Objects[objnum];
610
611         //      Do the special Omega Cannon stuff.  Then return on account of everything that follows does
612         //      not apply to the Omega Cannon.
613         if (weapon_type == OMEGA_ID) {
614                 // Create orientation matrix for tracking purposes.
615                 vm_vector_2_matrix( &obj->orient, direction, &Objects[parent].orient.uvec ,NULL);
616
617                 if (( &Objects[parent] != Viewer ) && (Objects[parent].type != OBJ_WEAPON))     {
618                         // Muzzle flash         
619                         if (Weapon_info[obj->id].flash_vclip > -1 )
620                                 object_create_muzzle_flash( obj->segnum, &obj->pos, Weapon_info[obj->id].flash_size, Weapon_info[obj->id].flash_vclip );
621                 }
622
623                 do_omega_stuff(&Objects[parent], position, obj);
624
625                 return objnum;
626         }
627
628         if (Objects[parent].type == OBJ_PLAYER) {
629                 if (weapon_type == FUSION_ID) {
630
631                         if (Fusion_charge <= 0)
632                                 obj->ctype.laser_info.multiplier = F1_0;
633                         else if (Fusion_charge <= 4*F1_0)
634                                 obj->ctype.laser_info.multiplier = F1_0 + Fusion_charge/2;
635                         else
636                                 obj->ctype.laser_info.multiplier = 4*F1_0;
637
638                 } else if ((weapon_type >= LASER_ID && weapon_type <= MAX_SUPER_LASER_LEVEL) && (Players[Objects[parent].id].flags & PLAYER_FLAGS_QUAD_LASERS))
639                         obj->ctype.laser_info.multiplier = F1_0*3/4;
640                 else if (weapon_type == GUIDEDMISS_ID) {
641                         if (parent==Players[Player_num].objnum) {
642                                 Guided_missile[Player_num]= obj;
643                                 Guided_missile_sig[Player_num] = obj->signature;
644                                 if (Newdemo_state==ND_STATE_RECORDING)
645                                         newdemo_record_guided_start();
646                         }
647                 }
648         }
649
650         //      Make children of smart bomb bounce so if they hit a wall right away, they
651         //      won't detonate.  The frame interval code will clear this bit after 1/2 second.
652         if ((weapon_type == PLAYER_SMART_HOMING_ID) || (weapon_type == SMART_MINE_HOMING_ID) || (weapon_type == ROBOT_SMART_HOMING_ID) || (weapon_type == ROBOT_SMART_MINE_HOMING_ID) || (weapon_type == EARTHSHAKER_MEGA_ID))
653                 obj->mtype.phys_info.flags |= PF_BOUNCE;
654
655         if (Weapon_info[weapon_type].render_type == WEAPON_RENDER_POLYMODEL)
656                 laser_length = Polygon_models[obj->rtype.pobj_info.model_num].rad * 2;
657
658         if (weapon_type == FLARE_ID)
659                 obj->mtype.phys_info.flags |= PF_STICK;         //this obj sticks to walls
660         
661         obj->shields = Weapon_info[obj->id].strength[Difficulty_level];
662         
663         // Fill in laser-specific data
664
665         obj->lifeleft                                                   = Weapon_info[obj->id].lifetime;
666         obj->ctype.laser_info.parent_type               = Objects[parent].type;
667         obj->ctype.laser_info.parent_signature = Objects[parent].signature;
668         obj->ctype.laser_info.parent_num                        = parent;
669
670         //      Assign parent type to highest level creator.  This propagates parent type down from
671         //      the original creator through weapons which create children of their own (ie, smart missile)
672         if (Objects[parent].type == OBJ_WEAPON) {
673                 int     highest_parent = parent;
674                 int     count;
675
676                 count = 0;
677                 while ((count++ < 10) && (Objects[highest_parent].type == OBJ_WEAPON)) {
678                         int     next_parent;
679
680                         next_parent = Objects[highest_parent].ctype.laser_info.parent_num;
681                         if (Objects[next_parent].signature != Objects[highest_parent].ctype.laser_info.parent_signature)
682                                 break;  //      Probably means parent was killed.  Just continue.
683
684                         if (next_parent == highest_parent) {
685                                 Int3(); //      Hmm, object is parent of itself.  This would seem to be bad, no?
686                                 break;
687                         }
688
689                         highest_parent = next_parent;
690
691                         obj->ctype.laser_info.parent_num                        = highest_parent;
692                         obj->ctype.laser_info.parent_type               = Objects[highest_parent].type;
693                         obj->ctype.laser_info.parent_signature = Objects[highest_parent].signature;
694                 }
695         }
696
697         // Create orientation matrix so we can look from this pov
698         //      Homing missiles also need an orientation matrix so they know if they can make a turn.
699         if ((obj->render_type == RT_POLYOBJ) || (Weapon_info[obj->id].homing_flag))
700                 vm_vector_2_matrix( &obj->orient,direction, &Objects[parent].orient.uvec ,NULL);
701
702         if (( &Objects[parent] != Viewer ) && (Objects[parent].type != OBJ_WEAPON))     {
703                 // Muzzle flash         
704                 if (Weapon_info[obj->id].flash_vclip > -1 )
705                         object_create_muzzle_flash( obj->segnum, &obj->pos, Weapon_info[obj->id].flash_size, Weapon_info[obj->id].flash_vclip );
706         }
707
708         volume = F1_0;
709         if (Weapon_info[obj->id].flash_sound > -1 )     {
710                 if (make_sound) {
711                         if ( parent == OBJECT_NUMBER(Viewer) )  {
712                                 if (weapon_type == VULCAN_ID)   // Make your own vulcan gun  1/2 as loud.
713                                         volume = F1_0 / 2;
714                                 digi_play_sample( Weapon_info[obj->id].flash_sound, volume );
715                         } else {
716                                 digi_link_sound_to_pos( Weapon_info[obj->id].flash_sound, obj->segnum, 0, &obj->pos, 0, volume );
717                         }
718                 }
719         }
720
721         //      Fire the laser from the gun tip so that the back end of the laser bolt is at the gun tip.
722         // Move 1 frame, so that the end-tip of the laser is touching the gun barrel.
723         // This also jitters the laser a bit so that it doesn't alias.
724         //      Don't do for weapons created by weapons.
725         if ((Objects[parent].type == OBJ_PLAYER) && (Weapon_info[weapon_type].render_type != WEAPON_RENDER_NONE) && (weapon_type != FLARE_ID)) {
726                 vms_vector      end_pos;
727                 int                     end_segnum;
728
729                 vm_vec_scale_add( &end_pos, &obj->pos, direction, Laser_offset+(laser_length/2) );
730                 end_segnum = find_point_seg(&end_pos, obj->segnum);
731                 if (end_segnum != obj->segnum) {
732                         // mprintf(0, "Warning: Laser tip not in same segment as player.\n");
733                         if (end_segnum != -1) {
734                                 obj->pos = end_pos;
735                                 obj_relink(OBJECT_NUMBER(obj), end_segnum);
736                         } else
737                                 mprintf((0, "Warning: Laser tip outside mine.  Laser not being moved to end of gun.\n"));
738                 } else
739                         obj->pos = end_pos;
740         }
741
742         //      Here's where to fix the problem with objects which are moving backwards imparting higher velocity to their weaponfire.
743         //      Find out if moving backwards.
744         if ((weapon_type == PROXIMITY_ID) || (weapon_type == SUPERPROX_ID)) {
745                 parent_speed = vm_vec_mag_quick(&Objects[parent].mtype.phys_info.velocity);
746                 if (vm_vec_dot(&Objects[parent].mtype.phys_info.velocity, &Objects[parent].orient.fvec) < 0)
747                         parent_speed = -parent_speed;
748         } else
749                 parent_speed = 0;
750
751         weapon_speed = Weapon_info[obj->id].speed[Difficulty_level];
752         if (Weapon_info[obj->id].speedvar != 128) {
753                 fix     randval;
754
755                 //      Get a scale factor between speedvar% and 1.0.
756                 randval = F1_0 - ((d_rand() * Weapon_info[obj->id].speedvar) >> 6);
757                 weapon_speed = fixmul(weapon_speed, randval);
758         }
759
760         //      Ugly hack (too bad we're on a deadline), for homing missiles dropped by smart bomb, start them out slower.
761         if ((obj->id == PLAYER_SMART_HOMING_ID) || (obj->id == SMART_MINE_HOMING_ID) || (obj->id == ROBOT_SMART_HOMING_ID) || (obj->id == ROBOT_SMART_MINE_HOMING_ID) || (obj->id == EARTHSHAKER_MEGA_ID))
762                 weapon_speed /= 4;
763
764         if (Weapon_info[obj->id].thrust != 0)
765                 weapon_speed /= 2;
766
767         vm_vec_copy_scale( &obj->mtype.phys_info.velocity, direction, weapon_speed + parent_speed );
768
769         //      Set thrust 
770         if (Weapon_info[weapon_type].thrust != 0) {
771                 obj->mtype.phys_info.thrust = obj->mtype.phys_info.velocity;
772                 vm_vec_scale(&obj->mtype.phys_info.thrust, fixdiv(Weapon_info[obj->id].thrust, weapon_speed+parent_speed));
773         }
774
775         if ((obj->type == OBJ_WEAPON) && (obj->id == FLARE_ID))
776                 obj->lifeleft += (d_rand()-16384) << 2;         //      add in -2..2 seconds
777
778         //      mprintf( 0, "Weapon speed = %.1f (%.1f)\n", f2fl(Weapon_info[obj->id].speed[Difficulty_level] + parent_speed), f2fl(parent_speed) );
779
780         return objnum;
781 }
782
783 //      -----------------------------------------------------------------------------------------------------------
784 //      Calls Laser_create_new, but takes care of the segment and point computation for you.
785 int Laser_create_new_easy( vms_vector * direction, vms_vector * position, int parent, int weapon_type, int make_sound )
786 {
787         fvi_query       fq;
788         fvi_info                hit_data;
789         object          *pobjp = &Objects[parent];
790         int                     fate;
791
792         //      Find segment containing laser fire position.  If the robot is straddling a segment, the position from
793         //      which it fires may be in a different segment, which is bad news for find_vector_intersection.  So, cast
794         //      a ray from the object center (whose segment we know) to the laser position.  Then, in the call to Laser_create_new
795         //      use the data returned from this call to find_vector_intersection.
796         //      Note that while find_vector_intersection is pretty slow, it is not terribly slow if the destination point is
797         //      in the same segment as the source point.
798
799         fq.p0                                           = &pobjp->pos;
800         fq.startseg                             = pobjp->segnum;
801         fq.p1                                           = position;
802         fq.rad                                  = 0;
803         fq.thisobjnum       = OBJECT_NUMBER(pobjp);
804         fq.ignore_obj_list      = NULL;
805         fq.flags                                        = FQ_TRANSWALL | FQ_CHECK_OBJS;         //what about trans walls???
806
807         fate = find_vector_intersection(&fq, &hit_data);
808         if (fate != HIT_NONE  || hit_data.hit_seg==-1) {
809                 mprintf((1, "Warning: Laser from parent=%i stuck in wall or object, didn't fire!\n", parent));
810                 return -1;
811         }
812
813         return Laser_create_new( direction, &hit_data.hit_pnt, hit_data.hit_seg, parent, weapon_type, make_sound );
814
815 }
816
817 int             Muzzle_queue_index = 0;
818
819 muzzle_info             Muzzle_data[MUZZLE_QUEUE_MAX];
820
821 //      -----------------------------------------------------------------------------------------------------------
822 //      Determine if two objects are on a line of sight.  If so, return true, else return false.
823 //      Calls fvi.
824 int object_to_object_visibility(object *obj1, object *obj2, int trans_type)
825 {
826         fvi_query       fq;
827         fvi_info                hit_data;
828         int                     fate;
829
830         fq.p0                                           = &obj1->pos;
831         fq.startseg                             = obj1->segnum;
832         fq.p1                                           = &obj2->pos;
833         fq.rad                                  = 0x10;
834         fq.thisobjnum       = OBJECT_NUMBER(obj1);
835         fq.ignore_obj_list      = NULL;
836         fq.flags                                        = trans_type;
837
838         fate = find_vector_intersection(&fq, &hit_data);
839
840         if (fate == HIT_WALL)
841                 return 0;
842         else if (fate == HIT_NONE)
843                 return 1;
844         else
845                 Int3();         //      Contact Mike: Oops, what happened?  What is fate?
846                                                 // 2 = hit object (impossible), 3 = bad starting point (bad)
847
848         return 0;
849 }
850
851 fix     Min_trackable_dot = MIN_TRACKABLE_DOT;
852
853 //      -----------------------------------------------------------------------------------------------------------
854 //      Return true if weapon *tracker is able to track object Objects[track_goal], else return false.
855 //      In order for the object to be trackable, it must be within a reasonable turning radius for the missile
856 //      and it must not be obstructed by a wall.
857 int object_is_trackable(int track_goal, object *tracker, fix *dot)
858 {
859         vms_vector      vector_to_goal;
860         object          *objp;
861
862         if (track_goal == -1)
863                 return 0;
864
865         if (Game_mode & GM_MULTI_COOP)
866                 return 0;
867
868         objp = &Objects[track_goal];
869
870         //      Don't track player if he's cloaked.
871         if ((track_goal == Players[Player_num].objnum) && (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED))
872                 return 0;
873
874         //      Can't track AI object if he's cloaked.
875         if (objp->type == OBJ_ROBOT) {
876                 if (objp->ctype.ai_info.CLOAKED)
877                         return 0;
878                 //      Your missiles don't track your escort.
879                 if (Robot_info[objp->id].companion)
880                         if (tracker->ctype.laser_info.parent_type == OBJ_PLAYER)
881                                 return 0;
882         }
883         vm_vec_sub(&vector_to_goal, &objp->pos, &tracker->pos);
884         vm_vec_normalize_quick(&vector_to_goal);
885         *dot = vm_vec_dot(&vector_to_goal, &tracker->orient.fvec);
886
887         if ((*dot < Min_trackable_dot) && (*dot > F1_0*9/10)) {
888                 // -- mprintf((0, "."));
889                 vm_vec_normalize(&vector_to_goal);
890                 *dot = vm_vec_dot(&vector_to_goal, &tracker->orient.fvec);
891         }
892
893 //mprintf((0, " OIT: dot=%5.2f ", f2fl(dot)));
894
895         // mprintf((0, "object_is_trackable: [%3i] %7.3f, min = %7.3f\n", track_goal, f2fl(dot), f2fl(Min_trackable_dot)));
896  
897         if (*dot >= Min_trackable_dot) {
898                 int     rval;
899                 //      dot is in legal range, now see if object is visible
900                 rval =  object_to_object_visibility(tracker, objp, FQ_TRANSWALL);
901 //mprintf((0, " TRACK "));
902                 return rval;
903         } else {
904 //mprintf((0, " LOST! "));
905                 return 0;
906         }
907
908 }
909
910 extern int Robots_kill_robots_cheat;
911
912 //      --------------------------------------------------------------------------------------------
913 int call_find_homing_object_complete(object *tracker, vms_vector *curpos)
914 {
915         if (Game_mode & GM_MULTI) {
916                 if (tracker->ctype.laser_info.parent_type == OBJ_PLAYER) {
917                         //      It's fired by a player, so if robots present, track robot, else track player.
918                         if (Game_mode & GM_MULTI_COOP)
919                                 return find_homing_object_complete( curpos, tracker, OBJ_ROBOT, -1);
920                         else
921                                 return find_homing_object_complete( curpos, tracker, OBJ_PLAYER, OBJ_ROBOT);
922                 } else {
923                         int     goal2_type = -1;
924
925                         if (Robots_kill_robots_cheat)
926                                 goal2_type = OBJ_ROBOT;
927                         Assert(tracker->ctype.laser_info.parent_type == OBJ_ROBOT);
928                         return find_homing_object_complete(curpos, tracker, OBJ_PLAYER, goal2_type);
929                 }               
930         } else
931                 return find_homing_object_complete( curpos, tracker, OBJ_ROBOT, -1);
932 }
933
934 //      --------------------------------------------------------------------------------------------
935 //      Find object to home in on.
936 //      Scan list of objects rendered last frame, find one that satisfies function of nearness to center and distance.
937 int find_homing_object(vms_vector *curpos, object *tracker)
938 {
939         int     i;
940         fix     max_dot = -F1_0*2;
941         int     best_objnum = -1;
942
943         //      Contact Mike: This is a bad and stupid thing.  Who called this routine with an illegal laser type??
944         Assert((Weapon_info[tracker->id].homing_flag) || (tracker->id == OMEGA_ID));
945
946         //      Find an object to track based on game mode (eg, whether in network play) and who fired it.
947
948         if (Game_mode & GM_MULTI)
949                 return call_find_homing_object_complete(tracker, curpos);
950         else {
951                 int     cur_min_trackable_dot;
952
953                 cur_min_trackable_dot = MIN_TRACKABLE_DOT;
954                 if ((tracker->type == OBJ_WEAPON) && (tracker->id == OMEGA_ID))
955                         cur_min_trackable_dot = OMEGA_MIN_TRACKABLE_DOT;
956
957                 //      Not in network mode.  If not fired by player, then track player.
958                 if (tracker->ctype.laser_info.parent_num != Players[Player_num].objnum) {
959                         if (!(Players[Player_num].flags & PLAYER_FLAGS_CLOAKED))
960                                 best_objnum = OBJECT_NUMBER(ConsoleObject);
961                 } else {
962                         int     window_num = -1;
963                         fix     dist, max_trackable_dist;
964
965                         //      Find the window which has the forward view.
966                         for (i=0; i<MAX_RENDERED_WINDOWS; i++)
967                                 if (Window_rendered_data[i].frame >= FrameCount-1)
968                                         if (Window_rendered_data[i].viewer == ConsoleObject)
969                                                 if (!Window_rendered_data[i].rear_view) {
970                                                         window_num = i;
971                                                         break;
972                                                 }
973
974                         //      Couldn't find suitable view from this frame, so do complete search.
975                         if (window_num == -1) {
976                                 mprintf((0, "Note: Calling find_homing_object_complete because no suitable rendered window.\n"));
977                                 return call_find_homing_object_complete(tracker, curpos);
978                         }
979
980                         max_trackable_dist = MAX_TRACKABLE_DIST;
981                         if (tracker->id == OMEGA_ID)
982                                 max_trackable_dist = OMEGA_MAX_TRACKABLE_DIST;
983
984                         //      Not in network mode and fired by player.
985                         for (i=Window_rendered_data[window_num].num_objects-1; i>=0; i--) {
986                                 fix                     dot; //, dist;
987                                 vms_vector      vec_to_curobj;
988                                 int                     objnum = Window_rendered_data[window_num].rendered_objects[i];
989                                 object          *curobjp = &Objects[objnum];
990
991                                 if (objnum == Players[Player_num].objnum)
992                                         continue;
993
994                                 //      Can't track AI object if he's cloaked.
995                                 if (curobjp->type == OBJ_ROBOT) {
996                                         if (curobjp->ctype.ai_info.CLOAKED)
997                                                 continue;
998
999                                         //      Your missiles don't track your escort.
1000                                         if (Robot_info[curobjp->id].companion)
1001                                                 if (tracker->ctype.laser_info.parent_type == OBJ_PLAYER)
1002                                                         continue;
1003                                 }
1004
1005                                 vm_vec_sub(&vec_to_curobj, &curobjp->pos, curpos);
1006                                 dist = vm_vec_normalize_quick(&vec_to_curobj);
1007                                 if (dist < max_trackable_dist) {
1008                                         dot = vm_vec_dot(&vec_to_curobj, &tracker->orient.fvec);
1009
1010                                         // mprintf(0, "Object %i: dist = %7.3f, dot = %7.3f\n", objnum, f2fl(dist), f2fl(dot));
1011
1012                                         //      Note: This uses the constant, not-scaled-by-frametime value, because it is only used
1013                                         //      to determine if an object is initially trackable.  find_homing_object is called on subsequent
1014                                         //      frames to determine if the object remains trackable.
1015                                         // mprintf((0, "find_homing_object:  [%3i] %7.3f, min = %7.3f\n", OBJECT_NUMBER(curobjp), f2fl(dot), f2fl(MIN_TRACKABLE_DOT)));
1016                                         if (dot > cur_min_trackable_dot) {
1017                                                 if (dot > max_dot) {
1018                                                         if (object_to_object_visibility(tracker, &Objects[objnum], FQ_TRANSWALL)) {
1019                                                                 max_dot = dot;
1020                                                                 best_objnum = objnum;
1021                                                         }
1022                                                 }
1023                                         } else if (dot > F1_0 - (F1_0 - cur_min_trackable_dot)*2) {
1024                                                 vm_vec_normalize(&vec_to_curobj);
1025                                                 dot = vm_vec_dot(&vec_to_curobj, &tracker->orient.fvec);
1026                                                 if (dot > cur_min_trackable_dot) {
1027                                                         if (dot > max_dot) {
1028                                                                 if (object_to_object_visibility(tracker, &Objects[objnum], FQ_TRANSWALL)) {
1029                                                                         max_dot = dot;
1030                                                                         best_objnum = objnum;
1031                                                                 }
1032                                                         }
1033                                                 }
1034                                         }
1035                                 }
1036                         }
1037                 }
1038         }
1039
1040         // mprintf(0, "Selecting object #%i\n=n", best_objnum);
1041
1042         return best_objnum;
1043 }
1044
1045 //      --------------------------------------------------------------------------------------------
1046 //      Find object to home in on.
1047 //      Scan list of objects rendered last frame, find one that satisfies function of nearness to center and distance.
1048 //      Can track two kinds of objects.  If you are only interested in one type, set track_obj_type2 to NULL
1049 //      Always track proximity bombs.  --MK, 06/14/95
1050 //      Make homing objects not track parent's prox bombs.
1051 int find_homing_object_complete(vms_vector *curpos, object *tracker, int track_obj_type1, int track_obj_type2)
1052 {
1053         int     objnum;
1054         fix     max_dot = -F1_0*2;
1055         int     best_objnum = -1;
1056         fix     max_trackable_dist;
1057         fix     min_trackable_dot;
1058
1059         //      Contact Mike: This is a bad and stupid thing.  Who called this routine with an illegal laser type??
1060         Assert((Weapon_info[tracker->id].homing_flag) || (tracker->id == OMEGA_ID));
1061
1062         max_trackable_dist = MAX_TRACKABLE_DIST;
1063         min_trackable_dot = MIN_TRACKABLE_DOT;
1064
1065         if (tracker->id == OMEGA_ID) {
1066                 max_trackable_dist = OMEGA_MAX_TRACKABLE_DIST;
1067                 min_trackable_dot = OMEGA_MIN_TRACKABLE_DOT;
1068         }
1069
1070         for (objnum=0; objnum<=Highest_object_index; objnum++) {
1071                 int                     is_proximity = 0;
1072                 fix                     dot, dist;
1073                 vms_vector      vec_to_curobj;
1074                 object          *curobjp = &Objects[objnum];
1075
1076                 if ((curobjp->type != track_obj_type1) && (curobjp->type != track_obj_type2))
1077                 {
1078                         if ((curobjp->type == OBJ_WEAPON) && ((curobjp->id == PROXIMITY_ID) || (curobjp->id == SUPERPROX_ID))) {
1079                                 if (curobjp->ctype.laser_info.parent_signature != tracker->ctype.laser_info.parent_signature)
1080                                         is_proximity = 1;
1081                                 else
1082                                         continue;
1083                         } else
1084                                 continue;
1085                 }
1086
1087                 if (objnum == tracker->ctype.laser_info.parent_num) // Don't track shooter
1088                         continue;
1089
1090                 //      Don't track cloaked players.
1091                 if (curobjp->type == OBJ_PLAYER)
1092                 {
1093                         if (Players[curobjp->id].flags & PLAYER_FLAGS_CLOAKED)
1094                                 continue;
1095                         // Don't track teammates in team games
1096                         #ifdef NETWORK
1097                         if ((Game_mode & GM_TEAM) && (Objects[tracker->ctype.laser_info.parent_num].type == OBJ_PLAYER) && (get_team(curobjp->id) == get_team(Objects[tracker->ctype.laser_info.parent_num].id)))
1098                                 continue;
1099                         #endif
1100                 }
1101
1102                 //      Can't track AI object if he's cloaked.
1103                 if (curobjp->type == OBJ_ROBOT) {
1104                         if (curobjp->ctype.ai_info.CLOAKED)
1105                                 continue;
1106
1107                         //      Your missiles don't track your escort.
1108                         if (Robot_info[curobjp->id].companion)
1109                                 if (tracker->ctype.laser_info.parent_type == OBJ_PLAYER)
1110                                         continue;
1111                 }
1112
1113                 vm_vec_sub(&vec_to_curobj, &curobjp->pos, curpos);
1114                 dist = vm_vec_mag_quick(&vec_to_curobj);
1115
1116                 if (dist < max_trackable_dist) {
1117                         vm_vec_normalize_quick(&vec_to_curobj);
1118                         dot = vm_vec_dot(&vec_to_curobj, &tracker->orient.fvec);
1119                         if (is_proximity)
1120                                 dot = ((dot << 3) + dot) >> 3;          //      I suspect Watcom would be too stupid to figure out the obvious...
1121
1122                         //      Note: This uses the constant, not-scaled-by-frametime value, because it is only used
1123                         //      to determine if an object is initially trackable.  find_homing_object is called on subsequent
1124                         //      frames to determine if the object remains trackable.
1125                         // mprintf((0, "fho_complete:        [%3i] %7.3f, min = %7.3f\n", OBJECT_NUMBER(curobjp), f2fl(dot), f2fl(MIN_TRACKABLE_DOT)));
1126                         if (dot > min_trackable_dot) {
1127                                 // mprintf(0, "Object %i: dist = %7.3f, dot = %7.3f\n", objnum, f2fl(dist), f2fl(dot));
1128                                 if (dot > max_dot) {
1129                                         if (object_to_object_visibility(tracker, &Objects[objnum], FQ_TRANSWALL)) {
1130                                                 max_dot = dot;
1131                                                 best_objnum = objnum;
1132                                         }
1133                                 }
1134                         }
1135                 }
1136
1137         }
1138
1139         // -- mprintf((0, "Selecting object #%i in find_homing_object_complete\n\n", best_objnum));
1140
1141         return best_objnum;
1142 }
1143
1144 //      ------------------------------------------------------------------------------------------------------------
1145 //      See if legal to keep tracking currently tracked object.  If not, see if another object is trackable.  If not, return -1,
1146 //      else return object number of tracking object.
1147 //      Computes and returns a fairly precise dot product.
1148 int track_track_goal(int track_goal, object *tracker, fix *dot)
1149 {
1150         //      Every 8 frames for each object, scan all objects.
1151         if (object_is_trackable(track_goal, tracker, dot) && (((OBJECT_NUMBER(tracker) ^ FrameCount) % 8) != 0)) {
1152                 //mprintf((0, "ttg: QO"));
1153                 return track_goal;
1154         } else if (((OBJECT_NUMBER(tracker) ^ FrameCount) % 4) == 0) {
1155                 int     rval = -2;
1156
1157                 //      If player fired missile, then search for an object, if not, then give up.
1158                 if (Objects[tracker->ctype.laser_info.parent_num].type == OBJ_PLAYER) {
1159                         int     goal_type;
1160
1161                         if (track_goal == -1) 
1162                         {
1163                                 if (Game_mode & GM_MULTI)
1164                                 {
1165                                         if (Game_mode & GM_MULTI_COOP)
1166                                                 rval = find_homing_object_complete( &tracker->pos, tracker, OBJ_ROBOT, -1);
1167                                         else if (Game_mode & GM_MULTI_ROBOTS)           //      Not cooperative, if robots, track either robot or player
1168                                                 rval = find_homing_object_complete( &tracker->pos, tracker, OBJ_PLAYER, OBJ_ROBOT);
1169                                         else            //      Not cooperative and no robots, track only a player
1170                                                 rval = find_homing_object_complete( &tracker->pos, tracker, OBJ_PLAYER, -1);
1171                                 }
1172                                 else
1173                                         rval = find_homing_object_complete(&tracker->pos, tracker, OBJ_PLAYER, OBJ_ROBOT);
1174                         } 
1175                         else 
1176                         {
1177                                 goal_type = Objects[tracker->ctype.laser_info.track_goal].type;
1178                                 if ((goal_type == OBJ_PLAYER) || (goal_type == OBJ_ROBOT))
1179                                         rval = find_homing_object_complete(&tracker->pos, tracker, goal_type, -1);
1180                                 else
1181                                         rval = -1;
1182                         }
1183                 } 
1184                 else {
1185                         int     goal_type, goal2_type = -1;
1186
1187                         if (Robots_kill_robots_cheat)
1188                                 goal2_type = OBJ_ROBOT;
1189
1190                         if (track_goal == -1)
1191                                 rval = find_homing_object_complete(&tracker->pos, tracker, OBJ_PLAYER, goal2_type);
1192                         else {
1193                                 goal_type = Objects[tracker->ctype.laser_info.track_goal].type;
1194                                 rval = find_homing_object_complete(&tracker->pos, tracker, goal_type, goal2_type);
1195                         }
1196                 }
1197
1198                 Assert(rval != -2);             //      This means it never got set which is bad!  Contact Mike.
1199                 return rval;
1200         }
1201
1202 //if (track_goal != -1)
1203 // mprintf((0, "Object %i not tracking anything.\n", OBJECT_NUMBER(tracker)));
1204
1205         return -1;
1206 }
1207
1208 //-------------- Initializes a laser after Fire is pressed -----------------
1209
1210 void Laser_player_fire_spread_delay(object *obj, int laser_type, int gun_num, fix spreadr, fix spreadu, fix delay_time, int make_sound, int harmless)
1211 {
1212         int                     LaserSeg, Fate; 
1213         vms_vector      LaserPos, LaserDir;
1214         fvi_query       fq;
1215         fvi_info                hit_data;
1216         vms_vector      gun_point, *pnt;
1217         vms_matrix      m;
1218         int                     objnum;
1219
1220         create_awareness_event(obj, PA_WEAPON_WALL_COLLISION);
1221
1222         // Find the initial position of the laser
1223         pnt = &Player_ship->gun_points[gun_num];
1224
1225         vm_copy_transpose_matrix(&m,&obj->orient);
1226         vm_vec_rotate(&gun_point,pnt,&m);
1227
1228         vm_vec_add(&LaserPos,&obj->pos,&gun_point);
1229
1230         //      If supposed to fire at a delayed time (delay_time), then move this point backwards.
1231         if (delay_time)
1232                 vm_vec_scale_add2(&LaserPos, &obj->orient.fvec, -fixmul(delay_time, Weapon_info[laser_type].speed[Difficulty_level]));
1233
1234 //      do_muzzle_stuff(obj, &Pos);
1235
1236         //--------------- Find LaserPos and LaserSeg ------------------
1237         fq.p0                                           = &obj->pos;
1238         fq.startseg                             = obj->segnum;
1239         fq.p1                                           = &LaserPos;
1240         fq.rad                                  = 0x10;
1241         fq.thisobjnum       = OBJECT_NUMBER(obj);
1242         fq.ignore_obj_list      = NULL;
1243         fq.flags                                        = FQ_CHECK_OBJS | FQ_IGNORE_POWERUPS;
1244
1245         Fate = find_vector_intersection(&fq, &hit_data);
1246
1247         LaserSeg = hit_data.hit_seg;
1248
1249         if (LaserSeg == -1)             //some sort of annoying error
1250                 return;
1251
1252         //SORT OF HACK... IF ABOVE WAS CORRECT THIS WOULDNT BE NECESSARY.
1253         if ( vm_vec_dist_quick(&LaserPos, &obj->pos) > 0x50000 )
1254                 return;
1255         
1256         if (Fate==HIT_WALL) {
1257                 if (delay_time)
1258                         mprintf((0, "Your DELAYED laser is stuck thru a wall!\n" ));
1259                 else
1260                         mprintf((0, "Your laser is stuck thru a wall!\n" ));
1261                 return;         
1262         }
1263
1264         if (Fate==HIT_OBJECT) {
1265 //              if ( Objects[hit_data.hit_object].type == OBJ_ROBOT )
1266 //                      Objects[hit_data.hit_object].flags |= OF_SHOULD_BE_DEAD;
1267                 mprintf((0, "Your laser is stuck in an object!\n" ));
1268 //              if ( Objects[hit_data.hit_object].type != OBJ_POWERUP )
1269 //                      return;         
1270         //as of 12/6/94, we don't care if the laser is stuck in an object. We
1271         //just fire away normally
1272         }
1273
1274         //      Now, make laser spread out.
1275         LaserDir = obj->orient.fvec;
1276         if ((spreadr != 0) || (spreadu != 0)) {
1277                 vm_vec_scale_add2(&LaserDir, &obj->orient.rvec, spreadr);
1278                 vm_vec_scale_add2(&LaserDir, &obj->orient.uvec, spreadu);
1279         }
1280
1281         objnum = Laser_create_new( &LaserDir, &LaserPos, LaserSeg, OBJECT_NUMBER(obj), laser_type, make_sound );
1282
1283         //      Omega cannon is a hack, not surprisingly.  Don't want to do the rest of this stuff.
1284         if (laser_type == OMEGA_ID)
1285                 return;
1286
1287         if (objnum == -1)
1288                 return;
1289
1290 #ifdef NETWORK
1291         if (laser_type==GUIDEDMISS_ID && Multi_is_guided) {
1292                 mprintf ((0,"Guided missile %s activated!\n",Players[obj->id].callsign));
1293                 Guided_missile[obj->id]=&Objects[objnum];
1294         }
1295
1296         Multi_is_guided=0;
1297 #endif
1298
1299         if (laser_type == CONCUSSION_ID ||
1300                          laser_type == HOMING_ID ||
1301                          laser_type == SMART_ID ||
1302                          laser_type == MEGA_ID ||
1303                          laser_type == FLASH_ID ||
1304                          //laser_type == GUIDEDMISS_ID ||
1305                          //laser_type == SUPERPROX_ID ||
1306                          laser_type == MERCURY_ID ||
1307                          laser_type == EARTHSHAKER_ID)
1308                 if (Missile_viewer == NULL && obj->id==Player_num)
1309                         Missile_viewer = &Objects[objnum];
1310
1311         //      If this weapon is supposed to be silent, set that bit!
1312         if (!make_sound)
1313                 Objects[objnum].flags |= OF_SILENT;
1314
1315         //      If this weapon is supposed to be silent, set that bit!
1316         if (harmless)
1317                 Objects[objnum].flags |= OF_HARMLESS;
1318
1319         //      If the object firing the laser is the player, then indicate the laser object so robots can dodge.
1320         //      New by MK on 6/8/95, don't let robots evade proximity bombs, thereby decreasing uselessness of bombs.
1321         if ((obj == ConsoleObject) && ((Objects[objnum].id != PROXIMITY_ID) && (Objects[objnum].id != SUPERPROX_ID)))
1322                 Player_fired_laser_this_frame = objnum;
1323
1324         if (Weapon_info[laser_type].homing_flag) {
1325                 if (obj == ConsoleObject)
1326                 {
1327                         Objects[objnum].ctype.laser_info.track_goal = find_homing_object(&LaserPos, &Objects[objnum]);
1328                         #ifdef NETWORK
1329                         Network_laser_track = Objects[objnum].ctype.laser_info.track_goal;
1330                         #endif
1331                 }
1332                 #ifdef NETWORK
1333                 else // Some other player shot the homing thing
1334                 {
1335                         Assert(Game_mode & GM_MULTI);
1336                         Objects[objnum].ctype.laser_info.track_goal = Network_laser_track;
1337                 }
1338                 #endif
1339 //              mprintf((0, "Selecting object #%i in find_homing_object_complete\n", Network_laser_track));
1340         }
1341 }
1342
1343 //      -----------------------------------------------------------------------------------------------------------
1344 void Laser_player_fire_spread(object *obj, int laser_type, int gun_num, fix spreadr, fix spreadu, int make_sound, int harmless)
1345 {
1346         Laser_player_fire_spread_delay(obj, laser_type, gun_num, spreadr, spreadu, 0, make_sound, harmless);
1347 }
1348
1349
1350 //      -----------------------------------------------------------------------------------------------------------
1351 void Laser_player_fire(object *obj, int laser_type, int gun_num, int make_sound, int harmless)
1352 {
1353         Laser_player_fire_spread(obj, laser_type, gun_num, 0, 0, make_sound, harmless);
1354 }
1355
1356 //      -----------------------------------------------------------------------------------------------------------
1357 void Flare_create(object *obj)
1358 {
1359         fix     energy_usage;
1360
1361         energy_usage = Weapon_info[FLARE_ID].energy_usage;
1362
1363         if (Difficulty_level < 2)
1364                 energy_usage = fixmul(energy_usage, i2f(Difficulty_level+2)/4);
1365
1366 //      MK, 11/04/95: Allowed to fire flare even if no energy.
1367 // --   if (Players[Player_num].energy >= energy_usage) {
1368                 Players[Player_num].energy -= energy_usage;
1369
1370                 if (Players[Player_num].energy <= 0) {
1371                         Players[Player_num].energy = 0; 
1372                         // -- auto_select_weapon(0);
1373                 }
1374
1375                 Laser_player_fire( obj, FLARE_ID, 6, 1, 0);
1376
1377                 #ifdef NETWORK
1378                 if (Game_mode & GM_MULTI) {
1379                         Network_laser_fired = 1;
1380                         Network_laser_gun = FLARE_ADJUST;
1381                         Network_laser_flags = 0;
1382                         Network_laser_level = 0;
1383                 }
1384                 #endif
1385 // --   }
1386
1387 }
1388
1389 #define HOMING_MISSILE_SCALE    16
1390
1391 #define LIMIT_HOMERS    1
1392 #define HOMER_MAX_FPS   30
1393 #define HOMER_MIN_DELAY (1000 / HOMER_MAX_FPS)
1394
1395 //--------------------------------------------------------------------
1396 //      Set object *objp's orientation to (or towards if I'm ambitious) its velocity.
1397 void homing_missile_turn_towards_velocity(object *objp, vms_vector *norm_vel)
1398 {
1399         vms_vector      new_fvec;
1400
1401 #ifdef LIMIT_HOMERS
1402         static time_t   last_time = -1;
1403         static int nFrames = 1;
1404         time_t this_time, delta_time;
1405         fix frame_time;
1406         int fps;
1407         if (last_time == -1) {
1408                 last_time = clock ();
1409                 frame_time = FrameTime;
1410         } else {
1411                 nFrames++;
1412                 this_time = clock ();
1413                 delta_time = this_time - last_time;
1414                 if (delta_time < HOMER_MIN_DELAY) {
1415                         return;
1416                 } else {
1417                         fps = (int)((1000 + delta_time / 2) / delta_time);
1418                         frame_time = fps ? (f1_0 + fps / 2) / fps : f1_0;
1419                         //                     frame_time /= nFrames;
1420                         nFrames = 0;
1421                         last_time = this_time;
1422                 }
1423         }
1424 #else
1425         fix frame_time = FrameTime;
1426 #endif
1427
1428         new_fvec = *norm_vel;
1429
1430         vm_vec_scale(&new_fvec, frame_time * HOMING_MISSILE_SCALE);
1431         vm_vec_add2(&new_fvec, &objp->orient.fvec);
1432         vm_vec_normalize_quick(&new_fvec);
1433
1434 //      if ((norm_vel->x == 0) && (norm_vel->y == 0) && (norm_vel->z == 0))
1435 //              return;
1436
1437         vm_vector_2_matrix(&objp->orient, &new_fvec, NULL, NULL);
1438 }
1439
1440 //-------------------------------------------------------------------------------------------
1441 //sequence this laser object for this _frame_ (underscores added here to aid MK in his searching!)
1442 void Laser_do_weapon_sequence(object *obj)
1443 {
1444         Assert(obj->control_type == CT_WEAPON);
1445
1446         //      Ok, this is a big hack by MK.
1447         //      If you want an object to last for exactly one frame, then give it a lifeleft of ONE_FRAME_TIME
1448         if (obj->lifeleft == ONE_FRAME_TIME) {
1449                 if (Game_mode & GM_MULTI)
1450                         obj->lifeleft = OMEGA_MULTI_LIFELEFT;
1451                 else
1452                         obj->lifeleft = 0;
1453                 obj->render_type = RT_NONE;
1454         }
1455
1456         if (obj->lifeleft < 0 ) {               // We died of old age
1457                 obj->flags |= OF_SHOULD_BE_DEAD;
1458                 if ( Weapon_info[obj->id].damage_radius )
1459                         explode_badass_weapon(obj,&obj->pos);
1460                 return;
1461         }
1462
1463         //delete weapons that are not moving
1464         if (    !((FrameCount ^ obj->signature) & 3) &&
1465                         (obj->id != FLARE_ID) &&
1466                         (Weapon_info[obj->id].speed[Difficulty_level] > 0) &&
1467                         (vm_vec_mag_quick(&obj->mtype.phys_info.velocity) < F2_0)) {
1468                 obj_delete(OBJECT_NUMBER(obj));
1469                 return;
1470         }
1471
1472         if ( obj->id == FUSION_ID ) {           //always set fusion weapon to max vel
1473
1474                 vm_vec_normalize_quick(&obj->mtype.phys_info.velocity);
1475
1476                 vm_vec_scale(&obj->mtype.phys_info.velocity, Weapon_info[obj->id].speed[Difficulty_level]);
1477         }
1478
1479 // --   //      The Super Spreadfire (Helix) blobs travel in a sinusoidal path.  That is accomplished
1480 // --   //      by modifying velocity (direction) in the frame interval.
1481 // --   if (obj->id == SSPREADFIRE_ID) {
1482 // --           fix     age, sinval, cosval;
1483 // --           vms_vector      p, newp;
1484 // --           fix     speed;
1485 // -- 
1486 // --           speed = vm_vec_mag_quick(&obj->phys_info.velocity);
1487 // -- 
1488 // --           age = Weapon_info[obj->id].lifetime - obj->lifeleft;
1489 // -- 
1490 // --           fix_fast_sincos(age, &sinval, &cosval);
1491 // -- 
1492 // --           //      Note: Below code assumes x=1, y=0.  Need to scale this for values around a circle for 5 helix positions.
1493 // --           p.x = cosval << 3;
1494 // --           p.y = sinval << 3;
1495 // --           p.z = 0;
1496 // -- 
1497 // --           vm_vec_rotate(&newp, &p, &obj->orient);
1498 // -- 
1499 // --           vm_vec_add(&goal_point, &obj->pos, &newp);
1500 // -- 
1501 // --           vm_vec_sub(&vec_to_goal, &goal_point, obj
1502 // --   }
1503
1504
1505         //      For homing missiles, turn towards target. (unless it's the guided missile)
1506         if (Weapon_info[obj->id].homing_flag && !(obj->id==GUIDEDMISS_ID && obj->ctype.laser_info.parent_type==OBJ_PLAYER && obj==Guided_missile[Objects[obj->ctype.laser_info.parent_num].id] && obj->signature==Guided_missile[Objects[obj->ctype.laser_info.parent_num].id]->signature))
1507         {
1508                 vms_vector              vector_to_object, temp_vec;
1509                 fix                             dot=F1_0;
1510                 fix                             speed, max_speed;
1511
1512                 //      For first 1/2 second of life, missile flies straight.
1513                 if (obj->ctype.laser_info.creation_time + HOMING_MISSILE_STRAIGHT_TIME < GameTime) {
1514
1515                         int     track_goal = obj->ctype.laser_info.track_goal;
1516
1517                         //mprintf((0, "%5i: mtd=%5.2f", FrameCount, f2fl(Min_trackable_dot)));
1518
1519                         //      If it's time to do tracking, then it's time to grow up, stop bouncing and start exploding!.
1520                         if ((obj->id == ROBOT_SMART_MINE_HOMING_ID) || (obj->id == ROBOT_SMART_HOMING_ID) || (obj->id == SMART_MINE_HOMING_ID) || (obj->id == PLAYER_SMART_HOMING_ID) || (obj->id == EARTHSHAKER_MEGA_ID)) {
1521                                 // if (obj->mtype.phys_info.flags & PF_BOUNCE) mprintf(0, "Debouncing smart child %i\n", OBJECT_NUMBER(obj));
1522                                 obj->mtype.phys_info.flags &= ~PF_BOUNCE;
1523                         }
1524
1525                         //      Make sure the object we are tracking is still trackable.
1526                         track_goal = track_track_goal(track_goal, obj, &dot);
1527
1528                         //mprintf((0, " after ttg=%3i ", track_goal));
1529
1530                         if (track_goal == Players[Player_num].objnum) {
1531                                 fix     dist_to_player;
1532
1533                                 dist_to_player = vm_vec_dist_quick(&obj->pos, &Objects[track_goal].pos);
1534                                 if ((dist_to_player < Players[Player_num].homing_object_dist) || (Players[Player_num].homing_object_dist < 0))
1535                                         Players[Player_num].homing_object_dist = dist_to_player;
1536                                         
1537                         }
1538
1539                         if (track_goal != -1) {
1540                                 vm_vec_sub(&vector_to_object, &Objects[track_goal].pos, &obj->pos);
1541
1542                                 vm_vec_normalize_quick(&vector_to_object);
1543                                 temp_vec = obj->mtype.phys_info.velocity;
1544                                 speed = vm_vec_normalize_quick(&temp_vec);
1545                                 max_speed = Weapon_info[obj->id].speed[Difficulty_level];
1546                                 if (speed+F1_0 < max_speed) {
1547                                         speed += fixmul(max_speed, FrameTime/2);
1548                                         if (speed > max_speed)
1549                                                 speed = max_speed;
1550                                 }
1551
1552                                 // -- dot = vm_vec_dot(&temp_vec, &vector_to_object);
1553 //mprintf((0, " dot=%5.2f ", f2fl(dot)));
1554                                 vm_vec_add2(&temp_vec, &vector_to_object);
1555                                 //      The boss' smart children track better...
1556                                 if (Weapon_info[obj->id].render_type != WEAPON_RENDER_POLYMODEL)
1557                                         vm_vec_add2(&temp_vec, &vector_to_object);
1558                                 vm_vec_normalize_quick(&temp_vec);
1559                                 obj->mtype.phys_info.velocity = temp_vec;
1560                                 vm_vec_scale(&obj->mtype.phys_info.velocity, speed);
1561
1562                                 //      Subtract off life proportional to amount turned.
1563                                 //      For hardest turn, it will lose 2 seconds per second.
1564                                 {
1565                                         fix     lifelost, absdot;
1566                                 
1567                                         absdot = abs(F1_0 - dot);
1568                                 
1569                                         lifelost = fixmul(absdot*32, FrameTime);
1570                                         obj->lifeleft -= lifelost;
1571                                         // -- mprintf((0, "Missile %3i, dot = %7.3f life lost = %7.3f, life left = %7.3f\n", OBJECT_NUMBER(obj), f2fl(dot), f2fl(lifelost), f2fl(obj->lifeleft)));
1572                                 }
1573
1574                                 //      Only polygon objects have visible orientation, so only they should turn.
1575                                 if (Weapon_info[obj->id].render_type == WEAPON_RENDER_POLYMODEL)
1576                                         homing_missile_turn_towards_velocity(obj, &temp_vec);           //      temp_vec is normalized velocity.
1577                         }
1578                 }
1579
1580 //mprintf((0, "\n"));
1581         }
1582
1583         //      Make sure weapon is not moving faster than allowed speed.
1584         {
1585                 fix     weapon_speed;
1586
1587                 weapon_speed = vm_vec_mag_quick(&obj->mtype.phys_info.velocity);
1588                 if (weapon_speed > Weapon_info[obj->id].speed[Difficulty_level]) {
1589                         //      Only slow down if not allowed to move.  Makes sense, huh?  Allows proxbombs to get moved by physics force. --MK, 2/13/96
1590                         if (Weapon_info[obj->id].speed[Difficulty_level]) {
1591                                 fix     scale_factor;
1592
1593                                 scale_factor = fixdiv(Weapon_info[obj->id].speed[Difficulty_level], weapon_speed);
1594                                 vm_vec_scale(&obj->mtype.phys_info.velocity, scale_factor);
1595                         }
1596                 }
1597         }
1598 }
1599
1600 fix     Last_laser_fired_time = 0;
1601
1602 extern int Player_fired_laser_this_frame;
1603
1604 int     Zbonkers = 0;
1605
1606 //      --------------------------------------------------------------------------------------------------
1607 // Assumption: This is only called by the actual console player, not for network players
1608
1609 int do_laser_firing_player(void)
1610 {
1611         player  *plp = &Players[Player_num];
1612         fix             energy_used;
1613         int             ammo_used,primary_ammo;
1614         int             weapon_index;
1615         int             rval = 0;
1616         int             nfires = 1;
1617         fix             addval;
1618         static int Spreadfire_toggle=0;
1619         static int Helix_orientation = 0;
1620
1621         if (Player_is_dead)
1622                 return 0;
1623
1624         weapon_index = Primary_weapon_to_weapon_info[Primary_weapon];
1625         energy_used = Weapon_info[weapon_index].energy_usage;
1626         if (Primary_weapon == OMEGA_INDEX)
1627                 energy_used = 0;        //      Omega consumes energy when recharging, not when firing.
1628
1629         if (Difficulty_level < 2)
1630                 energy_used = fixmul(energy_used, i2f(Difficulty_level+2)/4);
1631
1632         //      MK, 01/26/96, Helix use 2x energy in multiplayer.  bitmaps.tbl parm should have been reduced for single player.
1633         if (weapon_index == HELIX_INDEX)
1634                 if (Game_mode & GM_MULTI)
1635                         energy_used *= 2;
1636
1637         ammo_used = Weapon_info[weapon_index].ammo_usage;
1638
1639         addval = 2*FrameTime;
1640         if (addval > F1_0)
1641                 addval = F1_0;
1642
1643         if ((Last_laser_fired_time + 2*FrameTime < GameTime) || (GameTime < Last_laser_fired_time))
1644                 Next_laser_fire_time = GameTime;
1645
1646         Last_laser_fired_time = GameTime;
1647
1648         primary_ammo = (Primary_weapon == GAUSS_INDEX)?(plp->primary_ammo[VULCAN_INDEX]):(plp->primary_ammo[Primary_weapon]);
1649
1650         if      (!((plp->energy >= energy_used) && (primary_ammo >= ammo_used)))
1651                 auto_select_weapon(0);          //      Make sure the player can fire from this weapon.
1652
1653 if (Zbonkers) {
1654         Zbonkers = 0;
1655         GameTime = 0;
1656 }
1657
1658         while (Next_laser_fire_time <= GameTime) {
1659                 if      ((plp->energy >= energy_used) && (primary_ammo >= ammo_used)) {
1660                         int     laser_level, flags;
1661
1662 //mprintf(0, ".");
1663                         if (Laser_rapid_fire!=0xBADA55)
1664                                 Next_laser_fire_time += Weapon_info[weapon_index].fire_wait;
1665                         else
1666                                 Next_laser_fire_time += F1_0/25;
1667
1668                         laser_level = Players[Player_num].laser_level;
1669         
1670                         flags = 0;
1671
1672                         if (Primary_weapon == SPREADFIRE_INDEX) {
1673                                 if (Spreadfire_toggle)
1674                                         flags |= LASER_SPREADFIRE_TOGGLED;
1675                                 Spreadfire_toggle = !Spreadfire_toggle;
1676                         }
1677
1678                         if (Primary_weapon == HELIX_INDEX) {
1679                                 Helix_orientation++;
1680                                 flags |= ((Helix_orientation & LASER_HELIX_MASK) << LASER_HELIX_SHIFT);
1681                         }
1682
1683                         if (Players[Player_num].flags & PLAYER_FLAGS_QUAD_LASERS)
1684                                 flags |= LASER_QUAD;
1685
1686                         rval += do_laser_firing(Players[Player_num].objnum, Primary_weapon, laser_level, flags, nfires);
1687
1688                         plp->energy -= (energy_used * rval) / Weapon_info[weapon_index].fire_count;
1689                         if (plp->energy < 0)
1690                                 plp->energy = 0;
1691
1692                         if ((Primary_weapon == VULCAN_INDEX) || (Primary_weapon == GAUSS_INDEX)) {
1693                                 if (ammo_used > plp->primary_ammo[VULCAN_INDEX])
1694                                         plp->primary_ammo[VULCAN_INDEX] = 0;
1695                                 else
1696                                         plp->primary_ammo[VULCAN_INDEX] -= ammo_used;
1697                         }
1698
1699                         auto_select_weapon(0);          //      Make sure the player can fire from this weapon.
1700
1701                 } else {
1702                         auto_select_weapon(0);          //      Make sure the player can fire from this weapon.
1703                         Next_laser_fire_time = GameTime;        //      Prevents shots-to-fire from building up.
1704                         break;  //      Couldn't fire weapon, so abort.
1705                 }
1706         }
1707 //mprintf(0, "  fires = %i\n", rval);
1708
1709         Global_laser_firing_count = 0;  
1710
1711         return rval;
1712 }
1713
1714 // -- #define   MAX_LIGHTNING_DISTANCE  (F1_0*300)
1715 // -- #define   MAX_LIGHTNING_BLOBS             16
1716 // -- #define   LIGHTNING_BLOB_DISTANCE (MAX_LIGHTNING_DISTANCE/MAX_LIGHTNING_BLOBS)
1717 // -- 
1718 // -- #define   LIGHTNING_BLOB_ID                       13
1719 // -- 
1720 // -- #define   LIGHTNING_TIME          (F1_0/4)
1721 // -- #define   LIGHTNING_DELAY (F1_0/8)
1722 // -- 
1723 // -- int       Lightning_gun_num = 1;
1724 // -- 
1725 // -- fix       Lightning_start_time = -F1_0*10, Lightning_last_time;
1726 // -- 
1727 // -- //        --------------------------------------------------------------------------------------------------
1728 // -- //        Return -1 if failed to create at least one blob.  Else return index of last blob created.
1729 // -- int create_lightning_blobs(vms_vector *direction, vms_vector *start_pos, int start_segnum, int parent)
1730 // -- {
1731 // --   int                     i;
1732 // --   fvi_query       fq;
1733 // --   fvi_info                hit_data;
1734 // --   vms_vector      end_pos;
1735 // --   vms_vector      norm_dir;
1736 // --   int                     fate;
1737 // --   int                     num_blobs;
1738 // --   vms_vector      tvec;
1739 // --   fix                     dist_to_hit_point;
1740 // --   vms_vector      point_pos, delta_pos;
1741 // --   int                     objnum;
1742 // --   vms_vector      *gun_pos;
1743 // --   vms_matrix      m;
1744 // --   vms_vector      gun_pos2;
1745 // -- 
1746 // --   if (Players[Player_num].energy > F1_0)
1747 // --           Players[Player_num].energy -= F1_0;
1748 // -- 
1749 // --   if (Players[Player_num].energy <= F1_0) {
1750 // --           Players[Player_num].energy = 0; 
1751 // --           auto_select_weapon(0);
1752 // --           return -1;
1753 // --   }
1754 // -- 
1755 // --   norm_dir = *direction;
1756 // -- 
1757 // --   vm_vec_normalize_quick(&norm_dir);
1758 // --   vm_vec_scale_add(&end_pos, start_pos, &norm_dir, MAX_LIGHTNING_DISTANCE);
1759 // -- 
1760 // --   fq.p0                                           = start_pos;
1761 // --   fq.startseg                             = start_segnum;
1762 // --   fq.p1                                           = &end_pos;
1763 // --   fq.rad                                  = 0;
1764 // --   fq.thisobjnum                   = parent;
1765 // --   fq.ignore_obj_list      = NULL;
1766 // --   fq.flags                                        = FQ_TRANSWALL | FQ_CHECK_OBJS;
1767 // -- 
1768 // --   fate = find_vector_intersection(&fq, &hit_data);
1769 // --   if (hit_data.hit_seg == -1) {
1770 // --           mprintf((1, "Warning: Lightning bolt has hit seg of -1.\n"));
1771 // --           return -1;
1772 // --   }
1773 // -- 
1774 // --   dist_to_hit_point = vm_vec_mag(vm_vec_sub(&tvec, &hit_data.hit_pnt, start_pos));
1775 // --   num_blobs = dist_to_hit_point/LIGHTNING_BLOB_DISTANCE;
1776 // -- 
1777 // --   if (num_blobs > MAX_LIGHTNING_BLOBS)
1778 // --           num_blobs = MAX_LIGHTNING_BLOBS;
1779 // -- 
1780 // --   if (num_blobs < MAX_LIGHTNING_BLOBS/4)
1781 // --           num_blobs = MAX_LIGHTNING_BLOBS/4;
1782 // -- 
1783 // --   // Find the initial position of the laser
1784 // --   gun_pos = &Player_ship->gun_points[Lightning_gun_num];
1785 // --   vm_copy_transpose_matrix(&m,&Objects[parent].orient);
1786 // --   vm_vec_rotate(&gun_pos2, gun_pos, &m);
1787 // --   vm_vec_add(&point_pos, &Objects[parent].pos, &gun_pos2);
1788 // -- 
1789 // --   delta_pos = norm_dir;
1790 // --   vm_vec_scale(&delta_pos, dist_to_hit_point/num_blobs);
1791 // -- 
1792 // --   for (i=0; i<num_blobs; i++) {
1793 // --           int                     point_seg;
1794 // --           object          *obj;
1795 // -- 
1796 // --           vm_vec_add2(&point_pos, &delta_pos);
1797 // --           point_seg = find_point_seg(&point_pos, start_segnum);
1798 // --           if (point_seg == -1)    //      Hey, we thought we were creating points on a line, but we left the mine!
1799 // --                   continue;
1800 // -- 
1801 // --           objnum = Laser_create_new( direction, &point_pos, point_seg, parent, LIGHTNING_BLOB_ID, 0 );
1802 // -- 
1803 // --           if ( objnum < 0 )       {
1804 // --                   mprintf((1, "Can't create lightning blob - Out of objects!\n" ));
1805 // --                   Int3();
1806 // --                   return -1;
1807 // --           }
1808 // -- 
1809 // --           obj = &Objects[objnum];
1810 // -- 
1811 // --           digi_play_sample( Weapon_info[obj->id].flash_sound, F1_0 );
1812 // -- 
1813 // --           // -- vm_vec_scale( &obj->mtype.phys_info.velocity, F1_0/2);
1814 // -- 
1815 // --           obj->lifeleft = (LIGHTNING_TIME + LIGHTNING_DELAY)/2;
1816 // -- 
1817 // --   }
1818 // -- 
1819 // --   return objnum;
1820 // -- 
1821 // -- }
1822 // -- 
1823 // -- //        --------------------------------------------------------------------------------------------------
1824 // -- //        Lightning Cannon.
1825 // -- //        While being fired, creates path of blobs forward from player until it hits something.
1826 // -- //        Up to MAX_LIGHTNING_BLOBS blobs, spaced LIGHTNING_BLOB_DISTANCE units apart.
1827 // -- //        When the player releases the firing key, the blobs move forward.
1828 // -- void lightning_frame(void)
1829 // -- {
1830 // --   if ((GameTime - Lightning_start_time < LIGHTNING_TIME) && (GameTime - Lightning_start_time > 0)) {
1831 // --           if (GameTime - Lightning_last_time > LIGHTNING_DELAY) {
1832 // --                   create_lightning_blobs(&ConsoleObject->orient.fvec, &ConsoleObject->pos, ConsoleObject->segnum, OBJECT_NUMBER(ConsoleObject));
1833 // --                   Lightning_last_time = GameTime;
1834 // --           }
1835 // --   }
1836 // -- }
1837
1838 //      --------------------------------------------------------------------------------------------------
1839 //      Object "objnum" fires weapon "weapon_num" of level "level".  (Right now (9/24/94) level is used only for type 0 laser.
1840 //      Flags are the player flags.  For network mode, set to 0.
1841 //      It is assumed that this is a player object (as in multiplayer), and therefore the gun positions are known.
1842 //      Returns number of times a weapon was fired.  This is typically 1, but might be more for low frame rates.
1843 //      More than one shot is fired with a pseudo-delay so that players on show machines can fire (for themselves
1844 //      or other players) often enough for things like the vulcan cannon.
1845 int do_laser_firing(int objnum, int weapon_num, int level, int flags, int nfires)
1846 {
1847         object  *objp = &Objects[objnum];
1848
1849         switch (weapon_num) {
1850                 case LASER_INDEX: {
1851                         int weapon_num;
1852
1853                         Laser_offset = ((F1_0*2)*(d_rand()%8))/8;
1854
1855                         if (level <= MAX_LASER_LEVEL)
1856                                 weapon_num = LASER_ID + level;
1857                         else
1858                                 weapon_num = SUPER_LASER_ID + (level-MAX_LASER_LEVEL-1);
1859
1860                         Laser_player_fire( objp, weapon_num, 0, 1, 0);
1861                         Laser_player_fire( objp, weapon_num, 1, 0, 0);
1862
1863                         if (flags & LASER_QUAD) {
1864                                 //      hideous system to make quad laser 1.5x powerful as normal laser, make every other quad laser bolt harmless
1865                                 Laser_player_fire( objp, weapon_num, 2, 0, 0);
1866                                 Laser_player_fire( objp, weapon_num, 3, 0, 0);
1867                         }
1868                         break;
1869                 }
1870                 case VULCAN_INDEX: {
1871                         //      Only make sound for 1/4 of vulcan bullets.
1872                         int     make_sound = 1;
1873                         //if (d_rand() > 24576)
1874                         //      make_sound = 1;
1875                         Laser_player_fire_spread( objp, VULCAN_ID, 6, d_rand()/8 - 32767/16, d_rand()/8 - 32767/16, make_sound, 0);
1876                         if (nfires > 1) {
1877                                 Laser_player_fire_spread( objp, VULCAN_ID, 6, d_rand()/8 - 32767/16, d_rand()/8 - 32767/16, 0, 0);
1878                                 if (nfires > 2) {
1879                                         Laser_player_fire_spread( objp, VULCAN_ID, 6, d_rand()/8 - 32767/16, d_rand()/8 - 32767/16, 0, 0);
1880                                 }
1881                         }
1882                         break;
1883                 }
1884                 case SPREADFIRE_INDEX:
1885                         if (flags & LASER_SPREADFIRE_TOGGLED) {
1886                                 Laser_player_fire_spread( objp, SPREADFIRE_ID, 6, F1_0/16, 0, 0, 0);
1887                                 Laser_player_fire_spread( objp, SPREADFIRE_ID, 6, -F1_0/16, 0, 0, 0);
1888                                 Laser_player_fire_spread( objp, SPREADFIRE_ID, 6, 0, 0, 1, 0);
1889                         } else {
1890                                 Laser_player_fire_spread( objp, SPREADFIRE_ID, 6, 0, F1_0/16, 0, 0);
1891                                 Laser_player_fire_spread( objp, SPREADFIRE_ID, 6, 0, -F1_0/16, 0, 0);
1892                                 Laser_player_fire_spread( objp, SPREADFIRE_ID, 6, 0, 0, 1, 0);
1893                         }
1894                         break;
1895
1896                 case PLASMA_INDEX:
1897                         Laser_player_fire( objp, PLASMA_ID, 0, 1, 0);
1898                         Laser_player_fire( objp, PLASMA_ID, 1, 0, 0);
1899                         if (nfires > 1) {
1900                                 Laser_player_fire_spread_delay( objp, PLASMA_ID, 0, 0, 0, FrameTime/2, 1, 0);
1901                                 Laser_player_fire_spread_delay( objp, PLASMA_ID, 1, 0, 0, FrameTime/2, 0, 0);
1902                         }
1903                         break;
1904
1905                 case FUSION_INDEX: {
1906                         vms_vector      force_vec;
1907
1908 //                      mprintf((0, "Fusion multiplier %f.\n", f2fl(Fusion_charge)));
1909
1910                         Laser_player_fire( objp, FUSION_ID, 0, 1, 0);
1911                         Laser_player_fire( objp, FUSION_ID, 1, 1, 0);
1912
1913                         flags = (sbyte)(Fusion_charge >> 12);
1914
1915                         Fusion_charge = 0;
1916
1917                         force_vec.x = -(objp->orient.fvec.x << 7);
1918                         force_vec.y = -(objp->orient.fvec.y << 7);
1919                         force_vec.z = -(objp->orient.fvec.z << 7);
1920                         phys_apply_force(objp, &force_vec);
1921
1922                         force_vec.x = (force_vec.x >> 4) + d_rand() - 16384;
1923                         force_vec.y = (force_vec.y >> 4) + d_rand() - 16384;
1924                         force_vec.z = (force_vec.z >> 4) + d_rand() - 16384;
1925                         phys_apply_rot(objp, &force_vec);
1926
1927                 }
1928                         break;
1929                 case SUPER_LASER_INDEX: {
1930                         int super_level = 3;            //make some new kind of laser eventually
1931                         Laser_player_fire( objp, super_level, 0, 1, 0);
1932                         Laser_player_fire( objp, super_level, 1, 0, 0);
1933
1934                         if (flags & LASER_QUAD) {
1935                                 //      hideous system to make quad laser 1.5x powerful as normal laser, make every other quad laser bolt harmless
1936                                 Laser_player_fire( objp, super_level, 2, 0, 0);
1937                                 Laser_player_fire( objp, super_level, 3, 0, 0);
1938                         }
1939                         break;
1940                 }
1941                 case GAUSS_INDEX: {
1942                         //      Only make sound for 1/4 of vulcan bullets.
1943                         int     make_sound = 1;
1944                         //if (d_rand() > 24576)
1945                         //      make_sound = 1;
1946                         
1947                         Laser_player_fire_spread( objp, GAUSS_ID, 6, (d_rand()/8 - 32767/16)/5, (d_rand()/8 - 32767/16)/5, make_sound, 0);
1948                         if (nfires > 1) {
1949                                 Laser_player_fire_spread( objp, GAUSS_ID, 6, (d_rand()/8 - 32767/16)/5, (d_rand()/8 - 32767/16)/5, 0, 0);
1950                                 if (nfires > 2) {
1951                                         Laser_player_fire_spread( objp, GAUSS_ID, 6, (d_rand()/8 - 32767/16)/5, (d_rand()/8 - 32767/16)/5, 0, 0);
1952                                 }
1953                         }
1954                         break;
1955                 }
1956                 case HELIX_INDEX: {
1957                         int helix_orient;
1958                         fix spreadr,spreadu;
1959                         helix_orient = (flags >> LASER_HELIX_SHIFT) & LASER_HELIX_MASK;
1960                         switch(helix_orient) {
1961
1962                                 case 0: spreadr =  F1_0/16; spreadu = 0;       break; // Vertical
1963                                 case 1: spreadr =  F1_0/17; spreadu = F1_0/42; break; //  22.5 degrees
1964                                 case 2: spreadr =  F1_0/22; spreadu = F1_0/22; break; //  45   degrees
1965                                 case 3: spreadr =  F1_0/42; spreadu = F1_0/17; break; //  67.5 degrees
1966                                 case 4: spreadr =  0;       spreadu = F1_0/16; break; //  90   degrees
1967                                 case 5: spreadr = -F1_0/42; spreadu = F1_0/17; break; // 112.5 degrees
1968                                 case 6: spreadr = -F1_0/22; spreadu = F1_0/22; break; // 135   degrees  
1969                                 case 7: spreadr = -F1_0/17; spreadu = F1_0/42; break; // 157.5 degrees
1970                                 default:
1971                                         Error("Invalid helix_orientation value %x\n",helix_orient);
1972                         }
1973
1974                         Laser_player_fire_spread( objp, HELIX_ID, 6,  0,  0, 1, 0);
1975                         Laser_player_fire_spread( objp, HELIX_ID, 6,  spreadr,  spreadu, 0, 0);
1976                         Laser_player_fire_spread( objp, HELIX_ID, 6, -spreadr, -spreadu, 0, 0);
1977                         Laser_player_fire_spread( objp, HELIX_ID, 6,  spreadr*2,  spreadu*2, 0, 0);
1978                         Laser_player_fire_spread( objp, HELIX_ID, 6, -spreadr*2, -spreadu*2, 0, 0);
1979                         break;
1980                 }
1981
1982                 case PHOENIX_INDEX:
1983                         Laser_player_fire( objp, PHOENIX_ID, 0, 1, 0);
1984                         Laser_player_fire( objp, PHOENIX_ID, 1, 0, 0);
1985                         if (nfires > 1) {
1986                                 Laser_player_fire_spread_delay( objp, PHOENIX_ID, 0, 0, 0, FrameTime/2, 1, 0);
1987                                 Laser_player_fire_spread_delay( objp, PHOENIX_ID, 1, 0, 0, FrameTime/2, 0, 0);
1988                         }
1989                         break;
1990
1991                 case OMEGA_INDEX:
1992                         Laser_player_fire( objp, OMEGA_ID, 1, 1, 0);
1993                         break;
1994
1995                 default:
1996                         Int3(); //      Contact Yuan: Unknown Primary weapon type, setting to 0.
1997                         Primary_weapon = 0;
1998         }
1999
2000         // Set values to be recognized during comunication phase, if we are the
2001         //  one shooting
2002 #ifdef NETWORK
2003         if ((Game_mode & GM_MULTI) && (objnum == Players[Player_num].objnum))
2004         {
2005                 // mprintf((0, "Flags on fire: %d.\n", flags));
2006                 Network_laser_fired = nfires;
2007                 Network_laser_gun = weapon_num;
2008                 Network_laser_flags = flags;
2009                 Network_laser_level = level;
2010         }
2011 #endif
2012
2013         return nfires;
2014 }
2015
2016 #define MAX_SMART_DISTANCE      (F1_0*150)
2017 #define MAX_OBJDISTS                    30
2018
2019 //      -------------------------------------------------------------------------------------------
2020 //      if goal_obj == -1, then create random vector
2021 int create_homing_missile(object *objp, int goal_obj, int objtype, int make_sound)
2022 {
2023         int                     objnum;
2024         vms_vector      vector_to_goal;
2025         vms_vector      random_vector;
2026         //vms_vector    goal_pos;
2027
2028         if (goal_obj == -1) {
2029                 make_random_vector(&vector_to_goal);
2030         } else {
2031                 vm_vec_normalized_dir_quick(&vector_to_goal, &Objects[goal_obj].pos, &objp->pos);
2032                 make_random_vector(&random_vector);
2033                 vm_vec_scale_add2(&vector_to_goal, &random_vector, F1_0/4);
2034                 vm_vec_normalize_quick(&vector_to_goal);
2035         }               
2036
2037         //      Create a vector towards the goal, then add some noise to it.
2038         objnum = Laser_create_new(&vector_to_goal, &objp->pos, objp->segnum, OBJECT_NUMBER(objp), objtype, make_sound);
2039         if (objnum == -1)
2040                 return -1;
2041
2042         // Fixed to make sure the right person gets credit for the kill
2043
2044 //      Objects[objnum].ctype.laser_info.parent_num = objp->ctype.laser_info.parent_num;
2045 //      Objects[objnum].ctype.laser_info.parent_type = objp->ctype.laser_info.parent_type;
2046 //      Objects[objnum].ctype.laser_info.parent_signature = objp->ctype.laser_info.parent_signature;
2047
2048         Objects[objnum].ctype.laser_info.track_goal = goal_obj;
2049
2050         return objnum;
2051 }
2052
2053 extern void blast_nearby_glass(object *objp, fix damage);
2054
2055 //-----------------------------------------------------------------------------
2056 // Create the children of a smart bomb, which is a bunch of homing missiles.
2057 void create_smart_children(object *objp, int num_smart_children)
2058 {
2059         int     parent_type, parent_num;
2060         int     make_sound;
2061         int     numobjs=0;
2062         int     objlist[MAX_OBJDISTS];
2063         int     blob_id;
2064
2065         if (objp->type == OBJ_WEAPON) {
2066                 parent_type = objp->ctype.laser_info.parent_type;
2067                 parent_num = objp->ctype.laser_info.parent_num;
2068         } else if (objp->type == OBJ_ROBOT) {
2069                 parent_type = OBJ_ROBOT;
2070                 parent_num = OBJECT_NUMBER(objp);
2071         } else {
2072                 Int3(); //      Hey, what kind of object is this!?
2073                 parent_type = 0;
2074                 parent_num = 0;
2075         }
2076
2077         if (objp->id == EARTHSHAKER_ID)
2078                 blast_nearby_glass(objp, Weapon_info[EARTHSHAKER_ID].strength[Difficulty_level]);
2079
2080 // -- DEBUG --
2081         if ((objp->type == OBJ_WEAPON) && ((objp->id == SMART_ID) || (objp->id == SUPERPROX_ID) || (objp->id == ROBOT_SUPERPROX_ID) || (objp->id == EARTHSHAKER_ID)))
2082                 Assert(Weapon_info[objp->id].children != -1);
2083 // -- DEBUG --
2084
2085         if (((objp->type == OBJ_WEAPON) && (Weapon_info[objp->id].children != -1)) || (objp->type == OBJ_ROBOT)) {
2086                 int     i, objnum;
2087
2088                 if (Game_mode & GM_MULTI)
2089                         d_srand(8321L);
2090
2091                 for (objnum=0; objnum<=Highest_object_index; objnum++) {
2092                         object  *curobjp = &Objects[objnum];
2093
2094                         if ((((curobjp->type == OBJ_ROBOT) && (!curobjp->ctype.ai_info.CLOAKED)) || (curobjp->type == OBJ_PLAYER)) && (objnum != parent_num)) {
2095                                 fix     dist;
2096
2097                                 if (curobjp->type == OBJ_PLAYER)
2098                                 {
2099                                         if ((parent_type == OBJ_PLAYER) && (Game_mode & GM_MULTI_COOP))
2100                                                 continue;
2101 #ifdef NETWORK
2102                                         if ((Game_mode & GM_TEAM) && (get_team(curobjp->id) == get_team(Objects[parent_num].id)))
2103                                                 continue;
2104 #endif
2105
2106                                         if (Players[curobjp->id].flags & PLAYER_FLAGS_CLOAKED)
2107                                                 continue;
2108                                 }
2109
2110                                 //      Robot blobs can't track robots.
2111                                 if (curobjp->type == OBJ_ROBOT) {
2112                                         if (parent_type == OBJ_ROBOT)
2113                                                 continue;
2114
2115                                         //      Your shots won't track the buddy.
2116                                         if (parent_type == OBJ_PLAYER)
2117                                                 if (Robot_info[curobjp->id].companion)
2118                                                         continue;
2119                                 }
2120
2121                                 dist = vm_vec_dist_quick(&objp->pos, &curobjp->pos);
2122                                 if (dist < MAX_SMART_DISTANCE) {
2123                                         int     oovis;
2124
2125                                         oovis = object_to_object_visibility(objp, curobjp, FQ_TRANSWALL);
2126
2127                                         if (oovis) { //object_to_object_visibility(objp, curobjp, FQ_TRANSWALL)) {
2128                                                 objlist[numobjs] = objnum;
2129                                                 numobjs++;
2130                                                 if (numobjs >= MAX_OBJDISTS) {
2131                                                         mprintf((0, "Warning -- too many objects near smart bomb explosion.  See laser.c.\n"));
2132                                                         numobjs = MAX_OBJDISTS;
2133                                                         break;
2134                                                 }
2135                                         }
2136                                 }
2137                         }
2138                 }
2139
2140                 //      Get type of weapon for child from parent.
2141                 if (objp->type == OBJ_WEAPON) {
2142                         blob_id = Weapon_info[objp->id].children;
2143                         Assert(blob_id != -1);          //      Hmm, missing data in bitmaps.tbl.  Need "children=NN" parameter.
2144                 } else {
2145                         Assert(objp->type == OBJ_ROBOT);
2146                         blob_id = ROBOT_SMART_HOMING_ID;
2147                 }
2148
2149 // --           //determine what kind of blob to drop
2150 // --           //      Note: parent_type is not the type of the weapon's parent.  It is actually the type of the weapon's
2151 // --           //      earliest ancestor.  This deals with the issue of weapons spewing weapons which spew weapons.
2152 // --           switch (parent_type) {
2153 // --                   case OBJ_WEAPON:
2154 // --                           Int3(); //      Should this ever happen?
2155 // --                           switch (objp->id) {
2156 // --                                   case SUPERPROX_ID:                      blob_id = SMART_MINE_HOMING_ID; break;
2157 // --                                   case ROBOT_SUPERPROX_ID:        blob_id = ROBOT_SMART_MINE_HOMING_ID; break;
2158 // --                                   case EARTHSHAKER_ID:                    blob_id = EARTHSHAKER_MEGA_ID; break;
2159 // --                                   default:                                                        Int3(); //bogus id for weapon  
2160 // --                           }
2161 // --                           break;
2162 // --                   case OBJ_PLAYER:
2163 // --                           switch (objp->id) {
2164 // --                                   case SUPERPROX_ID:                      blob_id = SMART_MINE_HOMING_ID; break;
2165 // --                                   case ROBOT_SUPERPROX_ID:        Int3(); break;
2166 // --                                   case EARTHSHAKER_ID:                    blob_id = EARTHSHAKER_MEGA_ID; break;
2167 // --                                   case SMART_ID:                                  blob_id = PLAYER_SMART_HOMING_ID; break;
2168 // --                                   default:                                                        Int3(); //bogus id for weapon  
2169 // --                           }
2170 // --                           break;
2171 // --                   case OBJ_ROBOT:
2172 // --                           switch (objp->id) {
2173 // --                                   case ROBOT_SUPERPROX_ID:        blob_id = ROBOT_SMART_MINE_HOMING_ID; break;
2174 // --                                   // -- case EARTHSHAKER_ID:                      blob_id = EARTHSHAKER_MEGA_ID; break;
2175 // --                                   case SMART_ID:                                  blob_id = ROBOT_SMART_HOMING_ID; break;
2176 // --                                   default:                                                        blob_id = ROBOT_SMART_HOMING_ID; break;
2177 // --                           }
2178 // --                           break;
2179 // --                   default:                                        Int3(); //bogus type for parent object
2180 // --           }
2181
2182                 make_sound = 1;
2183                 for (i=0; i<num_smart_children; i++) {
2184                         int objnum;
2185                         objnum = (numobjs==0)?-1:objlist[(d_rand() * numobjs) >> 15];
2186                         create_homing_missile(objp, objnum, blob_id, make_sound);
2187                         make_sound = 0;
2188                 }
2189         }
2190 }
2191
2192 int Missile_gun = 0;
2193
2194 //give up control of the guided missile
2195 void release_guided_missile(int player_num)
2196 {
2197         if (player_num == Player_num)
2198          {                      
2199           if (Guided_missile[player_num]==NULL)
2200                         return;
2201         
2202                 Missile_viewer = Guided_missile[player_num];
2203 #ifdef NETWORK
2204                 if (Game_mode & GM_MULTI)
2205                  multi_send_guided_info (Guided_missile[Player_num],1);
2206 #endif
2207                 if (Newdemo_state==ND_STATE_RECORDING)
2208                  newdemo_record_guided_end();
2209          }      
2210
2211         Guided_missile[player_num] = NULL;
2212 }
2213
2214 int Proximity_dropped=0,Smartmines_dropped=0;
2215
2216 //      -------------------------------------------------------------------------------------------
2217 //parameter determines whether or not to do autoselect if have run out of ammo
2218 //this is needed because if you drop a bomb with the B key, you don't want
2219 //want to autoselect if the bomb isn't actually selected. 
2220 void do_missile_firing(int do_autoselect)
2221 {
2222         int gun_flag=0;
2223
2224         Assert(Secondary_weapon < MAX_SECONDARY_WEAPONS);
2225
2226         if (Guided_missile[Player_num] && Guided_missile[Player_num]->signature==Guided_missile_sig[Player_num]) {
2227                 release_guided_missile(Player_num);
2228                 Next_missile_fire_time = GameTime + Weapon_info[Secondary_weapon_to_weapon_info[Secondary_weapon]].fire_wait;
2229                 return;
2230         }
2231
2232         if (!Player_is_dead && (Players[Player_num].secondary_ammo[Secondary_weapon] > 0))      {
2233
2234                 int weapon_id,weapon_gun;
2235
2236                 Players[Player_num].secondary_ammo[Secondary_weapon]--;
2237
2238                 weapon_id = Secondary_weapon_to_weapon_info[Secondary_weapon];
2239
2240                 if (Laser_rapid_fire!=0xBADA55)
2241                         Next_missile_fire_time = GameTime + Weapon_info[weapon_id].fire_wait;
2242                 else
2243                         Next_missile_fire_time = GameTime + F1_0/25;
2244
2245                 weapon_gun = Secondary_weapon_to_gun_num[Secondary_weapon];
2246
2247                 if (weapon_gun==4) {            //alternate left/right
2248                         weapon_gun += (gun_flag = (Missile_gun & 1));
2249                         Missile_gun++;
2250                 }
2251
2252                 Laser_player_fire( ConsoleObject, weapon_id, weapon_gun, 1, 0);
2253
2254                 if (Secondary_weapon == PROXIMITY_INDEX) {
2255                         if (++Proximity_dropped == 4) {
2256                                 Proximity_dropped = 0;
2257 #ifdef NETWORK
2258                                 maybe_drop_net_powerup(POW_PROXIMITY_WEAPON);
2259 #endif
2260                         }
2261                 }
2262                 else if (Secondary_weapon == SMART_MINE_INDEX) {
2263                         if (++Smartmines_dropped == 4) {
2264                                 Smartmines_dropped = 0;
2265 #ifdef NETWORK
2266                                 maybe_drop_net_powerup(POW_SMART_MINE);
2267 #endif
2268                         }
2269                 }
2270 #ifdef NETWORK
2271                 else if (Secondary_weapon != CONCUSSION_INDEX)
2272                         maybe_drop_net_powerup(Secondary_weapon_to_powerup[Secondary_weapon]);
2273 #endif
2274                 
2275                 if (Secondary_weapon == MEGA_INDEX || Secondary_weapon == SMISSILE5_INDEX) {
2276                         vms_vector force_vec;
2277
2278                         force_vec.x = -(ConsoleObject->orient.fvec.x << 7);
2279                         force_vec.y = -(ConsoleObject->orient.fvec.y << 7);
2280                         force_vec.z = -(ConsoleObject->orient.fvec.z << 7);
2281                         phys_apply_force(ConsoleObject, &force_vec);
2282         
2283                         force_vec.x = (force_vec.x >> 4) + d_rand() - 16384;
2284                         force_vec.y = (force_vec.y >> 4) + d_rand() - 16384;
2285                         force_vec.z = (force_vec.z >> 4) + d_rand() - 16384;
2286                         phys_apply_rot(ConsoleObject, &force_vec);
2287                 }
2288
2289 #ifdef NETWORK
2290                 if (Game_mode & GM_MULTI) 
2291                 {
2292                         Network_laser_fired = 1;                //how many
2293                         Network_laser_gun = Secondary_weapon + MISSILE_ADJUST;
2294                         Network_laser_flags = gun_flag;
2295                         Network_laser_level = 0;
2296                 }
2297 #endif
2298
2299                 if (do_autoselect)
2300                         auto_select_weapon(1);          //select next missile, if this one out of ammo
2301         }
2302 }
2303