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"
2015-10-09 02:46:10 +00:00
# include "fwd-wall.h"
2013-12-25 23:47:30 +00:00
# include "reverse.h"
2015-09-26 18:28:49 +00:00
# include "playsave.h"
2013-12-25 23:47:30 +00:00
# include "compiler-range_for.h"
2014-10-12 23:05:46 +00:00
# include "highest_valid.h"
2015-02-14 22:48:27 +00:00
# include "partial_range.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)
2015-04-02 02:36:52 +00:00
array < object * , MAX_PLAYERS > Guided_missile ;
2015-03-22 18:49:21 +00:00
array < object_signature_t , MAX_PLAYERS > Guided_missile_sig ;
2013-03-03 01:03:33 +00:00
# endif
2013-12-29 04:28:07 +00:00
objnum_t Network_laser_track = object_none ;
2015-05-30 16:29:41 +00:00
static ubyte d_homer_tick_step = 0 ;
static fix d_homer_tick_count = 0 ;
2006-03-20 17:12:09 +00:00
2015-05-09 17:39:01 +00:00
static int Muzzle_queue_index ;
2015-12-22 04:18:50 +00:00
namespace dsx {
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-11-30 22:09:23 +00:00
void Laser_render ( const vobjptr_t 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
2014-11-30 22:09:23 +00:00
auto & wi = Weapon_info [ get_weapon_id ( obj ) ] ;
switch ( wi . 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-11-30 22:09:23 +00:00
draw_object_blob ( obj , wi . 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;
//
//}
2015-05-09 17:38:58 +00:00
static bool ignore_proximity_weapon ( const vcobjptr_t o )
{
if ( ! is_proximity_bomb_or_smart_mine ( get_weapon_id ( o ) ) )
return false ;
# if defined(DXX_BUILD_DESCENT_I)
return GameTime64 > o - > ctype . laser_info . creation_time + F1_0 * 2 ;
# elif defined(DXX_BUILD_DESCENT_II)
return GameTime64 > o - > ctype . laser_info . creation_time + F1_0 * 4 ;
# endif
}
# if defined(DXX_BUILD_DESCENT_I)
static bool ignore_phoenix_weapon ( vcobjptr_t )
{
return false ;
}
static bool ignore_guided_missile_weapon ( vcobjptr_t )
{
return false ;
}
# elif defined(DXX_BUILD_DESCENT_II)
static bool ignore_phoenix_weapon ( const vcobjptr_t o )
{
2015-12-03 03:26:49 +00:00
return get_weapon_id ( o ) = = weapon_id_type : : PHOENIX_ID & & GameTime64 > o - > ctype . laser_info . creation_time + F1_0 / 4 ;
2015-05-09 17:38:58 +00:00
}
static bool ignore_guided_missile_weapon ( const vcobjptr_t o )
{
2015-12-03 03:26:49 +00:00
return get_weapon_id ( o ) = = weapon_id_type : : GUIDEDMISS_ID & & GameTime64 > o - > ctype . laser_info . creation_time + F1_0 * 2 ;
2015-05-09 17:38:58 +00:00
}
# endif
2006-03-20 17:12:09 +00:00
// 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.
2015-05-09 17:38:58 +00:00
bool laser_are_related ( const vcobjptridx_t o1 , const vcobjptridx_t o2 )
2006-03-20 17:12:09 +00:00
{
// See if o2 is the parent of o1
2015-05-09 17:38:58 +00:00
if ( o1 - > type = = OBJ_WEAPON )
if ( o1 - > ctype . laser_info . parent_num = = o2 & & o1 - > ctype . laser_info . parent_signature = = o2 - > signature )
2006-03-20 17:12:09 +00:00
{
// 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
2015-05-09 17:38:58 +00:00
if ( ignore_proximity_weapon ( o1 ) | | ignore_guided_missile_weapon ( o1 ) | | ignore_phoenix_weapon ( o1 ) )
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
2015-05-09 17:38:58 +00:00
if ( o2 - > type = = OBJ_WEAPON )
2006-03-20 17:12:09 +00:00
{
2015-05-09 17:38:58 +00:00
if ( o2 - > ctype . laser_info . parent_num = = o1 & & o2 - > ctype . laser_info . parent_signature = = o1 - > signature )
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
// 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
2015-05-09 17:38:58 +00:00
if ( ignore_proximity_weapon ( o2 ) | | ignore_guided_missile_weapon ( o2 ) | | ignore_phoenix_weapon ( o2 ) )
2015-05-09 17:38:58 +00:00
{
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
2015-05-09 17:38:58 +00:00
if ( o1 - > type ! = OBJ_WEAPON | | 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.
2015-05-09 17:38:58 +00:00
if ( o1 - > ctype . laser_info . parent_signature = = o2 - > ctype . laser_info . parent_signature )
2006-03-20 17:12:09 +00:00
{
2015-05-09 17:38:58 +00:00
if ( is_proximity_bomb_or_smart_mine ( get_weapon_id ( o1 ) ) | | is_proximity_bomb_or_smart_mine ( get_weapon_id ( 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)
2015-05-09 17:38:58 +00:00
if ( ! ( GameTime64 > o1 - > ctype . laser_info . creation_time + F1_0 / 2 | | GameTime64 > 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.
2015-11-27 03:56:13 +00:00
if ( ! (
2015-12-03 03:26:49 +00:00
get_weapon_id ( o1 ) = = weapon_id_type : : ROBOT_SUPERPROX_ID | | get_weapon_id ( o2 ) = = weapon_id_type : : ROBOT_SUPERPROX_ID | |
get_weapon_id ( o1 ) = = weapon_id_type : : PROXIMITY_ID | | get_weapon_id ( o2 ) = = weapon_id_type : : PROXIMITY_ID | |
get_weapon_id ( o1 ) = = weapon_id_type : : SUPERPROX_ID | | get_weapon_id ( o2 ) = = weapon_id_type : : SUPERPROX_ID | |
get_weapon_id ( o1 ) = = weapon_id_type : : PMINE_ID | | get_weapon_id ( o2 ) = = weapon_id_type : : PMINE_ID
2015-11-27 03:56:13 +00:00
) )
2013-08-27 23:53:03 +00:00
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
}
2015-12-22 04:18:50 +00:00
}
namespace dcx {
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 ;
}
2015-12-22 04:18:50 +00:00
}
namespace dsx {
2006-03-20 17:12:09 +00:00
//creates a weapon object
2014-11-23 04:58:45 +00:00
static objptridx_t create_weapon_object ( int weapon_type , const vsegptridx_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.
2015-03-12 02:21:19 +00:00
constexpr vm_distance MAX_OMEGA_DIST { MAX_OMEGA_BLOBS * DESIRED_OMEGA_DIST } ; // Maximum extent of lightning blobs.
constexpr vm_distance_squared MAX_OMEGA_DIST_SQUARED { MAX_OMEGA_DIST * 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.
2015-03-12 02:21:19 +00:00
constexpr vm_distance 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
{
2015-12-03 03:26:49 +00:00
if ( weapon - > type ! = OBJ_WEAPON | | get_weapon_id ( weapon ) ! = weapon_id_type : : OMEGA_ID )
2011-04-16 14:44:36 +00:00
return 0 ;
2015-03-22 18:49:21 +00:00
const auto parent_sig = weapon - > ctype . laser_info . parent_signature ;
const auto parent_num = weapon - > ctype . laser_info . parent_num ;
2015-12-03 03:26:49 +00:00
const auto & & objp = vcobjptr ( parent_num ) ;
if ( objp - > signature = = parent_sig )
if ( vm_vec_dist2 ( weapon - > pos , objp - > 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
{
2015-12-03 03:26:49 +00:00
if ( weapon - > type ! = OBJ_WEAPON | | get_weapon_id ( weapon ) ! = weapon_id_type : : OMEGA_ID )
2011-04-09 23:48:15 +00:00
return 1 ;
if ( ! ( Game_mode & GM_MULTI ) )
return 1 ;
2015-03-22 18:49:21 +00:00
const auto parent_sig = weapon - > ctype . laser_info . parent_signature ;
const auto parent_num = weapon - > ctype . laser_info . parent_num ;
2015-12-03 03:26:49 +00:00
const auto & & objp = vcobjptr ( parent_num ) ;
if ( objp - > signature = = parent_sig )
if ( vm_vec_dist2 ( objp - > 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-11-26 03:39:21 +00:00
static void create_omega_blobs ( const segptridx_t 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-08 03:24:48 +00:00
objptridx_t last_created_objnum = object_none ;
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
2015-02-14 22:48:27 +00:00
unsigned num_omega_blobs = 0 ;
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 ;
}
}
2015-08-13 03:15:53 +00:00
vms_vector omega_delta_vector , blob_pos ;
2006-03-20 17:12:09 +00:00
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 ;
2014-11-26 03:39:21 +00:00
auto last_segnum = firing_segnum ;
2006-03-20 17:12:09 +00:00
// If nearby, don't perturb vector. If not nearby, start halfway out.
if ( dist_to_goal < MIN_OMEGA_DIST * 4 ) {
2015-02-14 22:48:27 +00:00
range_for ( auto & i , partial_range ( perturb_array , num_omega_blobs ) )
i = 0 ;
2006-03-20 17:12:09 +00:00
} 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 ;
2015-12-03 03:26:49 +00:00
auto blob_objnum = obj_create ( OBJ_WEAPON , weapon_id_type : : 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
2015-11-27 03:56:13 +00:00
const auto & weapon_info = Weapon_info [ get_weapon_id ( objp ) ] ;
objp - > size = weapon_info . blob_size ;
2006-03-20 17:12:09 +00:00
2015-11-27 03:56:13 +00:00
objp - > shields = fixmul ( OMEGA_DAMAGE_SCALE * OMEGA_BASE_TIME , weapon_info . 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 ) {
2015-12-03 03:26:49 +00:00
vm_vec_scale ( last_created_objnum - > mtype . phys_info . velocity , Weapon_info [ weapon_id_type : : 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 ;
2015-04-19 04:18:50 +00:00
if ( ! player_has_primary_weapon ( primary_weapon_index_t : : OMEGA_INDEX ) . has_weapon ( ) )
2006-03-20 17:12:09 +00:00
return ;
2015-12-15 04:09:35 +00:00
if ( Player_dead_state ! = player_dead_state : : no )
2006-03-20 17:12:09 +00:00
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 ;
2015-10-30 02:52:55 +00:00
if ( get_local_player_energy ( ) )
2015-07-25 23:10:46 +00:00
{
2006-03-20 17:12:09 +00:00
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 ) ;
2015-10-30 02:52:55 +00:00
get_local_player_energy ( ) - = energy_used ;
if ( get_local_player_energy ( ) < 0 )
get_local_player_energy ( ) = 0 ;
2006-03-20 17:12:09 +00:00
}
}
// -- 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
{
vms_vector goal_pos ;
2015-11-27 03:56:13 +00:00
const auto pnum = get_player_id ( parent_objp ) ;
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.
2015-11-07 21:55:58 +00:00
if ( ! ( ( Omega_charge > = MIN_OMEGA_CHARGE ) | | ( Omega_charge & & ! get_local_player_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 ;
2015-12-03 03:26:49 +00:00
weapon_objp - > ctype . laser_info . parent_signature = vcobjptr ( Players [ pnum ] . objnum ) - > signature ;
2006-03-20 17:12:09 +00:00
2015-12-03 03:26:49 +00:00
const auto & & lock_objnum = find_homing_object ( firing_pos , weapon_objp ) ;
2006-03-20 17:12:09 +00:00
2016-01-09 16:38:13 +00:00
const auto & & firing_segnum = find_point_seg ( firing_pos , vsegptridx ( parent_objp - > segnum ) ) ;
2006-03-20 17:12:09 +00:00
// Play sound.
2015-11-27 03:56:13 +00:00
{
const auto flash_sound = Weapon_info [ get_weapon_id ( weapon_objp ) ] . flash_sound ;
2011-04-09 23:48:15 +00:00
if ( parent_objp = = Viewer )
2015-11-27 03:56:13 +00:00
digi_play_sample ( flash_sound , F1_0 ) ;
2011-04-09 23:48:15 +00:00
else
2016-01-09 16:38:14 +00:00
digi_link_sound_to_pos ( flash_sound , vsegptridx ( weapon_objp - > segnum ) , 0 , weapon_objp - > pos , 0 , F1_0 ) ;
2015-11-27 03:56:13 +00:00
}
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 ;
2015-02-05 03:03:51 +00:00
fq . ignore_obj_list . first = nullptr ;
2006-03-20 17:12:09 +00:00
fq . flags = FQ_IGNORE_POWERUPS | FQ_TRANSPOINT | FQ_CHECK_OBJS ; //what about trans walls???
2015-01-20 02:46:42 +00:00
fate = find_vector_intersection ( fq , hit_data ) ;
2006-03-20 17:12:09 +00:00
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
2015-12-03 03:26:49 +00:00
goal_pos = lock_objnum - > pos ;
2006-03-20 17:12:09 +00:00
// 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
2015-12-03 03:26:48 +00:00
static inline int is_laser_weapon_type ( weapon_id_type weapon_type )
2013-10-09 01:54:12 +00:00
{
# if defined(DXX_BUILD_DESCENT_II)
2015-12-03 03:26:49 +00:00
if ( weapon_type = = weapon_id_type : : LASER_ID_L5 | | weapon_type = = weapon_id_type : : LASER_ID_L6 )
2013-10-09 01:54:12 +00:00
return 1 ;
# endif
2015-12-03 03:26:49 +00:00
return ( weapon_type = = weapon_id_type : : LASER_ID_L1 | | weapon_type = = weapon_id_type : : LASER_ID_L2 | | weapon_type = = weapon_id_type : : LASER_ID_L3 | | weapon_type = = weapon_id_type : : LASER_ID_L4 ) ;
2013-10-09 01:54:12 +00:00
}
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.
2016-01-09 16:38:14 +00:00
objptridx_t Laser_create_new ( const vms_vector & direction , const vms_vector & position , const vsegptridx_t segnum , const vobjptridx_t parent , weapon_id_type 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 )
{
2015-12-03 03:26:49 +00:00
case weapon_id_type : : LASER_ID_L1 :
case weapon_id_type : : LASER_ID_L2 :
case weapon_id_type : : LASER_ID_L3 :
case weapon_id_type : : LASER_ID_L4 :
case weapon_id_type : : CLS1_DRONE_FIRE :
case weapon_id_type : : CONTROLCEN_WEAPON_NUM :
case weapon_id_type : : CONCUSSION_ID :
case weapon_id_type : : FLARE_ID :
case weapon_id_type : : CLS2_DRONE_LASER :
case weapon_id_type : : VULCAN_ID :
2013-10-09 01:54:12 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2015-12-03 03:26:49 +00:00
case weapon_id_type : : SPREADFIRE_ID :
2013-10-09 01:54:12 +00:00
# endif
2015-12-03 03:26:49 +00:00
case weapon_id_type : : PLASMA_ID :
case weapon_id_type : : FUSION_ID :
case weapon_id_type : : HOMING_ID :
case weapon_id_type : : PROXIMITY_ID :
case weapon_id_type : : SMART_ID :
case weapon_id_type : : MEGA_ID :
case weapon_id_type : : PLAYER_SMART_HOMING_ID :
2013-10-09 01:54:12 +00:00
# if defined(DXX_BUILD_DESCENT_I)
2015-12-03 03:26:49 +00:00
case weapon_id_type : : SPREADFIRE_ID :
2013-10-09 01:54:12 +00:00
# endif
2015-12-03 03:26:49 +00:00
case weapon_id_type : : SUPER_MECH_MISS :
case weapon_id_type : : REGULAR_MECH_MISS :
case weapon_id_type : : SILENT_SPREADFIRE_ID :
case weapon_id_type : : MEDIUM_LIFTER_LASER :
case weapon_id_type : : SMALL_HULK_FIRE :
case weapon_id_type : : HEAVY_DRILLER_PLASMA :
case weapon_id_type : : SPIDER_ROBOT_FIRE :
case weapon_id_type : : ROBOT_MEGA_ID :
case weapon_id_type : : ROBOT_SMART_HOMING_ID :
2013-10-09 01:54:12 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2015-12-03 03:26:49 +00:00
case weapon_id_type : : LASER_ID_L5 :
case weapon_id_type : : LASER_ID_L6 :
case weapon_id_type : : GAUSS_ID :
case weapon_id_type : : HELIX_ID :
case weapon_id_type : : PHOENIX_ID :
case weapon_id_type : : OMEGA_ID :
case weapon_id_type : : FLASH_ID :
case weapon_id_type : : GUIDEDMISS_ID :
case weapon_id_type : : SUPERPROX_ID :
case weapon_id_type : : MERCURY_ID :
case weapon_id_type : : EARTHSHAKER_ID :
case weapon_id_type : : SMELTER_PHOENIX_ID :
case weapon_id_type : : SMART_MINE_HOMING_ID :
case weapon_id_type : : ROBOT_SMART_MINE_HOMING_ID :
case weapon_id_type : : ROBOT_SUPERPROX_ID :
case weapon_id_type : : EARTHSHAKER_MEGA_ID :
case weapon_id_type : : ROBOT_EARTHSHAKER_ID :
case weapon_id_type : : PMINE_ID :
case weapon_id_type : : ROBOT_26_WEAPON_46_ID :
case weapon_id_type : : ROBOT_27_WEAPON_52_ID :
case weapon_id_type : : ROBOT_28_WEAPON_42_ID :
case weapon_id_type : : ROBOT_29_WEAPON_20_ID :
case weapon_id_type : : ROBOT_30_WEAPON_48_ID :
case weapon_id_type : : ROBOT_36_WEAPON_41_ID :
case weapon_id_type : : ROBOT_39_WEAPON_43_ID :
case weapon_id_type : : ROBOT_43_WEAPON_55_ID :
case weapon_id_type : : ROBOT_45_WEAPON_45_ID :
case weapon_id_type : : ROBOT_50_WEAPON_50_ID :
case weapon_id_type : : ROBOT_62_WEAPON_60_ID :
case weapon_id_type : : ROBOT_47_WEAPON_57_ID :
case weapon_id_type : : ROBOT_62_WEAPON_61_ID :
case weapon_id_type : : 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
}
2015-11-27 03:56:13 +00:00
const auto & weapon_info = Weapon_info [ weapon_type ] ;
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.
2015-12-03 03:26:49 +00:00
if ( weapon_type = = weapon_id_type : : OMEGA_ID ) {
2006-03-20 17:12:09 +00:00
// 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
2015-11-27 03:56:13 +00:00
if ( weapon_info . flash_vclip > - 1 )
2016-01-09 16:38:14 +00:00
object_create_muzzle_flash ( vsegptridx ( obj - > segnum ) , obj - > pos , weapon_info . flash_size , weapon_info . 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 ) {
2015-12-03 03:26:49 +00:00
if ( weapon_type = = weapon_id_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)
2015-04-08 13:05:03 +00:00
if ( ( Game_mode & GM_MULTI ) & & ! ( Game_mode & GM_MULTI_COOP ) )
2013-03-03 01:03:33 +00:00
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.
2015-04-08 13:05:03 +00:00
if ( ( Game_mode & GM_MULTI ) & & ! ( Game_mode & GM_MULTI_COOP ) )
2013-03-03 01:03:33 +00:00
obj - > ctype . laser_info . multiplier / = 2 ;
# endif
2013-08-27 23:53:03 +00:00
}
2015-11-07 21:55:59 +00:00
else if ( is_laser_weapon_type ( weapon_type ) & & ( parent - > ctype . player_info . powerup_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)
2015-12-03 03:26:49 +00:00
else if ( weapon_type = = weapon_id_type : : GUIDEDMISS_ID ) {
2015-07-25 23:10:46 +00:00
if ( parent = = get_local_player ( ) . objnum ) {
2006-03-20 17:12:09 +00:00
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)
2015-12-03 03:26:49 +00:00
if ( ( weapon_type = = weapon_id_type : : PLAYER_SMART_HOMING_ID ) | | ( weapon_type = = weapon_id_type : : ROBOT_SMART_HOMING_ID ) )
2013-03-03 01:03:33 +00:00
# elif defined(DXX_BUILD_DESCENT_II)
2015-12-03 03:26:49 +00:00
if ( ( weapon_type = = weapon_id_type : : PLAYER_SMART_HOMING_ID ) | | ( weapon_type = = weapon_id_type : : SMART_MINE_HOMING_ID ) | | ( weapon_type = = weapon_id_type : : ROBOT_SMART_HOMING_ID ) | | ( weapon_type = = weapon_id_type : : ROBOT_SMART_MINE_HOMING_ID ) | | ( weapon_type = = weapon_id_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 ;
2015-11-27 03:56:13 +00:00
if ( weapon_info . render_type = = WEAPON_RENDER_POLYMODEL )
2006-03-20 17:12:09 +00:00
laser_length = Polygon_models [ obj - > rtype . pobj_info . model_num ] . rad * 2 ;
2015-12-03 03:26:49 +00:00
if ( weapon_type = = weapon_id_type : : FLARE_ID )
2006-03-20 17:12:09 +00:00
obj - > mtype . phys_info . flags | = PF_STICK ; //this obj sticks to walls
2010-06-15 16:24:56 +00:00
2015-11-27 03:56:13 +00:00
obj - > shields = weapon_info . strength [ Difficulty_level ] ;
2010-06-15 16:24:56 +00:00
2006-03-20 17:12:09 +00:00
// Fill in laser-specific data
2015-11-27 03:56:13 +00:00
obj - > lifeleft = weapon_info . 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 ) {
2014-11-20 03:00:41 +00:00
auto highest_parent = parent ;
2006-03-20 17:12:09 +00:00
int count ;
count = 0 ;
2014-11-20 03:00:41 +00:00
while ( ( count + + < 10 ) & & ( highest_parent - > type = = OBJ_WEAPON ) ) {
auto next_parent = highest_parent - > ctype . laser_info . parent_num ;
2015-12-22 04:18:51 +00:00
const auto & & parent_objp = parent . absolute_sibling ( next_parent ) ;
2015-12-03 03:26:49 +00:00
if ( parent_objp - > signature ! = highest_parent - > ctype . laser_info . parent_signature )
2006-03-20 17:12:09 +00:00
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 ;
}
2015-12-03 03:26:49 +00:00
highest_parent = parent_objp ;
2006-03-20 17:12:09 +00:00
obj - > ctype . laser_info . parent_num = highest_parent ;
2014-11-20 03:00:41 +00:00
obj - > ctype . laser_info . parent_type = highest_parent - > type ;
obj - > ctype . laser_info . parent_signature = highest_parent - > signature ;
2006-03-20 17:12:09 +00:00
}
}
// 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.
2015-11-27 03:56:13 +00:00
if ( ( obj - > render_type = = RT_POLYOBJ ) | | ( weapon_info . 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
2015-11-27 03:56:13 +00:00
if ( weapon_info . flash_vclip > - 1 )
2016-01-09 16:38:14 +00:00
object_create_muzzle_flash ( segnum . absolute_sibling ( obj - > segnum ) , obj - > pos , weapon_info . flash_size , weapon_info . flash_vclip ) ;
2006-03-20 17:12:09 +00:00
}
volume = F1_0 ;
2015-11-27 03:56:13 +00:00
if ( weapon_info . flash_sound > - 1 )
{
2006-03-20 17:12:09 +00:00
if ( make_sound ) {
2016-01-09 16:38:14 +00:00
if ( parent = = Viewer )
{
2015-12-03 03:26:49 +00:00
if ( weapon_type = = weapon_id_type : : VULCAN_ID ) // Make your own vulcan gun 1/2 as loud.
2006-03-20 17:12:09 +00:00
volume = F1_0 / 2 ;
2015-11-27 03:56:13 +00:00
digi_play_sample ( weapon_info . flash_sound , volume ) ;
2006-03-20 17:12:09 +00:00
} else {
2016-01-09 16:38:14 +00:00
digi_link_sound_to_pos ( weapon_info . flash_sound , segnum . absolute_sibling ( 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)
2015-12-03 03:26:49 +00:00
if ( parent - > type ! = OBJ_WEAPON & & weapon_info . render_type ! = WEAPON_RENDER_NONE & & weapon_type ! = weapon_id_type : : FLARE_ID )
2013-03-03 01:03:33 +00:00
# elif defined(DXX_BUILD_DESCENT_II)
2015-12-03 03:26:49 +00:00
if ( parent - > type = = OBJ_PLAYER & & weapon_info . render_type ! = WEAPON_RENDER_NONE & & weapon_type ! = weapon_id_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 ) ) ;
2016-01-09 16:38:13 +00:00
const auto & & end_segnum = find_point_seg ( end_pos , vsegptridx ( 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 ;
2015-11-27 03:56:13 +00:00
weapon_speed = weapon_info . speed [ Difficulty_level ] ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2015-11-27 03:56:13 +00:00
if ( weapon_info . speedvar ! = 128 )
{
2006-03-20 17:12:09 +00:00
fix randval ;
// Get a scale factor between speedvar% and 1.0.
2015-11-27 03:56:13 +00:00
randval = F1_0 - ( ( d_rand ( ) * weapon_info . speedvar ) > > 6 ) ;
2006-03-20 17:12:09 +00:00
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)
2015-12-03 03:26:49 +00:00
if ( weapon_type = = weapon_id_type : : PLAYER_SMART_HOMING_ID | | weapon_type = = weapon_id_type : : ROBOT_SMART_HOMING_ID )
2013-03-03 01:03:33 +00:00
# elif defined(DXX_BUILD_DESCENT_II)
2015-12-03 03:26:49 +00:00
if ( weapon_type = = weapon_id_type : : PLAYER_SMART_HOMING_ID | | weapon_type = = weapon_id_type : : SMART_MINE_HOMING_ID | | weapon_type = = weapon_id_type : : ROBOT_SMART_HOMING_ID | | weapon_type = = weapon_id_type : : ROBOT_SMART_MINE_HOMING_ID | | weapon_type = = weapon_id_type : : EARTHSHAKER_MEGA_ID )
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
weapon_speed / = 4 ;
2015-11-27 03:56:13 +00:00
if ( weapon_info . thrust )
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
2015-11-27 03:56:13 +00:00
if ( weapon_info . thrust )
{
2006-03-20 17:12:09 +00:00
obj - > mtype . phys_info . thrust = obj - > mtype . phys_info . velocity ;
2015-11-27 03:56:13 +00:00
vm_vec_scale ( obj - > mtype . phys_info . thrust , fixdiv ( weapon_info . thrust , weapon_speed + parent_speed ) ) ;
2006-03-20 17:12:09 +00:00
}
2015-12-03 03:26:49 +00:00
if ( obj - > type = = OBJ_WEAPON & & weapon_type = = weapon_id_type : : 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.
2015-12-03 03:26:48 +00:00
objptridx_t Laser_create_new_easy ( const vms_vector & direction , const vms_vector & position , const vobjptridx_t parent , weapon_id_type 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 ;
2015-02-05 03:03:51 +00:00
fq . ignore_obj_list . first = nullptr ;
2006-03-20 17:12:09 +00:00
fq . flags = FQ_TRANSWALL | FQ_CHECK_OBJS ; //what about trans walls???
2015-01-20 02:46:42 +00:00
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
}
2016-01-09 16:38:14 +00:00
return Laser_create_new ( direction , hit_data . hit_pnt , vsegptridx ( hit_data . hit_seg ) , parent , weapon_type , make_sound ) ;
2006-03-20 17:12:09 +00:00
}
2015-12-22 04:18:50 +00:00
}
namespace dcx {
2015-04-02 02:36:52 +00:00
array < muzzle_info , MUZZLE_QUEUE_MAX > Muzzle_data ;
2006-03-20 17:12:09 +00:00
2015-12-22 04:18:50 +00:00
}
namespace dsx {
2006-03-20 17:12:09 +00:00
// -----------------------------------------------------------------------------------------------------------
// 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 ;
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 ;
2015-02-05 03:03:51 +00:00
fq . ignore_obj_list . first = nullptr ;
2006-03-20 17:12:09 +00:00
fq . flags = trans_type ;
2015-04-24 01:39:35 +00:00
switch ( const auto fate = find_vector_intersection ( fq , hit_data ) )
{
case HIT_NONE :
return 1 ;
case HIT_WALL :
return 0 ;
default :
con_printf ( CON_VERBOSE , " object_to_object_visibility: fate=%u for object %hu{%hu/%i,%i,%i} to {%i,%i,%i} " , fate , static_cast < vcobjptridx_t : : integral_type > ( obj1 ) , obj1 - > segnum , obj1 - > pos . x , obj1 - > pos . y , obj1 - > pos . z , obj2 - > pos . x , obj2 - > pos . y , obj2 - > pos . z ) ;
// Int3(); // Contact Mike: Oops, what happened? What is fate?
2006-03-20 17:12:09 +00:00
// 2 = hit object (impossible), 3 = bad starting point (bad)
2015-04-24 01:39:35 +00:00
break ;
}
2006-03-20 17:12:09 +00:00
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.
2015-10-30 02:52:56 +00:00
if ( ( objp = = get_local_player ( ) . objnum ) & & ( get_local_player_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??
2015-11-27 03:56:13 +00:00
# ifndef NDEBUG
const auto tracker_id = get_weapon_id ( tracker ) ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2015-12-03 03:26:49 +00:00
if ( tracker_id ! = weapon_id_type : : OMEGA_ID )
2015-11-27 03:56:13 +00:00
# endif
assert ( Weapon_info [ tracker_id ] . homing_flag ) ;
2013-03-03 01:03:33 +00:00
# endif
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 ;
2015-11-27 03:56:13 +00:00
const auto tracker_id = get_weapon_id ( tracker ) ;
2014-08-08 03:02:32 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2015-12-03 03:26:49 +00:00
if ( tracker_id ! = weapon_id_type : : 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
{
2015-11-27 03:56:13 +00:00
if ( ! Weapon_info [ tracker_id ] . homing_flag )
2014-08-08 03:02:32 +00:00
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 ;
2015-03-12 02:21:19 +00:00
vm_distance_squared 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)
2015-12-03 03:26:49 +00:00
if ( tracker_id = = weapon_id_type : : OMEGA_ID )
2015-11-27 03:56:13 +00:00
{
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 ;
2015-12-22 04:18:51 +00:00
range_for ( const auto & & curobjp , highest_valid ( vobjptridx ) )
2014-10-12 23:05:46 +00:00
{
2006-03-20 17:12:09 +00:00
int is_proximity = 0 ;
2014-10-10 02:41:51 +00:00
fix dot ;
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 ;
}
2015-12-22 04:18:51 +00:00
if ( curobjp = = tracker - > ctype . laser_info . parent_num ) // Don't track shooter
2006-03-20 17:12:09 +00:00
continue ;
// Don't track cloaked players.
if ( curobjp - > type = = OBJ_PLAYER )
{
2015-11-07 21:55:59 +00:00
if ( curobjp - > ctype . player_info . powerup_flags & PLAYER_FLAGS_CLOAKED )
2006-03-20 17:12:09 +00:00
continue ;
// Don't track teammates in team games
2015-07-12 01:04:20 +00:00
if ( Game_mode & GM_TEAM )
{
const auto & & objparent = vcobjptr ( tracker - > ctype . laser_info . parent_num ) ;
if ( objparent - > type = = OBJ_PLAYER & & get_team ( get_player_id ( curobjp ) ) = = get_team ( get_player_id ( objparent ) ) )
continue ;
}
2006-03-20 17:12:09 +00:00
}
// 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.
2015-11-27 03:56:13 +00:00
if ( Robot_info [ get_robot_id ( curobjp ) ] . companion )
2006-03-20 17:12:09 +00:00
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 ;
}
2015-05-30 16:29:41 +00:00
// Similar to calc_d_tick but made just for the homers.
// Causes d_homer_tick_step to be true in intervals dictated by HOMING_TURN_TIME
// and increments d_homer_tick_count accordingly
void calc_d_homer_tick ( )
{
static fix timer = 0 ;
2015-05-30 16:53:49 +00:00
auto t = timer + FrameTime ;
d_homer_tick_step = t > = HOMING_TURN_TIME ;
if ( d_homer_tick_step )
2015-05-30 16:29:41 +00:00
{
d_homer_tick_count + + ;
if ( d_homer_tick_count > F1_0 )
d_homer_tick_count = 0 ;
2015-05-30 16:53:49 +00:00
t - = HOMING_TURN_TIME ;
2015-05-30 16:29:41 +00:00
// Don't let slowdowns have a lasting impact; allow you to build up at most 3 frames worth
2015-05-30 16:53:49 +00:00
if ( t > HOMING_TURN_TIME * 3 )
t = HOMING_TURN_TIME * 3 ;
2015-09-10 12:11:30 +00:00
2015-11-14 18:17:22 +00:00
get_local_plrobj ( ) . ctype . player_info . homing_object_dist = - 1 ; // Assume not being tracked. Laser_do_weapon_sequence modifies this. Let's do this here since the homers do not track every frame, we may not want to reset this ever frame.
2015-05-30 16:53:49 +00:00
}
timer = t ;
2015-05-30 16:29:41 +00:00
}
2006-03-20 17:12:09 +00:00
// ------------------------------------------------------------------------------------------------------------
// 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.
2015-05-30 16:29:41 +00:00
static objptridx_t track_track_goal ( const objptridx_t track_goal , const vobjptridx_t tracker , fix * dot , fix tick_count )
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 ;
2015-05-30 16:29:41 +00:00
} else if ( ( ( ( tracker ) ^ tick_count ) % 4 ) = = 0 )
2010-06-15 16:24:56 +00:00
{
2015-11-15 22:30:41 +00:00
int goal_type , goal2_type ;
2006-03-20 17:12:09 +00:00
// If player fired missile, then search for an object, if not, then give up.
2015-11-15 22:30:41 +00:00
if ( vcobjptr ( tracker - > ctype . laser_info . parent_num ) - > type = = OBJ_PLAYER )
{
2006-03-20 17:12:09 +00:00
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 )
2015-11-15 22:30:41 +00:00
goal_type = OBJ_ROBOT , goal2_type = - 1 ;
else
{
goal_type = OBJ_PLAYER ;
goal2_type = ( Game_mode & GM_MULTI_ROBOTS )
? OBJ_ROBOT // Not cooperative, if robots, track either robot or player
: - 1 ; // Not cooperative and no robots, track only a player
}
2006-03-20 17:12:09 +00:00
}
else
2015-11-15 22:30:41 +00:00
goal_type = OBJ_PLAYER , goal2_type = OBJ_ROBOT ;
2010-06-15 16:24:56 +00:00
}
else
2006-03-20 17:12:09 +00:00
{
2015-11-15 22:30:41 +00:00
goal_type = vcobjptr ( tracker - > ctype . laser_info . track_goal ) - > type ;
2006-03-20 17:12:09 +00:00
if ( ( goal_type = = OBJ_PLAYER ) | | ( goal_type = = OBJ_ROBOT ) )
2015-11-15 22:30:41 +00:00
goal2_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 {
2015-11-15 22:30:41 +00:00
goal2_type = - 1 ;
2006-03-20 17:12:09 +00:00
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 )
2015-11-15 22:30:41 +00:00
goal_type = OBJ_PLAYER ;
2006-03-20 17:12:09 +00:00
else {
2015-11-15 22:30:41 +00:00
goal_type = vcobjptr ( tracker - > ctype . laser_info . track_goal ) - > type ;
2015-09-10 11:19:34 +00:00
// HACK: Dead players can be identified as valid track_goal, making it impossible to track players that are alive (further down the object list) in a multibot match. Re-assigning OBJ_GHOST to OBJ_PLAYER so homers can check for valid targets again. I think this should be fixed at the root of the problem but I am not sure if this may break more than we aim to fix.
if ( ( Game_mode & GM_MULTI ) & & ( goal_type = = OBJ_GHOST ) )
goal_type = OBJ_PLAYER ;
2006-03-20 17:12:09 +00:00
}
}
2015-11-15 22:30:41 +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 -----------------
2015-12-03 03:26:48 +00:00
static objptridx_t Laser_player_fire_spread_delay ( const vobjptridx_t obj , weapon_id_type 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)
2015-04-02 02:36:53 +00:00
create_awareness_event ( obj , player_awareness_type_t : : 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 ;
2015-02-05 03:03:51 +00:00
fq . ignore_obj_list . first = nullptr ;
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
2015-01-20 02:46:42 +00:00
Fate = find_vector_intersection ( fq , hit_data ) ;
2006-03-20 17:12:09 +00:00
2014-11-20 03:00:36 +00:00
auto 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
}
2016-01-09 16:38:14 +00:00
const auto & & objnum = Laser_create_new ( LaserDir , LaserPos , vsegptridx ( 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.
2015-12-03 03:26:49 +00:00
if ( laser_type = = weapon_id_type : : OMEGA_ID )
2013-08-09 15:21:03 +00:00
return objnum ;
2006-03-20 17:12:09 +00:00
2015-12-03 03:26:49 +00:00
if ( laser_type = = weapon_id_type : : GUIDEDMISS_ID & & Multi_is_guided ) {
2015-11-27 03:56:13 +00:00
Guided_missile [ get_player_id ( obj ) ] = objnum ;
2006-03-20 17:12:09 +00:00
}
Multi_is_guided = 0 ;
2015-12-03 03:26:49 +00:00
if ( laser_type = = weapon_id_type : : CONCUSSION_ID | |
laser_type = = weapon_id_type : : HOMING_ID | |
laser_type = = weapon_id_type : : SMART_ID | |
laser_type = = weapon_id_type : : MEGA_ID | |
laser_type = = weapon_id_type : : FLASH_ID | |
2006-03-20 17:12:09 +00:00
//laser_type == GUIDEDMISS_ID ||
//laser_type == SUPERPROX_ID ||
2015-12-03 03:26:49 +00:00
laser_type = = weapon_id_type : : MERCURY_ID | |
laser_type = = weapon_id_type : : EARTHSHAKER_ID )
2015-02-14 22:48:28 +00:00
{
const auto need_new_missile_viewer = [ obj ] {
if ( ! Missile_viewer )
return true ;
if ( Missile_viewer - > type ! = OBJ_WEAPON )
return true ;
if ( Missile_viewer - > signature ! = Missile_viewer_sig )
return true ;
2015-11-27 03:56:13 +00:00
if ( get_player_id ( obj ) = = Player_num & & Missile_viewer - > ctype . laser_info . parent_num ! = get_local_player ( ) . objnum )
2015-02-14 22:48:28 +00:00
/* New missile fired-by local player &&
* currently viewing missile not - fired - by local player
*/
return true ;
return false ;
} ;
const auto can_view_missile = [ obj ] {
2015-11-27 03:56:13 +00:00
const auto obj_id = get_player_id ( obj ) ;
if ( obj_id = = Player_num )
2015-02-14 22:48:28 +00:00
return true ;
2015-09-26 21:17:14 +00:00
if ( PlayerCfg . MissileViewEnabled ! = MissileViewMode : : EnabledSelfAndAllies )
return false ;
2015-09-26 18:28:49 +00:00
{
if ( Game_mode & GM_MULTI_COOP )
return true ;
if ( Game_mode & GM_TEAM )
2015-11-27 03:56:13 +00:00
return get_team ( Player_num ) = = get_team ( obj_id ) ;
2015-09-26 18:28:49 +00:00
}
2015-02-14 22:48:28 +00:00
return false ;
} ;
if ( need_new_missile_viewer ( ) & & can_view_missile ( ) )
{
2014-08-23 23:53:56 +00:00
Missile_viewer = objnum ;
2015-02-14 22:48:28 +00:00
Missile_viewer_sig = objnum - > signature ;
}
}
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)
2015-11-27 03:56:13 +00:00
if ( ! is_proximity_bomb_or_smart_mine ( get_weapon_id ( objnum ) ) )
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
}
}
2013-08-09 15:21:03 +00:00
return objnum ;
2006-03-20 17:12:09 +00:00
}
// -----------------------------------------------------------------------------------------------------------
2015-12-03 03:26:48 +00:00
static objptridx_t Laser_player_fire_spread ( const vobjptridx_t obj , weapon_id_type 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
}
// -----------------------------------------------------------------------------------------------------------
2015-12-03 03:26:48 +00:00
objptridx_t Laser_player_fire ( const vobjptridx_t obj , weapon_id_type 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 ;
2015-12-03 03:26:49 +00:00
energy_usage = Weapon_info [ weapon_id_type : : 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)
2015-10-30 02:52:55 +00:00
if ( get_local_player_energy ( ) > 0 )
2013-03-03 01:03:33 +00:00
# endif
2013-08-27 23:53:03 +00:00
{
2015-10-30 02:52:55 +00:00
get_local_player_energy ( ) - = energy_usage ;
2006-03-20 17:12:09 +00:00
2015-10-30 02:52:55 +00:00
if ( get_local_player_energy ( ) < = 0 ) {
get_local_player_energy ( ) = 0 ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
2015-04-26 20:15:51 +00:00
auto_select_primary_weapon ( ) ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
}
2015-12-03 03:26:49 +00:00
Laser_player_fire ( obj , weapon_id_type : : FLARE_ID , 6 , 1 , get_local_plrobj ( ) . 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
2015-09-15 02:48:03 +00:00
static bool is_any_guided_missile ( const vcobjptr_t obj )
{
2015-12-03 03:26:49 +00:00
if ( get_weapon_id ( obj ) ! = weapon_id_type : : GUIDEDMISS_ID )
2015-09-15 02:48:03 +00:00
return false ;
if ( obj - > ctype . laser_info . parent_type ! = OBJ_PLAYER )
return false ;
const auto pnum = get_player_id ( vcobjptr ( obj - > ctype . laser_info . parent_num ) ) ;
if ( obj ! = Guided_missile [ pnum ] )
return false ;
return obj - > signature = = Guided_missile_sig [ pnum ] ;
}
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
2015-03-22 18:49:21 +00:00
if ( ! ( ( d_tick_count ^ obj - > signature . get ( ) ) & 3 ) & &
2015-12-03 03:26:49 +00:00
( get_weapon_id ( obj ) ! = weapon_id_type : : FLARE_ID ) & &
2013-10-07 23:52:33 +00:00
( 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 ;
}
2015-12-03 03:26:49 +00:00
if ( get_weapon_id ( obj ) = = weapon_id_type : : 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)
2015-09-15 02:48:03 +00:00
if ( Weapon_info [ get_weapon_id ( obj ) ] . homing_flag & & ! is_any_guided_missile ( obj ) )
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.
2015-05-28 13:22:40 +00:00
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)
2015-12-03 03:26:49 +00:00
if ( ( get_weapon_id ( obj ) = = weapon_id_type : : ROBOT_SMART_HOMING_ID ) | | ( get_weapon_id ( obj ) = = weapon_id_type : : PLAYER_SMART_HOMING_ID ) )
2013-03-03 01:03:33 +00:00
# elif defined(DXX_BUILD_DESCENT_II)
2015-12-03 03:26:49 +00:00
if ( ( get_weapon_id ( obj ) = = weapon_id_type : : ROBOT_SMART_MINE_HOMING_ID ) | | ( get_weapon_id ( obj ) = = weapon_id_type : : ROBOT_SMART_HOMING_ID ) | | ( get_weapon_id ( obj ) = = weapon_id_type : : SMART_MINE_HOMING_ID ) | | ( get_weapon_id ( obj ) = = weapon_id_type : : PLAYER_SMART_HOMING_ID ) | | ( get_weapon_id ( obj ) = = weapon_id_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 ;
}
2015-05-28 13:22:40 +00:00
# ifdef NEWHOMER
2015-05-30 16:29:41 +00:00
if ( d_homer_tick_step )
2015-05-28 13:22:40 +00:00
{
2016-01-09 16:38:14 +00:00
const auto & & track_goal = track_track_goal ( objptridx ( obj - > ctype . laser_info . track_goal ) , obj , & dot , d_homer_tick_count ) ;
2015-05-28 13:22:40 +00:00
2015-07-25 23:10:46 +00:00
if ( track_goal = = get_local_player ( ) . objnum ) {
2015-05-28 13:22:40 +00:00
fix dist_to_player ;
dist_to_player = vm_vec_dist_quick ( obj - > pos , track_goal - > pos ) ;
2015-11-14 18:17:22 +00:00
auto & homing_object_dist = get_local_plrobj ( ) . ctype . player_info . homing_object_dist ;
if ( dist_to_player < homing_object_dist | | homing_object_dist < 0 )
homing_object_dist = dist_to_player ;
2015-05-28 13:22:40 +00:00
}
if ( track_goal ! = object_none )
{
vm_vec_sub ( vector_to_object , track_goal - > pos , obj - > pos ) ;
vm_vec_normalize_quick ( vector_to_object ) ;
temp_vec = obj - > mtype . phys_info . velocity ;
speed = vm_vec_normalize_quick ( temp_vec ) ;
max_speed = Weapon_info [ get_weapon_id ( obj ) ] . speed [ Difficulty_level ] ;
if ( speed + F1_0 < max_speed ) {
2015-05-30 16:29:41 +00:00
speed + = fixmul ( max_speed , HOMING_TURN_TIME ) ;
2015-05-28 13:22:40 +00:00
if ( speed > max_speed )
speed = max_speed ;
}
vm_vec_add2 ( temp_vec , vector_to_object ) ;
// The boss' smart children track better...
if ( Weapon_info [ get_weapon_id ( obj ) ] . render_type ! = WEAPON_RENDER_POLYMODEL )
vm_vec_add2 ( temp_vec , vector_to_object ) ;
vm_vec_normalize_quick ( temp_vec ) ;
vm_vec_scale ( temp_vec , speed ) ;
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 ;
absdot = abs ( F1_0 - dot ) ;
2015-05-30 16:29:41 +00:00
lifelost = fixmul ( absdot * 32 , HOMING_TURN_TIME ) ;
2015-05-28 13:22:40 +00:00
obj - > lifeleft - = lifelost ;
}
// Only polygon objects have visible orientation, so only they should turn.
if ( Weapon_info [ get_weapon_id ( obj ) ] . render_type = = WEAPON_RENDER_POLYMODEL )
homing_missile_turn_towards_velocity ( obj , temp_vec ) ; // temp_vec is normalized velocity.
}
}
# else // old FPS-dependent homers
2006-03-20 17:12:09 +00:00
// 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 ;
2015-05-28 13:22:40 +00:00
auto track_goal = track_track_goal ( obj_track_goal , obj , & dot , d_tick_count ) ;
2006-03-20 17:12:09 +00:00
2015-07-25 23:10:46 +00:00
if ( track_goal = = get_local_player ( ) . objnum ) {
2006-03-20 17:12:09 +00:00
fix dist_to_player ;
2014-10-01 02:28:41 +00:00
dist_to_player = vm_vec_dist_quick ( obj - > pos , track_goal - > pos ) ;
2015-07-25 23:10:46 +00:00
if ( ( dist_to_player < get_local_player ( ) . homing_object_dist ) | | ( get_local_player ( ) . homing_object_dist < 0 ) )
get_local_player ( ) . homing_object_dist = dist_to_player ;
2010-06-15 16:24:56 +00:00
2006-03-20 17:12:09 +00:00
}
2015-05-28 13:22:40 +00:00
if ( track_goal ! = object_none )
{
2015-12-03 03:26:49 +00:00
vm_vec_sub ( vector_to_object , track_goal - > pos , obj - > pos ) ;
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
}
2015-05-28 13:22:40 +00:00
# endif
2006-03-20 17:12:09 +00:00
}
}
// Make sure weapon is not moving faster than allowed speed.
2015-11-27 03:56:13 +00:00
const auto & weapon_info = Weapon_info [ get_weapon_id ( obj ) ] ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
2015-11-27 03:56:13 +00:00
if ( weapon_info . 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 ) ;
2015-11-27 03:56:13 +00:00
if ( weapon_speed > weapon_info . 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)
2015-11-27 03:56:13 +00:00
if ( weapon_info . 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
}
}
}
}
2015-12-22 04:18:50 +00:00
}
namespace dcx {
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 ) ;
}
2015-12-22 04:18:50 +00:00
}
namespace dsx {
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 )
{
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
2015-12-15 04:09:35 +00:00
if ( Player_dead_state ! = player_dead_state : : no )
2006-03-20 17:12:09 +00:00
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 ) ;
2015-11-07 21:55:59 +00:00
auto & player_info = get_local_plrobj ( ) . ctype . player_info ;
auto & pl_energy = player_info . energy ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2015-10-18 21:01:18 +00:00
if ( Primary_weapon = = primary_weapon_index_t : : OMEGA_INDEX )
2013-08-27 23:53:03 +00:00
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.
2015-10-18 21:01:18 +00:00
if ( weapon_index = = primary_weapon_index_t : : HELIX_INDEX )
2006-03-20 17:12:09 +00:00
if ( Game_mode & GM_MULTI )
energy_used * = 2 ;
2015-11-07 21:55:59 +00:00
if ( ! ( sufficient_energy ( energy_used , pl_energy ) & & sufficient_ammo ( ammo_used , uses_vulcan_ammo , player_info . vulcan_ammo ) ) )
2015-04-26 20:15:51 +00:00
auto_select_primary_weapon ( ) ; // 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 ) {
2015-11-07 21:55:59 +00:00
if ( sufficient_energy ( energy_used , pl_energy ) & & sufficient_ammo ( ammo_used , uses_vulcan_ammo , player_info . 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 ;
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
2015-11-07 21:55:59 +00:00
laser_level = player_info . laser_level ;
2010-06-15 16:24:56 +00:00
2006-03-20 17:12:09 +00:00
flags = 0 ;
2015-12-22 04:18:52 +00:00
switch ( Primary_weapon )
{
case primary_weapon_index_t : : LASER_INDEX :
if ( player_info . powerup_flags & PLAYER_FLAGS_QUAD_LASERS )
flags | = LASER_QUAD ;
break ;
case primary_weapon_index_t : : SPREADFIRE_INDEX :
flags | = ( Spreadfire_toggle ^ = LASER_SPREADFIRE_TOGGLED ) & LASER_SPREADFIRE_TOGGLED ;
break ;
case primary_weapon_index_t : : VULCAN_INDEX :
case primary_weapon_index_t : : PLASMA_INDEX :
case primary_weapon_index_t : : FUSION_INDEX :
default :
break ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2015-12-22 04:18:52 +00:00
case primary_weapon_index_t : : HELIX_INDEX :
flags | = ( Helix_orientation + + & LASER_HELIX_MASK ) ;
break ;
case primary_weapon_index_t : : SUPER_LASER_INDEX :
case primary_weapon_index_t : : GAUSS_INDEX :
case primary_weapon_index_t : : PHOENIX_INDEX :
case primary_weapon_index_t : : OMEGA_INDEX :
break ;
2013-03-03 01:03:33 +00:00
# endif
2015-12-22 04:18:52 +00:00
}
2006-03-20 17:12:09 +00:00
2015-07-25 23:10:46 +00:00
rval + = do_laser_firing ( vobjptridx ( get_local_player ( ) . objnum ) , Primary_weapon , laser_level , flags , nfires , get_local_plrobj ( ) . orient . fvec ) ;
2006-03-20 17:12:09 +00:00
2015-11-07 21:55:58 +00:00
pl_energy - = ( energy_used * rval ) / Weapon_info [ weapon_index ] . fire_count ;
if ( pl_energy < 0 )
pl_energy = 0 ;
2006-03-20 17:12:09 +00:00
2013-09-21 21:29:58 +00:00
if ( uses_vulcan_ammo ) {
2015-11-07 21:55:59 +00:00
auto & v = player_info . vulcan_ammo ;
if ( v < ammo_used )
v = 0 ;
2006-03-20 17:12:09 +00:00
else
2015-11-07 21:55:59 +00:00
v - = ammo_used ;
2006-03-20 17:12:09 +00:00
}
2015-04-26 20:15:51 +00:00
auto_select_primary_weapon ( ) ; // Make sure the player can fire from this weapon.
2006-03-20 17:12:09 +00:00
} else {
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2015-04-26 20:15:51 +00:00
auto_select_primary_weapon ( ) ; // 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.
2014-12-23 04:20:27 +00:00
int do_laser_firing ( vobjptridx_t objp , int weapon_num , int level , int flags , int nfires , vms_vector shot_orientation )
2006-03-20 17:12:09 +00:00
{
switch ( weapon_num ) {
2015-10-18 21:01:18 +00:00
case primary_weapon_index_t : : LASER_INDEX : {
2015-12-03 03:26:48 +00:00
weapon_id_type weapon_type ;
2006-03-20 17:12:09 +00:00
2013-10-09 01:54:12 +00:00
switch ( level )
{
case LASER_LEVEL_1 :
2015-12-03 03:26:49 +00:00
weapon_type = weapon_id_type : : LASER_ID_L1 ;
2013-10-09 01:54:12 +00:00
break ;
case LASER_LEVEL_2 :
2015-12-03 03:26:49 +00:00
weapon_type = weapon_id_type : : LASER_ID_L2 ;
2013-10-09 01:54:12 +00:00
break ;
case LASER_LEVEL_3 :
2015-12-03 03:26:49 +00:00
weapon_type = weapon_id_type : : LASER_ID_L3 ;
2013-10-09 01:54:12 +00:00
break ;
case LASER_LEVEL_4 :
2015-12-03 03:26:49 +00:00
weapon_type = weapon_id_type : : LASER_ID_L4 ;
2013-10-09 01:54:12 +00:00
break ;
# if defined(DXX_BUILD_DESCENT_II)
case LASER_LEVEL_5 :
2015-12-03 03:26:49 +00:00
weapon_type = weapon_id_type : : LASER_ID_L5 ;
2013-10-09 01:54:12 +00:00
break ;
case LASER_LEVEL_6 :
2015-12-03 03:26:49 +00:00
weapon_type = weapon_id_type : : LASER_ID_L6 ;
2013-10-09 01:54:12 +00:00
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 ;
}
2015-10-18 21:01:18 +00:00
case primary_weapon_index_t : : VULCAN_INDEX : {
2006-03-20 17:12:09 +00:00
// Only make sound for 1/4 of vulcan bullets.
int make_sound = 1 ;
//if (d_rand() > 24576)
// make_sound = 1;
2015-12-03 03:26:49 +00:00
Laser_player_fire_spread ( objp , weapon_id_type : : 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 ) {
2015-12-03 03:26:49 +00:00
Laser_player_fire_spread ( objp , weapon_id_type : : 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 ) {
2015-12-03 03:26:49 +00:00
Laser_player_fire_spread ( objp , weapon_id_type : : 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 ;
}
2015-10-18 21:01:18 +00:00
case primary_weapon_index_t : : SPREADFIRE_INDEX :
2006-03-20 17:12:09 +00:00
if ( flags & LASER_SPREADFIRE_TOGGLED ) {
2015-12-03 03:26:49 +00:00
Laser_player_fire_spread ( objp , weapon_id_type : : SPREADFIRE_ID , 6 , F1_0 / 16 , 0 , 0 , shot_orientation ) ;
Laser_player_fire_spread ( objp , weapon_id_type : : SPREADFIRE_ID , 6 , - F1_0 / 16 , 0 , 0 , shot_orientation ) ;
Laser_player_fire_spread ( objp , weapon_id_type : : SPREADFIRE_ID , 6 , 0 , 0 , 1 , shot_orientation ) ;
2006-03-20 17:12:09 +00:00
} else {
2015-12-03 03:26:49 +00:00
Laser_player_fire_spread ( objp , weapon_id_type : : SPREADFIRE_ID , 6 , 0 , F1_0 / 16 , 0 , shot_orientation ) ;
Laser_player_fire_spread ( objp , weapon_id_type : : SPREADFIRE_ID , 6 , 0 , - F1_0 / 16 , 0 , shot_orientation ) ;
Laser_player_fire_spread ( objp , weapon_id_type : : SPREADFIRE_ID , 6 , 0 , 0 , 1 , shot_orientation ) ;
2006-03-20 17:12:09 +00:00
}
break ;
2015-10-18 21:01:18 +00:00
case primary_weapon_index_t : : PLASMA_INDEX :
2015-12-03 03:26:49 +00:00
Laser_player_fire ( objp , weapon_id_type : : PLASMA_ID , 0 , 1 , shot_orientation ) ;
Laser_player_fire ( objp , weapon_id_type : : PLASMA_ID , 1 , 0 , shot_orientation ) ;
2006-03-20 17:12:09 +00:00
if ( nfires > 1 ) {
2015-12-03 03:26:49 +00:00
Laser_player_fire_spread_delay ( objp , weapon_id_type : : PLASMA_ID , 0 , 0 , 0 , FrameTime / 2 , 1 , shot_orientation ) ;
Laser_player_fire_spread_delay ( objp , weapon_id_type : : PLASMA_ID , 1 , 0 , 0 , FrameTime / 2 , 0 , shot_orientation ) ;
2006-03-20 17:12:09 +00:00
}
break ;
2015-10-18 21:01:18 +00:00
case primary_weapon_index_t : : FUSION_INDEX : {
2006-03-20 17:12:09 +00:00
vms_vector force_vec ;
2015-12-03 03:26:49 +00:00
Laser_player_fire ( objp , weapon_id_type : : FUSION_ID , 0 , 1 , shot_orientation ) ;
Laser_player_fire ( objp , weapon_id_type : : 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)
2015-10-18 21:01:18 +00:00
case primary_weapon_index_t : : GAUSS_INDEX : {
2006-03-20 17:12:09 +00:00
// 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
2015-12-03 03:26:49 +00:00
Laser_player_fire_spread ( objp , weapon_id_type : : 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 ) {
2015-12-03 03:26:49 +00:00
Laser_player_fire_spread ( objp , weapon_id_type : : 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 ) {
2015-12-03 03:26:49 +00:00
Laser_player_fire_spread ( objp , weapon_id_type : : 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 ;
}
2015-10-18 21:01:18 +00:00
case primary_weapon_index_t : : HELIX_INDEX : {
2006-03-20 17:12:09 +00:00
int helix_orient ;
fix spreadr , spreadu ;
2015-12-22 04:18:52 +00:00
helix_orient = flags & LASER_HELIX_MASK ;
2006-03-20 17:12:09 +00:00
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 ) ;
}
2015-12-03 03:26:49 +00:00
Laser_player_fire_spread ( objp , weapon_id_type : : HELIX_ID , 6 , 0 , 0 , 1 , shot_orientation ) ;
Laser_player_fire_spread ( objp , weapon_id_type : : HELIX_ID , 6 , spreadr , spreadu , 0 , shot_orientation ) ;
Laser_player_fire_spread ( objp , weapon_id_type : : HELIX_ID , 6 , - spreadr , - spreadu , 0 , shot_orientation ) ;
Laser_player_fire_spread ( objp , weapon_id_type : : HELIX_ID , 6 , spreadr * 2 , spreadu * 2 , 0 , shot_orientation ) ;
Laser_player_fire_spread ( objp , weapon_id_type : : HELIX_ID , 6 , - spreadr * 2 , - spreadu * 2 , 0 , shot_orientation ) ;
2006-03-20 17:12:09 +00:00
break ;
}
2015-10-18 21:01:18 +00:00
case primary_weapon_index_t : : PHOENIX_INDEX :
2015-12-03 03:26:49 +00:00
Laser_player_fire ( objp , weapon_id_type : : PHOENIX_ID , 0 , 1 , shot_orientation ) ;
Laser_player_fire ( objp , weapon_id_type : : PHOENIX_ID , 1 , 0 , shot_orientation ) ;
2006-03-20 17:12:09 +00:00
if ( nfires > 1 ) {
2015-12-03 03:26:49 +00:00
Laser_player_fire_spread_delay ( objp , weapon_id_type : : PHOENIX_ID , 0 , 0 , 0 , FrameTime / 2 , 1 , shot_orientation ) ;
Laser_player_fire_spread_delay ( objp , weapon_id_type : : PHOENIX_ID , 1 , 0 , 0 , FrameTime / 2 , 0 , shot_orientation ) ;
2006-03-20 17:12:09 +00:00
}
break ;
2015-10-18 21:01:18 +00:00
case primary_weapon_index_t : : OMEGA_INDEX :
2015-12-03 03:26:49 +00:00
Laser_player_fire ( objp , weapon_id_type : : 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)
2015-10-18 21:01:18 +00:00
case primary_weapon_index_t : : SUPER_LASER_INDEX :
2013-10-06 17:00:03 +00:00
# endif
2006-03-20 17:12:09 +00:00
default :
Int3 ( ) ; // Contact Yuan: Unknown Primary weapon type, setting to 0.
2015-04-26 20:15:57 +00:00
Primary_weapon = primary_weapon_index_t : : LASER_INDEX ;
2006-03-20 17:12:09 +00:00
}
// Set values to be recognized during comunication phase, if we are the
// one shooting
2015-07-25 23:10:46 +00:00
if ( ( Game_mode & GM_MULTI ) & & objp = = get_local_player ( ) . 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 ;
}
2015-12-22 04:18:50 +00:00
}
namespace dcx {
2015-08-17 02:44:56 +00:00
const vm_distance MAX_SMART_DISTANCE ( F1_0 * 150 ) ;
const vm_distance_squared MAX_SMART_DISTANCE_SQUARED = MAX_SMART_DISTANCE * MAX_SMART_DISTANCE ;
2006-03-20 17:12:09 +00:00
# define MAX_OBJDISTS 30
2015-12-22 04:18:50 +00:00
}
namespace dsx {
2006-03-20 17:12:09 +00:00
// -------------------------------------------------------------------------------------------
// if goal_obj == -1, then create random vector
2015-12-03 03:26:48 +00:00
static objptridx_t create_homing_missile ( const vobjptridx_t objp , const objptridx_t goal_obj , weapon_id_type 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.
2016-01-09 16:38:14 +00:00
const auto & & objnum = Laser_create_new ( vector_to_goal , objp - > pos , vsegptridx ( 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 ;
}
2015-05-09 17:38:58 +00:00
namespace {
struct miniparent
{
short type ;
objnum_t num ;
} ;
}
2006-03-20 17:12:09 +00:00
//-----------------------------------------------------------------------------
// Create the children of a smart bomb, which is a bunch of homing missiles.
2015-05-09 17:38:58 +00:00
static void create_smart_children ( const vobjptridx_t objp , const uint_fast32_t num_smart_children , const miniparent parent )
2006-03-20 17:12:09 +00:00
{
2015-08-17 02:44:56 +00:00
unsigned numobjs = 0 ;
2015-12-03 03:26:48 +00:00
weapon_id_type blob_id ;
2006-03-20 17:12:09 +00:00
2015-05-09 17:38:58 +00:00
array < objnum_t , MAX_OBJDISTS > objlist ;
2013-03-03 01:03:33 +00:00
{
2006-03-20 17:12:09 +00:00
if ( Game_mode & GM_MULTI )
d_srand ( 8321L ) ;
2015-12-22 04:18:51 +00:00
range_for ( const auto & & curobjp , highest_valid ( vcobjptridx ) )
2014-10-12 23:05:46 +00:00
{
2015-12-22 04:18:51 +00:00
if ( ( ( curobjp - > type = = OBJ_ROBOT & & ! curobjp - > ctype . ai_info . CLOAKED ) | | curobjp - > type = = OBJ_PLAYER ) & & curobjp ! = parent . num )
2015-05-09 17:38:58 +00:00
{
2006-03-20 17:12:09 +00:00
if ( curobjp - > type = = OBJ_PLAYER )
{
2015-05-09 17:38:58 +00:00
if ( parent . type = = OBJ_PLAYER & & ( Game_mode & GM_MULTI_COOP ) )
2006-03-20 17:12:09 +00:00
continue ;
2015-05-09 17:38:58 +00:00
if ( ( Game_mode & GM_TEAM ) & & get_team ( get_player_id ( curobjp ) ) = = get_team ( get_player_id ( vcobjptr ( parent . num ) ) ) )
2006-03-20 17:12:09 +00:00
continue ;
2015-11-07 21:55:59 +00:00
if ( curobjp - > ctype . player_info . powerup_flags & PLAYER_FLAGS_CLOAKED )
2006-03-20 17:12:09 +00:00
continue ;
}
// Robot blobs can't track robots.
if ( curobjp - > type = = OBJ_ROBOT ) {
2015-05-09 17:38:58 +00:00
if ( parent . type = = OBJ_ROBOT )
2006-03-20 17:12:09 +00:00
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.
2015-05-09 17:38:58 +00:00
if ( parent . type = = OBJ_PLAYER )
2015-11-27 03:56:13 +00:00
if ( Robot_info [ get_robot_id ( curobjp ) ] . companion )
2006-03-20 17:12:09 +00:00
continue ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
}
2015-08-17 02:44:56 +00:00
const auto & & dist = vm_vec_dist2 ( objp - > pos , curobjp - > pos ) ;
if ( dist < MAX_SMART_DISTANCE_SQUARED )
{
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 ) {
2015-12-22 04:18:51 +00:00
objlist [ numobjs ] = curobjp ;
2006-03-20 17:12:09 +00:00
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)
2015-05-09 17:38:58 +00:00
if ( parent . type = = OBJ_PLAYER )
{
2015-12-03 03:26:49 +00:00
blob_id = weapon_id_type : : PLAYER_SMART_HOMING_ID ;
2013-03-03 01:03:33 +00:00
} else {
2015-12-03 03:26:49 +00:00
blob_id = ( ( N_weapon_types < weapon_id_type : : ROBOT_SMART_HOMING_ID ) ? ( weapon_id_type : : PLAYER_SMART_HOMING_ID ) : ( weapon_id_type : : 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..,
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 ) {
2015-12-03 03:26:49 +00:00
blob_id = Weapon_info [ get_weapon_id ( objp ) ] . children ;
2015-12-03 03:26:48 +00:00
Assert ( blob_id ! = weapon_none ) ; // Hmm, missing data in bitmaps.tbl. Need "children=NN" parameter.
2006-03-20 17:12:09 +00:00
} else {
Assert ( objp - > type = = OBJ_ROBOT ) ;
2015-12-03 03:26:49 +00:00
blob_id = weapon_id_type : : ROBOT_SMART_HOMING_ID ;
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-12-29 04:28:07 +00:00
objnum_t last_sel_objnum = object_none ;
2015-08-17 02:44:56 +00:00
const auto get_random_different_object = [ & ] {
for ( ; ; )
{
const auto r = objlist [ ( d_rand ( ) * numobjs ) > > 15 ] ;
if ( last_sel_objnum ! = r )
return last_sel_objnum = r ;
}
} ;
for ( auto i = num_smart_children ; i - - ; )
{
const auto & & sel_objnum = numobjs
? objptridx ( ( numobjs = = 1 )
? objlist [ 0 ]
: get_random_different_object ( ) )
: objptridx ( object_none ) ;
2012-05-08 23:22:31 +00:00
create_homing_missile ( objp , sel_objnum , blob_id , ( i = = 0 ) ? 1 : 0 ) ;
2006-03-20 17:12:09 +00:00
}
}
}
2015-05-09 17:38:58 +00:00
void create_weapon_smart_children ( const vobjptridx_t objp )
{
const auto wid = get_weapon_id ( objp ) ;
# if defined(DXX_BUILD_DESCENT_I)
2015-12-03 03:26:49 +00:00
if ( wid ! = weapon_id_type : : SMART_ID )
2015-05-09 17:38:58 +00:00
# elif defined(DXX_BUILD_DESCENT_II)
2015-12-03 03:26:49 +00:00
if ( Weapon_info [ wid ] . children = = weapon_id_type : : unspecified )
2015-05-09 17:38:58 +00:00
# endif
return ;
# if defined(DXX_BUILD_DESCENT_II)
2015-12-03 03:26:49 +00:00
if ( wid = = weapon_id_type : : EARTHSHAKER_ID )
blast_nearby_glass ( objp , Weapon_info [ weapon_id_type : : EARTHSHAKER_ID ] . strength [ Difficulty_level ] ) ;
2015-05-09 17:38:58 +00:00
# endif
create_smart_children ( objp , NUM_SMART_CHILDREN , { objp - > ctype . laser_info . parent_type , objp - > ctype . laser_info . parent_num } ) ;
}
2015-12-22 04:18:50 +00:00
}
namespace dcx {
2006-03-20 17:12:09 +00:00
int Missile_gun = 0 ;
2013-08-27 23:53:03 +00:00
int Proximity_dropped = 0 ;
2015-12-22 04:18:50 +00:00
}
namespace dsx {
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
2015-05-09 17:38:58 +00:00
void create_robot_smart_children ( const vobjptridx_t objp , const uint_fast32_t num_smart_children )
{
create_smart_children ( objp , num_smart_children , { OBJ_ROBOT , objp } ) ;
}
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 )
2016-01-09 16:38:13 +00:00
multi_send_guided_info ( vobjptr ( Missile_viewer ) , 1 ) ;
2006-03-20 17:12:09 +00:00
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
2015-12-15 04:09:35 +00:00
if ( Player_dead_state ! = player_dead_state : : no )
2015-10-10 03:44:14 +00:00
return ;
2015-10-30 02:52:56 +00:00
if ( auto & secondary_weapon_ammo = get_local_player_secondary_ammo ( ) [ weapon ] )
2015-10-10 03:44:14 +00:00
{
2006-03-20 17:12:09 +00:00
2013-10-08 03:20:09 +00:00
int weapon_gun ;
2006-03-20 17:12:09 +00:00
2015-10-10 03:44:14 +00:00
- - secondary_weapon_ammo ;
2006-03-20 17:12:09 +00:00
2015-12-03 03:26:49 +00:00
const auto weapon_index = 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 + + ;
}
2015-07-25 23:10:45 +00:00
const auto & & objnum = Laser_player_fire ( vobjptridx ( ConsoleObject ) , weapon_index , weapon_gun , 1 , get_local_plrobj ( ) . 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 ;
2015-07-12 01:04:20 +00:00
const auto & & console = vobjptr ( ConsoleObject ) ;
2006-03-20 17:12:09 +00:00
force_vec . x = - ( ConsoleObject - > orient . fvec . x < < 7 ) ;
force_vec . y = - ( ConsoleObject - > orient . fvec . y < < 7 ) ;
force_vec . z = - ( ConsoleObject - > orient . fvec . z < < 7 ) ;
2015-07-12 01:04:20 +00:00
phys_apply_force ( console , 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 ;
2015-07-12 01:04:20 +00:00
phys_apply_rot ( console , 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 )
2015-04-26 20:15:51 +00:00
auto_select_secondary_weapon ( ) ; //select next missile, if this one out of ammo
2006-03-20 17:12:09 +00:00
}
}
2015-12-22 04:18:50 +00:00
}