📄 cg_ents.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
===========================================================================
*/
//
// cg_ents.c -- present snapshot entities, happens every single frame
#include "cg_local.h"
/*
======================
CG_PositionEntityOnTag
Modifies the entities position and axis by the given
tag location
======================
*/
void CG_PositionEntityOnTag( refEntity_t *entity, const refEntity_t *parent,
qhandle_t parentModel, char *tagName ) {
int i;
orientation_t lerped;
// lerp the tag
trap_R_LerpTag( &lerped, parentModel, parent->oldframe, parent->frame,
1.0 - parent->backlerp, tagName );
// FIXME: allow origin offsets along tag?
VectorCopy( parent->origin, entity->origin );
for ( i = 0 ; i < 3 ; i++ ) {
VectorMA( entity->origin, lerped.origin[i], parent->axis[i], entity->origin );
}
// had to cast away the const to avoid compiler problems...
MatrixMultiply( lerped.axis, ((refEntity_t *)parent)->axis, entity->axis );
entity->backlerp = parent->backlerp;
}
/*
======================
CG_PositionRotatedEntityOnTag
Modifies the entities position and axis by the given
tag location
======================
*/
void CG_PositionRotatedEntityOnTag( refEntity_t *entity, const refEntity_t *parent,
qhandle_t parentModel, char *tagName ) {
int i;
orientation_t lerped;
vec3_t tempAxis[3];
//AxisClear( entity->axis );
// lerp the tag
trap_R_LerpTag( &lerped, parentModel, parent->oldframe, parent->frame,
1.0 - parent->backlerp, tagName );
// FIXME: allow origin offsets along tag?
VectorCopy( parent->origin, entity->origin );
for ( i = 0 ; i < 3 ; i++ ) {
VectorMA( entity->origin, lerped.origin[i], parent->axis[i], entity->origin );
}
// had to cast away the const to avoid compiler problems...
MatrixMultiply( entity->axis, lerped.axis, tempAxis );
MatrixMultiply( tempAxis, ((refEntity_t *)parent)->axis, entity->axis );
}
/*
==========================================================================
FUNCTIONS CALLED EACH FRAME
==========================================================================
*/
/*
======================
CG_SetEntitySoundPosition
Also called by event processing code
======================
*/
void CG_SetEntitySoundPosition( centity_t *cent ) {
if ( cent->currentState.solid == SOLID_BMODEL ) {
vec3_t origin;
float *v;
v = cgs.inlineModelMidpoints[ cent->currentState.modelindex ];
VectorAdd( cent->lerpOrigin, v, origin );
trap_S_UpdateEntityPosition( cent->currentState.number, origin );
} else {
trap_S_UpdateEntityPosition( cent->currentState.number, cent->lerpOrigin );
}
}
/*
==================
CG_EntityEffects
Add continuous entity effects, like local entity emission and lighting
==================
*/
static void CG_EntityEffects( centity_t *cent ) {
// update sound origins
CG_SetEntitySoundPosition( cent );
// add loop sound
if ( cent->currentState.loopSound ) {
if (cent->currentState.eType != ET_SPEAKER) {
trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin,
cgs.gameSounds[ cent->currentState.loopSound ] );
} else {
trap_S_AddRealLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin,
cgs.gameSounds[ cent->currentState.loopSound ] );
}
}
// constant light glow
if ( cent->currentState.constantLight ) {
int cl;
int i, r, g, b;
cl = cent->currentState.constantLight;
r = cl & 255;
g = ( cl >> 8 ) & 255;
b = ( cl >> 16 ) & 255;
i = ( ( cl >> 24 ) & 255 ) * 4;
trap_R_AddLightToScene( cent->lerpOrigin, i, r, g, b );
}
}
/*
==================
CG_General
==================
*/
static void CG_General( centity_t *cent ) {
refEntity_t ent;
entityState_t *s1;
s1 = ¢->currentState;
// if set to invisible, skip
if (!s1->modelindex) {
return;
}
memset (&ent, 0, sizeof(ent));
// set frame
ent.frame = s1->frame;
ent.oldframe = ent.frame;
ent.backlerp = 0;
VectorCopy( cent->lerpOrigin, ent.origin);
VectorCopy( cent->lerpOrigin, ent.oldorigin);
ent.hModel = cgs.gameModels[s1->modelindex];
// player model
if (s1->number == cg.snap->ps.clientNum) {
ent.renderfx |= RF_THIRD_PERSON; // only draw from mirrors
}
// convert angles to axis
AnglesToAxis( cent->lerpAngles, ent.axis );
// add to refresh list
trap_R_AddRefEntityToScene (&ent);
}
/*
==================
CG_Speaker
Speaker entities can automatically play sounds
==================
*/
static void CG_Speaker( centity_t *cent ) {
if ( ! cent->currentState.clientNum ) { // FIXME: use something other than clientNum...
return; // not auto triggering
}
if ( cg.time < cent->miscTime ) {
return;
}
trap_S_StartSound (NULL, cent->currentState.number, CHAN_ITEM, cgs.gameSounds[cent->currentState.eventParm] );
// ent->s.frame = ent->wait * 10;
// ent->s.clientNum = ent->random * 10;
cent->miscTime = cg.time + cent->currentState.frame * 100 + cent->currentState.clientNum * 100 * crandom();
}
/*
==================
CG_Item
==================
*/
static void CG_Item( centity_t *cent ) {
refEntity_t ent;
entityState_t *es;
gitem_t *item;
int msec;
float frac;
float scale;
weaponInfo_t *wi;
es = ¢->currentState;
if ( es->modelindex >= bg_numItems ) {
CG_Error( "Bad item index %i on entity", es->modelindex );
}
// if set to invisible, skip
if ( !es->modelindex || ( es->eFlags & EF_NODRAW ) ) {
return;
}
item = &bg_itemlist[ es->modelindex ];
if ( cg_simpleItems.integer && item->giType != IT_TEAM ) {
memset( &ent, 0, sizeof( ent ) );
ent.reType = RT_SPRITE;
VectorCopy( cent->lerpOrigin, ent.origin );
ent.radius = 14;
ent.customShader = cg_items[es->modelindex].icon;
ent.shaderRGBA[0] = 255;
ent.shaderRGBA[1] = 255;
ent.shaderRGBA[2] = 255;
ent.shaderRGBA[3] = 255;
trap_R_AddRefEntityToScene(&ent);
return;
}
// items bob up and down continuously
scale = 0.005 + cent->currentState.number * 0.00001;
cent->lerpOrigin[2] += 4 + cos( ( cg.time + 1000 ) * scale ) * 4;
memset (&ent, 0, sizeof(ent));
// autorotate at one of two speeds
if ( item->giType == IT_HEALTH ) {
VectorCopy( cg.autoAnglesFast, cent->lerpAngles );
AxisCopy( cg.autoAxisFast, ent.axis );
} else {
VectorCopy( cg.autoAngles, cent->lerpAngles );
AxisCopy( cg.autoAxis, ent.axis );
}
wi = NULL;
// the weapons have their origin where they attatch to player
// models, so we need to offset them or they will rotate
// eccentricly
if ( item->giType == IT_WEAPON ) {
wi = &cg_weapons[item->giTag];
cent->lerpOrigin[0] -=
wi->weaponMidpoint[0] * ent.axis[0][0] +
wi->weaponMidpoint[1] * ent.axis[1][0] +
wi->weaponMidpoint[2] * ent.axis[2][0];
cent->lerpOrigin[1] -=
wi->weaponMidpoint[0] * ent.axis[0][1] +
wi->weaponMidpoint[1] * ent.axis[1][1] +
wi->weaponMidpoint[2] * ent.axis[2][1];
cent->lerpOrigin[2] -=
wi->weaponMidpoint[0] * ent.axis[0][2] +
wi->weaponMidpoint[1] * ent.axis[1][2] +
wi->weaponMidpoint[2] * ent.axis[2][2];
cent->lerpOrigin[2] += 8; // an extra height boost
}
ent.hModel = cg_items[es->modelindex].models[0];
VectorCopy( cent->lerpOrigin, ent.origin);
VectorCopy( cent->lerpOrigin, ent.oldorigin);
ent.nonNormalizedAxes = qfalse;
// if just respawned, slowly scale up
msec = cg.time - cent->miscTime;
if ( msec >= 0 && msec < ITEM_SCALEUP_TIME ) {
frac = (float)msec / ITEM_SCALEUP_TIME;
VectorScale( ent.axis[0], frac, ent.axis[0] );
VectorScale( ent.axis[1], frac, ent.axis[1] );
VectorScale( ent.axis[2], frac, ent.axis[2] );
ent.nonNormalizedAxes = qtrue;
} else {
frac = 1.0;
}
// items without glow textures need to keep a minimum light value
// so they are always visible
if ( ( item->giType == IT_WEAPON ) ||
( item->giType == IT_ARMOR ) ) {
ent.renderfx |= RF_MINLIGHT;
}
// increase the size of the weapons when they are presented as items
if ( item->giType == IT_WEAPON ) {
VectorScale( ent.axis[0], 1.5, ent.axis[0] );
VectorScale( ent.axis[1], 1.5, ent.axis[1] );
VectorScale( ent.axis[2], 1.5, ent.axis[2] );
ent.nonNormalizedAxes = qtrue;
#ifdef MISSIONPACK
trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, cgs.media.weaponHoverSound );
#endif
}
#ifdef MISSIONPACK
if ( item->giType == IT_HOLDABLE && item->giTag == HI_KAMIKAZE ) {
VectorScale( ent.axis[0], 2, ent.axis[0] );
VectorScale( ent.axis[1], 2, ent.axis[1] );
VectorScale( ent.axis[2], 2, ent.axis[2] );
ent.nonNormalizedAxes = qtrue;
}
#endif
// add to refresh list
trap_R_AddRefEntityToScene(&ent);
#ifdef MISSIONPACK
if ( item->giType == IT_WEAPON && wi->barrelModel ) {
refEntity_t barrel;
memset( &barrel, 0, sizeof( barrel ) );
barrel.hModel = wi->barrelModel;
VectorCopy( ent.lightingOrigin, barrel.lightingOrigin );
barrel.shadowPlane = ent.shadowPlane;
barrel.renderfx = ent.renderfx;
CG_PositionRotatedEntityOnTag( &barrel, &ent, wi->weaponModel, "tag_barrel" );
AxisCopy( ent.axis, barrel.axis );
barrel.nonNormalizedAxes = ent.nonNormalizedAxes;
trap_R_AddRefEntityToScene( &barrel );
}
#endif
// accompanying rings / spheres for powerups
if ( !cg_simpleItems.integer )
{
vec3_t spinAngles;
VectorClear( spinAngles );
if ( item->giType == IT_HEALTH || item->giType == IT_POWERUP )
{
if ( ( ent.hModel = cg_items[es->modelindex].models[1] ) != 0 )
{
if ( item->giType == IT_POWERUP )
{
ent.origin[2] += 12;
spinAngles[1] = ( cg.time & 1023 ) * 360 / -1024.0f;
}
AnglesToAxis( spinAngles, ent.axis );
// scale up if respawning
if ( frac != 1.0 ) {
VectorScale( ent.axis[0], frac, ent.axis[0] );
VectorScale( ent.axis[1], frac, ent.axis[1] );
VectorScale( ent.axis[2], frac, ent.axis[2] );
ent.nonNormalizedAxes = qtrue;
}
trap_R_AddRefEntityToScene( &ent );
}
}
}
}
//============================================================================
/*
===============
CG_Missile
===============
*/
static void CG_Missile( centity_t *cent ) {
refEntity_t ent;
entityState_t *s1;
const weaponInfo_t *weapon;
// int col;
s1 = ¢->currentState;
if ( s1->weapon > WP_NUM_WEAPONS ) {
s1->weapon = 0;
}
weapon = &cg_weapons[s1->weapon];
// calculate the axis
VectorCopy( s1->angles, cent->lerpAngles);
// add trails
if ( weapon->missileTrailFunc )
{
weapon->missileTrailFunc( cent, weapon );
}
/*
if ( cent->currentState.modelindex == TEAM_RED ) {
col = 1;
}
else if ( cent->currentState.modelindex == TEAM_BLUE ) {
col = 2;
}
else {
col = 0;
}
// add dynamic light
if ( weapon->missileDlight ) {
trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight,
weapon->missileDlightColor[col][0], weapon->missileDlightColor[col][1], weapon->missileDlightColor[col][2] );
}
*/
// add dynamic light
if ( weapon->missileDlight ) {
trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight,
weapon->missileDlightColor[0], weapon->missileDlightColor[1], weapon->missileDlightColor[2] );
}
// add missile sound
if ( weapon->missileSound ) {
vec3_t velocity;
BG_EvaluateTrajectoryDelta( ¢->currentState.pos, cg.time, velocity );
trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, velocity, weapon->missileSound );
}
// create the render entity
memset (&ent, 0, sizeof(ent));
VectorCopy( cent->lerpOrigin, ent.origin);
VectorCopy( cent->lerpOrigin, ent.oldorigin);
if ( cent->currentState.weapon == WP_PLASMAGUN ) {
ent.reType = RT_SPRITE;
ent.radius = 16;
ent.rotation = 0;
ent.customShader = cgs.media.plasmaBallShader;
trap_R_AddRefEntityToScene( &ent );
return;
}
// flicker between two skins
ent.skinNum = cg.clientFrame & 1;
ent.hModel = weapon->missileModel;
ent.renderfx = weapon->missileRenderfx | RF_NOSHADOW;
#ifdef MISSIONPACK
if ( cent->currentState.weapon == WP_PROX_LAUNCHER ) {
if (s1->generic1 == TEAM_BLUE) {
ent.hModel = cgs.media.blueProxMine;
}
}
#endif
// convert direction of travel into axis
if ( VectorNormalize2( s1->pos.trDelta, ent.axis[0] ) == 0 ) {
ent.axis[0][2] = 1;
}
// spin as it moves
if ( s1->pos.trType != TR_STATIONARY ) {
RotateAroundDirection( ent.axis, cg.time / 4 );
} else {
#ifdef MISSIONPACK
if ( s1->weapon == WP_PROX_LAUNCHER ) {
AnglesToAxis( cent->lerpAngles, ent.axis );
}
else
#endif
{
RotateAroundDirection( ent.axis, s1->time );
}
}
// add to refresh list, possibly with quad glow
CG_AddRefEntityWithPowerups( &ent, s1, TEAM_FREE );
}
/*
===============
CG_Grapple
This is called when the grapple is sitting up against the wall
===============
*/
static void CG_Grapple( centity_t *cent ) {
refEntity_t ent;
entityState_t *s1;
const weaponInfo_t *weapon;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -