mirror of
https://github.com/ReactionQuake3/reaction.git
synced 2024-11-29 07:22:47 +00:00
537 lines
17 KiB
C
537 lines
17 KiB
C
//-----------------------------------------------------------------------------
|
|
//
|
|
// $Id$
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// $Log$
|
|
// Revision 1.35 2005/02/15 16:33:39 makro
|
|
// Tons of updates (entity tree attachment system, UI vectors)
|
|
//
|
|
// Revision 1.34 2003/04/26 22:33:06 jbravo
|
|
// Wratted all calls to G_FreeEnt() to avoid crashing and provide debugging
|
|
//
|
|
// Revision 1.33 2003/03/22 20:29:26 jbravo
|
|
// wrapping linkent and unlinkent calls
|
|
//
|
|
// Revision 1.32 2003/03/09 21:30:38 jbravo
|
|
// Adding unlagged. Still needs work.
|
|
//
|
|
// Revision 1.31 2002/09/29 16:06:45 jbravo
|
|
// Work done at the HPWorld expo
|
|
//
|
|
// Revision 1.30 2002/08/28 23:10:06 jbravo
|
|
// Added cg_RQ3_SuicideLikeARealMan, timestamping to server logs and
|
|
// fixed stats for non-TP modes.
|
|
//
|
|
// Revision 1.29 2002/06/16 20:06:14 jbravo
|
|
// Reindented all the source files with "indent -kr -ut -i8 -l120 -lc120 -sob -bad -bap"
|
|
//
|
|
// Revision 1.28 2002/06/16 17:38:00 jbravo
|
|
// Removed the MISSIONPACK ifdefs and missionpack only code.
|
|
//
|
|
// Revision 1.27 2002/06/11 15:52:21 makro
|
|
// Fixed knife + unbreakable breakables/breakables with high health bug
|
|
//
|
|
// Revision 1.26 2002/04/29 06:16:27 niceass
|
|
// small change to pressure system
|
|
//
|
|
// Revision 1.25 2002/04/06 21:42:20 makro
|
|
// Changes to bot code. New surfaceparm system.
|
|
//
|
|
// Revision 1.24 2002/04/05 18:53:26 jbravo
|
|
// Warning fixes
|
|
//
|
|
// Revision 1.23 2002/03/18 19:18:39 slicer
|
|
// Fixed bandage bugs ( i hope )
|
|
//
|
|
// Revision 1.22 2002/03/12 04:55:31 blaze
|
|
// stats should only be recored when the round is in progress
|
|
//
|
|
// Revision 1.21 2002/01/14 01:20:45 niceass
|
|
// No more default 800 gravity on items
|
|
// Thrown knife+Glass fix - NiceAss
|
|
//
|
|
// Revision 1.20 2002/01/11 19:48:30 jbravo
|
|
// Formatted the source in non DOS format.
|
|
//
|
|
// Revision 1.19 2001/12/31 16:28:42 jbravo
|
|
// I made a Booboo with the Log tag.
|
|
//
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
// Copyright (C) 1999-2000 Id Software, Inc.
|
|
//
|
|
#include "g_local.h"
|
|
|
|
#define MISSILE_PRESTEP_TIME 50
|
|
|
|
/*
|
|
================
|
|
G_BounceMissile
|
|
|
|
================
|
|
*/
|
|
void G_BounceMissile(gentity_t * ent, trace_t * trace)
|
|
{
|
|
vec3_t velocity;
|
|
float dot;
|
|
int hitTime;
|
|
|
|
// reflect the velocity on the trace plane
|
|
hitTime = level.previousTime + (level.time - level.previousTime) * trace->fraction;
|
|
G_EvaluateTrajectoryDelta(&ent->s.pos, hitTime, velocity);
|
|
dot = DotProduct(velocity, trace->plane.normal);
|
|
VectorMA(velocity, -2 * dot, trace->plane.normal, ent->s.pos.trDelta);
|
|
|
|
if (ent->s.eFlags & EF_BOUNCE_HALF) {
|
|
VectorScale(ent->s.pos.trDelta, 0.65, ent->s.pos.trDelta);
|
|
// check for stop
|
|
if (trace->plane.normal[2] > 0.2 && VectorLength(ent->s.pos.trDelta) < 40) {
|
|
G_SetOrigin(ent, trace->endpos);
|
|
ent->s.time = level.time / 4;
|
|
return;
|
|
}
|
|
}
|
|
|
|
VectorAdd(ent->r.currentOrigin, trace->plane.normal, ent->r.currentOrigin);
|
|
VectorCopy(ent->r.currentOrigin, ent->s.pos.trBase);
|
|
ent->s.pos.trTime = level.time;
|
|
}
|
|
|
|
/*
|
|
================
|
|
G_ExplodeMissile
|
|
|
|
Explode a missile without an impact
|
|
================
|
|
*/
|
|
void G_ExplodeMissile(gentity_t * ent)
|
|
{
|
|
vec3_t dir;
|
|
vec3_t origin;
|
|
|
|
G_EvaluateTrajectory(&ent->s.pos, level.time, origin);
|
|
SnapVector(origin);
|
|
G_SetOrigin(ent, origin);
|
|
|
|
// we don't have a valid direction, so just point straight up
|
|
dir[0] = dir[1] = 0;
|
|
dir[2] = 1;
|
|
|
|
ent->s.eType = ET_GENERAL;
|
|
G_AddEvent(ent, EV_MISSILE_MISS, DirToByte(dir));
|
|
|
|
ent->freeAfterEvent = qtrue;
|
|
|
|
// splash damage
|
|
if (ent->splashDamage) {
|
|
if (G_RadiusDamage
|
|
(ent->r.currentOrigin, ent->parent, ent->splashDamage, ent->splashRadius, ent,
|
|
ent->splashMethodOfDeath)) {
|
|
g_entities[ent->r.ownerNum].client->accuracy_hits++;
|
|
// Elder: Statistics tracking
|
|
if (ent->s.weapon == WP_KNIFE &&
|
|
((g_gametype.integer == GT_TEAMPLAY && level.team_round_going) || g_gametype.integer != GT_TEAMPLAY))
|
|
g_entities[ent->r.ownerNum].client->pers.records[REC_KNIFETHROWHITS]++;
|
|
if (ent->s.weapon == WP_GRENADE &&
|
|
((g_gametype.integer == GT_TEAMPLAY && level.team_round_going) || g_gametype.integer != GT_TEAMPLAY))
|
|
g_entities[ent->r.ownerNum].client->pers.records[REC_GRENADEHITS]++;
|
|
}
|
|
}
|
|
//Elder: huhh?
|
|
//G_Printf("exploding knife\n");
|
|
trap_LinkEntity(ent);
|
|
}
|
|
|
|
/*
|
|
================
|
|
G_MissileImpact
|
|
================
|
|
*/
|
|
void G_MissileImpact(gentity_t * ent, trace_t * trace)
|
|
{
|
|
gentity_t *other;
|
|
qboolean hitClient = qfalse;
|
|
vec3_t velocity;
|
|
|
|
//Elder: added for thrown knife condition
|
|
gitem_t *xr_item;
|
|
gentity_t *xr_drop;
|
|
|
|
other = &g_entities[trace->entityNum];
|
|
|
|
//Elder: no grenade explosion on impact
|
|
if (ent->s.weapon == WP_GRENADE && (ent->s.eFlags & (EF_BOUNCE | EF_BOUNCE_HALF))) {
|
|
G_BounceMissile(ent, trace);
|
|
G_AddEvent(ent, EV_GRENADE_BOUNCE, 0);
|
|
return;
|
|
}
|
|
|
|
// impact damage
|
|
if (other->takedamage) {
|
|
// FIXME: wrong damage direction?
|
|
if (ent->damage) {
|
|
if (LogAccuracyHit(other, &g_entities[ent->r.ownerNum])) {
|
|
g_entities[ent->r.ownerNum].client->accuracy_hits++;
|
|
// Elder: Statistics tracking
|
|
if (ent->s.weapon == WP_KNIFE &&
|
|
((g_gametype.integer == GT_TEAMPLAY && level.team_round_going) || g_gametype.integer != GT_TEAMPLAY))
|
|
g_entities[ent->r.ownerNum].client->pers.records[REC_KNIFETHROWHITS]++;
|
|
//g_entities[ent->r.ownerNum].client->knifeHits++;
|
|
if (ent->s.weapon == WP_GRENADE &&
|
|
((g_gametype.integer == GT_TEAMPLAY && level.team_round_going) || g_gametype.integer != GT_TEAMPLAY))
|
|
g_entities[ent->r.ownerNum].client->pers.records[REC_GRENADEHITS]++;
|
|
//g_entities[ent->r.ownerNum].client->grenHits++;
|
|
hitClient = qtrue;
|
|
}
|
|
G_EvaluateTrajectoryDelta(&ent->s.pos, level.time, velocity);
|
|
if (VectorLength(velocity) == 0) {
|
|
velocity[2] = 1; // stepped on a grenade
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!strcmp(ent->classname, "hook")) {
|
|
gentity_t *nent;
|
|
vec3_t v;
|
|
|
|
nent = G_Spawn();
|
|
if (other->takedamage && other->client) {
|
|
|
|
G_AddEvent(nent, EV_MISSILE_HIT, DirToByte(trace->plane.normal));
|
|
nent->s.otherEntityNum = other->s.number;
|
|
|
|
ent->enemy = other;
|
|
|
|
v[0] = other->r.currentOrigin[0] + (other->r.mins[0] + other->r.maxs[0]) * 0.5;
|
|
v[1] = other->r.currentOrigin[1] + (other->r.mins[1] + other->r.maxs[1]) * 0.5;
|
|
v[2] = other->r.currentOrigin[2] + (other->r.mins[2] + other->r.maxs[2]) * 0.5;
|
|
|
|
SnapVectorTowards(v, ent->s.pos.trBase); // save net bandwidth
|
|
} else {
|
|
VectorCopy(trace->endpos, v);
|
|
G_AddEvent(nent, EV_MISSILE_MISS, DirToByte(trace->plane.normal));
|
|
ent->enemy = NULL;
|
|
}
|
|
|
|
SnapVectorTowards(v, ent->s.pos.trBase); // save net bandwidth
|
|
|
|
nent->freeAfterEvent = qtrue;
|
|
// change over to a normal entity right at the point of impact
|
|
nent->s.eType = ET_GENERAL;
|
|
ent->s.eType = ET_GRAPPLE;
|
|
|
|
G_SetOrigin(ent, v);
|
|
G_SetOrigin(nent, v);
|
|
|
|
//Blaze: Hook dosent exist any more
|
|
//ent->think = Weapon_HookThink;
|
|
ent->nextthink = level.time + FRAMETIME;
|
|
|
|
ent->parent->client->ps.pm_flags |= PMF_GRAPPLE_PULL;
|
|
VectorCopy(ent->r.currentOrigin, ent->parent->client->ps.grapplePoint);
|
|
|
|
trap_LinkEntity(ent);
|
|
trap_LinkEntity(nent);
|
|
|
|
return;
|
|
}
|
|
// is it cheaper in bandwidth to just remove this ent and create a new
|
|
// one, rather than changing the missile into the explosion?
|
|
|
|
//Elder: I don't know but that's what we did for the knife
|
|
if (other->takedamage && other->client) {
|
|
G_AddEvent(ent, EV_MISSILE_HIT, DirToByte(trace->plane.normal));
|
|
ent->s.otherEntityNum = other->s.number;
|
|
//Makro - new surfaceparm system
|
|
//} else if( trace->surfaceFlags & SURF_METALSTEPS ) {
|
|
} else if (IsMetalFlag(trace->surfaceFlags)) {
|
|
G_AddEvent(ent, EV_MISSILE_MISS_METAL, DirToByte(trace->plane.normal));
|
|
} else {
|
|
G_AddEvent(ent, EV_MISSILE_MISS, DirToByte(trace->plane.normal));
|
|
}
|
|
|
|
//Elder: knife handling routines go HERE, not in g_main.c !!!
|
|
if (ent->s.weapon == WP_KNIFE) {
|
|
if (other->takedamage && other->client) {
|
|
// Elder: must add it in here if we remove it from above
|
|
// NiceAss: Fixed knife-leg damage bug here. Passed it the wrong vector I guess...
|
|
G_Damage(other, ent, &g_entities[ent->r.ownerNum], velocity, ent->r.currentOrigin, THROW_DAMAGE,
|
|
0, MOD_KNIFE_THROWN);
|
|
//hit a player - send the gurgle or embedding sound event
|
|
} else {
|
|
vec3_t temp;
|
|
vec3_t knifeOffset;
|
|
vec3_t knifeVelocity;
|
|
vec3_t origin;
|
|
|
|
//missed throw or hit func_breakable;
|
|
//spawn a knife at its trajectory end-point
|
|
xr_item = BG_FindItemForWeapon(WP_KNIFE);
|
|
|
|
G_EvaluateTrajectoryDelta(&ent->s.pos, level.time, knifeVelocity);
|
|
|
|
//Makro - added check for unbreakable breakables/breakables with high health
|
|
if (other->s.eType == ET_BREAKABLE && !other->unbreakable && other->health <= THROW_DAMAGE) {
|
|
VectorCopy(trace->endpos, origin);
|
|
G_Damage(other, ent, &g_entities[ent->r.ownerNum], velocity, origin, THROW_DAMAGE, 0,
|
|
MOD_KNIFE_THROWN);
|
|
|
|
// NiceAss: Find current position and set it.
|
|
VectorCopy(trace->endpos, ent->s.pos.trBase);
|
|
ent->s.pos.trTime = level.time;
|
|
// And cut the velocity down.
|
|
VectorScale(ent->s.pos.trDelta, .2, ent->s.pos.trDelta);
|
|
return;
|
|
} else {
|
|
//leave embedded in the wall
|
|
xr_drop = LaunchItem(xr_item, trace->endpos, 0, FL_THROWN_KNIFE | FL_DROPPED_ITEM);
|
|
VectorClear(xr_drop->s.pos.trDelta);
|
|
|
|
//Elder: make the knife stick out a bit more
|
|
//and transfer into shared entityState
|
|
VectorCopy(knifeVelocity, temp);
|
|
VectorNormalize(temp);
|
|
VectorScale(temp, -4, temp);
|
|
VectorAdd(trace->endpos, temp, knifeOffset);
|
|
|
|
//VectorCopy(xr_drop->s.origin, temp);
|
|
VectorAdd(xr_drop->s.origin, knifeOffset, xr_drop->s.origin);
|
|
|
|
//Makro - "hurt" the breakable if needed
|
|
if (other->s.eType == ET_BREAKABLE) {
|
|
VectorCopy(trace->endpos, origin);
|
|
G_Damage(other, ent, &g_entities[ent->r.ownerNum], velocity, origin,
|
|
THROW_DAMAGE, 0, MOD_KNIFE_THROWN);
|
|
}
|
|
|
|
if (other->s.eType == ET_PRESSURE)
|
|
G_CreatePressure(xr_drop->s.origin, trace->plane.normal,
|
|
&g_entities[trace->entityNum]);
|
|
}
|
|
|
|
//Elder: transfer entity data into the shared entityState
|
|
//They are rotated on the client side in cg_ents.c
|
|
|
|
//G_Printf("movedir: %s\n", vtos(ent->s.pos.trDelta));
|
|
xr_drop->s.eFlags = xr_drop->flags;
|
|
|
|
//vectoangles( trace->plane.normal, xr_drop->s.angles );
|
|
|
|
vectoangles(knifeVelocity, xr_drop->s.angles);
|
|
xr_drop->s.angles[0] += 90;
|
|
}
|
|
}
|
|
|
|
ent->freeAfterEvent = qtrue;
|
|
|
|
// change over to a normal entity right at the point of impact
|
|
ent->s.eType = ET_GENERAL;
|
|
|
|
SnapVectorTowards(trace->endpos, ent->s.pos.trBase); // save net bandwidth
|
|
|
|
G_SetOrigin(ent, trace->endpos);
|
|
|
|
// splash damage (doesn't apply to person directly hit)
|
|
if (ent->splashDamage) {
|
|
if (G_RadiusDamage(trace->endpos, ent->parent, ent->splashDamage, ent->splashRadius,
|
|
other, ent->splashMethodOfDeath)) {
|
|
if (!hitClient) {
|
|
g_entities[ent->r.ownerNum].client->accuracy_hits++;
|
|
// Elder: Statistics tracking
|
|
if (ent->s.weapon == WP_KNIFE &&
|
|
((g_gametype.integer == GT_TEAMPLAY && level.team_round_going) || g_gametype.integer != GT_TEAMPLAY))
|
|
g_entities[ent->r.ownerNum].client->pers.records[REC_KNIFETHROWHITS]++;
|
|
//g_entities[ent->r.ownerNum].client->knifeHits++;
|
|
if (ent->s.weapon == WP_GRENADE &&
|
|
((g_gametype.integer == GT_TEAMPLAY && level.team_round_going) || g_gametype.integer != GT_TEAMPLAY))
|
|
g_entities[ent->r.ownerNum].client->pers.records[REC_GRENADEHITS]++;
|
|
//g_entities[ent->r.ownerNum].client->grenHits++;
|
|
}
|
|
}
|
|
}
|
|
|
|
trap_LinkEntity(ent);
|
|
}
|
|
|
|
/*
|
|
================
|
|
G_RunMissile
|
|
================
|
|
*/
|
|
void G_RunMissile(gentity_t * ent)
|
|
{
|
|
vec3_t origin;
|
|
trace_t tr;
|
|
int passent;
|
|
|
|
// get current position
|
|
G_EvaluateTrajectory(&ent->s.pos, level.time, origin);
|
|
|
|
// if this missile bounced off an invulnerability sphere
|
|
if (ent->target_ent) {
|
|
passent = ent->target_ent->s.number;
|
|
} else {
|
|
// ignore interactions with the missile owner
|
|
passent = ent->r.ownerNum;
|
|
}
|
|
// trace a line from the previous position to the current position
|
|
trap_Trace(&tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, passent, ent->clipmask);
|
|
|
|
if (tr.startsolid || tr.allsolid) {
|
|
// make sure the tr.entityNum is set to the entity we're stuck in
|
|
trap_Trace(&tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, ent->r.currentOrigin, passent,
|
|
ent->clipmask);
|
|
tr.fraction = 0;
|
|
} else {
|
|
VectorCopy(tr.endpos, ent->r.currentOrigin);
|
|
}
|
|
|
|
trap_LinkEntity(ent);
|
|
|
|
if (tr.fraction != 1) {
|
|
// never explode or bounce on sky
|
|
if (tr.surfaceFlags & SURF_NOIMPACT) {
|
|
// If grapple, reset owner
|
|
if (ent->parent && ent->parent->client && ent->parent->client->hook == ent) {
|
|
ent->parent->client->hook = NULL;
|
|
}
|
|
G_FreeEntity(ent);
|
|
return;
|
|
}
|
|
G_MissileImpact(ent, &tr);
|
|
if (ent->s.eType != ET_MISSILE) {
|
|
return; // exploded
|
|
}
|
|
}
|
|
|
|
// check think function after bouncing
|
|
G_RunThink(ent);
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
/*
|
|
=================
|
|
fire_grenade
|
|
=================
|
|
*/
|
|
gentity_t *fire_grenade(gentity_t * self, vec3_t start, vec3_t dir)
|
|
{
|
|
gentity_t *bolt;
|
|
int speed = 0;
|
|
vec3_t up, right;
|
|
|
|
if (self->client)
|
|
AngleVectors(self->client->ps.viewangles, NULL, right, up);
|
|
else {
|
|
// just in case we put those shooters back
|
|
up[0] = up[1] = up[2] = 0;
|
|
right[0] = right[1] = right[2] = 0;
|
|
}
|
|
|
|
VectorNormalize(dir);
|
|
|
|
bolt = G_Spawn();
|
|
bolt->classname = "grenade";
|
|
bolt->nextthink = level.time + 2000; // Action had 2 seconds
|
|
bolt->think = G_ExplodeMissile;
|
|
bolt->s.eType = ET_MISSILE;
|
|
bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN;
|
|
bolt->s.weapon = WP_GRENADE;
|
|
bolt->s.eFlags = EF_BOUNCE_HALF;
|
|
bolt->r.ownerNum = self->s.number;
|
|
bolt->s.otherEntityNum = self->s.number;
|
|
bolt->parent = self;
|
|
bolt->damage = GRENADE_DAMAGE; //probably only used on a direct hit
|
|
bolt->splashDamage = GRENADE_SPLASH_DAMAGE; //Blaze: Reaction Grenade Damage
|
|
bolt->splashRadius = GRENADE_SPLASH_RADIUS; //Blaze: changed from 150 was 170, but I uped it some more
|
|
bolt->methodOfDeath = MOD_GRENADE;
|
|
bolt->splashMethodOfDeath = MOD_GRENADE_SPLASH;
|
|
bolt->clipmask = MASK_SHOT;
|
|
bolt->target_ent = NULL;
|
|
|
|
bolt->s.pos.trType = TR_GRAVITY;
|
|
bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame
|
|
VectorCopy(start, bolt->s.pos.trBase);
|
|
|
|
//Elder: grenade toggle distances/speeds
|
|
if (self->client) {
|
|
// Elder: Statistics tracking
|
|
if ((g_gametype.integer == GT_TEAMPLAY && level.team_round_going) || g_gametype.integer != GT_TEAMPLAY)
|
|
self->client->pers.records[REC_GRENADESHOTS]++;
|
|
if (self->client->ps.stats[STAT_HEALTH] <= 0 ||
|
|
self->client->ps.weaponstate == WEAPON_BANDAGING ||
|
|
// NiceAss: Should catch any case of switching weapons with a grenade "cocked"
|
|
self->client->ps.weaponstate == WEAPON_DROPPING) {
|
|
//Always drop close range if dead or about to bandage
|
|
speed = GRENADE_SHORT_SPEED / 2;
|
|
} else if ((self->client->ps.persistant[PERS_WEAPONMODES] & RQ3_GRENSHORT) == RQ3_GRENSHORT &&
|
|
(self->client->ps.persistant[PERS_WEAPONMODES] & RQ3_GRENMED) == RQ3_GRENMED) {
|
|
//Long range throw
|
|
speed = GRENADE_LONG_SPEED;
|
|
} else if ((self->client->ps.persistant[PERS_WEAPONMODES] & RQ3_GRENSHORT) == RQ3_GRENSHORT) {
|
|
//Short range throw
|
|
speed = GRENADE_SHORT_SPEED;
|
|
} else if ((self->client->ps.persistant[PERS_WEAPONMODES] & RQ3_GRENMED) == RQ3_GRENMED) {
|
|
//Medium range throw
|
|
speed = GRENADE_MEDIUM_SPEED;
|
|
} else {
|
|
//Elder: shouldn't be here
|
|
G_Printf("fire_grenade: Did not receive a grenade parm\n");
|
|
speed = GRENADE_MEDIUM_SPEED;
|
|
}
|
|
}
|
|
|
|
VectorScale(dir, speed, bolt->s.pos.trDelta);
|
|
VectorMA(bolt->s.pos.trDelta, 200 + crandom() * 10.0f, up, bolt->s.pos.trDelta);
|
|
VectorMA(bolt->s.pos.trDelta, crandom() * 10.0f, right, bolt->s.pos.trDelta);
|
|
SnapVector(bolt->s.pos.trDelta); // save net bandwidth
|
|
|
|
VectorCopy(start, bolt->r.currentOrigin);
|
|
|
|
return bolt;
|
|
}
|
|
|
|
gentity_t *fire_knife(gentity_t * self, vec3_t start, vec3_t dir)
|
|
{
|
|
|
|
gentity_t *bolt;
|
|
|
|
VectorNormalize(dir);
|
|
|
|
bolt = G_Spawn();
|
|
bolt->classname = "weapon_knife";
|
|
bolt->nextthink = level.time + 10000;
|
|
bolt->think = G_FreeEntity;
|
|
bolt->s.eType = ET_MISSILE;
|
|
bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN;
|
|
bolt->s.weapon = WP_KNIFE;
|
|
bolt->r.ownerNum = self->s.number;
|
|
bolt->s.otherEntityNum = self->s.number;
|
|
bolt->parent = self;
|
|
bolt->damage = THROW_DAMAGE;
|
|
bolt->splashDamage = 0;
|
|
bolt->splashRadius = 0;
|
|
bolt->methodOfDeath = MOD_KNIFE_THROWN;
|
|
bolt->clipmask = MASK_SHOT;
|
|
bolt->target_ent = NULL;
|
|
bolt->count = 1;
|
|
bolt->s.pos.trType = TR_GRAVITY;
|
|
bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame
|
|
VectorCopy(start, bolt->s.pos.trBase);
|
|
VectorScale(dir, THROW_SPEED, bolt->s.pos.trDelta);
|
|
SnapVector(bolt->s.pos.trDelta); // save net bandwidth
|
|
VectorCopy(start, bolt->r.currentOrigin);
|
|
|
|
VectorCopy(dir, bolt->s.apos.trBase);
|
|
VectorCopy(dir, bolt->r.currentAngles);
|
|
|
|
if (self->client && ((g_gametype.integer == GT_TEAMPLAY && level.team_round_going) || g_gametype.integer != GT_TEAMPLAY)) {
|
|
// Elder: Statistics tracking
|
|
self->client->pers.records[REC_KNIFETHROWSHOTS]++;
|
|
}
|
|
|
|
return bolt;
|
|
}
|