stvoy-mp-sdk/Code-DM/cgame/cg_effects.c
2000-12-11 00:00:00 +00:00

403 lines
9.6 KiB
C

// Copyright (C) 1999-2000 Id Software, Inc.
//
// cg_effects.c -- these functions generate localentities, usually as a result
// of event processing
#include "cg_local.h"
#include "fx_local.h"
/*
==================
CG_BubbleTrail
Bullets shot underwater
==================
*/
void CG_BubbleTrail( vec3_t start, vec3_t end, float spacing ) {
vec3_t move;
vec3_t vec;
float len;
int i;
VectorCopy (start, move);
VectorSubtract (end, start, vec);
len = VectorNormalize (vec);
// advance a random amount first
i = rand() % (int)spacing;
VectorMA( move, i, vec, move );
VectorScale (vec, spacing, vec);
for ( ; i < len; i += spacing ) {
localEntity_t *le;
refEntity_t *re;
le = CG_AllocLocalEntity();
le->leFlags = LEF_PUFF_DONT_SCALE;
le->leType = LE_MOVE_SCALE_FADE;
le->startTime = cg.time;
le->endTime = cg.time + 1000 + random() * 250;
le->lifeRate = 1.0 / ( le->endTime - le->startTime );
re = &le->refEntity;
re->shaderTime = cg.time / 1000.0f;
re->reType = RT_SPRITE;
re->data.sprite.rotation = 0;
re->data.sprite.radius = 3;
re->customShader = cgs.media.waterBubbleShader;
re->shaderRGBA[0] = 0xff;
re->shaderRGBA[1] = 0xff;
re->shaderRGBA[2] = 0xff;
re->shaderRGBA[3] = 0xff;
le->color[3] = 1.0;
le->pos.trType = TR_LINEAR;
le->pos.trTime = cg.time;
VectorCopy( move, le->pos.trBase );
le->pos.trDelta[0] = crandom()*5;
le->pos.trDelta[1] = crandom()*5;
le->pos.trDelta[2] = crandom()*5 + 6;
VectorAdd (move, vec, move);
}
}
/*
=====================
CG_SmokePuff
Adds a smoke puff or blood trail localEntity.
=====================
*/
localEntity_t *CG_SmokePuff( const vec3_t p, const vec3_t vel,
float radius,
float r, float g, float b, float a,
float duration,
int startTime,
int leFlags,
qhandle_t hShader ) {
static int seed = 0x92;
localEntity_t *le;
refEntity_t *re;
le = CG_AllocLocalEntity();
le->leFlags = leFlags;
le->data.sprite.radius = radius;
re = &le->refEntity;
re->data.sprite.rotation = Q_random( &seed ) * 360;
re->data.sprite.radius = radius;
re->shaderTime = startTime / 1000.0f;
le->leType = LE_MOVE_SCALE_FADE;
le->startTime = startTime;
le->endTime = startTime + duration;
le->lifeRate = 1.0 / ( le->endTime - le->startTime );
le->color[0] = r;
le->color[1] = g;
le->color[2] = b;
le->color[3] = a;
le->pos.trType = TR_LINEAR;
le->pos.trTime = startTime;
VectorCopy( vel, le->pos.trDelta );
VectorCopy( p, le->pos.trBase );
VectorCopy( p, re->origin );
re->customShader = hShader;
// rage pro can't alpha fade, so use a different shader
if ( cgs.glconfig.hardwareType == GLHW_RAGEPRO ) {
re->customShader = cgs.media.smokePuffRageProShader;
re->shaderRGBA[0] = 0xff;
re->shaderRGBA[1] = 0xff;
re->shaderRGBA[2] = 0xff;
re->shaderRGBA[3] = 0xff;
} else {
re->shaderRGBA[0] = le->color[0] * 0xff;
re->shaderRGBA[1] = le->color[1] * 0xff;
re->shaderRGBA[2] = le->color[2] * 0xff;
re->shaderRGBA[3] = 0xff;
}
re->reType = RT_SPRITE;
re->data.sprite.radius = le->data.sprite.radius;
return le;
}
/*
==================
CG_SpawnEffect
Player teleporting in or out
==================
*/
void CG_SpawnEffect( vec3_t org ) {
localEntity_t *le;
refEntity_t *re;
FX_Transporter(org);
le = CG_AllocLocalEntity();
le->leFlags = 0;
le->leType = LE_FADE_RGB;
le->startTime = cg.time;
le->endTime = cg.time + 500;
le->lifeRate = 1.0 / ( le->endTime - le->startTime );
le->color[0] = le->color[1] = le->color[2] = le->color[3] = 1.0;
re = &le->refEntity;
re->reType = RT_MODEL;
re->shaderTime = cg.time / 1000.0f;
re->customShader = cgs.media.teleportEffectShader;
re->hModel = cgs.media.teleportEffectModel;
AxisClear( re->axis );
VectorCopy( org, re->origin );
re->origin[2] -= 24;
}
/*
====================
CG_MakeExplosion
====================
*/
localEntity_t *CG_MakeExplosion( vec3_t origin, vec3_t dir,
qhandle_t hModel, qhandle_t shader,
int msec, qboolean isSprite ) {
float ang;
localEntity_t *ex;
int offset;
vec3_t tmpVec, newOrigin;
if ( msec <= 0 ) {
CG_Error( "CG_MakeExplosion: msec = %i", msec );
}
// skew the time a bit so they aren't all in sync
offset = rand() & 63;
ex = CG_AllocLocalEntity();
if ( isSprite ) {
ex->leType = LE_SPRITE_EXPLOSION;
// randomly rotate sprite orientation
ex->refEntity.data.sprite.rotation = rand() % 360;
VectorScale( dir, 16, tmpVec );
VectorAdd( tmpVec, origin, newOrigin );
} else {
ex->leType = LE_EXPLOSION;
VectorCopy( origin, newOrigin );
// set axis with random rotate
if ( !dir ) {
AxisClear( ex->refEntity.axis );
} else {
ang = rand() % 360;
VectorCopy( dir, ex->refEntity.axis[0] );
RotateAroundDirection( ex->refEntity.axis, ang );
}
}
ex->startTime = cg.time - offset;
ex->endTime = ex->startTime + msec;
// bias the time so all shader effects start correctly
ex->refEntity.shaderTime = ex->startTime / 1000.0f;
ex->refEntity.hModel = hModel;
ex->refEntity.customShader = shader;
// set origin
VectorCopy( newOrigin, ex->refEntity.origin );
VectorCopy( newOrigin, ex->refEntity.oldorigin );
ex->color[0] = ex->color[1] = ex->color[2] = 1.0;
return ex;
}
localEntity_t *CG_MakeExplosion2( vec3_t origin, vec3_t dir,
qhandle_t hModel, int numFrames, qhandle_t shader,
int msec, qboolean isSprite, float scale, int flags) {
float ang;
localEntity_t *ex;
int offset;
vec3_t tmpVec, newOrigin;
if ( msec <= 0 ) {
CG_Error( "CG_MakeExplosion: msec = %i", msec );
}
// skew the time a bit so they aren't all in sync
offset = rand() & 63;
ex = CG_AllocLocalEntity();
if ( isSprite ) {
ex->leType = LE_SPRITE_EXPLOSION;
// randomly rotate sprite orientation
ex->refEntity.data.sprite.rotation = rand() % 360;
VectorScale( dir, 16, tmpVec );
VectorAdd( tmpVec, origin, newOrigin );
} else {
ex->leType = LE_EXPLOSION;
VectorCopy( origin, newOrigin );
// set axis with random rotate
if ( !dir ) {
AxisClear( ex->refEntity.axis );
} else {
ang = rand() % 360;
VectorCopy( dir, ex->refEntity.axis[0] );
RotateAroundDirection( ex->refEntity.axis, ang );
}
}
ex->startTime = cg.time - offset;
ex->endTime = ex->startTime + msec;
// bias the time so all shader effects start correctly
ex->refEntity.shaderTime = ex->startTime / 1000.0f;
ex->refEntity.hModel = hModel;
ex->refEntity.customShader = shader;
ex->lifeRate = (float)numFrames / msec;
ex->leFlags = flags;
//Scale the explosion
if (scale != 1) {
ex->refEntity.nonNormalizedAxes = qtrue;
VectorScale( ex->refEntity.axis[0], scale, ex->refEntity.axis[0] );
VectorScale( ex->refEntity.axis[1], scale, ex->refEntity.axis[1] );
VectorScale( ex->refEntity.axis[2], scale, ex->refEntity.axis[2] );
}
// set origin
VectorCopy( newOrigin, ex->refEntity.origin );
VectorCopy( newOrigin, ex->refEntity.oldorigin );
ex->color[0] = ex->color[1] = ex->color[2] = 1.0;
return ex;
}
/*
=================
CG_Bleed
This is the spurt of blood when a character gets hit
=================
*/
void CG_Bleed( vec3_t origin, int entityNum ) {
localEntity_t *ex;
if ( !cg_blood.integer ) {
return;
}
ex = CG_AllocLocalEntity();
ex->leType = LE_EXPLOSION;
ex->startTime = cg.time;
ex->endTime = ex->startTime + 500;
VectorCopy ( origin, ex->refEntity.origin);
ex->refEntity.reType = RT_SPRITE;
ex->refEntity.data.sprite.rotation = rand() % 360;
ex->refEntity.data.sprite.radius = 16;
ex->refEntity.customShader = cgs.media.bloodExplosionShader;
// don't show player's own blood in view
if ( entityNum == cg.snap->ps.clientNum ) {
ex->refEntity.renderfx |= RF_THIRD_PERSON;
}
}
/*
-------------------------
CG_ExplosionEffects
Used to find the player and shake the camera if close enough
intensity ranges from 1 (minor tremble) to 16 (major quake)
-------------------------
*/
void CG_ExplosionEffects( vec3_t origin, int intensity, int radius)
{
//FIXME: When exactly is the vieworg calculated in relation to the rest of the frame?s
vec3_t dir;
float dist, intensityScale;
float realIntensity;
VectorSubtract( cg.refdef.vieworg, origin, dir );
dist = VectorNormalize( dir );
//Use the dir to add kick to the explosion
if ( dist > radius )
return;
intensityScale = 1 - ( dist / (float) radius );
realIntensity = intensity * intensityScale;
CG_CameraShake( realIntensity, 500 );
}
/*
=================
CG_Seeker
=================
*/
void CG_Seeker( centity_t *cent )
{
refEntity_t re;
vec3_t seekerOrg, viewAng;
float angle;
angle = cg.time/100.0f;
seekerOrg[0] = cent->lerpOrigin[0] + 18 * cos(angle);
seekerOrg[1] = cent->lerpOrigin[1] + 18 * sin(angle);
seekerOrg[2] = cent->lerpOrigin[2] + cg.predictedPlayerState.viewheight + 8 + (3*cos(cg.time/150.0f));
memset( &re, 0, sizeof( re ) );
re.reType = RT_MODEL;
VectorCopy ( seekerOrg, re.origin);
re.hModel = cgs.media.seekerModel;
re.shaderTime = (cg.time / 1000.0f);
VectorCopy (cent->lerpAngles , viewAng); // so the seeker faces the same direction the player is
viewAng[0] = -90; // but, we don't want the seeker facing up or down, always horizontal
AnglesToAxis( viewAng, re.axis );
VectorScale(re.axis[0], 0.5, re.axis[0]);
VectorScale(re.axis[1], 0.5, re.axis[1]);
VectorScale(re.axis[2], 0.5, re.axis[2]);
re.nonNormalizedAxes=qtrue;
trap_R_AddRefEntityToScene( &re );
}