sin-2015/bullet.cpp
1999-04-22 00:00:00 +00:00

348 lines
No EOL
9.5 KiB
C++

// Copyright (C) 1997 by Ritual Entertainment, Inc.
// All rights reserved.
//
// This source is may not be distributed and/or modified without
// expressly written permission by Ritual Entertainment, Inc.
//
// DESCRIPTION:
// Base class for all bullet (hitscan) weapons. Includes definition for shotgun.
//
#include "g_local.h"
#include "specialfx.h"
#include "misc.h"
#include "bullet.h"
#include "q_shared.h"
#include "surface.h"
#include "hoverbike.h" //###
CLASS_DECLARATION( Weapon, BulletWeapon, NULL );
ResponseDef BulletWeapon::Responses[] =
{
{ NULL, NULL }
};
BulletWeapon::BulletWeapon()
{
modelIndex( "sprites/tracer.spr" );
modelIndex( "sprites/bullethole.spr" );
}
void BulletWeapon::TraceAttack
(
Vector start,
Vector end,
int damage,
trace_t *trace,
int numricochets,
int kick,
int dflags,
int meansofdeath,
qboolean server_effects
)
{
Vector org;
Vector dir;
Vector endpos;
int surfflags;
int surftype;
int timeofs;
Entity *ent;
qboolean ricochet;
if ( HitSky( trace ) )
{
return;
}
ricochet = false;
dir = end - start;
dir.normalize();
org = end - dir;
ent = trace->ent->entity;
if ( !trace->surface )
{
surfflags = 0;
surftype = 0;
}
else
{
surfflags = trace->surface->flags;
surftype = SURFACETYPE_FROM_FLAGS( surfflags );
surfaceManager.DamageSurface( trace, damage, owner );
if ( surfflags & SURF_RICOCHET )
ricochet = true;
}
if ( trace->intersect.valid && ent )
{
//
// see if the parent group has ricochet turned on
//
if ( trace->intersect.parentgroup >= 0 )
{
int flags;
flags = gi.Group_Flags( ent->edict->s.modelindex, trace->intersect.parentgroup );
if ( flags & MDL_GROUP_RICOCHET )
{
surftype = ( flags >> 8 ) & 0xf;
ricochet = true;
}
}
}
if ( ent )
{
if ( !(ent->flags & FL_SHIELDS) )
{
if ( ent->flags & FL_SPARKS )
{
// Take care of ricochet effects on the server
if ( server_effects && !ricochet )
{
timeofs = MAX_RICOCHETS - numricochets;
if ( timeofs > 0xf )
{
timeofs = 0xf;
}
gi.WriteByte( svc_temp_entity );
gi.WriteByte( TE_GUNSHOT );
gi.WritePosition( org.vec3() );
gi.WriteDir( trace->plane.normal );
gi.WriteByte( 0 );
gi.multicast( org.vec3(), MULTICAST_PVS );
}
MadeBreakingSound( org, owner );
}
if ( ent->takedamage )
{
if ( trace->intersect.valid )
{
// We hit a valid group so send in location based damage
ent->Damage( this,
owner,
damage,
trace->endpos,
dir,
trace->plane.normal,
kick,
dflags,
meansofdeath,
trace->intersect.parentgroup,
-1,
trace->intersect.damage_multiplier );
}
else
{
// We didn't hit any groups, so send in generic damage
ent->Damage( this,
owner,
damage,
trace->endpos,
dir,
trace->plane.normal,
kick,
dflags,
meansofdeath,
-1,
-1,
1 );
}
}
}
else
{
surftype = SURF_TYPE_METAL;
ricochet = true;
}
}
if ( ricochet && ( server_effects || ( numricochets < MAX_RICOCHETS ) ) )
{
timeofs = MAX_RICOCHETS - numricochets;
if ( timeofs > 0xf )
{
timeofs = 0xf;
}
gi.WriteByte( svc_temp_entity );
gi.WriteByte( TE_GUNSHOT );
gi.WritePosition( org.vec3() );
gi.WriteDir( trace->plane.normal );
gi.WriteByte( timeofs );
gi.multicast( org.vec3(), MULTICAST_PVS );
}
if (
ricochet &&
numricochets &&
damage
)
{
dir += Vector( trace->plane.normal ) * 2;
endpos = org + dir * 8192;
//
// since this is a ricochet, we don't ignore the weapon owner this time.
//
if(DM_FLAG(DF_BBOX_BULLETS))
{
*trace = G_Trace( org, vec_zero, vec_zero, endpos, NULL, MASK_SHOT, "BulletWeapon::TraceAttack" );
}
else
{
*trace = G_FullTrace( org, vec_zero, vec_zero, endpos, 5, NULL, MASK_SHOT, "BulletWeapon::TraceAttack" );
}
if ( trace->fraction != 1.0 )
{
endpos = trace->endpos;
TraceAttack( org, endpos, damage * 0.8f, trace, numricochets - 1, kick, dflags, meansofdeath, true );
}
//###
}
}
void BulletWeapon::FireBullets
(
int numbullets,
Vector spread,
int mindamage,
int maxdamage,
int dflags,
int meansofdeath,
qboolean server_effects
)
{
Vector src;
Vector dir;
Vector end;
trace_t trace;
Vector right;
Vector up;
int i;
assert( owner );
if ( !owner )
{
return;
}
GetMuzzlePosition( &src, &dir );
owner->angles.AngleVectors( NULL, &right, &up );
angles = dir.toAngles();
setAngles( angles );
for( i = 0; i < numbullets; i++ )
{
end = src +
dir * 8192 +
right * G_CRandom() * spread.x +
up * G_CRandom() * spread.y;
//### first need to do a regular trace to check for hitting a hoverbike
trace = G_Trace(src, vec_zero, vec_zero, end, owner, MASK_SHOT, "BulletWeapon::FireBullets");
if(trace.fraction != 1)
{
Entity *hit;
hit = trace.ent->entity;
if(hit->isSubclassOf(Hoverbike) || hit->isSubclassOf(HoverbikeBox))
{
trace_t trace2;
// also do a short full trace to see if we're hitting a player on a hoverbike
end = trace.endpos + dir * 80;
trace2 = G_FullTrace(Vector(trace.endpos), vec_zero, vec_zero, end, 5, owner, MASK_SHOT, "BulletWeapon::FireBullets");
if(trace2.fraction != 1)
{
Entity *hit2;
hit2 = trace2.ent->entity;
if(hit2->takedamage && hit2->isClient())
{
// probably traced to the rider, so hit him instead
hit2->Damage(this, owner, mindamage + (int)G_Random(maxdamage - mindamage + 1),
trace.endpos, dir, trace.plane.normal, kick, dflags, meansofdeath,
trace.intersect.parentgroup, -1, trace.intersect.damage_multiplier);
return;
}
}
hit->Damage(this, owner, mindamage + (int)G_Random( maxdamage - mindamage + 1 ), trace.endpos, dir, trace.plane.normal, kick, dflags, meansofdeath, -1, -1, 1);
return; // hit something already, so don't do a regular full trace
}
}
if(!damagedtarget && DM_FLAG(DF_BBOX_BULLETS))
{
trace = G_Trace(src, vec_zero, vec_zero, end, owner, MASK_SHOT, "BulletWeapon::FireBullets");
if(trace.fraction != 1.0)
{
// do less than regular damage on a bbox hit
TraceAttack(src, trace.endpos, (mindamage + (int)G_Random( maxdamage - mindamage + 1 ))*0.85, &trace, MAX_RICOCHETS, kick, dflags, meansofdeath, server_effects);
}
}
else
{
//###
trace = G_FullTrace( src, vec_zero, vec_zero, end, 5, owner, MASK_SHOT, "BulletWeapon::FireBullets" );
#if 0
Com_Printf("Server OWNER Angles:%0.2f %0.2f %0.2f\n",owner->angles[0],owner->angles[1],owner->angles[2]);
Com_Printf("Server Bullet Angles:%0.2f %0.2f %0.2f\n",angles[0],angles[1],angles[2]);
Com_Printf("Right :%0.2f %0.2f %0.2f\n",right[0],right[1],right[2]);
Com_Printf("Up :%0.2f %0.2f %0.2f\n",up[0],up[1],up[2]);
Com_Printf("Direction :%0.2f %0.2f %0.2f\n",dir[0],dir[1],dir[2]);
Com_Printf("Endpoint :%0.2f %0.2f %0.2f\n",end[0],end[1],end[2]);
Com_Printf("Server Trace Start :%0.2f %0.2f %0.2f\n",src[0],src[1],src[2]);
Com_Printf("Server Trace End :%0.2f %0.2f %0.2f\n",trace.endpos[0],trace.endpos[1],trace.endpos[2]);
Com_Printf("\n");
#endif
if ( trace.fraction != 1.0 )
{
TraceAttack( src, trace.endpos, mindamage + (int)G_Random( maxdamage - mindamage + 1 ), &trace, MAX_RICOCHETS, kick, dflags, meansofdeath, server_effects );
}
} //###
}
}
void BulletWeapon::FireTracer( void )
{
Entity *tracer;
Vector src,dir,end;
trace_t trace;
GetMuzzlePosition( &src, &dir );
end = src + dir * 8192;
trace = G_Trace( src, vec_zero, vec_zero, end, owner, MASK_SHOT, "BulletWeapon::FireTracer" );
tracer = new Entity;
tracer->angles = dir.toAngles();
tracer->angles[ PITCH ] = -tracer->angles[ PITCH ] + 90;
tracer->setAngles( tracer->angles );
tracer->setMoveType( MOVETYPE_NONE );
tracer->setSolidType( SOLID_NOT );
tracer->setModel( "sprites/tracer.spr" );
tracer->setSize( "0 0 0", "0 0 0" );
tracer->setOrigin( trace.endpos );
tracer->edict->s.renderfx &= ~RF_FRAMELERP;
VectorCopy( src, tracer->edict->s.old_origin );
tracer->PostEvent(EV_Remove,0.1f);
}