sin-sdk/pulserifle.cpp
1999-11-02 00:00:00 +00:00

648 lines
15 KiB
C++

//-----------------------------------------------------------------------------
//
// $Logfile:: /Quake 2 Engine/Sin/code/game/pulserifle.cpp $
// $Revision:: 69 $
// $Author:: Aldie $
// $Date:: 3/02/99 9:14p $
//
// Copyright (C) 1998 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.
//
// $Log:: /Quake 2 Engine/Sin/code/game/pulserifle.cpp $
//
// 69 3/02/99 9:14p Aldie
// Added CTF game code
//
// 68 11/16/98 3:11a Jimdose
// pulse now takes gravity of owner
//
// 67 11/16/98 2:11a Jimdose
// removed unused variable in Shoot
//
// 66 11/15/98 12:22a Markd
// Fixed pre-caching issues
//
// 65 11/13/98 3:30p Markd
// put in more precaching on weapons
//
// 64 10/26/98 2:50p Aldie
// Fixed a bug with checking of NULL owners
//
// 63 10/24/98 12:42a Markd
// changed origins to worldorigins where appropriate
//
// 62 10/22/98 7:57p Markd
// put in proper pre-caching in all the classes
//
// 61 10/20/98 8:26p Markd
// Added Attacker to DamageSurface stuff
//
// 60 10/19/98 12:07a Jimdose
// made all code use fast checks for inheritance (no text lookups when
// possible)
// isSubclassOf no longer requires ::_classinfo()
//
// 59 10/16/98 10:22p Aldie
// Updated single player damage settings
//
// 58 10/16/98 9:50p Aldie
// Added SecondaryAmmo command
//
// 57 10/15/98 2:32p Aldie
// Updated model name for pulsebomb
//
// 56 10/10/98 9:18p Aldie
// Tweak damage
//
// 55 10/10/98 5:58p Aldie
// More quantumdestab fixes
//
// 54 10/09/98 11:56p Markd
// Added GenericPulseRifle
//
// 53 10/06/98 2:49p Aldie
// Tweak NextAttack
//
// 52 10/05/98 10:39p Aldie
// Fixed rank and updated damage.
//
// 51 10/04/98 10:28p Aldie
// Added multiple weapon changes. Damage, flashes, quantum stuff
//
// 50 10/03/98 1:09p Aldie
// Tweak the effects again
//
// 49 10/02/98 7:19p Aldie
// Orient the shockwave
//
// 48 10/02/98 5:47p Aldie
// New effects - yet again
//
// 47 10/01/98 4:01p Markd
// Added Archive and Unarchive functions
//
// 46 9/29/98 11:45p Aldie
// New effect for mode1
//
// 45 9/21/98 5:31p Markd
// took out owner out of projectile subclasses, working on archive and
// unarchive
//
// 44 9/18/98 8:14p Markd
// rewrote surface system so that surfaces are now damaged by surface name
// instead of by surfinfo
//
// 43 9/16/98 8:58p Aldie
// Added ability to do a hold down weapon charge
//
// 42 9/09/98 3:56p Aldie
// New lightning effect
//
// 41 9/01/98 3:05p Markd
// Rewrote explosion code
//
// 40 8/31/98 7:45p Aldie
// Updated surface data structure and removed surfinfo field
//
// 39 8/29/98 9:45p Jimdose
// Added call info to G_Trace
//
// 38 8/29/98 5:27p Markd
// added specialfx, replaced misc with specialfx where appropriate
//
// 37 8/18/98 8:12p Aldie
// Added dual mode weapons to base class
//
// 36 8/01/98 3:03p Aldie
// Client side muzzle flash (dynamic light)
//
// 35 7/25/98 7:10p Markd
// Put in EV_Removes for demo
//
// 34 7/23/98 6:17p Aldie
// Updated damage system and fixed some damage related bugs. Also put tracers
// back to the way they were, and added gib event to funcscriptmodels
//
// 33 7/22/98 9:57p Markd
// Defined weapon type
//
// 32 7/22/98 5:14p Aldie
// Fixed NextAttack time
//
// 31 7/21/98 1:10p Aldie
// Added meansofdeath to obituaries
//
// 30 7/01/98 4:39p Aldie
// Put Beaus' model explosion back in
//
// 29 6/30/98 6:48p Aldie
// Removed a printf
//
// 28 6/30/98 12:40p Aldie
// Fixed some bugs with vid_restart issue
//
// 27 6/29/98 8:19p Aldie
// Updated shockwave
//
// 26 6/25/98 7:32p Aldie
// Adding damage wall
//
// 25 6/19/98 9:30p Jimdose
// Moved gun orientation code to Weapon
//
// 24 6/17/98 6:30p Aldie
// Updated ammo requirements
//
// 23 6/17/98 5:42p Aldie
// Updated lots and made the beam do some damage.
//
// 22 6/15/98 9:09p Aldie
// Updated the special fx for beams trailing from the pulse.
//
// 21 6/15/98 12:25p Aldie
// Updated pulse beam parms
//
// 20 6/15/98 10:38a Aldie
// New version of pulse stuff
//
// 19 6/10/98 2:10p Aldie
// Updated damage function.
//
// 18 5/14/98 9:14p Markd
// Changed models
//
// 17 5/13/98 6:21p Markd
// Made pulse rifle burn wall as well as retrigger more often
//
// 16 5/03/98 4:36p Jimdose
// Changed Vector class
//
// 15 4/20/98 1:56p Markd
// SINED decelration is now in def file
//
// 14 4/18/98 3:12p Markd
// Changed view weapon naming convention
//
// 13 4/08/98 12:06a Jimdose
// Added temporary beam effect
//
// 12 4/07/98 6:41p Jimdose
// Rewrote weapon code.
// Added order to rank
//
// 11 4/02/98 4:20p Jimdose
// Tweaked for DM
//
// 10 3/30/98 9:55p Jimdose
// Changed location of .def files
//
// 9 3/30/98 2:34p Jimdose
// Moved firing to BulletWeapon to make more general
// Added Ammo
// Added world models
//
// 8 3/28/98 8:55p Jimdose
// Created file
//
// DESCRIPTION:
// Pulse rifle
//
#include "g_local.h"
#include "bullet.h"
#include "pulserifle.h"
#include "specialfx.h"
#include "misc.h"
#include "explosion.h"
#include "surface.h"
#define PULSE_MODE 0
// Projectile portion of the pulse rifle
template class EXPORT_FROM_DLL SafePtr<Beam>;
typedef SafePtr<Beam> BeamPtr;
class EXPORT_FROM_DLL Pulse : public Projectile
{
public:
CLASS_PROTOTYPE( Pulse );
Pulse();
virtual void Setup( Entity *owner, Vector pos, Vector dir );
virtual void Explode( Event *ev );
};
CLASS_DECLARATION( Projectile, Pulse, NULL );
Event EV_Pulse_UpdateBeams("update_beams");
Event EV_Pulse_Remove("remove_beams");
ResponseDef Pulse::Responses[] =
{
{ &EV_Touch, ( Response )Pulse::Explode },
{ NULL, NULL }
};
Pulse::Pulse()
{
}
void Pulse::Explode
(
Event *ev
)
{
Entity *other;
Entity *owner;
int damg;
Vector v;
Vector norm;
Vector shockangles;
Entity *pulseexpl;
other = ev->GetEntity( 1 );
assert( other );
if ( other->isSubclassOf( Teleporter ) )
{
return;
}
if ( other->entnum == this->owner )
{
return;
}
owner = G_GetEntity( this->owner );
if ( !owner )
owner = world;
stopsound( CHAN_VOICE );
setSolidType( SOLID_NOT );
// Hit the shy, so remove everything
if ( HitSky() )
{
PostEvent( EV_Remove, 0 );
return;
}
damg = 50 + ( int )G_Random( 25 );
// Single player packs a bigger punch
if ( !deathmatch->value && owner->isClient() )
damg *= 1.5;
if ( other->takedamage )
other->Damage( this, owner, damg, worldorigin, velocity, level.impact_trace.plane.normal, 32, 0, MOD_PULSE, -1, -1, 1.0f );
// Damage the surface
surfaceManager.DamageSurface ( &level.impact_trace, damg, owner );
damg = 100;
// Single player packs a bigger punch
if ( !deathmatch->value && owner->isClient() )
damg *= 1.5;
// Do an explosion but don't hurt other, since he already took damage.
RadiusDamage( this, owner, damg, other, MOD_PULSE );
v = velocity;
v.normalize();
v = worldorigin - v * 36;
norm = level.impact_trace.plane.normal;
norm.x = -norm.x;
norm.y = -norm.y;
shockangles = norm.toAngles();
shockangles.z = G_Random( 360 );
// Start up the shockwave effect
pulseexpl = new Entity;
pulseexpl->setModel( "pulseshock.def" );
pulseexpl->setOrigin( v );
pulseexpl->worldorigin.copyTo(pulseexpl->edict->s.old_origin);
pulseexpl->setAngles( shockangles );
pulseexpl->setMoveType( MOVETYPE_NONE );
pulseexpl->setSolidType( SOLID_NOT );
pulseexpl->RandomAnimate( "pulseexplode", NULL );
pulseexpl->PostEvent( EV_Remove, 0.1f );
FlashPlayers(v, 0.1, 0.1, 1, 0.5, 500 );
PostEvent( EV_Remove, 0 );
}
void Pulse::Setup
(
Entity *owner,
Vector pos,
Vector dir
)
{
Event *ev1, *ev2;
Vector forward, right, up;
Vector end, delta;
this->owner = owner->entnum;
edict->owner = owner->edict;
// Align the projectile
angles = dir.toAngles();
angles[ PITCH ] = -angles[ PITCH ];
setAngles( angles );
edict->s.angles[ROLL] = rand() % 360;
// Flies like a rocket
setMoveType( MOVETYPE_TOSS );
setSolidType( SOLID_BBOX );
edict->clipmask = MASK_PROJECTILE;
setModel( "sprites/pulsebomb.spr" );
edict->s.effects |= EF_AUTO_ANIMATE;
// Set the flying velocity
velocity = dir * 1000;
gravity = 0.7;
SetGravityAxis( owner->gravaxis );
takedamage = DAMAGE_NO;
// Set the light and effects
edict->s.renderfx |= RF_DLIGHT;
edict->s.effects |= EF_PULSE;
edict->s.radius = 100;
edict->s.color_r = 0.1f;
edict->s.color_g = 0.1f;
edict->s.color_b = 0.9f;
// setup ambient thrust sound
ev1 = new Event( EV_RandomEntitySound );
ev1->AddString( "thrust" );
ProcessEvent( ev1 );
// Set size and origin
setSize( "-1 -1 -1", "1 1 1" );
setOrigin( pos );
worldorigin.copyTo(edict->s.old_origin);
// Remove the projectile in the future
ev2 = new Event( EV_Pulse_Remove );
PostEvent( ev2, 30 );
}
CLASS_DECLARATION( BulletWeapon, PulseRifle, "weapon_pulserifle" );
ResponseDef PulseRifle::Responses[] =
{
{ &EV_Weapon_Shoot, ( Response )PulseRifle::Shoot },
{ NULL, NULL }
};
PulseRifle::PulseRifle()
{
#ifdef SIN_DEMO
PostEvent( EV_Remove, 0 );
return;
#endif
SetModels( "pulse2.def", "view_pulse2.def" );
SetAmmo( "BulletPulse", 10, 50 );
SetSecondaryAmmo( "BulletPulse", 5, 50 );
SetRank( 80, 80 );
SetType( WEAPON_2HANDED_LO );
dualmode = true;
modelIndex( "pulse_ammo.def" );
modelIndex( "beam.def" );
modelIndex( "pulseshock.def" );
modelIndex( "sprites/pulsebomb.spr" );
modelIndex( "sprites/pulse_muzzle.spr" );
}
// This is the trace that the laser portion of this weapon does.
void PulseRifle::TraceAttack
(
Vector start,
Vector end,
int damage,
trace_t *trace,
int numricochets,
int kick,
int dflags
)
{
Vector org;
Vector dir;
int surfflags;
int surftype;
Entity *ent;
if ( HitSky( trace ) )
{
return;
}
dir = end - start;
dir.normalize();
org = end - dir * 4;
if ( !trace->surface )
{
surfflags = 0;
surftype = 0;
}
else
{
surfflags = trace->surface->flags;
surftype = SURFACETYPE_FROM_FLAGS( surfflags );
surfaceManager.DamageSurface( trace, damage, owner );
}
ent = trace->ent->entity;
if ( ent && 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,
MOD_PULSELASER,
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,
MOD_PULSELASER,
-1,
-1,
1 );
}
}
}
void PulseRifle::PulseExplosion
(
trace_t *trace
)
{
Vector org, v;
float damg;
Entity *ent;
float kick = 250;
Vector shockangles;
Vector norm;
Vector tempvec;
float points;
Vector endpos;
float radius;
damg = 50 + ( int )G_Random( 20 );
RadiusDamage( this, owner, damg, NULL, MOD_PULSE );
VectorCopy( trace->dir, tempvec.vec3() );
endpos = trace->endpos - ( tempvec * 8 );
norm = trace->plane.normal;
norm.x = -norm.x;
norm.y = -norm.y;
shockangles = norm.toAngles();
shockangles.z = G_Random( 360 );
RandomPositionedSound( endpos, "impact_smallexplosion", 1.0, CHAN_AUTO, ATTN_NORM );
radius = damg + 50;
ent = findradius( NULL, endpos, radius );
while( ent )
{
if ( ( ent->takedamage ) &&
( ent->movetype != MOVETYPE_NONE ) &&
( ent->movetype != MOVETYPE_BOUNCE ) &&
( ent->movetype != MOVETYPE_PUSH ) &&
( ent->movetype != MOVETYPE_STOP ) )
{
org = ent->centroid;
v = org - endpos;
points = v.length();
v.normalize();
ent->velocity += (v * kick);
points *= (float)0.5;
if ( points < 0 )
{
points = 0;
}
points = damg - points;
if ( points > 0 )
{
if ( this->CanDamage( ent ) )
{
ent->Damage(this, owner, points,
org, v, vec_zero, points,
DAMAGE_RADIUS, MOD_PULSE,
-1, -1, 1.0f );
}
}
}
ent = findradius( ent, endpos, radius );
}
}
void PulseRifle::Shoot
(
Event *ev
)
{
Vector pos;
Vector end;
Vector dir;
Vector delta;
trace_t trace;
float dist;
float length;
float damg;
assert( owner );
if ( !owner )
{
return;
}
GetMuzzlePosition( &pos, &dir );
if ( weaponmode == PRIMARY )
{
Pulse *pulse;
pulse = new Pulse;
pulse->Setup( owner, pos, dir );
NextAttack( 0.5 );
}
else
{
// Fire the beam
length = ev->GetInteger( 1 );
end = pos + dir * length;
trace = G_Trace( pos, vec_zero, vec_zero, end, owner, MASK_SHOT, "PulseRifle::Shoot" );
delta = trace.endpos - pos;
dist = delta.length();
// Set the pitch of this weapon so the client can use it to fire bullets in the right directions
dir = Vector( owner->orientation[ 0 ] );
angles = dir.toAngles();
setAngles( angles );
if ( ctf->value )
damg = 30;
else
damg = 15;
TraceAttack( pos, trace.endpos, damg, &trace, 0, 0, 0 );
NextAttack( 0 );
}
}
CLASS_DECLARATION( PulseRifle, GenericPulseRifle, "weapon_genericpulserifle" );
ResponseDef GenericPulseRifle::Responses[] =
{
{ NULL, NULL }
};
GenericPulseRifle::GenericPulseRifle()
{
SetModels( NULL, "view_pulse2.def" );
}