2006-03-20 17:12:09 +00:00
/*
2014-06-01 17:55:23 +00:00
* Portions of this file are copyright Rebirth contributors and licensed as
* described in COPYING . txt .
* Portions of this file are copyright Parallax Software and licensed
* according to the Parallax license below .
* See COPYING . txt for license details .
2006-03-20 17:12:09 +00:00
THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
SOFTWARE CORPORATION ( " PARALLAX " ) . PARALLAX , IN DISTRIBUTING THE CODE TO
END - USERS , AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN , GRANTS A
ROYALTY - FREE , PERPETUAL LICENSE TO SUCH END - USERS FOR USE BY SUCH END - USERS
IN USING , DISPLAYING , AND CREATING DERIVATIVE WORKS THEREOF , SO LONG AS
SUCH USE , DISPLAY OR CREATION IS FOR NON - COMMERCIAL , ROYALTY OR REVENUE
FREE PURPOSES . IN NO EVENT SHALL THE END - USER USE THE COMPUTER CODE
CONTAINED HEREIN FOR REVENUE - BEARING PURPOSES . THE END - USER UNDERSTANDS
AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE .
COPYRIGHT 1993 - 1999 PARALLAX SOFTWARE CORPORATION . ALL RIGHTS RESERVED .
*/
/*
*
* This will contain the laser code
*
*/
# include <stdlib.h>
# include <stdio.h>
# include <time.h>
# include "inferno.h"
# include "game.h"
# include "bm.h"
# include "object.h"
# include "laser.h"
# include "args.h"
# include "segment.h"
# include "fvi.h"
# include "segpoint.h"
2012-07-07 18:35:06 +00:00
# include "dxxerror.h"
2006-03-20 17:12:09 +00:00
# include "key.h"
# include "texmap.h"
2013-12-26 04:18:28 +00:00
# include "gameseg.h"
2006-03-20 17:12:09 +00:00
# include "textures.h"
# include "render.h"
# include "vclip.h"
# include "fireball.h"
# include "polyobj.h"
# include "robot.h"
# include "weapon.h"
# include "newdemo.h"
# include "timer.h"
# include "player.h"
# include "sounds.h"
# include "ai.h"
# include "powerup.h"
# include "multi.h"
# include "physics.h"
# include "multi.h"
2012-11-11 00:14:30 +00:00
# include "wall.h"
2013-12-25 23:47:30 +00:00
# include "reverse.h"
# include "compiler-range_for.h"
2014-10-12 23:05:46 +00:00
# include "highest_valid.h"
2006-03-20 17:12:09 +00:00
2010-06-15 16:24:56 +00:00
# define NEWHOMER
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
object * Guided_missile [ MAX_PLAYERS ] = { NULL , NULL , NULL , NULL , NULL , NULL , NULL , NULL } ;
int Guided_missile_sig [ MAX_PLAYERS ] = { - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 } ;
2013-03-03 01:03:33 +00:00
# endif
2013-12-29 04:28:07 +00:00
objnum_t Network_laser_track = object_none ;
2006-03-20 17:12:09 +00:00
2014-10-02 03:02:34 +00:00
static objptridx_t find_homing_object_complete ( const vms_vector & curpos , const vobjptridx_t tracker , int track_obj_type1 , int track_obj_type2 ) ;
static objptridx_t find_homing_object ( const vms_vector & curpos , const vobjptridx_t tracker ) ;
2006-03-20 17:12:09 +00:00
//---------------------------------------------------------------------------------
// Called by render code.... determines if the laser is from a robot or the
// player and calls the appropriate routine.
2014-08-16 23:18:17 +00:00
void Laser_render ( object & obj )
2006-03-20 17:12:09 +00:00
{
// Commented out by John (sort of, typed by Mike) on 6/8/94
#if 0
switch ( obj - > id ) {
case WEAPON_TYPE_WEAK_LASER :
case WEAPON_TYPE_STRONG_LASER :
case WEAPON_TYPE_CANNON_BALL :
case WEAPON_TYPE_MISSILE :
break ;
default :
Error ( " Invalid weapon type in Laser_render \n " ) ;
}
# endif
2010-06-15 16:24:56 +00:00
2014-08-16 23:18:17 +00:00
switch ( Weapon_info [ get_weapon_id ( & obj ) ] . render_type ) {
2006-03-20 17:12:09 +00:00
case WEAPON_RENDER_LASER :
Int3 ( ) ; // Not supported anymore!
//Laser_draw_one(obj-Objects, Weapon_info[obj->id].bitmap );
break ;
case WEAPON_RENDER_BLOB :
2014-08-16 23:18:17 +00:00
draw_object_blob ( obj , Weapon_info [ get_weapon_id ( & obj ) ] . bitmap ) ;
2006-03-20 17:12:09 +00:00
break ;
case WEAPON_RENDER_POLYMODEL :
break ;
case WEAPON_RENDER_VCLIP :
Int3 ( ) ; // Oops, not supported, type added by mk on 09/09/94, but not for lasers...
default :
Error ( " Invalid weapon render type in Laser_render \n " ) ;
}
}
//---------------------------------------------------------------------------------
// Draws a texture-mapped laser bolt
//void Laser_draw_one( int objnum, grs_bitmap * bmp )
//{
// int t1, t2, t3;
// g3s_point p1, p2;
// object *obj;
// vms_vector start_pos,end_pos;
//
// obj = &Objects[objnum];
//
// start_pos = obj->pos;
// vm_vec_scale_add(&end_pos,&start_pos,&obj->orient.fvec,-Laser_length);
//
// g3_rotate_point(&p1,&start_pos);
// g3_rotate_point(&p2,&end_pos);
//
// t1 = Lighting_on;
// t2 = Interpolation_method;
// t3 = Transparency_on;
//
// Lighting_on = 0;
// //Interpolation_method = 3; // Full perspective
// Interpolation_method = 1; // Linear
// Transparency_on = 1;
//
// //gr_setcolor( gr_getcolor(31,15,0));
// //g3_draw_line_ptrs(p1,p2);
// //g3_draw_rod(p1,0x2000,p2,0x2000);
// //g3_draw_rod(p1,Laser_width,p2,Laser_width);
// g3_draw_rod_tmap(bmp,&p2,Laser_width,&p1,Laser_width,0);
// Lighting_on = t1;
// Interpolation_method = t2;
// Transparency_on = t3;
//
//}
// Changed by MK on 09/07/94
// I want you to be able to blow up your own bombs.
// AND...Your proximity bombs can blow you up if they're 2.0 seconds or more old.
// Changed by MK on 06/06/95: Now must be 4.0 seconds old. Much valid Net-complaining.
int laser_are_related ( int o1 , int o2 )
{
2010-06-15 16:24:56 +00:00
if ( ( o1 < 0 ) | | ( o2 < 0 ) )
2006-03-20 17:12:09 +00:00
return 0 ;
// See if o2 is the parent of o1
if ( Objects [ o1 ] . type = = OBJ_WEAPON )
if ( ( Objects [ o1 ] . ctype . laser_info . parent_num = = o2 ) & & ( Objects [ o1 ] . ctype . laser_info . parent_signature = = Objects [ o2 ] . signature ) )
{
// 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
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
2013-10-07 23:52:33 +00:00
if ( ! ( ( get_weapon_id ( & Objects [ o1 ] ) ! = PROXIMITY_ID ) | | ( Objects [ o1 ] . ctype . laser_info . creation_time + F1_0 * 2 > = GameTime64 ) ) )
2013-03-03 01:03:33 +00:00
# elif defined(DXX_BUILD_DESCENT_II)
2013-10-07 23:52:33 +00:00
if ( ( get_weapon_id ( & Objects [ o1 ] ) = = PHOENIX_ID & & ( GameTime64 > Objects [ o1 ] . ctype . laser_info . creation_time + F1_0 / 4 ) ) | |
( get_weapon_id ( & Objects [ o1 ] ) = = GUIDEDMISS_ID & & ( GameTime64 > Objects [ o1 ] . ctype . laser_info . creation_time + F1_0 * 2 ) ) | |
( ( ( get_weapon_id ( & Objects [ o1 ] ) = = PROXIMITY_ID ) | | ( get_weapon_id ( & Objects [ o1 ] ) = = SUPERPROX_ID ) ) & & ( GameTime64 > Objects [ o1 ] . ctype . laser_info . creation_time + F1_0 * 4 ) ) )
2013-03-03 01:03:33 +00:00
# endif
2013-08-27 23:53:03 +00:00
{
2006-03-20 17:12:09 +00:00
return 0 ;
} else
return 1 ;
}
// See if o1 is the parent of o2
if ( Objects [ o2 ] . type = = OBJ_WEAPON )
{
if ( ( Objects [ o2 ] . ctype . laser_info . parent_num = = o1 ) & & ( Objects [ o2 ] . ctype . laser_info . parent_signature = = Objects [ o1 ] . signature ) )
{
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
// 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
2010-12-22 00:17:59 +00:00
if ( ( Objects [ o2 ] . id = = PHOENIX_ID & & ( GameTime64 > Objects [ o2 ] . ctype . laser_info . creation_time + F1_0 / 4 ) ) | |
( Objects [ o2 ] . id = = GUIDEDMISS_ID & & ( GameTime64 > Objects [ o2 ] . ctype . laser_info . creation_time + F1_0 * 2 ) ) | |
( ( ( Objects [ o2 ] . id = = PROXIMITY_ID ) | | ( Objects [ o2 ] . id = = SUPERPROX_ID ) ) & & ( GameTime64 > Objects [ o2 ] . ctype . laser_info . creation_time + F1_0 * 4 ) ) ) {
2006-03-20 17:12:09 +00:00
return 0 ;
} else
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
return 1 ;
}
}
// They must both be weapons
2010-06-15 16:24:56 +00:00
if ( Objects [ o1 ] . type ! = OBJ_WEAPON | | Objects [ o2 ] . type ! = OBJ_WEAPON )
2006-03-20 17:12:09 +00:00
return 0 ;
// Here is the 09/07/94 change -- Siblings must be identical, others can hurt each other
// See if they're siblings...
// 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.
if ( Objects [ o1 ] . ctype . laser_info . parent_signature = = Objects [ o2 ] . ctype . laser_info . parent_signature )
{
2013-10-07 23:52:33 +00:00
if ( is_proximity_bomb_or_smart_mine ( get_weapon_id ( & Objects [ o1 ] ) ) | | is_proximity_bomb_or_smart_mine ( get_weapon_id ( & Objects [ o2 ] ) ) ) {
2006-03-20 17:12:09 +00:00
// If neither is older than 1/2 second, then can't blow up!
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
if ( ! ( ( GameTime64 > ( Objects [ o1 ] . ctype . laser_info . creation_time + F1_0 / 2 ) ) | | ( GameTime64 > ( Objects [ o2 ] . ctype . laser_info . creation_time + F1_0 / 2 ) ) ) )
2006-03-20 17:12:09 +00:00
return 1 ;
2013-03-03 01:03:33 +00:00
else
# endif
return 0 ;
2006-03-20 17:12:09 +00:00
} else
return 1 ;
}
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
// Anything can cause a collision with a robot super prox mine.
2013-08-27 23:53:03 +00:00
if ( ! ( Objects [ o1 ] . id = = ROBOT_SUPERPROX_ID | | Objects [ o2 ] . id = = ROBOT_SUPERPROX_ID | |
2006-03-20 17:12:09 +00:00
Objects [ o1 ] . id = = PROXIMITY_ID | | Objects [ o2 ] . id = = PROXIMITY_ID | |
Objects [ o1 ] . id = = SUPERPROX_ID | | Objects [ o2 ] . id = = SUPERPROX_ID | |
2013-08-27 23:53:03 +00:00
Objects [ o1 ] . id = = PMINE_ID | | Objects [ o2 ] . id = = PMINE_ID ) )
return 1 ;
2013-03-03 01:03:33 +00:00
# endif
2013-08-27 23:53:03 +00:00
return 0 ;
2006-03-20 17:12:09 +00:00
}
2014-10-26 21:37:27 +00:00
static void do_muzzle_stuff ( segnum_t segnum , const vms_vector & pos )
2006-03-20 17:12:09 +00:00
{
2010-12-10 23:18:17 +00:00
Muzzle_data [ Muzzle_queue_index ] . create_time = timer_query ( ) ;
2006-03-20 17:12:09 +00:00
Muzzle_data [ Muzzle_queue_index ] . segnum = segnum ;
2014-10-26 21:37:27 +00:00
Muzzle_data [ Muzzle_queue_index ] . pos = pos ;
2006-03-20 17:12:09 +00:00
Muzzle_queue_index + + ;
if ( Muzzle_queue_index > = MUZZLE_QUEUE_MAX )
Muzzle_queue_index = 0 ;
}
//creates a weapon object
2014-10-26 21:37:27 +00:00
static objptridx_t create_weapon_object ( int weapon_type , segnum_t segnum , const vms_vector & position )
2006-03-20 17:12:09 +00:00
{
int rtype = - 1 ;
fix laser_radius = - 1 ;
switch ( Weapon_info [ weapon_type ] . render_type ) {
case WEAPON_RENDER_BLOB :
rtype = RT_LASER ; // Render as a laser even if blob (see render code above for explanation)
laser_radius = Weapon_info [ weapon_type ] . blob_size ;
break ;
case WEAPON_RENDER_POLYMODEL :
laser_radius = 0 ; // Filled in below.
rtype = RT_POLYOBJ ;
break ;
case WEAPON_RENDER_LASER :
Int3 ( ) ; // Not supported anymore
break ;
case WEAPON_RENDER_NONE :
rtype = RT_NONE ;
laser_radius = F1_0 ;
break ;
case WEAPON_RENDER_VCLIP :
rtype = RT_WEAPON_VCLIP ;
laser_radius = Weapon_info [ weapon_type ] . blob_size ;
break ;
default :
Error ( " Invalid weapon render type in Laser_create_new \n " ) ;
}
Assert ( laser_radius ! = - 1 ) ;
Assert ( rtype ! = - 1 ) ;
2014-10-26 21:37:27 +00:00
auto obj = obj_create ( OBJ_WEAPON , weapon_type , segnum , position , NULL , laser_radius , CT_WEAPON , MT_PHYSICS , rtype ) ;
2014-01-11 17:55:01 +00:00
if ( obj = = object_none )
return obj ;
2006-03-20 17:12:09 +00:00
if ( Weapon_info [ weapon_type ] . render_type = = WEAPON_RENDER_POLYMODEL ) {
2013-10-07 23:52:33 +00:00
obj - > rtype . pobj_info . model_num = Weapon_info [ get_weapon_id ( obj ) ] . model_num ;
obj - > size = fixdiv ( Polygon_models [ obj - > rtype . pobj_info . model_num ] . rad , Weapon_info [ get_weapon_id ( obj ) ] . po_len_to_width_ratio ) ;
2006-03-20 17:12:09 +00:00
}
obj - > mtype . phys_info . mass = Weapon_info [ weapon_type ] . mass ;
obj - > mtype . phys_info . drag = Weapon_info [ weapon_type ] . drag ;
2014-09-28 21:11:04 +00:00
vm_vec_zero ( obj - > mtype . phys_info . thrust ) ;
2006-03-20 17:12:09 +00:00
if ( Weapon_info [ weapon_type ] . bounce = = 1 )
obj - > mtype . phys_info . flags | = PF_BOUNCE ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2011-02-14 21:27:07 +00:00
if ( Weapon_info [ weapon_type ] . bounce = = 2 | | cheats . bouncyfire )
2006-03-20 17:12:09 +00:00
obj - > mtype . phys_info . flags | = PF_BOUNCE + PF_BOUNCES_TWICE ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
2014-01-11 17:55:01 +00:00
return obj ;
2006-03-20 17:12:09 +00:00
}
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
// -------------------------------------------------------------------------------------------------------------------------------
// ***** HEY ARTISTS!! *****
// Here are the constants you're looking for! --MK
// Change the following constants to affect the look of the omega cannon.
// Changing these constants will not affect the damage done.
// WARNING: If you change DESIRED_OMEGA_DIST and MAX_OMEGA_BLOBS, you don't merely change the look of the cannon,
// you change its range. If you decrease DESIRED_OMEGA_DIST, you decrease how far the gun can fire.
2014-11-11 04:25:47 +00:00
const fix OMEGA_BASE_TIME = F1_0 / 20 ; // How many blobs per second!! No FPS-based blob creation anymore, no FPS-based damage anymore!
const unsigned MIN_OMEGA_BLOBS = 3 ; // No matter how close the obstruction, at this many blobs created.
const fix MIN_OMEGA_DIST = F1_0 * 3 ; // At least this distance between blobs, unless doing so would violate MIN_OMEGA_BLOBS
const fix 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.
const unsigned MAX_OMEGA_BLOBS = 16 ; // No matter how far away the obstruction, this is the maximum number of blobs.
const fix MAX_OMEGA_DIST = MAX_OMEGA_BLOBS * DESIRED_OMEGA_DIST ; // Maximum extent of lightning blobs.
const fix64 MAX_OMEGA_DIST_SQUARED = static_cast < fix64 > ( MAX_OMEGA_DIST ) * static_cast < fix64 > ( MAX_OMEGA_DIST ) ;
2006-03-20 17:12:09 +00:00
// Additionally, several constants which apply to homing objects in general control the behavior of the Omega Cannon.
// They are defined in laser.h. They are copied here for reference. These values are valid on 1/10/96:
// If you want the Omega Cannon view cone to be different than the Homing Missile viewcone, contact MK to make the change.
// (Unless you are a programmer, in which case, do it yourself!)
# 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.
2014-10-10 02:41:51 +00:00
static const fix64 OMEGA_MAX_TRACKABLE_DIST = MAX_OMEGA_DIST ; // An object must be at least this close to be tracked.
2006-03-20 17:12:09 +00:00
// Note, you don't need to change these constants. You can control damage and energy consumption by changing the
// usual bitmaps.tbl parameters.
2011-04-09 23:48:15 +00:00
# define OMEGA_DAMAGE_SCALE 32 // Controls how much damage is done. This gets multiplied by the damage specified in bitmaps.tbl in the $WEAPON line.
# define OMEGA_ENERGY_CONSUMPTION 16 // Controls how much energy is consumed. This gets multiplied by the energy parameter from bitmaps.tbl.
2006-03-20 17:12:09 +00:00
// -------------------------------------------------------------------------------------------------------------------------------
2011-04-09 23:48:15 +00:00
// Delete omega blobs further away than MAX_OMEGA_DIST
// Since last omega blob has VERY high velocity it's impossible to ensure a constant travel distance on varying FPS. So delete if they exceed their maximum distance.
2014-10-02 03:02:34 +00:00
static int omega_cleanup ( const vobjptridx_t weapon )
2006-03-20 17:12:09 +00:00
{
2011-04-09 23:48:15 +00:00
int parent_sig = weapon - > ctype . laser_info . parent_signature , parent_num = weapon - > ctype . laser_info . parent_num ;
2006-03-20 17:12:09 +00:00
2011-04-09 23:48:15 +00:00
if ( weapon - > type ! = OBJ_WEAPON | | weapon - > id ! = OMEGA_ID )
2011-04-16 14:44:36 +00:00
return 0 ;
2006-03-20 17:12:09 +00:00
2011-04-09 23:48:15 +00:00
if ( Objects [ parent_num ] . signature = = parent_sig )
2014-11-11 04:25:47 +00:00
if ( vm_vec_dist2 ( weapon - > pos , Objects [ parent_num ] . pos ) > MAX_OMEGA_DIST_SQUARED )
2011-04-16 14:44:36 +00:00
{
2013-12-24 04:53:59 +00:00
obj_delete ( weapon ) ;
2011-04-16 14:44:36 +00:00
return 1 ;
}
return 0 ;
2011-04-09 23:48:15 +00:00
}
// Return true if ok to do Omega damage. For Multiplayer games. See comment for omega_cleanup()
2014-10-02 03:02:34 +00:00
int ok_to_do_omega_damage ( const vcobjptr_t weapon )
2011-04-09 23:48:15 +00:00
{
int parent_sig = weapon - > ctype . laser_info . parent_signature , parent_num = weapon - > ctype . laser_info . parent_num ;
if ( weapon - > type ! = OBJ_WEAPON | | weapon - > id ! = OMEGA_ID )
return 1 ;
if ( ! ( Game_mode & GM_MULTI ) )
return 1 ;
if ( Objects [ parent_num ] . signature = = parent_sig )
2014-11-11 04:25:47 +00:00
if ( vm_vec_dist2 ( Objects [ parent_num ] . pos , weapon - > pos ) > MAX_OMEGA_DIST_SQUARED )
2011-04-09 23:48:15 +00:00
return 0 ;
return 1 ;
2006-03-20 17:12:09 +00:00
}
// ---------------------------------------------------------------------------------
2014-10-02 03:02:34 +00:00
static void create_omega_blobs ( int firing_segnum , const vms_vector & firing_pos , const vms_vector & goal_pos , const vobjptridx_t parent_objp )
2006-03-20 17:12:09 +00:00
{
2014-09-26 02:42:09 +00:00
int last_segnum = 0 , num_omega_blobs = 0 ;
2014-09-08 03:24:48 +00:00
objptridx_t last_created_objnum = object_none ;
2014-11-04 01:33:45 +00:00
vms_vector omega_delta_vector = ZERO_VECTOR , blob_pos = ZERO_VECTOR ;
2014-07-04 03:53:52 +00:00
fix dist_to_goal = 0 , omega_blob_dist = 0 , perturb_array [ MAX_OMEGA_BLOBS ] { } ;
2006-03-20 17:12:09 +00:00
2014-10-29 03:24:31 +00:00
auto vec_to_goal = vm_vec_sub ( goal_pos , firing_pos ) ;
2014-09-28 21:11:04 +00:00
dist_to_goal = vm_vec_normalize_quick ( vec_to_goal ) ;
2006-03-20 17:12:09 +00:00
if ( dist_to_goal < MIN_OMEGA_BLOBS * MIN_OMEGA_DIST ) {
omega_blob_dist = MIN_OMEGA_DIST ;
num_omega_blobs = dist_to_goal / omega_blob_dist ;
if ( num_omega_blobs = = 0 )
num_omega_blobs = 1 ;
} else {
omega_blob_dist = DESIRED_OMEGA_DIST ;
num_omega_blobs = dist_to_goal / omega_blob_dist ;
if ( num_omega_blobs > MAX_OMEGA_BLOBS ) {
num_omega_blobs = MAX_OMEGA_BLOBS ;
omega_blob_dist = dist_to_goal / num_omega_blobs ;
} else if ( num_omega_blobs < MIN_OMEGA_BLOBS ) {
num_omega_blobs = MIN_OMEGA_BLOBS ;
omega_blob_dist = dist_to_goal / num_omega_blobs ;
}
}
omega_delta_vector = vec_to_goal ;
2014-09-28 21:11:05 +00:00
vm_vec_scale ( omega_delta_vector , omega_blob_dist ) ;
2006-03-20 17:12:09 +00:00
// Now, create all the blobs
2014-10-26 21:37:27 +00:00
blob_pos = firing_pos ;
2006-03-20 17:12:09 +00:00
last_segnum = firing_segnum ;
// If nearby, don't perturb vector. If not nearby, start halfway out.
if ( dist_to_goal < MIN_OMEGA_DIST * 4 ) {
2014-09-26 02:42:09 +00:00
for ( int i = 0 ; i < num_omega_blobs ; i + + )
2006-03-20 17:12:09 +00:00
perturb_array [ i ] = 0 ;
} else {
2014-09-28 21:43:14 +00:00
vm_vec_scale_add2 ( blob_pos , omega_delta_vector , F1_0 / 2 ) ; // Put first blob half way out.
2014-09-26 02:42:09 +00:00
for ( int i = 0 ; i < num_omega_blobs / 2 ; i + + ) {
2006-03-20 17:12:09 +00:00
perturb_array [ i ] = F1_0 * i + F1_0 / 4 ;
perturb_array [ num_omega_blobs - 1 - i ] = F1_0 * i ;
}
}
// Create random perturbation vector, but favor _not_ going up in player's reference.
2014-11-04 01:33:45 +00:00
auto perturb_vec = make_random_vector ( ) ;
2014-09-28 21:43:14 +00:00
vm_vec_scale_add2 ( perturb_vec , parent_objp - > orient . uvec , - F1_0 / 2 ) ;
2006-03-20 17:12:09 +00:00
Doing_lighting_hack_flag = 1 ; // Ugly, but prevents blobs which are probably outside the mine from killing framerate.
2014-09-26 02:42:09 +00:00
for ( int i = 0 ; i < num_omega_blobs ; i + + ) {
2006-03-20 17:12:09 +00:00
// This will put the last blob right at the destination object, causing damage.
if ( i = = num_omega_blobs - 1 )
2014-09-28 21:43:14 +00:00
vm_vec_scale_add2 ( blob_pos , omega_delta_vector , 15 * F1_0 / 32 ) ; // Move last blob another (almost) half section
2006-03-20 17:12:09 +00:00
// Every so often, re-perturb blobs
if ( ( i % 4 ) = = 3 ) {
2014-11-04 01:33:45 +00:00
const auto temp_vec = make_random_vector ( ) ;
2014-09-28 21:43:14 +00:00
vm_vec_scale_add2 ( perturb_vec , temp_vec , F1_0 / 4 ) ;
2006-03-20 17:12:09 +00:00
}
2014-11-02 03:43:57 +00:00
const auto temp_pos = vm_vec_scale_add ( blob_pos , perturb_vec , perturb_array [ i ] ) ;
2006-03-20 17:12:09 +00:00
2014-10-22 02:46:03 +00:00
auto segnum = find_point_seg ( temp_pos , last_segnum ) ;
2013-12-26 22:21:16 +00:00
if ( segnum ! = segment_none ) {
2006-03-20 17:12:09 +00:00
last_segnum = segnum ;
2014-10-26 21:35:16 +00:00
auto blob_objnum = obj_create ( OBJ_WEAPON , OMEGA_ID , segnum , temp_pos , NULL , 0 , CT_WEAPON , MT_PHYSICS , RT_WEAPON_VCLIP ) ;
2013-12-26 22:21:16 +00:00
if ( blob_objnum = = object_none )
2006-03-20 17:12:09 +00:00
break ;
last_created_objnum = blob_objnum ;
2014-10-02 03:02:34 +00:00
auto & objp = blob_objnum ;
2006-03-20 17:12:09 +00:00
2011-04-09 23:48:15 +00:00
objp - > lifeleft = OMEGA_BASE_TIME + ( d_rand ( ) / 8 ) ; // add little randomness so the lighting effect becomes a little more interesting
2006-03-20 17:12:09 +00:00
objp - > mtype . phys_info . velocity = vec_to_goal ;
// Only make the last one move fast, else multiple blobs might collide with target.
2014-09-28 21:11:05 +00:00
vm_vec_scale ( objp - > mtype . phys_info . velocity , F1_0 * 4 ) ;
2006-03-20 17:12:09 +00:00
objp - > size = Weapon_info [ objp - > id ] . blob_size ;
2011-04-09 23:48:15 +00:00
objp - > shields = fixmul ( OMEGA_DAMAGE_SCALE * OMEGA_BASE_TIME , Weapon_info [ objp - > id ] . strength [ Difficulty_level ] ) ;
2010-06-15 16:24:56 +00:00
2006-03-20 17:12:09 +00:00
objp - > ctype . laser_info . parent_type = parent_objp - > type ;
objp - > ctype . laser_info . parent_signature = parent_objp - > signature ;
2013-12-24 04:53:59 +00:00
objp - > ctype . laser_info . parent_num = parent_objp ;
2006-03-20 17:12:09 +00:00
objp - > movement_type = MT_NONE ; // Only last one moves, that will get bashed below.
}
2014-09-28 21:43:00 +00:00
vm_vec_add2 ( blob_pos , omega_delta_vector ) ;
2006-03-20 17:12:09 +00:00
}
// Make last one move faster, but it's already moving at speed = F1_0*4.
2013-12-26 22:21:16 +00:00
if ( last_created_objnum ! = object_none ) {
2014-09-28 21:11:05 +00:00
vm_vec_scale ( last_created_objnum - > mtype . phys_info . velocity , Weapon_info [ OMEGA_ID ] . speed [ Difficulty_level ] / 4 ) ;
2014-09-08 03:24:48 +00:00
last_created_objnum - > movement_type = MT_PHYSICS ;
2006-03-20 17:12:09 +00:00
}
Doing_lighting_hack_flag = 0 ;
}
# define MIN_OMEGA_CHARGE (MAX_OMEGA_CHARGE / 8)
# define OMEGA_CHARGE_SCALE 4 // FrameTime / OMEGA_CHARGE_SCALE added to Omega_charge every frame.
fix Omega_charge = MAX_OMEGA_CHARGE ;
# define OMEGA_CHARGE_SCALE 4
2011-04-09 23:48:15 +00:00
int Last_omega_fire_time = 0 ;
2006-03-20 17:12:09 +00:00
// ---------------------------------------------------------------------------------
// Call this every frame to recharge the Omega Cannon.
void omega_charge_frame ( void )
{
fix delta_charge , old_omega_charge ;
if ( Omega_charge = = MAX_OMEGA_CHARGE )
return ;
if ( ! ( player_has_weapon ( OMEGA_INDEX , 0 ) & HAS_WEAPON_FLAG ) )
return ;
if ( Player_is_dead )
return ;
2011-04-09 23:48:15 +00:00
// Don't charge while firing. Wait 1/3 second after firing before recharging
if ( Last_omega_fire_time > GameTime64 )
Last_omega_fire_time = GameTime64 ;
if ( Last_omega_fire_time + F1_0 / 3 > GameTime64 )
2006-03-20 17:12:09 +00:00
return ;
if ( Players [ Player_num ] . energy ) {
fix energy_used ;
old_omega_charge = Omega_charge ;
Omega_charge + = FrameTime / OMEGA_CHARGE_SCALE ;
if ( Omega_charge > MAX_OMEGA_CHARGE )
Omega_charge = MAX_OMEGA_CHARGE ;
delta_charge = Omega_charge - old_omega_charge ;
energy_used = fixmul ( F1_0 * 190 / 17 , delta_charge ) ;
if ( Difficulty_level < 2 )
energy_used = fixmul ( energy_used , i2f ( Difficulty_level + 2 ) / 4 ) ;
Players [ Player_num ] . energy - = energy_used ;
if ( Players [ Player_num ] . energy < 0 )
Players [ Player_num ] . energy = 0 ;
}
}
// -- fix Last_omega_muzzle_flash_time;
// ---------------------------------------------------------------------------------
// *objp is the object firing the omega cannon
// *pos is the location from which the omega bolt starts
2014-10-02 03:02:34 +00:00
static void do_omega_stuff ( const vobjptridx_t parent_objp , const vms_vector & firing_pos , const vobjptridx_t weapon_objp )
2006-03-20 17:12:09 +00:00
{
2013-12-29 04:28:07 +00:00
objnum_t lock_objnum ;
2006-03-20 17:12:09 +00:00
vms_vector goal_pos ;
int pnum = parent_objp - > id ;
2013-06-11 10:19:03 +00:00
fix fire_frame_overhead = 0 ;
2006-03-20 17:12:09 +00:00
if ( pnum = = Player_num ) {
// If charge >= min, or (some charge and zero energy), allow to fire.
if ( ! ( ( Omega_charge > = MIN_OMEGA_CHARGE ) | | ( Omega_charge & & ! Players [ pnum ] . energy ) ) ) {
2013-12-24 04:53:59 +00:00
obj_delete ( weapon_objp ) ;
2006-03-20 17:12:09 +00:00
return ;
}
2011-04-12 00:31:51 +00:00
Omega_charge - = OMEGA_BASE_TIME ;
2006-03-20 17:12:09 +00:00
if ( Omega_charge < 0 )
Omega_charge = 0 ;
2013-06-11 10:19:03 +00:00
if ( GameTime64 - Last_omega_fire_time + OMEGA_BASE_TIME < = FrameTime ) // if firing is prolonged by FrameTime overhead, let's try to fix that. Since Next_laser_firing_time is probably changed already (in do_laser_firing_player), we need to calculate the overhead slightly different.
fire_frame_overhead = GameTime64 - Last_omega_fire_time + OMEGA_BASE_TIME ;
Next_laser_fire_time = GameTime64 + OMEGA_BASE_TIME - fire_frame_overhead ;
2011-04-09 23:48:15 +00:00
Last_omega_fire_time = GameTime64 ;
2006-03-20 17:12:09 +00:00
}
weapon_objp - > ctype . laser_info . parent_type = OBJ_PLAYER ;
weapon_objp - > ctype . laser_info . parent_num = Players [ pnum ] . objnum ;
weapon_objp - > ctype . laser_info . parent_signature = Objects [ Players [ pnum ] . objnum ] . signature ;
lock_objnum = find_homing_object ( firing_pos , weapon_objp ) ;
2014-10-26 21:37:27 +00:00
auto firing_segnum = find_point_seg ( firing_pos , parent_objp - > segnum ) ;
2006-03-20 17:12:09 +00:00
// Play sound.
2011-04-09 23:48:15 +00:00
if ( parent_objp = = Viewer )
digi_play_sample ( Weapon_info [ weapon_objp - > id ] . flash_sound , F1_0 ) ;
else
2014-10-26 21:33:50 +00:00
digi_link_sound_to_pos ( Weapon_info [ weapon_objp - > id ] . flash_sound , weapon_objp - > segnum , 0 , weapon_objp - > pos , 0 , F1_0 ) ;
2006-03-20 17:12:09 +00:00
// -- if ((Last_omega_muzzle_flash_time + F1_0/4 < GameTime) || (Last_omega_muzzle_flash_time > GameTime)) {
// -- do_muzzle_stuff(firing_segnum, firing_pos);
// -- Last_omega_muzzle_flash_time = GameTime;
// -- }
// Delete the original object. Its only purpose in life was to determine which object to home in on.
2013-12-24 04:53:59 +00:00
obj_delete ( weapon_objp ) ;
2006-03-20 17:12:09 +00:00
// If couldn't lock on anything, fire straight ahead.
2013-12-26 22:21:16 +00:00
if ( lock_objnum = = object_none ) {
2006-03-20 17:12:09 +00:00
fvi_query fq ;
fvi_info hit_data ;
int fate ;
2014-11-04 01:33:45 +00:00
const auto perturb_vec = make_random_vector ( ) ;
2014-11-02 03:43:57 +00:00
const auto perturbed_fvec = vm_vec_scale_add ( parent_objp - > orient . fvec , perturb_vec , F1_0 / 16 ) ;
2014-10-26 21:37:27 +00:00
vm_vec_scale_add ( goal_pos , firing_pos , perturbed_fvec , MAX_OMEGA_DIST ) ;
2006-03-20 17:12:09 +00:00
fq . startseg = firing_segnum ;
2013-12-26 22:21:16 +00:00
if ( fq . startseg = = segment_none ) {
2006-03-20 17:12:09 +00:00
return ;
}
2014-10-26 21:37:27 +00:00
fq . p0 = & firing_pos ;
2006-03-20 17:12:09 +00:00
fq . p1 = & goal_pos ;
fq . rad = 0 ;
2013-12-24 04:53:59 +00:00
fq . thisobjnum = parent_objp ;
2006-03-20 17:12:09 +00:00
fq . ignore_obj_list = NULL ;
fq . flags = FQ_IGNORE_POWERUPS | FQ_TRANSPOINT | FQ_CHECK_OBJS ; //what about trans walls???
fate = find_vector_intersection ( & fq , & hit_data ) ;
if ( fate ! = HIT_NONE ) {
2013-12-26 22:21:16 +00:00
Assert ( hit_data . hit_seg ! = segment_none ) ; // How can this be? We went from inside the mine to outside without hitting anything?
2006-03-20 17:12:09 +00:00
goal_pos = hit_data . hit_pnt ;
}
} else
goal_pos = Objects [ lock_objnum ] . pos ;
// This is where we create a pile of omega blobs!
2014-10-26 21:37:27 +00:00
create_omega_blobs ( firing_segnum , firing_pos , goal_pos , parent_objp ) ;
2006-03-20 17:12:09 +00:00
}
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
2013-10-09 01:54:12 +00:00
static inline int is_laser_weapon_type ( enum weapon_type_t weapon_type )
{
# if defined(DXX_BUILD_DESCENT_II)
if ( weapon_type = = LASER_ID_L5 | | weapon_type = = LASER_ID_L6 )
return 1 ;
# endif
return ( weapon_type = = LASER_ID_L1 | | weapon_type = = LASER_ID_L2 | | weapon_type = = LASER_ID_L3 | | weapon_type = = LASER_ID_L4 ) ;
}
2006-03-20 17:12:09 +00:00
// ---------------------------------------------------------------------------------
2010-06-15 16:24:56 +00:00
// Initializes a laser after Fire is pressed
2006-03-20 17:12:09 +00:00
// Returns object number.
2014-10-02 03:02:34 +00:00
objptridx_t Laser_create_new ( const vms_vector & direction , const vms_vector & position , segnum_t segnum , const vobjptridx_t parent , enum weapon_type_t weapon_type , int make_sound )
2006-03-20 17:12:09 +00:00
{
fix parent_speed , weapon_speed ;
fix volume ;
fix laser_length = 0 ;
Assert ( weapon_type < N_weapon_types ) ;
2013-10-09 01:54:12 +00:00
switch ( weapon_type )
{
case LASER_ID_L1 :
case LASER_ID_L2 :
case LASER_ID_L3 :
case LASER_ID_L4 :
case CLS1_DRONE_FIRE :
case CONTROLCEN_WEAPON_NUM :
case CONCUSSION_ID :
case FLARE_ID :
case CLS2_DRONE_LASER :
case VULCAN_ID :
# if defined(DXX_BUILD_DESCENT_II)
case SPREADFIRE_ID :
# endif
case PLASMA_ID :
case FUSION_ID :
case HOMING_ID :
case PROXIMITY_ID :
case SMART_ID :
case MEGA_ID :
case PLAYER_SMART_HOMING_ID :
# if defined(DXX_BUILD_DESCENT_I)
case SPREADFIRE_ID :
# endif
case SUPER_MECH_MISS :
case REGULAR_MECH_MISS :
case SILENT_SPREADFIRE_ID :
case MEDIUM_LIFTER_LASER :
case SMALL_HULK_FIRE :
case HEAVY_DRILLER_PLASMA :
case SPIDER_ROBOT_FIRE :
case ROBOT_MEGA_ID :
case ROBOT_SMART_HOMING_ID :
# if defined(DXX_BUILD_DESCENT_II)
case LASER_ID_L5 :
case LASER_ID_L6 :
case GAUSS_ID :
case HELIX_ID :
case PHOENIX_ID :
case OMEGA_ID :
case FLASH_ID :
case GUIDEDMISS_ID :
case SUPERPROX_ID :
case MERCURY_ID :
case EARTHSHAKER_ID :
case SMELTER_PHOENIX_ID :
case SMART_MINE_HOMING_ID :
case ROBOT_SMART_MINE_HOMING_ID :
case ROBOT_SUPERPROX_ID :
case EARTHSHAKER_MEGA_ID :
case ROBOT_EARTHSHAKER_ID :
case PMINE_ID :
case ROBOT_26_WEAPON_46_ID :
case ROBOT_27_WEAPON_52_ID :
case ROBOT_28_WEAPON_42_ID :
case ROBOT_29_WEAPON_20_ID :
case ROBOT_30_WEAPON_48_ID :
case ROBOT_36_WEAPON_41_ID :
case ROBOT_39_WEAPON_43_ID :
case ROBOT_43_WEAPON_55_ID :
case ROBOT_45_WEAPON_45_ID :
case ROBOT_50_WEAPON_50_ID :
case ROBOT_62_WEAPON_60_ID :
case ROBOT_47_WEAPON_57_ID :
case ROBOT_62_WEAPON_61_ID :
2013-12-22 01:15:30 +00:00
case ROBOT_71_WEAPON_62_ID :
2013-10-09 01:54:12 +00:00
# endif
break ;
default :
2013-12-22 01:15:30 +00:00
# ifdef NDEBUG
break ;
# else
2013-12-26 22:21:16 +00:00
return object_none ;
2013-12-22 01:15:30 +00:00
# endif
2013-10-09 01:54:12 +00:00
}
2006-03-20 17:12:09 +00:00
// Don't let homing blobs make muzzle flash.
2014-08-23 23:53:56 +00:00
if ( parent - > type = = OBJ_ROBOT )
2006-03-20 17:12:09 +00:00
do_muzzle_stuff ( segnum , position ) ;
2014-10-02 03:02:34 +00:00
const objptridx_t obj = create_weapon_object ( weapon_type , segnum , position ) ;
2006-03-20 17:12:09 +00:00
2014-01-11 17:55:32 +00:00
if ( obj = = object_none ) {
return obj ;
2006-03-20 17:12:09 +00:00
}
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
// Do the special Omega Cannon stuff. Then return on account of everything that follows does
// not apply to the Omega Cannon.
if ( weapon_type = = OMEGA_ID ) {
// Create orientation matrix for tracking purposes.
2014-10-26 21:37:27 +00:00
vm_vector_2_matrix ( obj - > orient , direction , & parent - > orient . uvec , nullptr ) ;
2006-03-20 17:12:09 +00:00
2014-08-23 23:53:56 +00:00
if ( parent ! = Viewer & & parent - > type ! = OBJ_WEAPON ) {
2010-06-15 16:24:56 +00:00
// Muzzle flash
2006-03-20 17:12:09 +00:00
if ( Weapon_info [ obj - > id ] . flash_vclip > - 1 )
2014-10-26 21:36:25 +00:00
object_create_muzzle_flash ( obj - > segnum , obj - > pos , Weapon_info [ obj - > id ] . flash_size , Weapon_info [ obj - > id ] . flash_vclip ) ;
2006-03-20 17:12:09 +00:00
}
2014-08-23 23:53:56 +00:00
do_omega_stuff ( parent , position , obj ) ;
2006-03-20 17:12:09 +00:00
2014-01-11 17:55:32 +00:00
return obj ;
2006-03-20 17:12:09 +00:00
}
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
2014-08-23 23:53:56 +00:00
if ( parent - > type = = OBJ_PLAYER ) {
2006-03-20 17:12:09 +00:00
if ( weapon_type = = FUSION_ID ) {
2013-08-27 23:53:03 +00:00
int fusion_scale ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
if ( Game_mode & GM_MULTI )
fusion_scale = 2 ;
else
# endif
2013-08-27 23:53:03 +00:00
fusion_scale = 4 ;
2006-03-20 17:12:09 +00:00
if ( Fusion_charge < = 0 )
obj - > ctype . laser_info . multiplier = F1_0 ;
2013-08-27 23:53:03 +00:00
else if ( Fusion_charge < = F1_0 * fusion_scale )
2006-03-20 17:12:09 +00:00
obj - > ctype . laser_info . multiplier = F1_0 + Fusion_charge / 2 ;
else
2013-08-27 23:53:03 +00:00
obj - > ctype . laser_info . multiplier = F1_0 * fusion_scale ;
2006-03-20 17:12:09 +00:00
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
// 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.
if ( Game_mode & GM_MULTI )
obj - > ctype . laser_info . multiplier / = 2 ;
# endif
2013-08-27 23:53:03 +00:00
}
2014-08-23 23:53:56 +00:00
else if ( is_laser_weapon_type ( weapon_type ) & & ( Players [ get_player_id ( parent ) ] . flags & PLAYER_FLAGS_QUAD_LASERS ) )
2006-03-20 17:12:09 +00:00
obj - > ctype . laser_info . multiplier = F1_0 * 3 / 4 ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
else if ( weapon_type = = GUIDEDMISS_ID ) {
if ( parent = = Players [ Player_num ] . objnum ) {
Guided_missile [ Player_num ] = obj ;
Guided_missile_sig [ Player_num ] = obj - > signature ;
if ( Newdemo_state = = ND_STATE_RECORDING )
newdemo_record_guided_start ( ) ;
}
}
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
}
// Make children of smart bomb bounce so if they hit a wall right away, they
// won't detonate. The frame interval code will clear this bit after 1/2 second.
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
if ( ( weapon_type = = PLAYER_SMART_HOMING_ID ) | | ( weapon_type = = ROBOT_SMART_HOMING_ID ) )
# elif defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
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 ) )
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
obj - > mtype . phys_info . flags | = PF_BOUNCE ;
if ( Weapon_info [ weapon_type ] . render_type = = WEAPON_RENDER_POLYMODEL )
laser_length = Polygon_models [ obj - > rtype . pobj_info . model_num ] . rad * 2 ;
if ( weapon_type = = FLARE_ID )
obj - > mtype . phys_info . flags | = PF_STICK ; //this obj sticks to walls
2010-06-15 16:24:56 +00:00
2013-10-07 23:52:33 +00:00
obj - > shields = Weapon_info [ get_weapon_id ( obj ) ] . strength [ Difficulty_level ] ;
2010-06-15 16:24:56 +00:00
2006-03-20 17:12:09 +00:00
// Fill in laser-specific data
2013-10-07 23:52:33 +00:00
obj - > lifeleft = Weapon_info [ get_weapon_id ( obj ) ] . lifetime ;
2014-08-23 23:53:56 +00:00
obj - > ctype . laser_info . parent_type = parent - > type ;
obj - > ctype . laser_info . parent_signature = parent - > signature ;
2006-03-20 17:12:09 +00:00
obj - > ctype . laser_info . parent_num = parent ;
// Assign parent type to highest level creator. This propagates parent type down from
// the original creator through weapons which create children of their own (ie, smart missile)
2014-08-23 23:53:56 +00:00
if ( parent - > type = = OBJ_WEAPON ) {
2013-12-29 04:28:07 +00:00
objnum_t highest_parent = parent ;
2006-03-20 17:12:09 +00:00
int count ;
count = 0 ;
while ( ( count + + < 10 ) & & ( Objects [ highest_parent ] . type = = OBJ_WEAPON ) ) {
2013-12-29 04:28:07 +00:00
objnum_t next_parent ;
2006-03-20 17:12:09 +00:00
next_parent = Objects [ highest_parent ] . ctype . laser_info . parent_num ;
if ( Objects [ next_parent ] . signature ! = Objects [ highest_parent ] . ctype . laser_info . parent_signature )
break ; // Probably means parent was killed. Just continue.
if ( next_parent = = highest_parent ) {
Int3 ( ) ; // Hmm, object is parent of itself. This would seem to be bad, no?
break ;
}
highest_parent = next_parent ;
obj - > ctype . laser_info . parent_num = highest_parent ;
obj - > ctype . laser_info . parent_type = Objects [ highest_parent ] . type ;
obj - > ctype . laser_info . parent_signature = Objects [ highest_parent ] . signature ;
}
}
// Create orientation matrix so we can look from this pov
// Homing missiles also need an orientation matrix so they know if they can make a turn.
2013-10-07 23:52:33 +00:00
if ( ( obj - > render_type = = RT_POLYOBJ ) | | ( Weapon_info [ get_weapon_id ( obj ) ] . homing_flag ) )
2014-10-26 21:37:27 +00:00
vm_vector_2_matrix ( obj - > orient , direction , & parent - > orient . uvec , nullptr ) ;
2006-03-20 17:12:09 +00:00
2014-08-23 23:53:56 +00:00
if ( ( parent ! = Viewer ) & & ( parent - > type ! = OBJ_WEAPON ) ) {
2010-06-15 16:24:56 +00:00
// Muzzle flash
2013-10-07 23:52:33 +00:00
if ( Weapon_info [ get_weapon_id ( obj ) ] . flash_vclip > - 1 )
2014-10-26 21:36:25 +00:00
object_create_muzzle_flash ( obj - > segnum , obj - > pos , Weapon_info [ get_weapon_id ( obj ) ] . flash_size , Weapon_info [ get_weapon_id ( obj ) ] . flash_vclip ) ;
2006-03-20 17:12:09 +00:00
}
volume = F1_0 ;
2013-10-07 23:52:33 +00:00
if ( Weapon_info [ get_weapon_id ( obj ) ] . flash_sound > - 1 ) {
2006-03-20 17:12:09 +00:00
if ( make_sound ) {
if ( parent = = ( Viewer - Objects ) ) {
if ( weapon_type = = VULCAN_ID ) // Make your own vulcan gun 1/2 as loud.
volume = F1_0 / 2 ;
2013-10-07 23:52:33 +00:00
digi_play_sample ( Weapon_info [ get_weapon_id ( obj ) ] . flash_sound , volume ) ;
2006-03-20 17:12:09 +00:00
} else {
2014-10-26 21:33:50 +00:00
digi_link_sound_to_pos ( Weapon_info [ get_weapon_id ( obj ) ] . flash_sound , obj - > segnum , 0 , obj - > pos , 0 , volume ) ;
2006-03-20 17:12:09 +00:00
}
}
}
// Fire the laser from the gun tip so that the back end of the laser bolt is at the gun tip.
// Move 1 frame, so that the end-tip of the laser is touching the gun barrel.
// This also jitters the laser a bit so that it doesn't alias.
// Don't do for weapons created by weapons.
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
2014-08-23 23:53:56 +00:00
if ( parent - > type ! = OBJ_WEAPON & & ( Weapon_info [ weapon_type ] . render_type ! = WEAPON_RENDER_NONE ) & & ( weapon_type ! = FLARE_ID ) )
2013-03-03 01:03:33 +00:00
# elif defined(DXX_BUILD_DESCENT_II)
2014-08-23 23:53:56 +00:00
if ( parent - > type = = OBJ_PLAYER & & ( Weapon_info [ weapon_type ] . render_type ! = WEAPON_RENDER_NONE ) & & ( weapon_type ! = FLARE_ID ) )
2013-03-03 01:03:33 +00:00
# endif
2014-08-23 23:53:56 +00:00
{
2014-11-02 03:43:57 +00:00
const auto end_pos = vm_vec_scale_add ( obj - > pos , direction , ( laser_length / 2 ) ) ;
2014-10-22 02:46:03 +00:00
auto end_segnum = find_point_seg ( end_pos , obj - > segnum ) ;
2006-03-20 17:12:09 +00:00
if ( end_segnum ! = obj - > segnum ) {
2013-12-26 22:21:16 +00:00
if ( end_segnum ! = segment_none ) {
2006-03-20 17:12:09 +00:00
obj - > pos = end_pos ;
2014-01-11 17:55:32 +00:00
obj_relink ( obj , end_segnum ) ;
2008-04-06 20:23:28 +00:00
}
2006-03-20 17:12:09 +00:00
} else
obj - > pos = end_pos ;
}
// Here's where to fix the problem with objects which are moving backwards imparting higher velocity to their weaponfire.
// Find out if moving backwards.
2013-03-30 20:46:13 +00:00
if ( is_proximity_bomb_or_smart_mine ( weapon_type ) ) {
2014-09-28 21:11:03 +00:00
parent_speed = vm_vec_mag_quick ( parent - > mtype . phys_info . velocity ) ;
2014-09-28 21:11:48 +00:00
if ( vm_vec_dot ( parent - > mtype . phys_info . velocity , parent - > orient . fvec ) < 0 )
2006-03-20 17:12:09 +00:00
parent_speed = - parent_speed ;
} else
parent_speed = 0 ;
2013-10-07 23:52:33 +00:00
weapon_speed = Weapon_info [ get_weapon_id ( obj ) ] . speed [ Difficulty_level ] ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2013-10-07 23:52:33 +00:00
if ( Weapon_info [ get_weapon_id ( obj ) ] . speedvar ! = 128 ) {
2006-03-20 17:12:09 +00:00
fix randval ;
// Get a scale factor between speedvar% and 1.0.
randval = F1_0 - ( ( d_rand ( ) * Weapon_info [ obj - > id ] . speedvar ) > > 6 ) ;
weapon_speed = fixmul ( weapon_speed , randval ) ;
}
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
// Ugly hack (too bad we're on a deadline), for homing missiles dropped by smart bomb, start them out slower.
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
2013-10-07 23:52:33 +00:00
if ( ( get_weapon_id ( obj ) = = PLAYER_SMART_HOMING_ID ) | | ( get_weapon_id ( obj ) = = ROBOT_SMART_HOMING_ID ) )
2013-03-03 01:03:33 +00:00
# elif defined(DXX_BUILD_DESCENT_II)
2013-10-07 23:52:33 +00:00
if ( ( get_weapon_id ( obj ) = = PLAYER_SMART_HOMING_ID ) | | ( get_weapon_id ( obj ) = = SMART_MINE_HOMING_ID ) | | ( get_weapon_id ( obj ) = = ROBOT_SMART_HOMING_ID ) | | ( get_weapon_id ( obj ) = = ROBOT_SMART_MINE_HOMING_ID ) | | ( get_weapon_id ( obj ) = = EARTHSHAKER_MEGA_ID ) )
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
weapon_speed / = 4 ;
2013-10-07 23:52:33 +00:00
if ( Weapon_info [ get_weapon_id ( obj ) ] . thrust ! = 0 )
2006-03-20 17:12:09 +00:00
weapon_speed / = 2 ;
2014-10-26 21:37:27 +00:00
vm_vec_copy_scale ( obj - > mtype . phys_info . velocity , direction , weapon_speed + parent_speed ) ;
2006-03-20 17:12:09 +00:00
2010-06-15 16:24:56 +00:00
// Set thrust
2006-03-20 17:12:09 +00:00
if ( Weapon_info [ weapon_type ] . thrust ! = 0 ) {
obj - > mtype . phys_info . thrust = obj - > mtype . phys_info . velocity ;
2014-09-28 21:11:05 +00:00
vm_vec_scale ( obj - > mtype . phys_info . thrust , fixdiv ( Weapon_info [ get_weapon_id ( obj ) ] . thrust , weapon_speed + parent_speed ) ) ;
2006-03-20 17:12:09 +00:00
}
2013-10-07 23:52:33 +00:00
if ( ( obj - > type = = OBJ_WEAPON ) & & ( get_weapon_id ( obj ) = = FLARE_ID ) )
2006-03-20 17:12:09 +00:00
obj - > lifeleft + = ( d_rand ( ) - 16384 ) < < 2 ; // add in -2..2 seconds
2014-01-11 17:55:32 +00:00
return obj ;
2006-03-20 17:12:09 +00:00
}
// -----------------------------------------------------------------------------------------------------------
// Calls Laser_create_new, but takes care of the segment and point computation for you.
2014-10-02 03:02:34 +00:00
objptridx_t Laser_create_new_easy ( const vms_vector & direction , const vms_vector & position , const vobjptridx_t parent , enum weapon_type_t weapon_type , int make_sound )
2006-03-20 17:12:09 +00:00
{
fvi_query fq ;
fvi_info hit_data ;
int fate ;
// Find segment containing laser fire position. If the robot is straddling a segment, the position from
// which it fires may be in a different segment, which is bad news for find_vector_intersection. So, cast
// a ray from the object center (whose segment we know) to the laser position. Then, in the call to Laser_create_new
// use the data returned from this call to find_vector_intersection.
// Note that while find_vector_intersection is pretty slow, it is not terribly slow if the destination point is
// in the same segment as the source point.
2013-12-24 04:53:59 +00:00
fq . p0 = & parent - > pos ;
fq . startseg = parent - > segnum ;
2014-10-26 21:37:27 +00:00
fq . p1 = & position ;
2006-03-20 17:12:09 +00:00
fq . rad = 0 ;
2013-12-24 04:53:59 +00:00
fq . thisobjnum = parent ;
2006-03-20 17:12:09 +00:00
fq . ignore_obj_list = NULL ;
fq . flags = FQ_TRANSWALL | FQ_CHECK_OBJS ; //what about trans walls???
fate = find_vector_intersection ( & fq , & hit_data ) ;
2013-12-26 22:21:16 +00:00
if ( fate ! = HIT_NONE | | hit_data . hit_seg = = segment_none ) {
return object_none ;
2006-03-20 17:12:09 +00:00
}
2014-10-26 21:37:27 +00:00
return Laser_create_new ( direction , hit_data . hit_pnt , hit_data . hit_seg , parent , weapon_type , make_sound ) ;
2006-03-20 17:12:09 +00:00
}
int Muzzle_queue_index = 0 ;
muzzle_info Muzzle_data [ MUZZLE_QUEUE_MAX ] ;
// -----------------------------------------------------------------------------------------------------------
// Determine if two objects are on a line of sight. If so, return true, else return false.
// Calls fvi.
2014-10-02 03:02:34 +00:00
int object_to_object_visibility ( const vcobjptridx_t obj1 , const vcobjptr_t obj2 , int trans_type )
2006-03-20 17:12:09 +00:00
{
fvi_query fq ;
fvi_info hit_data ;
int fate ;
fq . p0 = & obj1 - > pos ;
fq . startseg = obj1 - > segnum ;
fq . p1 = & obj2 - > pos ;
fq . rad = 0x10 ;
2013-12-24 04:53:59 +00:00
fq . thisobjnum = obj1 ;
2006-03-20 17:12:09 +00:00
fq . ignore_obj_list = NULL ;
fq . flags = trans_type ;
fate = find_vector_intersection ( & fq , & hit_data ) ;
if ( fate = = HIT_WALL )
return 0 ;
else if ( fate = = HIT_NONE )
return 1 ;
else
Int3 ( ) ; // Contact Mike: Oops, what happened? What is fate?
// 2 = hit object (impossible), 3 = bad starting point (bad)
return 0 ;
}
// -----------------------------------------------------------------------------------------------------------
// Return true if weapon *tracker is able to track object Objects[track_goal], else return false.
// In order for the object to be trackable, it must be within a reasonable turning radius for the missile
// and it must not be obstructed by a wall.
2014-10-02 03:02:34 +00:00
static int object_is_trackable ( const objptridx_t objp , const vobjptridx_t tracker , fix * dot )
2006-03-20 17:12:09 +00:00
{
2014-08-23 23:53:56 +00:00
if ( objp = = object_none )
2006-03-20 17:12:09 +00:00
return 0 ;
if ( Game_mode & GM_MULTI_COOP )
return 0 ;
// Don't track player if he's cloaked.
2014-08-23 23:53:56 +00:00
if ( ( objp = = Players [ Player_num ] . objnum ) & & ( Players [ Player_num ] . flags & PLAYER_FLAGS_CLOAKED ) )
2006-03-20 17:12:09 +00:00
return 0 ;
// Can't track AI object if he's cloaked.
if ( objp - > type = = OBJ_ROBOT ) {
if ( objp - > ctype . ai_info . CLOAKED )
return 0 ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
// Your missiles don't track your escort.
2013-10-07 23:52:33 +00:00
if ( Robot_info [ get_robot_id ( objp ) ] . companion )
2006-03-20 17:12:09 +00:00
if ( tracker - > ctype . laser_info . parent_type = = OBJ_PLAYER )
return 0 ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
}
2014-10-29 03:24:31 +00:00
const auto vector_to_goal = vm_vec_normalized_quick ( vm_vec_sub ( objp - > pos , tracker - > pos ) ) ;
2014-09-28 21:11:48 +00:00
* dot = vm_vec_dot ( vector_to_goal , tracker - > orient . fvec ) ;
2006-03-20 17:12:09 +00:00
2013-08-06 19:04:44 +00:00
if ( * dot > = HOMING_MIN_TRACKABLE_DOT ) {
2006-03-20 17:12:09 +00:00
int rval ;
// dot is in legal range, now see if object is visible
rval = object_to_object_visibility ( tracker , objp , FQ_TRANSWALL ) ;
return rval ;
} else {
return 0 ;
}
}
// --------------------------------------------------------------------------------------------
2014-10-02 03:02:34 +00:00
static objptridx_t call_find_homing_object_complete ( const vms_vector & curpos , const vobjptridx_t tracker )
2006-03-20 17:12:09 +00:00
{
if ( Game_mode & GM_MULTI ) {
if ( tracker - > ctype . laser_info . parent_type = = OBJ_PLAYER ) {
// It's fired by a player, so if robots present, track robot, else track player.
if ( Game_mode & GM_MULTI_COOP )
return find_homing_object_complete ( curpos , tracker , OBJ_ROBOT , - 1 ) ;
else
return find_homing_object_complete ( curpos , tracker , OBJ_PLAYER , OBJ_ROBOT ) ;
} else {
int goal2_type = - 1 ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2011-02-14 21:27:07 +00:00
if ( cheats . robotskillrobots )
2006-03-20 17:12:09 +00:00
goal2_type = OBJ_ROBOT ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
Assert ( tracker - > ctype . laser_info . parent_type = = OBJ_ROBOT ) ;
return find_homing_object_complete ( curpos , tracker , OBJ_PLAYER , goal2_type ) ;
2010-06-15 16:24:56 +00:00
}
2006-03-20 17:12:09 +00:00
} else
return find_homing_object_complete ( curpos , tracker , OBJ_ROBOT , - 1 ) ;
}
// --------------------------------------------------------------------------------------------
// Find object to home in on.
// Scan list of objects rendered last frame, find one that satisfies function of nearness to center and distance.
2014-10-02 03:02:34 +00:00
static objptridx_t find_homing_object ( const vms_vector & curpos , const vobjptridx_t tracker )
2006-03-20 17:12:09 +00:00
{
// Contact Mike: This is a bad and stupid thing. Who called this routine with an illegal laser type??
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2013-08-27 23:53:03 +00:00
if ( tracker - > id ! = OMEGA_ID )
2013-03-03 01:03:33 +00:00
# endif
2013-10-07 23:52:33 +00:00
Assert ( Weapon_info [ get_weapon_id ( tracker ) ] . homing_flag ) ;
2006-03-20 17:12:09 +00:00
// Find an object to track based on game mode (eg, whether in network play) and who fired it.
2014-10-26 21:37:27 +00:00
return call_find_homing_object_complete ( curpos , tracker ) ;
2006-03-20 17:12:09 +00:00
}
// --------------------------------------------------------------------------------------------
// Find object to home in on.
// Scan list of objects rendered last frame, find one that satisfies function of nearness to center and distance.
// Can track two kinds of objects. If you are only interested in one type, set track_obj_type2 to NULL
// Always track proximity bombs. --MK, 06/14/95
// Make homing objects not track parent's prox bombs.
2014-10-02 03:02:34 +00:00
objptridx_t find_homing_object_complete ( const vms_vector & curpos , const vobjptridx_t tracker , int track_obj_type1 , int track_obj_type2 )
2006-03-20 17:12:09 +00:00
{
fix max_dot = - F1_0 * 2 ;
2014-08-08 03:02:32 +00:00
# if defined(DXX_BUILD_DESCENT_II)
if ( tracker - > id ! = OMEGA_ID )
2006-03-20 17:12:09 +00:00
// Contact Mike: This is a bad and stupid thing. Who called this routine with an illegal laser type??
2013-03-03 01:03:33 +00:00
# endif
2014-08-08 03:02:32 +00:00
{
if ( ! Weapon_info [ get_weapon_id ( tracker ) ] . homing_flag )
throw std : : logic_error ( " tracking without homing_flag " ) ;
}
2006-03-20 17:12:09 +00:00
2014-10-10 02:41:51 +00:00
const fix64 HOMING_MAX_TRACKABLE_DIST = F1_0 * 250 ;
fix64 max_trackable_dist = HOMING_MAX_TRACKABLE_DIST * HOMING_MAX_TRACKABLE_DIST ;
2013-08-27 23:53:03 +00:00
fix min_trackable_dot = HOMING_MAX_TRACKABLE_DOT ;
2006-03-20 17:12:09 +00:00
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
if ( tracker - > id = = OMEGA_ID ) {
2014-10-10 02:41:51 +00:00
max_trackable_dist = OMEGA_MAX_TRACKABLE_DIST * OMEGA_MAX_TRACKABLE_DIST ;
2006-03-20 17:12:09 +00:00
min_trackable_dot = OMEGA_MIN_TRACKABLE_DOT ;
}
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
2014-08-23 23:53:56 +00:00
objptridx_t best_objnum = object_none ;
2014-10-12 23:05:46 +00:00
range_for ( auto objnum , highest_valid ( Objects ) )
{
2006-03-20 17:12:09 +00:00
int is_proximity = 0 ;
2014-10-10 02:41:51 +00:00
fix dot ;
2014-08-23 23:53:56 +00:00
auto curobjp = vobjptridx ( objnum ) ;
2006-03-20 17:12:09 +00:00
if ( ( curobjp - > type ! = track_obj_type1 ) & & ( curobjp - > type ! = track_obj_type2 ) )
{
2014-08-08 03:06:31 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2013-10-07 23:52:33 +00:00
if ( ( curobjp - > type = = OBJ_WEAPON ) & & ( is_proximity_bomb_or_smart_mine ( get_weapon_id ( curobjp ) ) ) ) {
2006-03-20 17:12:09 +00:00
if ( curobjp - > ctype . laser_info . parent_signature ! = tracker - > ctype . laser_info . parent_signature )
is_proximity = 1 ;
else
continue ;
} else
2014-08-08 03:06:31 +00:00
# endif
2006-03-20 17:12:09 +00:00
continue ;
}
if ( objnum = = tracker - > ctype . laser_info . parent_num ) // Don't track shooter
continue ;
// Don't track cloaked players.
if ( curobjp - > type = = OBJ_PLAYER )
{
2013-10-07 23:52:33 +00:00
if ( Players [ get_player_id ( curobjp ) ] . flags & PLAYER_FLAGS_CLOAKED )
2006-03-20 17:12:09 +00:00
continue ;
// Don't track teammates in team games
2013-10-07 23:52:33 +00:00
if ( ( Game_mode & GM_TEAM ) & & ( Objects [ tracker - > ctype . laser_info . parent_num ] . type = = OBJ_PLAYER ) & & ( get_team ( get_player_id ( curobjp ) ) = = get_team ( get_player_id ( & Objects [ tracker - > ctype . laser_info . parent_num ] ) ) ) )
2006-03-20 17:12:09 +00:00
continue ;
}
// Can't track AI object if he's cloaked.
if ( curobjp - > type = = OBJ_ROBOT ) {
if ( curobjp - > ctype . ai_info . CLOAKED )
continue ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
// Your missiles don't track your escort.
if ( Robot_info [ curobjp - > id ] . companion )
if ( tracker - > ctype . laser_info . parent_type = = OBJ_PLAYER )
continue ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
}
2014-10-29 03:24:31 +00:00
auto vec_to_curobj = vm_vec_sub ( curobjp - > pos , curpos ) ;
2014-10-10 02:41:51 +00:00
auto dist = vm_vec_mag2 ( vec_to_curobj ) ;
2006-03-20 17:12:09 +00:00
if ( dist < max_trackable_dist ) {
2014-09-28 21:11:03 +00:00
vm_vec_normalize ( vec_to_curobj ) ;
2014-09-28 21:11:48 +00:00
dot = vm_vec_dot ( vec_to_curobj , tracker - > orient . fvec ) ;
2006-03-20 17:12:09 +00:00
if ( is_proximity )
dot = ( ( dot < < 3 ) + dot ) > > 3 ; // I suspect Watcom would be too stupid to figure out the obvious...
if ( dot > min_trackable_dot ) {
if ( dot > max_dot ) {
2014-08-23 23:53:56 +00:00
if ( object_to_object_visibility ( tracker , curobjp , FQ_TRANSWALL ) ) {
2006-03-20 17:12:09 +00:00
max_dot = dot ;
2014-08-23 23:53:56 +00:00
best_objnum = curobjp ;
2006-03-20 17:12:09 +00:00
}
}
}
}
2012-04-15 01:05:28 +00:00
2006-03-20 17:12:09 +00:00
}
return best_objnum ;
}
// ------------------------------------------------------------------------------------------------------------
// See if legal to keep tracking currently tracked object. If not, see if another object is trackable. If not, return -1,
// else return object number of tracking object.
// Computes and returns a fairly precise dot product.
2014-10-02 03:02:34 +00:00
static objptridx_t track_track_goal ( const objptridx_t track_goal , const vobjptridx_t tracker , fix * dot )
2006-03-20 17:12:09 +00:00
{
2013-08-06 19:04:44 +00:00
if ( object_is_trackable ( track_goal , tracker , dot ) ) {
2010-06-15 16:24:56 +00:00
return track_goal ;
2013-12-24 04:53:59 +00:00
} else if ( ( ( ( tracker ) ^ d_tick_count ) % 4 ) = = 0 )
2010-06-15 16:24:56 +00:00
{
2006-03-20 17:12:09 +00:00
// If player fired missile, then search for an object, if not, then give up.
if ( Objects [ tracker - > ctype . laser_info . parent_num ] . type = = OBJ_PLAYER ) {
int goal_type ;
2013-12-26 22:21:16 +00:00
if ( track_goal = = object_none )
2006-03-20 17:12:09 +00:00
{
if ( Game_mode & GM_MULTI )
{
if ( Game_mode & GM_MULTI_COOP )
2014-10-26 21:37:27 +00:00
return find_homing_object_complete ( tracker - > pos , tracker , OBJ_ROBOT , - 1 ) ;
2006-03-20 17:12:09 +00:00
else if ( Game_mode & GM_MULTI_ROBOTS ) // Not cooperative, if robots, track either robot or player
2014-10-26 21:37:27 +00:00
return find_homing_object_complete ( tracker - > pos , tracker , OBJ_PLAYER , OBJ_ROBOT ) ;
2006-03-20 17:12:09 +00:00
else // Not cooperative and no robots, track only a player
2014-10-26 21:37:27 +00:00
return find_homing_object_complete ( tracker - > pos , tracker , OBJ_PLAYER , - 1 ) ;
2006-03-20 17:12:09 +00:00
}
else
2014-10-26 21:37:27 +00:00
return find_homing_object_complete ( tracker - > pos , tracker , OBJ_PLAYER , OBJ_ROBOT ) ;
2010-06-15 16:24:56 +00:00
}
else
2006-03-20 17:12:09 +00:00
{
goal_type = Objects [ tracker - > ctype . laser_info . track_goal ] . type ;
if ( ( goal_type = = OBJ_PLAYER ) | | ( goal_type = = OBJ_ROBOT ) )
2014-10-26 21:37:27 +00:00
return find_homing_object_complete ( tracker - > pos , tracker , goal_type , - 1 ) ;
2006-03-20 17:12:09 +00:00
else
2014-08-23 23:53:56 +00:00
return object_none ;
2006-03-20 17:12:09 +00:00
}
2010-06-15 16:24:56 +00:00
}
2006-03-20 17:12:09 +00:00
else {
int goal_type , goal2_type = - 1 ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2011-02-14 21:27:07 +00:00
if ( cheats . robotskillrobots )
2006-03-20 17:12:09 +00:00
goal2_type = OBJ_ROBOT ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
2013-12-26 22:21:16 +00:00
if ( track_goal = = object_none )
2014-10-26 21:37:27 +00:00
return find_homing_object_complete ( tracker - > pos , tracker , OBJ_PLAYER , goal2_type ) ;
2006-03-20 17:12:09 +00:00
else {
goal_type = Objects [ tracker - > ctype . laser_info . track_goal ] . type ;
2014-10-26 21:37:27 +00:00
return find_homing_object_complete ( tracker - > pos , tracker , goal_type , goal2_type ) ;
2006-03-20 17:12:09 +00:00
}
}
}
2013-12-26 22:21:16 +00:00
return object_none ;
2006-03-20 17:12:09 +00:00
}
//-------------- Initializes a laser after Fire is pressed -----------------
2014-10-02 03:02:34 +00:00
static objptridx_t Laser_player_fire_spread_delay ( const vobjptridx_t obj , enum weapon_type_t laser_type , int gun_num , fix spreadr , fix spreadu , fix delay_time , int make_sound , vms_vector shot_orientation )
2006-03-20 17:12:09 +00:00
{
2013-12-29 04:28:07 +00:00
int Fate ;
2014-10-30 03:11:06 +00:00
vms_vector LaserDir ;
2006-03-20 17:12:09 +00:00
fvi_query fq ;
fvi_info hit_data ;
2014-11-04 01:31:22 +00:00
vms_vector * pnt ;
2006-03-20 17:12:09 +00:00
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
create_awareness_event ( obj , PA_WEAPON_WALL_COLLISION ) ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
// Find the initial position of the laser
pnt = & Player_ship - > gun_points [ gun_num ] ;
2014-07-13 17:37:41 +00:00
vms_matrix m = vm_transposed_matrix ( obj - > orient ) ;
2014-11-04 01:31:22 +00:00
const auto gun_point = vm_vec_rotate ( * pnt , m ) ;
2006-03-20 17:12:09 +00:00
2014-10-30 03:11:06 +00:00
auto LaserPos = vm_vec_add ( obj - > pos , gun_point ) ;
2006-03-20 17:12:09 +00:00
// If supposed to fire at a delayed time (delay_time), then move this point backwards.
if ( delay_time )
2014-09-28 21:43:14 +00:00
vm_vec_scale_add2 ( LaserPos , shot_orientation , - fixmul ( delay_time , Weapon_info [ laser_type ] . speed [ Difficulty_level ] ) ) ;
2006-03-20 17:12:09 +00:00
// do_muzzle_stuff(obj, &Pos);
//--------------- Find LaserPos and LaserSeg ------------------
fq . p0 = & obj - > pos ;
fq . startseg = obj - > segnum ;
fq . p1 = & LaserPos ;
fq . rad = 0x10 ;
2013-12-24 04:53:59 +00:00
fq . thisobjnum = obj ;
2006-03-20 17:12:09 +00:00
fq . ignore_obj_list = NULL ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
fq . flags = FQ_CHECK_OBJS ;
# elif defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
fq . flags = FQ_CHECK_OBJS | FQ_IGNORE_POWERUPS ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
Fate = find_vector_intersection ( & fq , & hit_data ) ;
2013-12-29 04:28:07 +00:00
segnum_t LaserSeg = hit_data . hit_seg ;
2006-03-20 17:12:09 +00:00
2013-12-26 22:21:16 +00:00
if ( LaserSeg = = segment_none ) //some sort of annoying error
return object_none ;
2006-03-20 17:12:09 +00:00
//SORT OF HACK... IF ABOVE WAS CORRECT THIS WOULDNT BE NECESSARY.
2014-10-01 02:28:41 +00:00
if ( vm_vec_dist_quick ( LaserPos , obj - > pos ) > 0x50000 )
2013-12-26 22:21:16 +00:00
return object_none ;
2010-06-15 16:24:56 +00:00
2006-03-20 17:12:09 +00:00
if ( Fate = = HIT_WALL ) {
2013-12-26 22:21:16 +00:00
return object_none ;
2006-03-20 17:12:09 +00:00
}
if ( Fate = = HIT_OBJECT ) {
// if ( Objects[hit_data.hit_object].type == OBJ_ROBOT )
// Objects[hit_data.hit_object].flags |= OF_SHOULD_BE_DEAD;
// if ( Objects[hit_data.hit_object].type != OBJ_POWERUP )
2010-06-15 16:24:56 +00:00
// return;
2006-03-20 17:12:09 +00:00
//as of 12/6/94, we don't care if the laser is stuck in an object. We
//just fire away normally
}
// Now, make laser spread out.
2013-11-23 13:02:19 +00:00
LaserDir = shot_orientation ;
2006-03-20 17:12:09 +00:00
if ( ( spreadr ! = 0 ) | | ( spreadu ! = 0 ) ) {
2014-09-28 21:43:14 +00:00
vm_vec_scale_add2 ( LaserDir , obj - > orient . rvec , spreadr ) ;
vm_vec_scale_add2 ( LaserDir , obj - > orient . uvec , spreadu ) ;
2006-03-20 17:12:09 +00:00
}
2014-10-26 21:37:27 +00:00
auto objnum = Laser_create_new ( LaserDir , LaserPos , LaserSeg , obj , laser_type , make_sound ) ;
2006-03-20 17:12:09 +00:00
2013-12-26 22:21:16 +00:00
if ( objnum = = object_none )
return objnum ;
2013-08-27 23:53:03 +00:00
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
// Omega cannon is a hack, not surprisingly. Don't want to do the rest of this stuff.
if ( laser_type = = OMEGA_ID )
2013-08-09 15:21:03 +00:00
return objnum ;
2006-03-20 17:12:09 +00:00
if ( laser_type = = GUIDEDMISS_ID & & Multi_is_guided ) {
2014-08-23 23:53:56 +00:00
Guided_missile [ obj - > id ] = objnum ;
2006-03-20 17:12:09 +00:00
}
Multi_is_guided = 0 ;
if ( laser_type = = CONCUSSION_ID | |
laser_type = = HOMING_ID | |
laser_type = = SMART_ID | |
laser_type = = MEGA_ID | |
laser_type = = FLASH_ID | |
//laser_type == GUIDEDMISS_ID ||
//laser_type == SUPERPROX_ID ||
laser_type = = MERCURY_ID | |
laser_type = = EARTHSHAKER_ID )
if ( Missile_viewer = = NULL & & obj - > id = = Player_num )
2014-08-23 23:53:56 +00:00
Missile_viewer = objnum ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
// If this weapon is supposed to be silent, set that bit!
if ( ! make_sound )
2014-08-23 23:53:56 +00:00
objnum - > flags | = OF_SILENT ;
2006-03-20 17:12:09 +00:00
// If the object firing the laser is the player, then indicate the laser object so robots can dodge.
// New by MK on 6/8/95, don't let robots evade proximity bombs, thereby decreasing uselessness of bombs.
2013-08-27 23:53:03 +00:00
if ( obj = = ConsoleObject )
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2014-08-23 23:53:56 +00:00
if ( objnum - > id ! = PROXIMITY_ID & & objnum - > id ! = SUPERPROX_ID )
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
Player_fired_laser_this_frame = objnum ;
if ( Weapon_info [ laser_type ] . homing_flag ) {
if ( obj = = ConsoleObject )
{
2014-10-26 21:37:27 +00:00
objnum - > ctype . laser_info . track_goal = find_homing_object ( LaserPos , objnum ) ;
2014-08-23 23:53:56 +00:00
Network_laser_track = objnum - > ctype . laser_info . track_goal ;
2006-03-20 17:12:09 +00:00
}
else // Some other player shot the homing thing
{
Assert ( Game_mode & GM_MULTI ) ;
2014-08-23 23:53:56 +00:00
objnum - > ctype . laser_info . track_goal = Network_laser_track ;
2006-03-20 17:12:09 +00:00
}
2014-08-23 23:53:56 +00:00
objnum - > ctype . laser_info . track_turn_time = HOMING_TURN_TIME ;
2006-03-20 17:12:09 +00:00
}
2013-08-09 15:21:03 +00:00
return objnum ;
2006-03-20 17:12:09 +00:00
}
// -----------------------------------------------------------------------------------------------------------
2014-10-02 03:02:34 +00:00
static objptridx_t Laser_player_fire_spread ( const vobjptridx_t obj , enum weapon_type_t laser_type , int gun_num , fix spreadr , fix spreadu , int make_sound , vms_vector shot_orientation )
2006-03-20 17:12:09 +00:00
{
2013-11-23 13:02:19 +00:00
return Laser_player_fire_spread_delay ( obj , laser_type , gun_num , spreadr , spreadu , 0 , make_sound , shot_orientation ) ;
2006-03-20 17:12:09 +00:00
}
// -----------------------------------------------------------------------------------------------------------
2014-10-02 03:02:34 +00:00
objptridx_t Laser_player_fire ( const vobjptridx_t obj , enum weapon_type_t laser_type , int gun_num , int make_sound , vms_vector shot_orientation )
2006-03-20 17:12:09 +00:00
{
2013-11-23 13:02:19 +00:00
return Laser_player_fire_spread ( obj , laser_type , gun_num , 0 , 0 , make_sound , shot_orientation ) ;
2006-03-20 17:12:09 +00:00
}
// -----------------------------------------------------------------------------------------------------------
2014-10-02 03:02:34 +00:00
void Flare_create ( const vobjptridx_t obj )
2006-03-20 17:12:09 +00:00
{
fix energy_usage ;
2012-05-27 13:04:45 +00:00
energy_usage = Weapon_info [ FLARE_ID ] . energy_usage ;
2006-03-20 17:12:09 +00:00
if ( Difficulty_level < 2 )
energy_usage = fixmul ( energy_usage , i2f ( Difficulty_level + 2 ) / 4 ) ;
// MK, 11/04/95: Allowed to fire flare even if no energy.
2013-08-27 23:53:03 +00:00
// -- if (Players[Player_num].energy >= energy_usage)
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
if ( Players [ Player_num ] . energy > 0 )
# endif
2013-08-27 23:53:03 +00:00
{
2006-03-20 17:12:09 +00:00
Players [ Player_num ] . energy - = energy_usage ;
if ( Players [ Player_num ] . energy < = 0 ) {
2010-06-15 16:24:56 +00:00
Players [ Player_num ] . energy = 0 ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
auto_select_weapon ( 0 ) ;
# endif
2006-03-20 17:12:09 +00:00
}
2013-11-23 13:02:19 +00:00
Laser_player_fire ( obj , FLARE_ID , 6 , 1 , Objects [ Players [ Player_num ] . objnum ] . orient . fvec ) ;
2006-03-20 17:12:09 +00:00
2011-09-15 08:45:48 +00:00
if ( Game_mode & GM_MULTI )
2013-12-26 22:21:16 +00:00
multi_send_fire ( FLARE_ADJUST , 0 , 0 , 1 , object_none , object_none ) ;
2013-08-27 23:53:03 +00:00
}
2006-03-20 17:12:09 +00:00
}
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
# define HOMING_MISSILE_SCALE 8
# elif defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
# define HOMING_MISSILE_SCALE 16
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
//--------------------------------------------------------------------
// Set object *objp's orientation to (or towards if I'm ambitious) its velocity.
2014-10-02 03:02:34 +00:00
static void homing_missile_turn_towards_velocity ( const vobjptr_t objp , const vms_vector & norm_vel )
2006-03-20 17:12:09 +00:00
{
2014-10-26 21:37:27 +00:00
auto new_fvec = norm_vel ;
2014-09-28 21:11:05 +00:00
vm_vec_scale ( new_fvec , FrameTime * HOMING_MISSILE_SCALE ) ;
2014-09-28 21:43:00 +00:00
vm_vec_add2 ( new_fvec , objp - > orient . fvec ) ;
2014-09-28 21:11:04 +00:00
vm_vec_normalize_quick ( new_fvec ) ;
2006-03-20 17:12:09 +00:00
// if ((norm_vel->x == 0) && (norm_vel->y == 0) && (norm_vel->z == 0))
// return;
2014-10-01 02:28:42 +00:00
vm_vector_2_matrix ( objp - > orient , new_fvec , nullptr , nullptr ) ;
2006-03-20 17:12:09 +00:00
}
2010-06-15 16:24:56 +00:00
2006-03-20 17:12:09 +00:00
//-------------------------------------------------------------------------------------------
//sequence this laser object for this _frame_ (underscores added here to aid MK in his searching!)
2014-10-02 03:02:34 +00:00
void Laser_do_weapon_sequence ( const vobjptridx_t obj )
2006-03-20 17:12:09 +00:00
{
Assert ( obj - > control_type = = CT_WEAPON ) ;
if ( obj - > lifeleft < 0 ) { // We died of old age
obj - > flags | = OF_SHOULD_BE_DEAD ;
2013-10-07 23:52:33 +00:00
if ( Weapon_info [ get_weapon_id ( obj ) ] . damage_radius )
2014-10-26 21:36:31 +00:00
explode_badass_weapon ( obj , obj - > pos ) ;
2006-03-20 17:12:09 +00:00
return ;
}
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2011-04-16 14:44:36 +00:00
if ( omega_cleanup ( obj ) )
return ;
2013-03-03 01:03:33 +00:00
# endif
2011-04-09 23:48:15 +00:00
2006-03-20 17:12:09 +00:00
//delete weapons that are not moving
2012-05-14 17:06:28 +00:00
if ( ! ( ( d_tick_count ^ obj - > signature ) & 3 ) & &
2013-10-07 23:52:33 +00:00
( get_weapon_id ( obj ) ! = FLARE_ID ) & &
( Weapon_info [ get_weapon_id ( obj ) ] . speed [ Difficulty_level ] > 0 ) & &
2014-09-28 21:11:03 +00:00
( vm_vec_mag_quick ( obj - > mtype . phys_info . velocity ) < F2_0 ) ) {
2013-12-24 04:53:59 +00:00
obj_delete ( obj ) ;
2006-03-20 17:12:09 +00:00
return ;
}
2013-10-07 23:52:33 +00:00
if ( get_weapon_id ( obj ) = = FUSION_ID ) { //always set fusion weapon to max vel
2006-03-20 17:12:09 +00:00
2014-09-28 21:11:04 +00:00
vm_vec_normalize_quick ( obj - > mtype . phys_info . velocity ) ;
2006-03-20 17:12:09 +00:00
2014-09-28 21:11:05 +00:00
vm_vec_scale ( obj - > mtype . phys_info . velocity , Weapon_info [ get_weapon_id ( obj ) ] . speed [ Difficulty_level ] ) ;
2006-03-20 17:12:09 +00:00
}
// For homing missiles, turn towards target. (unless it's the guided missile)
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
2013-10-07 23:52:33 +00:00
if ( Weapon_info [ get_weapon_id ( obj ) ] . homing_flag )
2013-03-03 01:03:33 +00:00
# elif defined(DXX_BUILD_DESCENT_II)
2013-10-07 23:52:33 +00:00
if ( Weapon_info [ get_weapon_id ( obj ) ] . homing_flag & & ! ( get_weapon_id ( obj ) = = GUIDEDMISS_ID & & obj - > ctype . laser_info . parent_type = = OBJ_PLAYER & & obj = = Guided_missile [ get_player_id ( & Objects [ obj - > ctype . laser_info . parent_num ] ) ] & & obj - > signature = = Guided_missile [ Objects [ obj - > ctype . laser_info . parent_num ] . id ] - > signature ) )
2013-03-03 01:03:33 +00:00
# endif
2006-10-02 13:16:09 +00:00
{
2006-03-20 17:12:09 +00:00
vms_vector vector_to_object , temp_vec ;
fix dot = F1_0 ;
fix speed , max_speed ;
2013-08-06 19:04:44 +00:00
// For first 125ms of life, missile flies straight.
if ( obj - > ctype . laser_info . creation_time + HOMING_FLY_STRAIGHT_TIME < GameTime64 ) {
2006-03-20 17:12:09 +00:00
// If it's time to do tracking, then it's time to grow up, stop bouncing and start exploding!.
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
2013-10-07 23:52:33 +00:00
if ( ( get_weapon_id ( obj ) = = ROBOT_SMART_HOMING_ID ) | | ( get_weapon_id ( obj ) = = PLAYER_SMART_HOMING_ID ) )
2013-03-03 01:03:33 +00:00
# elif defined(DXX_BUILD_DESCENT_II)
2013-10-07 23:52:33 +00:00
if ( ( get_weapon_id ( obj ) = = ROBOT_SMART_MINE_HOMING_ID ) | | ( get_weapon_id ( obj ) = = ROBOT_SMART_HOMING_ID ) | | ( get_weapon_id ( obj ) = = SMART_MINE_HOMING_ID ) | | ( get_weapon_id ( obj ) = = PLAYER_SMART_HOMING_ID ) | | ( get_weapon_id ( obj ) = = EARTHSHAKER_MEGA_ID ) )
2013-03-03 01:03:33 +00:00
# endif
{
2006-03-20 17:12:09 +00:00
obj - > mtype . phys_info . flags & = ~ PF_BOUNCE ;
}
// Make sure the object we are tracking is still trackable.
2014-08-23 23:53:56 +00:00
objptridx_t obj_track_goal = object_none ;
if ( obj - > ctype . laser_info . track_goal ! = object_none )
obj_track_goal = obj - > ctype . laser_info . track_goal ;
auto track_goal = track_track_goal ( obj_track_goal , obj , & dot ) ;
2006-03-20 17:12:09 +00:00
if ( track_goal = = Players [ Player_num ] . objnum ) {
fix dist_to_player ;
2014-10-01 02:28:41 +00:00
dist_to_player = vm_vec_dist_quick ( obj - > pos , track_goal - > pos ) ;
2006-03-20 17:12:09 +00:00
if ( ( dist_to_player < Players [ Player_num ] . homing_object_dist ) | | ( Players [ Player_num ] . homing_object_dist < 0 ) )
Players [ Player_num ] . homing_object_dist = dist_to_player ;
2010-06-15 16:24:56 +00:00
2006-03-20 17:12:09 +00:00
}
2013-12-26 22:21:16 +00:00
if ( track_goal ! = object_none ) {
2010-06-15 16:24:56 +00:00
# ifdef NEWHOMER
2013-08-07 22:29:56 +00:00
// See if enough time (see HOMING_TURN_TIME) passed and if yes, allow a turn. If not, fly straight.
if ( obj - > ctype . laser_info . track_turn_time > = HOMING_TURN_TIME )
{
2014-09-28 21:11:45 +00:00
vm_vec_sub ( vector_to_object , track_goal - > pos , obj - > pos ) ;
2013-08-07 22:29:56 +00:00
obj - > ctype . laser_info . track_turn_time - = HOMING_TURN_TIME ;
2009-01-17 11:11:07 +00:00
}
2013-08-07 22:29:56 +00:00
else
2009-01-17 11:11:07 +00:00
{
2014-10-30 03:11:06 +00:00
const auto straight = vm_vec_add ( obj - > mtype . phys_info . velocity , obj - > pos ) ;
2014-09-28 21:11:45 +00:00
vm_vec_sub ( vector_to_object , straight , obj - > pos ) ;
2009-01-17 11:11:07 +00:00
}
2013-08-07 22:29:56 +00:00
obj - > ctype . laser_info . track_turn_time + = FrameTime ;
2010-06-15 16:24:56 +00:00
2013-08-07 22:29:56 +00:00
// Scale vector to object to current FrameTime if we run really low
if ( FrameTime > HOMING_TURN_TIME )
2014-09-28 21:11:05 +00:00
vm_vec_scale ( vector_to_object , F1_0 / ( ( float ) HOMING_TURN_TIME / FrameTime ) ) ;
2013-08-07 22:29:56 +00:00
# else
2014-09-28 21:11:45 +00:00
vm_vec_sub ( vector_to_object , Objects [ track_goal ] . pos , obj - > pos ) ;
2013-08-07 22:29:56 +00:00
# endif
2014-09-28 21:11:04 +00:00
vm_vec_normalize_quick ( vector_to_object ) ;
2009-01-17 11:11:07 +00:00
temp_vec = obj - > mtype . phys_info . velocity ;
2014-09-28 21:11:04 +00:00
speed = vm_vec_normalize_quick ( temp_vec ) ;
2013-10-07 23:52:33 +00:00
max_speed = Weapon_info [ get_weapon_id ( obj ) ] . speed [ Difficulty_level ] ;
2009-01-17 11:11:07 +00:00
if ( speed + F1_0 < max_speed ) {
speed + = fixmul ( max_speed , FrameTime / 2 ) ;
if ( speed > max_speed )
speed = max_speed ;
}
2014-09-28 21:11:48 +00:00
dot = vm_vec_dot ( temp_vec , vector_to_object ) ;
2013-08-07 22:29:56 +00:00
2014-09-28 21:43:00 +00:00
vm_vec_add2 ( temp_vec , vector_to_object ) ;
2009-01-17 11:11:07 +00:00
// The boss' smart children track better...
2013-10-07 23:52:33 +00:00
if ( Weapon_info [ get_weapon_id ( obj ) ] . render_type ! = WEAPON_RENDER_POLYMODEL )
2014-09-28 21:43:00 +00:00
vm_vec_add2 ( temp_vec , vector_to_object ) ;
2014-09-28 21:11:04 +00:00
vm_vec_normalize_quick ( temp_vec ) ;
2014-09-28 21:11:05 +00:00
vm_vec_scale ( temp_vec , speed ) ;
2009-01-17 11:11:07 +00:00
obj - > mtype . phys_info . velocity = temp_vec ;
// Subtract off life proportional to amount turned.
// For hardest turn, it will lose 2 seconds per second.
{
fix lifelost , absdot ;
2010-06-15 16:24:56 +00:00
2009-01-17 11:11:07 +00:00
absdot = abs ( F1_0 - dot ) ;
2010-06-15 16:24:56 +00:00
2009-01-17 11:11:07 +00:00
lifelost = fixmul ( absdot * 32 , FrameTime ) ;
obj - > lifeleft - = lifelost ;
2006-03-20 17:12:09 +00:00
}
2009-01-17 11:11:07 +00:00
// Only polygon objects have visible orientation, so only they should turn.
2013-10-07 23:52:33 +00:00
if ( Weapon_info [ get_weapon_id ( obj ) ] . render_type = = WEAPON_RENDER_POLYMODEL )
2014-10-26 21:37:27 +00:00
homing_missile_turn_towards_velocity ( obj , temp_vec ) ; // temp_vec is normalized velocity.
2006-03-20 17:12:09 +00:00
}
}
}
// Make sure weapon is not moving faster than allowed speed.
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
2013-10-07 23:52:33 +00:00
if ( Weapon_info [ get_weapon_id ( obj ) ] . thrust ! = 0 )
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
{
fix weapon_speed ;
2014-09-28 21:11:03 +00:00
weapon_speed = vm_vec_mag_quick ( obj - > mtype . phys_info . velocity ) ;
2013-10-07 23:52:33 +00:00
if ( weapon_speed > Weapon_info [ get_weapon_id ( obj ) ] . speed [ Difficulty_level ] ) {
2006-03-20 17:12:09 +00:00
// Only slow down if not allowed to move. Makes sense, huh? Allows proxbombs to get moved by physics force. --MK, 2/13/96
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2013-08-27 23:53:03 +00:00
if ( Weapon_info [ obj - > id ] . speed [ Difficulty_level ] )
2013-03-03 01:03:33 +00:00
# endif
2013-08-27 23:53:03 +00:00
{
2006-03-20 17:12:09 +00:00
fix scale_factor ;
2013-10-07 23:52:33 +00:00
scale_factor = fixdiv ( Weapon_info [ get_weapon_id ( obj ) ] . speed [ Difficulty_level ] , weapon_speed ) ;
2014-09-28 21:11:05 +00:00
vm_vec_scale ( obj - > mtype . phys_info . velocity , scale_factor ) ;
2006-03-20 17:12:09 +00:00
}
}
}
}
2010-12-22 00:17:59 +00:00
fix64 Last_laser_fired_time = 0 ;
2006-03-20 17:12:09 +00:00
2013-09-21 21:29:58 +00:00
static inline int sufficient_energy ( int energy_used , fix energy )
{
return ! energy_used | | ( energy > = energy_used ) ;
}
static inline int sufficient_ammo ( int ammo_used , int uses_vulcan_ammo , ushort vulcan_ammo )
{
return ! ammo_used | | ( ! uses_vulcan_ammo | | vulcan_ammo > = ammo_used ) ;
}
2006-03-20 17:12:09 +00:00
// --------------------------------------------------------------------------------------------------
// Assumption: This is only called by the actual console player, not for network players
int do_laser_firing_player ( void )
{
player * plp = & Players [ Player_num ] ;
fix energy_used ;
2013-09-21 21:29:58 +00:00
int ammo_used ;
2006-03-20 17:12:09 +00:00
int weapon_index ;
int rval = 0 ;
int nfires = 1 ;
static int Spreadfire_toggle = 0 ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
static int Helix_orientation = 0 ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
if ( Player_is_dead )
return 0 ;
weapon_index = Primary_weapon_to_weapon_info [ Primary_weapon ] ;
2012-05-27 13:04:45 +00:00
energy_used = Weapon_info [ weapon_index ] . energy_usage ;
2006-03-20 17:12:09 +00:00
if ( Difficulty_level < 2 )
energy_used = fixmul ( energy_used , i2f ( Difficulty_level + 2 ) / 4 ) ;
2013-08-27 23:53:03 +00:00
ammo_used = Weapon_info [ weapon_index ] . ammo_usage ;
2013-03-03 01:03:33 +00:00
int uses_vulcan_ammo = weapon_index_uses_vulcan_ammo ( Primary_weapon ) ;
# if defined(DXX_BUILD_DESCENT_II)
2013-08-27 23:53:03 +00:00
if ( Primary_weapon = = OMEGA_INDEX )
energy_used = 0 ; // Omega consumes energy when recharging, not when firing.
2006-03-20 17:12:09 +00:00
// MK, 01/26/96, Helix use 2x energy in multiplayer. bitmaps.tbl parm should have been reduced for single player.
if ( weapon_index = = HELIX_INDEX )
if ( Game_mode & GM_MULTI )
energy_used * = 2 ;
2013-09-21 21:29:58 +00:00
if ( ! ( sufficient_energy ( energy_used , plp - > energy ) & & sufficient_ammo ( ammo_used , uses_vulcan_ammo , plp - > vulcan_ammo ) ) )
2006-03-20 17:12:09 +00:00
auto_select_weapon ( 0 ) ; // Make sure the player can fire from this weapon.
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
2010-12-22 00:17:59 +00:00
while ( Next_laser_fire_time < = GameTime64 ) {
2013-09-21 21:29:58 +00:00
if ( sufficient_energy ( energy_used , plp - > energy ) & & sufficient_ammo ( ammo_used , uses_vulcan_ammo , plp - > vulcan_ammo ) ) {
2013-06-11 10:19:03 +00:00
int laser_level , flags , fire_frame_overhead = 0 ;
if ( GameTime64 - Next_laser_fire_time < = FrameTime ) // if firing is prolonged by FrameTime overhead, let's try to fix that.
fire_frame_overhead = GameTime64 - Next_laser_fire_time ;
Last_laser_fired_time = GameTime64 ;
2006-03-20 17:12:09 +00:00
2011-02-14 21:27:07 +00:00
if ( ! cheats . rapidfire )
2013-06-11 10:19:03 +00:00
Next_laser_fire_time = GameTime64 + Weapon_info [ weapon_index ] . fire_wait - fire_frame_overhead ;
2006-03-20 17:12:09 +00:00
else
2013-06-11 10:19:03 +00:00
Next_laser_fire_time = GameTime64 + ( F1_0 / 25 ) - fire_frame_overhead ;
2006-03-20 17:12:09 +00:00
laser_level = Players [ Player_num ] . laser_level ;
2010-06-15 16:24:56 +00:00
2006-03-20 17:12:09 +00:00
flags = 0 ;
if ( Primary_weapon = = SPREADFIRE_INDEX ) {
if ( Spreadfire_toggle )
flags | = LASER_SPREADFIRE_TOGGLED ;
Spreadfire_toggle = ! Spreadfire_toggle ;
}
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
if ( Primary_weapon = = HELIX_INDEX ) {
Helix_orientation + + ;
flags | = ( ( Helix_orientation & LASER_HELIX_MASK ) < < LASER_HELIX_SHIFT ) ;
}
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
if ( Players [ Player_num ] . flags & PLAYER_FLAGS_QUAD_LASERS )
flags | = LASER_QUAD ;
2013-11-23 13:02:19 +00:00
rval + = do_laser_firing ( Players [ Player_num ] . objnum , Primary_weapon , laser_level , flags , nfires , Objects [ Players [ Player_num ] . objnum ] . orient . fvec ) ;
2006-03-20 17:12:09 +00:00
plp - > energy - = ( energy_used * rval ) / Weapon_info [ weapon_index ] . fire_count ;
if ( plp - > energy < 0 )
plp - > energy = 0 ;
2013-09-21 21:29:58 +00:00
if ( uses_vulcan_ammo ) {
2013-09-20 23:12:54 +00:00
if ( ammo_used > plp - > vulcan_ammo )
plp - > vulcan_ammo = 0 ;
2006-03-20 17:12:09 +00:00
else
2013-09-20 23:12:54 +00:00
plp - > vulcan_ammo - = ammo_used ;
2006-03-20 17:12:09 +00:00
}
auto_select_weapon ( 0 ) ; // Make sure the player can fire from this weapon.
} else {
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
auto_select_weapon ( 0 ) ; // Make sure the player can fire from this weapon.
2010-12-22 00:17:59 +00:00
Next_laser_fire_time = GameTime64 ; // Prevents shots-to-fire from building up.
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
break ; // Couldn't fire weapon, so abort.
}
}
2010-06-15 16:24:56 +00:00
Global_laser_firing_count = 0 ;
2006-03-20 17:12:09 +00:00
return rval ;
}
// --------------------------------------------------------------------------------------------------
// Object "objnum" fires weapon "weapon_num" of level "level". (Right now (9/24/94) level is used only for type 0 laser.
// Flags are the player flags. For network mode, set to 0.
// It is assumed that this is a player object (as in multiplayer), and therefore the gun positions are known.
// Returns number of times a weapon was fired. This is typically 1, but might be more for low frame rates.
2013-03-03 01:03:33 +00:00
// More than one shot is fired with a pseudo-delay so that players on slow machines can fire (for themselves
2006-03-20 17:12:09 +00:00
// or other players) often enough for things like the vulcan cannon.
2013-11-23 13:02:19 +00:00
int do_laser_firing ( int objnum , int weapon_num , int level , int flags , int nfires , vms_vector shot_orientation )
2006-03-20 17:12:09 +00:00
{
2014-08-16 23:18:17 +00:00
auto objp = vobjptridx ( objnum ) ;
2006-03-20 17:12:09 +00:00
switch ( weapon_num ) {
case LASER_INDEX : {
2013-10-09 01:54:12 +00:00
enum weapon_type_t weapon_type ;
2006-03-20 17:12:09 +00:00
2013-10-09 01:54:12 +00:00
switch ( level )
{
case LASER_LEVEL_1 :
weapon_type = LASER_ID_L1 ;
break ;
case LASER_LEVEL_2 :
weapon_type = LASER_ID_L2 ;
break ;
case LASER_LEVEL_3 :
weapon_type = LASER_ID_L3 ;
break ;
case LASER_LEVEL_4 :
weapon_type = LASER_ID_L4 ;
break ;
# if defined(DXX_BUILD_DESCENT_II)
case LASER_LEVEL_5 :
weapon_type = LASER_ID_L5 ;
break ;
case LASER_LEVEL_6 :
weapon_type = LASER_ID_L6 ;
break ;
2013-03-03 01:03:33 +00:00
# endif
2013-10-09 01:54:12 +00:00
default :
Assert ( 0 ) ;
return nfires ;
}
2013-11-23 13:02:19 +00:00
Laser_player_fire ( objp , weapon_type , 0 , 1 , shot_orientation ) ;
Laser_player_fire ( objp , weapon_type , 1 , 0 , shot_orientation ) ;
2006-03-20 17:12:09 +00:00
if ( flags & LASER_QUAD ) {
// hideous system to make quad laser 1.5x powerful as normal laser, make every other quad laser bolt harmless
2013-11-23 13:02:19 +00:00
Laser_player_fire ( objp , weapon_type , 2 , 0 , shot_orientation ) ;
Laser_player_fire ( objp , weapon_type , 3 , 0 , shot_orientation ) ;
2006-03-20 17:12:09 +00:00
}
break ;
}
case VULCAN_INDEX : {
// Only make sound for 1/4 of vulcan bullets.
int make_sound = 1 ;
//if (d_rand() > 24576)
// make_sound = 1;
2013-11-23 13:02:19 +00:00
Laser_player_fire_spread ( objp , VULCAN_ID , 6 , d_rand ( ) / 8 - 32767 / 16 , d_rand ( ) / 8 - 32767 / 16 , make_sound , shot_orientation ) ;
2006-03-20 17:12:09 +00:00
if ( nfires > 1 ) {
2013-11-23 13:02:19 +00:00
Laser_player_fire_spread ( objp , VULCAN_ID , 6 , d_rand ( ) / 8 - 32767 / 16 , d_rand ( ) / 8 - 32767 / 16 , 0 , shot_orientation ) ;
2006-03-20 17:12:09 +00:00
if ( nfires > 2 ) {
2013-11-23 13:02:19 +00:00
Laser_player_fire_spread ( objp , VULCAN_ID , 6 , d_rand ( ) / 8 - 32767 / 16 , d_rand ( ) / 8 - 32767 / 16 , 0 , shot_orientation ) ;
2006-03-20 17:12:09 +00:00
}
}
break ;
}
case SPREADFIRE_INDEX :
if ( flags & LASER_SPREADFIRE_TOGGLED ) {
2013-11-23 13:02:19 +00:00
Laser_player_fire_spread ( objp , SPREADFIRE_ID , 6 , F1_0 / 16 , 0 , 0 , shot_orientation ) ;
Laser_player_fire_spread ( objp , SPREADFIRE_ID , 6 , - F1_0 / 16 , 0 , 0 , shot_orientation ) ;
Laser_player_fire_spread ( objp , SPREADFIRE_ID , 6 , 0 , 0 , 1 , shot_orientation ) ;
2006-03-20 17:12:09 +00:00
} else {
2013-11-23 13:02:19 +00:00
Laser_player_fire_spread ( objp , SPREADFIRE_ID , 6 , 0 , F1_0 / 16 , 0 , shot_orientation ) ;
Laser_player_fire_spread ( objp , SPREADFIRE_ID , 6 , 0 , - F1_0 / 16 , 0 , shot_orientation ) ;
Laser_player_fire_spread ( objp , SPREADFIRE_ID , 6 , 0 , 0 , 1 , shot_orientation ) ;
2006-03-20 17:12:09 +00:00
}
break ;
case PLASMA_INDEX :
2013-11-23 13:02:19 +00:00
Laser_player_fire ( objp , PLASMA_ID , 0 , 1 , shot_orientation ) ;
Laser_player_fire ( objp , PLASMA_ID , 1 , 0 , shot_orientation ) ;
2006-03-20 17:12:09 +00:00
if ( nfires > 1 ) {
2013-11-23 13:02:19 +00:00
Laser_player_fire_spread_delay ( objp , PLASMA_ID , 0 , 0 , 0 , FrameTime / 2 , 1 , shot_orientation ) ;
Laser_player_fire_spread_delay ( objp , PLASMA_ID , 1 , 0 , 0 , FrameTime / 2 , 0 , shot_orientation ) ;
2006-03-20 17:12:09 +00:00
}
break ;
case FUSION_INDEX : {
vms_vector force_vec ;
2013-11-23 13:02:19 +00:00
Laser_player_fire ( objp , FUSION_ID , 0 , 1 , shot_orientation ) ;
Laser_player_fire ( objp , FUSION_ID , 1 , 1 , shot_orientation ) ;
2006-03-20 17:12:09 +00:00
flags = ( sbyte ) ( Fusion_charge > > 12 ) ;
Fusion_charge = 0 ;
force_vec . x = - ( objp - > orient . fvec . x < < 7 ) ;
force_vec . y = - ( objp - > orient . fvec . y < < 7 ) ;
force_vec . z = - ( objp - > orient . fvec . z < < 7 ) ;
2014-10-26 21:37:34 +00:00
phys_apply_force ( objp , force_vec ) ;
2006-03-20 17:12:09 +00:00
force_vec . x = ( force_vec . x > > 4 ) + d_rand ( ) - 16384 ;
force_vec . y = ( force_vec . y > > 4 ) + d_rand ( ) - 16384 ;
force_vec . z = ( force_vec . z > > 4 ) + d_rand ( ) - 16384 ;
2014-10-26 21:37:34 +00:00
phys_apply_rot ( objp , force_vec ) ;
2006-03-20 17:12:09 +00:00
}
break ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
case GAUSS_INDEX : {
// Only make sound for 1/4 of vulcan bullets.
int make_sound = 1 ;
//if (d_rand() > 24576)
// make_sound = 1;
2010-06-15 16:24:56 +00:00
2013-11-23 13:02:19 +00:00
Laser_player_fire_spread ( objp , GAUSS_ID , 6 , ( d_rand ( ) / 8 - 32767 / 16 ) / 5 , ( d_rand ( ) / 8 - 32767 / 16 ) / 5 , make_sound , shot_orientation ) ;
2006-03-20 17:12:09 +00:00
if ( nfires > 1 ) {
2013-11-23 13:02:19 +00:00
Laser_player_fire_spread ( objp , GAUSS_ID , 6 , ( d_rand ( ) / 8 - 32767 / 16 ) / 5 , ( d_rand ( ) / 8 - 32767 / 16 ) / 5 , 0 , shot_orientation ) ;
2006-03-20 17:12:09 +00:00
if ( nfires > 2 ) {
2013-11-23 13:02:19 +00:00
Laser_player_fire_spread ( objp , GAUSS_ID , 6 , ( d_rand ( ) / 8 - 32767 / 16 ) / 5 , ( d_rand ( ) / 8 - 32767 / 16 ) / 5 , 0 , shot_orientation ) ;
2006-03-20 17:12:09 +00:00
}
}
break ;
}
case HELIX_INDEX : {
int helix_orient ;
fix spreadr , spreadu ;
helix_orient = ( flags > > LASER_HELIX_SHIFT ) & LASER_HELIX_MASK ;
switch ( helix_orient ) {
case 0 : spreadr = F1_0 / 16 ; spreadu = 0 ; break ; // Vertical
case 1 : spreadr = F1_0 / 17 ; spreadu = F1_0 / 42 ; break ; // 22.5 degrees
case 2 : spreadr = F1_0 / 22 ; spreadu = F1_0 / 22 ; break ; // 45 degrees
case 3 : spreadr = F1_0 / 42 ; spreadu = F1_0 / 17 ; break ; // 67.5 degrees
case 4 : spreadr = 0 ; spreadu = F1_0 / 16 ; break ; // 90 degrees
case 5 : spreadr = - F1_0 / 42 ; spreadu = F1_0 / 17 ; break ; // 112.5 degrees
2010-06-15 16:24:56 +00:00
case 6 : spreadr = - F1_0 / 22 ; spreadu = F1_0 / 22 ; break ; // 135 degrees
2006-03-20 17:12:09 +00:00
case 7 : spreadr = - F1_0 / 17 ; spreadu = F1_0 / 42 ; break ; // 157.5 degrees
default :
Error ( " Invalid helix_orientation value %x \n " , helix_orient ) ;
}
2013-11-23 13:02:19 +00:00
Laser_player_fire_spread ( objp , HELIX_ID , 6 , 0 , 0 , 1 , shot_orientation ) ;
Laser_player_fire_spread ( objp , HELIX_ID , 6 , spreadr , spreadu , 0 , shot_orientation ) ;
Laser_player_fire_spread ( objp , HELIX_ID , 6 , - spreadr , - spreadu , 0 , shot_orientation ) ;
Laser_player_fire_spread ( objp , HELIX_ID , 6 , spreadr * 2 , spreadu * 2 , 0 , shot_orientation ) ;
Laser_player_fire_spread ( objp , HELIX_ID , 6 , - spreadr * 2 , - spreadu * 2 , 0 , shot_orientation ) ;
2006-03-20 17:12:09 +00:00
break ;
}
case PHOENIX_INDEX :
2013-11-23 13:02:19 +00:00
Laser_player_fire ( objp , PHOENIX_ID , 0 , 1 , shot_orientation ) ;
Laser_player_fire ( objp , PHOENIX_ID , 1 , 0 , shot_orientation ) ;
2006-03-20 17:12:09 +00:00
if ( nfires > 1 ) {
2013-11-23 13:02:19 +00:00
Laser_player_fire_spread_delay ( objp , PHOENIX_ID , 0 , 0 , 0 , FrameTime / 2 , 1 , shot_orientation ) ;
Laser_player_fire_spread_delay ( objp , PHOENIX_ID , 1 , 0 , 0 , FrameTime / 2 , 0 , shot_orientation ) ;
2006-03-20 17:12:09 +00:00
}
break ;
case OMEGA_INDEX :
2013-11-23 13:02:19 +00:00
Laser_player_fire ( objp , OMEGA_ID , 1 , 1 , shot_orientation ) ;
2006-03-20 17:12:09 +00:00
break ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
2013-10-06 17:00:03 +00:00
# if defined(DXX_BUILD_DESCENT_II)
case SUPER_LASER_INDEX :
# endif
2006-03-20 17:12:09 +00:00
default :
Int3 ( ) ; // Contact Yuan: Unknown Primary weapon type, setting to 0.
Primary_weapon = 0 ;
}
// Set values to be recognized during comunication phase, if we are the
// one shooting
if ( ( Game_mode & GM_MULTI ) & & ( objnum = = Players [ Player_num ] . objnum ) )
2013-12-26 22:21:16 +00:00
multi_send_fire ( weapon_num , level , flags , nfires , object_none , object_none ) ;
2006-03-20 17:12:09 +00:00
return nfires ;
}
# define MAX_SMART_DISTANCE (F1_0*150)
# define MAX_OBJDISTS 30
// -------------------------------------------------------------------------------------------
// if goal_obj == -1, then create random vector
2014-10-02 03:02:34 +00:00
static objptridx_t create_homing_missile ( const vobjptridx_t objp , const objptridx_t goal_obj , enum weapon_type_t objtype , int make_sound )
2006-03-20 17:12:09 +00:00
{
vms_vector vector_to_goal ;
//vms_vector goal_pos;
2013-12-26 22:21:16 +00:00
if ( goal_obj = = object_none ) {
2014-10-02 03:02:38 +00:00
make_random_vector ( vector_to_goal ) ;
2006-03-20 17:12:09 +00:00
} else {
2014-10-01 02:28:41 +00:00
vm_vec_normalized_dir_quick ( vector_to_goal , goal_obj - > pos , objp - > pos ) ;
2014-11-04 01:33:45 +00:00
const auto random_vector = make_random_vector ( ) ;
2014-09-28 21:43:14 +00:00
vm_vec_scale_add2 ( vector_to_goal , random_vector , F1_0 / 4 ) ;
2014-09-28 21:11:04 +00:00
vm_vec_normalize_quick ( vector_to_goal ) ;
2010-06-15 16:24:56 +00:00
}
2006-03-20 17:12:09 +00:00
// Create a vector towards the goal, then add some noise to it.
2014-10-26 21:37:27 +00:00
auto objnum = Laser_create_new ( vector_to_goal , objp - > pos , objp - > segnum , objp , objtype , make_sound ) ;
2013-12-26 22:21:16 +00:00
if ( objnum = = object_none )
return objnum ;
2006-03-20 17:12:09 +00:00
// Fixed to make sure the right person gets credit for the kill
// Objects[objnum].ctype.laser_info.parent_num = objp->ctype.laser_info.parent_num;
// Objects[objnum].ctype.laser_info.parent_type = objp->ctype.laser_info.parent_type;
// Objects[objnum].ctype.laser_info.parent_signature = objp->ctype.laser_info.parent_signature;
2014-08-23 23:53:56 +00:00
objnum - > ctype . laser_info . track_goal = goal_obj ;
2006-03-20 17:12:09 +00:00
return objnum ;
}
//-----------------------------------------------------------------------------
// Create the children of a smart bomb, which is a bunch of homing missiles.
2014-10-02 03:02:34 +00:00
void create_smart_children ( const vobjptridx_t objp , int num_smart_children )
2006-03-20 17:12:09 +00:00
{
2012-05-08 23:22:31 +00:00
int parent_type , parent_num ;
2013-12-26 22:21:16 +00:00
int numobjs = 0 ;
2013-12-29 04:28:07 +00:00
objnum_t objlist [ MAX_OBJDISTS ] ;
2013-10-08 03:20:09 +00:00
enum weapon_type_t blob_id ;
2006-03-20 17:12:09 +00:00
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
parent_type = objp - > ctype . laser_info . parent_type ;
parent_num = objp - > ctype . laser_info . parent_num ;
2013-10-07 23:52:33 +00:00
if ( get_weapon_id ( objp ) = = SMART_ID )
2013-03-03 01:03:33 +00:00
# elif defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
if ( objp - > type = = OBJ_WEAPON ) {
parent_type = objp - > ctype . laser_info . parent_type ;
parent_num = objp - > ctype . laser_info . parent_num ;
} else if ( objp - > type = = OBJ_ROBOT ) {
parent_type = OBJ_ROBOT ;
2013-12-24 04:53:59 +00:00
parent_num = objp ;
2006-03-20 17:12:09 +00:00
} else {
Int3 ( ) ; // Hey, what kind of object is this!?
parent_type = 0 ;
parent_num = 0 ;
}
2011-09-26 16:58:12 +00:00
# ifndef NDEBUG
2006-03-20 17:12:09 +00:00
if ( ( objp - > type = = OBJ_WEAPON ) & & ( ( objp - > id = = SMART_ID ) | | ( objp - > id = = SUPERPROX_ID ) | | ( objp - > id = = ROBOT_SUPERPROX_ID ) | | ( objp - > id = = EARTHSHAKER_ID ) ) )
Assert ( Weapon_info [ objp - > id ] . children ! = - 1 ) ;
2011-09-26 16:58:12 +00:00
# endif
if ( objp - > type = = OBJ_WEAPON & & objp - > id = = EARTHSHAKER_ID )
2012-05-27 22:08:09 +00:00
blast_nearby_glass ( objp , Weapon_info [ EARTHSHAKER_ID ] . strength [ Difficulty_level ] ) ;
2006-03-20 17:12:09 +00:00
2013-03-03 01:03:33 +00:00
if ( ( ( objp - > type = = OBJ_WEAPON ) & & ( Weapon_info [ objp - > id ] . children ! = - 1 ) ) | | ( objp - > type = = OBJ_ROBOT ) )
# endif
{
2006-03-20 17:12:09 +00:00
if ( Game_mode & GM_MULTI )
d_srand ( 8321L ) ;
2014-10-12 23:05:46 +00:00
range_for ( auto objnum , highest_valid ( Objects ) )
{
2012-05-08 23:22:31 +00:00
object * curobjp = & Objects [ objnum ] ;
2006-03-20 17:12:09 +00:00
if ( ( ( ( curobjp - > type = = OBJ_ROBOT ) & & ( ! curobjp - > ctype . ai_info . CLOAKED ) ) | | ( curobjp - > type = = OBJ_PLAYER ) ) & & ( objnum ! = parent_num ) ) {
2012-05-08 23:22:31 +00:00
fix dist ;
2006-03-20 17:12:09 +00:00
if ( curobjp - > type = = OBJ_PLAYER )
{
if ( ( parent_type = = OBJ_PLAYER ) & & ( Game_mode & GM_MULTI_COOP ) )
continue ;
2013-10-07 23:52:33 +00:00
if ( ( Game_mode & GM_TEAM ) & & ( get_team ( get_player_id ( curobjp ) ) = = get_team ( get_player_id ( & Objects [ parent_num ] ) ) ) )
2006-03-20 17:12:09 +00:00
continue ;
2013-10-07 23:52:33 +00:00
if ( Players [ get_player_id ( curobjp ) ] . flags & PLAYER_FLAGS_CLOAKED )
2006-03-20 17:12:09 +00:00
continue ;
}
// Robot blobs can't track robots.
if ( curobjp - > type = = OBJ_ROBOT ) {
if ( parent_type = = OBJ_ROBOT )
continue ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
// Your shots won't track the buddy.
if ( parent_type = = OBJ_PLAYER )
if ( Robot_info [ curobjp - > id ] . companion )
continue ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
}
2014-10-01 02:28:41 +00:00
dist = vm_vec_dist_quick ( objp - > pos , curobjp - > pos ) ;
2006-03-20 17:12:09 +00:00
if ( dist < MAX_SMART_DISTANCE ) {
2012-05-08 23:22:31 +00:00
int oovis ;
2006-03-20 17:12:09 +00:00
oovis = object_to_object_visibility ( objp , curobjp , FQ_TRANSWALL ) ;
2014-08-23 23:53:56 +00:00
if ( oovis ) {
2006-03-20 17:12:09 +00:00
objlist [ numobjs ] = objnum ;
numobjs + + ;
if ( numobjs > = MAX_OBJDISTS ) {
numobjs = MAX_OBJDISTS ;
break ;
}
}
}
}
}
// Get type of weapon for child from parent.
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
if ( parent_type = = OBJ_PLAYER ) {
blob_id = PLAYER_SMART_HOMING_ID ;
} else {
blob_id = ( ( N_weapon_types < ROBOT_SMART_HOMING_ID ) ? ( PLAYER_SMART_HOMING_ID ) : ( ROBOT_SMART_HOMING_ID ) ) ; // NOTE: Shareware & reg 1.0 do not have their own Smart structure for bots. It was introduced in 1.4 to make Smart blobs from lvl 7 boss easier to dodge. So if we do not have this type, revert to player's Smart behaviour..,
}
# elif defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
if ( objp - > type = = OBJ_WEAPON ) {
2013-10-08 03:20:09 +00:00
blob_id = ( enum weapon_type_t ) Weapon_info [ objp - > id ] . children ;
2006-03-20 17:12:09 +00:00
Assert ( blob_id ! = - 1 ) ; // Hmm, missing data in bitmaps.tbl. Need "children=NN" parameter.
} else {
Assert ( objp - > type = = OBJ_ROBOT ) ;
blob_id = ROBOT_SMART_HOMING_ID ;
}
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
2013-12-29 04:28:07 +00:00
objnum_t last_sel_objnum = object_none ;
2014-09-26 02:42:09 +00:00
for ( int i = 0 ; i < num_smart_children ; i + + ) {
2014-08-23 23:53:56 +00:00
objptridx_t sel_objnum = object_none ;
if ( numobjs )
sel_objnum = objlist [ ( d_rand ( ) * numobjs ) > > 15 ] ;
2012-05-08 23:22:31 +00:00
if ( numobjs > 1 )
while ( sel_objnum = = last_sel_objnum )
sel_objnum = objlist [ ( d_rand ( ) * numobjs ) > > 15 ] ;
create_homing_missile ( objp , sel_objnum , blob_id , ( i = = 0 ) ? 1 : 0 ) ;
last_sel_objnum = sel_objnum ;
2006-03-20 17:12:09 +00:00
}
}
}
int Missile_gun = 0 ;
2013-08-27 23:53:03 +00:00
int Proximity_dropped = 0 ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2013-08-27 23:53:03 +00:00
int Smartmines_dropped = 0 ;
2006-03-20 17:12:09 +00:00
//give up control of the guided missile
void release_guided_missile ( int player_num )
{
if ( player_num = = Player_num )
2010-06-15 16:24:56 +00:00
{
2006-03-20 17:12:09 +00:00
if ( Guided_missile [ player_num ] = = NULL )
return ;
2010-06-15 16:24:56 +00:00
2006-03-20 17:12:09 +00:00
Missile_viewer = Guided_missile [ player_num ] ;
if ( Game_mode & GM_MULTI )
multi_send_guided_info ( Guided_missile [ Player_num ] , 1 ) ;
if ( Newdemo_state = = ND_STATE_RECORDING )
newdemo_record_guided_end ( ) ;
2010-06-15 16:24:56 +00:00
}
2006-03-20 17:12:09 +00:00
Guided_missile [ player_num ] = NULL ;
}
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
// -------------------------------------------------------------------------------------------
2010-03-31 06:19:37 +00:00
//changed on 31/3/10 by kreatordxx to distinguish between drop bomb and secondary fire
void do_missile_firing ( int drop_bomb )
2006-03-20 17:12:09 +00:00
{
int gun_flag = 0 ;
2010-03-31 06:19:37 +00:00
int bomb = which_bomb ( ) ;
int weapon = ( drop_bomb ) ? bomb : Secondary_weapon ;
2013-06-11 10:19:03 +00:00
fix fire_frame_overhead = 0 ;
2006-03-20 17:12:09 +00:00
2013-12-26 22:21:16 +00:00
Network_laser_track = object_none ;
2011-09-15 08:45:48 +00:00
2010-03-31 06:19:37 +00:00
Assert ( weapon < MAX_SECONDARY_WEAPONS ) ;
2006-03-20 17:12:09 +00:00
2013-06-11 10:19:03 +00:00
if ( GameTime64 - Next_missile_fire_time < = FrameTime ) // if firing is prolonged by FrameTime overhead, let's try to fix that.
fire_frame_overhead = GameTime64 - Next_missile_fire_time ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
if ( Guided_missile [ Player_num ] & & Guided_missile [ Player_num ] - > signature = = Guided_missile_sig [ Player_num ] ) {
release_guided_missile ( Player_num ) ;
2013-06-11 10:19:03 +00:00
Next_missile_fire_time = GameTime64 + Weapon_info [ Secondary_weapon_to_weapon_info [ weapon ] ] . fire_wait - fire_frame_overhead ;
2006-03-20 17:12:09 +00:00
return ;
}
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
2010-03-31 06:19:37 +00:00
if ( ! Player_is_dead & & ( Players [ Player_num ] . secondary_ammo [ weapon ] > 0 ) ) {
2006-03-20 17:12:09 +00:00
2013-10-08 03:20:09 +00:00
enum weapon_type_t weapon_index ;
int weapon_gun ;
2006-03-20 17:12:09 +00:00
2010-03-31 06:19:37 +00:00
Players [ Player_num ] . secondary_ammo [ weapon ] - - ;
2006-03-20 17:12:09 +00:00
2013-10-08 03:20:09 +00:00
weapon_index = ( enum weapon_type_t ) Secondary_weapon_to_weapon_info [ weapon ] ;
2006-03-20 17:12:09 +00:00
2011-02-14 21:27:07 +00:00
if ( ! cheats . rapidfire )
2013-06-11 10:19:03 +00:00
Next_missile_fire_time = GameTime64 + Weapon_info [ weapon_index ] . fire_wait - fire_frame_overhead ;
2006-03-20 17:12:09 +00:00
else
2013-06-11 10:19:03 +00:00
Next_missile_fire_time = GameTime64 + ( F1_0 / 25 ) - fire_frame_overhead ;
2006-03-20 17:12:09 +00:00
2010-03-31 06:19:37 +00:00
weapon_gun = Secondary_weapon_to_gun_num [ weapon ] ;
2006-03-20 17:12:09 +00:00
if ( weapon_gun = = 4 ) { //alternate left/right
weapon_gun + = ( gun_flag = ( Missile_gun & 1 ) ) ;
Missile_gun + + ;
}
2014-08-23 23:53:56 +00:00
auto objnum = Laser_player_fire ( ConsoleObject , weapon_index , weapon_gun , 1 , Objects [ Players [ Player_num ] . objnum ] . orient . fvec ) ;
2006-03-20 17:12:09 +00:00
2010-03-31 06:19:37 +00:00
if ( weapon = = PROXIMITY_INDEX ) {
2006-03-20 17:12:09 +00:00
if ( + + Proximity_dropped = = 4 ) {
Proximity_dropped = 0 ;
maybe_drop_net_powerup ( POW_PROXIMITY_WEAPON ) ;
}
}
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2010-03-31 06:19:37 +00:00
else if ( weapon = = SMART_MINE_INDEX ) {
2006-03-20 17:12:09 +00:00
if ( + + Smartmines_dropped = = 4 ) {
Smartmines_dropped = 0 ;
maybe_drop_net_powerup ( POW_SMART_MINE ) ;
}
}
2013-03-03 01:03:33 +00:00
# endif
2010-03-31 06:19:37 +00:00
else if ( weapon ! = CONCUSSION_INDEX )
maybe_drop_net_powerup ( Secondary_weapon_to_powerup [ weapon ] ) ;
2010-06-15 16:24:56 +00:00
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
if ( weapon = = MEGA_INDEX )
# elif defined(DXX_BUILD_DESCENT_II)
if ( weapon = = MEGA_INDEX | | weapon = = SMISSILE5_INDEX )
# endif
{
2006-03-20 17:12:09 +00:00
vms_vector force_vec ;
force_vec . x = - ( ConsoleObject - > orient . fvec . x < < 7 ) ;
force_vec . y = - ( ConsoleObject - > orient . fvec . y < < 7 ) ;
force_vec . z = - ( ConsoleObject - > orient . fvec . z < < 7 ) ;
2014-10-26 21:37:34 +00:00
phys_apply_force ( ConsoleObject , force_vec ) ;
2010-06-15 16:24:56 +00:00
2006-03-20 17:12:09 +00:00
force_vec . x = ( force_vec . x > > 4 ) + d_rand ( ) - 16384 ;
force_vec . y = ( force_vec . y > > 4 ) + d_rand ( ) - 16384 ;
force_vec . z = ( force_vec . z > > 4 ) + d_rand ( ) - 16384 ;
2014-10-26 21:37:34 +00:00
phys_apply_rot ( ConsoleObject , force_vec ) ;
2006-03-20 17:12:09 +00:00
}
2010-06-15 16:24:56 +00:00
if ( Game_mode & GM_MULTI )
2013-08-09 15:21:03 +00:00
{
2013-09-21 22:01:38 +00:00
if ( weapon_index_is_player_bomb ( weapon ) )
2013-08-09 15:21:03 +00:00
multi_send_fire ( weapon + MISSILE_ADJUST , 0 , gun_flag , 1 , Network_laser_track , objnum ) ;
else
2013-12-26 22:21:16 +00:00
multi_send_fire ( weapon + MISSILE_ADJUST , 0 , gun_flag , 1 , Network_laser_track , object_none ) ;
2013-08-09 15:21:03 +00:00
}
2006-03-20 17:12:09 +00:00
2010-03-31 06:19:37 +00:00
// don't autoselect if dropping prox and prox not current weapon
if ( ! drop_bomb | | Secondary_weapon = = bomb )
2006-03-20 17:12:09 +00:00
auto_select_weapon ( 1 ) ; //select next missile, if this one out of ammo
}
}