📄 g_missile.c
字号:
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
//
#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;
BG_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 );
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;
BG_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++;
}
}
trap_LinkEntity( ent );
}
#ifdef MISSIONPACK
/*
================
ProximityMine_Explode
================
*/
static void ProximityMine_Explode( gentity_t *mine ) {
G_ExplodeMissile( mine );
// if the prox mine has a trigger free it
if (mine->activator) {
G_FreeEntity(mine->activator);
mine->activator = NULL;
}
}
/*
================
ProximityMine_Die
================
*/
static void ProximityMine_Die( gentity_t *ent, gentity_t *inflictor, gentity_t *attacker, int damage, int mod ) {
ent->think = ProximityMine_Explode;
ent->nextthink = level.time + 1;
}
/*
================
ProximityMine_Trigger
================
*/
void ProximityMine_Trigger( gentity_t *trigger, gentity_t *other, trace_t *trace ) {
vec3_t v;
gentity_t *mine;
if( !other->client ) {
return;
}
// trigger is a cube, do a distance test now to act as if it's a sphere
VectorSubtract( trigger->s.pos.trBase, other->s.pos.trBase, v );
if( VectorLength( v ) > trigger->parent->splashRadius ) {
return;
}
if ( g_gametype.integer >= GT_TEAM ) {
// don't trigger same team mines
if (trigger->parent->s.generic1 == other->client->sess.sessionTeam) {
return;
}
}
// ok, now check for ability to damage so we don't get triggered thru walls, closed doors, etc...
if( !CanDamage( other, trigger->s.pos.trBase ) ) {
return;
}
// trigger the mine!
mine = trigger->parent;
mine->s.loopSound = 0;
G_AddEvent( mine, EV_PROXIMITY_MINE_TRIGGER, 0 );
mine->nextthink = level.time + 500;
G_FreeEntity( trigger );
}
/*
================
ProximityMine_Activate
================
*/
static void ProximityMine_Activate( gentity_t *ent ) {
gentity_t *trigger;
float r;
ent->think = ProximityMine_Explode;
ent->nextthink = level.time + g_proxMineTimeout.integer;
ent->takedamage = qtrue;
ent->health = 1;
ent->die = ProximityMine_Die;
ent->s.loopSound = G_SoundIndex( "sound/weapons/proxmine/wstbtick.wav" );
// build the proximity trigger
trigger = G_Spawn ();
trigger->classname = "proxmine_trigger";
r = ent->splashRadius;
VectorSet( trigger->r.mins, -r, -r, -r );
VectorSet( trigger->r.maxs, r, r, r );
G_SetOrigin( trigger, ent->s.pos.trBase );
trigger->parent = ent;
trigger->r.contents = CONTENTS_TRIGGER;
trigger->touch = ProximityMine_Trigger;
trap_LinkEntity (trigger);
// set pointer to trigger so the entity can be freed when the mine explodes
ent->activator = trigger;
}
/*
================
ProximityMine_ExplodeOnPlayer
================
*/
static void ProximityMine_ExplodeOnPlayer( gentity_t *mine ) {
gentity_t *player;
player = mine->enemy;
player->client->ps.eFlags &= ~EF_TICKING;
if ( player->client->invulnerabilityTime > level.time ) {
G_Damage( player, mine->parent, mine->parent, vec3_origin, mine->s.origin, 1000, DAMAGE_NO_KNOCKBACK, MOD_JUICED );
player->client->invulnerabilityTime = 0;
G_TempEntity( player->client->ps.origin, EV_JUICED );
}
else {
G_SetOrigin( mine, player->s.pos.trBase );
// make sure the explosion gets to the client
mine->r.svFlags &= ~SVF_NOCLIENT;
mine->splashMethodOfDeath = MOD_PROXIMITY_MINE;
G_ExplodeMissile( mine );
}
}
/*
================
ProximityMine_Player
================
*/
static void ProximityMine_Player( gentity_t *mine, gentity_t *player ) {
if( mine->s.eFlags & EF_NODRAW ) {
return;
}
G_AddEvent( mine, EV_PROXIMITY_MINE_STICK, 0 );
if( player->s.eFlags & EF_TICKING ) {
player->activator->splashDamage += mine->splashDamage;
player->activator->splashRadius *= 1.50;
mine->think = G_FreeEntity;
mine->nextthink = level.time;
return;
}
player->client->ps.eFlags |= EF_TICKING;
player->activator = mine;
mine->s.eFlags |= EF_NODRAW;
mine->r.svFlags |= SVF_NOCLIENT;
mine->s.pos.trType = TR_LINEAR;
VectorClear( mine->s.pos.trDelta );
mine->enemy = player;
mine->think = ProximityMine_ExplodeOnPlayer;
if ( player->client->invulnerabilityTime > level.time ) {
mine->nextthink = level.time + 2 * 1000;
}
else {
mine->nextthink = level.time + 10 * 1000;
}
}
#endif
/*
================
G_MissileImpact
================
*/
void G_MissileImpact( gentity_t *ent, trace_t *trace ) {
gentity_t *other;
qboolean hitClient = qfalse;
#ifdef MISSIONPACK
vec3_t forward, impactpoint, bouncedir;
int eFlags;
#endif
other = &g_entities[trace->entityNum];
// check for bounce
if ( !other->takedamage &&
( ent->s.eFlags & ( EF_BOUNCE | EF_BOUNCE_HALF ) ) ) {
G_BounceMissile( ent, trace );
G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 );
return;
}
#ifdef MISSIONPACK
if ( other->takedamage ) {
if ( ent->s.weapon != WP_PROX_LAUNCHER ) {
if ( other->client && other->client->invulnerabilityTime > level.time ) {
//
VectorCopy( ent->s.pos.trDelta, forward );
VectorNormalize( forward );
if (G_InvulnerabilityEffect( other, forward, ent->s.pos.trBase, impactpoint, bouncedir )) {
VectorCopy( bouncedir, trace->plane.normal );
eFlags = ent->s.eFlags & EF_BOUNCE_HALF;
ent->s.eFlags &= ~EF_BOUNCE_HALF;
G_BounceMissile( ent, trace );
ent->s.eFlags |= eFlags;
}
ent->target_ent = other;
return;
}
}
}
#endif
// impact damage
if (other->takedamage) {
// FIXME: wrong damage direction?
if ( ent->damage ) {
vec3_t velocity;
if( LogAccuracyHit( other, &g_entities[ent->r.ownerNum] ) ) {
g_entities[ent->r.ownerNum].client->accuracy_hits++;
hitClient = qtrue;
}
BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity );
if ( VectorLength( velocity ) == 0 ) {
velocity[2] = 1; // stepped on a grenade
}
G_Damage (other, ent, &g_entities[ent->r.ownerNum], velocity,
ent->s.origin, ent->damage,
0, ent->methodOfDeath);
}
}
#ifdef MISSIONPACK
if( ent->s.weapon == WP_PROX_LAUNCHER ) {
if( ent->s.pos.trType != TR_GRAVITY ) {
return;
}
// if it's a player, stick it on to them (flag them and remove this entity)
if( other->s.eType == ET_PLAYER && other->health > 0 ) {
ProximityMine_Player( ent, other );
return;
}
SnapVectorTowards( trace->endpos, ent->s.pos.trBase );
G_SetOrigin( ent, trace->endpos );
ent->s.pos.trType = TR_STATIONARY;
VectorClear( ent->s.pos.trDelta );
G_AddEvent( ent, EV_PROXIMITY_MINE_STICK, trace->surfaceFlags );
ent->think = ProximityMine_Activate;
ent->nextthink = level.time + 2000;
vectoangles( trace->plane.normal, ent->s.angles );
ent->s.angles[0] += 90;
// link the prox mine to the other entity
ent->enemy = other;
ent->die = ProximityMine_Die;
VectorCopy(trace->plane.normal, ent->movedir);
VectorSet(ent->r.mins, -4, -4, -4);
VectorSet(ent->r.maxs, 4, 4, 4);
trap_LinkEntity(ent);
return;
}
#endif
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 );
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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -