mirror of
https://github.com/ioquake/jedi-academy.git
synced 2024-11-25 21:51:04 +00:00
3809 lines
98 KiB
C
3809 lines
98 KiB
C
// Copyright (C) 1999-2000 Id Software, Inc.
|
|
//
|
|
// cg_ents.c -- present snapshot entities, happens every single frame
|
|
|
|
#include "cg_local.h"
|
|
/*
|
|
Ghoul2 Insert Start
|
|
*/
|
|
#include "../game/q_shared.h"
|
|
#include "../ghoul2/G2.h"
|
|
/*
|
|
Ghoul2 Insert end
|
|
*/
|
|
|
|
extern qboolean CG_InFighter( void );
|
|
static void CG_Missile( centity_t *cent );
|
|
|
|
/*
|
|
======================
|
|
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
|
|
|
|
==========================================================================
|
|
*/
|
|
|
|
//only need to use the CG_S_ system when you want a looping sound that isn't going to add itself
|
|
//via trap each frame. Such as ones generated by events.
|
|
/*
|
|
======================
|
|
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_S_AddLoopingSound
|
|
|
|
Set the current looping sounds on the entity.
|
|
==================
|
|
*/
|
|
void CG_S_AddLoopingSound(int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx)
|
|
{
|
|
centity_t *cent = &cg_entities[entityNum];
|
|
cgLoopSound_t *cSound = NULL;
|
|
int i = 0;
|
|
qboolean alreadyPlaying = qfalse;
|
|
|
|
//first see if we're already looping this sound handle.
|
|
while (i < cent->numLoopingSounds)
|
|
{
|
|
cSound = ¢->loopingSound[i];
|
|
|
|
if (cSound->sfx == sfx)
|
|
{
|
|
alreadyPlaying = qtrue;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (alreadyPlaying && cSound)
|
|
{ //if this is the case, just update the properties of the looping sound and return.
|
|
VectorCopy(origin, cSound->origin);
|
|
VectorCopy(velocity, cSound->velocity);
|
|
}
|
|
else if (cent->numLoopingSounds >= MAX_CG_LOOPSOUNDS)
|
|
{ //Just don't add it then I suppose.
|
|
#ifdef _XBOX // We decreased this number, so I'd like to know if it gets overflowed
|
|
Com_Printf( S_COLOR_YELLOW "Warning: MAX_CG_LOOPSOUNDS exceeded!!\n" );
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
//Add a new looping sound.
|
|
cSound = ¢->loopingSound[cent->numLoopingSounds];
|
|
|
|
cSound->entityNum = entityNum;
|
|
VectorCopy(origin, cSound->origin);
|
|
VectorCopy(velocity, cSound->velocity);
|
|
cSound->sfx = sfx;
|
|
|
|
cent->numLoopingSounds++;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
CG_S_AddLoopingSound
|
|
|
|
For now just redirect, might eventually do something different.
|
|
==================
|
|
*/
|
|
void CG_S_AddRealLoopingSound(int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx)
|
|
{
|
|
CG_S_AddLoopingSound(entityNum, origin, velocity, sfx);
|
|
}
|
|
|
|
/*
|
|
==================
|
|
CG_S_AddLoopingSound
|
|
|
|
Clear looping sounds.
|
|
==================
|
|
*/
|
|
void CG_S_StopLoopingSound(int entityNum, sfxHandle_t sfx)
|
|
{
|
|
centity_t *cent = &cg_entities[entityNum];
|
|
cgLoopSound_t *cSound;
|
|
|
|
if (sfx == -1)
|
|
{ //clear all the looping sounds on the entity
|
|
cent->numLoopingSounds = 0;
|
|
}
|
|
else
|
|
{ //otherwise, clear only the specified looping sound
|
|
int i = 0;
|
|
|
|
while (i < cent->numLoopingSounds)
|
|
{
|
|
cSound = ¢->loopingSound[i];
|
|
|
|
if (cSound->sfx == sfx)
|
|
{ //remove it then
|
|
int x = i+1;
|
|
|
|
while (x < cent->numLoopingSounds)
|
|
{
|
|
memcpy(¢->loopingSound[x-1], ¢->loopingSound[x], sizeof(cent->loopingSound[x]));
|
|
x++;
|
|
}
|
|
cent->numLoopingSounds--;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
}
|
|
//trap_S_StopLoopingSound(entityNum);
|
|
}
|
|
|
|
/*
|
|
==================
|
|
CG_S_UpdateLoopingSounds
|
|
|
|
Update any existing looping sounds on the entity.
|
|
==================
|
|
*/
|
|
void CG_S_UpdateLoopingSounds(int entityNum)
|
|
{
|
|
centity_t *cent = &cg_entities[entityNum];
|
|
cgLoopSound_t *cSound;
|
|
vec3_t lerpOrg;
|
|
float *v;
|
|
int i = 0;
|
|
|
|
if (!cent->numLoopingSounds)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (cent->currentState.eType == ET_MOVER)
|
|
{
|
|
v = cgs.inlineModelMidpoints[ cent->currentState.modelindex ];
|
|
VectorAdd( cent->lerpOrigin, v, lerpOrg );
|
|
}
|
|
else
|
|
{
|
|
VectorCopy(cent->lerpOrigin, lerpOrg);
|
|
}
|
|
|
|
while (i < cent->numLoopingSounds)
|
|
{
|
|
cSound = ¢->loopingSound[i];
|
|
|
|
//trap_S_AddLoopingSound(entityNum, cSound->origin, cSound->velocity, cSound->sfx);
|
|
//I guess just keep using lerpOrigin for now,
|
|
trap_S_AddLoopingSound(entityNum, lerpOrg, cSound->velocity, cSound->sfx);
|
|
i++;
|
|
}
|
|
}
|
|
|
|
/*
|
|
==================
|
|
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 || (cent->currentState.loopIsSoundset && cent->currentState.number >= MAX_CLIENTS) ) {
|
|
sfxHandle_t realSoundIndex = -1;
|
|
|
|
if (cent->currentState.loopIsSoundset && cent->currentState.number >= MAX_CLIENTS)
|
|
{ //If this is so, then first get our soundset from the index, and loopSound actually contains which part of the set to
|
|
//use rather than a sound index (BMS_START [0], BMS_MID [1], or BMS_END [2]). Typically loop sounds will be BMS_MID.
|
|
const char *soundSet;
|
|
|
|
soundSet = CG_ConfigString( CS_AMBIENT_SET + cent->currentState.soundSetIndex );
|
|
|
|
if (soundSet && soundSet[0])
|
|
{
|
|
realSoundIndex = trap_AS_GetBModelSound(soundSet, cent->currentState.loopSound);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
realSoundIndex = cgs.gameSounds[ cent->currentState.loopSound ];
|
|
}
|
|
|
|
//rww - doors and things with looping sounds have a crazy origin (being brush models and all)
|
|
if (realSoundIndex != -1)
|
|
{
|
|
if ( cent->currentState.solid == SOLID_BMODEL )
|
|
{
|
|
vec3_t origin;
|
|
float *v;
|
|
|
|
v = cgs.inlineModelMidpoints[ cent->currentState.modelindex ];
|
|
VectorAdd( cent->lerpOrigin, v, origin );
|
|
trap_S_AddLoopingSound( cent->currentState.number, origin, vec3_origin,
|
|
realSoundIndex );
|
|
}
|
|
else if (cent->currentState.eType != ET_SPEAKER) {
|
|
trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin,
|
|
realSoundIndex );
|
|
} else {
|
|
trap_S_AddRealLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin,
|
|
realSoundIndex );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// 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 );
|
|
}
|
|
|
|
}
|
|
|
|
localEntity_t *FX_AddOrientedLine(vec3_t start, vec3_t end, vec3_t normal, float stScale, float scale,
|
|
float dscale, float startalpha, float endalpha, float killTime, qhandle_t shader)
|
|
{
|
|
localEntity_t *le;
|
|
|
|
#ifdef _DEBUG
|
|
if (!shader)
|
|
{
|
|
Com_Printf("FX_AddLine: NULL shader\n");
|
|
}
|
|
#endif
|
|
|
|
le = CG_AllocLocalEntity();
|
|
le->leType = LE_OLINE;
|
|
|
|
le->startTime = cg.time;
|
|
le->endTime = le->startTime + killTime;
|
|
le->data.line.width = scale;
|
|
le->data.line.dwidth = dscale;
|
|
|
|
le->alpha = startalpha;
|
|
le->dalpha = endalpha - startalpha;
|
|
|
|
le->refEntity.data.line.stscale = stScale;
|
|
le->refEntity.data.line.width = scale;
|
|
|
|
le->refEntity.customShader = shader;
|
|
|
|
// set origin
|
|
VectorCopy ( start, le->refEntity.origin);
|
|
VectorCopy ( end, le->refEntity.oldorigin );
|
|
|
|
AxisClear(le->refEntity.axis);
|
|
VectorCopy( normal, le->refEntity.axis[0] );
|
|
RotateAroundDirection( le->refEntity.axis, 0); // le->refEntity.data.sprite.rotation ); This is roll in quad land
|
|
|
|
le->refEntity.shaderRGBA[0] = 0xff;
|
|
le->refEntity.shaderRGBA[1] = 0xff;
|
|
le->refEntity.shaderRGBA[2] = 0xff;
|
|
le->refEntity.shaderRGBA[3] = 0xff;
|
|
|
|
le->color[0] = 1.0;
|
|
le->color[1] = 1.0;
|
|
le->color[2] = 1.0;
|
|
le->color[3] = 1.0;
|
|
le->lifeRate = 1.0 / ( le->endTime - le->startTime );
|
|
|
|
return(le);
|
|
}
|
|
|
|
void FX_DrawPortableShield(centity_t *cent)
|
|
{
|
|
//rww - this code differs a bit from the draw code in EF, I don't know why I had to do
|
|
//it this way yet it worked in EF the other way.
|
|
|
|
int xaxis, height, posWidth, negWidth, team;
|
|
vec3_t start, end, normal;
|
|
localEntity_t *le;
|
|
qhandle_t shader;
|
|
char buf[1024];
|
|
|
|
trap_Cvar_VariableStringBuffer("cl_paused", buf, sizeof(buf));
|
|
|
|
if (atoi(buf))
|
|
{ //rww - fix to keep from rendering repeatedly while HUD menu is up
|
|
return;
|
|
}
|
|
|
|
if (cent->currentState.eFlags & EF_NODRAW)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// decode the data stored in time2
|
|
xaxis = ((cent->currentState.time2 >> 24) & 1);
|
|
height = ((cent->currentState.time2 >> 16) & 255);
|
|
posWidth = ((cent->currentState.time2 >> 8) & 255);
|
|
negWidth = (cent->currentState.time2 & 255);
|
|
|
|
team = (cent->currentState.otherEntityNum2);
|
|
|
|
VectorClear(normal);
|
|
|
|
VectorCopy(cent->lerpOrigin, start);
|
|
VectorCopy(cent->lerpOrigin, end);
|
|
|
|
if (xaxis) // drawing along x-axis
|
|
{
|
|
start[0] -= negWidth;
|
|
end[0] += posWidth;
|
|
}
|
|
else
|
|
{
|
|
start[1] -= negWidth;
|
|
end[1] += posWidth;
|
|
}
|
|
|
|
normal[0] = 1;
|
|
normal[1] = 1;
|
|
|
|
start[2] += height/2;
|
|
end[2] += height/2;
|
|
|
|
if (team == TEAM_RED)
|
|
{
|
|
if (cent->currentState.trickedentindex)
|
|
{
|
|
shader = trap_R_RegisterShader( "gfx/misc/red_dmgshield" );
|
|
}
|
|
else
|
|
{
|
|
shader = trap_R_RegisterShader( "gfx/misc/red_portashield" );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (cent->currentState.trickedentindex)
|
|
{
|
|
shader = trap_R_RegisterShader( "gfx/misc/blue_dmgshield" );
|
|
}
|
|
else
|
|
{
|
|
shader = trap_R_RegisterShader( "gfx/misc/blue_portashield" );
|
|
}
|
|
}
|
|
|
|
le = FX_AddOrientedLine(start, end, normal, 1.0f, height, 0.0f, 1.0f, 1.0f, 50.0, shader);
|
|
}
|
|
|
|
/*
|
|
==================
|
|
CG_Special
|
|
==================
|
|
*/
|
|
void CG_Special( centity_t *cent ) {
|
|
entityState_t *s1;
|
|
|
|
s1 = ¢->currentState;
|
|
|
|
if (!s1)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// if set to invisible, skip
|
|
if (!s1->modelindex) {
|
|
return;
|
|
}
|
|
|
|
if (s1->modelindex == HI_SHIELD)
|
|
{ // The portable shield should go through a different rendering function.
|
|
FX_DrawPortableShield(cent);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Ghoul2 Insert Start
|
|
*/
|
|
|
|
// Copy the ghoul2 data into the ref ent correctly
|
|
void CG_SetGhoul2Info( refEntity_t *ent, centity_t *cent)
|
|
{
|
|
|
|
ent->ghoul2 = cent->ghoul2;
|
|
VectorCopy( cent->modelScale, ent->modelScale);
|
|
ent->radius = cent->radius;
|
|
VectorCopy (cent->lerpAngles, ent->angles);
|
|
}
|
|
|
|
|
|
|
|
// create 8 new points on screen around a model so we can see it's bounding box
|
|
void CG_CreateBBRefEnts(entityState_t *s1, vec3_t origin )
|
|
{
|
|
/*
|
|
//g2r
|
|
#if _DEBUG
|
|
refEntity_t point[8];
|
|
int i;
|
|
vec3_t angles = {0,0,0};
|
|
|
|
for (i=0; i<8; i++)
|
|
{
|
|
memset (&point[i], 0, sizeof(refEntity_t));
|
|
point[i].reType = RT_SPRITE;
|
|
point[i].radius = 1;
|
|
point[i].customShader = trap_R_RegisterShader("textures/tests/circle");
|
|
point[i].shaderRGBA[0] = 255;
|
|
point[i].shaderRGBA[1] = 255;
|
|
point[i].shaderRGBA[2] = 255;
|
|
point[i].shaderRGBA[3] = 255;
|
|
|
|
AnglesToAxis( angles, point[i].axis );
|
|
|
|
// now, we need to put the correct origins into each origin from the mins and max's
|
|
switch(i)
|
|
{
|
|
case 0:
|
|
VectorCopy(s1->mins, point[i].origin);
|
|
break;
|
|
case 1:
|
|
VectorCopy(s1->mins, point[i].origin);
|
|
point[i].origin[0] = s1->maxs[0];
|
|
break;
|
|
case 2:
|
|
VectorCopy(s1->mins, point[i].origin);
|
|
point[i].origin[1] = s1->maxs[1];
|
|
break;
|
|
case 3:
|
|
VectorCopy(s1->mins, point[i].origin);
|
|
point[i].origin[0] = s1->maxs[0];
|
|
point[i].origin[1] = s1->maxs[1];
|
|
break;
|
|
case 4:
|
|
VectorCopy(s1->maxs, point[i].origin);
|
|
break;
|
|
case 5:
|
|
VectorCopy(s1->maxs, point[i].origin);
|
|
point[i].origin[0] = s1->mins[0];
|
|
break;
|
|
case 6:
|
|
VectorCopy(s1->maxs, point[i].origin);
|
|
point[i].origin[1] = s1->mins[1];
|
|
break;
|
|
case 7:
|
|
VectorCopy(s1->maxs, point[i].origin);
|
|
point[i].origin[0] = s1->mins[0];
|
|
point[i].origin[1] = s1->mins[1];
|
|
break;
|
|
}
|
|
|
|
// add the original origin to each point and then stuff them out there
|
|
VectorAdd(point[i].origin, origin, point[i].origin);
|
|
|
|
trap_R_AddRefEntityToScene (&point[i]);
|
|
}
|
|
#endif
|
|
*/
|
|
}
|
|
|
|
// write in the axis and stuff
|
|
void G2_BoltToGhoul2Model(centity_t *cent, refEntity_t *ent)
|
|
{
|
|
// extract the wraith ID from the bolt info
|
|
int modelNum = cent->boltInfo >> MODEL_SHIFT;
|
|
int boltNum = cent->boltInfo >> BOLT_SHIFT;
|
|
int entNum = cent->boltInfo >> ENTITY_SHIFT;
|
|
mdxaBone_t boltMatrix;
|
|
|
|
modelNum &= MODEL_AND;
|
|
boltNum &= BOLT_AND;
|
|
entNum &= ENTITY_AND;
|
|
|
|
|
|
//NOTENOTE I put this here because the cgs.gamemodels array no longer gets initialized.
|
|
assert(0);
|
|
|
|
|
|
// go away and get me the bolt position for this frame please
|
|
trap_G2API_GetBoltMatrix(cent->ghoul2, modelNum, boltNum, &boltMatrix, cg_entities[entNum].currentState.angles, cg_entities[entNum].currentState.origin, cg.time, cgs.gameModels, cent->modelScale);
|
|
|
|
// set up the axis and origin we need for the actual effect spawning
|
|
ent->origin[0] = boltMatrix.matrix[0][3];
|
|
ent->origin[1] = boltMatrix.matrix[1][3];
|
|
ent->origin[2] = boltMatrix.matrix[2][3];
|
|
|
|
ent->axis[0][0] = boltMatrix.matrix[0][0];
|
|
ent->axis[0][1] = boltMatrix.matrix[1][0];
|
|
ent->axis[0][2] = boltMatrix.matrix[2][0];
|
|
|
|
ent->axis[1][0] = boltMatrix.matrix[0][1];
|
|
ent->axis[1][1] = boltMatrix.matrix[1][1];
|
|
ent->axis[1][2] = boltMatrix.matrix[2][1];
|
|
|
|
ent->axis[2][0] = boltMatrix.matrix[0][2];
|
|
ent->axis[2][1] = boltMatrix.matrix[1][2];
|
|
ent->axis[2][2] = boltMatrix.matrix[2][2];
|
|
}
|
|
|
|
void ScaleModelAxis(refEntity_t *ent)
|
|
|
|
{ // scale the model should we need to
|
|
if (ent->modelScale[0] && ent->modelScale[0] != 1.0f)
|
|
{
|
|
VectorScale( ent->axis[0], ent->modelScale[0] , ent->axis[0] );
|
|
ent->nonNormalizedAxes = qtrue;
|
|
}
|
|
if (ent->modelScale[1] && ent->modelScale[1] != 1.0f)
|
|
{
|
|
VectorScale( ent->axis[1], ent->modelScale[1] , ent->axis[1] );
|
|
ent->nonNormalizedAxes = qtrue;
|
|
}
|
|
if (ent->modelScale[2] && ent->modelScale[2] != 1.0f)
|
|
{
|
|
VectorScale( ent->axis[2], ent->modelScale[2] , ent->axis[2] );
|
|
ent->nonNormalizedAxes = qtrue;
|
|
}
|
|
}
|
|
/*
|
|
Ghoul2 Insert End
|
|
*/
|
|
|
|
char *forceHolocronModels[] = {
|
|
"models/map_objects/mp/lt_heal.md3", //FP_HEAL,
|
|
"models/map_objects/mp/force_jump.md3", //FP_LEVITATION,
|
|
"models/map_objects/mp/force_speed.md3", //FP_SPEED,
|
|
"models/map_objects/mp/force_push.md3", //FP_PUSH,
|
|
"models/map_objects/mp/force_pull.md3", //FP_PULL,
|
|
"models/map_objects/mp/lt_telepathy.md3", //FP_TELEPATHY,
|
|
"models/map_objects/mp/dk_grip.md3", //FP_GRIP,
|
|
"models/map_objects/mp/dk_lightning.md3", //FP_LIGHTNING,
|
|
"models/map_objects/mp/dk_rage.md3", //FP_RAGE,
|
|
"models/map_objects/mp/lt_protect.md3", //FP_PROTECT,
|
|
"models/map_objects/mp/lt_absorb.md3", //FP_ABSORB,
|
|
"models/map_objects/mp/lt_healother.md3", //FP_TEAM_HEAL,
|
|
"models/map_objects/mp/dk_powerother.md3", //FP_TEAM_FORCE,
|
|
"models/map_objects/mp/dk_drain.md3", //FP_DRAIN,
|
|
"models/map_objects/mp/force_sight.md3", //FP_SEE,
|
|
"models/map_objects/mp/saber_attack.md3", //FP_SABER_OFFENSE,
|
|
"models/map_objects/mp/saber_defend.md3", //FP_SABER_DEFENSE,
|
|
"models/map_objects/mp/saber_throw.md3" //FP_SABERTHROW
|
|
};
|
|
|
|
void CG_Disintegration(centity_t *cent, refEntity_t *ent)
|
|
{
|
|
vec3_t tempAng, hitLoc;
|
|
float tempLength;
|
|
|
|
VectorCopy(cent->currentState.origin2, hitLoc);
|
|
|
|
VectorSubtract( hitLoc, ent->origin, ent->oldorigin );
|
|
|
|
tempLength = VectorNormalize( ent->oldorigin );
|
|
vectoangles( ent->oldorigin, tempAng );
|
|
tempAng[YAW] -= cent->lerpAngles[YAW];
|
|
AngleVectors( tempAng, ent->oldorigin, NULL, NULL );
|
|
VectorScale( ent->oldorigin, tempLength, ent->oldorigin );
|
|
|
|
ent->endTime = cent->dustTrailTime;
|
|
|
|
ent->renderfx |= RF_DISINTEGRATE2;
|
|
ent->customShader = cgs.media.disruptorShader;
|
|
trap_R_AddRefEntityToScene( ent );
|
|
|
|
ent->renderfx &= ~(RF_DISINTEGRATE2);
|
|
ent->renderfx |= (RF_DISINTEGRATE1);
|
|
ent->customShader = 0;
|
|
trap_R_AddRefEntityToScene( ent );
|
|
|
|
if ( cg.time - ent->endTime < 1000 && (cg_timescale.value * cg_timescale.value * random()) > 0.05f )
|
|
{
|
|
vec3_t fxOrg, fxDir;
|
|
mdxaBone_t boltMatrix;
|
|
int torsoBolt = trap_G2API_AddBolt(cent->ghoul2, 0, "lower_lumbar");
|
|
|
|
VectorSet(fxDir, 0, 1, 0);
|
|
|
|
trap_G2API_GetBoltMatrix( cent->ghoul2, 0, torsoBolt, &boltMatrix, cent->lerpAngles, cent->lerpOrigin, cg.time,
|
|
cgs.gameModels, cent->modelScale);
|
|
BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, fxOrg );
|
|
|
|
VectorMA( fxOrg, -18, cg.refdef.viewaxis[0], fxOrg );
|
|
fxOrg[2] += crandom() * 20;
|
|
trap_FX_PlayEffectID( cgs.effects.mDisruptorDeathSmoke, fxOrg, fxDir, -1, -1 );
|
|
|
|
if ( random() > 0.5f )
|
|
{
|
|
trap_FX_PlayEffectID( cgs.effects.mDisruptorDeathSmoke, fxOrg, fxDir, -1, -1 );
|
|
}
|
|
}
|
|
}
|
|
|
|
extern int cgSiegeEntityRender;
|
|
static qboolean CG_RenderTimeEntBolt(centity_t *cent)
|
|
{
|
|
int clientNum = cent->currentState.boltToPlayer-1;
|
|
centity_t *cl;
|
|
mdxaBone_t matrix;
|
|
vec3_t boltOrg, boltAng;
|
|
int getBolt = -1;
|
|
|
|
if (clientNum >= MAX_CLIENTS || clientNum < 0)
|
|
{
|
|
assert(0);
|
|
return qfalse;
|
|
}
|
|
|
|
cl = &cg_entities[clientNum];
|
|
|
|
if (!cl->ghoul2)
|
|
{
|
|
assert(0);
|
|
return qfalse;
|
|
}
|
|
|
|
if (clientNum == cg.predictedPlayerState.clientNum &&
|
|
!cg.renderingThirdPerson)
|
|
{ //If in first person and you have it then render the thing spinning around on your hud.
|
|
cgSiegeEntityRender = cent->currentState.number; //set it to render at the end of the frame.
|
|
return qfalse;
|
|
}
|
|
|
|
getBolt = trap_G2API_AddBolt(cl->ghoul2, 0, "lhand");
|
|
|
|
trap_G2API_GetBoltMatrix(cl->ghoul2, 0, getBolt, &matrix, cl->turAngles, cl->lerpOrigin, cg.time, cgs.gameModels, cl->modelScale);
|
|
|
|
BG_GiveMeVectorFromMatrix(&matrix, ORIGIN, boltOrg);
|
|
BG_GiveMeVectorFromMatrix(&matrix, NEGATIVE_Y, boltAng);
|
|
vectoangles(boltAng, boltAng);
|
|
boltAng[PITCH] = boltAng[ROLL] = 0;
|
|
|
|
VectorCopy(boltOrg, cent->lerpOrigin);
|
|
VectorCopy(boltAng, cent->lerpAngles);
|
|
|
|
return qtrue;
|
|
}
|
|
|
|
/*
|
|
static void CG_SiegeEntRenderAboveHead(centity_t *cent)
|
|
{
|
|
int clientNum = cent->currentState.boltToPlayer-1;
|
|
centity_t *cl;
|
|
refEntity_t ent;
|
|
vec3_t renderAngles;
|
|
|
|
if (clientNum >= MAX_CLIENTS || clientNum < 0)
|
|
{
|
|
assert(0);
|
|
return;
|
|
}
|
|
|
|
cl = &cg_entities[clientNum];
|
|
|
|
memset(&ent, 0, sizeof(ent));
|
|
|
|
//Set the angles to the global auto rotating ones, and the origin to slightly above the client
|
|
VectorCopy(cg.autoAngles, renderAngles);
|
|
AnglesToAxis( renderAngles, ent.axis );
|
|
VectorCopy(cl->lerpOrigin, ent.origin);
|
|
ent.origin[2] += 50;
|
|
|
|
//Set the model (ghoul2 or md3/other)
|
|
if (cent->ghoul2)
|
|
{
|
|
ent.ghoul2 = cent->ghoul2;
|
|
ent.hModel = 0;
|
|
}
|
|
else
|
|
{
|
|
ent.ghoul2 = NULL;
|
|
ent.hModel = cgs.gameModels[cent->currentState.modelindex];
|
|
}
|
|
|
|
//Scale it up
|
|
ent.modelScale[0] = 1.5f;
|
|
ent.modelScale[1] = 1.5f;
|
|
ent.modelScale[2] = 1.5f;
|
|
ScaleModelAxis(&ent);
|
|
|
|
//Make it transparent
|
|
ent.renderfx = RF_FORCE_ENT_ALPHA;
|
|
ent.shaderRGBA[0] = ent.shaderRGBA[1] = ent.shaderRGBA[2] = 255;
|
|
ent.shaderRGBA[3] = 100;
|
|
|
|
//And finally add it
|
|
trap_R_AddRefEntityToScene(&ent);
|
|
}
|
|
*/
|
|
|
|
void CG_AddRadarEnt(centity_t *cent)
|
|
{
|
|
if (cg.radarEntityCount == sizeof(cg.radarEntities)/sizeof(cg.radarEntities[0]))
|
|
{
|
|
#ifdef _DEBUG
|
|
Com_Printf("^3Warning: CG_AddRadarEnt full. (%d max)\n", sizeof(cg.radarEntities)/sizeof(cg.radarEntities[0]));
|
|
#endif
|
|
return;
|
|
}
|
|
cg.radarEntities[cg.radarEntityCount++] = cent->currentState.number;
|
|
}
|
|
|
|
void CG_AddBracketedEnt(centity_t *cent)
|
|
{
|
|
if (cg.bracketedEntityCount == sizeof(cg.bracketedEntities)/sizeof(cg.bracketedEntities[0]))
|
|
{
|
|
#ifdef _DEBUG
|
|
Com_Printf("^3Warning: CG_AddBracketedEnt full. (%d max)\n", sizeof(cg.radarEntities)/sizeof(cg.bracketedEntities[0]));
|
|
#endif
|
|
return;
|
|
}
|
|
cg.bracketedEntities[cg.bracketedEntityCount++] = cent->currentState.number;
|
|
}
|
|
/*
|
|
==================
|
|
CG_General
|
|
==================
|
|
*/
|
|
void CG_G2ServerBoneAngles(centity_t *cent);
|
|
|
|
#include "../namespace_begin.h"
|
|
extern qboolean BG_GetRootSurfNameWithVariant( void *ghoul2, const char *rootSurfName, char *returnSurfName, int returnSize );
|
|
#include "../namespace_end.h"
|
|
|
|
static void CG_General( centity_t *cent ) {
|
|
refEntity_t ent;
|
|
entityState_t *s1;
|
|
float val;
|
|
int beamID;
|
|
vec3_t beamOrg;
|
|
mdxaBone_t matrix;
|
|
qboolean doNotSetModel = qfalse;
|
|
|
|
if (cent->currentState.modelGhoul2 == 127)
|
|
{ //not ready to be drawn or initialized..
|
|
return;
|
|
}
|
|
|
|
if (cent->ghoul2 && !cent->currentState.modelGhoul2 && cent->currentState.eType != ET_BODY &&
|
|
cent->currentState.number >= MAX_CLIENTS)
|
|
{ //this is a bad thing
|
|
if (trap_G2_HaveWeGhoul2Models(cent->ghoul2))
|
|
{
|
|
trap_G2API_CleanGhoul2Models(&(cent->ghoul2));
|
|
}
|
|
}
|
|
|
|
if (cent->currentState.eFlags & EF_RADAROBJECT)
|
|
{
|
|
CG_AddRadarEnt(cent);
|
|
}
|
|
if (cent->currentState.eFlags2 & EF2_BRACKET_ENTITY)
|
|
{
|
|
if ( CG_InFighter() )
|
|
{//only bracken when in a fighter
|
|
CG_AddBracketedEnt(cent);
|
|
}
|
|
}
|
|
|
|
if (cent->currentState.boltToPlayer)
|
|
{ //Shove it into the player's left hand then.
|
|
centity_t *pl = &cg_entities[cent->currentState.boltToPlayer-1];
|
|
if (CG_IsMindTricked(pl->currentState.trickedentindex,
|
|
pl->currentState.trickedentindex2,
|
|
pl->currentState.trickedentindex3,
|
|
pl->currentState.trickedentindex4,
|
|
cg.predictedPlayerState.clientNum))
|
|
{ //don't show if this guy is mindtricking
|
|
return;
|
|
}
|
|
if (!CG_RenderTimeEntBolt(cent))
|
|
{ //If this function returns qfalse we shouldn't render this ent at all.
|
|
if (cent->currentState.boltToPlayer > 0 &&
|
|
cent->currentState.boltToPlayer <= MAX_CLIENTS)
|
|
{
|
|
VectorCopy(pl->lerpOrigin, cent->lerpOrigin);
|
|
|
|
if (cent->currentState.eFlags & EF_CLIENTSMOOTH)
|
|
{ //if it's set to smooth keep the smoothed lerp origin updated, as we don't want to smooth while bolted.
|
|
VectorCopy(cent->lerpOrigin, cent->turAngles);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (cent->currentState.eFlags & EF_CLIENTSMOOTH)
|
|
{ //if it's set to smooth keep the smoothed lerp origin updated, as we don't want to smooth while bolted.
|
|
VectorCopy(cent->lerpOrigin, cent->turAngles);
|
|
}
|
|
|
|
/* disabled for now
|
|
if (pl->currentState.number != cg.predictedPlayerState.clientNum)
|
|
{ //don't render thing above head to self
|
|
CG_SiegeEntRenderAboveHead(cent);
|
|
}
|
|
*/
|
|
}
|
|
else if (cent->currentState.eFlags & EF_CLIENTSMOOTH)
|
|
{
|
|
if (cent->currentState.groundEntityNum >= ENTITYNUM_WORLD)
|
|
{
|
|
float smoothFactor = 0.5f*cg_timescale.value;
|
|
int k = 0;
|
|
vec3_t posDif;
|
|
|
|
//Use origin smoothing since dismembered limbs use ExPhys
|
|
if (DistanceSquared(cent->turAngles,cent->lerpOrigin)>18000.0f)
|
|
{
|
|
VectorCopy(cent->lerpOrigin, cent->turAngles);
|
|
}
|
|
|
|
VectorSubtract(cent->lerpOrigin, cent->turAngles, posDif);
|
|
|
|
for (k=0;k<3;k++)
|
|
{
|
|
cent->turAngles[k]=(cent->turAngles[k]+posDif[k]*smoothFactor);
|
|
cent->lerpOrigin[k]=cent->turAngles[k];
|
|
}
|
|
}
|
|
else
|
|
{ //if we're sitting on an entity like a moving plat then we don't want to smooth either
|
|
VectorCopy(cent->lerpOrigin, cent->turAngles);
|
|
}
|
|
}
|
|
|
|
//rww - now do ragdoll stuff
|
|
if (cent->ghoul2 &&
|
|
(cent->currentState.eType == ET_BODY || (cent->currentState.eFlags & EF_RAG)))
|
|
{
|
|
if (!(cent->currentState.eFlags & EF_NODRAW) &&
|
|
!(cent->currentState.eFlags & EF_DISINTEGRATION) &&
|
|
cent->bodyFadeTime <= cg.time)
|
|
{
|
|
vec3_t forcedAngles;
|
|
|
|
VectorClear(forcedAngles);
|
|
forcedAngles[YAW] = cent->lerpAngles[YAW];
|
|
|
|
CG_RagDoll(cent, forcedAngles);
|
|
}
|
|
}
|
|
else if (cent->isRagging)
|
|
{
|
|
cent->isRagging = qfalse;
|
|
|
|
if (cent->ghoul2 && trap_G2_HaveWeGhoul2Models(cent->ghoul2))
|
|
{ //May not be valid, in the case of a ragged entity being removed and a non-g2 ent filling its slot.
|
|
trap_G2API_SetRagDoll(cent->ghoul2, NULL); //calling with null parms resets to no ragdoll.
|
|
}
|
|
}
|
|
|
|
if (cent->currentState.boneOrient && cent->ghoul2)
|
|
{ //server sent us some bone angles to use
|
|
CG_G2ServerBoneAngles(cent);
|
|
}
|
|
|
|
if ((cent->currentState.eFlags & EF_G2ANIMATING) && cent->ghoul2)
|
|
{ //mini-animation routine for general objects that want to play quick ghoul2 anims
|
|
//obviously lacks much of the functionality contained in player/npc animation.
|
|
//we actually use torsoAnim as the start frame and legsAnim as the end frame and
|
|
//always play the anim on the root bone.
|
|
if (cent->currentState.torsoAnim != cent->pe.torso.animationNumber ||
|
|
cent->currentState.legsAnim != cent->pe.legs.animationNumber ||
|
|
cent->currentState.torsoFlip != cent->pe.torso.lastFlip)
|
|
{
|
|
trap_G2API_SetBoneAnim(cent->ghoul2, 0, "model_root", cent->currentState.torsoAnim,
|
|
cent->currentState.legsAnim, (BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND), 1.0f, cg.time, -1, 100);
|
|
|
|
cent->pe.torso.animationNumber = cent->currentState.torsoAnim;
|
|
cent->pe.legs.animationNumber = cent->currentState.legsAnim;
|
|
cent->pe.torso.lastFlip = cent->currentState.torsoFlip;
|
|
}
|
|
}
|
|
|
|
memset (&ent, 0, sizeof(ent));
|
|
|
|
ent.shaderRGBA[0] = cent->currentState.customRGBA[0];
|
|
ent.shaderRGBA[1] = cent->currentState.customRGBA[1];
|
|
ent.shaderRGBA[2] = cent->currentState.customRGBA[2];
|
|
ent.shaderRGBA[3] = cent->currentState.customRGBA[3];
|
|
|
|
if (cent->currentState.modelGhoul2 >= G2_MODELPART_HEAD &&
|
|
cent->currentState.modelGhoul2 <= G2_MODELPART_RLEG &&
|
|
/*cent->currentState.modelindex < MAX_CLIENTS &&*/
|
|
cent->currentState.weapon == G2_MODEL_PART)
|
|
{ //special case for client limbs
|
|
centity_t *clEnt;
|
|
int dismember_settings = cg_dismember.integer;
|
|
float smoothFactor = 0.5f*cg_timescale.value;
|
|
int k = 0;
|
|
vec3_t posDif;
|
|
|
|
doNotSetModel = qtrue;
|
|
|
|
if (cent->currentState.modelindex >= 0)
|
|
{
|
|
clEnt = &cg_entities[cent->currentState.modelindex];
|
|
}
|
|
else
|
|
{
|
|
clEnt = &cg_entities[cent->currentState.otherEntityNum2];
|
|
}
|
|
|
|
if (!dismember_settings)
|
|
{ //This client does not wish to see dismemberment.
|
|
return;
|
|
}
|
|
|
|
if (dismember_settings < 2 && (cent->currentState.modelGhoul2 == G2_MODELPART_HEAD || cent->currentState.modelGhoul2 == G2_MODELPART_WAIST))
|
|
{ //dismember settings are not high enough to display decaps and torso slashes
|
|
return;
|
|
}
|
|
|
|
if (!cent->ghoul2)
|
|
{
|
|
const char *limbBone;
|
|
const char *rotateBone;
|
|
char limbName[MAX_QPATH];
|
|
char stubName[MAX_QPATH];
|
|
char limbCapName[MAX_QPATH];
|
|
char stubCapName[MAX_QPATH];
|
|
char *limbTagName;
|
|
char *stubTagName;
|
|
int limb_anim;
|
|
int newBolt;
|
|
int limbBit = (1 << (cent->currentState.modelGhoul2-10));
|
|
|
|
if (clEnt && (clEnt->torsoBolt & limbBit))
|
|
{ //already have this limb missing!
|
|
return;
|
|
}
|
|
|
|
|
|
if (clEnt && !(clEnt->currentState.eFlags & EF_DEAD))
|
|
{ //death flag hasn't made it through yet for the limb owner, we cannot create the limb until he's flagged as dead
|
|
return;
|
|
}
|
|
|
|
if (clEnt && (!BG_InDeathAnim(clEnt->currentState.torsoAnim) || !BG_InDeathAnim(clEnt->pe.torso.animationNumber)))
|
|
{ //don't make it unless we're in an actual death anim already
|
|
if (clEnt->currentState.torsoAnim != BOTH_RIGHTHANDCHOPPEDOFF)
|
|
{ //exception
|
|
return;
|
|
}
|
|
}
|
|
|
|
cent->bolt4 = -1;
|
|
cent->trailTime = 0;
|
|
|
|
if (cent->currentState.modelGhoul2 == G2_MODELPART_HEAD)
|
|
{
|
|
limbBone = "cervical";
|
|
rotateBone = "cranium";
|
|
Q_strncpyz( limbName , "head", sizeof( limbName ) );
|
|
Q_strncpyz( limbCapName, "head_cap_torso", sizeof( limbCapName ) );
|
|
Q_strncpyz( stubCapName, "torso_cap_head", sizeof( stubCapName ) );
|
|
limbTagName = "*head_cap_torso";
|
|
stubTagName = "*torso_cap_head";
|
|
limb_anim = BOTH_DISMEMBER_HEAD1;
|
|
}
|
|
else if (cent->currentState.modelGhoul2 == G2_MODELPART_WAIST)
|
|
{
|
|
limbBone = "pelvis";
|
|
|
|
if (clEnt->localAnimIndex <= 1)
|
|
{ //humanoid/rtrooper
|
|
rotateBone = "thoracic";
|
|
}
|
|
else
|
|
{
|
|
rotateBone = "pelvis";
|
|
}
|
|
Q_strncpyz( limbName, "torso", sizeof( limbName ) );
|
|
Q_strncpyz( limbCapName, "torso_cap_hips", sizeof( limbCapName ) );
|
|
Q_strncpyz( stubCapName, "hips_cap_torso", sizeof( stubCapName ) );
|
|
limbTagName = "*torso_cap_hips";
|
|
stubTagName = "*hips_cap_torso";
|
|
limb_anim = BOTH_DISMEMBER_TORSO1;
|
|
}
|
|
else if (cent->currentState.modelGhoul2 == G2_MODELPART_LARM)
|
|
{
|
|
limbBone = "lhumerus";
|
|
rotateBone = "lradius";
|
|
BG_GetRootSurfNameWithVariant( clEnt->ghoul2, "l_arm", limbName, sizeof(limbName) );
|
|
BG_GetRootSurfNameWithVariant( clEnt->ghoul2, "torso", stubName, sizeof(stubName) );
|
|
Com_sprintf( limbCapName, sizeof( limbCapName ), "%s_cap_torso", limbName );
|
|
Com_sprintf( stubCapName, sizeof( stubCapName), "%s_cap_l_arm", stubName );
|
|
limbTagName = "*l_arm_cap_torso";
|
|
stubTagName = "*torso_cap_l_arm";
|
|
limb_anim = BOTH_DISMEMBER_LARM;
|
|
}
|
|
else if (cent->currentState.modelGhoul2 == G2_MODELPART_RARM)
|
|
{
|
|
limbBone = "rhumerus";
|
|
rotateBone = "rradius";
|
|
BG_GetRootSurfNameWithVariant( clEnt->ghoul2, "r_arm", limbName, sizeof(limbName) );
|
|
BG_GetRootSurfNameWithVariant( clEnt->ghoul2, "torso", stubName, sizeof(stubName) );
|
|
Com_sprintf( limbCapName, sizeof( limbCapName ), "%s_cap_torso", limbName );
|
|
Com_sprintf( stubCapName, sizeof( stubCapName), "%s_cap_r_arm", stubName );
|
|
limbTagName = "*r_arm_cap_torso";
|
|
stubTagName = "*torso_cap_r_arm";
|
|
limb_anim = BOTH_DISMEMBER_RARM;
|
|
}
|
|
else if (cent->currentState.modelGhoul2 == G2_MODELPART_RHAND)
|
|
{
|
|
limbBone = "rradiusX";
|
|
rotateBone = "rhand";
|
|
BG_GetRootSurfNameWithVariant( clEnt->ghoul2, "r_hand", limbName, sizeof(limbName) );
|
|
BG_GetRootSurfNameWithVariant( clEnt->ghoul2, "r_arm", stubName, sizeof(stubName) );
|
|
Com_sprintf( limbCapName, sizeof( limbCapName ), "%s_cap_r_arm", limbName );
|
|
Com_sprintf( stubCapName, sizeof( stubCapName), "%s_cap_r_hand", stubName );
|
|
limbTagName = "*r_hand_cap_r_arm";
|
|
stubTagName = "*r_arm_cap_r_hand";
|
|
limb_anim = BOTH_DISMEMBER_RARM;
|
|
}
|
|
else if (cent->currentState.modelGhoul2 == G2_MODELPART_LLEG)
|
|
{
|
|
limbBone = "lfemurYZ";
|
|
rotateBone = "ltibia";
|
|
BG_GetRootSurfNameWithVariant( clEnt->ghoul2, "l_leg", limbName, sizeof(limbName) );
|
|
BG_GetRootSurfNameWithVariant( clEnt->ghoul2, "hips", stubName, sizeof(stubName) );
|
|
Com_sprintf( limbCapName, sizeof( limbCapName ), "%s_cap_hips", limbName );
|
|
Com_sprintf( stubCapName, sizeof( stubCapName), "%s_cap_l_leg", stubName );
|
|
limbTagName = "*l_leg_cap_hips";
|
|
stubTagName = "*hips_cap_l_leg";
|
|
limb_anim = BOTH_DISMEMBER_LLEG;
|
|
}
|
|
else if (cent->currentState.modelGhoul2 == G2_MODELPART_RLEG)
|
|
{
|
|
limbBone = "rfemurYZ";
|
|
rotateBone = "rtibia";
|
|
BG_GetRootSurfNameWithVariant( clEnt->ghoul2, "r_leg", limbName, sizeof(limbName) );
|
|
BG_GetRootSurfNameWithVariant( clEnt->ghoul2, "hips", stubName, sizeof(stubName) );
|
|
Com_sprintf( limbCapName, sizeof( limbCapName ), "%s_cap_hips", limbName );
|
|
Com_sprintf( stubCapName, sizeof( stubCapName), "%s_cap_r_leg", stubName );
|
|
limbTagName = "*r_leg_cap_hips";
|
|
stubTagName = "*hips_cap_r_leg";
|
|
limb_anim = BOTH_DISMEMBER_RLEG;
|
|
}
|
|
else
|
|
{//umm... just default to the right leg, I guess (same as on server)
|
|
limbBone = "rfemurYZ";
|
|
rotateBone = "rtibia";
|
|
BG_GetRootSurfNameWithVariant( clEnt->ghoul2, "r_leg", limbName, sizeof(limbName) );
|
|
BG_GetRootSurfNameWithVariant( clEnt->ghoul2, "hips", stubName, sizeof(stubName) );
|
|
Com_sprintf( limbCapName, sizeof( limbCapName ), "%s_cap_hips", limbName );
|
|
Com_sprintf( stubCapName, sizeof( stubCapName), "%s_cap_r_leg", stubName );
|
|
limbTagName = "*r_leg_cap_hips";
|
|
stubTagName = "*hips_cap_r_leg";
|
|
limb_anim = BOTH_DISMEMBER_RLEG;
|
|
}
|
|
|
|
if (clEnt && clEnt->ghoul2)
|
|
{
|
|
if (trap_G2API_HasGhoul2ModelOnIndex(&(clEnt->ghoul2), 2))
|
|
{ //don't want to bother dealing with a second saber on limbs and stuff, just remove the thing
|
|
trap_G2API_RemoveGhoul2Model(&(clEnt->ghoul2), 2);
|
|
}
|
|
|
|
if (trap_G2API_HasGhoul2ModelOnIndex(&(clEnt->ghoul2), 3))
|
|
{ //turn off jetpack also I suppose
|
|
trap_G2API_RemoveGhoul2Model(&(clEnt->ghoul2), 3);
|
|
}
|
|
|
|
if (clEnt->localAnimIndex <= 0)
|
|
{ //humanoid
|
|
trap_G2API_SetBoneAngles(clEnt->ghoul2, 0, "model_root", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.gameModels, 100, cg.time);
|
|
trap_G2API_SetBoneAngles(clEnt->ghoul2, 0, "pelvis", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.gameModels, 0, cg.time);
|
|
trap_G2API_SetBoneAngles(clEnt->ghoul2, 0, "thoracic", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.gameModels, 0, cg.time);
|
|
trap_G2API_SetBoneAngles(clEnt->ghoul2, 0, "upper_lumbar", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.gameModels, 100, cg.time);
|
|
trap_G2API_SetBoneAngles(clEnt->ghoul2, 0, "lower_lumbar", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.gameModels, 100, cg.time);
|
|
trap_G2API_SetBoneAngles(clEnt->ghoul2, 0, "cranium", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_Y, POSITIVE_X, cgs.gameModels, 100, cg.time);
|
|
}
|
|
else
|
|
{
|
|
trap_G2API_SetBoneAngles(clEnt->ghoul2, 0, "model_root", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.gameModels, 100, cg.time);
|
|
trap_G2API_SetBoneAngles(clEnt->ghoul2, 0, "pelvis", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.gameModels, 0, cg.time);
|
|
trap_G2API_SetBoneAngles(clEnt->ghoul2, 0, "upper_lumbar", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.gameModels, 100, cg.time);
|
|
trap_G2API_SetBoneAngles(clEnt->ghoul2, 0, "lower_lumbar", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.gameModels, 100, cg.time);
|
|
}
|
|
|
|
trap_G2API_DuplicateGhoul2Instance(clEnt->ghoul2, ¢->ghoul2);
|
|
}
|
|
|
|
if (!cent->ghoul2)
|
|
{
|
|
return;
|
|
}
|
|
|
|
newBolt = trap_G2API_AddBolt( cent->ghoul2, 0, limbTagName );
|
|
if ( newBolt != -1 )
|
|
{
|
|
vec3_t boltOrg, boltAng;
|
|
|
|
trap_G2API_GetBoltMatrix(cent->ghoul2, 0, newBolt, &matrix, cent->lerpAngles, cent->lerpOrigin, cg.time, cgs.gameModels, cent->modelScale);
|
|
|
|
BG_GiveMeVectorFromMatrix(&matrix, ORIGIN, boltOrg);
|
|
BG_GiveMeVectorFromMatrix(&matrix, NEGATIVE_Y, boltAng);
|
|
|
|
trap_FX_PlayEffectID(cgs.effects.mBlasterSmoke, boltOrg, boltAng, -1, -1);
|
|
}
|
|
|
|
cent->bolt4 = newBolt;
|
|
|
|
trap_G2API_SetRootSurface(cent->ghoul2, 0, limbName);
|
|
|
|
trap_G2API_SetNewOrigin(cent->ghoul2, trap_G2API_AddBolt(cent->ghoul2, 0, rotateBone));
|
|
|
|
trap_G2API_SetSurfaceOnOff(cent->ghoul2, limbCapName, 0);
|
|
|
|
trap_G2API_SetSurfaceOnOff(clEnt->ghoul2, limbName, 0x00000100);
|
|
trap_G2API_SetSurfaceOnOff(clEnt->ghoul2, stubCapName, 0);
|
|
|
|
newBolt = trap_G2API_AddBolt( clEnt->ghoul2, 0, stubTagName );
|
|
if ( newBolt != -1 )
|
|
{
|
|
vec3_t boltOrg, boltAng;
|
|
|
|
trap_G2API_GetBoltMatrix(clEnt->ghoul2, 0, newBolt, &matrix, clEnt->lerpAngles, clEnt->lerpOrigin, cg.time, cgs.gameModels, clEnt->modelScale);
|
|
|
|
BG_GiveMeVectorFromMatrix(&matrix, ORIGIN, boltOrg);
|
|
BG_GiveMeVectorFromMatrix(&matrix, NEGATIVE_Y, boltAng);
|
|
|
|
trap_FX_PlayEffectID(cgs.effects.mBlasterSmoke, boltOrg, boltAng, -1, -1);
|
|
}
|
|
|
|
if (cent->currentState.modelGhoul2 == G2_MODELPART_RARM || cent->currentState.modelGhoul2 == G2_MODELPART_RHAND || cent->currentState.modelGhoul2 == G2_MODELPART_WAIST)
|
|
{ //Cut his weapon holding arm off, so remove the weapon
|
|
if (trap_G2API_HasGhoul2ModelOnIndex(&(clEnt->ghoul2), 1))
|
|
{
|
|
trap_G2API_RemoveGhoul2Model(&(clEnt->ghoul2), 1);
|
|
}
|
|
}
|
|
|
|
clEnt->torsoBolt |= limbBit; //reinit model after copying limbless one to queue
|
|
//This causes issues after respawning.. just keep track of limbs cut/made on server or something.
|
|
/*
|
|
if (cent->currentState.modelGhoul2 == G2_MODELPART_WAIST)
|
|
{
|
|
clEnt->torsoBolt |= (1 << (G2_MODELPART_HEAD-10));
|
|
clEnt->torsoBolt |= (1 << (G2_MODELPART_RARM-10));
|
|
clEnt->torsoBolt |= (1 << (G2_MODELPART_LARM-10));
|
|
clEnt->torsoBolt |= (1 << (G2_MODELPART_RHAND-10));
|
|
}
|
|
else if (cent->currentState.modelGhoul2 == G2_MODELPART_RARM)
|
|
{
|
|
clEnt->torsoBolt |= (1 << (G2_MODELPART_RHAND-10));
|
|
}
|
|
*/
|
|
|
|
VectorCopy(cent->lerpOrigin, cent->turAngles);
|
|
// return;
|
|
}
|
|
|
|
//Use origin smoothing since dismembered limbs use ExPhys
|
|
if (DistanceSquared(cent->turAngles,cent->lerpOrigin)>18000.0f)
|
|
{
|
|
VectorCopy(cent->lerpOrigin, cent->turAngles);
|
|
}
|
|
|
|
VectorSubtract(cent->lerpOrigin, cent->turAngles, posDif);
|
|
|
|
for (k=0;k<3;k++)
|
|
{
|
|
cent->turAngles[k]=(cent->turAngles[k]+posDif[k]*smoothFactor);
|
|
cent->lerpOrigin[k]=cent->turAngles[k];
|
|
}
|
|
|
|
if (cent->ghoul2 && cent->bolt4 != -1 && cent->trailTime < cg.time)
|
|
{
|
|
if ( cent->bolt4 != -1 &&
|
|
(cent->currentState.pos.trDelta[0] || cent->currentState.pos.trDelta[1] || cent->currentState.pos.trDelta[2]) )
|
|
{
|
|
vec3_t boltOrg, boltAng;
|
|
|
|
trap_G2API_GetBoltMatrix(cent->ghoul2, 0, cent->bolt4, &matrix, cent->lerpAngles, cent->lerpOrigin, cg.time, cgs.gameModels, cent->modelScale);
|
|
|
|
BG_GiveMeVectorFromMatrix(&matrix, ORIGIN, boltOrg);
|
|
BG_GiveMeVectorFromMatrix(&matrix, NEGATIVE_Y, boltAng);
|
|
|
|
if (!boltAng[0] && !boltAng[1] && !boltAng[2])
|
|
{
|
|
boltAng[1] = 1;
|
|
}
|
|
trap_FX_PlayEffectID(cgs.effects.mBlasterSmoke, boltOrg, boltAng, -1, -1);
|
|
|
|
cent->trailTime = cg.time + 400;
|
|
}
|
|
}
|
|
|
|
ent.radius = cent->currentState.g2radius;
|
|
ent.hModel = 0;
|
|
}
|
|
|
|
if (cent->currentState.number >= MAX_CLIENTS &&
|
|
cent->currentState.activeForcePass == NUM_FORCE_POWERS+1&&
|
|
cent->currentState.NPC_class != CLASS_VEHICLE )
|
|
{
|
|
vec3_t empAngles;
|
|
centity_t *empOwn;
|
|
|
|
empOwn = &cg_entities[cent->currentState.emplacedOwner];
|
|
|
|
if (cg.snap->ps.clientNum == empOwn->currentState.number &&
|
|
!cg.renderingThirdPerson)
|
|
{
|
|
VectorCopy(cg.refdef.viewangles, empAngles);
|
|
}
|
|
else
|
|
{
|
|
VectorCopy(empOwn->lerpAngles, empAngles);
|
|
}
|
|
|
|
if (empAngles[PITCH] > 40)
|
|
{
|
|
empAngles[PITCH] = 40;
|
|
}
|
|
empAngles[YAW] -= cent->currentState.angles[YAW];
|
|
|
|
trap_G2API_SetBoneAngles( cent->ghoul2, 0, "Bone02", empAngles, BONE_ANGLES_REPLACE, NEGATIVE_Y, NEGATIVE_X, POSITIVE_Z, NULL, 0, cg.time);
|
|
}
|
|
|
|
s1 = ¢->currentState;
|
|
|
|
// if set to invisible, skip
|
|
if ((!s1->modelindex) && !(trap_G2_HaveWeGhoul2Models(cent->ghoul2)))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( ( s1->eFlags & EF_NODRAW ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// set frame
|
|
if ( s1->eFlags & EF_SHADER_ANIM )
|
|
{
|
|
// Deliberately setting it up so that shader anim will completely override any kind of model animation frame setting.
|
|
ent.renderfx|=RF_SETANIMINDEX;
|
|
ent.skinNum = s1->frame;
|
|
}
|
|
else
|
|
{
|
|
ent.frame = s1->frame;
|
|
}
|
|
ent.oldframe = ent.frame;
|
|
ent.backlerp = 0;
|
|
|
|
/*
|
|
Ghoul2 Insert Start
|
|
*/
|
|
CG_SetGhoul2Info(&ent, cent);
|
|
|
|
/*
|
|
Ghoul2 Insert End
|
|
*/
|
|
VectorCopy( cent->lerpOrigin, ent.origin);
|
|
VectorCopy( cent->lerpOrigin, ent.oldorigin);
|
|
|
|
if (cent->currentState.modelGhoul2)
|
|
{ //If the game says this guy uses a ghoul2 model and the g2 instance handle is null, then initialize it
|
|
if (!cent->ghoul2 && !cent->currentState.bolt1)
|
|
{
|
|
char skinName[MAX_QPATH];
|
|
const char *modelName = CG_ConfigString( CS_MODELS+cent->currentState.modelindex );
|
|
int l;
|
|
int skin = 0;
|
|
|
|
trap_G2API_InitGhoul2Model(¢->ghoul2, modelName, 0, 0, 0, 0, 0);
|
|
if (cent->ghoul2 && trap_G2API_SkinlessModel(cent->ghoul2, 0))
|
|
{ //well, you'd never want a skinless model, so try to get his skin...
|
|
Q_strncpyz(skinName, modelName, MAX_QPATH);
|
|
l = strlen(skinName);
|
|
while (l > 0 && skinName[l] != '/')
|
|
{ //parse back to first /
|
|
l--;
|
|
}
|
|
if (skinName[l] == '/')
|
|
{ //got it
|
|
l++;
|
|
skinName[l] = 0;
|
|
Q_strcat(skinName, MAX_QPATH, "model_default.skin");
|
|
|
|
skin = trap_R_RegisterSkin(skinName);
|
|
}
|
|
trap_G2API_SetSkin(cent->ghoul2, 0, skin, skin);
|
|
}
|
|
}
|
|
else if (cent->currentState.bolt1)
|
|
{
|
|
TurretClientRun(cent);
|
|
}
|
|
|
|
if (cent->ghoul2)
|
|
{ //give us a proper radius
|
|
ent.radius = cent->currentState.g2radius;
|
|
}
|
|
}
|
|
|
|
if (s1->eType == ET_BODY)
|
|
{ //bodies should have a radius as well
|
|
ent.radius = cent->currentState.g2radius;
|
|
|
|
if (cent->ghoul2)
|
|
{ //all bodies should already have a ghoul2 instance. Use it to set the torso/head angles to 0.
|
|
cent->lerpAngles[PITCH] = 0;
|
|
cent->lerpAngles[ROLL] = 0;
|
|
trap_G2API_SetBoneAngles(cent->ghoul2, 0, "pelvis", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.gameModels, 0, cg.time);
|
|
trap_G2API_SetBoneAngles(cent->ghoul2, 0, "thoracic", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.gameModels, 0, cg.time);
|
|
trap_G2API_SetBoneAngles(cent->ghoul2, 0, "upper_lumbar", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.gameModels, 100, cg.time);
|
|
trap_G2API_SetBoneAngles(cent->ghoul2, 0, "lower_lumbar", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.gameModels, 100, cg.time);
|
|
trap_G2API_SetBoneAngles(cent->ghoul2, 0, "cranium", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_Y, POSITIVE_X, cgs.gameModels, 100, cg.time);
|
|
}
|
|
}
|
|
|
|
if (s1->eType == ET_HOLOCRON && s1->modelindex < -100)
|
|
{ //special render, it's a holocron
|
|
//Using actual models now:
|
|
ent.hModel = trap_R_RegisterModel(forceHolocronModels[s1->modelindex+128]);
|
|
|
|
//Rotate them
|
|
VectorCopy( cg.autoAngles, cent->lerpAngles );
|
|
AxisCopy( cg.autoAxis, ent.axis );
|
|
}
|
|
else if (!doNotSetModel)
|
|
{
|
|
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 );
|
|
|
|
if (cent->currentState.iModelScale)
|
|
{ //if the server says we have a custom scale then set it now.
|
|
if ( cent->currentState.legsFlip )
|
|
{//scalar
|
|
cent->modelScale[0] = cent->modelScale[1] = cent->modelScale[2] = cent->currentState.iModelScale;
|
|
}
|
|
else
|
|
{//percentage
|
|
cent->modelScale[0] = cent->modelScale[1] = cent->modelScale[2] = cent->currentState.iModelScale/100.0f;
|
|
}
|
|
VectorCopy(cent->modelScale, ent.modelScale);
|
|
ScaleModelAxis(&ent);
|
|
}
|
|
else
|
|
{
|
|
VectorClear(cent->modelScale);
|
|
}
|
|
|
|
if ( cent->currentState.time > cg.time && cent->currentState.weapon == WP_EMPLACED_GUN )
|
|
{
|
|
// make the gun pulse red to warn about it exploding
|
|
val = (1.0f - (float)(cent->currentState.time - cg.time) / 3200.0f ) * 0.3f;
|
|
|
|
ent.customShader = trap_R_RegisterShader( "gfx/effects/turretflashdie" );
|
|
ent.shaderRGBA[0] = (sin( cg.time * 0.04f ) * val * 0.4f + val) * 255;
|
|
ent.shaderRGBA[1] = ent.shaderRGBA[2] = 0;
|
|
|
|
ent.shaderRGBA[3] = 100;
|
|
trap_R_AddRefEntityToScene( &ent );
|
|
ent.customShader = 0;
|
|
}
|
|
else if ( cent->currentState.time == -1 && cent->currentState.weapon == WP_EMPLACED_GUN)
|
|
{
|
|
ent.customShader = trap_R_RegisterShader( "models/map_objects/imp_mine/turret_chair_dmg.tga" );
|
|
//trap_R_AddRefEntityToScene( &ent );
|
|
}
|
|
|
|
if ((cent->currentState.eFlags & EF_DISINTEGRATION) && cent->currentState.eType == ET_BODY)
|
|
{
|
|
if (!cent->dustTrailTime)
|
|
{
|
|
cent->dustTrailTime = cg.time;
|
|
}
|
|
|
|
CG_Disintegration(cent, &ent);
|
|
return;
|
|
}
|
|
else if (cent->currentState.eType == ET_BODY)
|
|
{
|
|
if (cent->bodyFadeTime > cg.time)
|
|
{
|
|
qboolean lightSide = cent->teamPowerType;
|
|
vec3_t hitLoc, tempAng;
|
|
float tempLength;
|
|
int curTimeDif = ((cg.time + 60000) - cent->bodyFadeTime);
|
|
int tMult = curTimeDif*0.08;
|
|
|
|
ent.renderfx |= RF_FORCE_ENT_ALPHA;
|
|
|
|
/*
|
|
if (!cent->bodyHeight)
|
|
{
|
|
cent->bodyHeight = ent.origin[2];
|
|
}
|
|
*/
|
|
|
|
if (curTimeDif*0.1 > 254)
|
|
{
|
|
ent.shaderRGBA[3] = 0;
|
|
}
|
|
else
|
|
{
|
|
ent.shaderRGBA[3] = (254 - tMult);
|
|
}
|
|
|
|
if (ent.shaderRGBA[3] >= 1)
|
|
{ //add the transparent body section
|
|
trap_R_AddRefEntityToScene (&ent);
|
|
}
|
|
|
|
ent.renderfx &= ~RF_FORCE_ENT_ALPHA;
|
|
ent.renderfx |= RF_RGB_TINT;
|
|
|
|
if (tMult > 200)
|
|
{ //begin the disintegration effect
|
|
ent.shaderRGBA[3] = 200;
|
|
if (!cent->dustTrailTime)
|
|
{
|
|
cent->dustTrailTime = cg.time;
|
|
if (lightSide)
|
|
{
|
|
trap_S_StartSound ( NULL, cent->currentState.number, CHAN_AUTO, trap_S_RegisterSound("sound/weapons/force/see.wav") );
|
|
}
|
|
else
|
|
{
|
|
trap_S_StartSound ( NULL, cent->currentState.number, CHAN_AUTO, trap_S_RegisterSound("sound/weapons/force/lightning") );
|
|
}
|
|
}
|
|
ent.endTime = cent->dustTrailTime;
|
|
ent.renderfx |= RF_DISINTEGRATE2;
|
|
}
|
|
else
|
|
{ //set the alpha on the to-be-disintegrated layer
|
|
ent.shaderRGBA[3] = tMult;
|
|
if (ent.shaderRGBA[3] < 1)
|
|
{
|
|
ent.shaderRGBA[3] = 1;
|
|
}
|
|
}
|
|
//Set everything up on the disint ref
|
|
ent.shaderRGBA[0] = ent.shaderRGBA[1] = ent.shaderRGBA[2] = ent.shaderRGBA[3];
|
|
VectorCopy(cent->lerpOrigin, hitLoc);
|
|
|
|
VectorSubtract( hitLoc, ent.origin, ent.oldorigin );
|
|
|
|
tempLength = VectorNormalize( ent.oldorigin );
|
|
vectoangles( ent.oldorigin, tempAng );
|
|
tempAng[YAW] -= cent->lerpAngles[YAW];
|
|
AngleVectors( tempAng, ent.oldorigin, NULL, NULL );
|
|
VectorScale( ent.oldorigin, tempLength, ent.oldorigin );
|
|
|
|
if (lightSide)
|
|
{ //might be temporary, dunno.
|
|
ent.customShader = cgs.media.playerShieldDamage;
|
|
}
|
|
else
|
|
{
|
|
ent.customShader = cgs.media.redSaberGlowShader;
|
|
}
|
|
|
|
//slowly move the glowing part upward, out of the fading body
|
|
/*
|
|
cent->bodyHeight += 0.4f;
|
|
ent.origin[2] = cent->bodyHeight;
|
|
*/
|
|
|
|
trap_R_AddRefEntityToScene( &ent );
|
|
ent.renderfx &= ~RF_DISINTEGRATE2;
|
|
ent.customShader = 0;
|
|
|
|
if (curTimeDif < 3400)
|
|
{
|
|
if (lightSide)
|
|
{
|
|
if (curTimeDif < 2200)
|
|
{ //probably temporary
|
|
trap_S_StartSound ( NULL, cent->currentState.number, CHAN_AUTO, trap_S_RegisterSound( "sound/weapons/saber/saberhum1.wav" ) );
|
|
}
|
|
}
|
|
else
|
|
{ //probably temporary as well
|
|
ent.renderfx |= RF_RGB_TINT;
|
|
ent.shaderRGBA[0] = 255;
|
|
ent.shaderRGBA[1] = ent.shaderRGBA[2] = 0;
|
|
ent.shaderRGBA[3] = 255;
|
|
if ( rand() & 1 )
|
|
{
|
|
ent.customShader = cgs.media.electricBodyShader;
|
|
}
|
|
else
|
|
{
|
|
ent.customShader = cgs.media.electricBody2Shader;
|
|
}
|
|
if ( random() > 0.9f )
|
|
{
|
|
trap_S_StartSound ( NULL, cent->currentState.number, CHAN_AUTO, cgs.media.crackleSound );
|
|
}
|
|
trap_R_AddRefEntityToScene( &ent );
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
cent->dustTrailTime = 0;
|
|
}
|
|
}
|
|
|
|
if (cent->currentState.modelGhoul2 &&
|
|
!ent.ghoul2 &&
|
|
!ent.hModel)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// add to refresh list
|
|
trap_R_AddRefEntityToScene (&ent);
|
|
|
|
if (cent->bolt3 == 999)
|
|
{ //this is an in-flight saber being rendered manually
|
|
vec3_t org;
|
|
float wv;
|
|
int i;
|
|
addspriteArgStruct_t fxSArgs;
|
|
//refEntity_t sRef;
|
|
//memcpy( &sRef, &ent, sizeof( sRef ) );
|
|
|
|
ent.customShader = cgs.media.solidWhite;
|
|
ent.renderfx = RF_RGB_TINT;
|
|
wv = sin( cg.time * 0.003f ) * 0.08f + 0.1f;
|
|
ent.shaderRGBA[0] = wv * 255;
|
|
ent.shaderRGBA[1] = wv * 255;
|
|
ent.shaderRGBA[2] = wv * 0;
|
|
trap_R_AddRefEntityToScene (&ent);
|
|
|
|
for ( i = -4; i < 10; i += 1 )
|
|
{
|
|
VectorMA( ent.origin, -i, ent.axis[2], org );
|
|
|
|
VectorCopy(org, fxSArgs.origin);
|
|
VectorClear(fxSArgs.vel);
|
|
VectorClear(fxSArgs.accel);
|
|
fxSArgs.scale = 5.5f;
|
|
fxSArgs.dscale = 5.5f;
|
|
fxSArgs.sAlpha = wv;
|
|
fxSArgs.eAlpha = wv;
|
|
fxSArgs.rotation = 0.0f;
|
|
fxSArgs.bounce = 0.0f;
|
|
fxSArgs.life = 1.0f;
|
|
fxSArgs.shader = cgs.media.yellowDroppedSaberShader;
|
|
fxSArgs.flags = 0x08000000;
|
|
|
|
//trap_FX_AddSprite( org, NULL, NULL, 5.5f, 5.5f, wv, wv, 0.0f, 0.0f, 1.0f, cgs.media.yellowSaberGlowShader, 0x08000000 );
|
|
trap_FX_AddSprite(&fxSArgs);
|
|
}
|
|
}
|
|
else if (cent->currentState.trickedentindex3)
|
|
{ //holocron special effects
|
|
vec3_t org;
|
|
float wv;
|
|
addspriteArgStruct_t fxSArgs;
|
|
//refEntity_t sRef;
|
|
//memcpy( &sRef, &ent, sizeof( sRef ) );
|
|
|
|
ent.customShader = cgs.media.solidWhite;
|
|
ent.renderfx = RF_RGB_TINT;
|
|
wv = sin( cg.time * 0.005f ) * 0.08f + 0.1f; //* 0.08f + 0.1f;
|
|
|
|
if (cent->currentState.trickedentindex3 == 1)
|
|
{ //dark
|
|
ent.shaderRGBA[0] = wv*255;
|
|
ent.shaderRGBA[1] = 0;
|
|
ent.shaderRGBA[2] = 0;
|
|
}
|
|
else if (cent->currentState.trickedentindex3 == 2)
|
|
{ //light
|
|
ent.shaderRGBA[0] = wv*255;
|
|
ent.shaderRGBA[1] = wv*255;
|
|
ent.shaderRGBA[2] = wv*255;
|
|
}
|
|
else
|
|
{ //neutral
|
|
if ((s1->modelindex+128) == FP_SABER_OFFENSE ||
|
|
(s1->modelindex+128) == FP_SABER_DEFENSE ||
|
|
(s1->modelindex+128) == FP_SABERTHROW)
|
|
{ //saber power
|
|
ent.shaderRGBA[0] = 0;
|
|
ent.shaderRGBA[1] = wv*255;
|
|
ent.shaderRGBA[2] = 0;
|
|
}
|
|
else
|
|
{
|
|
ent.shaderRGBA[0] = 0;
|
|
ent.shaderRGBA[1] = wv*255;
|
|
ent.shaderRGBA[2] = wv*255;
|
|
}
|
|
}
|
|
|
|
ent.modelScale[0] = 1.1;
|
|
ent.modelScale[1] = 1.1;
|
|
ent.modelScale[2] = 1.1;
|
|
|
|
ent.origin[2] -= 2;
|
|
ScaleModelAxis(&ent);
|
|
|
|
trap_R_AddRefEntityToScene (&ent);
|
|
|
|
VectorMA( ent.origin, 1, ent.axis[2], org );
|
|
|
|
org[2] += 18;
|
|
|
|
wv = sin( cg.time * 0.002f ) * 0.08f + 0.1f; //* 0.08f + 0.1f;
|
|
|
|
VectorCopy(org, fxSArgs.origin);
|
|
VectorClear(fxSArgs.vel);
|
|
VectorClear(fxSArgs.accel);
|
|
fxSArgs.scale = wv*120;//16.0f;
|
|
fxSArgs.dscale = wv*120;//16.0f;
|
|
fxSArgs.sAlpha = wv*12;
|
|
fxSArgs.eAlpha = wv*12;
|
|
fxSArgs.rotation = 0.0f;
|
|
fxSArgs.bounce = 0.0f;
|
|
fxSArgs.life = 1.0f;
|
|
|
|
fxSArgs.flags = 0x08000000|0x00000001;
|
|
|
|
if (cent->currentState.trickedentindex3 == 1)
|
|
{ //dark
|
|
fxSArgs.sAlpha *= 3;
|
|
fxSArgs.eAlpha *= 3;
|
|
fxSArgs.shader = cgs.media.redSaberGlowShader;
|
|
trap_FX_AddSprite(&fxSArgs);
|
|
}
|
|
else if (cent->currentState.trickedentindex3 == 2)
|
|
{ //light
|
|
fxSArgs.sAlpha *= 1.5;
|
|
fxSArgs.eAlpha *= 1.5;
|
|
fxSArgs.shader = cgs.media.redSaberGlowShader;
|
|
trap_FX_AddSprite(&fxSArgs);
|
|
fxSArgs.shader = cgs.media.greenSaberGlowShader;
|
|
trap_FX_AddSprite(&fxSArgs);
|
|
fxSArgs.shader = cgs.media.blueSaberGlowShader;
|
|
trap_FX_AddSprite(&fxSArgs);
|
|
}
|
|
else
|
|
{ //neutral
|
|
if ((s1->modelindex+128) == FP_SABER_OFFENSE ||
|
|
(s1->modelindex+128) == FP_SABER_DEFENSE ||
|
|
(s1->modelindex+128) == FP_SABERTHROW)
|
|
{ //saber power
|
|
fxSArgs.sAlpha *= 1.5;
|
|
fxSArgs.eAlpha *= 1.5;
|
|
fxSArgs.shader = cgs.media.greenSaberGlowShader;
|
|
trap_FX_AddSprite(&fxSArgs);
|
|
}
|
|
else
|
|
{
|
|
fxSArgs.sAlpha *= 0.5;
|
|
fxSArgs.eAlpha *= 0.5;
|
|
fxSArgs.shader = cgs.media.greenSaberGlowShader;
|
|
trap_FX_AddSprite(&fxSArgs);
|
|
fxSArgs.shader = cgs.media.blueSaberGlowShader;
|
|
trap_FX_AddSprite(&fxSArgs);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( cent->currentState.time == -1 && cent->currentState.weapon == WP_TRIP_MINE && (cent->currentState.eFlags & EF_FIRING) )
|
|
{ //if force sight is active, render the laser multiple times up to the force sight level to increase visibility
|
|
if (cent->currentState.bolt2 == 1)
|
|
{
|
|
VectorMA( ent.origin, 6.6f, ent.axis[0], beamOrg );// forward
|
|
beamID = cgs.effects.tripmineGlowFX;
|
|
trap_FX_PlayEffectID( beamID, beamOrg, cent->currentState.pos.trDelta, -1, -1 );
|
|
}
|
|
else
|
|
{
|
|
int i = 0;
|
|
|
|
VectorMA( ent.origin, 6.6f, ent.axis[0], beamOrg );// forward
|
|
beamID = cgs.effects.tripmineLaserFX;
|
|
|
|
if (cg.snap->ps.fd.forcePowersActive & (1 << FP_SEE))
|
|
{
|
|
i = cg.snap->ps.fd.forcePowerLevel[FP_SEE];
|
|
|
|
while (i > 0)
|
|
{
|
|
trap_FX_PlayEffectID( beamID, beamOrg, cent->currentState.pos.trDelta, -1, -1 );
|
|
trap_FX_PlayEffectID( beamID, beamOrg, cent->currentState.pos.trDelta, -1, -1 );
|
|
i--;
|
|
}
|
|
}
|
|
|
|
trap_FX_PlayEffectID( beamID, beamOrg, cent->currentState.pos.trDelta, -1, -1 );
|
|
}
|
|
}
|
|
/*
|
|
Ghoul2 Insert Start
|
|
*/
|
|
|
|
if (cg_debugBB.integer)
|
|
{
|
|
CG_CreateBBRefEnts(s1, cent->lerpOrigin);
|
|
}
|
|
/*
|
|
Ghoul2 Insert End
|
|
*/
|
|
}
|
|
|
|
/*
|
|
==================
|
|
CG_Speaker
|
|
|
|
Speaker entities can automatically play sounds
|
|
==================
|
|
*/
|
|
static void CG_Speaker( centity_t *cent ) {
|
|
if (cent->currentState.trickedentindex)
|
|
{
|
|
CG_S_StopLoopingSound(cent->currentState.number, -1);
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
qboolean CG_GreyItem(int type, int tag, int plSide)
|
|
{
|
|
if (type == IT_POWERUP &&
|
|
(tag == PW_FORCE_ENLIGHTENED_LIGHT || tag == PW_FORCE_ENLIGHTENED_DARK))
|
|
{
|
|
if (plSide == FORCE_LIGHTSIDE)
|
|
{
|
|
if (tag == PW_FORCE_ENLIGHTENED_DARK)
|
|
{
|
|
return qtrue;
|
|
}
|
|
}
|
|
else if (plSide == FORCE_DARKSIDE)
|
|
{
|
|
if (tag == PW_FORCE_ENLIGHTENED_LIGHT)
|
|
{
|
|
return qtrue;
|
|
}
|
|
}
|
|
}
|
|
|
|
return qfalse;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
CG_Item
|
|
==================
|
|
*/
|
|
static void CG_Item( centity_t *cent ) {
|
|
refEntity_t ent;
|
|
entityState_t *es;
|
|
gitem_t *item;
|
|
int msec;
|
|
float scale;
|
|
weaponInfo_t *wi;
|
|
|
|
es = ¢->currentState;
|
|
if ( es->modelindex >= bg_numItems ) {
|
|
CG_Error( "Bad item index %i on entity", es->modelindex );
|
|
}
|
|
|
|
/*
|
|
Ghoul2 Insert Start
|
|
*/
|
|
|
|
if ((es->eFlags & EF_NODRAW) && (es->eFlags & EF_ITEMPLACEHOLDER))
|
|
{
|
|
es->eFlags &= ~EF_NODRAW;
|
|
}
|
|
|
|
if ( !es->modelindex )
|
|
{
|
|
return;
|
|
}
|
|
|
|
item = &bg_itemlist[ es->modelindex ];
|
|
|
|
if ((item->giType == IT_WEAPON || item->giType == IT_POWERUP) &&
|
|
!(cent->currentState.eFlags & EF_DROPPEDWEAPON) &&
|
|
!cg_simpleItems.integer)
|
|
{
|
|
vec3_t uNorm;
|
|
qboolean doGrey;
|
|
|
|
VectorClear(uNorm);
|
|
|
|
uNorm[2] = 1;
|
|
|
|
memset( &ent, 0, sizeof( ent ) );
|
|
|
|
ent.customShader = 0;
|
|
VectorCopy(cent->lerpOrigin, ent.origin);
|
|
VectorCopy( cent->currentState.angles, cent->lerpAngles );
|
|
AnglesToAxis(cent->lerpAngles, ent.axis);
|
|
ent.hModel = cgs.media.itemHoloModel;
|
|
|
|
doGrey = CG_GreyItem(item->giType, item->giTag, cg.snap->ps.fd.forceSide);
|
|
|
|
if (doGrey)
|
|
{
|
|
ent.renderfx |= RF_RGB_TINT;
|
|
|
|
ent.shaderRGBA[0] = 150;
|
|
ent.shaderRGBA[1] = 150;
|
|
ent.shaderRGBA[2] = 150;
|
|
}
|
|
|
|
trap_R_AddRefEntityToScene(&ent);
|
|
|
|
if (!doGrey)
|
|
{
|
|
trap_FX_PlayEffectID(cgs.effects.itemCone, ent.origin, uNorm, -1, -1);
|
|
}
|
|
}
|
|
|
|
// if set to invisible, skip
|
|
if ( ( es->eFlags & EF_NODRAW ) )
|
|
{
|
|
return;
|
|
}
|
|
/*
|
|
Ghoul2 Insert End
|
|
*/
|
|
|
|
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.origin[2] += 16;
|
|
|
|
if (item->giType != IT_POWERUP || item->giTag != PW_FORCE_BOON)
|
|
{
|
|
ent.renderfx |= RF_FORCE_ENT_ALPHA;
|
|
}
|
|
|
|
if ( es->eFlags & EF_ITEMPLACEHOLDER )
|
|
{
|
|
if (item->giType == IT_POWERUP && item->giTag == PW_FORCE_BOON)
|
|
{
|
|
return;
|
|
}
|
|
ent.shaderRGBA[0] = 200;
|
|
ent.shaderRGBA[1] = 200;
|
|
ent.shaderRGBA[2] = 200;
|
|
ent.shaderRGBA[3] = 150 + sin(cg.time*0.01)*30;
|
|
}
|
|
else
|
|
{
|
|
ent.shaderRGBA[3] = 255;
|
|
}
|
|
|
|
if (CG_GreyItem(item->giType, item->giTag, cg.snap->ps.fd.forceSide))
|
|
{
|
|
ent.shaderRGBA[0] = 100;
|
|
ent.shaderRGBA[1] = 100;
|
|
ent.shaderRGBA[2] = 100;
|
|
|
|
ent.shaderRGBA[3] = 200;
|
|
|
|
if (item->giTag == PW_FORCE_ENLIGHTENED_LIGHT)
|
|
{
|
|
ent.customShader = trap_R_RegisterShader("gfx/misc/mp_light_enlight_disable");
|
|
}
|
|
else
|
|
{
|
|
ent.customShader = trap_R_RegisterShader("gfx/misc/mp_dark_enlight_disable");
|
|
}
|
|
}
|
|
trap_R_AddRefEntityToScene(&ent);
|
|
return;
|
|
}
|
|
|
|
if ((item->giType == IT_WEAPON || item->giType == IT_POWERUP) &&
|
|
!(cent->currentState.eFlags & EF_DROPPEDWEAPON))
|
|
{
|
|
cent->lerpOrigin[2] += 16;
|
|
}
|
|
|
|
if ((!(cent->currentState.eFlags & EF_DROPPEDWEAPON) || item->giType == IT_POWERUP) &&
|
|
(item->giType == IT_WEAPON || item->giType == IT_POWERUP))
|
|
{
|
|
// items bob up and down continuously
|
|
scale = 0.005 + cent->currentState.number * 0.00001;
|
|
cent->lerpOrigin[2] += 4 + cos( ( cg.time + 1000 ) * scale ) * 4;
|
|
}
|
|
else
|
|
{
|
|
if (item->giType == IT_HOLDABLE)
|
|
{
|
|
if (item->giTag == HI_SEEKER)
|
|
{
|
|
cent->lerpOrigin[2] += 5;
|
|
}
|
|
if (item->giTag == HI_SHIELD)
|
|
{
|
|
cent->lerpOrigin[2] += 2;
|
|
}
|
|
if (item->giTag == HI_BINOCULARS)
|
|
{
|
|
cent->lerpOrigin[2] += 2;
|
|
}
|
|
}
|
|
if (item->giType == IT_HEALTH)
|
|
{
|
|
cent->lerpOrigin[2] += 2;
|
|
}
|
|
if (item->giType == IT_ARMOR)
|
|
{
|
|
if (item->quantity == 100)
|
|
{
|
|
cent->lerpOrigin[2] += 7;
|
|
}
|
|
}
|
|
}
|
|
|
|
memset (&ent, 0, sizeof(ent));
|
|
|
|
if ( (!(cent->currentState.eFlags & EF_DROPPEDWEAPON) || item->giType == IT_POWERUP) &&
|
|
(item->giType == IT_WEAPON || item->giType == IT_POWERUP) )
|
|
{ //only weapons and powerups rotate now
|
|
// autorotate at one of two speeds
|
|
VectorCopy( cg.autoAngles, cent->lerpAngles );
|
|
AxisCopy( cg.autoAxis, ent.axis );
|
|
}
|
|
else
|
|
{
|
|
VectorCopy( cent->currentState.angles, cent->lerpAngles );
|
|
AnglesToAxis(cent->lerpAngles, 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 (!(cent->currentState.eFlags & EF_DROPPEDWEAPON))
|
|
{
|
|
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
|
|
}
|
|
}
|
|
else
|
|
{
|
|
wi = &cg_weapons[item->giTag];
|
|
|
|
switch(item->giTag)
|
|
{
|
|
case WP_BLASTER:
|
|
cent->lerpOrigin[2] -= 12;
|
|
break;
|
|
case WP_DISRUPTOR:
|
|
cent->lerpOrigin[2] -= 13;
|
|
break;
|
|
case WP_BOWCASTER:
|
|
cent->lerpOrigin[2] -= 16;
|
|
break;
|
|
case WP_REPEATER:
|
|
cent->lerpOrigin[2] -= 12;
|
|
break;
|
|
case WP_DEMP2:
|
|
cent->lerpOrigin[2] -= 10;
|
|
break;
|
|
case WP_FLECHETTE:
|
|
cent->lerpOrigin[2] -= 6;
|
|
break;
|
|
case WP_ROCKET_LAUNCHER:
|
|
cent->lerpOrigin[2] -= 11;
|
|
break;
|
|
case WP_THERMAL:
|
|
cent->lerpOrigin[2] -= 12;
|
|
break;
|
|
case WP_TRIP_MINE:
|
|
cent->lerpOrigin[2] -= 16;
|
|
break;
|
|
case WP_DET_PACK:
|
|
cent->lerpOrigin[2] -= 16;
|
|
break;
|
|
default:
|
|
cent->lerpOrigin[2] -= 8;
|
|
break;
|
|
}
|
|
}
|
|
|
|
ent.hModel = cg_items[es->modelindex].models[0];
|
|
/*
|
|
Ghoul2 Insert Start
|
|
*/
|
|
ent.ghoul2 = cg_items[es->modelindex].g2Models[0];
|
|
ent.radius = cg_items[es->modelindex].radius[0];
|
|
VectorCopy (cent->lerpAngles, ent.angles);
|
|
/*
|
|
Ghoul2 Insert End
|
|
*/
|
|
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 (CG_GreyItem(item->giType, item->giTag, cg.snap->ps.fd.forceSide))
|
|
{
|
|
ent.renderfx |= RF_RGB_TINT;
|
|
|
|
ent.shaderRGBA[0] = 150;
|
|
ent.shaderRGBA[1] = 150;
|
|
ent.shaderRGBA[2] = 150;
|
|
|
|
ent.renderfx |= RF_FORCE_ENT_ALPHA;
|
|
|
|
ent.shaderRGBA[3] = 200;
|
|
|
|
if (item->giTag == PW_FORCE_ENLIGHTENED_LIGHT)
|
|
{
|
|
ent.customShader = trap_R_RegisterShader("gfx/misc/mp_light_enlight_disable");
|
|
}
|
|
else
|
|
{
|
|
ent.customShader = trap_R_RegisterShader("gfx/misc/mp_dark_enlight_disable");
|
|
}
|
|
|
|
trap_R_AddRefEntityToScene( &ent );
|
|
return;
|
|
}
|
|
|
|
if ( es->eFlags & EF_ITEMPLACEHOLDER ) // item has been picked up
|
|
{
|
|
if ( es->eFlags & EF_DEAD ) // if item had been droped, don't show at all
|
|
return;
|
|
|
|
ent.renderfx |= RF_RGB_TINT;
|
|
ent.shaderRGBA[0] = 0;
|
|
ent.shaderRGBA[1] = 200;
|
|
ent.shaderRGBA[2] = 85;
|
|
ent.customShader = cgs.media.itemRespawningPlaceholder;
|
|
}
|
|
|
|
// 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;
|
|
//trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, cgs.media.weaponHoverSound );
|
|
}
|
|
|
|
if (!(cent->currentState.eFlags & EF_DROPPEDWEAPON) &&
|
|
(item->giType == IT_WEAPON || item->giType == IT_POWERUP))
|
|
{
|
|
ent.renderfx |= RF_MINLIGHT;
|
|
}
|
|
|
|
if (item->giType != IT_TEAM && msec >= 0 && msec < ITEM_SCALEUP_TIME && !(es->eFlags & EF_ITEMPLACEHOLDER) && !(es->eFlags & EF_DROPPEDWEAPON))
|
|
{ // if just respawned, fade in, but don't do this for flags.
|
|
float alpha;
|
|
int a;
|
|
|
|
alpha = (float)msec / ITEM_SCALEUP_TIME;
|
|
a = alpha * 255.0;
|
|
if (a <= 0)
|
|
a=1;
|
|
|
|
ent.shaderRGBA[3] = a;
|
|
if (item->giType != IT_POWERUP || item->giTag != PW_FORCE_BOON)
|
|
{ //boon model uses a different blending mode for the sprite inside and doesn't look proper with this method
|
|
ent.renderfx |= RF_FORCE_ENT_ALPHA;
|
|
}
|
|
trap_R_AddRefEntityToScene(&ent);
|
|
|
|
ent.renderfx &= ~RF_FORCE_ENT_ALPHA;
|
|
|
|
// Now draw the static shader over it.
|
|
// Alpha in over half the time, out over half.
|
|
|
|
//alpha = sin(M_PI*alpha);
|
|
a = alpha * 255.0;
|
|
|
|
a = 255 - a;
|
|
|
|
if (a <= 0)
|
|
a=1;
|
|
if (a > 255)
|
|
a=255;
|
|
|
|
ent.customShader = cgs.media.itemRespawningRezOut;
|
|
|
|
/*
|
|
ent.shaderRGBA[0] = 0;
|
|
ent.shaderRGBA[1] = a;
|
|
ent.shaderRGBA[2] = a-100;
|
|
|
|
if (ent.shaderRGBA[2] < 0)
|
|
{
|
|
ent.shaderRGBA[2] = 0;
|
|
}
|
|
*/
|
|
|
|
/*
|
|
ent.shaderRGBA[0] =
|
|
ent.shaderRGBA[1] =
|
|
ent.shaderRGBA[2] = a;
|
|
*/
|
|
|
|
ent.renderfx |= RF_RGB_TINT;
|
|
ent.shaderRGBA[0] = 0;
|
|
ent.shaderRGBA[1] = 200;
|
|
ent.shaderRGBA[2] = 85;
|
|
|
|
trap_R_AddRefEntityToScene( &ent );
|
|
}
|
|
else
|
|
{ // add to refresh list -- normal item
|
|
if (item->giType == IT_TEAM &&
|
|
(item->giTag == PW_REDFLAG || item->giTag == PW_BLUEFLAG))
|
|
{
|
|
ent.modelScale[0] = 0.7;
|
|
ent.modelScale[1] = 0.7;
|
|
ent.modelScale[2] = 0.7;
|
|
ScaleModelAxis(&ent);
|
|
}
|
|
trap_R_AddRefEntityToScene(&ent);
|
|
}
|
|
|
|
//rww - As far as I can see, this is useless.
|
|
/*
|
|
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;
|
|
|
|
barrel.customShader = ent.customShader;
|
|
|
|
CG_PositionRotatedEntityOnTag( &barrel, &ent, wi->weaponModel, "tag_barrel" );
|
|
|
|
AxisCopy( ent.axis, barrel.axis );
|
|
barrel.nonNormalizedAxes = ent.nonNormalizedAxes;
|
|
|
|
trap_R_AddRefEntityToScene( &barrel );
|
|
}
|
|
*/
|
|
|
|
// 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 );
|
|
|
|
trap_R_AddRefEntityToScene( &ent );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
|
|
void CG_CreateDistortionTrailPart(centity_t *cent, float scale, vec3_t pos)
|
|
{
|
|
refEntity_t ent;
|
|
vec3_t ang;
|
|
float vLen;
|
|
|
|
if (!cg_renderToTextureFX.integer)
|
|
{
|
|
return;
|
|
}
|
|
memset( &ent, 0, sizeof( ent ) );
|
|
|
|
VectorCopy( pos, ent.origin );
|
|
|
|
VectorSubtract(ent.origin, cg.refdef.vieworg, ent.axis[0]);
|
|
vLen = VectorLength(ent.axis[0]);
|
|
if (VectorNormalize(ent.axis[0]) <= 0.1f)
|
|
{ // Entity is right on vieworg. quit.
|
|
return;
|
|
}
|
|
|
|
VectorCopy(cent->lerpAngles, ang);
|
|
ang[PITCH] += 90.0f;
|
|
AnglesToAxis(ang, ent.axis);
|
|
|
|
//radius must be a power of 2, and is the actual captured texture size
|
|
if (vLen < 512)
|
|
{
|
|
ent.radius = 256;
|
|
}
|
|
else if (vLen < 1024)
|
|
{
|
|
ent.radius = 128;
|
|
}
|
|
else if (vLen < 2048)
|
|
{
|
|
ent.radius = 64;
|
|
}
|
|
else
|
|
{
|
|
ent.radius = 32;
|
|
}
|
|
|
|
ent.modelScale[0] = scale;
|
|
ent.modelScale[1] = scale;
|
|
ent.modelScale[2] = scale*16.0f;
|
|
ScaleModelAxis(&ent);
|
|
|
|
ent.hModel = trap_R_RegisterModel("models/weapons2/merr_sonn/trailmodel.md3");
|
|
ent.customShader = cgs.media.itemRespawningRezOut;//cgs.media.cloakedShader;//cgs.media.halfShieldShader;
|
|
|
|
#if 1
|
|
ent.renderfx = (RF_DISTORTION|RF_FORCE_ENT_ALPHA);
|
|
ent.shaderRGBA[0] = 255.0f;
|
|
ent.shaderRGBA[1] = 255.0f;
|
|
ent.shaderRGBA[2] = 255.0f;
|
|
ent.shaderRGBA[3] = 100.0f;
|
|
#else //no alpha
|
|
ent.renderfx = RF_DISTORTION;
|
|
#endif
|
|
|
|
trap_R_AddRefEntityToScene( &ent );
|
|
}
|
|
|
|
//distortion trail effect for rockets -rww
|
|
/*
|
|
static void CG_DistortionTrail( centity_t *cent )
|
|
{
|
|
vec3_t fwd;
|
|
vec3_t pos;
|
|
float overallScale = 4.0f;
|
|
|
|
VectorCopy(cent->currentState.pos.trDelta, fwd);
|
|
VectorNormalize(fwd);
|
|
|
|
VectorMA(cent->lerpOrigin, -8.0f*overallScale, fwd, pos);
|
|
CG_CreateDistortionTrailPart(cent, 0.5f*overallScale, pos);
|
|
VectorMA(cent->lerpOrigin, -12.0f*overallScale, fwd, pos);
|
|
CG_CreateDistortionTrailPart(cent, 0.6f*overallScale, pos);
|
|
VectorMA(cent->lerpOrigin, -16.0f*overallScale, fwd, pos);
|
|
CG_CreateDistortionTrailPart(cent, 0.7f*overallScale, pos);
|
|
VectorMA(cent->lerpOrigin, -20.0f*overallScale, fwd, pos);
|
|
CG_CreateDistortionTrailPart(cent, 0.8f*overallScale, pos);
|
|
VectorMA(cent->lerpOrigin, -30.0f*overallScale, fwd, pos);
|
|
CG_CreateDistortionTrailPart(cent, 0.9f*overallScale, pos);
|
|
VectorMA(cent->lerpOrigin, -40.0f*overallScale, fwd, pos);
|
|
CG_CreateDistortionTrailPart(cent, 1.0f*overallScale, pos);
|
|
}
|
|
*/
|
|
|
|
/*
|
|
===============
|
|
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 != G2_MODEL_PART ) {
|
|
s1->weapon = 0;
|
|
}
|
|
|
|
if (cent->ghoul2 && s1->weapon == G2_MODEL_PART)
|
|
{
|
|
weapon = &cg_weapons[WP_SABER];
|
|
}
|
|
else
|
|
{
|
|
weapon = &cg_weapons[s1->weapon];
|
|
}
|
|
|
|
if (cent->currentState.eFlags & EF_RADAROBJECT)
|
|
{
|
|
CG_AddRadarEnt(cent);
|
|
}
|
|
|
|
if (s1->weapon == WP_SABER)
|
|
{
|
|
if ((cent->currentState.modelindex != cent->serverSaberHitIndex || !cent->ghoul2) && !(s1->eFlags & EF_NODRAW))
|
|
{ //no g2, or server changed the model we are using
|
|
const char *saberModel = CG_ConfigString( CS_MODELS+cent->currentState.modelindex );
|
|
|
|
cent->serverSaberHitIndex = cent->currentState.modelindex;
|
|
|
|
if (cent->ghoul2)
|
|
{ //clean if we already have one (because server changed model string index)
|
|
trap_G2API_CleanGhoul2Models(&(cent->ghoul2));
|
|
cent->ghoul2 = 0;
|
|
}
|
|
|
|
if (saberModel && saberModel[0])
|
|
{
|
|
trap_G2API_InitGhoul2Model(¢->ghoul2, saberModel, 0, 0, 0, 0, 0);
|
|
}
|
|
else
|
|
{
|
|
trap_G2API_InitGhoul2Model(¢->ghoul2, "models/weapons2/saber/saber_w.glm", 0, 0, 0, 0, 0);
|
|
}
|
|
return;
|
|
}
|
|
else if (s1->eFlags & EF_NODRAW)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (cent->ghoul2)
|
|
{ //give us a proper radius
|
|
ent.radius = cent->currentState.g2radius;
|
|
}
|
|
|
|
// calculate the axis
|
|
VectorCopy( s1->angles, cent->lerpAngles);
|
|
|
|
if ( s1->otherEntityNum2 && s1->weapon != WP_SABER )
|
|
{//using an over-ridden trail effect!
|
|
vec3_t forward;
|
|
|
|
if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f )
|
|
{
|
|
forward[2] = 1.0f;
|
|
}
|
|
if ((s1->eFlags&EF_JETPACK_ACTIVE)//hack so we know we're a vehicle Weapon shot
|
|
&& (g_vehWeaponInfo[s1->otherEntityNum2].iShotFX
|
|
|| g_vehWeaponInfo[s1->otherEntityNum2].iModel != NULL_HANDLE) )
|
|
{ //a vehicle with an override for the weapon trail fx or model
|
|
trap_FX_PlayEffectID( g_vehWeaponInfo[s1->otherEntityNum2].iShotFX, cent->lerpOrigin, forward, -1, -1 );
|
|
if ( g_vehWeaponInfo[s1->otherEntityNum2].iLoopSound )
|
|
{
|
|
vec3_t velocity;
|
|
BG_EvaluateTrajectoryDelta( ¢->currentState.pos, cg.time, velocity );
|
|
trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, velocity, g_vehWeaponInfo[s1->otherEntityNum2].iLoopSound );
|
|
}
|
|
//add custom model
|
|
if ( g_vehWeaponInfo[s1->otherEntityNum2].iModel == NULL_HANDLE )
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{//a regular missile
|
|
trap_FX_PlayEffectID( cgs.gameEffects[s1->otherEntityNum2], cent->lerpOrigin, forward, -1, -1 );
|
|
if ( s1->loopSound )
|
|
{
|
|
vec3_t velocity;
|
|
BG_EvaluateTrajectoryDelta( ¢->currentState.pos, cg.time, velocity );
|
|
trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, velocity, s1->loopSound );
|
|
}
|
|
//FIXME: if has a custom model, too, then set it and do rest of code below?
|
|
return;
|
|
}
|
|
}
|
|
else if ( cent->currentState.eFlags & EF_ALT_FIRING )
|
|
{
|
|
// add trails
|
|
if ( weapon->altMissileTrailFunc )
|
|
{
|
|
weapon->altMissileTrailFunc( cent, weapon );
|
|
}
|
|
|
|
// add dynamic light
|
|
if ( weapon->altMissileDlight )
|
|
{
|
|
trap_R_AddLightToScene(cent->lerpOrigin, weapon->altMissileDlight,
|
|
weapon->altMissileDlightColor[0], weapon->altMissileDlightColor[1], weapon->altMissileDlightColor[2] );
|
|
}
|
|
|
|
// add missile sound
|
|
if ( weapon->altMissileSound ) {
|
|
vec3_t velocity;
|
|
|
|
BG_EvaluateTrajectoryDelta( ¢->currentState.pos, cg.time, velocity );
|
|
|
|
trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, velocity, weapon->altMissileSound );
|
|
}
|
|
|
|
//Don't draw something without a model
|
|
if ( weapon->altMissileModel == NULL_HANDLE )
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
// add trails
|
|
if ( weapon->missileTrailFunc )
|
|
{
|
|
weapon->missileTrailFunc( cent, weapon );
|
|
}
|
|
|
|
// 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 );
|
|
}
|
|
|
|
//Don't draw something without a model
|
|
if ( weapon->missileModel == NULL_HANDLE && s1->weapon != WP_SABER && s1->weapon != G2_MODEL_PART ) //saber uses ghoul2 model, doesn't matter
|
|
return;
|
|
}
|
|
|
|
// create the render entity
|
|
memset (&ent, 0, sizeof(ent));
|
|
VectorCopy( cent->lerpOrigin, ent.origin);
|
|
VectorCopy( cent->lerpOrigin, ent.oldorigin);
|
|
/*
|
|
Ghoul2 Insert Start
|
|
*/
|
|
CG_SetGhoul2Info(&ent, cent);
|
|
|
|
/*
|
|
Ghoul2 Insert End
|
|
*/
|
|
|
|
// flicker between two skins
|
|
ent.skinNum = cg.clientFrame & 1;
|
|
ent.renderfx = /*weapon->missileRenderfx | */RF_NOSHADOW;
|
|
|
|
if ( !(s1->eFlags&EF_JETPACK_ACTIVE) )
|
|
{
|
|
if (s1->weapon != WP_SABER && s1->weapon != G2_MODEL_PART)
|
|
{
|
|
//if ( cent->currentState.eFlags | EF_ALT_FIRING )
|
|
//rww - why was this like this?
|
|
if ( cent->currentState.eFlags & EF_ALT_FIRING )
|
|
{
|
|
ent.hModel = weapon->altMissileModel;
|
|
}
|
|
else
|
|
{
|
|
ent.hModel = weapon->missileModel;
|
|
}
|
|
}
|
|
}
|
|
//add custom model
|
|
else
|
|
{
|
|
if ( g_vehWeaponInfo[s1->otherEntityNum2].iModel != NULL_HANDLE )
|
|
{
|
|
ent.hModel = g_vehWeaponInfo[s1->otherEntityNum2].iModel;
|
|
}
|
|
else
|
|
{//wtf? how did we get here?
|
|
return;
|
|
}
|
|
}
|
|
|
|
// spin as it moves
|
|
if ( s1->apos.trType != TR_INTERPOLATE )
|
|
{
|
|
// 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 )
|
|
{
|
|
if ( s1->eFlags & EF_MISSILE_STICK )
|
|
{
|
|
RotateAroundDirection( ent.axis, cg.time * 0.5f );//Did this so regular missiles don't get broken
|
|
}
|
|
else
|
|
{
|
|
RotateAroundDirection( ent.axis, cg.time * 0.25f );//JFM:FLOAT FIX
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( s1->eFlags & EF_MISSILE_STICK )
|
|
{
|
|
RotateAroundDirection( ent.axis, (float)s1->pos.trTime * 0.5f );
|
|
}
|
|
else
|
|
{
|
|
RotateAroundDirection( ent.axis, (float)s1->time );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
AnglesToAxis( cent->lerpAngles, ent.axis );
|
|
}
|
|
|
|
if (s1->weapon == WP_SABER)
|
|
{
|
|
ent.radius = s1->g2radius;
|
|
}
|
|
|
|
// add to refresh list, possibly with quad glow
|
|
CG_AddRefEntityWithPowerups( &ent, s1, TEAM_FREE );
|
|
|
|
if (s1->weapon == WP_SABER && cgs.gametype == GT_JEDIMASTER)
|
|
{ //in jedimaster always make the saber glow when on the ground
|
|
vec3_t org;
|
|
float wv;
|
|
int i;
|
|
addspriteArgStruct_t fxSArgs;
|
|
//refEntity_t sRef;
|
|
//memcpy( &sRef, &ent, sizeof( sRef ) );
|
|
|
|
ent.customShader = cgs.media.solidWhite;
|
|
ent.renderfx = RF_RGB_TINT;
|
|
wv = sin( cg.time * 0.003f ) * 0.08f + 0.1f;
|
|
ent.shaderRGBA[0] = wv * 255;
|
|
ent.shaderRGBA[1] = wv * 255;
|
|
ent.shaderRGBA[2] = wv * 0;
|
|
trap_R_AddRefEntityToScene (&ent);
|
|
|
|
for ( i = -4; i < 10; i += 1 )
|
|
{
|
|
VectorMA( ent.origin, -i, ent.axis[2], org );
|
|
|
|
VectorCopy(org, fxSArgs.origin);
|
|
VectorClear(fxSArgs.vel);
|
|
VectorClear(fxSArgs.accel);
|
|
fxSArgs.scale = 5.5f;
|
|
fxSArgs.dscale = 5.5f;
|
|
fxSArgs.sAlpha = wv;
|
|
fxSArgs.eAlpha = wv;
|
|
fxSArgs.rotation = 0.0f;
|
|
fxSArgs.bounce = 0.0f;
|
|
fxSArgs.life = 1.0f;
|
|
fxSArgs.shader = cgs.media.yellowDroppedSaberShader;
|
|
fxSArgs.flags = 0x08000000;
|
|
|
|
//trap_FX_AddSprite( org, NULL, NULL, 5.5f, 5.5f, wv, wv, 0.0f, 0.0f, 1.0f, cgs.media.yellowSaberGlowShader, 0x08000000 );
|
|
trap_FX_AddSprite(&fxSArgs);
|
|
}
|
|
|
|
if (cgs.gametype == GT_JEDIMASTER)
|
|
{
|
|
ent.shaderRGBA[0] = 255;
|
|
ent.shaderRGBA[1] = 255;
|
|
ent.shaderRGBA[2] = 0;
|
|
|
|
ent.renderfx |= RF_DEPTHHACK;
|
|
ent.customShader = cgs.media.forceSightBubble;
|
|
|
|
trap_R_AddRefEntityToScene( &ent );
|
|
}
|
|
}
|
|
|
|
if ( s1->eFlags & EF_FIRING )
|
|
{//special code for adding the beam to the attached tripwire mine
|
|
vec3_t beamOrg;
|
|
|
|
VectorMA( ent.origin, 8, ent.axis[0], beamOrg );// forward
|
|
trap_FX_PlayEffectID( cgs.effects.mTripMineLaster, beamOrg, ent.axis[0], -1, -1 );
|
|
}
|
|
}
|
|
|
|
int CG_BMS_START = 0;
|
|
int CG_BMS_MID = 1;
|
|
int CG_BMS_END = 2;
|
|
|
|
/*
|
|
-------------------------
|
|
CG_PlayDoorLoopSound
|
|
-------------------------
|
|
*/
|
|
|
|
void CG_PlayDoorLoopSound( centity_t *cent )
|
|
{
|
|
sfxHandle_t sfx;
|
|
const char *soundSet;
|
|
vec3_t origin;
|
|
float *v;
|
|
|
|
if ( !cent->currentState.soundSetIndex )
|
|
{
|
|
return;
|
|
}
|
|
|
|
soundSet = CG_ConfigString( CS_AMBIENT_SET + cent->currentState.soundSetIndex );
|
|
|
|
if (!soundSet || !soundSet[0])
|
|
{
|
|
return;
|
|
}
|
|
|
|
sfx = trap_AS_GetBModelSound( soundSet, CG_BMS_MID );
|
|
|
|
if ( sfx == -1 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (cent->currentState.eType == ET_MOVER) //shouldn't be in here otherwise, but just in case.
|
|
{
|
|
v = cgs.inlineModelMidpoints[ cent->currentState.modelindex ];
|
|
VectorAdd( cent->lerpOrigin, v, origin );
|
|
}
|
|
else
|
|
{
|
|
VectorCopy(cent->lerpOrigin, origin);
|
|
}
|
|
|
|
//ent->s.loopSound = sfx;
|
|
CG_S_AddRealLoopingSound(cent->currentState.number, origin, vec3_origin, sfx);
|
|
}
|
|
|
|
/*
|
|
-------------------------
|
|
CG_PlayDoorSound
|
|
-------------------------
|
|
*/
|
|
|
|
void CG_PlayDoorSound( centity_t *cent, int type )
|
|
{
|
|
sfxHandle_t sfx;
|
|
const char *soundSet;
|
|
|
|
if ( !cent->currentState.soundSetIndex )
|
|
{
|
|
return;
|
|
}
|
|
|
|
soundSet = CG_ConfigString( CS_AMBIENT_SET + cent->currentState.soundSetIndex );
|
|
|
|
if (!soundSet || !soundSet[0])
|
|
{
|
|
return;
|
|
}
|
|
|
|
sfx = trap_AS_GetBModelSound( soundSet, type );
|
|
|
|
if ( sfx == -1 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
trap_S_StartSound( NULL, cent->currentState.number, CHAN_AUTO, sfx );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
CG_Mover
|
|
===============
|
|
*/
|
|
static void CG_Mover( centity_t *cent ) {
|
|
refEntity_t ent;
|
|
entityState_t *s1;
|
|
|
|
s1 = ¢->currentState;
|
|
|
|
// create the render entity
|
|
memset (&ent, 0, sizeof(ent));
|
|
|
|
if ( (cent->currentState.eFlags2&EF2_HYPERSPACE) )
|
|
{//I'm the hyperspace brush
|
|
qboolean drawMe = qfalse;
|
|
if ( cg.predictedPlayerState.m_iVehicleNum
|
|
&& cg.predictedVehicleState.hyperSpaceTime
|
|
&& (cg.time-cg.predictedVehicleState.hyperSpaceTime) < HYPERSPACE_TIME
|
|
&& (cg.time-cg.predictedVehicleState.hyperSpaceTime) > 1000 )
|
|
{
|
|
if ( cg.snap
|
|
&& cg.snap->ps.pm_type == PM_INTERMISSION )
|
|
{//in the intermission, stop drawing hyperspace ent
|
|
}
|
|
else if ( (cg.predictedVehicleState.eFlags2&EF2_HYPERSPACE) )
|
|
{//actually hyperspacing now
|
|
float timeFrac = ((float)(cg.time-cg.predictedVehicleState.hyperSpaceTime-1000))/(HYPERSPACE_TIME-1000);
|
|
if ( timeFrac < (HYPERSPACE_TELEPORT_FRAC+0.1f) )
|
|
{//still in hyperspace or just popped out
|
|
const float alpha = timeFrac<0.5f?timeFrac/0.5f:1.0f;
|
|
drawMe = qtrue;
|
|
VectorMA( cg.refdef.vieworg, 1000.0f+((1.0f-timeFrac)*1000.0f), cg.refdef.viewaxis[0], cent->lerpOrigin );
|
|
VectorSet( cent->lerpAngles, cg.refdef.viewangles[PITCH], cg.refdef.viewangles[YAW]-90.0f, 0 );//cos( ( cg.time + 1000 ) * scale ) * 4 );
|
|
ent.shaderRGBA[0] = ent.shaderRGBA[1] = ent.shaderRGBA[2] = 255;
|
|
ent.shaderRGBA[3] = alpha*255;
|
|
}
|
|
}
|
|
}
|
|
if ( !drawMe )
|
|
{//else, never draw
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (cent->currentState.eFlags & EF_RADAROBJECT)
|
|
{
|
|
CG_AddRadarEnt(cent);
|
|
}
|
|
|
|
VectorCopy( cent->lerpOrigin, ent.origin);
|
|
VectorCopy( cent->lerpOrigin, ent.oldorigin);
|
|
AnglesToAxis( cent->lerpAngles, ent.axis );
|
|
|
|
ent.renderfx = RF_NOSHADOW;
|
|
/*
|
|
Ghoul2 Insert Start
|
|
*/
|
|
|
|
CG_SetGhoul2Info(&ent, cent);
|
|
/*
|
|
Ghoul2 Insert End
|
|
*/
|
|
// flicker between two skins (FIXME?)
|
|
ent.skinNum = ( cg.time >> 6 ) & 1;
|
|
|
|
// get the model, either as a bmodel or a modelindex
|
|
if ( s1->solid == SOLID_BMODEL )
|
|
{
|
|
ent.hModel = cgs.inlineDrawModel[s1->modelindex];
|
|
}
|
|
else
|
|
{
|
|
ent.hModel = cgs.gameModels[s1->modelindex];
|
|
}
|
|
|
|
if ( s1->eFlags & EF_SHADER_ANIM )
|
|
{
|
|
ent.renderfx|=RF_SETANIMINDEX;
|
|
ent.skinNum = s1->frame;
|
|
//ent.shaderTime = cg.time*0.001f - s1->frame/s1->time;//NOTE: s1->time is number of frames
|
|
}
|
|
|
|
// add to refresh list
|
|
trap_R_AddRefEntityToScene(&ent);
|
|
|
|
// add the secondary model
|
|
if ( s1->modelindex2 )
|
|
{
|
|
ent.skinNum = 0;
|
|
ent.hModel = cgs.gameModels[s1->modelindex2];
|
|
if (s1->iModelScale)
|
|
{ //custom model2 scale
|
|
if ( s1->legsFlip )
|
|
{//scalar
|
|
ent.modelScale[0] = ent.modelScale[1] = ent.modelScale[2] = s1->iModelScale;
|
|
}
|
|
else
|
|
{//percentage
|
|
ent.modelScale[0] = ent.modelScale[1] = ent.modelScale[2] = s1->iModelScale/100.0f;
|
|
}
|
|
ScaleModelAxis(&ent);
|
|
}
|
|
trap_R_AddRefEntityToScene(&ent);
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
===============
|
|
CG_Beam
|
|
|
|
Also called as an event
|
|
===============
|
|
*/
|
|
void CG_Beam( centity_t *cent ) {
|
|
refEntity_t ent;
|
|
entityState_t *s1;
|
|
|
|
s1 = ¢->currentState;
|
|
|
|
// create the render entity
|
|
memset (&ent, 0, sizeof(ent));
|
|
VectorCopy( s1->pos.trBase, ent.origin );
|
|
VectorCopy( s1->origin2, ent.oldorigin );
|
|
AxisClear( ent.axis );
|
|
ent.reType = RT_BEAM;
|
|
|
|
ent.renderfx = RF_NOSHADOW;
|
|
/*
|
|
Ghoul2 Insert Start
|
|
*/
|
|
CG_SetGhoul2Info(&ent, cent);
|
|
|
|
/*
|
|
Ghoul2 Insert End
|
|
*/
|
|
// add to refresh list
|
|
trap_R_AddRefEntityToScene(&ent);
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
CG_Portal
|
|
===============
|
|
*/
|
|
static void CG_Portal( centity_t *cent ) {
|
|
refEntity_t ent;
|
|
entityState_t *s1;
|
|
|
|
s1 = ¢->currentState;
|
|
|
|
// create the render entity
|
|
memset (&ent, 0, sizeof(ent));
|
|
VectorCopy( cent->lerpOrigin, ent.origin );
|
|
VectorCopy( s1->origin2, ent.oldorigin );
|
|
ByteToDir( s1->eventParm, ent.axis[0] );
|
|
PerpendicularVector( ent.axis[1], ent.axis[0] );
|
|
|
|
// negating this tends to get the directions like they want
|
|
// we really should have a camera roll value
|
|
VectorSubtract( vec3_origin, ent.axis[1], ent.axis[1] );
|
|
|
|
CrossProduct( ent.axis[0], ent.axis[1], ent.axis[2] );
|
|
ent.reType = RT_PORTALSURFACE;
|
|
ent.oldframe = s1->powerups;
|
|
ent.frame = s1->frame; // rotation speed
|
|
ent.skinNum = s1->clientNum/256.0 * 360; // roll offset
|
|
/*
|
|
Ghoul2 Insert Start
|
|
*/
|
|
CG_SetGhoul2Info(&ent, cent);
|
|
/*
|
|
Ghoul2 Insert End
|
|
*/
|
|
// add to refresh list
|
|
trap_R_AddRefEntityToScene(&ent);
|
|
}
|
|
|
|
|
|
/*
|
|
=========================
|
|
CG_AdjustPositionForMover
|
|
|
|
Also called by client movement prediction code
|
|
=========================
|
|
*/
|
|
void CG_AdjustPositionForMover( const vec3_t in, int moverNum, int fromTime, int toTime, vec3_t out ) {
|
|
centity_t *cent;
|
|
vec3_t oldOrigin, origin, deltaOrigin;
|
|
vec3_t oldAngles, angles, deltaAngles;
|
|
|
|
if ( moverNum <= 0 || moverNum >= ENTITYNUM_MAX_NORMAL ) {
|
|
VectorCopy( in, out );
|
|
return;
|
|
}
|
|
|
|
cent = &cg_entities[ moverNum ];
|
|
if ( cent->currentState.eType != ET_MOVER ) {
|
|
VectorCopy( in, out );
|
|
return;
|
|
}
|
|
|
|
BG_EvaluateTrajectory( ¢->currentState.pos, fromTime, oldOrigin );
|
|
BG_EvaluateTrajectory( ¢->currentState.apos, fromTime, oldAngles );
|
|
|
|
BG_EvaluateTrajectory( ¢->currentState.pos, toTime, origin );
|
|
BG_EvaluateTrajectory( ¢->currentState.apos, toTime, angles );
|
|
|
|
VectorSubtract( origin, oldOrigin, deltaOrigin );
|
|
VectorSubtract( angles, oldAngles, deltaAngles );
|
|
|
|
VectorAdd( in, deltaOrigin, out );
|
|
|
|
// FIXME: origin change when on a rotating object
|
|
}
|
|
|
|
/*
|
|
=============================
|
|
CG_InterpolateEntityPosition
|
|
=============================
|
|
*/
|
|
static void CG_InterpolateEntityPosition( centity_t *cent ) {
|
|
vec3_t current, next;
|
|
float f;
|
|
|
|
// it would be an internal error to find an entity that interpolates without
|
|
// a snapshot ahead of the current one
|
|
if ( cg.nextSnap == NULL ) {
|
|
CG_Error( "CG_InterpoateEntityPosition: cg.nextSnap == NULL" );
|
|
}
|
|
|
|
f = cg.frameInterpolation;
|
|
|
|
// this will linearize a sine or parabolic curve, but it is important
|
|
// to not extrapolate player positions if more recent data is available
|
|
BG_EvaluateTrajectory( ¢->currentState.pos, cg.snap->serverTime, current );
|
|
BG_EvaluateTrajectory( ¢->nextState.pos, cg.nextSnap->serverTime, next );
|
|
|
|
cent->lerpOrigin[0] = current[0] + f * ( next[0] - current[0] );
|
|
cent->lerpOrigin[1] = current[1] + f * ( next[1] - current[1] );
|
|
cent->lerpOrigin[2] = current[2] + f * ( next[2] - current[2] );
|
|
|
|
BG_EvaluateTrajectory( ¢->currentState.apos, cg.snap->serverTime, current );
|
|
BG_EvaluateTrajectory( ¢->nextState.apos, cg.nextSnap->serverTime, next );
|
|
|
|
cent->lerpAngles[0] = LerpAngle( current[0], next[0], f );
|
|
cent->lerpAngles[1] = LerpAngle( current[1], next[1], f );
|
|
cent->lerpAngles[2] = LerpAngle( current[2], next[2], f );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
CG_CalcEntityLerpPositions
|
|
|
|
===============
|
|
*/
|
|
void CG_CalcEntityLerpPositions( centity_t *cent ) {
|
|
qboolean goAway = qfalse;
|
|
|
|
// if this player does not want to see extrapolated players
|
|
if ( !cg_smoothClients.integer ) {
|
|
// make sure the clients use TR_INTERPOLATE
|
|
if ( cent->currentState.number < MAX_CLIENTS ) {
|
|
cent->currentState.pos.trType = TR_INTERPOLATE;
|
|
cent->nextState.pos.trType = TR_INTERPOLATE;
|
|
}
|
|
}
|
|
|
|
if (cg.predictedPlayerState.m_iVehicleNum &&
|
|
cg.predictedPlayerState.m_iVehicleNum == cent->currentState.number &&
|
|
cent->currentState.eType == ET_NPC && cent->currentState.NPC_class == CLASS_VEHICLE)
|
|
{ //special case for vehicle we are riding
|
|
centity_t *veh = &cg_entities[cg.predictedPlayerState.m_iVehicleNum];
|
|
|
|
if (veh->currentState.owner == cg.predictedPlayerState.clientNum)
|
|
{ //only do this if the vehicle is pilotted by this client and predicting properly
|
|
BG_EvaluateTrajectory( ¢->currentState.pos, cg.time, cent->lerpOrigin );
|
|
BG_EvaluateTrajectory( ¢->currentState.apos, cg.time, cent->lerpAngles );
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ( cent->interpolate && cent->currentState.pos.trType == TR_INTERPOLATE ) {
|
|
CG_InterpolateEntityPosition( cent );
|
|
return;
|
|
}
|
|
|
|
// first see if we can interpolate between two snaps for
|
|
// linear extrapolated clients
|
|
if ( cent->interpolate
|
|
&& cent->currentState.pos.trType == TR_LINEAR_STOP
|
|
&& cent->currentState.number < MAX_CLIENTS )
|
|
{
|
|
CG_InterpolateEntityPosition( cent );
|
|
goAway = qtrue;
|
|
}
|
|
else if (cent->interpolate &&
|
|
cent->currentState.eType == ET_NPC && cent->currentState.NPC_class == CLASS_VEHICLE)
|
|
{
|
|
CG_InterpolateEntityPosition( cent );
|
|
goAway = qtrue;
|
|
}
|
|
else
|
|
{
|
|
// just use the current frame and evaluate as best we can
|
|
BG_EvaluateTrajectory( ¢->currentState.pos, cg.time, cent->lerpOrigin );
|
|
BG_EvaluateTrajectory( ¢->currentState.apos, cg.time, cent->lerpAngles );
|
|
}
|
|
|
|
#if 0
|
|
if (cent->hasRagOffset && cent->ragOffsetTime < cg.time)
|
|
{ //take all of the offsets from last frame and normalize the total direction and add it in
|
|
vec3_t slideDir;
|
|
vec3_t preOffset;
|
|
vec3_t addedOffset;
|
|
vec3_t playerMins = {-15, -15, DEFAULT_MINS_2};
|
|
vec3_t playerMaxs = {15, 15, DEFAULT_MAXS_2};
|
|
trace_t tr;
|
|
|
|
//VectorSubtract(cent->lerpOrigin, callData->bonePos, slideDir);
|
|
VectorCopy(cent->ragOffsets, slideDir);
|
|
VectorNormalize(slideDir);
|
|
|
|
//Store it in case we want to go back
|
|
VectorCopy(cent->lerpOriginOffset, preOffset);
|
|
|
|
//just add a little at a time
|
|
VectorMA(cent->lerpOriginOffset, 0.4f, slideDir, cent->lerpOriginOffset);
|
|
|
|
if (VectorLength(cent->lerpOriginOffset) > 10.0f)
|
|
{ //don't go too far away
|
|
VectorCopy(preOffset, cent->lerpOriginOffset);
|
|
}
|
|
else
|
|
{
|
|
//Let's trace there to make sure we can make it
|
|
VectorAdd(cent->lerpOrigin, cent->lerpOriginOffset, addedOffset);
|
|
CG_Trace(&tr, cent->lerpOrigin, playerMins, playerMaxs, addedOffset, cent->currentState.number, MASK_PLAYERSOLID);
|
|
|
|
if (tr.startsolid || tr.allsolid || tr.fraction != 1.0f)
|
|
{ //can't get there
|
|
VectorCopy(preOffset, cent->lerpOriginOffset);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
if (cent->lerpOriginOffset[2] > 4.0f)
|
|
{ //don't go too far off the ground
|
|
cent->lerpOriginOffset[2] = 4.0f;
|
|
}
|
|
*/
|
|
//I guess I just don't want this happening.
|
|
cent->lerpOriginOffset[2] = 0.0f;
|
|
}
|
|
}
|
|
|
|
//done with this bit
|
|
cent->hasRagOffset = qfalse;
|
|
VectorClear(cent->ragOffsets);
|
|
cent->ragOffsetTime = cg.time + 50;
|
|
}
|
|
|
|
//See if we should add in the offset for ragdoll
|
|
if (cent->isRagging && ((cent->currentState.eFlags & EF_DEAD) || (cent->currentState.eFlags & EF_RAG)))
|
|
{
|
|
VectorAdd(cent->lerpOrigin, cent->lerpOriginOffset, cent->lerpOrigin);
|
|
}
|
|
#endif
|
|
|
|
if (goAway)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// adjust for riding a mover if it wasn't rolled into the predicted
|
|
// player state
|
|
if ( cent->currentState.number != cg.predictedPlayerState.clientNum ) {
|
|
CG_AdjustPositionForMover( cent->lerpOrigin, cent->currentState.groundEntityNum,
|
|
cg.snap->serverTime, cg.time, cent->lerpOrigin );
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
CG_TeamBase
|
|
===============
|
|
*/
|
|
static void CG_TeamBase( centity_t *cent ) {
|
|
refEntity_t model;
|
|
if ( cgs.gametype == GT_CTF || cgs.gametype == GT_CTY ) {
|
|
// show the flag base
|
|
memset(&model, 0, sizeof(model));
|
|
model.reType = RT_MODEL;
|
|
VectorCopy( cent->lerpOrigin, model.lightingOrigin );
|
|
VectorCopy( cent->lerpOrigin, model.origin );
|
|
AnglesToAxis( cent->currentState.angles, model.axis );
|
|
if ( cent->currentState.modelindex == TEAM_RED ) {
|
|
model.hModel = cgs.media.redFlagBaseModel;
|
|
}
|
|
else if ( cent->currentState.modelindex == TEAM_BLUE ) {
|
|
model.hModel = cgs.media.blueFlagBaseModel;
|
|
}
|
|
else {
|
|
model.hModel = cgs.media.neutralFlagBaseModel;
|
|
}
|
|
|
|
if (cent->currentState.eType != ET_NPC)
|
|
{ //do not do this for g2animents
|
|
trap_R_AddRefEntityToScene( &model );
|
|
}
|
|
}
|
|
}
|
|
|
|
void CG_G2Animated( centity_t *cent );
|
|
|
|
static void CG_FX( centity_t *cent )
|
|
{
|
|
vec3_t fxDir;
|
|
int efxIndex = 0;
|
|
entityState_t *s1;
|
|
const char *s;
|
|
|
|
if (cent->miscTime > cg.time)
|
|
{
|
|
return;
|
|
}
|
|
|
|
s1 = ¢->currentState;
|
|
|
|
if (!s1)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (s1->modelindex2 == FX_STATE_OFF)
|
|
{ // fx not active
|
|
return;
|
|
}
|
|
|
|
if (s1->modelindex2 < FX_STATE_ONE_SHOT_LIMIT)
|
|
{ // fx is single shot
|
|
if (cent->muzzleFlashTime == s1->modelindex2)
|
|
{
|
|
return;
|
|
}
|
|
|
|
cent->muzzleFlashTime = s1->modelindex2;
|
|
}
|
|
|
|
cent->miscTime = cg.time + s1->speed + random() * s1->time;
|
|
|
|
AngleVectors(s1->angles, fxDir, 0, 0);
|
|
|
|
if (!fxDir[0] && !fxDir[1] && !fxDir[2])
|
|
{
|
|
fxDir[1] = 1;
|
|
}
|
|
|
|
if ( cgs.gameEffects[ s1->modelindex ] )
|
|
{
|
|
efxIndex = cgs.gameEffects[s1->modelindex];
|
|
}
|
|
else
|
|
{
|
|
s = CG_ConfigString( CS_EFFECTS + s1->modelindex );
|
|
if (s && s[0])
|
|
{
|
|
efxIndex = trap_FX_RegisterEffect(s);
|
|
cgs.gameEffects[s1->modelindex] = efxIndex;
|
|
}
|
|
}
|
|
|
|
if (efxIndex)
|
|
{
|
|
if (s1->isPortalEnt)
|
|
{
|
|
trap_FX_PlayPortalEffectID(efxIndex, cent->lerpOrigin, fxDir, -1, -1 );
|
|
}
|
|
else
|
|
{
|
|
trap_FX_PlayEffectID(efxIndex, cent->lerpOrigin, fxDir, -1, -1 );
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
CG_AddCEntity
|
|
|
|
===============
|
|
*/
|
|
static void CG_AddCEntity( centity_t *cent ) {
|
|
// event-only entities will have been dealt with already
|
|
if ( cent->currentState.eType >= ET_EVENTS ) {
|
|
return;
|
|
}
|
|
|
|
if (cg.predictedPlayerState.pm_type == PM_INTERMISSION)
|
|
{ //don't render anything then
|
|
if (cent->currentState.eType == ET_GENERAL ||
|
|
cent->currentState.eType == ET_PLAYER ||
|
|
cent->currentState.eType == ET_INVISIBLE)
|
|
{
|
|
return;
|
|
}
|
|
if ( cent->currentState.eType == ET_NPC )
|
|
{//NPC in intermission
|
|
if ( cent->currentState.NPC_class == CLASS_VEHICLE )
|
|
{//don't render vehicles in intermissions, allow other NPCs for scripts
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// calculate the current origin
|
|
CG_CalcEntityLerpPositions( cent );
|
|
|
|
// add automatic effects
|
|
CG_EntityEffects( cent );
|
|
/*
|
|
Ghoul2 Insert Start
|
|
*/
|
|
|
|
// add local sound set if any
|
|
if ( cent->currentState.soundSetIndex && cent->currentState.eType != ET_MOVER )
|
|
{
|
|
const char *soundSet = CG_ConfigString( CS_AMBIENT_SET + cent->currentState.soundSetIndex );
|
|
|
|
if (soundSet && soundSet[0])
|
|
{
|
|
trap_S_AddLocalSet(soundSet, cg.refdef.vieworg, cent->lerpOrigin, cent->currentState.number, cg.time);
|
|
}
|
|
}
|
|
/*
|
|
Ghoul2 Insert End
|
|
*/
|
|
switch ( cent->currentState.eType ) {
|
|
default:
|
|
CG_Error( "Bad entity type: %i\n", cent->currentState.eType );
|
|
break;
|
|
|
|
case ET_FX:
|
|
CG_FX( cent );
|
|
break;
|
|
|
|
case ET_INVISIBLE:
|
|
case ET_PUSH_TRIGGER:
|
|
case ET_TELEPORT_TRIGGER:
|
|
case ET_TERRAIN:
|
|
break;
|
|
case ET_GENERAL:
|
|
CG_General( cent );
|
|
break;
|
|
case ET_PLAYER:
|
|
CG_Player( cent );
|
|
break;
|
|
case ET_ITEM:
|
|
CG_Item( cent );
|
|
break;
|
|
case ET_MISSILE:
|
|
CG_Missile( cent );
|
|
break;
|
|
case ET_SPECIAL:
|
|
CG_Special( cent );
|
|
break;
|
|
case ET_HOLOCRON:
|
|
CG_General( cent );
|
|
break;
|
|
case ET_MOVER:
|
|
CG_Mover( cent );
|
|
break;
|
|
case ET_BEAM:
|
|
CG_Beam( cent );
|
|
break;
|
|
case ET_PORTAL:
|
|
CG_Portal( cent );
|
|
break;
|
|
case ET_SPEAKER:
|
|
CG_Speaker( cent );
|
|
break;
|
|
case ET_NPC: //An entity that wants to be able to use ghoul2 humanoid (and other) anims. Like a player, but not.
|
|
CG_G2Animated( cent );
|
|
break;
|
|
case ET_TEAM:
|
|
CG_TeamBase( cent );
|
|
break;
|
|
case ET_BODY:
|
|
CG_General( cent );
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CG_ManualEntityRender(centity_t *cent)
|
|
{
|
|
CG_AddCEntity(cent);
|
|
}
|
|
|
|
/*
|
|
===============
|
|
CG_AddPacketEntities
|
|
|
|
===============
|
|
*/
|
|
void CG_AddPacketEntities( qboolean isPortal ) {
|
|
int num;
|
|
centity_t *cent;
|
|
playerState_t *ps;
|
|
|
|
if (isPortal)
|
|
{
|
|
for ( num = 0 ; num < cg.snap->numEntities ; num++ )
|
|
{
|
|
cent = &cg_entities[ cg.snap->entities[ num ].number ];
|
|
|
|
if (cent->currentState.isPortalEnt)
|
|
{
|
|
CG_AddCEntity( cent );
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
// set cg.frameInterpolation
|
|
if ( cg.nextSnap ) {
|
|
int delta;
|
|
|
|
delta = (cg.nextSnap->serverTime - cg.snap->serverTime);
|
|
if ( delta == 0 ) {
|
|
cg.frameInterpolation = 0;
|
|
} else {
|
|
cg.frameInterpolation = (float)( cg.time - cg.snap->serverTime ) / delta;
|
|
}
|
|
} else {
|
|
cg.frameInterpolation = 0; // actually, it should never be used, because
|
|
// no entities should be marked as interpolating
|
|
}
|
|
|
|
// the auto-rotating items will all have the same axis
|
|
cg.autoAngles[0] = 0;
|
|
cg.autoAngles[1] = ( cg.time & 2047 ) * 360 / 2048.0;
|
|
cg.autoAngles[2] = 0;
|
|
|
|
cg.autoAnglesFast[0] = 0;
|
|
cg.autoAnglesFast[1] = ( cg.time & 1023 ) * 360 / 1024.0f;
|
|
cg.autoAnglesFast[2] = 0;
|
|
|
|
AnglesToAxis( cg.autoAngles, cg.autoAxis );
|
|
AnglesToAxis( cg.autoAnglesFast, cg.autoAxisFast );
|
|
|
|
// Reset radar entities
|
|
cg.radarEntityCount = 0;
|
|
cg.bracketedEntityCount = 0;
|
|
|
|
// generate and add the entity from the playerstate
|
|
ps = &cg.predictedPlayerState;
|
|
|
|
CG_CheckPlayerG2Weapons(ps, &cg_entities[cg.predictedPlayerState.clientNum]);
|
|
BG_PlayerStateToEntityState( ps, &cg_entities[cg.predictedPlayerState.clientNum].currentState, qfalse );
|
|
|
|
if (cg.predictedPlayerState.m_iVehicleNum)
|
|
{ //add the vehicle I'm riding first
|
|
//BG_PlayerStateToEntityState( &cg.predictedVehicleState, &cg_entities[cg.predictedPlayerState.m_iVehicleNum].currentState, qfalse );
|
|
//cg_entities[cg.predictedPlayerState.m_iVehicleNum].currentState.eType = ET_NPC;
|
|
centity_t *veh = &cg_entities[cg.predictedPlayerState.m_iVehicleNum];
|
|
|
|
if (veh->currentState.owner == cg.predictedPlayerState.clientNum)
|
|
{
|
|
BG_PlayerStateToEntityState( &cg.predictedVehicleState, &veh->currentState, qfalse );
|
|
veh->currentState.eType = ET_NPC;
|
|
|
|
veh->currentState.pos.trType = TR_INTERPOLATE;
|
|
}
|
|
CG_AddCEntity(veh);
|
|
veh->bodyHeight = cg.time; //indicate we have already been added
|
|
}
|
|
|
|
CG_AddCEntity( &cg_entities[cg.predictedPlayerState.clientNum] );
|
|
|
|
/*
|
|
// lerp the non-predicted value for lightning gun origins
|
|
CG_CalcEntityLerpPositions( &cg_entities[ cg.snap->ps.clientNum ] );
|
|
*/
|
|
//No longer have to do this.
|
|
|
|
// add each entity sent over by the server
|
|
for ( num = 0 ; num < cg.snap->numEntities ; num++ ) {
|
|
// Don't re-add ents that have been predicted.
|
|
if (cg.snap->entities[ num ].number != cg.snap->ps.clientNum)
|
|
{
|
|
cent = &cg_entities[ cg.snap->entities[ num ].number ];
|
|
if (cent->currentState.eType == ET_PLAYER &&
|
|
cent->currentState.m_iVehicleNum)
|
|
{ //add his veh first
|
|
int j = 0;
|
|
|
|
while (j < cg.snap->numEntities)
|
|
{
|
|
if (cg.snap->entities[j].number == cent->currentState.m_iVehicleNum)
|
|
{
|
|
centity_t *veh = &cg_entities[cg.snap->entities[j].number];
|
|
|
|
CG_AddCEntity(veh);
|
|
veh->bodyHeight = cg.time; //indicate we have already been added
|
|
break;
|
|
}
|
|
|
|
j++;
|
|
}
|
|
}
|
|
else if (cent->currentState.eType == ET_NPC &&
|
|
cent->currentState.m_iVehicleNum &&
|
|
cent->bodyHeight == cg.time)
|
|
{ //never add a vehicle with a pilot, his pilot entity will get him added first.
|
|
//if we were to add the vehicle after the pilot, the pilot's bolt would lag a frame behind.
|
|
continue;
|
|
}
|
|
CG_AddCEntity( cent );
|
|
}
|
|
}
|
|
|
|
for(num=0;num<cg_numpermanents;num++)
|
|
{
|
|
cent = cg_permanents[num];
|
|
if (cent->currentValid)
|
|
{
|
|
CG_AddCEntity( cent );
|
|
}
|
|
}
|
|
}
|
|
|
|
void CG_ROFF_NotetrackCallback( centity_t *cent, const char *notetrack)
|
|
{
|
|
int i = 0, r = 0, objectID = 0, anglesGathered = 0, posoffsetGathered = 0;
|
|
char type[256];
|
|
char argument[512];
|
|
char addlArg[512];
|
|
char errMsg[256];
|
|
char t[64];
|
|
int addlArgs = 0;
|
|
vec3_t parsedAngles, parsedOffset, useAngles, useOrigin, forward, right, up;
|
|
|
|
if (!cent || !notetrack)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//notetrack = "effect effects/explosion1.efx 0+0+64 0-0-1";
|
|
|
|
while (notetrack[i] && notetrack[i] != ' ')
|
|
{
|
|
type[i] = notetrack[i];
|
|
i++;
|
|
}
|
|
|
|
type[i] = '\0';
|
|
|
|
if (notetrack[i] != ' ')
|
|
{ //didn't pass in a valid notetrack type, or forgot the argument for it
|
|
return;
|
|
}
|
|
|
|
i++;
|
|
|
|
while (notetrack[i] && notetrack[i] != ' ')
|
|
{
|
|
argument[r] = notetrack[i];
|
|
r++;
|
|
i++;
|
|
}
|
|
argument[r] = '\0';
|
|
|
|
if (!r)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (notetrack[i] == ' ')
|
|
{ //additional arguments...
|
|
addlArgs = 1;
|
|
|
|
i++;
|
|
r = 0;
|
|
while (notetrack[i])
|
|
{
|
|
addlArg[r] = notetrack[i];
|
|
r++;
|
|
i++;
|
|
}
|
|
addlArg[r] = '\0';
|
|
}
|
|
|
|
if (strcmp(type, "effect") == 0)
|
|
{
|
|
if (!addlArgs)
|
|
{
|
|
//sprintf(errMsg, "Offset position argument for 'effect' type is invalid.");
|
|
//goto functionend;
|
|
VectorClear(parsedOffset);
|
|
goto defaultoffsetposition;
|
|
}
|
|
|
|
i = 0;
|
|
|
|
while (posoffsetGathered < 3)
|
|
{
|
|
r = 0;
|
|
while (addlArg[i] && addlArg[i] != '+' && addlArg[i] != ' ')
|
|
{
|
|
t[r] = addlArg[i];
|
|
r++;
|
|
i++;
|
|
}
|
|
t[r] = '\0';
|
|
i++;
|
|
if (!r)
|
|
{ //failure..
|
|
//sprintf(errMsg, "Offset position argument for 'effect' type is invalid.");
|
|
//goto functionend;
|
|
VectorClear(parsedOffset);
|
|
i = 0;
|
|
goto defaultoffsetposition;
|
|
}
|
|
parsedOffset[posoffsetGathered] = atof(t);
|
|
posoffsetGathered++;
|
|
}
|
|
|
|
if (posoffsetGathered < 3)
|
|
{
|
|
Com_sprintf(errMsg, sizeof(errMsg), "Offset position argument for 'effect' type is invalid.");
|
|
goto functionend;
|
|
}
|
|
|
|
i--;
|
|
|
|
if (addlArg[i] != ' ')
|
|
{
|
|
addlArgs = 0;
|
|
}
|
|
|
|
defaultoffsetposition:
|
|
|
|
objectID = trap_FX_RegisterEffect(argument);
|
|
|
|
if (objectID)
|
|
{
|
|
if (addlArgs)
|
|
{ //if there is an additional argument for an effect it is expected to be XANGLE-YANGLE-ZANGLE
|
|
i++;
|
|
while (anglesGathered < 3)
|
|
{
|
|
r = 0;
|
|
while (addlArg[i] && addlArg[i] != '-')
|
|
{
|
|
t[r] = addlArg[i];
|
|
r++;
|
|
i++;
|
|
}
|
|
t[r] = '\0';
|
|
i++;
|
|
|
|
if (!r)
|
|
{ //failed to get a new part of the vector
|
|
anglesGathered = 0;
|
|
break;
|
|
}
|
|
|
|
parsedAngles[anglesGathered] = atof(t);
|
|
anglesGathered++;
|
|
}
|
|
|
|
if (anglesGathered)
|
|
{
|
|
VectorCopy(parsedAngles, useAngles);
|
|
}
|
|
else
|
|
{ //failed to parse angles from the extra argument provided..
|
|
VectorCopy(cent->lerpAngles, useAngles);
|
|
}
|
|
}
|
|
else
|
|
{ //if no constant angles, play in direction entity is facing
|
|
VectorCopy(cent->lerpAngles, useAngles);
|
|
}
|
|
|
|
AngleVectors(useAngles, forward, right, up);
|
|
|
|
VectorCopy(cent->lerpOrigin, useOrigin);
|
|
|
|
//forward
|
|
useOrigin[0] += forward[0]*parsedOffset[0];
|
|
useOrigin[1] += forward[1]*parsedOffset[0];
|
|
useOrigin[2] += forward[2]*parsedOffset[0];
|
|
|
|
//right
|
|
useOrigin[0] += right[0]*parsedOffset[1];
|
|
useOrigin[1] += right[1]*parsedOffset[1];
|
|
useOrigin[2] += right[2]*parsedOffset[1];
|
|
|
|
//up
|
|
useOrigin[0] += up[0]*parsedOffset[2];
|
|
useOrigin[1] += up[1]*parsedOffset[2];
|
|
useOrigin[2] += up[2]*parsedOffset[2];
|
|
|
|
trap_FX_PlayEffectID(objectID, useOrigin, useAngles, -1, -1);
|
|
}
|
|
}
|
|
else if (strcmp(type, "sound") == 0)
|
|
{
|
|
objectID = trap_S_RegisterSound(argument);
|
|
trap_S_StartSound(cent->lerpOrigin, cent->currentState.number, CHAN_BODY, objectID);
|
|
}
|
|
else if (strcmp(type, "loop") == 0)
|
|
{ //handled server-side
|
|
return;
|
|
}
|
|
//else if ...
|
|
else
|
|
{
|
|
if (type[0])
|
|
{
|
|
Com_Printf("^3Warning: \"%s\" is an invalid ROFF notetrack function\n", type);
|
|
}
|
|
else
|
|
{
|
|
Com_Printf("^3Warning: Notetrack is missing function and/or arguments\n");
|
|
}
|
|
}
|
|
|
|
return;
|
|
|
|
functionend:
|
|
Com_Printf("^3Type-specific notetrack error: %s\n", errMsg);
|
|
return;
|
|
}
|
|
|
|
void CG_Cube( vec3_t mins, vec3_t maxs, vec3_t color, float alpha )
|
|
{
|
|
vec3_t rot={0,0,0};
|
|
int vec[3];
|
|
int axis, i;
|
|
addpolyArgStruct_t apArgs;
|
|
|
|
memset (&apArgs, 0, sizeof(apArgs));
|
|
|
|
for ( axis = 0, vec[0] = 0, vec[1] = 1, vec[2] = 2; axis < 3; axis++, vec[0]++, vec[1]++, vec[2]++ )
|
|
{
|
|
for ( i = 0; i < 3; i++ )
|
|
{
|
|
if ( vec[i] > 2 )
|
|
{
|
|
vec[i] = 0;
|
|
}
|
|
}
|
|
|
|
apArgs.p[0][vec[1]] = mins[vec[1]];
|
|
apArgs.p[0][vec[2]] = mins[vec[2]];
|
|
|
|
apArgs.p[1][vec[1]] = mins[vec[1]];
|
|
apArgs.p[1][vec[2]] = maxs[vec[2]];
|
|
|
|
apArgs.p[2][vec[1]] = maxs[vec[1]];
|
|
apArgs.p[2][vec[2]] = maxs[vec[2]];
|
|
|
|
apArgs.p[3][vec[1]] = maxs[vec[1]];
|
|
apArgs.p[3][vec[2]] = mins[vec[2]];
|
|
|
|
//- face
|
|
apArgs.p[0][vec[0]] = apArgs.p[1][vec[0]] = apArgs.p[2][vec[0]] = apArgs.p[3][vec[0]] = mins[vec[0]];
|
|
|
|
apArgs.numVerts = 4;
|
|
apArgs.alpha1 = apArgs.alpha2 = alpha;
|
|
VectorCopy( color, apArgs.rgb1 );
|
|
VectorCopy( color, apArgs.rgb2 );
|
|
VectorCopy( rot, apArgs.rotationDelta );
|
|
apArgs.killTime = cg.frametime;
|
|
apArgs.shader = cgs.media.solidWhite;
|
|
|
|
trap_FX_AddPoly( &apArgs );
|
|
|
|
//+ face
|
|
apArgs.p[0][vec[0]] = apArgs.p[1][vec[0]] = apArgs.p[2][vec[0]] = apArgs.p[3][vec[0]] = maxs[vec[0]];
|
|
|
|
trap_FX_AddPoly( &apArgs );
|
|
}
|
|
}
|