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