2006-03-20 16:43:15 +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-1998 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
|
|
|
|
*/
|
|
|
|
/*
|
|
|
|
* $Source: /cvsroot/dxx-rebirth/d1x-rebirth/main/laser.c,v $
|
|
|
|
* $Revision: 1.1.1.1 $
|
|
|
|
* $Author: zicodxx $
|
|
|
|
* $Date: 2006/03/17 19:42:14 $
|
|
|
|
*
|
|
|
|
* This will contain the laser code
|
|
|
|
*
|
|
|
|
* Revision 1.1 1993/11/29 17:19:02 john
|
|
|
|
* Initial revision
|
|
|
|
*
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef RCS
|
|
|
|
static char rcsid[] = "$Id: laser.c,v 1.1.1.1 2006/03/17 19:42:14 zicodxx Exp $";
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
2007-01-13 16:22:27 +00:00
|
|
|
#include <time.h>
|
2006-03-20 16:43:15 +00:00
|
|
|
|
|
|
|
#include "inferno.h"
|
|
|
|
#include "game.h"
|
|
|
|
#include "bm.h"
|
|
|
|
#include "object.h"
|
|
|
|
#include "laser.h"
|
|
|
|
#include "segment.h"
|
|
|
|
#include "fvi.h"
|
|
|
|
#include "segpoint.h"
|
|
|
|
#include "error.h"
|
|
|
|
#include "mono.h"
|
|
|
|
#include "key.h"
|
|
|
|
#include "texmap.h"
|
|
|
|
#include "textures.h"
|
|
|
|
#include "render.h"
|
|
|
|
#include "vclip.h"
|
|
|
|
#include "fireball.h"
|
|
|
|
#include "polyobj.h"
|
|
|
|
#include "robot.h"
|
|
|
|
#include "weapon.h"
|
|
|
|
#include "timer.h"
|
|
|
|
#include "player.h"
|
|
|
|
#include "sounds.h"
|
|
|
|
#include "network.h"
|
|
|
|
#include "ai.h"
|
|
|
|
#include "modem.h"
|
|
|
|
#include "powerup.h"
|
|
|
|
#include "multi.h"
|
|
|
|
#include "physics.h"
|
|
|
|
|
|
|
|
|
|
|
|
#include "hudmsg.h"
|
|
|
|
|
|
|
|
|
|
|
|
int Laser_rapid_fire = 0;
|
|
|
|
|
|
|
|
int Laser_drop_vulcan_ammo = 0;
|
|
|
|
|
|
|
|
int find_homing_object_complete(vms_vector *curpos, object *tracker, int track_obj_type1, int track_obj_type2);
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------------
|
|
|
|
// Called by render code.... determines if the laser is from a robot or the
|
|
|
|
// player and calls the appropriate routine.
|
|
|
|
|
|
|
|
void Laser_render(object *obj)
|
|
|
|
{
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
|
|
|
switch( Weapon_info[obj->id].render_type ) {
|
|
|
|
case WEAPON_RENDER_LASER:
|
|
|
|
Int3(); // Not supported anymore!
|
|
|
|
//Laser_draw_one(obj-Objects, Weapon_info[obj->id].bitmap );
|
|
|
|
break;
|
|
|
|
case WEAPON_RENDER_BLOB:
|
|
|
|
draw_object_blob(obj, Weapon_info[obj->id].bitmap );
|
|
|
|
break;
|
|
|
|
case WEAPON_RENDER_POLYMODEL:
|
|
|
|
break;
|
|
|
|
case WEAPON_RENDER_VCLIP:
|
|
|
|
Int3(); // Oops, not supported, type added by mk on 09/09/94, but not for lasers...
|
|
|
|
default:
|
|
|
|
Error( "Invalid weapon render type in Laser_render\n" );
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------------
|
|
|
|
// Draws a texture-mapped laser bolt
|
|
|
|
|
|
|
|
//void Laser_draw_one( int objnum, grs_bitmap * bmp )
|
|
|
|
//{
|
|
|
|
// int t1, t2, t3;
|
|
|
|
// g3s_point p1, p2;
|
|
|
|
// object *obj;
|
|
|
|
// vms_vector start_pos,end_pos;
|
|
|
|
//
|
|
|
|
// obj = &Objects[objnum];
|
|
|
|
//
|
|
|
|
// start_pos = obj->pos;
|
|
|
|
// vm_vec_scale_add(&end_pos,&start_pos,&obj->orient.fvec,-Laser_length);
|
|
|
|
//
|
|
|
|
// g3_rotate_point(&p1,&start_pos);
|
|
|
|
// g3_rotate_point(&p2,&end_pos);
|
|
|
|
//
|
|
|
|
// t1 = Lighting_on;
|
|
|
|
// t2 = Interpolation_method;
|
|
|
|
// t3 = Transparency_on;
|
|
|
|
//
|
|
|
|
// Lighting_on = 0;
|
|
|
|
// //Interpolation_method = 3; // Full perspective
|
|
|
|
// Interpolation_method = 1; // Linear
|
|
|
|
// Transparency_on = 1;
|
|
|
|
//
|
|
|
|
// //gr_setcolor( gr_getcolor(31,15,0));
|
|
|
|
// //g3_draw_line_ptrs(p1,p2);
|
|
|
|
// //g3_draw_rod(p1,0x2000,p2,0x2000);
|
|
|
|
// //g3_draw_rod(p1,Laser_width,p2,Laser_width);
|
|
|
|
// g3_draw_rod_tmap(bmp,&p2,Laser_width,&p1,Laser_width,0);
|
|
|
|
// Lighting_on = t1;
|
|
|
|
// Interpolation_method = t2;
|
|
|
|
// Transparency_on = t3;
|
|
|
|
//
|
|
|
|
//}
|
|
|
|
|
|
|
|
// Changed by MK on 09/07/94
|
|
|
|
// I want you to be able to blow up your own bombs.
|
|
|
|
// AND...Your proximity bombs can blow you up if they're 2.0 seconds or more old.
|
|
|
|
int laser_are_related( int o1, int o2 )
|
|
|
|
{
|
|
|
|
if ( (o1<0) || (o2<0) )
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
// See if o2 is the parent of o1
|
|
|
|
if ( Objects[o1].type == OBJ_WEAPON )
|
|
|
|
if ( (Objects[o1].ctype.laser_info.parent_num==o2) && (Objects[o1].ctype.laser_info.parent_signature==Objects[o2].signature) )
|
|
|
|
{
|
|
|
|
// o1 is a weapon, o2 is the parent of 1, so if o1 is PROXIMITY_BOMB and o2 is player, they are related only if o1 < 2.0 seconds old
|
|
|
|
if ((Objects[o1].id != PROXIMITY_ID) || (Objects[o1].ctype.laser_info.creation_time + F1_0*2 >= GameTime)) {
|
|
|
|
return 1;
|
|
|
|
} else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// See if o1 is the parent of o2
|
|
|
|
if ( Objects[o2].type == OBJ_WEAPON )
|
|
|
|
if ( (Objects[o2].ctype.laser_info.parent_num==o1) && (Objects[o2].ctype.laser_info.parent_signature==Objects[o1].signature) )
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
// They must both be weapons
|
|
|
|
if ( Objects[o1].type != OBJ_WEAPON || Objects[o2].type != OBJ_WEAPON )
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
// Here is the 09/07/94 change -- Siblings must be identical, others can hurt each other
|
|
|
|
// See if they're siblings...
|
|
|
|
if ( Objects[o1].ctype.laser_info.parent_signature==Objects[o2].ctype.laser_info.parent_signature )
|
|
|
|
{
|
|
|
|
if (Objects[o1].id == PROXIMITY_ID || Objects[o2].id == PROXIMITY_ID)
|
|
|
|
return 0; //if either is proximity, then can blow up, so say not related
|
|
|
|
else
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//--unused-- int Muzzle_scale=2;
|
|
|
|
int Laser_offset=0;
|
|
|
|
|
|
|
|
void do_muzzle_stuff(int segnum, vms_vector *pos)
|
|
|
|
{
|
|
|
|
Muzzle_data[Muzzle_queue_index].create_time = timer_get_fixed_seconds();
|
|
|
|
Muzzle_data[Muzzle_queue_index].segnum = segnum;
|
|
|
|
Muzzle_data[Muzzle_queue_index].pos = *pos;
|
|
|
|
Muzzle_queue_index++;
|
|
|
|
if (Muzzle_queue_index >= MUZZLE_QUEUE_MAX)
|
|
|
|
Muzzle_queue_index = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------------
|
|
|
|
// Initializes a laser after Fire is pressed
|
|
|
|
|
|
|
|
// Returns object number.
|
|
|
|
int Laser_create_new( vms_vector * direction, vms_vector * position, int segnum, int parent, int weapon_type, int make_sound )
|
|
|
|
{
|
|
|
|
int objnum;
|
|
|
|
object *obj;
|
|
|
|
int rtype=-1;
|
|
|
|
fix parent_speed, weapon_speed;
|
|
|
|
fix volume;
|
|
|
|
fix laser_radius = -1;
|
|
|
|
fix laser_length = 0;
|
|
|
|
|
|
|
|
Assert( weapon_type < N_weapon_types );
|
|
|
|
|
|
|
|
if ( (weapon_type<0) || (weapon_type>=N_weapon_types) )
|
|
|
|
weapon_type = 0;
|
|
|
|
|
|
|
|
// Don't let homing blobs make muzzle flash.
|
|
|
|
if (Objects[parent].type == OBJ_ROBOT)
|
|
|
|
do_muzzle_stuff(segnum, position);
|
|
|
|
|
|
|
|
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;
|
|
|
|
laser_length = 0;
|
|
|
|
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;
|
|
|
|
laser_length = 0;
|
|
|
|
break;
|
|
|
|
case WEAPON_RENDER_VCLIP:
|
|
|
|
rtype = RT_WEAPON_VCLIP;
|
|
|
|
laser_radius = Weapon_info[weapon_type].blob_size;
|
|
|
|
laser_length = 0;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
Error( "Invalid weapon render type in Laser_create_new\n" );
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add to object list
|
|
|
|
Assert(laser_radius != -1);
|
|
|
|
Assert(rtype != -1);
|
|
|
|
objnum = obj_create( OBJ_WEAPON, weapon_type, segnum, position, NULL, laser_radius, CT_WEAPON, MT_PHYSICS, rtype );
|
|
|
|
|
|
|
|
if ( objnum < 0 ) {
|
|
|
|
mprintf((1, "Can't create laser - Out of objects!\n" ));
|
|
|
|
Int3();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
obj = &Objects[objnum];
|
|
|
|
|
|
|
|
if (Objects[parent].type == OBJ_PLAYER) {
|
|
|
|
if (weapon_type == FUSION_ID) {
|
|
|
|
int fusion_scale;
|
|
|
|
|
|
|
|
//added/killed on 8/13/98 by Victor Rachels to make scale equivalant multi/single
|
|
|
|
//added/readded/changed on 9/19/98 by Victor Rachels to screw descentr's
|
|
|
|
fusion_scale = 4;
|
|
|
|
#ifdef NETWORK
|
|
|
|
if (Game_mode & GM_MULTI)
|
|
|
|
if(!*Net_D1xPlayer[Objects[parent].id].ver)
|
|
|
|
fusion_scale = 2;
|
|
|
|
#endif
|
|
|
|
//end this section edit - Victor Rachels
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (Fusion_charge <= 0)
|
|
|
|
obj->ctype.laser_info.multiplier = F1_0;
|
|
|
|
else if (Fusion_charge <= F1_0*fusion_scale)
|
|
|
|
obj->ctype.laser_info.multiplier = F1_0 + Fusion_charge/2;
|
|
|
|
else
|
|
|
|
obj->ctype.laser_info.multiplier = F1_0*fusion_scale;
|
|
|
|
|
|
|
|
// Fusion damage was boosted by mk on 3/27 (for reg 1.1 release), but we only want it to apply to single player games.
|
|
|
|
if (Game_mode & GM_MULTI)
|
|
|
|
obj->ctype.laser_info.multiplier /= 2;
|
|
|
|
} else if ((weapon_type == LASER_ID) && (Players[Objects[parent].id].flags & PLAYER_FLAGS_QUAD_LASERS))
|
|
|
|
obj->ctype.laser_info.multiplier = F1_0*3/4;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// This is strange: 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.
|
|
|
|
if ((weapon_type == PLAYER_SMART_HOMING_ID) || (weapon_type == ROBOT_SMART_HOMING_ID))
|
|
|
|
obj->mtype.phys_info.flags |= PF_BOUNCE;
|
|
|
|
|
|
|
|
if (Weapon_info[weapon_type].render_type == WEAPON_RENDER_POLYMODEL) {
|
|
|
|
obj->rtype.pobj_info.model_num = Weapon_info[obj->id].model_num;
|
|
|
|
laser_radius = fixdiv(Polygon_models[obj->rtype.pobj_info.model_num].rad,Weapon_info[obj->id].po_len_to_width_ratio);
|
|
|
|
laser_length = Polygon_models[obj->rtype.pobj_info.model_num].rad * 2;
|
|
|
|
obj->size = laser_radius;
|
|
|
|
}
|
|
|
|
|
|
|
|
obj->mtype.phys_info.mass = Weapon_info[weapon_type].mass;
|
|
|
|
obj->mtype.phys_info.drag = Weapon_info[weapon_type].drag;
|
|
|
|
if (Weapon_info[weapon_type].bounce)
|
|
|
|
obj->mtype.phys_info.flags |= PF_BOUNCE;
|
|
|
|
|
|
|
|
vm_vec_zero(&obj->mtype.phys_info.thrust);
|
|
|
|
|
|
|
|
if (weapon_type == FLARE_ID)
|
|
|
|
obj->mtype.phys_info.flags |= PF_STICK; //this obj sticks to walls
|
|
|
|
|
|
|
|
obj->shields = Weapon_info[obj->id].strength[Difficulty_level];
|
|
|
|
|
|
|
|
// Fill in laser-specific data
|
|
|
|
|
|
|
|
obj->lifeleft = Weapon_info[obj->id].lifetime;
|
|
|
|
obj->ctype.laser_info.parent_type = Objects[parent].type;
|
|
|
|
obj->ctype.laser_info.parent_signature = Objects[parent].signature;
|
|
|
|
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)
|
|
|
|
if (Objects[parent].type == OBJ_WEAPON) {
|
|
|
|
int highest_parent = parent;
|
|
|
|
|
|
|
|
while (Objects[highest_parent].type == OBJ_WEAPON) {
|
|
|
|
highest_parent = Objects[highest_parent].ctype.laser_info.parent_num;
|
|
|
|
obj->ctype.laser_info.parent_num = highest_parent;
|
|
|
|
obj->ctype.laser_info.parent_type = Objects[highest_parent].type;
|
|
|
|
obj->ctype.laser_info.parent_signature = Objects[highest_parent].signature;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create orientation matrix so we can look from this pov
|
|
|
|
// Homing missiles also need an orientation matrix so they know if they can make a turn.
|
|
|
|
if ((obj->render_type == RT_POLYOBJ) || (Weapon_info[obj->id].homing_flag))
|
|
|
|
vm_vector_2_matrix( &obj->orient,direction, &Objects[parent].orient.uvec ,NULL);
|
|
|
|
|
|
|
|
if (( &Objects[parent] != Viewer ) && (Objects[parent].type != OBJ_WEAPON)) {
|
|
|
|
// Muzzle flash
|
|
|
|
if (Weapon_info[obj->id].flash_vclip > -1 )
|
|
|
|
object_create_muzzle_flash( obj->segnum, &obj->pos, Weapon_info[obj->id].flash_size, Weapon_info[obj->id].flash_vclip );
|
|
|
|
}
|
|
|
|
|
|
|
|
// Re-enable, 09/09/94:
|
|
|
|
volume = F1_0;
|
|
|
|
if (Weapon_info[obj->id].flash_sound > -1 ) {
|
|
|
|
if (make_sound) {
|
|
|
|
if ( parent == (Viewer-Objects) ) {
|
|
|
|
if (weapon_type == VULCAN_ID) // Make your own vulcan gun 1/2 as loud.
|
|
|
|
volume = F1_0 / 2;
|
|
|
|
digi_play_sample( Weapon_info[obj->id].flash_sound, volume );
|
|
|
|
} else {
|
|
|
|
digi_link_sound_to_pos( Weapon_info[obj->id].flash_sound, obj->segnum, 0, &obj->pos, 0, volume );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// WARNING! NOTE! HEY! DEBUG! ETC! --MK 10/26/94
|
|
|
|
// This is John's new code to fire the laser from the gun tip so that the back end of the laser bolt is
|
|
|
|
// at the gun tip. A problem that needs to be fixed is that the laser bolt might be in another segment.
|
|
|
|
// Use find_vector_interesection to detect the segment.
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
if ((Objects[parent].type != OBJ_WEAPON) && (Weapon_info[weapon_type].render_type != WEAPON_RENDER_NONE) && (weapon_type != FLARE_ID)) {
|
|
|
|
// if ((Objects[parent].type != OBJ_WEAPON) && (weapon_type != FLARE_ID) ) {
|
|
|
|
vms_vector end_pos;
|
|
|
|
int end_segnum;
|
|
|
|
|
|
|
|
vm_vec_scale_add( &end_pos, &obj->pos, direction, Laser_offset+(laser_length/2) );
|
|
|
|
end_segnum = find_point_seg(&end_pos, obj->segnum);
|
|
|
|
if (end_segnum != obj->segnum) {
|
|
|
|
// mprintf(0, "Warning: Laser tip not in same segment as player.\n");
|
|
|
|
if (end_segnum != -1) {
|
|
|
|
obj->pos = end_pos;
|
|
|
|
obj_relink(obj-Objects, end_segnum);
|
|
|
|
} else
|
|
|
|
mprintf((0, "Warning: Laser tip outside mine. Laser not being moved to end of gun.\n"));
|
|
|
|
} 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.
|
|
|
|
if (weapon_type == PROXIMITY_ID) {
|
|
|
|
parent_speed = vm_vec_mag_quick(&Objects[parent].mtype.phys_info.velocity);
|
|
|
|
if (vm_vec_dot(&Objects[parent].mtype.phys_info.velocity, &Objects[parent].orient.fvec) < 0)
|
|
|
|
parent_speed = -parent_speed;
|
|
|
|
// if(parent_speed>(F1_0*60))
|
|
|
|
// parent_speed=F1_0*1600;
|
|
|
|
/* { char *murp;
|
|
|
|
sprintf(murp,"%i.%i",parent_speed>>16,(parent_speed<<16)>>16);
|
|
|
|
nm_messagebox(NULL,1,"OK",murp);
|
|
|
|
}*/
|
|
|
|
} else
|
|
|
|
parent_speed = 0;
|
|
|
|
|
|
|
|
weapon_speed = Weapon_info[obj->id].speed[Difficulty_level];
|
|
|
|
|
|
|
|
// Ugly hack (too bad we're on a deadline), for homing missiles dropped by smart bomb, start them out slower.
|
|
|
|
if ((obj->id == PLAYER_SMART_HOMING_ID) || (obj->id == ROBOT_SMART_HOMING_ID))
|
|
|
|
weapon_speed /= 4;
|
|
|
|
|
|
|
|
if (Weapon_info[obj->id].thrust != 0)
|
|
|
|
weapon_speed /= 2;
|
|
|
|
|
|
|
|
vm_vec_copy_scale( &obj->mtype.phys_info.velocity, direction, weapon_speed + parent_speed );
|
|
|
|
////Debug 101594
|
|
|
|
//if ((vm_vec_mag(&obj->mtype.phys_info.velocity) == 0) && (obj->id != PROXIMITY_ID))
|
|
|
|
// Int3(); // Curious. This weapon starts with a velocity of 0 and it's not a proximity bomb.
|
|
|
|
|
|
|
|
// Set thrust
|
|
|
|
if (Weapon_info[weapon_type].thrust != 0) {
|
|
|
|
obj->mtype.phys_info.thrust = obj->mtype.phys_info.velocity;
|
|
|
|
vm_vec_scale(&obj->mtype.phys_info.thrust, fixdiv(Weapon_info[obj->id].thrust, weapon_speed+parent_speed));
|
|
|
|
}
|
|
|
|
|
|
|
|
// THIS CODE MAY NOT BE NEEDED... it was used to move the lasers out of the gun, since the
|
|
|
|
// laser pos is acutally the head of the laser, and we want the tail to be at the starting
|
|
|
|
// point, not the head.
|
|
|
|
// object_move_one( obj );
|
|
|
|
// This next, apparently redundant line, appears necessary due to a hack in render.c
|
|
|
|
// obj->lifeleft = Weapon_info[obj->id].lifetime;
|
|
|
|
|
|
|
|
if ((obj->type == OBJ_WEAPON) && (obj->id == FLARE_ID))
|
|
|
|
obj->lifeleft += (d_rand()-16384) << 2; // add in -2..2 seconds
|
|
|
|
|
|
|
|
// mprintf( 0, "Weapon speed = %.1f (%.1f)\n", f2fl(Weapon_info[obj->id].speed[Difficulty_level] + parent_speed), f2fl(parent_speed) );
|
|
|
|
|
|
|
|
return objnum;
|
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------------------------------------
|
|
|
|
// Calls Laser_create_new, but takes care of the segment and point computation for you.
|
|
|
|
int Laser_create_new_easy( vms_vector * direction, vms_vector * position, int parent, int weapon_type, int make_sound )
|
|
|
|
{
|
|
|
|
fvi_query fq;
|
|
|
|
fvi_info hit_data;
|
|
|
|
object *pobjp = &Objects[parent];
|
|
|
|
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.
|
|
|
|
|
|
|
|
fq.p0 = &pobjp->pos;
|
|
|
|
fq.startseg = pobjp->segnum;
|
|
|
|
fq.p1 = position;
|
|
|
|
fq.rad = 0;
|
|
|
|
fq.thisobjnum = pobjp-Objects;
|
|
|
|
fq.ignore_obj_list = NULL;
|
|
|
|
fq.flags = FQ_TRANSWALL | FQ_CHECK_OBJS; //what about trans walls???
|
|
|
|
|
|
|
|
fate = find_vector_intersection(&fq, &hit_data);
|
|
|
|
if (fate != HIT_NONE || hit_data.hit_seg==-1) {
|
|
|
|
mprintf((1, "Warning: Laser from parent=%i stuck in wall or object, didn't fire!\n", parent));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return Laser_create_new( direction, &hit_data.hit_pnt, hit_data.hit_seg, parent, weapon_type, make_sound );
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
int Muzzle_queue_index = 0;
|
|
|
|
|
|
|
|
muzzle_info Muzzle_data[MUZZLE_QUEUE_MAX];
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------------------------------------
|
|
|
|
// Determine if two objects are on a line of sight. If so, return true, else return false.
|
|
|
|
// Calls fvi.
|
|
|
|
int object_to_object_visibility(object *obj1, object *obj2, int trans_type)
|
|
|
|
{
|
|
|
|
fvi_query fq;
|
|
|
|
fvi_info hit_data;
|
|
|
|
int fate;
|
|
|
|
|
|
|
|
fq.p0 = &obj1->pos;
|
|
|
|
fq.startseg = obj1->segnum;
|
|
|
|
fq.p1 = &obj2->pos;
|
|
|
|
fq.rad = 0x10;
|
|
|
|
fq.thisobjnum = obj1-Objects;
|
|
|
|
fq.ignore_obj_list = NULL;
|
|
|
|
fq.flags = trans_type;
|
|
|
|
|
|
|
|
fate = find_vector_intersection(&fq, &hit_data);
|
|
|
|
|
|
|
|
if (fate == HIT_WALL)
|
|
|
|
return 0;
|
|
|
|
else if (fate == HIT_NONE)
|
|
|
|
return 1;
|
|
|
|
else
|
|
|
|
Int3(); // Contact Mike: Oops, what happened? What is fate?
|
|
|
|
// 2 = hit object (impossible), 3 = bad starting point (bad)
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
fix Min_trackable_dot = MIN_TRACKABLE_DOT;
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------------------------------------
|
|
|
|
// 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.
|
|
|
|
int object_is_trackable(int track_goal, object *tracker)
|
|
|
|
{
|
|
|
|
fix dot; //, dist_to_goal;
|
|
|
|
vms_vector vector_to_goal;
|
|
|
|
object *objp;
|
|
|
|
|
|
|
|
if (track_goal == -1)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (Game_mode & GM_MULTI_COOP)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
objp = &Objects[track_goal];
|
|
|
|
|
|
|
|
// Don't track player if he's cloaked.
|
|
|
|
if ((track_goal == Players[Player_num].objnum) && (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
// Can't track AI object if he's cloaked.
|
|
|
|
if (objp->type == OBJ_ROBOT)
|
|
|
|
if (objp->ctype.ai_info.CLOAKED)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
vm_vec_sub(&vector_to_goal, &objp->pos, &tracker->pos);
|
|
|
|
vm_vec_normalize_quick(&vector_to_goal);
|
|
|
|
dot = vm_vec_dot(&vector_to_goal, &tracker->orient.fvec);
|
|
|
|
|
|
|
|
// mprintf((0, "object_is_trackable: [%3i] %7.3f, min = %7.3f\n", track_goal, f2fl(dot), f2fl(Min_trackable_dot)));
|
|
|
|
|
|
|
|
if (dot >= Min_trackable_dot) {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------------------------
|
|
|
|
// Find object to home in on.
|
|
|
|
// Scan list of objects rendered last frame, find one that satisfies function of nearness to center and distance.
|
|
|
|
int find_homing_object(vms_vector *curpos, object *tracker)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
fix max_dot = -F1_0*2;
|
|
|
|
int best_objnum = -1;
|
|
|
|
|
|
|
|
if (!Weapon_info[tracker->id].homing_flag) {
|
|
|
|
Int3(); // Contact Mike: This is a bad and stupid thing. Who called this routine with an illegal laser type??
|
|
|
|
return 0; // Track the damn stupid player for causing this problem!
|
|
|
|
}
|
|
|
|
|
|
|
|
// Find an object to track based on game mode (eg, whether in network play) and who fired it.
|
|
|
|
if (Game_mode & GM_MULTI) {
|
|
|
|
// In network mode.
|
|
|
|
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 {
|
|
|
|
Assert(tracker->ctype.laser_info.parent_type == OBJ_ROBOT);
|
|
|
|
return find_homing_object_complete(curpos, tracker, OBJ_PLAYER, -1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Not in network mode. If not fired by player, then track player.
|
|
|
|
if (tracker->ctype.laser_info.parent_num != Players[Player_num].objnum) {
|
|
|
|
if (!(Players[Player_num].flags & PLAYER_FLAGS_CLOAKED))
|
|
|
|
best_objnum = ConsoleObject - Objects;
|
|
|
|
} else {
|
|
|
|
// Not in network mode and fired by player.
|
|
|
|
for (i=Num_rendered_objects-1; i>=0; i--) {
|
|
|
|
fix dot; //, dist;
|
|
|
|
vms_vector vec_to_curobj;
|
|
|
|
int objnum = Ordered_rendered_object_list[i];
|
|
|
|
object *curobjp = &Objects[objnum];
|
|
|
|
|
|
|
|
if (objnum == Players[Player_num].objnum)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Can't track AI object if he's cloaked.
|
|
|
|
if (curobjp->type == OBJ_ROBOT)
|
|
|
|
if (curobjp->ctype.ai_info.CLOAKED)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
vm_vec_sub(&vec_to_curobj, &curobjp->pos, curpos);
|
|
|
|
vm_vec_normalize_quick(&vec_to_curobj);
|
|
|
|
dot = vm_vec_dot(&vec_to_curobj, &tracker->orient.fvec);
|
|
|
|
|
|
|
|
// mprintf(0, "Object %i: dist = %7.3f, dot = %7.3f\n", objnum, f2fl(dist), f2fl(dot));
|
|
|
|
|
|
|
|
// Note: This uses the constant, not-scaled-by-frametime value, because it is only used
|
|
|
|
// to determine if an object is initially trackable. find_homing_object is called on subsequent
|
|
|
|
// frames to determine if the object remains trackable.
|
|
|
|
// mprintf((0, "find_homing_object: [%3i] %7.3f, min = %7.3f\n", curobjp-Objects, f2fl(dot), f2fl(MIN_TRACKABLE_DOT)));
|
|
|
|
if (dot > MIN_TRACKABLE_DOT) {
|
|
|
|
if (dot > max_dot) {
|
|
|
|
if (object_to_object_visibility(tracker, &Objects[objnum], FQ_TRANSWALL)) {
|
|
|
|
max_dot = dot;
|
|
|
|
best_objnum = objnum;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// mprintf(0, "Selecting object #%i\n=n", best_objnum);
|
|
|
|
|
|
|
|
return best_objnum;
|
|
|
|
}
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------------------------
|
|
|
|
// 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
|
|
|
|
int find_homing_object_complete(vms_vector *curpos, object *tracker, int track_obj_type1, int track_obj_type2)
|
|
|
|
{
|
|
|
|
int objnum;
|
|
|
|
//added/killed on 9/30/98 by Victor Rachels to remove unnescesary vars
|
|
|
|
//-killed- fix max_dot = -F1_0*2;
|
|
|
|
//end this section kill - Victor Rachels
|
|
|
|
int best_objnum = -1;
|
|
|
|
|
|
|
|
//added on 8/14/98 by Victor Rachels to fix player/object numbering tracking... doing best distance now
|
|
|
|
//moved on 9/29/98 by Victor Rachels ... DUH! yeesh.
|
|
|
|
// fix best_dist=MAX_TRACKABLE_DIST;
|
|
|
|
//end this section - Victor Rachels
|
|
|
|
fix best_dot= MIN_TRACKABLE_DOT;
|
|
|
|
|
|
|
|
if (!Weapon_info[tracker->id].homing_flag) {
|
|
|
|
Int3(); // Contact Mike: This is a bad and stupid thing. Who called this routine with an illegal laser type??
|
|
|
|
return 0; // Track the damn stupid player for causing this problem!
|
|
|
|
}
|
|
|
|
|
|
|
|
for (objnum=0; objnum<=Highest_object_index; objnum++) {
|
|
|
|
fix dot, dist;
|
|
|
|
vms_vector vec_to_curobj;
|
|
|
|
object *curobjp = &Objects[objnum];
|
|
|
|
|
|
|
|
if ((curobjp->type != track_obj_type1) && (curobjp->type != track_obj_type2))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (objnum == tracker->ctype.laser_info.parent_num) // Don't track shooter
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Don't track cloaked players.
|
|
|
|
if (curobjp->type == OBJ_PLAYER)
|
|
|
|
{
|
|
|
|
if (Players[curobjp->id].flags & PLAYER_FLAGS_CLOAKED)
|
|
|
|
continue;
|
|
|
|
// Don't track teammates in team games
|
|
|
|
#ifdef NETWORK
|
|
|
|
if ((Game_mode & GM_TEAM) && (Objects[tracker->ctype.laser_info.parent_num].type == OBJ_PLAYER) && (get_team(curobjp->id) == get_team(Objects[tracker->ctype.laser_info.parent_num].id)))
|
|
|
|
continue;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
// Can't track AI object if he's cloaked.
|
|
|
|
if (curobjp->type == OBJ_ROBOT)
|
|
|
|
if (curobjp->ctype.ai_info.CLOAKED)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
vm_vec_sub(&vec_to_curobj, &curobjp->pos, curpos);
|
|
|
|
dist = vm_vec_mag_quick(&vec_to_curobj);
|
|
|
|
|
|
|
|
//added/rewritten on 9/30/98 by Victor Rachels to calc less
|
|
|
|
/* if(dist < best_dist)
|
|
|
|
{
|
|
|
|
vm_vec_normalize_quick(&vec_to_curobj);
|
|
|
|
dot = vm_vec_dot(&vec_to_curobj, &tracker->orient.fvec);
|
|
|
|
if(dot > MIN_TRACKABLE_DOT)
|
|
|
|
if(object_to_object_visibility(tracker, &Objects[objnum], FQ_TRANSWALL))
|
|
|
|
{
|
|
|
|
best_dist = dist;
|
|
|
|
best_objnum=objnum;
|
|
|
|
}
|
|
|
|
}*/
|
|
|
|
//end rewrite - Victor Rachels
|
|
|
|
//added/changed on 10/16/98 by Victor Rachels to track on closest to reticle
|
|
|
|
if(dist < MAX_TRACKABLE_DIST)
|
|
|
|
{
|
|
|
|
vm_vec_normalize_quick(&vec_to_curobj);
|
|
|
|
dot = vm_vec_dot(&vec_to_curobj, &tracker->orient.fvec);
|
|
|
|
if(dot > best_dot)
|
|
|
|
if(object_to_object_visibility(tracker, &Objects[objnum], FQ_TRANSWALL))
|
|
|
|
{
|
|
|
|
best_dot = dot;
|
|
|
|
best_objnum=objnum;
|
|
|
|
// hud_message(MSGC_GAME_FEEDBACK,"dot: %d bestdot:%d",dot,best_dot);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//end rewrite - Victor Rachels
|
|
|
|
|
|
|
|
//added/killed on 9/30/98 by Victor Rachels to remove unnescessary compares
|
|
|
|
//-killed- if ( dist < MAX_TRACKABLE_DIST ) {
|
|
|
|
//-killed- vm_vec_normalize_quick(&vec_to_curobj);
|
|
|
|
//-killed- dot = vm_vec_dot(&vec_to_curobj, &tracker->orient.fvec);
|
|
|
|
//-killed- // Note: This uses the constant, not-scaled-by-frametime value, because it is only used
|
|
|
|
//-killed- // to determine if an object is initially trackable. find_homing_object is called on subsequent
|
|
|
|
//-killed- // frames to determine if the object remains trackable.
|
|
|
|
//-killed- // mprintf((0, "fho_complete: [%3i] %7.3f, min = %7.3f\n", curobjp-Objects, f2fl(dot), f2fl(MIN_TRACKABLE_DOT)));
|
|
|
|
//-killed- if (dot > MIN_TRACKABLE_DOT) {
|
|
|
|
//-killed- //mprintf(0,
|
|
|
|
//-killed- //hud_message(MSGC_GAME_FEEDBACK, "Object %i: dist = %7.3f bdist = %7.3f, dot = %7.3f\n", objnum, f2fl(dist), f2fl(best_dist), f2fl(dot) );
|
|
|
|
//-killed- if (dot > max_dot) {
|
|
|
|
//-killed- if (object_to_object_visibility(tracker, &Objects[objnum], FQ_TRANSWALL))
|
|
|
|
//-killed-//added on 8/14/98 by Victor Rachels to change numbering tracking
|
|
|
|
//-killed- if(dist < best_dist)
|
|
|
|
//-killed- {
|
|
|
|
//-killed- max_dot = dot;
|
|
|
|
//-killed- best_dist = dist;
|
|
|
|
//-killed-//end addition - Victor Rachels
|
|
|
|
//-killed- best_objnum = objnum;
|
|
|
|
//-killed- }
|
|
|
|
//-killed-
|
|
|
|
//-killed- }
|
|
|
|
//-killed- }
|
|
|
|
//-killed- }
|
|
|
|
//end this kill - Victor Rachels
|
|
|
|
}
|
|
|
|
|
|
|
|
// mprintf((0, "Selecting object #%i in find_homing_object_complete\n\n", best_objnum));
|
|
|
|
|
|
|
|
return best_objnum;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------------------------
|
|
|
|
// See if legal to keep tracking currently tracked object. If not, see if another object is trackable. If not, return -1,
|
|
|
|
// else return object number of tracking object.
|
|
|
|
int track_track_goal(int track_goal, object *tracker)
|
|
|
|
{
|
|
|
|
if (object_is_trackable(track_goal, tracker))
|
|
|
|
return track_goal;
|
|
|
|
else if ((((tracker-Objects) ^ FrameCount) % 4) == 0) {
|
|
|
|
int rval = -2;
|
|
|
|
|
|
|
|
// If player fired missile, then search for an object, if not, then give up.
|
|
|
|
if (Objects[tracker->ctype.laser_info.parent_num].type == OBJ_PLAYER) {
|
|
|
|
int goal_type;
|
|
|
|
|
|
|
|
if (track_goal == -1)
|
|
|
|
{
|
|
|
|
if (Game_mode & GM_MULTI)
|
|
|
|
{
|
|
|
|
if (Game_mode & GM_MULTI_COOP)
|
|
|
|
rval = find_homing_object_complete( &tracker->pos, tracker, OBJ_ROBOT, -1);
|
|
|
|
else if (Game_mode & GM_MULTI_ROBOTS) // Not cooperative, if robots, track either robot or player
|
|
|
|
rval = find_homing_object_complete( &tracker->pos, tracker, OBJ_PLAYER, OBJ_ROBOT);
|
|
|
|
else // Not cooperative and no robots, track only a player
|
|
|
|
rval = find_homing_object_complete( &tracker->pos, tracker, OBJ_PLAYER, -1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
rval = find_homing_object_complete(&tracker->pos, tracker, OBJ_PLAYER, OBJ_ROBOT);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
goal_type = Objects[tracker->ctype.laser_info.track_goal].type;
|
|
|
|
if ((goal_type == OBJ_PLAYER) || (goal_type == OBJ_ROBOT))
|
|
|
|
rval = find_homing_object_complete(&tracker->pos, tracker, goal_type, -1);
|
|
|
|
else
|
|
|
|
rval = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
int goal_type;
|
|
|
|
|
|
|
|
if (track_goal == -1)
|
|
|
|
rval = find_homing_object_complete(&tracker->pos, tracker, OBJ_PLAYER, -1);
|
|
|
|
else {
|
|
|
|
goal_type = Objects[tracker->ctype.laser_info.track_goal].type;
|
|
|
|
rval = find_homing_object_complete(&tracker->pos, tracker, goal_type, -1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Assert(rval != -2); // This means it never got set which is bad! Contact Mike.
|
|
|
|
return rval;
|
|
|
|
}
|
|
|
|
|
|
|
|
//if (track_goal != -1)
|
|
|
|
// mprintf((0, "Object %i not tracking anything.\n", tracker-Objects));
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-------------- Initializes a laser after Fire is pressed -----------------
|
|
|
|
|
|
|
|
void Laser_player_fire_spread_delay(object *obj, int laser_type, int gun_num, fix spreadr, fix spreadu, fix delay_time, int make_sound, int harmless)
|
|
|
|
{
|
|
|
|
int LaserSeg, Fate;
|
|
|
|
vms_vector LaserPos, LaserDir;
|
|
|
|
fvi_query fq;
|
|
|
|
fvi_info hit_data;
|
|
|
|
vms_vector gun_point, *pnt;
|
|
|
|
vms_matrix m;
|
|
|
|
int objnum;
|
|
|
|
|
|
|
|
// Find the initial position of the laser
|
|
|
|
pnt = &Player_ship->gun_points[gun_num];
|
|
|
|
|
|
|
|
vm_copy_transpose_matrix(&m,&obj->orient);
|
|
|
|
vm_vec_rotate(&gun_point,pnt,&m);
|
|
|
|
|
|
|
|
vm_vec_add(&LaserPos,&obj->pos,&gun_point);
|
|
|
|
|
|
|
|
// If supposed to fire at a delayed time (delay_time), then move this point backwards.
|
|
|
|
if (delay_time)
|
|
|
|
vm_vec_scale_add2(&LaserPos, &obj->orient.fvec, -fixmul(delay_time, Weapon_info[laser_type].speed[Difficulty_level]));
|
|
|
|
|
|
|
|
// do_muzzle_stuff(obj, &Pos);
|
|
|
|
|
|
|
|
//--------------- Find LaserPos and LaserSeg ------------------
|
|
|
|
fq.p0 = &obj->pos;
|
|
|
|
fq.startseg = obj->segnum;
|
|
|
|
fq.p1 = &LaserPos;
|
|
|
|
fq.rad = 0x10;
|
|
|
|
fq.thisobjnum = obj-Objects;
|
|
|
|
fq.ignore_obj_list = NULL;
|
|
|
|
fq.flags = FQ_CHECK_OBJS;
|
|
|
|
|
|
|
|
Fate = find_vector_intersection(&fq, &hit_data);
|
|
|
|
|
|
|
|
LaserSeg = hit_data.hit_seg;
|
|
|
|
|
|
|
|
if (LaserSeg == -1) //some sort of annoying error
|
|
|
|
return;
|
|
|
|
|
|
|
|
//SORT OF HACK... IF ABOVE WAS CORRECT THIS WOULDNT BE NECESSARY.
|
|
|
|
if ( vm_vec_dist_quick(&LaserPos, &obj->pos) > 0x50000 )
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (Fate==HIT_WALL) {
|
|
|
|
if (delay_time)
|
|
|
|
mprintf((0, "Your DELAYED laser is stuck thru a wall!\n" ));
|
|
|
|
else
|
|
|
|
mprintf((0, "Your laser is stuck thru a wall!\n" ));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Fate==HIT_OBJECT) {
|
|
|
|
// if ( Objects[hit_data.hit_object].type == OBJ_ROBOT )
|
|
|
|
// Objects[hit_data.hit_object].flags |= OF_SHOULD_BE_DEAD;
|
|
|
|
mprintf((0, "Your laser is stuck in an object!\n" ));
|
|
|
|
// if ( Objects[hit_data.hit_object].type != OBJ_POWERUP )
|
|
|
|
// return;
|
|
|
|
//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.
|
|
|
|
LaserDir = obj->orient.fvec;
|
|
|
|
if ((spreadr != 0) || (spreadu != 0)) {
|
|
|
|
vm_vec_scale_add2(&LaserDir, &obj->orient.rvec, spreadr);
|
|
|
|
vm_vec_scale_add2(&LaserDir, &obj->orient.uvec, spreadu);
|
|
|
|
}
|
|
|
|
|
|
|
|
objnum = Laser_create_new( &LaserDir, &LaserPos, LaserSeg, obj-Objects, laser_type, make_sound );
|
|
|
|
if (objnum == -1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// If this weapon is supposed to be silent, set that bit!
|
|
|
|
if (!make_sound)
|
|
|
|
Objects[objnum].flags |= OF_SILENT;
|
|
|
|
|
|
|
|
// If this weapon is supposed to be harmless, set that bit!
|
|
|
|
if (harmless)
|
|
|
|
Objects[objnum].flags |= OF_HARMLESS;
|
|
|
|
|
|
|
|
// If the object firing the laser is the player, then indicate the laser object so robots can dodge.
|
|
|
|
if (obj == ConsoleObject)
|
|
|
|
Player_fired_laser_this_frame = objnum;
|
|
|
|
|
|
|
|
if (Weapon_info[laser_type].homing_flag) {
|
|
|
|
if (obj == ConsoleObject)
|
|
|
|
{
|
|
|
|
Objects[objnum].ctype.laser_info.track_goal = find_homing_object(&LaserPos, &Objects[objnum]);
|
|
|
|
#ifdef NETWORK
|
|
|
|
Network_laser_track = Objects[objnum].ctype.laser_info.track_goal;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
#ifdef NETWORK
|
|
|
|
else // Some other player shot the homing thing
|
|
|
|
{
|
|
|
|
Assert(Game_mode & GM_MULTI);
|
|
|
|
Objects[objnum].ctype.laser_info.track_goal = Network_laser_track;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
// mprintf((0, "Selecting object #%i in find_homing_object_complete\n", Network_laser_track));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------------------------------------
|
|
|
|
void Laser_player_fire_spread(object *obj, int laser_type, int gun_num, fix spreadr, fix spreadu, int make_sound, int harmless)
|
|
|
|
{
|
|
|
|
Laser_player_fire_spread_delay(obj, laser_type, gun_num, spreadr, spreadu, 0, make_sound, harmless);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------------------------------------
|
|
|
|
void Laser_player_fire(object *obj, int laser_type, int gun_num, int make_sound, int harmless)
|
|
|
|
{
|
|
|
|
Laser_player_fire_spread(obj, laser_type, gun_num, 0, 0, make_sound, harmless);
|
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------------------------------------
|
|
|
|
void Flare_create(object *obj)
|
|
|
|
{
|
|
|
|
fix energy_usage;
|
|
|
|
|
|
|
|
energy_usage = Weapon_info[FLARE_ID].energy_usage;
|
|
|
|
|
|
|
|
if (Difficulty_level < 2)
|
|
|
|
energy_usage = fixmul(energy_usage, i2f(Difficulty_level+2)/4);
|
|
|
|
|
|
|
|
if (Players[Player_num].energy > 0) {
|
|
|
|
Players[Player_num].energy -= energy_usage;
|
|
|
|
|
|
|
|
if (Players[Player_num].energy <= 0) {
|
|
|
|
Players[Player_num].energy = 0;
|
|
|
|
auto_select_weapon(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
Laser_player_fire( obj, FLARE_ID, 6, 1, 0);
|
|
|
|
|
|
|
|
#ifdef NETWORK
|
|
|
|
if (Game_mode & GM_MULTI)
|
|
|
|
{
|
|
|
|
Network_laser_fired = 1;
|
|
|
|
Network_laser_gun = FLARE_ID+MISSILE_ADJUST;
|
|
|
|
Network_laser_flags = 0;
|
|
|
|
Network_laser_level = 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2007-01-13 16:22:27 +00:00
|
|
|
#define HOMING_MISSILE_SCALE 16
|
|
|
|
|
|
|
|
#define LIMIT_HOMERS 1
|
|
|
|
#define HOMER_MAX_FPS 30
|
|
|
|
#define HOMER_MIN_DELAY (1000 / HOMER_MAX_FPS)
|
2006-03-20 16:43:15 +00:00
|
|
|
|
|
|
|
//-------------------------------------------------------------------------------------------
|
|
|
|
// Set object *objp's orientation to (or towards if I'm ambitious) its velocity.
|
|
|
|
void homing_missile_turn_towards_velocity(object *objp, vms_vector *norm_vel)
|
|
|
|
{
|
|
|
|
vms_vector new_fvec;
|
|
|
|
|
2007-01-13 16:22:27 +00:00
|
|
|
#ifdef LIMIT_HOMERS
|
|
|
|
static time_t last_time = -1;
|
|
|
|
static int nFrames = 1;
|
|
|
|
time_t this_time, delta_time;
|
|
|
|
fix frame_time;
|
|
|
|
int fps;
|
|
|
|
if (last_time == -1) {
|
|
|
|
last_time = clock ();
|
|
|
|
frame_time = FrameTime;
|
|
|
|
} else {
|
|
|
|
nFrames++;
|
|
|
|
this_time = clock ();
|
|
|
|
delta_time = this_time - last_time;
|
|
|
|
if (delta_time < HOMER_MIN_DELAY) {
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
fps = (1000 + delta_time / 2) / delta_time;
|
|
|
|
frame_time = fps ? (f1_0 + fps / 2) / fps : f1_0;
|
|
|
|
// frame_time /= nFrames;
|
|
|
|
nFrames = 0;
|
|
|
|
last_time = this_time;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
fix frame_time = FrameTime;
|
|
|
|
#endif
|
|
|
|
|
2006-03-20 16:43:15 +00:00
|
|
|
new_fvec = *norm_vel;
|
|
|
|
|
2007-01-13 16:22:27 +00:00
|
|
|
vm_vec_scale(&new_fvec, frame_time * HOMING_MISSILE_SCALE);
|
2006-03-20 16:43:15 +00:00
|
|
|
vm_vec_add2(&new_fvec, &objp->orient.fvec);
|
|
|
|
vm_vec_normalize_quick(&new_fvec);
|
|
|
|
|
|
|
|
// if ((norm_vel->x == 0) && (norm_vel->y == 0) && (norm_vel->z == 0))
|
|
|
|
// return;
|
|
|
|
|
|
|
|
vm_vector_2_matrix(&objp->orient, &new_fvec, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------------------------------
|
|
|
|
//sequence this laser object for this _frame_ (underscores added here to aid MK in his searching!)
|
|
|
|
void Laser_do_weapon_sequence(object *obj)
|
|
|
|
{
|
|
|
|
Assert(obj->control_type == CT_WEAPON);
|
|
|
|
|
|
|
|
if (obj->lifeleft < 0 ) { // We died of old age
|
|
|
|
obj->flags |= OF_SHOULD_BE_DEAD;
|
|
|
|
if ( Weapon_info[obj->id].damage_radius )
|
|
|
|
explode_badass_weapon(obj);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
//delete weapons that are not moving
|
|
|
|
if ( !((FrameCount ^ obj->signature) & 3) &&
|
|
|
|
(obj->id != FLARE_ID) &&
|
|
|
|
(Weapon_info[obj->id].speed[Difficulty_level] > 0) &&
|
|
|
|
(vm_vec_mag_quick(&obj->mtype.phys_info.velocity) < F2_0)) {
|
|
|
|
obj_delete(obj-Objects);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( obj->id == FUSION_ID ) { //always set fusion weapon to max vel
|
|
|
|
|
|
|
|
vm_vec_normalize_quick(&obj->mtype.phys_info.velocity);
|
|
|
|
|
|
|
|
vm_vec_scale(&obj->mtype.phys_info.velocity, Weapon_info[obj->id].speed[Difficulty_level]);
|
|
|
|
}
|
|
|
|
|
|
|
|
// For homing missiles, turn towards target.
|
|
|
|
if (Weapon_info[obj->id].homing_flag) {
|
|
|
|
vms_vector vector_to_object, temp_vec;
|
|
|
|
fix dot;
|
|
|
|
fix speed, max_speed;
|
|
|
|
|
|
|
|
// For first 1/2 second of life, missile flies straight.
|
|
|
|
if (obj->ctype.laser_info.creation_time + HOMING_MISSILE_STRAIGHT_TIME < GameTime) {
|
|
|
|
|
|
|
|
int track_goal = obj->ctype.laser_info.track_goal;
|
|
|
|
|
|
|
|
// If it's time to do tracking, then it's time to grow up, stop bouncing and start exploding!.
|
|
|
|
if ((obj->id == ROBOT_SMART_HOMING_ID) || (obj->id == PLAYER_SMART_HOMING_ID)) {
|
|
|
|
// if (obj->mtype.phys_info.flags & PF_BOUNCE) mprintf(0, "Debouncing smart child %i\n", obj-Objects);
|
|
|
|
obj->mtype.phys_info.flags &= ~PF_BOUNCE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure the object we are tracking is still trackable.
|
|
|
|
track_goal = track_track_goal(track_goal, obj);
|
|
|
|
|
|
|
|
if (track_goal == Players[Player_num].objnum) {
|
|
|
|
fix dist_to_player;
|
|
|
|
|
|
|
|
dist_to_player = vm_vec_dist_quick(&obj->pos, &Objects[track_goal].pos);
|
|
|
|
if ((dist_to_player < Players[Player_num].homing_object_dist) || (Players[Player_num].homing_object_dist < 0))
|
|
|
|
Players[Player_num].homing_object_dist = dist_to_player;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (track_goal != -1) {
|
|
|
|
//edited 8/3/98 by Victor Rachels to fix homer high cpu effect
|
|
|
|
fixang tang;
|
|
|
|
//end this section editing - Victor Rachels
|
|
|
|
|
|
|
|
vm_vec_sub(&vector_to_object, &Objects[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[obj->id].speed[Difficulty_level];
|
|
|
|
if (speed+F1_0 < max_speed) {
|
|
|
|
speed += fixmul(max_speed, FrameTime/2);
|
|
|
|
if (speed > max_speed)
|
|
|
|
speed = max_speed;
|
|
|
|
}
|
|
|
|
|
|
|
|
dot = vm_vec_dot(&temp_vec, &vector_to_object);
|
|
|
|
|
|
|
|
//edited 8/2/98 by Victor Rachels to fix homer high cpu effect
|
|
|
|
//check angle would be turning, if greater than maximum angle/sec, set to
|
|
|
|
// new angle, scaled by FrameTime for how much to turn this frame
|
|
|
|
|
|
|
|
tang = vm_vec_delta_ang_norm(&temp_vec,&vector_to_object,NULL);
|
|
|
|
if(tang > F1_0/(80/(Difficulty_level+1)-10 ) * FrameTime)
|
|
|
|
{
|
|
|
|
//make new vector at MaxHomingAng between previous vector and otherwise new vector;
|
|
|
|
//Vector = ( (New - Old)* MaxAngle/OldAngle ) + Old
|
|
|
|
vm_vec_sub2(&temp_vec,&vector_to_object);
|
|
|
|
vm_vec_scale(&temp_vec,F1_0/(80/(Difficulty_level+1)-10 ) * FrameTime/tang);
|
|
|
|
vm_vec_add2(&temp_vec,&vector_to_object);
|
|
|
|
}
|
|
|
|
//end changes for fixing homers - Victor Rachels
|
|
|
|
|
|
|
|
vm_vec_add2(&temp_vec, &vector_to_object);
|
|
|
|
// The boss' smart children track better...
|
|
|
|
if (Weapon_info[obj->id].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);
|
|
|
|
|
|
|
|
if (absdot > F1_0/8) {
|
|
|
|
if (absdot > F1_0/4)
|
|
|
|
absdot = F1_0/4;
|
|
|
|
lifelost = fixmul(absdot*16, FrameTime);
|
|
|
|
obj->lifeleft -= lifelost;
|
|
|
|
// mprintf((0, "Missile %3i, dot = %7.3f life lost = %7.3f, life left = %7.3f\n", obj-Objects, f2fl(dot), f2fl(lifelost), f2fl(obj->lifeleft)));
|
|
|
|
}
|
|
|
|
//added 8/14/98 by Victor Rachels to make homers lose life while going straight, too
|
|
|
|
obj->lifeleft -= fixmul(F1_0, FrameTime);
|
|
|
|
//end addition - Victor Rachels
|
|
|
|
//added 8/18/98 by Victor Rachels to make homers go straight after lifelost
|
|
|
|
//killed 8/28/98 by Victor Rachles - doesn't work
|
|
|
|
// if(obj->lifeleft <= F1_0)
|
|
|
|
// {
|
|
|
|
// obj->lifeleft = F1_0*100;
|
|
|
|
// Weapon_info[obj->id].homing_flag=0;
|
|
|
|
// }
|
|
|
|
//end kill - Victor Rachels
|
|
|
|
//end addition - Victor Rachels
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Only polygon objects have visible orientation, so only they should turn.
|
|
|
|
if (Weapon_info[obj->id].render_type == WEAPON_RENDER_POLYMODEL)
|
|
|
|
homing_missile_turn_towards_velocity(obj, &temp_vec); // temp_vec is normalized velocity.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure weapon is not moving faster than allowed speed.
|
|
|
|
if (Weapon_info[obj->id].thrust != 0) {
|
|
|
|
fix weapon_speed;
|
|
|
|
|
|
|
|
weapon_speed = vm_vec_mag_quick(&obj->mtype.phys_info.velocity);
|
|
|
|
if (weapon_speed > Weapon_info[obj->id].speed[Difficulty_level]) {
|
|
|
|
fix scale_factor;
|
|
|
|
|
|
|
|
scale_factor = fixdiv(Weapon_info[obj->id].speed[Difficulty_level], weapon_speed);
|
|
|
|
vm_vec_scale(&obj->mtype.phys_info.velocity, scale_factor);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int Spreadfire_toggle=0;
|
|
|
|
fix Last_laser_fired_time = 0;
|
|
|
|
|
|
|
|
extern int Player_fired_laser_this_frame;
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------------------------------
|
|
|
|
// Assumption: This is only called by the actual console player, not for
|
|
|
|
// network players
|
|
|
|
|
|
|
|
int do_laser_firing_player(void)
|
|
|
|
{
|
|
|
|
static int vulcan_ammo_used = 0;
|
|
|
|
player *plp = &Players[Player_num];
|
|
|
|
fix energy_used;
|
|
|
|
int ammo_used;
|
|
|
|
int weapon_index;
|
|
|
|
int rval = 0;
|
|
|
|
int nfires = 1;
|
|
|
|
// fix addval;
|
|
|
|
|
|
|
|
if (Player_is_dead)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
weapon_index = Primary_weapon_to_weapon_info[Primary_weapon];
|
|
|
|
energy_used = Weapon_info[weapon_index].energy_usage;
|
|
|
|
|
|
|
|
if (Difficulty_level < 2)
|
|
|
|
energy_used = fixmul(energy_used, i2f(Difficulty_level+2)/4);
|
|
|
|
|
|
|
|
ammo_used = Weapon_info[weapon_index].ammo_usage;
|
|
|
|
|
|
|
|
// addval = 2*FrameTime;
|
|
|
|
// if (addval > F1_0)
|
|
|
|
// addval = F1_0;
|
|
|
|
|
|
|
|
if (Last_laser_fired_time + 2*FrameTime < GameTime)
|
|
|
|
Next_laser_fire_time = GameTime;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
while (Next_laser_fire_time <= GameTime) {
|
|
|
|
if ((plp->energy >= energy_used) || ((Primary_weapon == VULCAN_INDEX) && (plp->primary_ammo[Primary_weapon] >= ammo_used)) ) {
|
|
|
|
int laser_level, flags;
|
|
|
|
|
|
|
|
//mprintf(0, ".");
|
|
|
|
//added/moved on 8/16/98 by Victor Rachels (also from Arne de Bruijn) to fix EBlB
|
|
|
|
Last_laser_fired_time = GameTime;
|
|
|
|
//end move - Victor Rachels
|
|
|
|
|
|
|
|
if (Laser_rapid_fire!=0xBADA55)
|
|
|
|
Next_laser_fire_time += Weapon_info[weapon_index].fire_wait;
|
|
|
|
else
|
|
|
|
Next_laser_fire_time += F1_0/25;
|
|
|
|
|
|
|
|
laser_level = Players[Player_num].laser_level;
|
|
|
|
|
|
|
|
flags = 0;
|
|
|
|
|
|
|
|
if (Primary_weapon == SPREADFIRE_INDEX) {
|
|
|
|
if (Spreadfire_toggle)
|
|
|
|
flags |= LASER_SPREADFIRE_TOGGLED;
|
|
|
|
Spreadfire_toggle = !Spreadfire_toggle;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Players[Player_num].flags & PLAYER_FLAGS_QUAD_LASERS)
|
|
|
|
flags |= LASER_QUAD;
|
|
|
|
|
|
|
|
rval += do_laser_firing(Players[Player_num].objnum, Primary_weapon, laser_level, flags, nfires);
|
|
|
|
|
|
|
|
plp->energy -= (energy_used * rval) / Weapon_info[weapon_index].fire_count;
|
|
|
|
if (plp->energy < 0)
|
|
|
|
plp->energy = 0;
|
|
|
|
|
|
|
|
if (Primary_weapon == VULCAN_INDEX && Laser_drop_vulcan_ammo)
|
|
|
|
if ((vulcan_ammo_used += ammo_used) >= VULCAN_AMMO_AMOUNT)
|
|
|
|
{
|
|
|
|
vulcan_ammo_used = 0;
|
|
|
|
if(plp->primary_ammo[VULCAN_INDEX] >= VULCAN_AMMO_AMOUNT*2){
|
|
|
|
#ifdef NETWORK
|
|
|
|
maybe_drop_net_powerup(POW_VULCAN_AMMO);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ammo_used > plp->primary_ammo[Primary_weapon])
|
|
|
|
plp->primary_ammo[Primary_weapon] = 0;
|
|
|
|
else
|
|
|
|
plp->primary_ammo[Primary_weapon] -= ammo_used;
|
|
|
|
|
|
|
|
auto_select_weapon(0); // Make sure the player can fire from this weapon.
|
|
|
|
|
|
|
|
} else
|
|
|
|
break; // Couldn't fire weapon, so abort.
|
|
|
|
}
|
|
|
|
|
|
|
|
//mprintf(0, " fires = %i\n", rval);
|
|
|
|
|
|
|
|
Global_laser_firing_count = 0;
|
|
|
|
|
|
|
|
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.
|
|
|
|
// More than one shot is fired with a pseudo-delay so that players on slow machines can fire (for themselves
|
|
|
|
// or other players) often enough for things like the vulcan cannon.
|
|
|
|
int do_laser_firing(int objnum, int weapon_num, int level, int flags, int nfires)
|
|
|
|
{
|
|
|
|
object *objp = &Objects[objnum];
|
|
|
|
|
|
|
|
// The Laser_offset is used to "jitter" the laser fire so that lasers don't always appear
|
|
|
|
// right in front of your face. I put it here instead of laser_create_new because I want
|
|
|
|
// both of the dual laser beams to be fired from the same distance.
|
|
|
|
|
|
|
|
//added/changed on 8/14/98 by Victor Rachels to fix fire-through-door bug
|
|
|
|
// Laser_offset = ((F1_0*2)*(d_rand()%10))/10;
|
|
|
|
Laser_offset = 0;
|
|
|
|
//end add/change - Victor Rachels
|
|
|
|
|
|
|
|
switch (weapon_num) {
|
|
|
|
case LASER_INDEX: {
|
|
|
|
Laser_player_fire( objp, level, 0, 1, 0);
|
|
|
|
Laser_player_fire( objp, level, 1, 0, 0);
|
|
|
|
|
|
|
|
if (flags & LASER_QUAD) {
|
|
|
|
// hideous system to make quad laser 1.5x powerful as normal laser, make every other quad laser bolt harmless
|
|
|
|
Laser_player_fire( objp, level, 2, 0, 0);
|
|
|
|
Laser_player_fire( objp, level, 3, 0, 0);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case VULCAN_INDEX: {
|
|
|
|
// Only make sound for 1/4 of vulcan bullets.
|
|
|
|
int make_sound = 1;
|
|
|
|
//if (d_rand() > 24576)
|
|
|
|
// make_sound = 1;
|
|
|
|
Laser_player_fire_spread( objp, VULCAN_ID, 6, d_rand()/8 - 32767/16, d_rand()/8 - 32767/16, make_sound, 0);
|
|
|
|
if (nfires > 1) {
|
|
|
|
Laser_player_fire_spread( objp, VULCAN_ID, 6, d_rand()/8 - 32767/16, d_rand()/8 - 32767/16, 0, 0);
|
|
|
|
if (nfires > 2) {
|
|
|
|
Laser_player_fire_spread( objp, VULCAN_ID, 6, d_rand()/8 - 32767/16, d_rand()/8 - 32767/16, 0, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SPREADFIRE_INDEX:
|
|
|
|
if (flags & LASER_SPREADFIRE_TOGGLED) {
|
|
|
|
Laser_player_fire_spread( objp, SPREADFIRE_ID, 6, F1_0/16, 0, 0, 0);
|
|
|
|
Laser_player_fire_spread( objp, SPREADFIRE_ID, 6, -F1_0/16, 0, 0, 0);
|
|
|
|
Laser_player_fire_spread( objp, SPREADFIRE_ID, 6, 0, 0, 1, 0);
|
|
|
|
} else {
|
|
|
|
Laser_player_fire_spread( objp, SPREADFIRE_ID, 6, 0, F1_0/16, 0, 0);
|
|
|
|
Laser_player_fire_spread( objp, SPREADFIRE_ID, 6, 0, -F1_0/16, 0, 0);
|
|
|
|
Laser_player_fire_spread( objp, SPREADFIRE_ID, 6, 0, 0, 1, 0);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
#ifndef SHAREWARE
|
|
|
|
case PLASMA_INDEX:
|
|
|
|
Laser_player_fire( objp, PLASMA_ID, 0, 1, 0);
|
|
|
|
Laser_player_fire( objp, PLASMA_ID, 1, 0, 0);
|
|
|
|
if (nfires > 1) {
|
|
|
|
Laser_player_fire_spread_delay( objp, PLASMA_ID, 0, 0, 0, FrameTime/2, 1, 0);
|
|
|
|
Laser_player_fire_spread_delay( objp, PLASMA_ID, 1, 0, 0, FrameTime/2, 0, 0);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case FUSION_INDEX: {
|
|
|
|
vms_vector force_vec;
|
|
|
|
|
|
|
|
// mprintf((0, "Fusion multiplier %f.\n", f2fl(Fusion_charge)));
|
|
|
|
|
|
|
|
Laser_player_fire( objp, FUSION_ID, 0, 1, 0);
|
|
|
|
Laser_player_fire( objp, FUSION_ID, 1, 1, 0);
|
|
|
|
|
2006-10-06 14:41:31 +00:00
|
|
|
flags = (sbyte)(Fusion_charge >> 12);
|
2006-03-20 16:43:15 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
phys_apply_force(objp, &force_vec);
|
|
|
|
|
|
|
|
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;
|
|
|
|
phys_apply_rot(objp, &force_vec);
|
|
|
|
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
default:
|
|
|
|
Int3(); // Contact Mike: Unknown Primary weapon type, setting to 0.
|
|
|
|
Primary_weapon = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set values to be recognized during comunication phase, if we are the
|
|
|
|
// one shooting
|
|
|
|
#ifdef NETWORK
|
|
|
|
if ((Game_mode & GM_MULTI) && (objnum == Players[Player_num].objnum))
|
|
|
|
{
|
|
|
|
// mprintf((0, "Flags on fire: %d.\n", flags));
|
|
|
|
Network_laser_fired = nfires;
|
|
|
|
Network_laser_gun = weapon_num;
|
|
|
|
Network_laser_flags = flags;
|
|
|
|
Network_laser_level = level;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return nfires;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define MAX_SMART_DISTANCE (F1_0*150)
|
|
|
|
#define MAX_OBJDISTS 30
|
|
|
|
#define NUM_SMART_CHILDREN 6
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
int objnum;
|
|
|
|
fix dist;
|
|
|
|
} objdist;
|
|
|
|
|
|
|
|
// -------------------------------------------------------------------------------------------
|
|
|
|
// if goal_obj == -1, then create random vector
|
|
|
|
int create_homing_missile(object *objp, int goal_obj, int objtype, int make_sound)
|
|
|
|
{
|
|
|
|
int objnum;
|
|
|
|
vms_vector vector_to_goal;
|
|
|
|
vms_vector random_vector;
|
|
|
|
//vms_vector goal_pos;
|
|
|
|
|
|
|
|
if (goal_obj == -1) {
|
|
|
|
make_random_vector(&vector_to_goal);
|
|
|
|
} else {
|
|
|
|
vm_vec_sub(&vector_to_goal, &Objects[goal_obj].pos, &objp->pos);
|
|
|
|
vm_vec_normalize_quick(&vector_to_goal);
|
|
|
|
make_random_vector(&random_vector);
|
|
|
|
vm_vec_scale_add2(&vector_to_goal, &random_vector, F1_0/4);
|
|
|
|
vm_vec_normalize_quick(&vector_to_goal);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create a vector towards the goal, then add some noise to it.
|
|
|
|
objnum = Laser_create_new(&vector_to_goal, &objp->pos, objp->segnum, objp-Objects, objtype, make_sound);
|
|
|
|
if (objnum == -1)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
|
|
|
Objects[objnum].ctype.laser_info.track_goal = goal_obj;
|
|
|
|
|
|
|
|
return objnum;
|
|
|
|
}
|
|
|
|
|
|
|
|
// -------------------------------------------------------------------------------------------
|
|
|
|
// Create the children of a smart bomb, which is a bunch of homing missiles.
|
|
|
|
void create_smart_children(object *objp)
|
|
|
|
{
|
|
|
|
int make_sound;
|
|
|
|
int numobjs=0;
|
|
|
|
int parent_type;
|
|
|
|
objdist objlist[MAX_OBJDISTS];
|
|
|
|
|
|
|
|
if (Game_mode & GM_MULTI)
|
|
|
|
d_srand(8321L);
|
|
|
|
|
|
|
|
parent_type = objp->ctype.laser_info.parent_type;
|
|
|
|
|
|
|
|
if (objp->id == SMART_ID) {
|
|
|
|
int i, objnum;
|
|
|
|
|
|
|
|
for (objnum=0; objnum<=Highest_object_index; objnum++) {
|
|
|
|
object *curobjp = &Objects[objnum];
|
|
|
|
|
|
|
|
if ((((curobjp->type == OBJ_ROBOT) && (!curobjp->ctype.ai_info.CLOAKED)) || (curobjp->type == OBJ_PLAYER)) && (objnum != objp->ctype.laser_info.parent_num)) {
|
|
|
|
fix dist;
|
|
|
|
|
|
|
|
if (curobjp->type == OBJ_PLAYER)
|
|
|
|
{
|
|
|
|
if ((parent_type == OBJ_PLAYER) && (Game_mode & GM_MULTI_COOP))
|
|
|
|
continue;
|
|
|
|
#ifdef NETWORK
|
|
|
|
if ((Game_mode & GM_TEAM) && (get_team(curobjp->id) == get_team(Objects[objp->ctype.laser_info.parent_num].id)))
|
|
|
|
continue;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
// Robot blobs can't track robots.
|
|
|
|
if (curobjp->type == OBJ_ROBOT)
|
|
|
|
if (parent_type == OBJ_ROBOT)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
dist = vm_vec_dist_quick(&objp->pos, &curobjp->pos);
|
|
|
|
if (dist < MAX_SMART_DISTANCE) {
|
|
|
|
int oovis;
|
|
|
|
|
|
|
|
oovis = object_to_object_visibility(objp, curobjp, FQ_TRANSWALL);
|
|
|
|
|
|
|
|
if (oovis) { //object_to_object_visibility(objp, curobjp, FQ_TRANSWALL)) {
|
|
|
|
objlist[numobjs].objnum = objnum;
|
|
|
|
objlist[numobjs].dist = dist;
|
|
|
|
numobjs++;
|
|
|
|
if (numobjs >= MAX_OBJDISTS) {
|
|
|
|
mprintf((0, "Warning -- too many objects near smart bomb explosion. See laser.c.\n"));
|
|
|
|
numobjs = MAX_OBJDISTS;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
make_sound = 1;
|
|
|
|
if (numobjs == 0) {
|
|
|
|
for (i=0; i<NUM_SMART_CHILDREN; i++) {
|
|
|
|
if (parent_type == OBJ_PLAYER) {
|
|
|
|
int hobjnum;
|
|
|
|
hobjnum = create_homing_missile(objp, -1, PLAYER_SMART_HOMING_ID, make_sound);
|
|
|
|
// mprintf((0, "Object #%i is a PLAYER smart blob.\n", hobjnum));
|
|
|
|
} else {
|
|
|
|
int hobjnum;
|
|
|
|
hobjnum = create_homing_missile(objp, -1, ROBOT_SMART_HOMING_ID, make_sound);
|
|
|
|
// mprintf((0, "Object #%i is a robot smart blob.\n", hobjnum));
|
|
|
|
}
|
|
|
|
make_sound = 0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (i=0; i<NUM_SMART_CHILDREN; i++) {
|
|
|
|
if (parent_type == OBJ_PLAYER) {
|
|
|
|
int hobjnum;
|
|
|
|
hobjnum = create_homing_missile(objp, objlist[(d_rand() * numobjs) >> 15].objnum, PLAYER_SMART_HOMING_ID, make_sound);
|
|
|
|
// mprintf((0, "Object #%i is a PLAYER smart blob.\n", hobjnum));
|
|
|
|
} else {
|
|
|
|
int hobjnum;
|
|
|
|
hobjnum = create_homing_missile(objp, objlist[(d_rand() * numobjs) >> 15].objnum, ROBOT_SMART_HOMING_ID, make_sound);
|
|
|
|
// mprintf((0, "Object #%i is a robot smart blob.\n", hobjnum));
|
|
|
|
}
|
|
|
|
make_sound = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#define CONCUSSION_GUN 4
|
|
|
|
#define HOMING_GUN 4
|
|
|
|
|
|
|
|
#define PROXIMITY_GUN 7
|
|
|
|
#define SMART_GUN 7
|
|
|
|
#define MEGA_GUN 7
|
|
|
|
|
|
|
|
|
|
|
|
int Missile_gun=0;
|
|
|
|
|
|
|
|
// -------------------------------------------------------------------------------------------
|
|
|
|
//changed on 9/16/98 by adb to distinguish between drop bomb and secondary fire
|
|
|
|
static void do_missile_firing_all(int drop_bomb)
|
|
|
|
{
|
|
|
|
static int proximity = 0;
|
|
|
|
|
|
|
|
int weapon = (drop_bomb) ? PROXIMITY_INDEX : Secondary_weapon;
|
|
|
|
|
|
|
|
Assert(weapon < MAX_SECONDARY_WEAPONS);
|
|
|
|
|
|
|
|
if (!Player_is_dead && (Players[Player_num].secondary_ammo[weapon] > 0)) {
|
|
|
|
|
|
|
|
int weapon_index;
|
|
|
|
|
|
|
|
Players[Player_num].secondary_ammo[weapon]--;
|
|
|
|
|
|
|
|
weapon_index = Secondary_weapon_to_weapon_info[weapon];
|
|
|
|
if (Laser_rapid_fire!=0xBADA55)
|
|
|
|
Next_missile_fire_time = GameTime + Weapon_info[weapon_index].fire_wait;
|
|
|
|
else
|
|
|
|
Next_missile_fire_time = GameTime + F1_0/25;
|
|
|
|
|
|
|
|
switch (weapon) {
|
|
|
|
case CONCUSSION_INDEX:
|
|
|
|
Laser_player_fire( ConsoleObject, CONCUSSION_ID, CONCUSSION_GUN+(Missile_gun & 1), 1, 0 );
|
|
|
|
Missile_gun++;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PROXIMITY_INDEX:
|
|
|
|
Laser_offset = F1_0 * -1 * 3;
|
|
|
|
proximity ++;
|
|
|
|
if (proximity == 4)
|
|
|
|
{
|
|
|
|
proximity = 0;
|
|
|
|
#ifdef NETWORK
|
|
|
|
maybe_drop_net_powerup(POW_PROXIMITY_WEAPON);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
Laser_player_fire( ConsoleObject, PROXIMITY_ID, PROXIMITY_GUN, 1, 0);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case HOMING_INDEX:
|
|
|
|
Laser_player_fire( ConsoleObject, HOMING_ID, HOMING_GUN+(Missile_gun & 1), 1, 0 );
|
|
|
|
Missile_gun++;
|
|
|
|
#ifdef NETWORK
|
|
|
|
maybe_drop_net_powerup(POW_HOMING_AMMO_1);
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
|
|
|
|
#ifndef SHAREWARE
|
|
|
|
case SMART_INDEX:
|
|
|
|
Laser_player_fire( ConsoleObject, SMART_ID, SMART_GUN, 1, 0);
|
|
|
|
#ifdef NETWORK
|
|
|
|
maybe_drop_net_powerup(POW_SMARTBOMB_WEAPON);
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MEGA_INDEX:
|
|
|
|
Laser_player_fire( ConsoleObject, MEGA_ID, MEGA_GUN, 1, 0);
|
|
|
|
#ifdef NETWORK
|
|
|
|
maybe_drop_net_powerup(POW_MEGA_WEAPON);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
{ vms_vector force_vec;
|
|
|
|
force_vec.x = -(ConsoleObject->orient.fvec.x << 7);
|
|
|
|
force_vec.y = -(ConsoleObject->orient.fvec.y << 7);
|
|
|
|
force_vec.z = -(ConsoleObject->orient.fvec.z << 7);
|
|
|
|
phys_apply_force(ConsoleObject, &force_vec);
|
|
|
|
|
|
|
|
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;
|
|
|
|
phys_apply_rot(ConsoleObject, &force_vec);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef NETWORK
|
|
|
|
if (Game_mode & GM_MULTI)
|
|
|
|
{
|
|
|
|
Network_laser_gun = weapon+MISSILE_ADJUST;
|
|
|
|
Network_laser_level = 0;
|
|
|
|
Network_laser_flags = (Missile_gun-1);
|
|
|
|
Network_laser_fired = 1;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// don't autoselect if dropping prox and prox not current weapon
|
|
|
|
if (!drop_bomb || Secondary_weapon == PROXIMITY_INDEX)
|
|
|
|
auto_select_weapon(1); //select next missile, if this one out of ammo
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void do_missile_firing(void)
|
|
|
|
{
|
|
|
|
do_missile_firing_all(0);
|
|
|
|
}
|
|
|
|
//end changes - adb
|
|
|
|
|
|
|
|
//added on 9/16/98 by adb to distinguish between drop bomb and secondary fire
|
|
|
|
void do_drop_bomb(void)
|
|
|
|
{
|
|
|
|
do_missile_firing_all(1);
|
|
|
|
}
|
|
|
|
//end additions - adb
|
|
|
|
|
|
|
|
#ifdef NETWORK
|
|
|
|
void net_missile_firing(int player, int gun, int flags)
|
|
|
|
{
|
|
|
|
|
|
|
|
switch (gun-MISSILE_ADJUST) {
|
|
|
|
case CONCUSSION_INDEX:
|
|
|
|
Laser_player_fire( Objects+Players[player].objnum, CONCUSSION_ID, CONCUSSION_GUN+(flags & 1), 1, 0 );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PROXIMITY_INDEX:
|
|
|
|
Laser_player_fire( Objects+Players[player].objnum, PROXIMITY_ID, PROXIMITY_GUN, 1, 0);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case HOMING_INDEX:
|
|
|
|
Laser_player_fire( Objects+Players[player].objnum, HOMING_ID, HOMING_GUN+(flags & 1), 1, 0);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SMART_INDEX:
|
|
|
|
Laser_player_fire( Objects+Players[player].objnum, SMART_ID, SMART_GUN, 1, 0);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MEGA_INDEX:
|
|
|
|
Laser_player_fire( Objects+Players[player].objnum, MEGA_ID, MEGA_GUN, 1, 0);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case FLARE_ID:
|
|
|
|
Laser_player_fire( Objects+Players[player].objnum, FLARE_ID, 6, 1, 0);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
mprintf((0,"net_missing_firing(): Unknown missile weapon type.\n"));
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|