summaryrefslogtreecommitdiff
path: root/src/cgame/cg_event.c
diff options
context:
space:
mode:
authorMikko Tiusanen <ams@daug.net>2014-05-04 01:18:52 +0300
committerMikko Tiusanen <ams@daug.net>2014-05-04 01:18:52 +0300
commit01beb9919b95479d8be040bec74abc5cc67a5e43 (patch)
tree65f0b79e793848491832756a4c3a32b23668fab3 /src/cgame/cg_event.c
parent191d731da136b7ee959a17e63111c9146219a768 (diff)
Initial import.
Diffstat (limited to 'src/cgame/cg_event.c')
-rw-r--r--src/cgame/cg_event.c1305
1 files changed, 1305 insertions, 0 deletions
diff --git a/src/cgame/cg_event.c b/src/cgame/cg_event.c
new file mode 100644
index 0000000..ddce6a6
--- /dev/null
+++ b/src/cgame/cg_event.c
@@ -0,0 +1,1305 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+Copyright (C) 2000-2009 Darklegion Development
+
+This file is part of Tremulous.
+
+Tremulous 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.
+
+Tremulous 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 Tremulous; 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"
+
+/*
+=============
+CG_Obituary
+=============
+*/
+static void CG_Obituary( entityState_t *ent )
+{
+ int mod;
+ int target, attacker;
+ char *message;
+ char *message2;
+ const char *targetInfo;
+ const char *attackerInfo;
+ char targetName[ MAX_NAME_LENGTH ];
+ char attackerName[ MAX_NAME_LENGTH ];
+ char className[ 64 ];
+ gender_t gender;
+ clientInfo_t *ci;
+ qboolean teamKill = qfalse;
+
+ 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 ];
+ gender = ci->gender;
+
+ if( attacker < 0 || attacker >= MAX_CLIENTS )
+ {
+ attacker = ENTITYNUM_WORLD;
+ attackerInfo = NULL;
+ }
+ else
+ {
+ attackerInfo = CG_ConfigString( CS_PLAYERS + attacker );
+ if( ci && cgs.clientinfo[ attacker ].team == ci->team )
+ teamKill = qtrue;
+ }
+
+ targetInfo = CG_ConfigString( CS_PLAYERS + target );
+
+ if( !targetInfo )
+ return;
+
+ Q_strncpyz( targetName, Info_ValueForKey( targetInfo, "n" ), sizeof( targetName ));
+
+ message2 = "";
+
+ // check for single client messages
+
+ switch( mod )
+ {
+ case MOD_FALLING:
+ message = "^5fell fowl to gravity";
+ break;
+ case MOD_CRUSH:
+ message = "^5was squished";
+ break;
+ case MOD_WATER:
+ message = "^5forgot to pack a snorkel";
+ break;
+ case MOD_SLIME:
+ message = "^5was sucked by an infestation slime";
+ break;
+ case MOD_LAVA:
+ message = "^5did a back flip into the lava";
+ break;
+ case MOD_TARGET_LASER:
+ message = "^5saw the light";
+ break;
+ case MOD_TRIGGER_HURT:
+ message = "^5was in the wrong place";
+ break;
+ case MOD_HSPAWN:
+ message = "^5should have run further";
+ break;
+ case MOD_ASPAWN:
+ message = "^5shouldn't have trod in the acid";
+ break;
+ case MOD_MGTURRET:
+ message = "^5was gunned down by a turret";
+ break;
+ case MOD_MGTURRET2:
+ message = "^5was roasted by a flame turret";
+ break;
+ case MOD_TESLAGEN:
+ message = "^5was zapped by a tesla generator";
+ break;
+ case MOD_ATUBE:
+ message = "^5was melted by an acid tube";
+ break;
+ case MOD_OVERMIND:
+ message = "^5got too close to the overmind";
+ break;
+ case MOD_REACTOR:
+ message = "^5got too close to the reactor";
+ break;
+ case MOD_SLOWBLOB:
+ message = "^5should have visited a medical station";
+ break;
+ case MOD_SWARM:
+ message = "^5was hunted down by the swarm";
+ break;
+ case MOD_SPITEFUL_ABCESS:
+ message = "^5was raped by a Spiteful Abcess";
+ break;
+ default:
+ message = NULL;
+ break;
+ }
+
+ if( !message && attacker == target )
+ {
+ switch( mod )
+ {
+ case MOD_FLAMER_SPLASH:
+ if( gender == GENDER_FEMALE )
+ message = "^5toasted herself";
+ else if( gender == GENDER_NEUTER )
+ message = "^5toasted itself";
+ else
+ message = "^5toasted himself";
+ break;
+
+ case MOD_LCANNON_SPLASH:
+ if( gender == GENDER_FEMALE )
+ message = "^5irradiated herself";
+ else if( gender == GENDER_NEUTER )
+ message = "^5irradiated itself";
+ else
+ message = "^5irradiated himself";
+ break;
+
+ case MOD_PSAWBLADE:
+ if( gender == GENDER_FEMALE )
+ message = "^5sliced herself up";
+ else if( gender == GENDER_NEUTER )
+ message = "^5sliced itself up";
+ else
+ message = "^5sliced himself up";
+ break;
+
+ case MOD_MD2:
+ if( gender == GENDER_FEMALE )
+ message = "^5 vaporized herself up";
+ else if( gender == GENDER_NEUTER )
+ message = "^vaporized itself up";
+ else
+ message = "^vaporized himself up";
+ break;
+
+ case MOD_GRENADE:
+ if( gender == GENDER_FEMALE )
+ message = "^5blew herself up";
+ else if( gender == GENDER_NEUTER )
+ message = "^5blew itself up";
+ else
+ message = "^5blew himself up";
+ break;
+
+ case MOD_LEVEL3_BOUNCEBALL:
+ if( gender == GENDER_FEMALE )
+ message = "^5sniped herself";
+ else if( gender == GENDER_NEUTER )
+ message = "^5sniped itself";
+ else
+ message = "^5sniped himself";
+ break;
+
+ case MOD_LEVEL5_PRICKLES:
+ if( gender == GENDER_FEMALE )
+ message = "^5spiked herself";
+ else if( gender == GENDER_NEUTER )
+ message = "^5spiked itself";
+ else
+ message = "^5spiked himself";
+ break;
+
+ case MOD_PRIFLE:
+ if( gender == GENDER_FEMALE )
+ message = "^5pulse rifled herself";
+ else if( gender == GENDER_NEUTER )
+ message = "^5pulse rifled itself";
+ else
+ message = "^5pulse rifled himself";
+ break;
+
+
+ case MOD_MINE:
+ if( gender == GENDER_FEMALE )
+ message = "^5was betrayed by own mine";
+ else if( gender == GENDER_NEUTER )
+ message = "^5it betrayed by own mine";
+ else
+ message = "^5was betrayed by own mine";
+ break;
+
+ case MOD_FLAMES:
+ if( gender == GENDER_FEMALE )
+ message = "^5was terminated by own flames";
+ else if( gender == GENDER_NEUTER )
+ message = "^5it terminated by own flames";
+ else
+ message = "^5was terminated by own flames";
+ break;
+
+ case MOD_SMOKE:
+ message = "^5smoked himself up";
+ break;
+ case MOD_ABOMB:
+ message = "^5bombed himself up";
+ break;
+
+ default:
+ if( gender == GENDER_FEMALE )
+ message = "^5killed herself";
+ else if( gender == GENDER_NEUTER )
+ message = "^5killed itself";
+ else
+ message = "^5killed himself";
+ break;
+ }
+ }
+
+ if( message )
+ {
+ CG_Printf( "%s" S_COLOR_WHITE " %s\n", targetName, message );
+ return;
+ }
+
+ // check for double client messages
+ if( !attackerInfo )
+ {
+ attacker = ENTITYNUM_WORLD;
+ strcpy( attackerName, "noname" );
+ }
+ else
+ {
+ Q_strncpyz( attackerName, Info_ValueForKey( attackerInfo, "n" ), sizeof( attackerName ));
+ // 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_PAINSAW:
+ message = "^5was sawn by^7";
+ break;
+ case MOD_BLASTER:
+ message = "^5was blasted by^7";
+ break;
+ case MOD_MACHINEGUN:
+ message = "^5was machinegunned by^7";
+ break;
+ case MOD_CHAINGUN:
+ message = "^5was chaingunned by^7";
+ break;
+ case MOD_SHOTGUN:
+ message = "^5was gunned down by^7";
+ break;
+ case MOD_PRIFLE:
+ message = "^5was pulse rifled by^7";
+ break;
+ case MOD_MDRIVER:
+ message = "^5was mass driven by^7";
+ break;
+ case MOD_MD2:
+ message = "^5was vaporized by^7";
+ break;
+ case MOD_LASGUN:
+ message = "^5was lasgunned by^7";
+ break;
+ case MOD_FLAMER:
+ message = "^5was grilled by^7";
+ message2 = "^5's ^5flamer";
+ break;
+ case MOD_FLAMER_SPLASH:
+ message = "^5was toasted by^7";
+ message2 = "^5's ^5flamer";
+ break;
+ case MOD_LCANNON:
+ message = "^5felt the full force of^7";
+ message2 = "^5's ^5lucifer cannon";
+ break;
+ case MOD_LCANNON_SPLASH:
+ message = "^5was caught in the fallout of^7";
+ message2 = "^5's ^5lucifer cannon";
+ break;
+ case MOD_GRENADE:
+ message = "^5couldn't escape^7";
+ message2 = "^5's ^5grenade";
+ break;
+ case MOD_PSAWBLADE:
+ message = "^5was sliced by^7";
+ message2 = "^5's ^5blades";
+ break;
+ case MOD_MINE:
+ message = "^5found^7";
+ message2 = "^5's ^5mine";
+ break;
+
+
+ case MOD_FLAMES:
+ message = "^5tasted^7";
+ message2 = "^5's ^5flames";
+ break;
+
+ case MOD_SMOKE:
+ message = "^5tasted^7";
+ message2 = "^5's ^5smoke";
+ break;
+
+ case MOD_ABUILDER_CLAW:
+ message = "^5should leave^7";
+ message2 = "^5's ^5buildings alone";
+ break;
+ case MOD_LEVEL0_BITE:
+ message = "^5was bitten by^7";
+ break;
+ case MOD_LEVEL1_CLAW:
+ message = "^5was swiped by^7";
+ Com_sprintf( className, 64, "^5's %s",
+ BG_ClassConfig( PCL_ALIEN_LEVEL1 )->humanName );
+ message2 = className;
+ break;
+ case MOD_LEVEL2_CLAW:
+ message = "^5was clawed by^7";
+ Com_sprintf( className, 64, "^5's %s",
+ BG_ClassConfig( PCL_ALIEN_LEVEL2 )->humanName );
+ message2 = className;
+ break;
+ case MOD_LEVEL2_ZAP:
+ message = "^5was zapped by^7";
+ Com_sprintf( className, 64, "^5's %s",
+ BG_ClassConfig( PCL_ALIEN_LEVEL2 )->humanName );
+ message2 = className;
+ break;
+ case MOD_LEVEL2_BOUNCEBALL:
+ message = "^5was sniped by^7";
+ Com_sprintf( className, 64, "^5's %s",
+ BG_ClassConfig( PCL_ALIEN_LEVEL2 )->humanName );
+ message2 = className;
+ break;
+ case MOD_LEVEL5_CLAW:
+ message = "^5was sliced by^7";
+ Com_sprintf( className, 64, "^5's %s",
+ BG_ClassConfig( PCL_ALIEN_LEVEL5 )->humanName );
+ message2 = className;
+ break;
+ case MOD_LEVEL5_ZAP:
+ message = "^5was zapped by^7";
+ Com_sprintf( className, 64, "^5's %s",
+ BG_ClassConfig( PCL_ALIEN_LEVEL5 )->humanName );
+ message2 = className;
+ break;
+ case MOD_LEVEL5_BOUNCEBALL:
+ message = "^5was sniped by^7";
+ Com_sprintf( className, 64, "^5's %s",
+ BG_ClassConfig( PCL_ALIEN_LEVEL5 )->humanName );
+ message2 = className;
+ break;
+ case MOD_LEVEL3_CLAW:
+ message = "^5was chomped by^7";
+ Com_sprintf( className, 64, "^5's %s",
+ BG_ClassConfig( PCL_ALIEN_LEVEL3 )->humanName );
+ message2 = className;
+ break;
+ case MOD_LEVEL3_POUNCE:
+ message = "^5was pounced upon by^7";
+ Com_sprintf( className, 64, "^5's %s",
+ BG_ClassConfig( PCL_ALIEN_LEVEL3 )->humanName );
+ message2 = className;
+ break;
+
+ case MOD_LEVEL5_POUNCE:
+ message = "^5was air pounced upon by^7";
+ Com_sprintf( className, 64, "^5's %s",
+ BG_ClassConfig( PCL_ALIEN_LEVEL5 )->humanName );
+ message2 = className;
+ break;
+
+ case MOD_LEVEL5_PRICKLES:
+ message = "^5was spiked by^7";
+ Com_sprintf( className, 64, "^5's %s",
+ BG_ClassConfig( PCL_ALIEN_LEVEL5 )->humanName );
+ message2 = className;
+ break;
+
+ case MOD_LEVEL3_BOUNCEBALL:
+ message = "^5was sniped by^7";
+ Com_sprintf( className, 64, "^5's %s",
+ BG_ClassConfig( PCL_ALIEN_LEVEL3 )->humanName );
+ message2 = className;
+ break;
+ case MOD_LEVEL4_CLAW:
+ message = "^5was mauled by^7";
+ Com_sprintf( className, 64, "^5's %s",
+ BG_ClassConfig( PCL_ALIEN_LEVEL4 )->humanName );
+ message2 = className;
+ break;
+ case MOD_LEVEL4_TRAMPLE:
+ message = "^5should have gotten out of the way of^7";
+ Com_sprintf( className, 64, "^5's %s",
+ BG_ClassConfig( PCL_ALIEN_LEVEL4 )->humanName );
+ message2 = className;
+ break;
+ case MOD_LEVEL4_CRUSH:
+ message = "^5was crushed under^7";
+ message2 = "^5's weight";
+ break;
+
+ case MOD_POISON:
+ message = "^5should have used a medkit against^7";
+ message2 = "^5's poison";
+ break;
+ case MOD_INFECTION:
+ message = "^5got infected by^7";
+ break;
+ case MOD_LEVEL1_PCLOUD:
+ message = "^5was gassed by^7";
+ Com_sprintf( className, 64, "^5's %s",
+ BG_ClassConfig( PCL_ALIEN_LEVEL1 )->humanName );
+ message2 = className;
+ break;
+
+
+ case MOD_TELEFRAG:
+ message = "^5tried to invade^7";
+ message2 = "^5's personal space";
+ break;
+
+ case MOD_ABOMB:
+ message = "^5was bombed by^7";
+ Com_sprintf( className, 64, "^5's %s",
+ BG_ClassConfig( PCL_ALIEN_LEVEL1 )->humanName );
+ message2 = className;
+ break;
+
+ default:
+ message = "^5was killed by^7";
+ break;
+ }
+
+ if( message )
+ {
+ CG_Printf( "%s" S_COLOR_WHITE " %s %s%s" S_COLOR_WHITE "%s\n",
+ targetName, message,
+ ( teamKill ) ? S_COLOR_RED "TEAMMATE " S_COLOR_WHITE : "",
+ attackerName, message2 );
+ if( teamKill && attacker == cg.clientNum )
+ {
+ CG_CenterPrint( va ( "You killed " S_COLOR_RED "TEAMMATE "
+ S_COLOR_WHITE "%s", targetName ),
+ SCREEN_HEIGHT * 0.30, BIGCHAR_WIDTH );
+ }
+ return;
+ }
+ }
+
+ // we don't know what it was
+ CG_Printf( "%s" S_COLOR_CYAN " ^5died\n", targetName );
+}
+
+
+
+//==========================================================================
+
+/*
+================
+CG_PainEvent
+
+Also called by playerstate transition
+================
+*/
+void CG_PainEvent( centity_t *cent, int health )
+{
+ char *snd;
+
+ // don't do more than two pain sounds a second
+ if( cg.time - cent->pe.painTime < 500 )
+ return;
+
+ 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 ) );
+
+ // save pain time for programitic twitch animation
+ cent->pe.painTime = cg.time;
+ cent->pe.painDirection ^= 1;
+}
+
+/*
+=========================
+CG_Level2Zap
+=========================
+*/
+static void CG_Level2Zap( entityState_t *es )
+{
+ int i;
+ centity_t *source = NULL, *target = NULL;
+
+ if( es->misc < 0 || es->misc >= MAX_CLIENTS )
+ return;
+
+ source = &cg_entities[ es->misc ];
+ for( i = 0; i <= 2; i++ )
+ {
+ switch( i )
+ {
+ case 0:
+ if( es->time <= 0 )
+ continue;
+
+ target = &cg_entities[ es->time ];
+ break;
+
+ case 1:
+ if( es->time2 <= 0 )
+ continue;
+
+ target = &cg_entities[ es->time2 ];
+ break;
+
+ case 2:
+ if( es->constantLight <= 0 )
+ continue;
+
+ target = &cg_entities[ es->constantLight ];
+ break;
+ }
+
+ if( !CG_IsTrailSystemValid( &source->level2ZapTS[ i ] ) )
+ source->level2ZapTS[ i ] = CG_SpawnNewTrailSystem( cgs.media.level2ZapTS );
+
+ if( CG_IsTrailSystemValid( &source->level2ZapTS[ i ] ) )
+ {
+ CG_SetAttachmentCent( &source->level2ZapTS[ i ]->frontAttachment, source );
+ CG_SetAttachmentCent( &source->level2ZapTS[ i ]->backAttachment, target );
+ CG_AttachToCent( &source->level2ZapTS[ i ]->frontAttachment );
+ CG_AttachToCent( &source->level2ZapTS[ i ]->backAttachment );
+ }
+ }
+ source->level2ZapTime = cg.time;
+}
+
+/*
+==============
+CG_EntityEvent
+
+An entity has an event value
+also called by CG_CheckPlayerstateEvents
+==============
+*/
+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;
+ int steptime;
+
+ if( cg.snap->ps.persistant[ PERS_SPECSTATE ] != SPECTATOR_NOT )
+ steptime = 200;
+ else
+ steptime = BG_Class( cg.snap->ps.stats[ STAT_CLASS ] )->steptime;
+
+ es = &cent->currentState;
+ event = es->event & ~EV_EVENT_BITS;
+
+ if( cg_debugEvents.integer )
+ CG_Printf( "ent:%3i event:%3i %s\n", es->number, event,
+ BG_EventName( event ) );
+
+ if( !event )
+ return;
+
+ clientNum = es->clientNum;
+ if( clientNum < 0 || clientNum >= MAX_CLIENTS )
+ clientNum = 0;
+
+ ci = &cgs.clientinfo[ clientNum ];
+
+ switch( event )
+ {
+ //
+ // movement generated events
+ //
+ case EV_FOOTSTEP:
+ if( cg_footsteps.integer && ci->footsteps != FOOTSTEP_NONE )
+ {
+ if( ci->footsteps == FOOTSTEP_CUSTOM )
+ trap_S_StartSound( NULL, es->number, CHAN_BODY,
+ ci->customFootsteps[ rand( ) & 3 ] );
+ else
+ trap_S_StartSound( NULL, es->number, CHAN_BODY,
+ cgs.media.footsteps[ ci->footsteps ][ rand( ) & 3 ] );
+ }
+ break;
+
+ case EV_FOOTSTEP_METAL:
+ if( cg_footsteps.integer && ci->footsteps != FOOTSTEP_NONE )
+ {
+ if( ci->footsteps == FOOTSTEP_CUSTOM )
+ trap_S_StartSound( NULL, es->number, CHAN_BODY,
+ ci->customMetalFootsteps[ rand( ) & 3 ] );
+ else
+ trap_S_StartSound( NULL, es->number, CHAN_BODY,
+ cgs.media.footsteps[ FOOTSTEP_METAL ][ rand( ) & 3 ] );
+ }
+ break;
+
+ case EV_FOOTSTEP_SQUELCH:
+ if( cg_footsteps.integer && ci->footsteps != FOOTSTEP_NONE )
+ {
+ trap_S_StartSound( NULL, es->number, CHAN_BODY,
+ cgs.media.footsteps[ FOOTSTEP_FLESH ][ rand( ) & 3 ] );
+ }
+ break;
+
+ case EV_FOOTSPLASH:
+ if( cg_footsteps.integer && ci->footsteps != FOOTSTEP_NONE )
+ {
+ trap_S_StartSound( NULL, es->number, CHAN_BODY,
+ cgs.media.footsteps[ FOOTSTEP_SPLASH ][ rand( ) & 3 ] );
+ }
+ break;
+
+ case EV_FOOTWADE:
+ if( cg_footsteps.integer && ci->footsteps != FOOTSTEP_NONE )
+ {
+ trap_S_StartSound( NULL, es->number, CHAN_BODY,
+ cgs.media.footsteps[ FOOTSTEP_SPLASH ][ rand( ) & 3 ] );
+ }
+ break;
+
+ case EV_SWIM:
+ if( cg_footsteps.integer && ci->footsteps != FOOTSTEP_NONE )
+ {
+ trap_S_StartSound( NULL, es->number, CHAN_BODY,
+ cgs.media.footsteps[ FOOTSTEP_SPLASH ][ rand( ) & 3 ] );
+ }
+ break;
+
+
+ case 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:
+ // 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:
+ trap_S_StartSound (NULL, es->number, CHAN_AUTO, CG_CustomSound( es->number, "*fall1.wav" ) );
+ 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_FALLING:
+ trap_S_StartSound( NULL, es->number, CHAN_AUTO, CG_CustomSound( es->number, "*falling1.wav" ) );
+ break;
+
+ case EV_STEP_4:
+ case EV_STEP_8:
+ case EV_STEP_12:
+ case EV_STEP_16: // smooth out step up transitions
+ case EV_STEPDN_4:
+ case EV_STEPDN_8:
+ case EV_STEPDN_12:
+ case EV_STEPDN_16: // smooth out step down transitions
+ {
+ 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 < steptime )
+ oldStep = cg.stepChange * ( steptime - delta ) / steptime;
+ else
+ oldStep = 0;
+
+ // add this amount
+ if( event >= EV_STEPDN_4 )
+ {
+ step = 4 * ( event - EV_STEPDN_4 + 1 );
+ cg.stepChange = oldStep - step;
+ }
+ else
+ {
+ step = 4 * ( event - EV_STEP_4 + 1 );
+ cg.stepChange = oldStep + step;
+ }
+
+ if( cg.stepChange > MAX_STEP_CHANGE )
+ cg.stepChange = MAX_STEP_CHANGE;
+ else if( cg.stepChange < -MAX_STEP_CHANGE )
+ cg.stepChange = -MAX_STEP_CHANGE;
+
+ cg.stepTime = cg.time;
+ break;
+ }
+
+ case EV_JUMP:
+ trap_S_StartSound( NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "*jump1.wav" ) );
+
+ if( BG_ClassHasAbility( cg.predictedPlayerState.stats[ STAT_CLASS ], SCA_WALLJUMPER ) )
+ {
+ vec3_t surfNormal, refNormal = { 0.0f, 0.0f, 1.0f };
+ vec3_t is;
+
+ if( clientNum != cg.predictedPlayerState.clientNum )
+ break;
+
+ //set surfNormal
+ VectorCopy( cg.predictedPlayerState.grapplePoint, surfNormal );
+
+ //if we are moving from one surface to another smooth the transition
+ if( !VectorCompare( surfNormal, cg.lastNormal ) && surfNormal[ 2 ] != 1.0f )
+ {
+ CrossProduct( refNormal, surfNormal, is );
+ VectorNormalize( is );
+
+ //add the op
+ CG_addSmoothOp( is, 15.0f, 1.0f );
+ }
+
+ //copy the current normal to the lastNormal
+ VectorCopy( surfNormal, cg.lastNormal );
+ }
+
+ break;
+
+ case EV_AIRPOUNCE:
+ trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.airpounce);
+ //airpounce gfx effect
+ {
+ particleSystem_t *ps = CG_SpawnNewParticleSystem( cgs.media.airpounceblast );
+
+ if( CG_IsParticleSystemValid( &ps ) )
+ {
+ CG_SetAttachmentCent( &ps->attachment, cent );
+ CG_AttachToCent( &ps->attachment );
+ }
+ }
+
+ if( BG_ClassHasAbility( cg.predictedPlayerState.stats[ STAT_CLASS ], SCA_WALLJUMPER ) )
+ {
+ vec3_t surfNormal, refNormal = { 0.0f, 0.0f, 1.0f };
+ vec3_t is;
+
+ if( clientNum != cg.predictedPlayerState.clientNum )
+ break;
+
+ //set surfNormal
+ VectorCopy( cg.predictedPlayerState.grapplePoint, surfNormal );
+
+ //if we are moving from one surface to another smooth the transition
+ if( !VectorCompare( surfNormal, cg.lastNormal ) && surfNormal[ 2 ] != 1.0f )
+ {
+ CrossProduct( refNormal, surfNormal, is );
+ VectorNormalize( is );
+
+ //add the op
+ CG_addSmoothOp( is, 15.0f, 1.0f );
+ }
+
+ //copy the current normal to the lastNormal
+ VectorCopy( surfNormal, cg.lastNormal );
+ }
+
+ break;
+
+ case EV_LEV1_GRAB:
+ trap_S_StartSound( NULL, es->number, CHAN_VOICE, cgs.media.alienL1Grab );
+ break;
+
+ case EV_LEV4_TRAMPLE_PREPARE:
+ trap_S_StartSound( NULL, es->number, CHAN_VOICE, cgs.media.alienL4ChargePrepare );
+ break;
+
+ case EV_LEV4_TRAMPLE_START:
+ //FIXME: stop cgs.media.alienL4ChargePrepare playing here
+ trap_S_StartSound( NULL, es->number, CHAN_VOICE, cgs.media.alienL4ChargeStart );
+ break;
+
+ case EV_TAUNT:
+ if( !cg_noTaunt.integer )
+ trap_S_StartSound( NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "*taunt.wav" ) );
+ break;
+
+ case EV_HUMMEL:
+ trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.hummelSound );
+ break;
+
+ case EV_WATER_TOUCH:
+ trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.watrInSound );
+ break;
+
+ case EV_WATER_LEAVE:
+ trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.watrOutSound );
+ break;
+
+ case EV_WATER_UNDER:
+ trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.watrUnSound );
+ break;
+
+ case EV_WATER_CLEAR:
+ trap_S_StartSound( NULL, es->number, CHAN_AUTO, CG_CustomSound( es->number, "*gasp.wav" ) );
+ break;
+
+ //
+ // weapon events
+ //
+ case EV_NOAMMO:
+ trap_S_StartSound( NULL, es->number, CHAN_WEAPON,
+ cgs.media.weaponEmptyClick );
+ break;
+
+ case EV_CHANGE_WEAPON:
+ trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.selectSound );
+ break;
+
+ case EV_FIRE_WEAPON:
+ CG_FireWeapon( cent, WPM_PRIMARY );
+ break;
+
+ case EV_FIRE_WEAPON2:
+ CG_FireWeapon( cent, WPM_SECONDARY );
+ break;
+
+ case EV_FIRE_WEAPON3:
+ CG_FireWeapon( cent, WPM_TERTIARY );
+ break;
+
+ //=================================================================
+
+ //
+ // other events
+ //
+ case EV_PLAYER_TELEPORT_IN:
+ //deprecated
+ break;
+
+ case EV_PLAYER_TELEPORT_OUT:
+ CG_PlayerDisconnect( position );
+ break;
+
+ case EV_BUILD_CONSTRUCT:
+ //do something useful here
+ break;
+
+ case EV_BUILD_DESTROY:
+ //do something useful here
+ break;
+
+ case EV_RPTUSE_SOUND:
+ trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.repeaterUseSound );
+ break;
+
+ case EV_GRENADE_BOUNCE:
+ if( rand( ) & 1 )
+ trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.hardBounceSound1 );
+ else
+ trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.hardBounceSound2 );
+ break;
+
+ case EV_MINE_BOUNCE:
+ if( rand( ) & 1 )
+ trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.mineBounceSound1 );
+ else
+ trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.mineBounceSound1 );
+ break;
+ break;
+
+ case EV_ACIDBOMB_BOUNCE:
+ if( rand( ) & 1 )
+ trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.acidBombBounceSound1 );
+ else
+ trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.acidBombBounceSound2 );
+ break;
+
+ //
+ // missile impacts
+ //
+ case EV_MISSILE_HIT:
+ ByteToDir( es->eventParm, dir );
+ CG_MissileHitEntity( es->weapon, es->generic1, position, dir, es->otherEntityNum, es->torsoAnim );
+ break;
+
+ case EV_MISSILE_MISS:
+ ByteToDir( es->eventParm, dir );
+ CG_MissileHitWall( es->weapon, es->generic1, 0, position, dir, IMPACTSOUND_DEFAULT, es->torsoAnim );
+ break;
+
+ case EV_MISSILE_MISS_METAL:
+ ByteToDir( es->eventParm, dir );
+ CG_MissileHitWall( es->weapon, es->generic1, 0, position, dir, IMPACTSOUND_METAL, es->torsoAnim );
+ break;
+
+ case EV_HUMAN_BUILDABLE_EXPLOSION:
+ ByteToDir( es->eventParm, dir );
+ CG_HumanBuildableExplosion( position, dir, es->modelindex );
+ break;
+
+ case EV_ALIEN_BUILDABLE_EXPLOSION:
+ ByteToDir( es->eventParm, dir );
+ CG_AlienBuildableExplosion( position, dir, es->modelindex );
+
+ if ( es->modelindex == BA_A_SPITEFUL_ABCESS )
+ CG_AlienSPITEFUL_ABCESSExplosion( position, dir );
+ break;
+
+//Scleim greifer schwanz für slime
+ case EV_SLIMETRAIL:
+ cent->currentState.weapon = WP_NONE;
+ {
+ centity_t *source = &cg_entities[ es->generic1 ];
+ centity_t *target = &cg_entities[ es->clientNum ];
+ vec3_t sourceOffset = { 0.0f, 0.0f, 0.0f };
+
+ if( !CG_IsTrailSystemValid( &source->muzzleTS ) )
+ {
+ //trailsystem
+ source->muzzleTS = CG_SpawnNewTrailSystem( cgs.media.slimeTS );
+
+ if( CG_IsTrailSystemValid( &source->muzzleTS ) )
+ {
+ CG_SetAttachmentCent( &source->muzzleTS->frontAttachment, source );
+ CG_SetAttachmentCent( &source->muzzleTS->backAttachment, target );
+ CG_AttachToCent( &source->muzzleTS->frontAttachment );
+ CG_AttachToCent( &source->muzzleTS->backAttachment );
+ CG_SetAttachmentOffset( &source->muzzleTS->frontAttachment, sourceOffset );
+
+ source->muzzleTSDeathTime = cg.time + cg_teslaTrailTime.integer;
+ }
+ }
+ }
+ break;
+
+
+
+
+ case EV_TESLATRAIL:
+ cent->currentState.weapon = WP_TESLAGEN;
+ {
+ centity_t *source = &cg_entities[ es->generic1 ];
+ centity_t *target = &cg_entities[ es->clientNum ];
+ vec3_t sourceOffset = { 0.0f, 0.0f, 28.0f };
+
+ if( !CG_IsTrailSystemValid( &source->muzzleTS ) )
+ {
+ source->muzzleTS = CG_SpawnNewTrailSystem( cgs.media.teslaZapTS );
+
+ if( CG_IsTrailSystemValid( &source->muzzleTS ) )
+ {
+ CG_SetAttachmentCent( &source->muzzleTS->frontAttachment, source );
+ CG_SetAttachmentCent( &source->muzzleTS->backAttachment, target );
+ CG_AttachToCent( &source->muzzleTS->frontAttachment );
+ CG_AttachToCent( &source->muzzleTS->backAttachment );
+ CG_SetAttachmentOffset( &source->muzzleTS->frontAttachment, sourceOffset );
+
+ source->muzzleTSDeathTime = cg.time + cg_teslaTrailTime.integer;
+ }
+ }
+ }
+ break;
+
+ case 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:
+ CG_Bullet( es->pos.trBase, es->otherEntityNum, dir, qtrue, es->eventParm );
+ break;
+
+ case EV_SHOTGUN:
+ CG_ShotgunFire( es );
+ break;
+
+ case 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
+ 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_PAIN:
+ // local player sounds are triggered in CG_CheckLocalSounds,
+ // so ignore events on the player
+ if( cent->currentState.number != cg.snap->ps.clientNum )
+ CG_PainEvent( cent, es->eventParm );
+ break;
+
+ case EV_DEATH1:
+ case EV_DEATH2:
+ case EV_DEATH3:
+ trap_S_StartSound( NULL, es->number, CHAN_VOICE,
+ CG_CustomSound( es->number, va( "*death%i.wav", event - EV_DEATH1 + 1 ) ) );
+ break;
+
+ case EV_OBITUARY:
+ CG_Obituary( es );
+ break;
+
+ case EV_GIB_PLAYER:
+ // no gibbing
+ break;
+
+ case EV_STOPLOOPINGSOUND:
+ trap_S_StopLoopingSound( es->number );
+ es->loopSound = 0;
+ break;
+
+ case EV_DEBUG_LINE:
+ CG_Beam( cent );
+ break;
+
+ case EV_BUILD_DELAY:
+ if( clientNum == cg.predictedPlayerState.clientNum )
+ {
+ trap_S_StartLocalSound( cgs.media.buildableRepairedSound, CHAN_LOCAL_SOUND );
+ cg.lastBuildAttempt = cg.time;
+ }
+ break;
+
+ case EV_BUILD_REPAIR:
+ trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.buildableRepairSound );
+ break;
+
+ case EV_BUILD_REPAIRED:
+ trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.buildableRepairedSound );
+ break;
+
+ case EV_OVERMIND_ATTACK:
+ if( cg.predictedPlayerState.stats[ STAT_TEAM ] == TEAM_ALIENS )
+ {
+ trap_S_StartLocalSound( cgs.media.alienOvermindAttack, CHAN_ANNOUNCER );
+ CG_CenterPrint( "The Overmind is under attack!", 200, GIANTCHAR_WIDTH * 4 );
+ }
+ break;
+
+ case EV_OVERMIND_DYING:
+ if( cg.predictedPlayerState.stats[ STAT_TEAM ] == TEAM_ALIENS )
+ {
+ trap_S_StartLocalSound( cgs.media.alienOvermindDying, CHAN_ANNOUNCER );
+ CG_CenterPrint( "The Overmind is dying!", 200, GIANTCHAR_WIDTH * 4 );
+ }
+ break;
+
+ case EV_DCC_ATTACK:
+ if( cg.predictedPlayerState.stats[ STAT_TEAM ] == TEAM_HUMANS )
+ {
+ CG_CenterPrint( "Our base is under attack!", 200, GIANTCHAR_WIDTH * 4 );
+ trap_S_StartLocalSound( cgs.media.humanbaseunderatt, CHAN_ANNOUNCER );
+ }
+ break;
+
+ case EV_MGTURRET_SPINUP:
+ trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.turretSpinupSound );
+ break;
+
+ case EV_OVERMIND_SPAWNS:
+ if( cg.predictedPlayerState.stats[ STAT_TEAM ] == TEAM_ALIENS )
+ {
+ trap_S_StartLocalSound( cgs.media.alienOvermindSpawns, CHAN_ANNOUNCER );
+ CG_CenterPrint( "^5The Overmind needs spawns!", 200, GIANTCHAR_WIDTH * 4 );
+ }
+ break;
+
+ case EV_ALIEN_EVOLVE:
+ trap_S_StartSound( NULL, es->number, CHAN_BODY, cgs.media.alienEvolveSound );
+ {
+ particleSystem_t *ps = CG_SpawnNewParticleSystem( cgs.media.alienEvolvePS );
+
+ if( CG_IsParticleSystemValid( &ps ) )
+ {
+ CG_SetAttachmentCent( &ps->attachment, cent );
+ CG_AttachToCent( &ps->attachment );
+ }
+ }
+
+ if( es->number == cg.clientNum )
+ {
+ CG_ResetPainBlend( );
+ cg.spawnTime = cg.time;
+ }
+ break;
+
+ case EV_ALIEN_EVOLVE_FAILED:
+ if( clientNum == cg.predictedPlayerState.clientNum )
+ {
+ //FIXME: change to "negative" sound
+ trap_S_StartLocalSound( cgs.media.buildableRepairedSound, CHAN_LOCAL_SOUND );
+ cg.lastEvolveAttempt = cg.time;
+ }
+ break;
+
+ case EV_ALIEN_ACIDTUBE:
+ {
+ particleSystem_t *ps = CG_SpawnNewParticleSystem( cgs.media.alienAcidTubePS );
+
+ if( CG_IsParticleSystemValid( &ps ) )
+ {
+ CG_SetAttachmentCent( &ps->attachment, cent );
+ ByteToDir( es->eventParm, dir );
+ CG_SetParticleSystemNormal( ps, dir );
+ CG_AttachToCent( &ps->attachment );
+ }
+ }
+ break;
+
+ case EV_FORCE_FIELD:
+ {
+ particleSystem_t *ps = CG_SpawnNewParticleSystem( cgs.media.forcefieldPS );
+ trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.forcefieldSound );
+
+ if( CG_IsParticleSystemValid( &ps ) )
+ {
+ CG_SetAttachmentCent( &ps->attachment, cent );
+ ByteToDir( es->eventParm, dir );
+ CG_SetParticleSystemNormal( ps, dir );
+ CG_AttachToCent( &ps->attachment );
+ }
+ }
+ break;
+ case EV_ALIEN_SLIME:
+ {
+ particleSystem_t *ps = CG_SpawnNewParticleSystem( cgs.media.alienSlimePS );
+
+
+ if( CG_IsParticleSystemValid( &ps ) )
+ {
+ CG_SetAttachmentCent( &ps->attachment, cent );
+ ByteToDir( es->eventParm, dir );
+ CG_SetParticleSystemNormal( ps, dir );
+ CG_AttachToCent( &ps->attachment );
+ }
+ }
+ break;
+
+ case EV_MEDKIT_USED:
+ trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.medkitUseSound );
+ break;
+
+ case EV_PLAYER_RESPAWN:
+ if( es->number == cg.clientNum )
+ cg.spawnTime = cg.time;
+ break;
+
+ case EV_LEV2_ZAP:
+ CG_Level2Zap( es );
+ break;
+
+ default:
+ CG_Error( "Unknown event: %i", event );
+ break;
+ }
+}
+
+
+/*
+==============
+CG_CheckEvents
+
+==============
+*/
+void CG_CheckEvents( centity_t *cent )
+{
+ entity_event_t event;
+ entity_event_t oldEvent = EV_NONE;
+
+
+ // check for event-only entities
+ if( cent->currentState.eType > ET_EVENTS )
+ {
+ event = cent->currentState.eType - ET_EVENTS;
+
+ if( cent->previousEvent )
+ return; // already fired
+
+ cent->previousEvent = 1;
+
+ cent->currentState.event = cent->currentState.eType - ET_EVENTS;
+
+ // Move the pointer to the entity that the
+ // event was originally attached to
+ if( cent->currentState.eFlags & EF_PLAYER_EVENT )
+ {
+ cent = &cg_entities[ cent->currentState.otherEntityNum ];
+ oldEvent = cent->currentState.event;
+ cent->currentState.event = event;
+ }
+
+ }
+ 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 );
+
+ // If this was a reattached spilled event, restore the original event
+ if( oldEvent != EV_NONE )
+ cent->currentState.event = oldEvent;
+}
+