348 lines
No EOL
9.5 KiB
C++
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);
|
|
} |