mirror of
https://github.com/Q3Rally-Team/q3rally.git
synced 2024-11-23 04:12:02 +00:00
6bc3b33eab
Fix axis returned by IQM's LerpTag Calculate bounds for unanimated IQM models Fix loading favorites as initial source in server browser Improve finding obelisk entitynum for bot AI Fix SDL audio playback with surround sound Fix predicting entity origin on rotating mover Allow binds to use hex values for all key codes Disable pulseaudio capture regardless of SDL version Fix SDL audio playback with 16-bit stereo sound Make s_info command display channels instead of stereo Fix cross-compiling using mingw-w64 on Ubuntu 18.04
1455 lines
39 KiB
C
1455 lines
39 KiB
C
/*
|
|
===========================================================================
|
|
Copyright (C) 1999-2005 Id Software, Inc.
|
|
Copyright (C) 2002-2015 Q3Rally Team (Per Thormann - q3rally@gmail.com)
|
|
|
|
This file is part of q3rally source code.
|
|
|
|
q3rally source code is free software; you can redistribute it
|
|
and/or modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of the License,
|
|
or (at your option) any later version.
|
|
|
|
q3rally source code is distributed in the hope that it will be
|
|
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with q3rally; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
===========================================================================
|
|
*/
|
|
//
|
|
// cg_ents.c -- present snapshot entities, happens every single frame
|
|
|
|
#include "cg_local.h"
|
|
|
|
|
|
// Q3Rally Code Start
|
|
// used to know when to update physics variables from server
|
|
//qboolean updateEnts;
|
|
// Q3Rally Code END
|
|
/*
|
|
======================
|
|
CG_PositionEntityOnTag
|
|
|
|
Modifies the entities position and axis by the given
|
|
tag location
|
|
======================
|
|
*/
|
|
void CG_PositionEntityOnTag( refEntity_t *entity, const refEntity_t *parent,
|
|
qhandle_t parentModel, char *tagName ) {
|
|
int i;
|
|
orientation_t lerped;
|
|
|
|
// lerp the tag
|
|
trap_R_LerpTag( &lerped, parentModel, parent->oldframe, parent->frame,
|
|
1.0 - parent->backlerp, tagName );
|
|
|
|
// FIXME: allow origin offsets along tag?
|
|
VectorCopy( parent->origin, entity->origin );
|
|
for ( i = 0 ; i < 3 ; i++ ) {
|
|
VectorMA( entity->origin, lerped.origin[i], parent->axis[i], entity->origin );
|
|
}
|
|
|
|
// had to cast away the const to avoid compiler problems...
|
|
MatrixMultiply( lerped.axis, ((refEntity_t *)parent)->axis, entity->axis );
|
|
entity->backlerp = parent->backlerp;
|
|
}
|
|
|
|
|
|
/*
|
|
======================
|
|
CG_PositionRotatedEntityOnTag
|
|
|
|
Modifies the entities position and axis by the given
|
|
tag location
|
|
======================
|
|
*/
|
|
void CG_PositionRotatedEntityOnTag( refEntity_t *entity, const refEntity_t *parent,
|
|
qhandle_t parentModel, char *tagName ) {
|
|
int i;
|
|
orientation_t lerped;
|
|
vec3_t tempAxis[3];
|
|
|
|
//AxisClear( entity->axis );
|
|
// lerp the tag
|
|
trap_R_LerpTag( &lerped, parentModel, parent->oldframe, parent->frame,
|
|
1.0 - parent->backlerp, tagName );
|
|
|
|
// FIXME: allow origin offsets along tag?
|
|
VectorCopy( parent->origin, entity->origin );
|
|
for ( i = 0 ; i < 3 ; i++ ) {
|
|
VectorMA( entity->origin, lerped.origin[i], parent->axis[i], entity->origin );
|
|
}
|
|
|
|
// had to cast away the const to avoid compiler problems...
|
|
MatrixMultiply( entity->axis, lerped.axis, tempAxis );
|
|
MatrixMultiply( tempAxis, ((refEntity_t *)parent)->axis, entity->axis );
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
==========================================================================
|
|
|
|
FUNCTIONS CALLED EACH FRAME
|
|
|
|
==========================================================================
|
|
*/
|
|
|
|
/*
|
|
======================
|
|
CG_SetEntitySoundPosition
|
|
|
|
Also called by event processing code
|
|
======================
|
|
*/
|
|
void CG_SetEntitySoundPosition( centity_t *cent ) {
|
|
if ( cent->currentState.solid == SOLID_BMODEL ) {
|
|
vec3_t origin;
|
|
float *v;
|
|
|
|
v = cgs.inlineModelMidpoints[ cent->currentState.modelindex ];
|
|
VectorAdd( cent->lerpOrigin, v, origin );
|
|
trap_S_UpdateEntityPosition( cent->currentState.number, origin );
|
|
} else {
|
|
trap_S_UpdateEntityPosition( cent->currentState.number, cent->lerpOrigin );
|
|
}
|
|
}
|
|
|
|
/*
|
|
==================
|
|
CG_EntityEffects
|
|
|
|
Add continuous entity effects, like local entity emission and lighting
|
|
==================
|
|
*/
|
|
static void CG_EntityEffects( centity_t *cent ) {
|
|
|
|
// update sound origins
|
|
CG_SetEntitySoundPosition( cent );
|
|
|
|
// add loop sound
|
|
if ( cent->currentState.loopSound ) {
|
|
if (cent->currentState.eType != ET_SPEAKER) {
|
|
trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin,
|
|
cgs.gameSounds[ cent->currentState.loopSound ] );
|
|
} else {
|
|
trap_S_AddRealLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin,
|
|
cgs.gameSounds[ cent->currentState.loopSound ] );
|
|
}
|
|
}
|
|
else {
|
|
trap_S_StopLoopingSound(cent->currentState.number);
|
|
}
|
|
|
|
|
|
// constant light glow
|
|
if(cent->currentState.constantLight)
|
|
{
|
|
int cl;
|
|
float i, r, g, b;
|
|
|
|
cl = cent->currentState.constantLight;
|
|
r = (float) (cl & 0xFF) / 255.0;
|
|
g = (float) ((cl >> 8) & 0xFF) / 255.0;
|
|
b = (float) ((cl >> 16) & 0xFF) / 255.0;
|
|
i = (float) ((cl >> 24) & 0xFF) * 4.0;
|
|
trap_R_AddLightToScene(cent->lerpOrigin, i, r, g, b);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
==================
|
|
CG_General
|
|
==================
|
|
*/
|
|
static void CG_General( centity_t *cent ) {
|
|
refEntity_t ent;
|
|
entityState_t *s1;
|
|
|
|
s1 = ¢->currentState;
|
|
|
|
// if set to invisible, skip
|
|
if (!s1->modelindex) {
|
|
return;
|
|
}
|
|
|
|
memset (&ent, 0, sizeof(ent));
|
|
|
|
// set frame
|
|
|
|
ent.frame = s1->frame;
|
|
ent.oldframe = ent.frame;
|
|
ent.backlerp = 0;
|
|
|
|
VectorCopy( cent->lerpOrigin, ent.origin);
|
|
VectorCopy( cent->lerpOrigin, ent.oldorigin);
|
|
|
|
ent.hModel = cgs.gameModels[s1->modelindex];
|
|
|
|
// player model
|
|
if (s1->number == cg.snap->ps.clientNum) {
|
|
ent.renderfx |= RF_THIRD_PERSON; // only draw from mirrors
|
|
}
|
|
|
|
// convert angles to axis
|
|
AnglesToAxis( cent->lerpAngles, ent.axis );
|
|
|
|
// add to refresh list
|
|
trap_R_AddRefEntityToScene (&ent);
|
|
}
|
|
|
|
/*
|
|
==================
|
|
CG_Speaker
|
|
|
|
Speaker entities can automatically play sounds
|
|
==================
|
|
*/
|
|
static void CG_Speaker( centity_t *cent ) {
|
|
if ( ! cent->currentState.clientNum ) { // FIXME: use something other than clientNum...
|
|
return; // not auto triggering
|
|
}
|
|
|
|
if ( cg.time < cent->miscTime ) {
|
|
return;
|
|
}
|
|
|
|
trap_S_StartSound (NULL, cent->currentState.number, CHAN_ITEM, cgs.gameSounds[cent->currentState.eventParm] );
|
|
|
|
// ent->s.frame = ent->wait * 10;
|
|
// ent->s.clientNum = ent->random * 10;
|
|
cent->miscTime = cg.time + cent->currentState.frame * 100 + cent->currentState.clientNum * 100 * crandom();
|
|
}
|
|
|
|
/*
|
|
==================
|
|
CG_Item
|
|
==================
|
|
*/
|
|
static void CG_Item( centity_t *cent ) {
|
|
refEntity_t ent;
|
|
entityState_t *es;
|
|
gitem_t *item;
|
|
int msec;
|
|
float frac;
|
|
float scale;
|
|
weaponInfo_t *wi;
|
|
|
|
es = ¢->currentState;
|
|
if ( es->modelindex >= bg_numItems ) {
|
|
CG_Error( "Bad item index %i on entity", es->modelindex );
|
|
}
|
|
|
|
// if set to invisible, skip
|
|
if ( !es->modelindex || ( es->eFlags & EF_NODRAW ) ) {
|
|
return;
|
|
}
|
|
|
|
item = &bg_itemlist[ es->modelindex ];
|
|
if ( cg_simpleItems.integer && item->giType != IT_TEAM ) {
|
|
memset( &ent, 0, sizeof( ent ) );
|
|
ent.reType = RT_SPRITE;
|
|
VectorCopy( cent->lerpOrigin, ent.origin );
|
|
ent.radius = 14;
|
|
ent.customShader = cg_items[es->modelindex].icon;
|
|
ent.shaderRGBA[0] = 255;
|
|
ent.shaderRGBA[1] = 255;
|
|
ent.shaderRGBA[2] = 255;
|
|
ent.shaderRGBA[3] = 255;
|
|
trap_R_AddRefEntityToScene(&ent);
|
|
return;
|
|
}
|
|
|
|
// items bob up and down continuously
|
|
scale = 0.005 + cent->currentState.number * 0.00001;
|
|
cent->lerpOrigin[2] += 4 + cos( ( cg.time + 1000 ) * scale ) * 4;
|
|
|
|
memset (&ent, 0, sizeof(ent));
|
|
|
|
// autorotate at one of two speeds
|
|
if ( item->giType == IT_HEALTH ) {
|
|
VectorCopy( cg.autoAnglesFast, cent->lerpAngles );
|
|
AxisCopy( cg.autoAxisFast, ent.axis );
|
|
} else {
|
|
VectorCopy( cg.autoAngles, cent->lerpAngles );
|
|
AxisCopy( cg.autoAxis, ent.axis );
|
|
}
|
|
|
|
wi = NULL;
|
|
// the weapons have their origin where they attatch to player
|
|
// models, so we need to offset them or they will rotate
|
|
// eccentricly
|
|
if ( item->giType == IT_WEAPON ) {
|
|
wi = &cg_weapons[item->giTag];
|
|
cent->lerpOrigin[0] -=
|
|
wi->weaponMidpoint[0] * ent.axis[0][0] +
|
|
wi->weaponMidpoint[1] * ent.axis[1][0] +
|
|
wi->weaponMidpoint[2] * ent.axis[2][0];
|
|
cent->lerpOrigin[1] -=
|
|
wi->weaponMidpoint[0] * ent.axis[0][1] +
|
|
wi->weaponMidpoint[1] * ent.axis[1][1] +
|
|
wi->weaponMidpoint[2] * ent.axis[2][1];
|
|
cent->lerpOrigin[2] -=
|
|
wi->weaponMidpoint[0] * ent.axis[0][2] +
|
|
wi->weaponMidpoint[1] * ent.axis[1][2] +
|
|
wi->weaponMidpoint[2] * ent.axis[2][2];
|
|
|
|
cent->lerpOrigin[2] += 8; // an extra height boost
|
|
}
|
|
|
|
if( item->giType == IT_WEAPON && item->giTag == WP_RAILGUN ) {
|
|
clientInfo_t *ci = &cgs.clientinfo[cg.snap->ps.clientNum];
|
|
Byte4Copy( ci->c1RGBA, ent.shaderRGBA );
|
|
}
|
|
|
|
ent.hModel = cg_items[es->modelindex].models[0];
|
|
|
|
VectorCopy( cent->lerpOrigin, ent.origin);
|
|
VectorCopy( cent->lerpOrigin, ent.oldorigin);
|
|
|
|
ent.nonNormalizedAxes = qfalse;
|
|
|
|
// if just respawned, slowly scale up
|
|
msec = cg.time - cent->miscTime;
|
|
if ( msec >= 0 && msec < ITEM_SCALEUP_TIME ) {
|
|
frac = (float)msec / ITEM_SCALEUP_TIME;
|
|
VectorScale( ent.axis[0], frac, ent.axis[0] );
|
|
VectorScale( ent.axis[1], frac, ent.axis[1] );
|
|
VectorScale( ent.axis[2], frac, ent.axis[2] );
|
|
ent.nonNormalizedAxes = qtrue;
|
|
} else {
|
|
frac = 1.0;
|
|
}
|
|
|
|
// items without glow textures need to keep a minimum light value
|
|
// so they are always visible
|
|
// Q3Rally Code Start
|
|
// if ( ( item->giType == IT_WEAPON ) || ( item->giType == IT_ARMOR ) ) {
|
|
if ( ( item->giType == IT_WEAPON ) || ( item->giType == IT_RFWEAPON ) ||
|
|
( item->giType == IT_ARMOR ) ) {
|
|
// Q3Rally Code END
|
|
ent.renderfx |= RF_MINLIGHT;
|
|
}
|
|
|
|
// increase the size of the weapons when they are presented as items
|
|
if ( item->giType == IT_WEAPON ) {
|
|
VectorScale( ent.axis[0], 1.5, ent.axis[0] );
|
|
VectorScale( ent.axis[1], 1.5, ent.axis[1] );
|
|
VectorScale( ent.axis[2], 1.5, ent.axis[2] );
|
|
ent.nonNormalizedAxes = qtrue;
|
|
#ifdef MISSIONPACK
|
|
trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, cgs.media.weaponHoverSound );
|
|
#endif
|
|
}
|
|
|
|
#ifdef MISSIONPACK
|
|
if ( item->giType == IT_HOLDABLE && item->giTag == HI_KAMIKAZE ) {
|
|
VectorScale( ent.axis[0], 2, ent.axis[0] );
|
|
VectorScale( ent.axis[1], 2, ent.axis[1] );
|
|
VectorScale( ent.axis[2], 2, ent.axis[2] );
|
|
ent.nonNormalizedAxes = qtrue;
|
|
}
|
|
#endif
|
|
|
|
// Q3Rally Code Start
|
|
ent.origin[2] += 6;
|
|
// Q3Rally Code END
|
|
|
|
// add to refresh list
|
|
trap_R_AddRefEntityToScene(&ent);
|
|
|
|
if ( item->giType == IT_WEAPON && wi && wi->barrelModel ) {
|
|
refEntity_t barrel;
|
|
vec3_t angles;
|
|
|
|
memset( &barrel, 0, sizeof( barrel ) );
|
|
|
|
barrel.hModel = wi->barrelModel;
|
|
|
|
VectorCopy( ent.lightingOrigin, barrel.lightingOrigin );
|
|
barrel.shadowPlane = ent.shadowPlane;
|
|
barrel.renderfx = ent.renderfx;
|
|
|
|
angles[YAW] = 0;
|
|
angles[PITCH] = 0;
|
|
angles[ROLL] = 0;
|
|
AnglesToAxis( angles, barrel.axis );
|
|
|
|
CG_PositionRotatedEntityOnTag( &barrel, &ent, wi->weaponModel, "tag_barrel" );
|
|
|
|
barrel.nonNormalizedAxes = ent.nonNormalizedAxes;
|
|
|
|
trap_R_AddRefEntityToScene( &barrel );
|
|
}
|
|
|
|
// accompanying rings / spheres for powerups
|
|
if ( !cg_simpleItems.integer )
|
|
{
|
|
vec3_t spinAngles;
|
|
|
|
VectorClear( spinAngles );
|
|
|
|
// Q3Rally Code Start
|
|
// if ( item->giType == IT_HEALTH || item->giType == IT_POWERUP )
|
|
if ( item->giType == IT_HEALTH || item->giType == IT_POWERUP || item->giTag == HI_TURBO)
|
|
// Q3Rally Code END
|
|
{
|
|
if ( ( ent.hModel = cg_items[es->modelindex].models[1] ) != 0 )
|
|
{
|
|
// Q3Rally Code Start
|
|
/*
|
|
if ( item->giType == IT_POWERUP )
|
|
{
|
|
ent.origin[2] += 12;
|
|
spinAngles[1] = ( cg.time & 1023 ) * 360 / -1024.0f;
|
|
}
|
|
*/
|
|
if ( item->giType == IT_POWERUP || item->giTag == HI_TURBO)
|
|
{
|
|
spinAngles[ROLL] = ( (cg.time/2) & 1023 ) * 360 / -1024.0f;
|
|
spinAngles[1] = ( (cg.time/2) & 1023 ) * 360 / -1024.0f;
|
|
}
|
|
// Q3Rally Code END
|
|
AnglesToAxis( spinAngles, ent.axis );
|
|
|
|
// scale up if respawning
|
|
if ( frac != 1.0 ) {
|
|
VectorScale( ent.axis[0], frac, ent.axis[0] );
|
|
VectorScale( ent.axis[1], frac, ent.axis[1] );
|
|
VectorScale( ent.axis[2], frac, ent.axis[2] );
|
|
ent.nonNormalizedAxes = qtrue;
|
|
}
|
|
trap_R_AddRefEntityToScene( &ent );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Q3Rally Code Start
|
|
if ( !cg_simpleItems.integer )
|
|
{
|
|
vec3_t spinAngles;
|
|
|
|
VectorClear ( spinAngles );
|
|
|
|
if (item->giTag == PW_HASTE){
|
|
if ( ( ent.hModel = cg_items[es->modelindex].models[2] ) != 0 )
|
|
{
|
|
AxisCopy( cg.autoAxis, ent.axis );
|
|
|
|
// scale up if respawning
|
|
if ( frac != 1.0 ) {
|
|
VectorScale( ent.axis[0], frac, ent.axis[0] );
|
|
VectorScale( ent.axis[1], frac, ent.axis[1] );
|
|
VectorScale( ent.axis[2], frac, ent.axis[2] );
|
|
ent.nonNormalizedAxes = qtrue;
|
|
}
|
|
trap_R_AddRefEntityToScene( &ent );
|
|
}
|
|
}
|
|
}
|
|
// Q3Rally Code END
|
|
}
|
|
|
|
//============================================================================
|
|
|
|
/*
|
|
===============
|
|
CG_Missile
|
|
===============
|
|
*/
|
|
static void CG_Missile( centity_t *cent ) {
|
|
refEntity_t ent;
|
|
entityState_t *s1;
|
|
const weaponInfo_t *weapon;
|
|
// int col;
|
|
|
|
s1 = ¢->currentState;
|
|
if ( s1->weapon >= WP_NUM_WEAPONS ) {
|
|
s1->weapon = 0;
|
|
}
|
|
weapon = &cg_weapons[s1->weapon];
|
|
|
|
// calculate the axis
|
|
VectorCopy( s1->angles, cent->lerpAngles);
|
|
|
|
// add trails
|
|
if ( weapon->missileTrailFunc )
|
|
{
|
|
weapon->missileTrailFunc( cent, weapon );
|
|
}
|
|
/*
|
|
if ( cent->currentState.modelindex == TEAM_RED ) {
|
|
col = 1;
|
|
}
|
|
else if ( cent->currentState.modelindex == TEAM_BLUE ) {
|
|
col = 2;
|
|
}
|
|
else {
|
|
col = 0;
|
|
}
|
|
|
|
// add dynamic light
|
|
if ( weapon->missileDlight ) {
|
|
trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight,
|
|
weapon->missileDlightColor[col][0], weapon->missileDlightColor[col][1], weapon->missileDlightColor[col][2] );
|
|
}
|
|
*/
|
|
// add dynamic light
|
|
if ( weapon->missileDlight ) {
|
|
trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight,
|
|
weapon->missileDlightColor[0], weapon->missileDlightColor[1], weapon->missileDlightColor[2] );
|
|
}
|
|
|
|
// add missile sound
|
|
if ( weapon->missileSound ) {
|
|
vec3_t velocity;
|
|
|
|
BG_EvaluateTrajectoryDelta( ¢->currentState.pos, cg.time, velocity );
|
|
|
|
trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, velocity, weapon->missileSound );
|
|
}
|
|
|
|
// create the render entity
|
|
memset (&ent, 0, sizeof(ent));
|
|
VectorCopy( cent->lerpOrigin, ent.origin);
|
|
VectorCopy( cent->lerpOrigin, ent.oldorigin);
|
|
|
|
if ( cent->currentState.weapon == WP_PLASMAGUN ) {
|
|
ent.reType = RT_SPRITE;
|
|
ent.radius = 16;
|
|
ent.rotation = 0;
|
|
ent.customShader = cgs.media.plasmaBallShader;
|
|
trap_R_AddRefEntityToScene( &ent );
|
|
return;
|
|
}
|
|
|
|
if ( cent->currentState.weapon == WP_FLAME_THROWER ) {
|
|
ent.reType = RT_SPRITE;
|
|
ent.radius = 32;
|
|
ent.rotation = 0;
|
|
ent.customShader = cgs.media.flameBallShader;
|
|
trap_R_AddRefEntityToScene( &ent );
|
|
return;
|
|
}
|
|
|
|
// Q3Rally Code Start
|
|
if (cent->currentState.weapon == RWP_MINE){
|
|
if (cgs.gametype >= GT_TEAM){
|
|
switch(cgs.clientinfo[cent->currentState.otherEntityNum].team){
|
|
default:
|
|
case TEAM_RED:
|
|
ent.customSkin = trap_R_RegisterSkin( "models/rearfire/red.skin" );
|
|
break;
|
|
|
|
case TEAM_BLUE:
|
|
ent.customSkin = trap_R_RegisterSkin( "models/rearfire/blue.skin" );
|
|
break;
|
|
|
|
case TEAM_GREEN:
|
|
ent.customSkin = trap_R_RegisterSkin( "models/rearfire/green.skin" );
|
|
break;
|
|
|
|
case TEAM_YELLOW:
|
|
ent.customSkin = trap_R_RegisterSkin( "models/rearfire/yellow.skin" );
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
ent.skinNum = 0;
|
|
}
|
|
}
|
|
else{
|
|
// flicker between two skins
|
|
ent.skinNum = cg.clientFrame & 1;
|
|
}
|
|
//ent.skinNum = cg.clientFrame & 1;
|
|
// Q3Rally Code END
|
|
|
|
ent.hModel = weapon->missileModel;
|
|
ent.renderfx = weapon->missileRenderfx | RF_NOSHADOW;
|
|
|
|
#ifdef MISSIONPACK
|
|
if ( cent->currentState.weapon == WP_PROX_LAUNCHER ) {
|
|
if (s1->generic1 == TEAM_BLUE) {
|
|
ent.hModel = cgs.media.blueProxMine;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// convert direction of travel into axis
|
|
if ( VectorNormalize2( s1->pos.trDelta, ent.axis[0] ) == 0 ) {
|
|
// Q3Rally Code Start
|
|
if (cent->currentState.weapon == RWP_MINE)
|
|
AxisClear(ent.axis);
|
|
else
|
|
// Q3Rally Code END
|
|
ent.axis[0][2] = 1;
|
|
}
|
|
|
|
// spin as it moves
|
|
if ( s1->pos.trType != TR_STATIONARY ) {
|
|
RotateAroundDirection( ent.axis, cg.time / 4 );
|
|
} else {
|
|
#ifdef MISSIONPACK
|
|
if ( s1->weapon == WP_PROX_LAUNCHER ) {
|
|
AnglesToAxis( cent->lerpAngles, ent.axis );
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
RotateAroundDirection( ent.axis, s1->time );
|
|
}
|
|
}
|
|
|
|
// add to refresh list, possibly with quad glow
|
|
CG_AddRefEntityWithPowerups( &ent, s1, TEAM_FREE );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
CG_Grapple
|
|
|
|
This is called when the grapple is sitting up against the wall
|
|
===============
|
|
*/
|
|
// Q3Rally Code Start - removed
|
|
/*
|
|
static void CG_Grapple( centity_t *cent ) {
|
|
refEntity_t ent;
|
|
entityState_t *s1;
|
|
const weaponInfo_t *weapon;
|
|
|
|
s1 = ¢->currentState;
|
|
if ( s1->weapon >= WP_NUM_WEAPONS ) {
|
|
s1->weapon = 0;
|
|
}
|
|
weapon = &cg_weapons[s1->weapon];
|
|
|
|
// calculate the axis
|
|
VectorCopy( s1->angles, cent->lerpAngles);
|
|
|
|
#if 0 // FIXME add grapple pull sound here..?
|
|
// add missile sound
|
|
if ( weapon->missileSound ) {
|
|
trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, weapon->missileSound );
|
|
}
|
|
#endif
|
|
|
|
// Will draw cable if needed
|
|
CG_GrappleTrail ( cent, weapon );
|
|
|
|
// create the render entity
|
|
memset (&ent, 0, sizeof(ent));
|
|
VectorCopy( cent->lerpOrigin, ent.origin);
|
|
VectorCopy( cent->lerpOrigin, ent.oldorigin);
|
|
|
|
// flicker between two skins
|
|
ent.skinNum = cg.clientFrame & 1;
|
|
ent.hModel = weapon->missileModel;
|
|
ent.renderfx = weapon->missileRenderfx | RF_NOSHADOW;
|
|
|
|
// convert direction of travel into axis
|
|
if ( VectorNormalize2( s1->pos.trDelta, ent.axis[0] ) == 0 ) {
|
|
ent.axis[0][2] = 1;
|
|
}
|
|
|
|
trap_R_AddRefEntityToScene( &ent );
|
|
}
|
|
*/
|
|
|
|
|
|
/* Q3Rally Code Start
|
|
==================
|
|
CG_Auxent
|
|
|
|
car wheel entities
|
|
==================
|
|
*/
|
|
static void CG_Auxent( centity_t *cent ) {
|
|
refEntity_t ent;
|
|
entityState_t *s1;
|
|
|
|
s1 = ¢->currentState;
|
|
|
|
if ( s1->otherEntityNum == cg.snap->ps.clientNum && !cg.newSnap )
|
|
return;
|
|
|
|
cg_entities[s1->otherEntityNum].wheelSpeeds[s1->otherEntityNum2] = s1->apos.trDelta[0];
|
|
cg_entities[s1->otherEntityNum].wheelSkidding[s1->otherEntityNum2] = s1->frame;
|
|
cg_entities[s1->otherEntityNum].steeringAngle = s1->apos.trDelta[1];
|
|
|
|
if (s1->otherEntityNum == cg.snap->ps.clientNum){
|
|
// Com_Printf("updating wheels\n");
|
|
|
|
cg.car.sPoints[s1->otherEntityNum2].w = s1->apos.trDelta[0];
|
|
cg.car.sPoints[s1->otherEntityNum2].slipping = s1->frame;
|
|
cg.car.wheelAngle = s1->apos.trDelta[1];
|
|
VectorCopy(s1->pos.trBase, cg.car.sPoints[s1->otherEntityNum2].r);
|
|
VectorCopy(s1->pos.trDelta, cg.car.sPoints[s1->otherEntityNum2].v);
|
|
|
|
VectorCopy(s1->origin2, cg.car.sPoints[s1->otherEntityNum2].normals[0]);
|
|
cg.car.sPoints[s1->otherEntityNum2].onGround = s1->groundEntityNum;
|
|
}
|
|
|
|
// if set to invisible, skip
|
|
if (!s1->modelindex) {
|
|
return;
|
|
}
|
|
|
|
memset (&ent, 0, sizeof(ent));
|
|
|
|
// set frame
|
|
|
|
ent.frame = s1->frame;
|
|
ent.oldframe = ent.frame;
|
|
ent.backlerp = 0;
|
|
|
|
VectorCopy( cent->lerpOrigin, ent.origin);
|
|
VectorCopy( cent->lerpOrigin, ent.oldorigin);
|
|
|
|
ent.hModel = cgs.gameModels[s1->modelindex];
|
|
|
|
// player model
|
|
if (s1->number == cg.snap->ps.clientNum) {
|
|
ent.renderfx |= RF_THIRD_PERSON; // only draw from mirrors
|
|
}
|
|
|
|
// convert angles to axis
|
|
AnglesToAxis( cent->lerpAngles, ent.axis );
|
|
|
|
// add to refresh list
|
|
trap_R_AddRefEntityToScene (&ent);
|
|
}
|
|
|
|
/*
|
|
==================
|
|
CG_Weather
|
|
==================
|
|
*/
|
|
static void CG_Weather( centity_t *cent ) {
|
|
entityState_t *s1;
|
|
|
|
s1 = ¢->currentState;
|
|
|
|
// CG_EffectParse( "T=RAIN,B=5 10,C=0.5,G=0.5 2,BV=0,GV=0 100,W=1 2,D=300" );
|
|
CG_Atmospheric_SetParticles( s1->weapon, s1->powerups, s1->legsAnim );
|
|
}
|
|
// Q3Rally Code END
|
|
|
|
|
|
/*
|
|
===============
|
|
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));
|
|
VectorCopy( cent->lerpOrigin, ent.origin);
|
|
VectorCopy( cent->lerpOrigin, ent.oldorigin);
|
|
AnglesToAxis( cent->lerpAngles, ent.axis );
|
|
|
|
ent.renderfx = RF_NOSHADOW;
|
|
|
|
// 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];
|
|
}
|
|
|
|
// add to refresh list
|
|
trap_R_AddRefEntityToScene(&ent);
|
|
|
|
// add the secondary model
|
|
if ( s1->modelindex2 ) {
|
|
ent.skinNum = 0;
|
|
ent.hModel = cgs.gameModels[s1->modelindex2];
|
|
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;
|
|
|
|
// 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
|
|
|
|
// add to refresh list
|
|
trap_R_AddRefEntityToScene(&ent);
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
CG_CreateRotationMatrix
|
|
================
|
|
*/
|
|
void CG_CreateRotationMatrix(vec3_t angles, vec3_t matrix[3]) {
|
|
AngleVectors(angles, matrix[0], matrix[1], matrix[2]);
|
|
VectorInverse(matrix[1]);
|
|
}
|
|
|
|
/*
|
|
================
|
|
CG_TransposeMatrix
|
|
================
|
|
*/
|
|
void CG_TransposeMatrix(vec3_t matrix[3], vec3_t transpose[3]) {
|
|
int i, j;
|
|
for (i = 0; i < 3; i++) {
|
|
for (j = 0; j < 3; j++) {
|
|
transpose[i][j] = matrix[j][i];
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
CG_RotatePoint
|
|
================
|
|
*/
|
|
void CG_RotatePoint(vec3_t point, vec3_t matrix[3]) {
|
|
vec3_t tvec;
|
|
|
|
VectorCopy(point, tvec);
|
|
point[0] = DotProduct(matrix[0], tvec);
|
|
point[1] = DotProduct(matrix[1], tvec);
|
|
point[2] = DotProduct(matrix[2], tvec);
|
|
}
|
|
|
|
/*
|
|
=========================
|
|
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, vec3_t angles_in, vec3_t angles_out) {
|
|
centity_t *cent;
|
|
vec3_t oldOrigin, origin, deltaOrigin;
|
|
vec3_t oldAngles, angles, deltaAngles;
|
|
vec3_t matrix[3], transpose[3];
|
|
vec3_t org, org2, move2;
|
|
|
|
if ( moverNum <= 0 || moverNum >= ENTITYNUM_MAX_NORMAL ) {
|
|
VectorCopy( in, out );
|
|
VectorCopy(angles_in, angles_out);
|
|
return;
|
|
}
|
|
|
|
cent = &cg_entities[ moverNum ];
|
|
if ( cent->currentState.eType != ET_MOVER ) {
|
|
VectorCopy( in, out );
|
|
VectorCopy(angles_in, angles_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 );
|
|
|
|
// origin change when on a rotating object
|
|
CG_CreateRotationMatrix( deltaAngles, transpose );
|
|
CG_TransposeMatrix( transpose, matrix );
|
|
VectorSubtract( in, oldOrigin, org );
|
|
VectorCopy( org, org2 );
|
|
CG_RotatePoint( org2, matrix );
|
|
VectorSubtract( org2, org, move2 );
|
|
VectorAdd( deltaOrigin, move2, deltaOrigin );
|
|
|
|
VectorAdd( in, deltaOrigin, out );
|
|
VectorAdd( angles_in, deltaAngles, angles_out );
|
|
}
|
|
|
|
|
|
/*
|
|
=============================
|
|
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
|
|
|
|
===============
|
|
*/
|
|
static void CG_CalcEntityLerpPositions( centity_t *cent ) {
|
|
|
|
// 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;
|
|
// Q3Rally Code Start
|
|
cent->currentState.apos.trType = TR_INTERPOLATE;
|
|
cent->nextState.apos.trType = TR_INTERPOLATE;
|
|
// Q3Rally Code END
|
|
}
|
|
}
|
|
|
|
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 );
|
|
return;
|
|
}
|
|
|
|
// 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 );
|
|
|
|
// Q3Rally Code Start
|
|
/*
|
|
if( !cg_paused.integer &&
|
|
cent->currentState.number < MAX_CLIENTS &&
|
|
cent->currentState.number != cg.snap->ps.clientNum )
|
|
{
|
|
Com_Printf( "angles %f %f %f\n", cent->lerpAngles[0], cent->lerpAngles[1], cent->lerpAngles[2] );
|
|
}
|
|
*/
|
|
// Q3Rally Code END
|
|
|
|
// adjust for riding a mover if it wasn't rolled into the predicted
|
|
// player state
|
|
if ( cent != &cg.predictedPlayerEntity ) {
|
|
CG_AdjustPositionForMover( cent->lerpOrigin, cent->currentState.groundEntityNum,
|
|
cg.snap->serverTime, cg.time, cent->lerpOrigin, cent->lerpAngles, cent->lerpAngles);
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
CG_TeamBase
|
|
===============
|
|
*/
|
|
static void CG_TeamBase( centity_t *cent ) {
|
|
refEntity_t model;
|
|
#ifdef MISSIONPACK
|
|
vec3_t angles;
|
|
int t, h;
|
|
float c;
|
|
|
|
if ( cgs.gametype == GT_CTF || cgs.gametype == GT_1FCTF ) {
|
|
#else
|
|
if ( cgs.gametype == GT_CTF) {
|
|
#endif
|
|
// 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;
|
|
}
|
|
trap_R_AddRefEntityToScene( &model );
|
|
}
|
|
#ifdef MISSIONPACK
|
|
else if ( cgs.gametype == GT_OBELISK ) {
|
|
// show the obelisk
|
|
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 );
|
|
|
|
model.hModel = cgs.media.overloadBaseModel;
|
|
trap_R_AddRefEntityToScene( &model );
|
|
// if hit
|
|
if ( cent->currentState.frame == 1) {
|
|
// show hit model
|
|
// modelindex2 is the health value of the obelisk
|
|
c = cent->currentState.modelindex2;
|
|
model.shaderRGBA[0] = 0xff;
|
|
model.shaderRGBA[1] = c;
|
|
model.shaderRGBA[2] = c;
|
|
model.shaderRGBA[3] = 0xff;
|
|
//
|
|
model.hModel = cgs.media.overloadEnergyModel;
|
|
trap_R_AddRefEntityToScene( &model );
|
|
}
|
|
// if respawning
|
|
if ( cent->currentState.frame == 2) {
|
|
if ( !cent->miscTime ) {
|
|
cent->miscTime = cg.time;
|
|
}
|
|
t = cg.time - cent->miscTime;
|
|
h = (cg_obeliskRespawnDelay.integer - 5) * 1000;
|
|
//
|
|
if (t > h) {
|
|
c = (float) (t - h) / h;
|
|
if (c > 1)
|
|
c = 1;
|
|
}
|
|
else {
|
|
c = 0;
|
|
}
|
|
// show the lights
|
|
AnglesToAxis( cent->currentState.angles, model.axis );
|
|
//
|
|
model.shaderRGBA[0] = c * 0xff;
|
|
model.shaderRGBA[1] = c * 0xff;
|
|
model.shaderRGBA[2] = c * 0xff;
|
|
model.shaderRGBA[3] = c * 0xff;
|
|
|
|
model.hModel = cgs.media.overloadLightsModel;
|
|
trap_R_AddRefEntityToScene( &model );
|
|
// show the target
|
|
if (t > h) {
|
|
if ( !cent->muzzleFlashTime ) {
|
|
trap_S_StartSound (cent->lerpOrigin, ENTITYNUM_NONE, CHAN_BODY, cgs.media.obeliskRespawnSound);
|
|
cent->muzzleFlashTime = 1;
|
|
}
|
|
VectorCopy(cent->currentState.angles, angles);
|
|
angles[YAW] += (float) 16 * Q_acos(1-c) * 180 / M_PI;
|
|
AnglesToAxis( angles, model.axis );
|
|
|
|
VectorScale( model.axis[0], c, model.axis[0]);
|
|
VectorScale( model.axis[1], c, model.axis[1]);
|
|
VectorScale( model.axis[2], c, model.axis[2]);
|
|
|
|
model.shaderRGBA[0] = 0xff;
|
|
model.shaderRGBA[1] = 0xff;
|
|
model.shaderRGBA[2] = 0xff;
|
|
model.shaderRGBA[3] = 0xff;
|
|
//
|
|
model.origin[2] += 56;
|
|
model.hModel = cgs.media.overloadTargetModel;
|
|
trap_R_AddRefEntityToScene( &model );
|
|
}
|
|
else {
|
|
//FIXME: show animated smoke
|
|
}
|
|
}
|
|
else {
|
|
cent->miscTime = 0;
|
|
cent->muzzleFlashTime = 0;
|
|
// modelindex2 is the health value of the obelisk
|
|
c = cent->currentState.modelindex2;
|
|
model.shaderRGBA[0] = 0xff;
|
|
model.shaderRGBA[1] = c;
|
|
model.shaderRGBA[2] = c;
|
|
model.shaderRGBA[3] = 0xff;
|
|
// show the lights
|
|
model.hModel = cgs.media.overloadLightsModel;
|
|
trap_R_AddRefEntityToScene( &model );
|
|
// show the target
|
|
model.origin[2] += 56;
|
|
model.hModel = cgs.media.overloadTargetModel;
|
|
trap_R_AddRefEntityToScene( &model );
|
|
}
|
|
}
|
|
else if ( cgs.gametype == GT_HARVESTER ) {
|
|
// show harvester model
|
|
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.harvesterModel;
|
|
model.customSkin = cgs.media.harvesterRedSkin;
|
|
}
|
|
else if ( cent->currentState.modelindex == TEAM_BLUE ) {
|
|
model.hModel = cgs.media.harvesterModel;
|
|
model.customSkin = cgs.media.harvesterBlueSkin;
|
|
}
|
|
else {
|
|
model.hModel = cgs.media.harvesterNeutralModel;
|
|
model.customSkin = 0;
|
|
}
|
|
trap_R_AddRefEntityToScene( &model );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
===============
|
|
CG_AddCEntity
|
|
|
|
===============
|
|
*/
|
|
// Q3Rally Code Start
|
|
// static void CG_AddCEntity( centity_t *cent ) {
|
|
void CG_AddCEntity( centity_t *cent ) {
|
|
// Q3Rally Code END
|
|
// event-only entities will have been dealt with already
|
|
if ( cent->currentState.eType >= ET_EVENTS ) {
|
|
return;
|
|
}
|
|
|
|
// calculate the current origin
|
|
CG_CalcEntityLerpPositions( cent );
|
|
|
|
// add automatic effects
|
|
CG_EntityEffects( cent );
|
|
|
|
switch ( cent->currentState.eType ) {
|
|
default:
|
|
CG_Error( "Bad entity type: %i", cent->currentState.eType );
|
|
break;
|
|
// Q3Rally Code Start
|
|
case ET_CHECKPOINT:
|
|
// Q3Rally Code END
|
|
case ET_INVISIBLE:
|
|
case ET_PUSH_TRIGGER:
|
|
case ET_TELEPORT_TRIGGER:
|
|
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_MOVER:
|
|
CG_Mover( cent );
|
|
break;
|
|
// Q3Rally Code Start
|
|
case ET_BREAKGLASS:
|
|
CG_Mover( cent );
|
|
break;
|
|
case ET_BREAKWOOD:
|
|
CG_Mover( cent );
|
|
break;
|
|
case ET_BREAKMETAL:
|
|
CG_Mover( cent );
|
|
break;
|
|
// Q3Rally Code END
|
|
case ET_BEAM:
|
|
CG_Beam( cent );
|
|
break;
|
|
case ET_PORTAL:
|
|
CG_Portal( cent );
|
|
break;
|
|
case ET_SPEAKER:
|
|
CG_Speaker( cent );
|
|
break;
|
|
// Q3Rally Code Start
|
|
/*
|
|
case ET_GRAPPLE:
|
|
CG_Grapple( cent );
|
|
break;
|
|
*/
|
|
case ET_AUXENT:
|
|
CG_Auxent( cent );
|
|
break;
|
|
case ET_WEATHER:
|
|
CG_Weather( cent );
|
|
break;
|
|
case ET_SCRIPTED:
|
|
CG_Scripted_Object( cent );
|
|
break;
|
|
// Q3Rally Code END
|
|
case ET_TEAM:
|
|
CG_TeamBase( cent );
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
CG_AddPacketEntities
|
|
|
|
===============
|
|
*/
|
|
// Q3Rally Code Start
|
|
//static vec3_t lastVel;
|
|
//static vec3_t lastAngM;
|
|
//static int lastPMType;
|
|
// Q3Rally Code END
|
|
void CG_AddPacketEntities( void ) {
|
|
int num;
|
|
centity_t *cent;
|
|
playerState_t *ps;
|
|
// Q3Rally Code Start
|
|
// char value[16];
|
|
// int i;
|
|
// Q3Rally Code END
|
|
|
|
// 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 );
|
|
|
|
// generate and add the entity from the playerstate
|
|
ps = &cg.predictedPlayerState;
|
|
BG_PlayerStateToEntityState( ps, &cg.predictedPlayerEntity.currentState, qfalse );
|
|
CG_AddCEntity( &cg.predictedPlayerEntity );
|
|
|
|
// lerp the non-predicted value for lightning gun origins
|
|
CG_CalcEntityLerpPositions( &cg_entities[ cg.snap->ps.clientNum ] );
|
|
|
|
// add each entity sent over by the server
|
|
for ( num = 0 ; num < cg.snap->numEntities ; num++ ) {
|
|
cent = &cg_entities[ cg.snap->entities[ num ].number ];
|
|
CG_AddCEntity( cent );
|
|
}
|
|
|
|
// Q3Rally Code Start
|
|
if( cg_drawBotPaths.integer )
|
|
CG_DrawCheckpointLinks();
|
|
|
|
if ( cg.newSnap )
|
|
{
|
|
// int weaponTime;
|
|
/*
|
|
float m[3][3];
|
|
float m2[3][3];
|
|
vec3_t angles, delta_angles;
|
|
float time = 0.5f;
|
|
*/
|
|
// Com_Printf("updating car body variables\n");
|
|
|
|
if ( !cg_paused.integer && cg_debugpredict.integer )
|
|
Com_Printf( "updateEnts\n" );
|
|
/*
|
|
if ( !cg_paused.integer )
|
|
{
|
|
int frontTime;
|
|
int rearTime;
|
|
int newFrontTime;
|
|
int newRearTime;
|
|
|
|
rearTime = ( cg.predictedPlayerState.weaponTime & REAR_WEAPON_TIME_MASK ) >> 16;
|
|
newRearTime = ( cg.snap->ps.weaponTime & REAR_WEAPON_TIME_MASK ) >> 16;
|
|
Com_Printf( "cg forward: cur weapon time %i new weap time %i\n", cg.predictedPlayerState.weaponTime & NORMAL_WEAPON_TIME_MASK, cg.snap->ps.weaponTime & NORMAL_WEAPON_TIME_MASK );
|
|
Com_Printf( "cg rear: cur weapon time %i new weap time %i\n", rearTime, newRearTime );
|
|
Com_Printf( "cg: cur weapon time %i new weap time %i\n", cg.predictedPlayerState.weaponTime, cg.snap->ps.weaponTime );
|
|
}
|
|
*/
|
|
|
|
// cg.predictedPlayerState = cg.snap->ps;
|
|
|
|
// HACK: save the rear weapon time because for some stupid reason q3
|
|
// engine fucks that part up when it sends the weapon time.
|
|
{
|
|
int rearTime = cg.predictedPlayerState.weaponTime & REAR_WEAPON_TIME_MASK;
|
|
|
|
cg.predictedPlayerState = cg.snap->ps;
|
|
|
|
cg.predictedPlayerState.weaponTime &= ~REAR_WEAPON_TIME_MASK;
|
|
cg.predictedPlayerState.weaponTime |= rearTime;
|
|
}
|
|
|
|
CG_UpdateCarFromPS ( &cg.snap->ps );
|
|
/*
|
|
m[0][0] = 0; m[0][1] = time * -cg.car.sBody.w[2]; m[0][2] = time * cg.car.sBody.w[1];
|
|
m[1][0] = time * cg.car.sBody.w[2]; m[1][1] = 0; m[1][2] = time * -cg.car.sBody.w[0];
|
|
m[2][0] = time * -cg.car.sBody.w[1]; m[2][1] = time * cg.car.sBody.w[0]; m[2][2] = 0;
|
|
|
|
MatrixMultiply(m, cg.car.sBody.t, m2);
|
|
MatrixAdd(cg.car.sBody.t, m2, cg.car.tBody.t);
|
|
|
|
OrthonormalizeOrientation(cg.car.tBody.t);
|
|
OrientationToAngles( cg.car.tBody.t, angles );
|
|
|
|
OrientationToDeltaAngles( cg.car.sBody.t, cg.car.sBody.w, delta_angles );
|
|
|
|
Com_Printf( "view angles1 (%f %f %f)\n", cg.snap->ps.viewangles[0], cg.snap->ps.viewangles[1], cg.snap->ps.viewangles[2] );
|
|
Com_Printf( "delta angles (%f %f %f)\n", delta_angles[0], delta_angles[1], delta_angles[2] );
|
|
Com_Printf( "view angles2 (%f %f %f)\n", angles[0], angles[1], angles[2] );
|
|
*/
|
|
if (!cg_paused.integer){
|
|
// Com_Printf("client time %d\n", ps->commandTime);
|
|
|
|
/* Car
|
|
Com_Printf("springStrength %f\n", cg.car.springStrength);
|
|
Com_Printf("springMaxLength %f\n", cg.car.springMaxLength);
|
|
Com_Printf("springMinLength %f\n", cg.car.springMinLength);
|
|
Com_Printf("shockStrength %f\n", cg.car.shockStrength);
|
|
Com_Printf("wheelAngle %f\n", cg.car.wheelAngle);
|
|
Com_Printf("throttle %f\n", cg.car.throttle);
|
|
Com_Printf("gear %d\n", cg.car.gear);
|
|
Com_Printf("rpm %f\n", cg.car.rpm);
|
|
Com_Printf("aCOF %f\n", cg.car.aCOF);
|
|
Com_Printf("sCOF %f\n", cg.car.sCOF);
|
|
Com_Printf("kCOF %f\n", cg.car.kCOF);
|
|
Com_Printf("dfCOF %f\n", cg.car.dfCOF);
|
|
Com_Printf("ewCOF %f\n", cg.car.ewCOF);
|
|
Com_Printf("inverseBodyInertiaTensor:\n");
|
|
Com_Printf("%f, %f, %f\n", cg.car.inverseBodyInertiaTensor[0][0], cg.car.inverseBodyInertiaTensor[0][1], cg.car.inverseBodyInertiaTensor[0][2]);
|
|
Com_Printf("%f, %f, %f\n", cg.car.inverseBodyInertiaTensor[1][0], cg.car.inverseBodyInertiaTensor[1][1], cg.car.inverseBodyInertiaTensor[1][2]);
|
|
Com_Printf("%f, %f, %f\n", cg.car.inverseBodyInertiaTensor[2][0], cg.car.inverseBodyInertiaTensor[2][1], cg.car.inverseBodyInertiaTensor[2][2]);
|
|
*/
|
|
|
|
/* Body
|
|
Com_Printf("r %f, %f, %f\n", cg.car.sBody.r[0], cg.car.sBody.r[1], cg.car.sBody.r[2]);
|
|
Com_Printf("v %f, %f, %f\n", cg.car.sBody.v[0], cg.car.sBody.v[1], cg.car.sBody.v[2]);
|
|
Com_Printf("w %f, %f, %f\n", cg.car.sBody.w[0], cg.car.sBody.w[1], cg.car.sBody.w[2]);
|
|
Com_Printf("L %f, %f, %f\n", cg.car.sBody.L[0], cg.car.sBody.L[1], cg.car.sBody.L[2]);
|
|
Com_Printf("CoM %f, %f, %f\n", cg.car.sBody.CoM[0], cg.car.sBody.CoM[1], cg.car.sBody.CoM[2]);
|
|
Com_Printf("t:\n");
|
|
Com_Printf("%f, %f, %f\n", cg.car.sBody.t[0][0], cg.car.sBody.t[0][1], cg.car.sBody.t[0][2]);
|
|
Com_Printf("%f, %f, %f\n", cg.car.sBody.t[1][0], cg.car.sBody.t[1][1], cg.car.sBody.t[1][2]);
|
|
Com_Printf("%f, %f, %f\n", cg.car.sBody.t[2][0], cg.car.sBody.t[2][1], cg.car.sBody.t[2][2]);
|
|
*/
|
|
|
|
/* Point
|
|
i = 4;
|
|
Com_Printf("r %f, %f, %f\n", cg.car.tPoints[i].r[0], cg.car.tPoints[i].r[1], cg.car.tPoints[i].r[2]);
|
|
Com_Printf("v %f, %f, %f\n", cg.car.tPoints[i].v[0], cg.car.tPoints[i].v[1], cg.car.tPoints[i].v[2]);
|
|
Com_Printf("w %f\n", cg.car.tPoints[i].w);
|
|
Com_Printf("netForce %f, %f, %f\n", cg.car.sPoints[i].netForce[0], cg.car.sPoints[i].netForce[1], cg.car.sPoints[i].netForce[2]);
|
|
Com_Printf("netMoment %f\n", cg.car.sPoints[i].netMoment);
|
|
Com_Printf("normals %f, %f, %f\n", cg.car.sPoints[i].normals[0][0], cg.car.sPoints[i].normals[0][1], cg.car.sPoints[i].normals[0][2]);
|
|
Com_Printf("mass %f\n", cg.car.sPoints[i].mass);
|
|
Com_Printf("elasticity %f\n", cg.car.sPoints[i].elasticity);
|
|
Com_Printf("kcof %f\n", cg.car.sPoints[i].kcof);
|
|
Com_Printf("scof %f\n", cg.car.sPoints[i].scof);
|
|
Com_Printf("fluidDensity %f\n", cg.car.sPoints[i].fluidDensity);
|
|
Com_Printf("onGround %d\n", cg.car.sPoints[i].onGround);
|
|
Com_Printf("slipping %d\n", cg.car.sPoints[i].slipping);
|
|
*/
|
|
|
|
}
|
|
}
|
|
|
|
// VectorCopy(cg.snap->ps.velocity, lastVel);
|
|
// VectorCopy(cg.snap->ps.origin, lastAngM);
|
|
// lastPMType = cg.snap->ps.pm_type;
|
|
cg.newSnap = qfalse;
|
|
// Q3Rally Code END
|
|
}
|
|
|