home *** CD-ROM | disk | FTP | other *** search
/ Amiga ACS 1998 #6 / amigaacscoverdisc1998-061998.iso / games / descent / source / main / laser.c < prev    next >
C/C++ Source or Header  |  1998-06-08  |  51KB  |  1,494 lines

  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-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
  12. */
  13. /*     
  14.  * $Source: f:/miner/source/main/rcs/laser.c $
  15.  * $Revision: 2.6 $
  16.  * $Author: mike $
  17.  * $Date: 1995/04/05 13:18:31 $
  18.  * 
  19.  * This will contain the laser code
  20.  * 
  21.  * Revision 1.1  1993/11/29  17:19:02  john
  22.  * Initial revision
  23.  * 
  24.  * 
  25.  */
  26.  
  27. #pragma off (unreferenced)
  28. static char rcsid[] = "$Id: laser.c 2.6 1995/04/05 13:18:31 mike Exp $";
  29. #pragma on (unreferenced)
  30.  
  31. #include <stdlib.h>
  32.  
  33. #include "inferno.h"
  34. #include "game.h"
  35. #include "bm.h"
  36. #include "object.h"
  37. #include "laser.h"
  38. #include "segment.h"
  39. #include "fvi.h"
  40. #include "segpoint.h"
  41. #include "error.h"
  42. #include "mono.h"
  43. #include "key.h"
  44. #include "texmap.h"
  45. #include "textures.h"
  46. #include "render.h"
  47. #include "vclip.h"
  48. #include "fireball.h"
  49. #include "polyobj.h"
  50. #include "robot.h"
  51. #include "weapon.h"
  52. #include "timer.h"
  53. #include "player.h"
  54. #include "sounds.h"
  55. #include "network.h"
  56. #include "ai.h"
  57. #include "modem.h"
  58. #include "powerup.h"
  59. #include "multi.h"
  60. #include "physics.h"
  61.  
  62. int Laser_rapid_fire = 0;
  63.  
  64. //---------------------------------------------------------------------------------
  65. // Called by render code.... determines if the laser is from a robot or the
  66. // player and calls the appropriate routine.
  67.  
  68. void Laser_render(object *obj)
  69. {
  70.  
  71. //    Commented out by John (sort of, typed by Mike) on 6/8/94
  72. #if 0
  73.     switch( obj->id )    {
  74.     case WEAPON_TYPE_WEAK_LASER:
  75.     case WEAPON_TYPE_STRONG_LASER:
  76.     case WEAPON_TYPE_CANNON_BALL:
  77.     case WEAPON_TYPE_MISSILE:
  78.         break;
  79.     default:
  80.         Error( "Invalid weapon type in Laser_render\n" );
  81.     }
  82. #endif
  83.     
  84.     switch( Weapon_info[obj->id].render_type )    {
  85.     case WEAPON_RENDER_LASER:
  86.         Int3();    // Not supported anymore!
  87.                     //Laser_draw_one(obj-Objects, Weapon_info[obj->id].bitmap );
  88.         break;
  89.     case WEAPON_RENDER_BLOB:
  90.         draw_object_blob(obj, Weapon_info[obj->id].bitmap  );
  91.         break;
  92.     case WEAPON_RENDER_POLYMODEL:
  93.         break;
  94.     case WEAPON_RENDER_VCLIP:
  95.         Int3();    //    Oops, not supported, type added by mk on 09/09/94, but not for lasers...
  96.     default:
  97.         Error( "Invalid weapon render type in Laser_render\n" );
  98.     }
  99.  
  100. }
  101.  
  102. //---------------------------------------------------------------------------------
  103. // Draws a texture-mapped laser bolt
  104.  
  105. //void Laser_draw_one( int objnum, grs_bitmap * bmp )
  106. //{
  107. //    int t1, t2, t3;
  108. //    g3s_point p1, p2;
  109. //    object *obj;
  110. //    vms_vector start_pos,end_pos;
  111. //
  112. //    obj = &Objects[objnum];
  113. //
  114. //    start_pos = obj->pos;
  115. //    vm_vec_scale_add(&end_pos,&start_pos,&obj->orient.fvec,-Laser_length);
  116. //
  117. //    g3_rotate_point(&p1,&start_pos);
  118. //    g3_rotate_point(&p2,&end_pos);
  119. //
  120. //    t1 = Lighting_on;
  121. //    t2 = Interpolation_method;
  122. //    t3 = Transparency_on;
  123. //
  124. //    Lighting_on  = 0;
  125. //    //Interpolation_method = 3;    // Full perspective
  126. //    Interpolation_method = 1;    // Linear
  127. //    Transparency_on = 1;
  128. //
  129. //    //gr_setcolor( gr_getcolor(31,15,0));
  130. //    //g3_draw_line_ptrs(p1,p2);
  131. //    //g3_draw_rod(p1,0x2000,p2,0x2000);
  132. //    //g3_draw_rod(p1,Laser_width,p2,Laser_width);
  133. //    g3_draw_rod_tmap(bmp,&p2,Laser_width,&p1,Laser_width,0);
  134. //    Lighting_on = t1;
  135. //    Interpolation_method = t2;
  136. //    Transparency_on = t3;
  137. //
  138. //}
  139.  
  140. //    Changed by MK on 09/07/94
  141. //    I want you to be able to blow up your own bombs.
  142. //    AND...Your proximity bombs can blow you up if they're 2.0 seconds or more old.
  143. int laser_are_related( int o1, int o2 )
  144. {
  145.     if ( (o1<0) || (o2<0) )    
  146.         return 0;
  147.  
  148.     // See if o2 is the parent of o1
  149.     if ( Objects[o1].type == OBJ_WEAPON  )
  150.         if ( (Objects[o1].ctype.laser_info.parent_num==o2) && (Objects[o1].ctype.laser_info.parent_signature==Objects[o2].signature) )
  151.             //    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
  152.             if ((Objects[o1].id != PROXIMITY_ID) || (Objects[o1].ctype.laser_info.creation_time + F1_0*2 >= GameTime)) {
  153.                 return 1;
  154.             } else
  155.                 return 0;
  156.  
  157.     // See if o1 is the parent of o2
  158.     if ( Objects[o2].type == OBJ_WEAPON  )
  159.         if ( (Objects[o2].ctype.laser_info.parent_num==o1) && (Objects[o2].ctype.laser_info.parent_signature==Objects[o1].signature) )
  160.             return 1;
  161.  
  162.     // They must both be weapons
  163.     if ( Objects[o1].type != OBJ_WEAPON || Objects[o2].type != OBJ_WEAPON )    
  164.         return 0;
  165.  
  166.     //    Here is the 09/07/94 change -- Siblings must be identical, others can hurt each other
  167.     // See if they're siblings...
  168.     if ( Objects[o1].ctype.laser_info.parent_signature==Objects[o2].ctype.laser_info.parent_signature )
  169.         if (Objects[o1].id == PROXIMITY_ID  || Objects[o2].id == PROXIMITY_ID)
  170.             return 0;        //if either is proximity, then can blow up, so say not related
  171.         else
  172.             return 1;
  173.  
  174.     return 0;
  175. }
  176.  
  177. //--unused-- int Muzzle_scale=2;
  178. int Laser_offset=0;
  179.  
  180. void do_muzzle_stuff(int segnum, vms_vector *pos)
  181. {
  182.     Muzzle_data[Muzzle_queue_index].create_time = timer_get_fixed_seconds();
  183.     Muzzle_data[Muzzle_queue_index].segnum = segnum;
  184.     Muzzle_data[Muzzle_queue_index].pos = *pos;
  185.     Muzzle_queue_index++;
  186.     if (Muzzle_queue_index >= MUZZLE_QUEUE_MAX)
  187.         Muzzle_queue_index = 0;
  188. }
  189.  
  190. //---------------------------------------------------------------------------------
  191. // Initializes a laser after Fire is pressed 
  192.  
  193. //    Returns object number.
  194. int Laser_create_new( vms_vector * direction, vms_vector * position, int segnum, int parent, int weapon_type, int make_sound )
  195. {
  196.     int objnum;
  197.     object *obj;
  198.     int rtype=-1;
  199.     fix parent_speed, weapon_speed;
  200.     fix volume;
  201.     fix laser_radius = -1;
  202.     fix laser_length;
  203.  
  204.     Assert( weapon_type < N_weapon_types );
  205.  
  206.     if ( (weapon_type<0) || (weapon_type>=N_weapon_types) )
  207.         weapon_type = 0;
  208.  
  209.     //    Don't let homing blobs make muzzle flash.
  210.     if (Objects[parent].type == OBJ_ROBOT)
  211.         do_muzzle_stuff(segnum, position);
  212.  
  213.     switch( Weapon_info[weapon_type].render_type )    {
  214.     case WEAPON_RENDER_BLOB:
  215.         rtype = RT_LASER;            // Render as a laser even if blob (see render code above for explanation)
  216.         laser_radius = Weapon_info[weapon_type].blob_size;
  217.         laser_length = 0;
  218.         break;
  219.     case WEAPON_RENDER_POLYMODEL:
  220.         laser_radius = 0;    //    Filled in below.
  221.         rtype = RT_POLYOBJ;
  222.         break;
  223.     case WEAPON_RENDER_LASER:
  224.         Int3();     // Not supported anymore
  225.         break;
  226.     case WEAPON_RENDER_NONE:
  227.         rtype = RT_NONE;
  228.         laser_radius = F1_0;
  229.         laser_length = 0;
  230.         break;
  231.     case WEAPON_RENDER_VCLIP:
  232.         rtype = RT_WEAPON_VCLIP;
  233.         laser_radius = Weapon_info[weapon_type].blob_size;
  234.         laser_length = 0;
  235.         break;
  236.     default:
  237.         Error( "Invalid weapon render type in Laser_create_new\n" );
  238.     }
  239.  
  240.     // Add to object list 
  241.     Assert(laser_radius != -1);
  242.     Assert(rtype != -1);
  243.     objnum = obj_create( OBJ_WEAPON, weapon_type, segnum, position, NULL, laser_radius, CT_WEAPON, MT_PHYSICS, rtype );
  244.  
  245.     if ( objnum < 0 )     {
  246.         mprintf((1, "Can't create laser - Out of objects!\n" ));
  247.         Int3();
  248.         return -1;
  249.     }
  250.  
  251.     obj = &Objects[objnum];
  252.  
  253.     if (Objects[parent].type == OBJ_PLAYER) {
  254.         if (weapon_type == FUSION_ID) {
  255.             int    fusion_scale;
  256.     
  257.             if (Game_mode & GM_MULTI)
  258.                 fusion_scale = 2;
  259.             else
  260.                 fusion_scale = 4;
  261.  
  262.  
  263.             if (Fusion_charge <= 0)
  264.                 obj->ctype.laser_info.multiplier = F1_0;
  265.             else if (Fusion_charge <= F1_0*fusion_scale)
  266.                 obj->ctype.laser_info.multiplier = F1_0 + Fusion_charge/2;
  267.             else
  268.                 obj->ctype.laser_info.multiplier = F1_0*fusion_scale;
  269.  
  270.             //    Fusion damage was boosted by mk on 3/27 (for reg 1.1 release), but we only want it to apply to single player games.
  271.             if (Game_mode & GM_MULTI)
  272.                 obj->ctype.laser_info.multiplier /= 2;
  273.         } else if ((weapon_type == LASER_ID) && (Players[Objects[parent].id].flags & PLAYER_FLAGS_QUAD_LASERS))
  274.             obj->ctype.laser_info.multiplier = F1_0*3/4;
  275.     }
  276.  
  277.  
  278.     //    This is strange:  Make children of smart bomb bounce so if they hit a wall right away, they
  279.     //    won't detonate.  The frame interval code will clear this bit after 1/2 second.
  280.     if ((weapon_type == PLAYER_SMART_HOMING_ID) || (weapon_type == ROBOT_SMART_HOMING_ID))
  281.         obj->mtype.phys_info.flags |= PF_BOUNCE;
  282.  
  283.     if (Weapon_info[weapon_type].render_type == WEAPON_RENDER_POLYMODEL) {
  284.         obj->rtype.pobj_info.model_num = Weapon_info[obj->id].model_num;
  285.         laser_radius = fixdiv(Polygon_models[obj->rtype.pobj_info.model_num].rad,Weapon_info[obj->id].po_len_to_width_ratio);
  286.         laser_length = Polygon_models[obj->rtype.pobj_info.model_num].rad * 2;
  287.         obj->size = laser_radius;
  288.     }
  289.  
  290.     obj->mtype.phys_info.mass = Weapon_info[weapon_type].mass;
  291.     obj->mtype.phys_info.drag = Weapon_info[weapon_type].drag;
  292.     if (Weapon_info[weapon_type].bounce)
  293.         obj->mtype.phys_info.flags |= PF_BOUNCE;
  294.  
  295.     vm_vec_zero(&obj->mtype.phys_info.thrust);
  296.  
  297.     if (weapon_type == FLARE_ID)
  298.         obj->mtype.phys_info.flags |= PF_STICK;        //this obj sticks to walls
  299.     
  300.     obj->shields = Weapon_info[obj->id].strength[Difficulty_level];
  301.     
  302.     // Fill in laser-specific data
  303.  
  304.     obj->lifeleft                            = Weapon_info[obj->id].lifetime;
  305.     obj->ctype.laser_info.parent_type        = Objects[parent].type;
  306.     obj->ctype.laser_info.parent_signature = Objects[parent].signature;
  307.     obj->ctype.laser_info.parent_num            = parent;
  308.  
  309.     //    Assign parent type to highest level creator.  This propagates parent type down from
  310.     //    the original creator through weapons which create children of their own (ie, smart missile)
  311.     if (Objects[parent].type == OBJ_WEAPON) {
  312.         int    highest_parent = parent;
  313.  
  314.         while (Objects[highest_parent].type == OBJ_WEAPON) {
  315.             highest_parent                            = Objects[highest_parent].ctype.laser_info.parent_num;
  316.             obj->ctype.laser_info.parent_num            = highest_parent;
  317.             obj->ctype.laser_info.parent_type        = Objects[highest_parent].type;
  318.             obj->ctype.laser_info.parent_signature = Objects[highest_parent].signature;
  319.         }
  320.     }
  321.  
  322.     // Create orientation matrix so we can look from this pov
  323.     //    Homing missiles also need an orientation matrix so they know if they can make a turn.
  324.     if ((obj->render_type == RT_POLYOBJ) || (Weapon_info[obj->id].homing_flag))
  325.         vm_vector_2_matrix( &obj->orient,direction, &Objects[parent].orient.uvec ,NULL);
  326.  
  327.     if (( &Objects[parent] != Viewer ) && (Objects[parent].type != OBJ_WEAPON))    {
  328.         // Muzzle flash        
  329.         if (Weapon_info[obj->id].flash_vclip > -1 )
  330.             object_create_muzzle_flash( obj->segnum, &obj->pos, Weapon_info[obj->id].flash_size, Weapon_info[obj->id].flash_vclip );
  331.     }
  332.  
  333. //    Re-enable, 09/09/94:
  334.     volume = F1_0;
  335.     if (Weapon_info[obj->id].flash_sound > -1 )    {
  336.         if (make_sound)    {
  337.             if ( parent == (Viewer-Objects) )    {
  338.                 if (weapon_type == VULCAN_ID)            // Make your own vulcan gun  1/2 as loud.
  339.                     volume = F1_0 / 2;
  340.                 digi_play_sample( Weapon_info[obj->id].flash_sound, volume );
  341.             } else {
  342.                 digi_link_sound_to_pos( Weapon_info[obj->id].flash_sound, obj->segnum, 0, &obj->pos, 0, volume );
  343.             }
  344.         }
  345.     }
  346.  
  347.     //    WARNING! NOTE! HEY! DEBUG! ETC! --MK 10/26/94
  348.     //    This is John's new code to fire the laser from the gun tip so that the back end of the laser bolt is
  349.     //    at the gun tip.  A problem that needs to be fixed is that the laser bolt might be in another segment.
  350.     //    Use find_vector_interesection to detect the segment.
  351.  
  352.     // Move 1 frame, so that the end-tip of the laser is touching the gun barrel.
  353.     // This also jitters the laser a bit so that it doesn't alias.
  354.     //    Don't do for weapons created by weapons.
  355.     if ((Objects[parent].type != OBJ_WEAPON) && (Weapon_info[weapon_type].render_type != WEAPON_RENDER_NONE) && (weapon_type != FLARE_ID)) {
  356. //    if ((Objects[parent].type != OBJ_WEAPON) && (weapon_type != FLARE_ID) ) {
  357.         vms_vector    end_pos;
  358.         int            end_segnum;
  359.  
  360.          vm_vec_scale_add( &end_pos, &obj->pos, direction, Laser_offset+(laser_length/2) );
  361.         end_segnum = find_point_seg(&end_pos, obj->segnum);
  362.         if (end_segnum != obj->segnum) {
  363.             // mprintf(0, "Warning: Laser tip not in same segment as player.\n");
  364.             if (end_segnum != -1) {
  365.                 obj->pos = end_pos;
  366.                 obj_relink(obj-Objects, end_segnum);
  367.             } else
  368.                 mprintf((0, "Warning: Laser tip outside mine.  Laser not being moved to end of gun.\n"));
  369.         } else
  370.             obj->pos = end_pos;
  371.     }
  372.  
  373.     //    Here's where to fix the problem with objects which are moving backwards imparting higher velocity to their weaponfire.
  374.     //    Find out if moving backwards.
  375.     if (weapon_type == PROXIMITY_ID) {
  376.         parent_speed = vm_vec_mag_quick(&Objects[parent].mtype.phys_info.velocity);
  377.         if (vm_vec_dot(&Objects[parent].mtype.phys_info.velocity, &Objects[parent].orient.fvec) < 0)
  378.             parent_speed = -parent_speed;
  379.     } else
  380.         parent_speed = 0;
  381.  
  382.     weapon_speed = Weapon_info[obj->id].speed[Difficulty_level];
  383.  
  384.     //    Ugly hack (too bad we're on a deadline), for homing missiles dropped by smart bomb, start them out slower.
  385.     if ((obj->id == PLAYER_SMART_HOMING_ID) || (obj->id == ROBOT_SMART_HOMING_ID))
  386.         weapon_speed /= 4;
  387.  
  388.     if (Weapon_info[obj->id].thrust != 0)
  389.         weapon_speed /= 2;
  390.  
  391.     vm_vec_copy_scale( &obj->mtype.phys_info.velocity, direction, weapon_speed + parent_speed );
  392. ////Debug 101594
  393. //if ((vm_vec_mag(&obj->mtype.phys_info.velocity) == 0) && (obj->id != PROXIMITY_ID))
  394. //    Int3();    //    Curious.  This weapon starts with a velocity of 0 and it's not a proximity bomb.
  395.  
  396.     //    Set thrust 
  397.     if (Weapon_info[weapon_type].thrust != 0) {
  398.         obj->mtype.phys_info.thrust = obj->mtype.phys_info.velocity;
  399.         vm_vec_scale(&obj->mtype.phys_info.thrust, fixdiv(Weapon_info[obj->id].thrust, weapon_speed+parent_speed));
  400.     }
  401.  
  402. // THIS CODE MAY NOT BE NEEDED... it was used to move the lasers out of the gun, since the
  403. // laser pos is acutally the head of the laser, and we want the tail to be at the starting
  404. // point, not the head.  
  405. //    object_move_one( obj );
  406. //    This next, apparently redundant line, appears necessary due to a hack in render.c
  407. //    obj->lifeleft = Weapon_info[obj->id].lifetime;
  408.  
  409.     if ((obj->type == OBJ_WEAPON) && (obj->id == FLARE_ID))
  410.         obj->lifeleft += (rand()-16384) << 2;        //    add in -2..2 seconds
  411.  
  412. //    mprintf( 0, "Weapon speed = %.1f (%.1f)\n", f2fl(Weapon_info[obj->id].speed[Difficulty_level] + parent_speed), f2fl(parent_speed) );
  413.  
  414.     return objnum;
  415. }
  416.  
  417. //    -----------------------------------------------------------------------------------------------------------
  418. //    Calls Laser_create_new, but takes care of the segment and point computation for you.
  419. int Laser_create_new_easy( vms_vector * direction, vms_vector * position, int parent, int weapon_type, int make_sound )
  420. {
  421.     fvi_query    fq;
  422.     fvi_info        hit_data;
  423.     object        *pobjp = &Objects[parent];
  424.     int            fate;
  425.  
  426.     //    Find segment containing laser fire position.  If the robot is straddling a segment, the position from
  427.     //    which it fires may be in a different segment, which is bad news for find_vector_intersection.  So, cast
  428.     //    a ray from the object center (whose segment we know) to the laser position.  Then, in the call to Laser_create_new
  429.     //    use the data returned from this call to find_vector_intersection.
  430.     //    Note that while find_vector_intersection is pretty slow, it is not terribly slow if the destination point is
  431.     //    in the same segment as the source point.
  432.  
  433.     fq.p0                        = &pobjp->pos;
  434.     fq.startseg                = pobjp->segnum;
  435.     fq.p1                        = position;
  436.     fq.rad                    = 0;
  437.     fq.thisobjnum            = pobjp-Objects;
  438.     fq.ignore_obj_list    = NULL;
  439.     fq.flags                    = FQ_TRANSWALL | FQ_CHECK_OBJS;        //what about trans walls???
  440.  
  441.     fate = find_vector_intersection(&fq, &hit_data);
  442.     if (fate != HIT_NONE  || hit_data.hit_seg==-1) {
  443.         mprintf((1, "Warning: Laser from parent=%i stuck in wall or object, didn't fire!\n", parent));
  444.         return -1;
  445.     }
  446.  
  447.     return Laser_create_new( direction, &hit_data.hit_pnt, hit_data.hit_seg, parent, weapon_type, make_sound );
  448.  
  449. }
  450.  
  451. int        Muzzle_queue_index = 0;
  452.  
  453. muzzle_info        Muzzle_data[MUZZLE_QUEUE_MAX];
  454.  
  455. //    -----------------------------------------------------------------------------------------------------------
  456. //    Determine if two objects are on a line of sight.  If so, return true, else return false.
  457. //    Calls fvi.
  458. int object_to_object_visibility(object *obj1, object *obj2, int trans_type)
  459. {
  460.     fvi_query    fq;
  461.     fvi_info        hit_data;
  462.     int            fate;
  463.  
  464.     fq.p0                        = &obj1->pos;
  465.     fq.startseg                = obj1->segnum;
  466.     fq.p1                        = &obj2->pos;
  467.     fq.rad                    = 0x10;
  468.     fq.thisobjnum            = obj1-Objects;
  469.     fq.ignore_obj_list    = NULL;
  470.     fq.flags                    = trans_type;
  471.  
  472.     fate = find_vector_intersection(&fq, &hit_data);
  473.  
  474.     if (fate == HIT_WALL)
  475.         return 0;
  476.     else if (fate == HIT_NONE)
  477.         return 1;
  478.     else
  479.         Int3();        //    Contact Mike: Oops, what happened?  What is fate?
  480.                         // 2 = hit object (impossible), 3 = bad starting point (bad)
  481.  
  482.     return 0;
  483. }
  484.  
  485. fix    Min_trackable_dot = MIN_TRACKABLE_DOT;
  486.  
  487. //    -----------------------------------------------------------------------------------------------------------
  488. //    Return true if weapon *tracker is able to track object Objects[track_goal], else return false.
  489. //    In order for the object to be trackable, it must be within a reasonable turning radius for the missile
  490. //    and it must not be obstructed by a wall.
  491. int object_is_trackable(int track_goal, object *tracker)
  492. {
  493.     fix            dot; //, dist_to_goal;
  494.     vms_vector    vector_to_goal;
  495.     object        *objp;
  496.  
  497.     if (track_goal == -1)
  498.         return 0;
  499.  
  500.     if (Game_mode & GM_MULTI_COOP)
  501.         return 0;
  502.  
  503.     objp = &Objects[track_goal];
  504.  
  505.     //    Don't track player if he's cloaked.
  506.     if ((track_goal == Players[Player_num].objnum) && (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED))
  507.         return 0;
  508.  
  509.     //    Can't track AI object if he's cloaked.
  510.     if (objp->type == OBJ_ROBOT)
  511.         if (objp->ctype.ai_info.CLOAKED)
  512.             return 0;
  513.  
  514.     vm_vec_sub(&vector_to_goal, &objp->pos, &tracker->pos);
  515.     vm_vec_normalize_quick(&vector_to_goal);
  516.     dot = vm_vec_dot(&vector_to_goal, &tracker->orient.fvec);
  517.  
  518.     // mprintf((0, "object_is_trackable: [%3i] %7.3f, min = %7.3f\n", track_goal, f2fl(dot), f2fl(Min_trackable_dot)));
  519.  
  520.     if (dot >= Min_trackable_dot) {
  521.         int    rval;
  522.         //    dot is in legal range, now see if object is visible
  523.         rval =  object_to_object_visibility(tracker, objp, FQ_TRANSWALL);
  524.         return rval;
  525.     } else {
  526.         return 0;
  527.     }
  528.  
  529. }
  530.  
  531. //    --------------------------------------------------------------------------------------------
  532. //    Find object to home in on.
  533. //    Scan list of objects rendered last frame, find one that satisfies function of nearness to center and distance.
  534. int find_homing_object(vms_vector *curpos, object *tracker)
  535. {
  536.     int    i;
  537.     fix    max_dot = -F1_0*2;
  538.     int    best_objnum = -1;
  539.  
  540.     if (!Weapon_info[tracker->id].homing_flag) {
  541.         Int3();        //    Contact Mike: This is a bad and stupid thing.  Who called this routine with an illegal laser type??
  542.         return 0;    //    Track the damn stupid player for causing this problem!
  543.     }
  544.  
  545.     //    Find an object to track based on game mode (eg, whether in network play) and who fired it.
  546.     if (Game_mode & GM_MULTI) {
  547.         //    In network mode.
  548.         if (tracker->ctype.laser_info.parent_type == OBJ_PLAYER) {
  549.             //    It's fired by a player, so if robots present, track robot, else track player.
  550.             if (Game_mode & GM_MULTI_COOP)
  551.                 return find_homing_object_complete( curpos, tracker, OBJ_ROBOT, -1);
  552.             else
  553.                 return find_homing_object_complete( curpos, tracker, OBJ_PLAYER, OBJ_ROBOT);
  554.         } else {
  555.             Assert(tracker->ctype.laser_info.parent_type == OBJ_ROBOT);
  556.             return find_homing_object_complete(curpos, tracker, OBJ_PLAYER, -1);
  557.         }        
  558.     } 
  559.     else {
  560.         //    Not in network mode.  If not fired by player, then track player.
  561.         if (tracker->ctype.laser_info.parent_num != Players[Player_num].objnum) {
  562.             if (!(Players[Player_num].flags & PLAYER_FLAGS_CLOAKED))
  563.                 best_objnum = ConsoleObject - Objects;
  564.         } else {
  565.             //    Not in network mode and fired by player.
  566.             for (i=Num_rendered_objects-1; i>=0; i--) {
  567.                 fix            dot; //, dist;
  568.                 vms_vector    vec_to_curobj;
  569.                 int            objnum = Ordered_rendered_object_list[i];
  570.                 object        *curobjp = &Objects[objnum];
  571.  
  572.                 if (objnum == Players[Player_num].objnum)
  573.                     continue;
  574.  
  575.                 //    Can't track AI object if he's cloaked.
  576.                 if (curobjp->type == OBJ_ROBOT)
  577.                     if (curobjp->ctype.ai_info.CLOAKED)
  578.                         continue;
  579.  
  580.                 vm_vec_sub(&vec_to_curobj, &curobjp->pos, curpos);
  581.                 vm_vec_normalize_quick(&vec_to_curobj);
  582.                 dot = vm_vec_dot(&vec_to_curobj, &tracker->orient.fvec);
  583.  
  584.                 // mprintf(0, "Object %i: dist = %7.3f, dot = %7.3f\n", objnum, f2fl(dist), f2fl(dot));
  585.  
  586.                 //    Note: This uses the constant, not-scaled-by-frametime value, because it is only used
  587.                 //    to determine if an object is initially trackable.  find_homing_object is called on subsequent
  588.                 //    frames to determine if the object remains trackable.
  589.                 // mprintf((0, "find_homing_object:  [%3i] %7.3f, min = %7.3f\n", curobjp-Objects, f2fl(dot), f2fl(MIN_TRACKABLE_DOT)));
  590.                 if (dot > MIN_TRACKABLE_DOT) {
  591.                     if (dot > max_dot) {
  592.                         if (object_to_object_visibility(tracker, &Objects[objnum], FQ_TRANSWALL)) {
  593.                             max_dot = dot;
  594.                             best_objnum = objnum;
  595.                         }
  596.                     }
  597.                 }
  598.             }
  599.         }
  600.     }
  601.  
  602.     // mprintf(0, "Selecting object #%i\n=n", best_objnum);
  603.  
  604.     return best_objnum;
  605. }
  606.  
  607. //    --------------------------------------------------------------------------------------------
  608. //    Find object to home in on.
  609. //    Scan list of objects rendered last frame, find one that satisfies function of nearness to center and distance.
  610. //    Can track two kinds of objects.  If you are only interested in one type, set track_obj_type2 to NULL
  611. int find_homing_object_complete(vms_vector *curpos, object *tracker, int track_obj_type1, int track_obj_type2)
  612. {
  613.     int    objnum;
  614.     fix    max_dot = -F1_0*2;
  615.     int    best_objnum = -1;
  616.  
  617.     if (!Weapon_info[tracker->id].homing_flag) {
  618.         Int3();        //    Contact Mike: This is a bad and stupid thing.  Who called this routine with an illegal laser type??
  619.         return 0;    //    Track the damn stupid player for causing this problem!
  620.     }
  621.  
  622.     for (objnum=0; objnum<=Highest_object_index; objnum++) {
  623.         fix            dot, dist;
  624.         vms_vector    vec_to_curobj;
  625.         object        *curobjp = &Objects[objnum];
  626.  
  627.         if ((curobjp->type != track_obj_type1) && (curobjp->type != track_obj_type2))
  628.             continue;
  629.  
  630.         if (objnum == tracker->ctype.laser_info.parent_num) // Don't track shooter
  631.             continue;
  632.  
  633.         //    Don't track cloaked players.
  634.         if (curobjp->type == OBJ_PLAYER)
  635.         {
  636.             if (Players[curobjp->id].flags & PLAYER_FLAGS_CLOAKED)
  637.                 continue;
  638.             // Don't track teammates in team games
  639.             #ifdef NETWORK
  640.             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)))
  641.                 continue;
  642.             #endif
  643.         }
  644.  
  645.         //    Can't track AI object if he's cloaked.
  646.         if (curobjp->type == OBJ_ROBOT)
  647.             if (curobjp->ctype.ai_info.CLOAKED)
  648.                 continue;
  649.  
  650.         vm_vec_sub(&vec_to_curobj, &curobjp->pos, curpos);
  651.         dist = vm_vec_mag_quick(&vec_to_curobj);
  652.  
  653.         if (dist < MAX_TRACKABLE_DIST) {
  654.             vm_vec_normalize_quick(&vec_to_curobj);
  655.             dot = vm_vec_dot(&vec_to_curobj, &tracker->orient.fvec);
  656.             //    Note: This uses the constant, not-scaled-by-frametime value, because it is only used
  657.             //    to determine if an object is initially trackable.  find_homing_object is called on subsequent
  658.             //    frames to determine if the object remains trackable.
  659.             // mprintf((0, "fho_complete:        [%3i] %7.3f, min = %7.3f\n", curobjp-Objects, f2fl(dot), f2fl(MIN_TRACKABLE_DOT)));
  660.             if (dot > MIN_TRACKABLE_DOT) {
  661.                 // mprintf(0, "Object %i: dist = %7.3f, dot = %7.3f\n", objnum, f2fl(dist), f2fl(dot));
  662.                 if (dot > max_dot) {
  663.                     if (object_to_object_visibility(tracker, &Objects[objnum], FQ_TRANSWALL)) {
  664.                         max_dot = dot;
  665.                         best_objnum = objnum;
  666.                     }
  667.                 }
  668.             }
  669.         }
  670.  
  671.     }
  672.  
  673. //    mprintf((0, "Selecting object #%i in find_homing_object_complete\n\n", best_objnum));
  674.  
  675.     return best_objnum;
  676. }
  677.  
  678. //    ------------------------------------------------------------------------------------------------------------
  679. //    See if legal to keep tracking currently tracked object.  If not, see if another object is trackable.  If not, return -1,
  680. //    else return object number of tracking object.
  681. int track_track_goal(int track_goal, object *tracker)
  682. {
  683.     if (object_is_trackable(track_goal, tracker))
  684.         return track_goal;
  685.     else if ((((tracker-Objects) ^ FrameCount) % 4) == 0) {
  686.         int    rval = -2;
  687.  
  688.         //    If player fired missile, then search for an object, if not, then give up.
  689.         if (Objects[tracker->ctype.laser_info.parent_num].type == OBJ_PLAYER) {
  690.             int    goal_type;
  691.  
  692.             if (track_goal == -1) 
  693.             {
  694.                 if (Game_mode & GM_MULTI)
  695.                 {
  696.                     if (Game_mode & GM_MULTI_COOP)
  697.                         rval = find_homing_object_complete( &tracker->pos, tracker, OBJ_ROBOT, -1);
  698.                     else if (Game_mode & GM_MULTI_ROBOTS)        //    Not cooperative, if robots, track either robot or player
  699.                         rval = find_homing_object_complete( &tracker->pos, tracker, OBJ_PLAYER, OBJ_ROBOT);
  700.                     else        //    Not cooperative and no robots, track only a player
  701.                         rval = find_homing_object_complete( &tracker->pos, tracker, OBJ_PLAYER, -1);
  702.                 }
  703.                 else
  704.                     rval = find_homing_object_complete(&tracker->pos, tracker, OBJ_PLAYER, OBJ_ROBOT);
  705.             } 
  706.             else 
  707.             {
  708.                 goal_type = Objects[tracker->ctype.laser_info.track_goal].type;
  709.                 if ((goal_type == OBJ_PLAYER) || (goal_type == OBJ_ROBOT))
  710.                     rval = find_homing_object_complete(&tracker->pos, tracker, goal_type, -1);
  711.                 else
  712.                     rval = -1;
  713.             }
  714.         } 
  715.         else {
  716.             int    goal_type;
  717.  
  718.             if (track_goal == -1)
  719.                 rval = find_homing_object_complete(&tracker->pos, tracker, OBJ_PLAYER, -1);
  720.             else {
  721.                 goal_type = Objects[tracker->ctype.laser_info.track_goal].type;
  722.                 rval = find_homing_object_complete(&tracker->pos, tracker, goal_type, -1);
  723.             }
  724.         }
  725.  
  726.         Assert(rval != -2);        //    This means it never got set which is bad!  Contact Mike.
  727.         return rval;
  728.     }
  729.  
  730. //if (track_goal != -1)
  731. // mprintf((0, "Object %i not tracking anything.\n", tracker-Objects));
  732.  
  733.     return -1;
  734. }
  735.  
  736.  
  737. //-------------- Initializes a laser after Fire is pressed -----------------
  738.  
  739. 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)
  740. {
  741.     int            LaserSeg, Fate; 
  742.     vms_vector    LaserPos, LaserDir;
  743.     fvi_query    fq;
  744.     fvi_info        hit_data;
  745.     vms_vector    gun_point, *pnt;
  746.     vms_matrix    m;
  747.     int            objnum;
  748.  
  749.     // Find the initial position of the laser
  750.     pnt = &Player_ship->gun_points[gun_num];
  751.  
  752.     vm_copy_transpose_matrix(&m,&obj->orient);
  753.     vm_vec_rotate(&gun_point,pnt,&m);
  754.  
  755.     vm_vec_add(&LaserPos,&obj->pos,&gun_point);
  756.  
  757.     //    If supposed to fire at a delayed time (delay_time), then move this point backwards.
  758.     if (delay_time)
  759.         vm_vec_scale_add2(&LaserPos, &obj->orient.fvec, -fixmul(delay_time, Weapon_info[laser_type].speed[Difficulty_level]));
  760.  
  761. //    do_muzzle_stuff(obj, &Pos);
  762.  
  763.     //--------------- Find LaserPos and LaserSeg ------------------
  764.     fq.p0                        = &obj->pos;
  765.     fq.startseg                = obj->segnum;
  766.     fq.p1                        = &LaserPos;
  767.     fq.rad                    = 0x10;
  768.     fq.thisobjnum            = obj-Objects;
  769.     fq.ignore_obj_list    = NULL;
  770.     fq.flags                    = FQ_CHECK_OBJS;
  771.  
  772.     Fate = find_vector_intersection(&fq, &hit_data);
  773.  
  774.     LaserSeg = hit_data.hit_seg;
  775.  
  776.     if (LaserSeg == -1)        //some sort of annoying error
  777.         return;
  778.  
  779.     //SORT OF HACK... IF ABOVE WAS CORRECT THIS WOULDNT BE NECESSARY.
  780.     if ( vm_vec_dist_quick(&LaserPos, &obj->pos) > 0x50000 )
  781.         return;
  782.     
  783.     if (Fate==HIT_WALL) {
  784.         if (delay_time)
  785.             mprintf((0, "Your DELAYED laser is stuck thru a wall!\n" ));
  786.         else
  787.             mprintf((0, "Your laser is stuck thru a wall!\n" ));
  788.         return;        
  789.     }
  790.  
  791.     if (Fate==HIT_OBJECT) {
  792. //        if ( Objects[hit_data.hit_object].type == OBJ_ROBOT )
  793. //            Objects[hit_data.hit_object].flags |= OF_SHOULD_BE_DEAD;
  794.         mprintf((0, "Your laser is stuck in an object!\n" ));
  795. //        if ( Objects[hit_data.hit_object].type != OBJ_POWERUP )
  796. //            return;        
  797.     //as of 12/6/94, we don't care if the laser is stuck in an object. We
  798.     //just fire away normally
  799.     }
  800.  
  801.     //    Now, make laser spread out.
  802.     LaserDir = obj->orient.fvec;
  803.     if ((spreadr != 0) || (spreadu != 0)) {
  804.         vm_vec_scale_add2(&LaserDir, &obj->orient.rvec, spreadr);
  805.         vm_vec_scale_add2(&LaserDir, &obj->orient.uvec, spreadu);
  806.     }
  807.  
  808.     objnum = Laser_create_new( &LaserDir, &LaserPos, LaserSeg, obj-Objects, laser_type, make_sound );
  809.     if (objnum == -1)
  810.         return;
  811.  
  812.     //    If this weapon is supposed to be silent, set that bit!
  813.     if (!make_sound)
  814.         Objects[objnum].flags |= OF_SILENT;
  815.  
  816.     //    If this weapon is supposed to be silent, set that bit!
  817.     if (harmless)
  818.         Objects[objnum].flags |= OF_HARMLESS;
  819.  
  820.     //    If the object firing the laser is the player, then indicate the laser object so robots can dodge.
  821.     if (obj == ConsoleObject)
  822.         Player_fired_laser_this_frame = objnum;
  823.  
  824.     if (Weapon_info[laser_type].homing_flag) {
  825.         if (obj == ConsoleObject)
  826.         {
  827.             Objects[objnum].ctype.laser_info.track_goal = find_homing_object(&LaserPos, &Objects[objnum]);
  828.             #ifdef NETWORK
  829.             Network_laser_track = Objects[objnum].ctype.laser_info.track_goal;
  830.             #endif
  831.         }
  832.         #ifdef NETWORK
  833.         else // Some other player shot the homing thing
  834.         {
  835.             Assert(Game_mode & GM_MULTI);
  836.             Objects[objnum].ctype.laser_info.track_goal = Network_laser_track;
  837.         }
  838.         #endif
  839. //        mprintf((0, "Selecting object #%i in find_homing_object_complete\n", Network_laser_track));
  840.     }
  841. }
  842.  
  843. //    -----------------------------------------------------------------------------------------------------------
  844. void Laser_player_fire_spread(object *obj, int laser_type, int gun_num, fix spreadr, fix spreadu, int make_sound, int harmless)
  845. {
  846.     Laser_player_fire_spread_delay(obj, laser_type, gun_num, spreadr, spreadu, 0, make_sound, harmless);
  847. }
  848.  
  849.  
  850. //    -----------------------------------------------------------------------------------------------------------
  851. void Laser_player_fire(object *obj, int laser_type, int gun_num, int make_sound, int harmless)
  852. {
  853.     Laser_player_fire_spread(obj, laser_type, gun_num, 0, 0, make_sound, harmless);
  854. }
  855.  
  856. //    -----------------------------------------------------------------------------------------------------------
  857. void Flare_create(object *obj)
  858. {
  859.     fix    energy_usage;
  860.  
  861.     energy_usage = Weapon_info[FLARE_ID].energy_usage;
  862.  
  863.     if (Difficulty_level < 2)
  864.         energy_usage = fixmul(energy_usage, i2f(Difficulty_level+2)/4);
  865.  
  866.     if (Players[Player_num].energy > 0) {
  867.         Players[Player_num].energy -= energy_usage;
  868.  
  869.         if (Players[Player_num].energy <= 0) {
  870.             Players[Player_num].energy = 0;    
  871.             auto_select_weapon(0);
  872.         }
  873.  
  874.         Laser_player_fire( obj, FLARE_ID, 6, 1, 0);
  875.  
  876.         #ifdef NETWORK
  877.         if (Game_mode & GM_MULTI)
  878.         {
  879.             Network_laser_fired = 1;
  880.             Network_laser_gun = FLARE_ID+MISSILE_ADJUST;
  881.             Network_laser_flags = 0;
  882.             Network_laser_level = 0;
  883.         }
  884.         #endif
  885.     }
  886.  
  887. }
  888.  
  889. #define    HOMING_MISSILE_SCALE    8
  890.  
  891. //-------------------------------------------------------------------------------------------
  892. //    Set object *objp's orientation to (or towards if I'm ambitious) its velocity.
  893. void homing_missile_turn_towards_velocity(object *objp, vms_vector *norm_vel)
  894. {
  895.     vms_vector    new_fvec;
  896.  
  897.     new_fvec = *norm_vel;
  898.  
  899.     vm_vec_scale(&new_fvec, FrameTime*HOMING_MISSILE_SCALE);
  900.     vm_vec_add2(&new_fvec, &objp->orient.fvec);
  901.     vm_vec_normalize_quick(&new_fvec);
  902.  
  903. //    if ((norm_vel->x == 0) && (norm_vel->y == 0) && (norm_vel->z == 0))
  904. //        return;
  905.  
  906.     vm_vector_2_matrix(&objp->orient, &new_fvec, NULL, NULL);
  907. }
  908.  
  909. //-------------------------------------------------------------------------------------------
  910. //sequence this laser object for this _frame_ (underscores added here to aid MK in his searching!)
  911. void Laser_do_weapon_sequence(object *obj)
  912. {
  913.     Assert(obj->control_type == CT_WEAPON);
  914.  
  915.     if (obj->lifeleft < 0 ) {        // We died of old age
  916.         obj->flags |= OF_SHOULD_BE_DEAD;
  917.         if ( Weapon_info[obj->id].damage_radius )
  918.             explode_badass_weapon(obj);
  919.         return;
  920.     }
  921.  
  922.     //delete weapons that are not moving
  923.     if (    !((FrameCount ^ obj->signature) & 3) &&
  924.             (obj->id != FLARE_ID) &&
  925.             (Weapon_info[obj->id].speed[Difficulty_level] > 0) &&
  926.             (vm_vec_mag_quick(&obj->mtype.phys_info.velocity) < F2_0)) {
  927.         obj_delete(obj-Objects);
  928.         return;
  929.     }
  930.  
  931.     if ( obj->id == FUSION_ID ) {        //always set fusion weapon to max vel
  932.  
  933.         vm_vec_normalize_quick(&obj->mtype.phys_info.velocity);
  934.  
  935.         vm_vec_scale(&obj->mtype.phys_info.velocity, Weapon_info[obj->id].speed[Difficulty_level]);
  936.     }
  937.  
  938.     //    For homing missiles, turn towards target.
  939.     if (Weapon_info[obj->id].homing_flag) {
  940.         vms_vector        vector_to_object, temp_vec;
  941.         fix                dot;
  942.         fix                speed, max_speed;
  943.  
  944.         //    For first 1/2 second of life, missile flies straight.
  945.         if (obj->ctype.laser_info.creation_time + HOMING_MISSILE_STRAIGHT_TIME < GameTime) {
  946.  
  947.             int    track_goal = obj->ctype.laser_info.track_goal;
  948.  
  949.             //    If it's time to do tracking, then it's time to grow up, stop bouncing and start exploding!.
  950.             if ((obj->id == ROBOT_SMART_HOMING_ID) || (obj->id == PLAYER_SMART_HOMING_ID)) {
  951.                 // if (obj->mtype.phys_info.flags & PF_BOUNCE) mprintf(0, "Debouncing smart child %i\n", obj-Objects);
  952.                 obj->mtype.phys_info.flags &= ~PF_BOUNCE;
  953.             }
  954.  
  955.             //    Make sure the object we are tracking is still trackable.
  956.             track_goal = track_track_goal(track_goal, obj);
  957.  
  958.             if (track_goal == Players[Player_num].objnum) {
  959.                 fix    dist_to_player;
  960.  
  961.                 dist_to_player = vm_vec_dist_quick(&obj->pos, &Objects[track_goal].pos);
  962.                 if ((dist_to_player < Players[Player_num].homing_object_dist) || (Players[Player_num].homing_object_dist < 0))
  963.                     Players[Player_num].homing_object_dist = dist_to_player;
  964.                     
  965.             }
  966.  
  967.             if (track_goal != -1) {
  968.                 vm_vec_sub(&vector_to_object, &Objects[track_goal].pos, &obj->pos);
  969.  
  970.                 vm_vec_normalize_quick(&vector_to_object);
  971.                 temp_vec = obj->mtype.phys_info.velocity;
  972.                 speed = vm_vec_normalize_quick(&temp_vec);
  973.                 max_speed = Weapon_info[obj->id].speed[Difficulty_level];
  974.                 if (speed+F1_0 < max_speed) {
  975.                     speed += fixmul(max_speed, FrameTime/2);
  976.                     if (speed > max_speed)
  977.                         speed = max_speed;
  978.                 }
  979.  
  980.                 dot = vm_vec_dot(&temp_vec, &vector_to_object);
  981.                 vm_vec_add2(&temp_vec, &vector_to_object);
  982.                 //    The boss' smart children track better...
  983.                 if (Weapon_info[obj->id].render_type != WEAPON_RENDER_POLYMODEL)
  984.                     vm_vec_add2(&temp_vec, &vector_to_object);
  985.                 vm_vec_normalize_quick(&temp_vec);
  986.                 vm_vec_scale(&temp_vec, speed);
  987.                 obj->mtype.phys_info.velocity = temp_vec;
  988.  
  989.                 //    Subtract off life proportional to amount turned.
  990.                 //    For hardest turn, it will lose 2 seconds per second.
  991.                 {
  992.                     fix    lifelost, absdot;
  993.                 
  994.                     absdot = abs(F1_0 - dot);
  995.                 
  996.                     if (absdot > F1_0/8) {
  997.                         if (absdot > F1_0/4)
  998.                             absdot = F1_0/4;
  999.                         lifelost = fixmul(absdot*16, FrameTime);
  1000.                         obj->lifeleft -= lifelost;
  1001.                         // mprintf((0, "Missile %3i, dot = %7.3f life lost = %7.3f, life left = %7.3f\n", obj-Objects, f2fl(dot), f2fl(lifelost), f2fl(obj->lifeleft)));
  1002.                     }
  1003.                 }
  1004.  
  1005.                 //    Only polygon objects have visible orientation, so only they should turn.
  1006.                 if (Weapon_info[obj->id].render_type == WEAPON_RENDER_POLYMODEL)
  1007.                     homing_missile_turn_towards_velocity(obj, &temp_vec);        //    temp_vec is normalized velocity.
  1008.             }
  1009.         }
  1010.  
  1011.     }
  1012.  
  1013.     //    Make sure weapon is not moving faster than allowed speed.
  1014.     if (Weapon_info[obj->id].thrust != 0) {
  1015.         fix    weapon_speed;
  1016.  
  1017.         weapon_speed = vm_vec_mag_quick(&obj->mtype.phys_info.velocity);
  1018.         if (weapon_speed > Weapon_info[obj->id].speed[Difficulty_level]) {
  1019.             fix    scale_factor;
  1020.  
  1021.             scale_factor = fixdiv(Weapon_info[obj->id].speed[Difficulty_level], weapon_speed);
  1022.             vm_vec_scale(&obj->mtype.phys_info.velocity, scale_factor);
  1023.         }
  1024.  
  1025.     }
  1026. }
  1027.  
  1028. int    Spreadfire_toggle=0;
  1029. fix    Last_laser_fired_time = 0;
  1030.  
  1031. extern int Player_fired_laser_this_frame;
  1032.  
  1033. //    --------------------------------------------------------------------------------------------------
  1034. // Assumption: This is only called by the actual console player, not for
  1035. //   network players
  1036.  
  1037. int do_laser_firing_player(void)
  1038. {
  1039.     player    *plp = &Players[Player_num];
  1040.     fix        energy_used;
  1041.     int        ammo_used;
  1042.     int        weapon_index;
  1043.     int        rval = 0;
  1044.     int         nfires = 1;
  1045.     fix        addval;
  1046.  
  1047.     if (Player_is_dead)
  1048.         return 0;
  1049.  
  1050.     weapon_index = Primary_weapon_to_weapon_info[Primary_weapon];
  1051.     energy_used = Weapon_info[weapon_index].energy_usage;
  1052.  
  1053.     if (Difficulty_level < 2)
  1054.         energy_used = fixmul(energy_used, i2f(Difficulty_level+2)/4);
  1055.  
  1056.     ammo_used = Weapon_info[weapon_index].ammo_usage;
  1057.  
  1058.     addval = 2*FrameTime;
  1059.     if (addval > F1_0)
  1060.         addval = F1_0;
  1061.  
  1062.     if (Last_laser_fired_time + 2*FrameTime < GameTime)
  1063.         Next_laser_fire_time = GameTime;
  1064.  
  1065.     Last_laser_fired_time = GameTime;
  1066.  
  1067.     while (Next_laser_fire_time <= GameTime) {
  1068.         if    ((plp->energy >= energy_used) || ((Primary_weapon == VULCAN_INDEX) && (plp->primary_ammo[Primary_weapon] >= ammo_used)) ) {
  1069.             int    laser_level, flags;
  1070.  
  1071. //mprintf(0, ".");
  1072.             if (Laser_rapid_fire!=0xBADA55)
  1073.                 Next_laser_fire_time += Weapon_info[weapon_index].fire_wait;
  1074.             else
  1075.                 Next_laser_fire_time += F1_0/25;
  1076.  
  1077.             laser_level = Players[Player_num].laser_level;
  1078.     
  1079.             flags = 0;
  1080.  
  1081.             if (Primary_weapon == SPREADFIRE_INDEX) {
  1082.                 if (Spreadfire_toggle)
  1083.                     flags |= LASER_SPREADFIRE_TOGGLED;
  1084.                 Spreadfire_toggle = !Spreadfire_toggle;
  1085.             }
  1086.  
  1087.             if (Players[Player_num].flags & PLAYER_FLAGS_QUAD_LASERS)
  1088.                 flags |= LASER_QUAD;
  1089.  
  1090.             rval += do_laser_firing(Players[Player_num].objnum, Primary_weapon, laser_level, flags, nfires);
  1091.  
  1092.             plp->energy -= (energy_used * rval) / Weapon_info[weapon_index].fire_count;
  1093.             if (plp->energy < 0)
  1094.                 plp->energy = 0;
  1095.  
  1096.             if (ammo_used > plp->primary_ammo[Primary_weapon])
  1097.                 plp->primary_ammo[Primary_weapon] = 0;
  1098.             else
  1099.                 plp->primary_ammo[Primary_weapon] -= ammo_used;
  1100.  
  1101.             auto_select_weapon(0);        //    Make sure the player can fire from this weapon.
  1102.  
  1103.         } else
  1104.             break;    //    Couldn't fire weapon, so abort.
  1105.     }
  1106. //mprintf(0, "  fires = %i\n", rval);
  1107.  
  1108.     Global_laser_firing_count = 0;    
  1109.  
  1110.     return rval;
  1111. }
  1112.  
  1113. //    --------------------------------------------------------------------------------------------------
  1114. //    Object "objnum" fires weapon "weapon_num" of level "level".  (Right now (9/24/94) level is used only for type 0 laser.
  1115. //    Flags are the player flags.  For network mode, set to 0.
  1116. //    It is assumed that this is a player object (as in multiplayer), and therefore the gun positions are known.
  1117. //    Returns number of times a weapon was fired.  This is typically 1, but might be more for low frame rates.
  1118. //    More than one shot is fired with a pseudo-delay so that players on show machines can fire (for themselves
  1119. //    or other players) often enough for things like the vulcan cannon.
  1120. int do_laser_firing(int objnum, int weapon_num, int level, int flags, int nfires)
  1121. {
  1122.     object    *objp = &Objects[objnum];
  1123.  
  1124.     // The Laser_offset is used to "jitter" the laser fire so that lasers don't always appear
  1125.     // right in front of your face.   I put it here instead of laser_create_new because I want
  1126.     // both of the dual laser beams to be fired from the same distance.
  1127.     Laser_offset = ((F1_0*2)*(rand()%10))/10;
  1128.  
  1129.     switch (weapon_num) {
  1130.         case LASER_INDEX: {
  1131.             Laser_player_fire( objp, level, 0, 1, 0);
  1132.             Laser_player_fire( objp, level, 1, 0, 0);
  1133.  
  1134.             if (flags & LASER_QUAD) {
  1135.                 //    hideous system to make quad laser 1.5x powerful as normal laser, make every other quad laser bolt harmless
  1136.                 Laser_player_fire( objp, level, 2, 0, 0);
  1137.                 Laser_player_fire( objp, level, 3, 0, 0);
  1138.             }
  1139.             break;
  1140.         }
  1141.         case VULCAN_INDEX: {
  1142.             //    Only make sound for 1/4 of vulcan bullets.
  1143.             int    make_sound = 1;
  1144.             //if (rand() > 24576)
  1145.             //    make_sound = 1;
  1146.             Laser_player_fire_spread( objp, VULCAN_ID, 6, rand()/8 - 32767/16, rand()/8 - 32767/16, make_sound, 0);
  1147.             if (nfires > 1) {
  1148.                 Laser_player_fire_spread( objp, VULCAN_ID, 6, rand()/8 - 32767/16, rand()/8 - 32767/16, 0, 0);
  1149.                 if (nfires > 2) {
  1150.                     Laser_player_fire_spread( objp, VULCAN_ID, 6, rand()/8 - 32767/16, rand()/8 - 32767/16, 0, 0);
  1151.                 }
  1152.             }
  1153.             break;
  1154.         }
  1155.         case SPREADFIRE_INDEX:
  1156.             if (flags & LASER_SPREADFIRE_TOGGLED) {
  1157.                 Laser_player_fire_spread( objp, SPREADFIRE_ID, 6, F1_0/16, 0, 0, 0);
  1158.                 Laser_player_fire_spread( objp, SPREADFIRE_ID, 6, -F1_0/16, 0, 0, 0);
  1159.                 Laser_player_fire_spread( objp, SPREADFIRE_ID, 6, 0, 0, 1, 0);
  1160.             } else {
  1161.                 Laser_player_fire_spread( objp, SPREADFIRE_ID, 6, 0, F1_0/16, 0, 0);
  1162.                 Laser_player_fire_spread( objp, SPREADFIRE_ID, 6, 0, -F1_0/16, 0, 0);
  1163.                 Laser_player_fire_spread( objp, SPREADFIRE_ID, 6, 0, 0, 1, 0);
  1164.             }
  1165.             break;
  1166.  
  1167. #ifndef SHAREWARE
  1168.         case PLASMA_INDEX:
  1169.             Laser_player_fire( objp, PLASMA_ID, 0, 1, 0);
  1170.             Laser_player_fire( objp, PLASMA_ID, 1, 0, 0);
  1171.             if (nfires > 1) {
  1172.                 Laser_player_fire_spread_delay( objp, PLASMA_ID, 0, 0, 0, FrameTime/2, 1, 0);
  1173.                 Laser_player_fire_spread_delay( objp, PLASMA_ID, 1, 0, 0, FrameTime/2, 0, 0);
  1174.             }
  1175.             break;
  1176.  
  1177.         case FUSION_INDEX: {
  1178.             vms_vector    force_vec;
  1179.  
  1180. //            mprintf((0, "Fusion multiplier %f.\n", f2fl(Fusion_charge)));
  1181.  
  1182.             Laser_player_fire( objp, FUSION_ID, 0, 1, 0);
  1183.             Laser_player_fire( objp, FUSION_ID, 1, 1, 0);
  1184.  
  1185.             flags = (byte)(Fusion_charge >> 12);
  1186.  
  1187.             Fusion_charge = 0;
  1188.  
  1189.             force_vec.x = -(objp->orient.fvec.x << 7);
  1190.             force_vec.y = -(objp->orient.fvec.y << 7);
  1191.             force_vec.z = -(objp->orient.fvec.z << 7);
  1192.             phys_apply_force(objp, &force_vec);
  1193.  
  1194.             force_vec.x = (force_vec.x >> 4) + rand() - 16384;
  1195.             force_vec.y = (force_vec.y >> 4) + rand() - 16384;
  1196.             force_vec.z = (force_vec.z >> 4) + rand() - 16384;
  1197.             phys_apply_rot(objp, &force_vec);
  1198.  
  1199.         }
  1200.             break;
  1201. #endif
  1202.  
  1203.         default:
  1204.             Int3();    //    Contact Mike: Unknown Primary weapon type, setting to 0.
  1205.             Primary_weapon = 0;
  1206.     }
  1207.  
  1208.     // Set values to be recognized during comunication phase, if we are the
  1209.     //  one shooting
  1210.     #ifdef NETWORK
  1211.     if ((Game_mode & GM_MULTI) && (objnum == Players[Player_num].objnum))
  1212.     {
  1213. //        mprintf((0, "Flags on fire: %d.\n", flags));
  1214.         Network_laser_fired = nfires;
  1215.         Network_laser_gun = weapon_num;
  1216.         Network_laser_flags = flags;
  1217.         Network_laser_level = level;
  1218.     }
  1219.     #endif
  1220.  
  1221.     return nfires;
  1222. }
  1223.  
  1224. #define    MAX_SMART_DISTANCE    (F1_0*150)
  1225. #define    MAX_OBJDISTS            30
  1226. #define    NUM_SMART_CHILDREN    6
  1227.  
  1228. typedef    struct {
  1229.     int    objnum;
  1230.     fix    dist;
  1231. } objdist;
  1232.  
  1233. //    -------------------------------------------------------------------------------------------
  1234. //    if goal_obj == -1, then create random vector
  1235. int create_homing_missile(object *objp, int goal_obj, int objtype, int make_sound)
  1236. {
  1237.     int            objnum;
  1238.     vms_vector    vector_to_goal;
  1239.     vms_vector    random_vector;
  1240.     //vms_vector    goal_pos;
  1241.  
  1242.     if (goal_obj == -1) {
  1243.         make_random_vector(&vector_to_goal);
  1244.     } else {
  1245.         vm_vec_sub(&vector_to_goal, &Objects[goal_obj].pos, &objp->pos);
  1246.         vm_vec_normalize_quick(&vector_to_goal);
  1247.         make_random_vector(&random_vector);
  1248.         vm_vec_scale_add2(&vector_to_goal, &random_vector, F1_0/4);
  1249.         vm_vec_normalize_quick(&vector_to_goal);
  1250.     }        
  1251.  
  1252.     //    Create a vector towards the goal, then add some noise to it.
  1253.     objnum = Laser_create_new(&vector_to_goal, &objp->pos, objp->segnum, objp-Objects, objtype, make_sound);
  1254.     if (objnum == -1)
  1255.         return -1;
  1256.  
  1257.     // Fixed to make sure the right person gets credit for the kill
  1258.  
  1259. //    Objects[objnum].ctype.laser_info.parent_num = objp->ctype.laser_info.parent_num;
  1260. //    Objects[objnum].ctype.laser_info.parent_type = objp->ctype.laser_info.parent_type;
  1261. //    Objects[objnum].ctype.laser_info.parent_signature = objp->ctype.laser_info.parent_signature;
  1262.  
  1263.     Objects[objnum].ctype.laser_info.track_goal = goal_obj;
  1264.  
  1265.     return objnum;
  1266. }
  1267.  
  1268. //    -------------------------------------------------------------------------------------------
  1269. //    Create the children of a smart bomb, which is a bunch of homing missiles.
  1270. void create_smart_children(object *objp)
  1271. {
  1272.     int        make_sound;
  1273.     int        numobjs=0;
  1274.     int        parent_type;
  1275.     objdist    objlist[MAX_OBJDISTS];
  1276.  
  1277.     if (Game_mode & GM_MULTI)
  1278.         srand(8321L);
  1279.  
  1280.     parent_type = objp->ctype.laser_info.parent_type;
  1281.  
  1282.     if (objp->id == SMART_ID) {
  1283.         int    i, objnum;
  1284.  
  1285.         for (objnum=0; objnum<=Highest_object_index; objnum++) {
  1286.             object    *curobjp = &Objects[objnum];
  1287.  
  1288.             if ((((curobjp->type == OBJ_ROBOT) && (!curobjp->ctype.ai_info.CLOAKED)) || (curobjp->type == OBJ_PLAYER)) && (objnum != objp->ctype.laser_info.parent_num)) {
  1289.                 fix    dist;
  1290.  
  1291.                 if (curobjp->type == OBJ_PLAYER) 
  1292.                 {
  1293.                     if ((parent_type == OBJ_PLAYER) && (Game_mode & GM_MULTI_COOP))
  1294.                         continue;
  1295.                     #ifdef NETWORK
  1296.                     if ((Game_mode & GM_TEAM) && (get_team(curobjp->id) == get_team(Objects[objp->ctype.laser_info.parent_num].id)))
  1297.                         continue;
  1298.                     #endif
  1299.                 }
  1300.  
  1301.                 //    Robot blobs can't track robots.
  1302.                 if (curobjp->type == OBJ_ROBOT)
  1303.                     if (parent_type == OBJ_ROBOT)
  1304.                         continue;
  1305.  
  1306.                 dist = vm_vec_dist_quick(&objp->pos, &curobjp->pos);
  1307.                 if (dist < MAX_SMART_DISTANCE) {
  1308.                     int    oovis;
  1309.  
  1310.                     oovis = object_to_object_visibility(objp, curobjp, FQ_TRANSWALL);
  1311.  
  1312.                     if (oovis) { //object_to_object_visibility(objp, curobjp, FQ_TRANSWALL)) {
  1313.                         objlist[numobjs].objnum = objnum;
  1314.                         objlist[numobjs].dist = dist;
  1315.                         numobjs++;
  1316.                         if (numobjs >= MAX_OBJDISTS) {
  1317.                             mprintf((0, "Warning -- too many objects near smart bomb explosion.  See laser.c.\n"));
  1318.                             numobjs = MAX_OBJDISTS;
  1319.                             break;
  1320.                         }
  1321.                     }
  1322.                 }
  1323.             }
  1324.         }
  1325.  
  1326.  
  1327.         make_sound = 1;
  1328.         if (numobjs == 0) {
  1329.             for (i=0; i<NUM_SMART_CHILDREN; i++) {
  1330.                 if (parent_type == OBJ_PLAYER) {
  1331.                     int    hobjnum;
  1332.                     hobjnum = create_homing_missile(objp, -1, PLAYER_SMART_HOMING_ID, make_sound);
  1333.                     // mprintf((0, "Object #%i is a PLAYER smart blob.\n", hobjnum));
  1334.                 } else {
  1335.                     int    hobjnum;
  1336.                     hobjnum = create_homing_missile(objp, -1, ROBOT_SMART_HOMING_ID, make_sound);
  1337.                     // mprintf((0, "Object #%i is a robot smart blob.\n", hobjnum));
  1338.                 }
  1339.                 make_sound = 0;
  1340.             }
  1341.         } else {
  1342.             for (i=0; i<NUM_SMART_CHILDREN; i++) {
  1343.                 if (parent_type == OBJ_PLAYER) {
  1344.                     int    hobjnum;
  1345.                     hobjnum = create_homing_missile(objp, objlist[(rand() * numobjs) >> 15].objnum, PLAYER_SMART_HOMING_ID, make_sound);
  1346.                     // mprintf((0, "Object #%i is a PLAYER smart blob.\n", hobjnum));
  1347.                 } else {
  1348.                     int    hobjnum;
  1349.                     hobjnum = create_homing_missile(objp, objlist[(rand() * numobjs) >> 15].objnum, ROBOT_SMART_HOMING_ID, make_sound);
  1350.                     // mprintf((0, "Object #%i is a robot smart blob.\n", hobjnum));
  1351.                 }
  1352.                 make_sound = 0;
  1353.             }
  1354.         }
  1355.     }
  1356. }
  1357.  
  1358. #define    CONCUSSION_GUN        4
  1359. #define    HOMING_GUN            4
  1360.  
  1361. #define    PROXIMITY_GUN        7
  1362. #define    SMART_GUN            7
  1363. #define    MEGA_GUN                7
  1364.  
  1365.  
  1366. int Missile_gun=0;
  1367.  
  1368. //    -------------------------------------------------------------------------------------------
  1369. void do_missile_firing(void)
  1370. {
  1371.     static int proximity = 0;
  1372.  
  1373.     Assert(Secondary_weapon < MAX_SECONDARY_WEAPONS);
  1374.  
  1375.     if (!Player_is_dead && (Players[Player_num].secondary_ammo[Secondary_weapon] > 0))    {
  1376.  
  1377.         int    weapon_index;
  1378.  
  1379.         Players[Player_num].secondary_ammo[Secondary_weapon]--;
  1380.  
  1381.         weapon_index = Secondary_weapon_to_weapon_info[Secondary_weapon];
  1382.         if (Laser_rapid_fire!=0xBADA55)
  1383.             Next_missile_fire_time = GameTime + Weapon_info[weapon_index].fire_wait;
  1384.         else
  1385.             Next_missile_fire_time = GameTime + F1_0/25;
  1386.  
  1387.         switch (Secondary_weapon) {
  1388.             case CONCUSSION_INDEX:
  1389.                 Laser_player_fire( ConsoleObject, CONCUSSION_ID, CONCUSSION_GUN+(Missile_gun & 1), 1, 0 ); 
  1390.                 Missile_gun++;
  1391.                 break;
  1392.  
  1393.             case PROXIMITY_INDEX:
  1394.                 proximity ++;
  1395.                 if (proximity == 4)
  1396.                 {
  1397.                     proximity = 0;
  1398.                     #ifdef NETWORK
  1399.                     maybe_drop_net_powerup(POW_PROXIMITY_WEAPON);
  1400.                     #endif
  1401.                 }
  1402.                 Laser_player_fire( ConsoleObject, PROXIMITY_ID, PROXIMITY_GUN, 1, 0);
  1403.                 break;
  1404.  
  1405.             case HOMING_INDEX:
  1406.                 Laser_player_fire( ConsoleObject, HOMING_ID, HOMING_GUN+(Missile_gun & 1), 1, 0 );
  1407.                 Missile_gun++;
  1408.                 #ifdef NETWORK
  1409.                 maybe_drop_net_powerup(POW_HOMING_AMMO_1);
  1410.                 #endif
  1411.                 break;
  1412.  
  1413. #ifndef SHAREWARE
  1414.             case SMART_INDEX:
  1415.                 Laser_player_fire( ConsoleObject, SMART_ID, SMART_GUN, 1, 0);
  1416.                 #ifdef NETWORK
  1417.                 maybe_drop_net_powerup(POW_SMARTBOMB_WEAPON);
  1418.                 #endif
  1419.                 break;
  1420.  
  1421.             case MEGA_INDEX:
  1422.                 Laser_player_fire( ConsoleObject, MEGA_ID, MEGA_GUN, 1, 0);
  1423.                 #ifdef NETWORK
  1424.                 maybe_drop_net_powerup(POW_MEGA_WEAPON);
  1425.                 #endif
  1426.  
  1427.                 { vms_vector force_vec;
  1428.                 force_vec.x = -(ConsoleObject->orient.fvec.x << 7);
  1429.                 force_vec.y = -(ConsoleObject->orient.fvec.y << 7);
  1430.                 force_vec.z = -(ConsoleObject->orient.fvec.z << 7);
  1431.                 phys_apply_force(ConsoleObject, &force_vec);
  1432.     
  1433.                 force_vec.x = (force_vec.x >> 4) + rand() - 16384;
  1434.                 force_vec.y = (force_vec.y >> 4) + rand() - 16384;
  1435.                 force_vec.z = (force_vec.z >> 4) + rand() - 16384;
  1436.                 phys_apply_rot(ConsoleObject, &force_vec);
  1437.                 }
  1438.                 break;
  1439. #endif
  1440.         }
  1441.  
  1442.         #ifdef NETWORK
  1443.         if (Game_mode & GM_MULTI) 
  1444.         {
  1445.             Network_laser_gun = Secondary_weapon+MISSILE_ADJUST;
  1446.             Network_laser_level = 0;
  1447.             Network_laser_flags = (Missile_gun-1);
  1448.             Network_laser_fired = 1;
  1449.         }
  1450.         #endif
  1451.  
  1452.         auto_select_weapon(1);        //select next missile, if this one out of ammo
  1453.     }
  1454. }
  1455.  
  1456. #ifdef NETWORK
  1457. void net_missile_firing(int player, int gun, int flags)
  1458. {
  1459.  
  1460.     switch (gun-MISSILE_ADJUST) {
  1461.         case CONCUSSION_INDEX:
  1462.             Laser_player_fire( Objects+Players[player].objnum, CONCUSSION_ID, CONCUSSION_GUN+(flags & 1), 1, 0 );
  1463.             break;
  1464.  
  1465.         case PROXIMITY_INDEX:
  1466.             Laser_player_fire( Objects+Players[player].objnum, PROXIMITY_ID, PROXIMITY_GUN, 1, 0);
  1467.             break;
  1468.  
  1469.         case HOMING_INDEX:
  1470.             Laser_player_fire( Objects+Players[player].objnum, HOMING_ID, HOMING_GUN+(flags & 1), 1, 0);
  1471.             break;
  1472.  
  1473.         case SMART_INDEX:
  1474.             Laser_player_fire( Objects+Players[player].objnum, SMART_ID, SMART_GUN, 1, 0);
  1475.             break;
  1476.  
  1477.         case MEGA_INDEX:
  1478.             Laser_player_fire( Objects+Players[player].objnum, MEGA_ID, MEGA_GUN, 1, 0);
  1479.             break;
  1480.  
  1481.         case FLARE_ID:
  1482.             Laser_player_fire( Objects+Players[player].objnum, FLARE_ID, 6, 1, 0);
  1483.             break;
  1484.  
  1485.         default:
  1486.             mprintf((0,"net_missing_firing(): Unknown missile weapon type.\n"));
  1487.     }
  1488.     
  1489. }
  1490. #endif
  1491.  
  1492.  
  1493.  
  1494.