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