heretic2-sdk/Toolkit/Programming/GameCode/game/spl_ripper.c
1998-11-24 00:00:00 +00:00

269 lines
8.7 KiB
C

//
// Heretic II
// Copyright 1998 Raven Software
//
#include "g_local.h"
#include "fx.h"
#include "vector.h"
#include "angles.h"
#include "matrix.h"
#include "g_volume_effect.h"
#include "Utilities.h"
#include "g_ClassStatics.h"
#include "g_Physics.h"
#include "g_playstats.h"
#include "decals.h"
#include "random.h"
#include "m_beast.h"
#define RIPPER_RADIUS 12.0
#define RIPPER_EXPLODE_BALL_RADIUS 8.0
#define RIPPER_MAX_DISTANCE 2000
void RipperThink(edict_t *Self);
extern void AlertMonsters (edict_t *self, edict_t *enemy, float lifetime, qboolean ignore_shadows);
//---------------------
//---------------------
//---------------------
// RIPPER
//---------------------
//---------------------
//---------------------
// ****************************************************************************
// RipperExplodeBallThink
// ****************************************************************************
static void RipperExplodeBallThink(edict_t *self)
{
vec3_t vel;
vec3_t startpos, endpos, vect, forward;
edict_t *tracebuddy;
trace_t trace;
int numHit = 0; // can hit no more than 8 guys...
VectorCopy(self->s.origin, startpos);
VectorCopy(self->last_org, endpos);
tracebuddy = self;
do
{
gi.trace(startpos, self->mins, self->maxs, endpos, tracebuddy, MASK_SHOT,&trace);
// if we hit anything that won't take damage, kill the beam
if (!trace.ent->takedamage)
break;
if(trace.fraction < .99 )
{
// did we hit something that reflects ?
if(!EntReflecting(trace.ent, true, true))
{
T_Damage(trace.ent, self, self->owner, self->movedir, trace.endpos, vec3_origin,
self->dmg, 0, DAMAGE_SPELL | DAMAGE_EXTRA_BLOOD,MOD_IRONDOOM);
if (trace.ent->svflags & SVF_MONSTER)
{
// What the hell, let's spawn a big gush of blood in the travelling direction.
VectorScale(self->movedir, RIPPER_EXPLODE_SPEED*0.25, vel);
if(trace.ent->materialtype == MAT_INSECT)
gi.CreateEffect(NULL, FX_BLOOD, CEF_FLAG8, self->last_org, "ub", vel, (byte)20);
else
gi.CreateEffect(NULL, FX_BLOOD, 0, self->last_org, "ub", vel, (byte)20);
gi.sound(self, CHAN_WEAPON, gi.soundindex("weapons/RipperDamage.wav"), 1, ATTN_NORM, 0);
}
}
// this seems to alleviate the problem of a trace hitting the same ent multiple times...
VectorCopy(trace.endpos, startpos);
VectorSubtract(endpos, startpos, vect);
if(VectorLength(vect) > 16.0)
{
VectorSubtract(startpos,endpos,forward);
VectorNormalize(forward);
VectorMA(startpos, 16.0, forward, startpos);
}
tracebuddy = trace.ent;
numHit++;
}
} while((trace.fraction < .99) && !(trace.contents & MASK_SOLID) && (numHit < 6) );
// Prevent any further transmission of this entity to clients.
self->svflags |= SVF_NOCLIENT;
VectorCopy(self->s.origin, self->last_org);
self->nextthink = level.time + 0.1;
}
static void RipperExplodeBallTouch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surface)
{
G_SetToFree(self);
}
// ****************************************************************************
// RipperImpact
// ****************************************************************************
// This is like a touch function, except since the ripper is instant now...
static void RipperImpact(edict_t *caster, edict_t *other, vec3_t startpos, vec3_t endpos, vec3_t angles)
{
edict_t *ripper;
byte makeScorch = 0;
int i;
int dmg;
vec3_t hitpos, fwd;
short ballarray[RIPPER_BALLS];
byte byaw;
float curyaw;
// Get the forward vector for various calculations
AngleVectors(angles, fwd, NULL, NULL);
// did we hit someone where reflection is functional ?
if(EntReflecting(other, true, true))
{
// Erg... Do nothing right now.
}
AlertMonsters (caster, caster, 2, false);
if(other && other->takedamage)
{
dmg = irand(RIPPER_DAMAGE_MIN, RIPPER_DAMAGE_MAX);
VectorCopy(endpos, hitpos);
T_Damage(other, caster, caster, fwd, endpos, fwd, dmg, dmg*2, DAMAGE_SPELL,MOD_IRONDOOM);
}
else
{
// Back off the origin for the damage a bit. We are a point and this will
// help fix hitting base of a stair and not hurting a guy on next step up.
VectorMA(endpos, -8.0, fwd, hitpos);
}
// Shoot out ripper balls
curyaw = (angles[YAW]*ANGLE_TO_RAD) + (RIPPER_BALL_ANGLE*0.5);
// Add half an increment so that the balls don't come right back.
// STORE THE CURRENT YAW IN RADIANS FOR SIN/COS OPERATIONS
// Reduce precision to a 0-255 byte.
byaw = (byte)(curyaw*(256.0/(M_PI*2.0))); // The byte yaw loses lots of precision here
curyaw = ((float)byaw)*((M_PI*2.0)/256.0); // ...and pass this imprecision back to the float yaw.
for (i=0; i<RIPPER_BALLS; i++)
{
ripper = G_Spawn();
// Set up the angle vectors
ripper->movedir[0] = cos(curyaw);
ripper->movedir[1] = sin(curyaw);
ripper->movedir[2] = 0.0;
// Place the ball
VectorCopy(hitpos, ripper->s.origin);
VectorScale(ripper->movedir, RIPPER_EXPLODE_SPEED, ripper->velocity);
// Set up the net transmission attributes
ripper->s.effects |= EF_ALWAYS_ADD_EFFECTS;
ripper->svflags |= SVF_ALWAYS_SEND;
ripper->movetype = MOVETYPE_FLYMISSILE;
// Set up the dimentsions
VectorSet(ripper->mins, -RIPPER_EXPLODE_BALL_RADIUS, -RIPPER_EXPLODE_BALL_RADIUS, -RIPPER_EXPLODE_BALL_RADIUS);
VectorSet(ripper->maxs, RIPPER_EXPLODE_BALL_RADIUS, RIPPER_EXPLODE_BALL_RADIUS, RIPPER_EXPLODE_BALL_RADIUS);
// Set up physics attributes
ripper->solid = SOLID_BBOX;
ripper->clipmask = MASK_SOLID;
ripper->touch = RipperExplodeBallTouch;
ripper->think = RipperExplodeBallThink;
ripper->dmg = RIPPER_EXPLODE_DAMAGE;
ripper->classname = "Spell_Ripper";
ripper->nextthink = level.time + 0.1;
// last_org is used for damaging things
VectorCopy(ripper->s.origin, ripper->last_org);
ripper->owner = caster;
// Store the entity numbers for sending with the effect.
ballarray[i] = ripper->s.number;
curyaw += RIPPER_BALL_ANGLE;
}
gi.sound(ripper, CHAN_WEAPON, gi.soundindex("weapons/RipperImpact.wav"), 1, ATTN_NORM, 0);
// Okay (whew) now we send the big ol' mutherlovin' effect. There's a lot here, but believe me, this is WAY more
// efficient than sending out eight (well, nine with the impact) seperate effects...
// Send it attached to the last ball...
assert(ripper);
assert(RIPPER_BALLS == 8);
// So we send this with:
// --The position of the caster (for the launch trail effect)
// --The byte angle to shoot things at.
// --All eight entity numbers to attach things to.
// Okay, so I only need seven (the eighth comes with the effect), but for consistency I'm going to send all eight.
// (NOTE: Further optimization would maybe pass a pitch and distance, and make the yaw precise, but that would save
// little and make things more confusing than they are...)
// Currently this sends 29 bytes plus the entity it's attached to.
// This is down from (12 plus the entity) times eight, plus another effect for impact, plus another for the trail.
gi.CreateEffect(&ripper->s, FX_WEAPON_RIPPEREXPLODE, CEF_OWNERS_ORIGIN, NULL, "vbssssssss",
startpos,
byaw,
ballarray[0],
ballarray[1],
ballarray[2],
ballarray[3],
ballarray[4],
ballarray[5],
ballarray[6],
ballarray[7]);
}
// ****************************************************************************
// SpellCastRipper
// ****************************************************************************
void SpellCastRipper(edict_t *caster, vec3_t StartPos, vec3_t AimAngles, vec3_t unused)
{
trace_t trace;
vec3_t endpos, forward;
vec3_t mins={-RIPPER_RADIUS, -RIPPER_RADIUS, -RIPPER_RADIUS}, maxs={RIPPER_RADIUS, RIPPER_RADIUS, RIPPER_RADIUS};
gi.sound(caster, CHAN_WEAPON, gi.soundindex("weapons/RipperFire.wav"), 1, ATTN_NORM, 0);
// Make sure we don't spawn in a wall.
gi.trace(caster->s.origin, mins, maxs, StartPos, caster, MASK_PLAYERSOLID,&trace);
if (trace.startsolid || trace.fraction<.99)
{
RipperImpact(caster, trace.ent, caster->s.origin, trace.endpos, AimAngles);
return;
}
// Get the forward angle
AngleVectors(AimAngles, forward, NULL, NULL);
// Now trace from the starting point to the final destination.
VectorMA(StartPos, RIPPER_MAX_DISTANCE, forward, endpos);
gi.trace(StartPos, mins, maxs, endpos, caster, MASK_SHOT,&trace);
if(level.fighting_beast)
{
edict_t *ent;
if(ent = check_hit_beast(StartPos, trace.endpos))
trace.ent = ent;
}
RipperImpact(caster, trace.ent, StartPos, trace.endpos, AimAngles);
}
// end