q3rally/engine/code/cgame/cg_event.c

1487 lines
39 KiB
C

/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
Copyright (C) 2002-2009 Q3Rally Team (Per Thormann - perle@q3rally.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_event.c -- handle entity events at snapshot or playerstate transitions
#include "cg_local.h"
// for the voice chats
#ifdef MISSIONPACK // bk001205
#include "../../ui/menudef.h"
#endif
//==========================================================================
/*
===================
CG_PlaceString
Also called by scoreboard drawing
===================
*/
const char *CG_PlaceString( int rank ) {
static char str[64];
char *s, *t;
if ( rank & RANK_TIED_FLAG ) {
rank &= ~RANK_TIED_FLAG;
t = "Tied for ";
} else {
t = "";
}
if ( rank == 1 ) {
s = S_COLOR_BLUE "1st" S_COLOR_WHITE; // draw in blue
} else if ( rank == 2 ) {
s = S_COLOR_RED "2nd" S_COLOR_WHITE; // draw in red
} else if ( rank == 3 ) {
s = S_COLOR_YELLOW "3rd" S_COLOR_WHITE; // draw in yellow
} else if ( rank == 11 ) {
s = "11th";
} else if ( rank == 12 ) {
s = "12th";
} else if ( rank == 13 ) {
s = "13th";
} else if ( rank % 10 == 1 ) {
s = va("%ist", rank);
} else if ( rank % 10 == 2 ) {
s = va("%ind", rank);
} else if ( rank % 10 == 3 ) {
s = va("%ird", rank);
} else {
s = va("%ith", rank);
}
Com_sprintf( str, sizeof( str ), "%s%s", t, s );
return str;
}
/*
=============
CG_Obituary
=============
*/
static void CG_Obituary( entityState_t *ent ) {
int mod;
int target, attacker;
char *message;
// Q3Rally Code Start
char *pronoun;
// Q3Rally Code END
char *message2;
const char *targetInfo;
const char *attackerInfo;
char targetName[32];
char attackerName[32];
gender_t gender;
clientInfo_t *ci;
target = ent->otherEntityNum;
attacker = ent->otherEntityNum2;
mod = ent->eventParm;
if ( target < 0 || target >= MAX_CLIENTS ) {
CG_Error( "CG_Obituary: target out of range" );
}
ci = &cgs.clientinfo[target];
if ( attacker < 0 || attacker >= MAX_CLIENTS ) {
attacker = ENTITYNUM_WORLD;
attackerInfo = NULL;
} else {
attackerInfo = CG_ConfigString( CS_PLAYERS + attacker );
}
targetInfo = CG_ConfigString( CS_PLAYERS + target );
if ( !targetInfo ) {
return;
}
Q_strncpyz( targetName, Info_ValueForKey( targetInfo, "n" ), sizeof(targetName) - 2);
strcat( targetName, S_COLOR_WHITE );
message2 = "";
// check for single client messages
switch( mod ) {
case MOD_SUICIDE:
message = "suicides";
break;
case MOD_FALLING:
message = "cratered";
break;
case MOD_CRUSH:
message = "was squished";
break;
case MOD_WATER:
message = "sank like a rock";
break;
case MOD_SLIME:
message = "melted";
break;
case MOD_LAVA:
message = "does a back flip into the lava";
break;
case MOD_TARGET_LASER:
message = "saw the light";
break;
case MOD_TRIGGER_HURT:
message = "was in the wrong place";
break;
// Q3Rally Code Start
case MOD_BIOHAZARD:
if (attacker != ENTITYNUM_WORLD) message = NULL;
else message = "was melted by bio";
break;
case MOD_POISON:
if (attacker != ENTITYNUM_WORLD) message = NULL;
else message = "couldn't stand the gas";
break;
case MOD_FIRE:
if (attacker != ENTITYNUM_WORLD) message = NULL;
else message = "went up in flames";
break;
case MOD_BO_SHOCKS:
switch((int)(random() * 2) % 2){
case 0:
message = "jumped a little too high";
break;
default:
message = "'s shocks couldn't handle the stress";
break;
}
break;
case MOD_WORLD_COLLISION:
message = "couldn't stay on the road";
break;
case MOD_HIGH_FORCES:
message = "put a little too much stress on his car";
break;
// Q3Rally Code END
default:
message = NULL;
break;
}
if (attacker == target) {
gender = ci->gender;
// Q3Rally Code Start
if ( gender == GENDER_FEMALE )
pronoun = "her";
else if ( gender == GENDER_NEUTER )
pronoun = "its";
else
pronoun = "his";
// Q3Rally Code END
switch (mod) {
#ifdef MISSIONPACK
case MOD_KAMIKAZE:
message = "goes out with a bang";
break;
#endif
case MOD_GRENADE_SPLASH:
if ( gender == GENDER_FEMALE )
message = "tripped on her own grenade";
else if ( gender == GENDER_NEUTER )
message = "tripped on its own grenade";
else
message = "tripped on his own grenade";
break;
// Q3Rally Code Start
case MOD_MINE_SPLASH:
message = va("got too close to %s own mine", pronoun);
break;
case MOD_MINE:
message = va("forgot about %s own mine", pronoun);
break;
case MOD_BIOHAZARD:
message = va("was melted by %s own bio", pronoun);
break;
case MOD_POISON:
message = va("couldn't stand %s own poison", pronoun);
break;
case MOD_FIRE:
message = va("burned in %s own flames", pronoun);
break;
// Q3Rally Code END
case MOD_ROCKET_SPLASH:
if ( gender == GENDER_FEMALE )
message = "blew herself up";
else if ( gender == GENDER_NEUTER )
message = "blew itself up";
else
message = "blew himself up";
break;
case MOD_PLASMA_SPLASH:
if ( gender == GENDER_FEMALE )
message = "melted herself";
else if ( gender == GENDER_NEUTER )
message = "melted itself";
else
message = "melted himself";
break;
case MOD_BFG_SPLASH:
message = "should have used a smaller gun";
break;
case MOD_FLAME_THROWER:
message = "was flame grilled by";
break;
#ifdef MISSIONPACK
case MOD_PROXIMITY_MINE:
if( gender == GENDER_FEMALE ) {
message = "found her prox mine";
} else if ( gender == GENDER_NEUTER ) {
message = "found it's prox mine";
} else {
message = "found his prox mine";
}
break;
#endif
default:
if ( gender == GENDER_FEMALE )
message = "killed herself";
else if ( gender == GENDER_NEUTER )
message = "killed itself";
else
message = "killed himself";
break;
}
}
if (message) {
// Q3Rally Code Start - take out the space
if (message[0] == '\'')
CG_Printf( "%s%s.\n", targetName, message);
else
// Q3Rally Code END
CG_Printf( "%s %s.\n", targetName, message);
return;
}
// check for kill messages from the current clientNum
if ( attacker == cg.snap->ps.clientNum ) {
char *s;
if ( cgs.gametype < GT_TEAM ) {
s = va("You fragged %s\n%s place with %i", targetName,
CG_PlaceString( cg.snap->ps.persistant[PERS_RANK] + 1 ),
cg.snap->ps.persistant[PERS_SCORE] );
} else {
s = va("You fragged %s", targetName );
}
#ifdef MISSIONPACK
if (!(cg_singlePlayerActive.integer && cg_cameraOrbit.integer)) {
CG_CenterPrint( s, SCREEN_HEIGHT * 0.30, BIGCHAR_WIDTH );
}
#else
CG_CenterPrint( s, SCREEN_HEIGHT * 0.30, BIGCHAR_WIDTH );
#endif
// print the text message as well
}
// check for double client messages
if ( !attackerInfo ) {
attacker = ENTITYNUM_WORLD;
strcpy( attackerName, "noname" );
} else {
Q_strncpyz( attackerName, Info_ValueForKey( attackerInfo, "n" ), sizeof(attackerName) - 2);
strcat( attackerName, S_COLOR_WHITE );
// check for kill messages about the current clientNum
if ( target == cg.snap->ps.clientNum ) {
Q_strncpyz( cg.killerName, attackerName, sizeof( cg.killerName ) );
}
}
if ( attacker != ENTITYNUM_WORLD ) {
switch (mod) {
case MOD_GRAPPLE:
message = "was caught by";
break;
case MOD_GAUNTLET:
message = "was pummeled by";
break;
case MOD_MACHINEGUN:
message = "was machinegunned by";
break;
case MOD_SHOTGUN:
message = "was gunned down by";
break;
case MOD_GRENADE:
message = "ate";
message2 = "'s grenade";
break;
case MOD_GRENADE_SPLASH:
message = "was shredded by";
message2 = "'s shrapnel";
break;
case MOD_ROCKET:
message = "ate";
message2 = "'s rocket";
break;
case MOD_ROCKET_SPLASH:
message = "almost dodged";
message2 = "'s rocket";
break;
case MOD_PLASMA:
message = "was melted by";
message2 = "'s plasmagun";
break;
case MOD_PLASMA_SPLASH:
message = "was melted by";
message2 = "'s plasmagun";
break;
case MOD_RAILGUN:
message = "was railed by";
break;
case MOD_LIGHTNING:
message = "was electrocuted by";
break;
case MOD_BFG:
case MOD_BFG_SPLASH:
message = "was blasted by";
message2 = "'s BFG";
break;
#ifdef MISSIONPACK
case MOD_NAIL:
message = "was nailed by";
break;
case MOD_CHAINGUN:
message = "got lead poisoning from";
message2 = "'s Chaingun";
break;
case MOD_PROXIMITY_MINE:
message = "was too close to";
message2 = "'s Prox Mine";
break;
case MOD_KAMIKAZE:
message = "falls to";
message2 = "'s Kamikaze blast";
break;
case MOD_JUICED:
message = "was juiced by";
break;
#endif
case MOD_TELEFRAG:
message = "tried to invade";
message2 = "'s personal space";
break;
// Q3Rally Code Start
case MOD_BIOHAZARD:
message = "was melted by";
message2 = "'s patch of bio";
break;
case MOD_FIRE:
message = "was burnt by";
message2 = "'s trail of flame";
break;
case MOD_MINE:
message = "ran over";
message2 = "'s mine";
break;
case MOD_MINE_SPLASH:
message = "got too close to";
message2 = "'s mine";
break;
case MOD_CAR_COLLISION:
message = "got rammed by";
message2 = "'s vehicle";
break;
// Q3Rally Code END
default:
message = "was killed by";
break;
}
if (message) {
CG_Printf( "%s %s %s%s\n",
targetName, message, attackerName, message2);
return;
}
}
// we don't know what it was
CG_Printf( "%s died.\n", targetName );
}
//==========================================================================
/*
===============
CG_UseItem
===============
*/
static void CG_UseItem( centity_t *cent ) {
clientInfo_t *ci;
int itemNum, clientNum;
gitem_t *item;
entityState_t *es;
es = &cent->currentState;
itemNum = (es->event & ~EV_EVENT_BITS) - EV_USE_ITEM0;
if ( itemNum < 0 || itemNum > HI_NUM_HOLDABLE ) {
itemNum = 0;
}
// print a message if the local player
if ( es->number == cg.snap->ps.clientNum ) {
if ( !itemNum ) {
CG_CenterPrint( "No item to use", SCREEN_HEIGHT * 0.30, BIGCHAR_WIDTH );
} else {
item = BG_FindItemForHoldable( itemNum );
CG_CenterPrint( va("Use %s", item->pickup_name), SCREEN_HEIGHT * 0.30, BIGCHAR_WIDTH );
}
}
switch ( itemNum ) {
default:
case HI_NONE:
trap_S_StartSound (NULL, es->number, CHAN_BODY, cgs.media.useNothingSound );
break;
case HI_TELEPORTER:
break;
case HI_MEDKIT:
clientNum = cent->currentState.clientNum;
if ( clientNum >= 0 && clientNum < MAX_CLIENTS ) {
ci = &cgs.clientinfo[ clientNum ];
ci->medkitUsageTime = cg.time;
}
trap_S_StartSound (NULL, es->number, CHAN_BODY, cgs.media.medkitSound );
break;
#ifdef MISSIONPACK
case HI_KAMIKAZE:
break;
case HI_PORTAL:
break;
case HI_INVULNERABILITY:
trap_S_StartSound (NULL, es->number, CHAN_BODY, cgs.media.useInvulnerabilitySound );
break;
#endif
// Q3Rally Code Start
case HI_TURBO:
break;
// Q3Rally Code END
}
}
/*
================
CG_ItemPickup
A new item was picked up this frame
================
*/
static void CG_ItemPickup( int itemNum ) {
cg.itemPickup = itemNum;
cg.itemPickupTime = cg.time;
cg.itemPickupBlendTime = cg.time;
// see if it should be the grabbed weapon
if ( bg_itemlist[itemNum].giType == IT_WEAPON ) {
// select it immediately
// Q3Rally Code Start (improved auto switch)
if ( cg_autoswitch.integer && bg_itemlist[itemNum].giTag < RWP_SMOKE &&
bg_itemlist[itemNum].giTag > cg.weaponSelect ) {
cg.weaponSelectTime = cg.time;
cg.weaponSelect = bg_itemlist[itemNum].giTag;
}
/*
if ( cg_autoswitch.integer && bg_itemlist[itemNum].giTag != WP_MACHINEGUN ) {
cg.weaponSelectTime = cg.time;
cg.weaponSelect = bg_itemlist[itemNum].giTag;
}
*/
// Q3Rally Code END
}
}
/*
================
CG_PainEvent
Also called by playerstate transition
================
*/
void CG_PainEvent( centity_t *cent, int health ) {
// Q3Rally Code Start
// char *snd;
sfxHandle_t snd;
// Q3Rally Code END
// don't do more than two pain sounds a second
if ( cg.time - cent->pe.painTime < 500 ) {
return;
}
// Q3Rally Code Start
/*
if ( health < 25 ) {
snd = "*pain25_1.wav";
} else if ( health < 50 ) {
snd = "*pain50_1.wav";
} else if ( health < 75 ) {
snd = "*pain75_1.wav";
} else {
snd = "*pain100_1.wav";
}
trap_S_StartSound( NULL, cent->currentState.number, CHAN_VOICE,
CG_CustomSound( cent->currentState.number, snd ) );
*/
if ( health < 25 ) {
snd = cgs.media.damage25[(int)(random() * 2)];
} else if ( health < 50 ) {
snd = cgs.media.damage50[(int)(random() * 2)];
} else if ( health < 75 ) {
snd = cgs.media.damage75[(int)(random() * 2)];
} else {
snd = cgs.media.damage100[(int)(random() * 2)];
}
trap_S_StartSound( NULL, cent->currentState.number, CHAN_VOICE, snd );
// Q3Rally Code END
// save pain time for programitic twitch animation
cent->pe.painTime = cg.time;
cent->pe.painDirection ^= 1;
}
/*
==============
CG_EntityEvent
An entity has an event value
also called by CG_CheckPlayerstateEvents
==============
*/
#define DEBUGNAME(x) if(cg_debugEvents.integer){CG_Printf(x"\n");}
void CG_EntityEvent( centity_t *cent, vec3_t position ) {
entityState_t *es;
int event;
vec3_t dir;
const char *s;
int clientNum;
clientInfo_t *ci;
es = &cent->currentState;
event = es->event & ~EV_EVENT_BITS;
if ( cg_debugEvents.integer ) {
CG_Printf( "ent:%3i event:%3i ", es->number, event );
}
if ( !event ) {
DEBUGNAME("ZEROEVENT");
return;
}
clientNum = es->clientNum;
if ( clientNum < 0 || clientNum >= MAX_CLIENTS ) {
clientNum = 0;
}
ci = &cgs.clientinfo[ clientNum ];
switch ( event ) {
//
// movement generated events
//
case EV_FOOTSTEP:
DEBUGNAME("EV_FOOTSTEP");
// Q3Rally Code Start
/*
if (cg_footsteps.integer) {
trap_S_StartSound (NULL, es->number, CHAN_BODY,
cgs.media.footsteps[ ci->footsteps ][rand()&3] );
}
*/
// Q3Rally Code END
break;
case EV_FOOTSTEP_METAL:
DEBUGNAME("EV_FOOTSTEP_METAL");
// Q3Rally Code Start
/*
if (cg_footsteps.integer) {
trap_S_StartSound (NULL, es->number, CHAN_BODY,
cgs.media.footsteps[ FOOTSTEP_METAL ][rand()&3] );
}
*/
// Q3Rally Code END
break;
case EV_FOOTSPLASH:
DEBUGNAME("EV_FOOTSPLASH");
// Q3Rally Code Start
/*
if (cg_footsteps.integer) {
trap_S_StartSound (NULL, es->number, CHAN_BODY,
cgs.media.footsteps[ FOOTSTEP_SPLASH ][rand()&3] );
}
*/
// Q3Rally Code END
break;
case EV_FOOTWADE:
DEBUGNAME("EV_FOOTWADE");
// Q3Rally Code Start
/*
if (cg_footsteps.integer) {
trap_S_StartSound (NULL, es->number, CHAN_BODY,
cgs.media.footsteps[ FOOTSTEP_SPLASH ][rand()&3] );
}
*/
// Q3Rally Code END
break;
case EV_SWIM:
DEBUGNAME("EV_SWIM");
// Q3Rally Code Start
/*
if (cg_footsteps.integer) {
trap_S_StartSound (NULL, es->number, CHAN_BODY,
cgs.media.footsteps[ FOOTSTEP_SPLASH ][rand()&3] );
}
*/
// Q3Rally Code END
break;
case EV_FALL_SHORT:
DEBUGNAME("EV_FALL_SHORT");
trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.landSound );
if ( clientNum == cg.predictedPlayerState.clientNum ) {
// smooth landing z changes
cg.landChange = -8;
cg.landTime = cg.time;
}
break;
case EV_FALL_MEDIUM:
DEBUGNAME("EV_FALL_MEDIUM");
// use normal pain sound
trap_S_StartSound( NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "*pain100_1.wav" ) );
if ( clientNum == cg.predictedPlayerState.clientNum ) {
// smooth landing z changes
cg.landChange = -16;
cg.landTime = cg.time;
}
break;
case EV_FALL_FAR:
DEBUGNAME("EV_FALL_FAR");
// Q3Rally Code Start
// trap_S_StartSound (NULL, es->number, CHAN_AUTO, CG_CustomSound( es->number, "*fall1.wav" ) );
// Q3Rally Code END
cent->pe.painTime = cg.time; // don't play a pain sound right after this
if ( clientNum == cg.predictedPlayerState.clientNum ) {
// smooth landing z changes
cg.landChange = -24;
cg.landTime = cg.time;
}
break;
case EV_STEP_4:
case EV_STEP_8:
case EV_STEP_12:
case EV_STEP_16: // smooth out step up transitions
DEBUGNAME("EV_STEP");
{
float oldStep;
int delta;
int step;
if ( clientNum != cg.predictedPlayerState.clientNum ) {
break;
}
// if we are interpolating, we don't need to smooth steps
if ( cg.demoPlayback || (cg.snap->ps.pm_flags & PMF_FOLLOW) ||
cg_nopredict.integer || cg_synchronousClients.integer ) {
break;
}
// check for stepping up before a previous step is completed
delta = cg.time - cg.stepTime;
if (delta < STEP_TIME) {
oldStep = cg.stepChange * (STEP_TIME - delta) / STEP_TIME;
} else {
oldStep = 0;
}
// add this amount
step = 4 * (event - EV_STEP_4 + 1 );
cg.stepChange = oldStep + step;
if ( cg.stepChange > MAX_STEP_CHANGE ) {
cg.stepChange = MAX_STEP_CHANGE;
}
cg.stepTime = cg.time;
break;
}
case EV_JUMP_PAD:
DEBUGNAME("EV_JUMP_PAD");
// CG_Printf( "EV_JUMP_PAD w/effect #%i\n", es->eventParm );
{
localEntity_t *smoke;
vec3_t up = {0, 0, 1};
smoke = CG_SmokePuff( cent->lerpOrigin, up,
32,
1, 1, 1, 0.33f,
1000,
cg.time, 0,
LEF_PUFF_DONT_SCALE,
cgs.media.smokePuffShader );
}
// boing sound at origin, jump sound on player
trap_S_StartSound ( cent->lerpOrigin, -1, CHAN_VOICE, cgs.media.jumpPadSound );
// Q3Rally Code Start
// trap_S_StartSound (NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "*jump1.wav" ) );
// Q3Rally Code END
break;
case EV_JUMP:
DEBUGNAME("EV_JUMP");
// Q3Rally Code Start
// trap_S_StartSound (NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "*jump1.wav" ) );
// Q3Rally Code END
break;
case EV_TAUNT:
DEBUGNAME("EV_TAUNT");
// Q3Rally Code Start
// trap_S_StartSound (NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "*taunt.wav" ) );
// Q3Rally Code END
break;
#ifdef MISSIONPACK
case EV_TAUNT_YES:
DEBUGNAME("EV_TAUNT_YES");
CG_VoiceChatLocal(SAY_TEAM, qfalse, es->number, COLOR_CYAN, VOICECHAT_YES);
break;
case EV_TAUNT_NO:
DEBUGNAME("EV_TAUNT_NO");
CG_VoiceChatLocal(SAY_TEAM, qfalse, es->number, COLOR_CYAN, VOICECHAT_NO);
break;
case EV_TAUNT_FOLLOWME:
DEBUGNAME("EV_TAUNT_FOLLOWME");
CG_VoiceChatLocal(SAY_TEAM, qfalse, es->number, COLOR_CYAN, VOICECHAT_FOLLOWME);
break;
case EV_TAUNT_GETFLAG:
DEBUGNAME("EV_TAUNT_GETFLAG");
CG_VoiceChatLocal(SAY_TEAM, qfalse, es->number, COLOR_CYAN, VOICECHAT_ONGETFLAG);
break;
case EV_TAUNT_GUARDBASE:
DEBUGNAME("EV_TAUNT_GUARDBASE");
CG_VoiceChatLocal(SAY_TEAM, qfalse, es->number, COLOR_CYAN, VOICECHAT_ONDEFENSE);
break;
case EV_TAUNT_PATROL:
DEBUGNAME("EV_TAUNT_PATROL");
CG_VoiceChatLocal(SAY_TEAM, qfalse, es->number, COLOR_CYAN, VOICECHAT_ONPATROL);
break;
#endif
case EV_WATER_TOUCH:
DEBUGNAME("EV_WATER_TOUCH");
trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.watrInSound );
break;
case EV_WATER_LEAVE:
DEBUGNAME("EV_WATER_LEAVE");
trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.watrOutSound );
break;
case EV_WATER_UNDER:
DEBUGNAME("EV_WATER_UNDER");
trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.watrUnSound );
break;
case EV_WATER_CLEAR:
DEBUGNAME("EV_WATER_CLEAR");
// Q3Rally Code Start
// trap_S_StartSound (NULL, es->number, CHAN_AUTO, CG_CustomSound( es->number, "*gasp.wav" ) );
// Q3Rally Code END
break;
case EV_ITEM_PICKUP:
DEBUGNAME("EV_ITEM_PICKUP");
{
gitem_t *item;
int index;
index = es->eventParm; // player predicted
if ( index < 1 || index >= bg_numItems ) {
break;
}
item = &bg_itemlist[ index ];
// powerups and team items will have a separate global sound, this one
// will be played at prediction time
if ( item->giType == IT_POWERUP || item->giType == IT_TEAM ) {
trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.n_healthSound );
} else if (item->giType == IT_PERSISTANT_POWERUP) {
#ifdef MISSIONPACK
switch (item->giTag ) {
case PW_SCOUT:
trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.scoutSound );
break;
case PW_GUARD:
trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.guardSound );
break;
case PW_DOUBLER:
trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.doublerSound );
break;
case PW_AMMOREGEN:
trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.ammoregenSound );
break;
}
#endif
} else {
trap_S_StartSound (NULL, es->number, CHAN_AUTO, trap_S_RegisterSound( item->pickup_sound, qfalse ) );
}
// show icon and name on status bar
if ( es->number == cg.snap->ps.clientNum ) {
CG_ItemPickup( index );
}
}
break;
case EV_GLOBAL_ITEM_PICKUP:
DEBUGNAME("EV_GLOBAL_ITEM_PICKUP");
{
gitem_t *item;
int index;
index = es->eventParm; // player predicted
if ( index < 1 || index >= bg_numItems ) {
break;
}
item = &bg_itemlist[ index ];
// powerup pickups are global
if( item->pickup_sound ) {
trap_S_StartSound (NULL, cg.snap->ps.clientNum, CHAN_AUTO, trap_S_RegisterSound( item->pickup_sound, qfalse ) );
}
// show icon and name on status bar
if ( es->number == cg.snap->ps.clientNum ) {
CG_ItemPickup( index );
}
}
break;
//
// weapon events
//
case EV_NOAMMO:
DEBUGNAME("EV_NOAMMO");
// trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.noAmmoSound );
if ( es->number == cg.snap->ps.clientNum ) {
CG_OutOfAmmoChange();
}
break;
case EV_CHANGE_WEAPON:
DEBUGNAME("EV_CHANGE_WEAPON");
trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.selectSound );
break;
case EV_FIRE_WEAPON:
DEBUGNAME("EV_FIRE_WEAPON");
CG_FireWeapon( cent );
break;
case EV_ALTFIRE_WEAPON:
DEBUGNAME("EV_ALTFIRE_WEAPON")
CG_FireAltWeapon( cent );
break;
// Q3Rally Code Start
case EV_FIRE_REARWEAPON:
DEBUGNAME("EV_FIRE_REARWEAPON");
CG_FireRearWeapon( cent, es->eventParm );
break;
case EV_HAZARD:
DEBUGNAME("EV_HAZARD");
CG_Hazard( es->weapon, position, es->eventParm );
break;
// Q3Rally Code END
case EV_USE_ITEM0:
DEBUGNAME("EV_USE_ITEM0");
CG_UseItem( cent );
break;
case EV_USE_ITEM1:
DEBUGNAME("EV_USE_ITEM1");
CG_UseItem( cent );
break;
case EV_USE_ITEM2:
DEBUGNAME("EV_USE_ITEM2");
CG_UseItem( cent );
break;
case EV_USE_ITEM3:
DEBUGNAME("EV_USE_ITEM3");
CG_UseItem( cent );
break;
case EV_USE_ITEM4:
DEBUGNAME("EV_USE_ITEM4");
CG_UseItem( cent );
break;
case EV_USE_ITEM5:
DEBUGNAME("EV_USE_ITEM5");
CG_UseItem( cent );
break;
case EV_USE_ITEM6:
DEBUGNAME("EV_USE_ITEM6");
CG_UseItem( cent );
break;
case EV_USE_ITEM7:
DEBUGNAME("EV_USE_ITEM7");
CG_UseItem( cent );
break;
case EV_USE_ITEM8:
DEBUGNAME("EV_USE_ITEM8");
CG_UseItem( cent );
break;
case EV_USE_ITEM9:
DEBUGNAME("EV_USE_ITEM9");
CG_UseItem( cent );
break;
case EV_USE_ITEM10:
DEBUGNAME("EV_USE_ITEM10");
CG_UseItem( cent );
break;
case EV_USE_ITEM11:
DEBUGNAME("EV_USE_ITEM11");
CG_UseItem( cent );
break;
case EV_USE_ITEM12:
DEBUGNAME("EV_USE_ITEM12");
CG_UseItem( cent );
break;
case EV_USE_ITEM13:
DEBUGNAME("EV_USE_ITEM13");
CG_UseItem( cent );
break;
case EV_USE_ITEM14:
DEBUGNAME("EV_USE_ITEM14");
CG_UseItem( cent );
break;
//=================================================================
//
// other events
//
case EV_PLAYER_TELEPORT_IN:
DEBUGNAME("EV_PLAYER_TELEPORT_IN");
// Q3Rally Code Start
cg_entities[cent->currentState.clientNum].teleportFlag = 1;
cg_entities[cent->currentState.clientNum].wheelSkidding[0] = qfalse;
cg_entities[cent->currentState.clientNum].wheelSkidding[1] = qfalse;
cg_entities[cent->currentState.clientNum].wheelSkidding[2] = qfalse;
cg_entities[cent->currentState.clientNum].wheelSkidding[3] = qfalse;
// Q3Rally Code END
trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.teleInSound );
CG_SpawnEffect( position);
break;
case EV_PLAYER_TELEPORT_OUT:
DEBUGNAME("EV_PLAYER_TELEPORT_OUT");
trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.teleOutSound );
CG_SpawnEffect( position);
break;
case EV_ITEM_POP:
DEBUGNAME("EV_ITEM_POP");
trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.respawnSound );
break;
case EV_ITEM_RESPAWN:
DEBUGNAME("EV_ITEM_RESPAWN");
cent->miscTime = cg.time; // scale up from this
trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.respawnSound );
break;
case EV_GRENADE_BOUNCE:
DEBUGNAME("EV_GRENADE_BOUNCE");
if ( rand() & 1 ) {
trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.hgrenb1aSound );
} else {
trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.hgrenb2aSound );
}
break;
#ifdef MISSIONPACK
case EV_PROXIMITY_MINE_STICK:
DEBUGNAME("EV_PROXIMITY_MINE_STICK");
if( es->eventParm & SURF_FLESH ) {
trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.wstbimplSound );
} else if( es->eventParm & SURF_METALSTEPS ) {
trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.wstbimpmSound );
} else {
trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.wstbimpdSound );
}
break;
case EV_PROXIMITY_MINE_TRIGGER:
DEBUGNAME("EV_PROXIMITY_MINE_TRIGGER");
trap_S_StartSound (NULL, es->number, CHAN_AUTO, cgs.media.wstbactvSound );
break;
case EV_KAMIKAZE:
DEBUGNAME("EV_KAMIKAZE");
CG_KamikazeEffect( cent->lerpOrigin );
break;
case EV_OBELISKEXPLODE:
DEBUGNAME("EV_OBELISKEXPLODE");
CG_ObeliskExplode( cent->lerpOrigin, es->eventParm );
break;
case EV_OBELISKPAIN:
DEBUGNAME("EV_OBELISKPAIN");
CG_ObeliskPain( cent->lerpOrigin );
break;
case EV_INVUL_IMPACT:
DEBUGNAME("EV_INVUL_IMPACT");
CG_InvulnerabilityImpact( cent->lerpOrigin, cent->currentState.angles );
break;
case EV_JUICED:
DEBUGNAME("EV_JUICED");
CG_InvulnerabilityJuiced( cent->lerpOrigin );
break;
case EV_LIGHTNINGBOLT:
DEBUGNAME("EV_LIGHTNINGBOLT");
CG_LightningBoltBeam(es->origin2, es->pos.trBase);
break;
#endif
case EV_SCOREPLUM:
DEBUGNAME("EV_SCOREPLUM");
CG_ScorePlum( cent->currentState.otherEntityNum, cent->lerpOrigin, cent->currentState.time );
break;
//
// missile impacts
//
case EV_MISSILE_HIT:
DEBUGNAME("EV_MISSILE_HIT");
ByteToDir( es->eventParm, dir );
CG_MissileHitPlayer( es->weapon, position, dir, es->otherEntityNum );
break;
case EV_MISSILE_MISS:
DEBUGNAME("EV_MISSILE_MISS");
ByteToDir( es->eventParm, dir );
CG_MissileHitWall( es->weapon, 0, position, dir, IMPACTSOUND_DEFAULT );
break;
case EV_MISSILE_MISS_METAL:
DEBUGNAME("EV_MISSILE_MISS_METAL");
ByteToDir( es->eventParm, dir );
CG_MissileHitWall( es->weapon, 0, position, dir, IMPACTSOUND_METAL );
break;
case EV_RAILTRAIL:
DEBUGNAME("EV_RAILTRAIL");
cent->currentState.weapon = WP_RAILGUN;
// if the end was on a nomark surface, don't make an explosion
CG_RailTrail( ci, es->origin2, es->pos.trBase );
if ( es->eventParm != 255 ) {
ByteToDir( es->eventParm, dir );
CG_MissileHitWall( es->weapon, es->clientNum, position, dir, IMPACTSOUND_DEFAULT );
}
break;
case EV_BULLET_HIT_WALL:
DEBUGNAME("EV_BULLET_HIT_WALL");
ByteToDir( es->eventParm, dir );
CG_Bullet( es->pos.trBase, es->otherEntityNum, dir, qfalse, ENTITYNUM_WORLD );
break;
case EV_BULLET_HIT_FLESH:
DEBUGNAME("EV_BULLET_HIT_FLESH");
CG_Bullet( es->pos.trBase, es->otherEntityNum, dir, qtrue, es->eventParm );
break;
case EV_SHOTGUN:
DEBUGNAME("EV_SHOTGUN");
CG_ShotgunFire( es );
break;
case EV_GENERAL_SOUND:
DEBUGNAME("EV_GENERAL_SOUND");
if ( cgs.gameSounds[ es->eventParm ] ) {
trap_S_StartSound (NULL, es->number, CHAN_VOICE, cgs.gameSounds[ es->eventParm ] );
} else {
s = CG_ConfigString( CS_SOUNDS + es->eventParm );
trap_S_StartSound (NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, s ) );
}
break;
case EV_GLOBAL_SOUND: // play from the player's head so it never diminishes
DEBUGNAME("EV_GLOBAL_SOUND");
if ( cgs.gameSounds[ es->eventParm ] ) {
trap_S_StartSound (NULL, cg.snap->ps.clientNum, CHAN_AUTO, cgs.gameSounds[ es->eventParm ] );
} else {
s = CG_ConfigString( CS_SOUNDS + es->eventParm );
trap_S_StartSound (NULL, cg.snap->ps.clientNum, CHAN_AUTO, CG_CustomSound( es->number, s ) );
}
break;
case EV_GLOBAL_TEAM_SOUND: // play from the player's head so it never diminishes
{
DEBUGNAME("EV_GLOBAL_TEAM_SOUND");
switch( es->eventParm ) {
case GTS_RED_CAPTURE: // CTF: red team captured the blue flag, 1FCTF: red team captured the neutral flag
if ( cgs.clientinfo[cg.clientNum].team == TEAM_RED )
CG_AddBufferedSound( cgs.media.captureYourTeamSound );
else
CG_AddBufferedSound( cgs.media.captureOpponentSound );
break;
case GTS_BLUE_CAPTURE: // CTF: blue team captured the red flag, 1FCTF: blue team captured the neutral flag
if ( cgs.clientinfo[cg.clientNum].team == TEAM_BLUE )
CG_AddBufferedSound( cgs.media.captureYourTeamSound );
else
CG_AddBufferedSound( cgs.media.captureOpponentSound );
break;
case GTS_RED_RETURN: // CTF: blue flag returned, 1FCTF: never used
if ( cgs.clientinfo[cg.clientNum].team == TEAM_RED )
CG_AddBufferedSound( cgs.media.returnYourTeamSound );
else
CG_AddBufferedSound( cgs.media.returnOpponentSound );
//
CG_AddBufferedSound( cgs.media.blueFlagReturnedSound );
break;
case GTS_BLUE_RETURN: // CTF red flag returned, 1FCTF: neutral flag returned
if ( cgs.clientinfo[cg.clientNum].team == TEAM_BLUE )
CG_AddBufferedSound( cgs.media.returnYourTeamSound );
else
CG_AddBufferedSound( cgs.media.returnOpponentSound );
//
CG_AddBufferedSound( cgs.media.redFlagReturnedSound );
break;
case GTS_RED_TAKEN: // CTF: red team took blue flag, 1FCTF: blue team took the neutral flag
// if this player picked up the flag then a sound is played in CG_CheckLocalSounds
if (cg.snap->ps.powerups[PW_BLUEFLAG] || cg.snap->ps.powerups[PW_NEUTRALFLAG]) {
}
else {
if (cgs.clientinfo[cg.clientNum].team == TEAM_BLUE) {
#ifdef MISSIONPACK
if (cgs.gametype == GT_1FCTF)
CG_AddBufferedSound( cgs.media.yourTeamTookTheFlagSound );
else
#endif
CG_AddBufferedSound( cgs.media.enemyTookYourFlagSound );
}
else if (cgs.clientinfo[cg.clientNum].team == TEAM_RED) {
#ifdef MISSIONPACK
if (cgs.gametype == GT_1FCTF)
CG_AddBufferedSound( cgs.media.enemyTookTheFlagSound );
else
#endif
CG_AddBufferedSound( cgs.media.yourTeamTookEnemyFlagSound );
}
}
break;
case GTS_BLUE_TAKEN: // CTF: blue team took the red flag, 1FCTF red team took the neutral flag
// if this player picked up the flag then a sound is played in CG_CheckLocalSounds
if (cg.snap->ps.powerups[PW_REDFLAG] || cg.snap->ps.powerups[PW_NEUTRALFLAG]) {
}
else {
if (cgs.clientinfo[cg.clientNum].team == TEAM_RED) {
#ifdef MISSIONPACK
if (cgs.gametype == GT_1FCTF)
CG_AddBufferedSound( cgs.media.yourTeamTookTheFlagSound );
else
#endif
CG_AddBufferedSound( cgs.media.enemyTookYourFlagSound );
}
else if (cgs.clientinfo[cg.clientNum].team == TEAM_BLUE) {
#ifdef MISSIONPACK
if (cgs.gametype == GT_1FCTF)
CG_AddBufferedSound( cgs.media.enemyTookTheFlagSound );
else
#endif
CG_AddBufferedSound( cgs.media.yourTeamTookEnemyFlagSound );
}
}
break;
case GTS_REDOBELISK_ATTACKED: // Overload: red obelisk is being attacked
if (cgs.clientinfo[cg.clientNum].team == TEAM_RED) {
CG_AddBufferedSound( cgs.media.yourBaseIsUnderAttackSound );
}
break;
case GTS_BLUEOBELISK_ATTACKED: // Overload: blue obelisk is being attacked
if (cgs.clientinfo[cg.clientNum].team == TEAM_BLUE) {
CG_AddBufferedSound( cgs.media.yourBaseIsUnderAttackSound );
}
break;
case GTS_REDTEAM_SCORED:
CG_AddBufferedSound(cgs.media.redScoredSound);
break;
case GTS_BLUETEAM_SCORED:
CG_AddBufferedSound(cgs.media.blueScoredSound);
break;
case GTS_REDTEAM_TOOK_LEAD:
CG_AddBufferedSound(cgs.media.redLeadsSound);
break;
case GTS_BLUETEAM_TOOK_LEAD:
CG_AddBufferedSound(cgs.media.blueLeadsSound);
break;
case GTS_TEAMS_ARE_TIED:
CG_AddBufferedSound( cgs.media.teamsTiedSound );
break;
#ifdef MISSIONPACK
case GTS_KAMIKAZE:
trap_S_StartLocalSound(cgs.media.kamikazeFarSound, CHAN_ANNOUNCER);
break;
#endif
default:
break;
}
break;
}
case EV_PAIN:
// local player sounds are triggered in CG_CheckLocalSounds,
// so ignore events on the player
DEBUGNAME("EV_PAIN");
if ( cent->currentState.number != cg.snap->ps.clientNum ) {
CG_PainEvent( cent, es->eventParm );
}
break;
case EV_DEATH1:
case EV_DEATH2:
case EV_DEATH3:
DEBUGNAME("EV_DEATHx");
// Q3Rally Code Start
// trap_S_StartSound( NULL, es->number, CHAN_VOICE,
// CG_CustomSound( es->number, va("*death%i.wav", event - EV_DEATH1 + 1) ) );
trap_S_StartSound( NULL, es->number, CHAN_VOICE, cgs.media.death[(int)(random()*2)] );
// Q3Rally Code END
break;
case EV_OBITUARY:
DEBUGNAME("EV_OBITUARY");
CG_Obituary( es );
break;
//
// powerup events
//
case EV_POWERUP_QUAD:
DEBUGNAME("EV_POWERUP_QUAD");
if ( es->number == cg.snap->ps.clientNum ) {
cg.powerupActive = PW_QUAD;
cg.powerupTime = cg.time;
}
trap_S_StartSound (NULL, es->number, CHAN_ITEM, cgs.media.quadSound );
break;
case EV_POWERUP_BATTLESUIT:
DEBUGNAME("EV_POWERUP_BATTLESUIT");
if ( es->number == cg.snap->ps.clientNum ) {
cg.powerupActive = PW_BATTLESUIT;
cg.powerupTime = cg.time;
}
trap_S_StartSound (NULL, es->number, CHAN_ITEM, cgs.media.protectSound );
break;
// Q3Rally Code Start
case EV_POWERUP_SHIELD:
DEBUGNAME("EV_POWERUP_SHIELD");
if ( es->number == cg.snap->ps.clientNum ) {
cg.powerupActive = PW_SHIELD;
cg.powerupTime = cg.time;
}
trap_S_StartSound (NULL, es->number, CHAN_ITEM, cgs.media.shieldSound );
break;
// Q3Rally Code END
case EV_POWERUP_REGEN:
DEBUGNAME("EV_POWERUP_REGEN");
if ( es->number == cg.snap->ps.clientNum ) {
cg.powerupActive = PW_REGEN;
cg.powerupTime = cg.time;
}
trap_S_StartSound (NULL, es->number, CHAN_ITEM, cgs.media.regenSound );
break;
case EV_GIB_PLAYER:
DEBUGNAME("EV_GIB_PLAYER");
// don't play gib sound when using the kamikaze because it interferes
// with the kamikaze sound, downside is that the gib sound will also
// not be played when someone is gibbed while just carrying the kamikaze
if ( !(es->eFlags & EF_KAMIKAZE) ) {
// Q3Rally Code Start - explosion sound instead
// trap_S_StartSound( NULL, es->number, CHAN_BODY, cgs.media.gibSound );
trap_S_StartSound( NULL, es->number, CHAN_BODY, cgs.media.sfx_rockexp );
// Q3Rally Code END
}
// Q3Rally Code Start
// CG_GibPlayer( cent->lerpOrigin );
CG_GibPlayer( cent->lerpOrigin, cent->currentState.pos.trDelta, cent->currentState.clientNum );
// Q3Rally Code END
break;
case EV_STOPLOOPINGSOUND:
DEBUGNAME("EV_STOPLOOPINGSOUND");
trap_S_StopLoopingSound( es->number );
es->loopSound = 0;
break;
case EV_DEBUG_LINE:
DEBUGNAME("EV_DEBUG_LINE");
CG_Beam( cent );
break;
case EV_EMIT_DEBRIS_LIGHT:
DEBUGNAME("EV_EMIT_DEBRIS_LIGHT");
CG_ShowDebris( cent->lerpOrigin, es->eventParm, EV_EMIT_DEBRIS_LIGHT );
break;
case EV_EMIT_DEBRIS_DARK:
DEBUGNAME("EV_EMIT_DEBRIS_DARK");
CG_ShowDebris( cent->lerpOrigin, es->eventParm, EV_EMIT_DEBRIS_DARK );
break;
case EV_EMIT_DEBRIS_LIGHT_LARGE:
DEBUGNAME("EV_EMIT_DEBRIS_LIGHT_LARGE");
CG_ShowDebris( cent->lerpOrigin, es->eventParm, EV_EMIT_DEBRIS_LIGHT_LARGE );
break;
case EV_EMIT_DEBRIS_DARK_LARGE:
DEBUGNAME("EV_EMIT_DEBRIS_DARK_LARGE");
CG_ShowDebris( cent->lerpOrigin, es->eventParm, EV_EMIT_DEBRIS_DARK_LARGE );
break;
case EV_EMIT_DEBRIS_WOOD:
DEBUGNAME("EV_EMIT_DEBRIS_WOOD");
CG_ShowDebris( cent->lerpOrigin, es->eventParm, EV_EMIT_DEBRIS_WOOD );
break;
case EV_EMIT_DEBRIS_FLESH:
DEBUGNAME("EV_EMIT_DEBRIS_FLESH");
CG_ShowDebris( cent->lerpOrigin, es->eventParm, EV_EMIT_DEBRIS_FLESH );
break;
case EV_EMIT_DEBRIS_GLASS:
DEBUGNAME("EV_EMIT_DEBRIS_GLASS");
CG_ShowDebris( cent->lerpOrigin, es->eventParm, EV_EMIT_DEBRIS_GLASS );
break;
case EV_EMIT_DEBRIS_STONE:
DEBUGNAME("EV_EMIT_DEBRIS_STONE");
CG_ShowDebris( cent->lerpOrigin, es->eventParm, EV_EMIT_DEBRIS_STONE );
break;
case EV_EARTHQUAKE:
DEBUGNAME("EV_EARTHQUAKE");
CG_StartEarthquake((es->eventParm & 0x0F) + 1, ((1 + ((es->eventParm & 0xF0) >> 4)) * 2000) + 1000);
break;
case EV_EXPLOSION:
DEBUGNAME("EV_EXPLOSION");
// show plume (if enabled)
if ( cg_oldRocket.integer == 0 ) {
dir[0] = 0;
dir[1] = 0;
dir[2] = 25;
CG_ParticleExplosion( "explode1", cent->lerpOrigin, dir, 1400, 20, 30 );
}
// show explosion
dir[0] = 0;
dir[1] = 0;
dir[2] = 0;
CG_MakeExplosion( cent->lerpOrigin, dir, cgs.media.dishFlashModel, cgs.media.rocketExplosionShader, 1000, qtrue );
break;
case EV_PARTICLES_GRAVITY:
DEBUGNAME("EV_PARTICLES_GRAVITY");
CG_ParticlesFromEntityState( cent->lerpOrigin, PT_GRAVITY, es );
break;
case EV_PARTICLES_LINEAR:
DEBUGNAME("EV_PARTICLES_LINEAR");
CG_ParticlesFromEntityState( cent->lerpOrigin, PT_LINEAR_BOTH, es );
break;
case EV_PARTICLES_LINEAR_UP:
DEBUGNAME("EV_PARTICLES_LINEAR_UP");
CG_ParticlesFromEntityState( cent->lerpOrigin, PT_LINEAR_UP, es );
break;
case EV_PARTICLES_LINEAR_DOWN:
DEBUGNAME("EV_PARTICLES_LINEAR_DOWN");
CG_ParticlesFromEntityState( cent->lerpOrigin, PT_LINEAR_DOWN, es );
break;
default:
DEBUGNAME("UNKNOWN");
CG_Error( "Unknown event: %i", event );
break;
}
}
/*
==============
CG_CheckEvents
==============
*/
void CG_CheckEvents( centity_t *cent ) {
// check for event-only entities
if ( cent->currentState.eType > ET_EVENTS ) {
if ( cent->previousEvent ) {
return; // already fired
}
// if this is a player event set the entity number of the client entity number
if ( cent->currentState.eFlags & EF_PLAYER_EVENT ) {
cent->currentState.number = cent->currentState.otherEntityNum;
}
cent->previousEvent = 1;
cent->currentState.event = cent->currentState.eType - ET_EVENTS;
} else {
// check for events riding with another entity
if ( cent->currentState.event == cent->previousEvent ) {
return;
}
cent->previousEvent = cent->currentState.event;
if ( ( cent->currentState.event & ~EV_EVENT_BITS ) == 0 ) {
return;
}
}
// calculate the position at exactly the frame time
BG_EvaluateTrajectory( &cent->currentState.pos, cg.snap->serverTime, cent->lerpOrigin );
CG_SetEntitySoundPosition( cent );
CG_EntityEvent( cent, cent->lerpOrigin );
}