/* Copyright (C) 1997-2001 Id Software, Inc. This program 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. This program 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 this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "cg_local.h" /* ============== CG_TouchJumpPad ============== */ void CG_TouchJumpPad( int entNum ) { CG_SexedSound( entNum, CHAN_VOICE, va( S_PLAYER_JUMP_1_to_2, (rand()&1)+1 ), cg_volume_players->value ); } //============== //CG_StartVoiceTokenEffect //============== #ifdef VSAYS void CG_StartVoiceTokenEffect( int entNum, int type, int vsay ) { centity_t *cent; cgs_media_handle_t *sound = NULL; if( !cg_voiceChats->integer || cg_volume_voicechats->value <= 0.0f ) return; cent = &cg_entities[entNum]; // set the icon effect cent->localEffects[LOCALEFFECT_VSAY_HEADICON] = vsay; cent->localEffects[LOCALEFFECT_VSAY_HEADICON_TIMEOUT] = cg.time + HEADICON_TIMEOUT; // play the sound sound = cgs.media.sfxVSaySounds[vsay]; // played as it was made by the 1st person player trap_S_StartSound( NULL, cg.chasedNum+1, CHAN_AUTO, CG_MediaSfx(sound), cg_volume_voicechats->value, ATTN_NONE, 0 ); } #endif // VSAYS /* ============== CG_PlayerMuzzleFlash ============== */ void CG_PlayerMuzzleFlash( entity_state_t *ent, int parms ) { int i; centity_t *pl; float radius, volume; vec3_t lightcolor; vec3_t muzzle; qboolean silenced = parms & MZ_SILENCED; int strongmode = parms & ~MZ_SILENCED; cgs_media_handle_t *sound = NULL; pl = &cg_entities[ent->number]; if ( silenced ) { volume = 0.2f; //radius = 100 + (rand()&31); } else { volume = 1.0f; //radius = 200 + (rand()&31); } if( strongmode ) radius = 200 + (rand()&31); else radius = 100 + (rand()&31); if( ent->renderfx & RF_FRAMELERP ) { VectorCopy( ent->origin, muzzle ); } else { for( i = 0; i < 3; i++ ) muzzle[i] = pl->prev.origin[i] + cg.lerpfrac * (pl->current.origin[i] - pl->prev.origin[i]); } switch( ent->weapon ) { case WEAP_GUNBLADE: if( strongmode ) { VectorSet( lightcolor, 1.0f, 0.0f, 0.2f ); sound = cgs.media.sfxGunbladeStrongShot; } else { radius = 0; sound = cgs.media.sfxGunbladeWeakShot[(int)(random()*3)]; } break; case WEAP_RIOTGUN: VectorSet( lightcolor, 1.0f, 0.0f, 0.2f ); sound = strongmode ? cgs.media.sfxRiotgunStrongShot : cgs.media.sfxRiotgunWeakShot; break; case WEAP_GRENADELAUNCHER: VectorSet( lightcolor, 1.0f, 0.0f, 0.2f ); sound = strongmode ? cgs.media.sfxGrenadeLauncherStrongShot : cgs.media.sfxGrenadeLauncherWeakShot; break; case WEAP_ROCKETLAUNCHER: VectorSet( lightcolor, 1.0f, 0.0f, 0.2f ); sound = strongmode ? cgs.media.sfxRocketLauncherStrongShot : cgs.media.sfxRocketLauncherWeakShot; break; case WEAP_PLASMAGUN: VectorSet( lightcolor, 0.0f, 1.0f, 0.0f ); sound = strongmode ? cgs.media.sfxPlasmagunStrongShot[rand()%3] : cgs.media.sfxPlasmagunWeakShot; break; case WEAP_ELECTROBOLT: VectorSet( lightcolor, 0.9f, 0.9f, 1.0f ); sound = strongmode ? cgs.media.sfxElectroboltStrongShot : cgs.media.sfxElectroboltWeakShot; break; case WEAP_LASERGUN: sound = strongmode ? cgs.media.sfxLasergunStrongShot : cgs.media.sfxLasergunWeakShot; radius = 0; VectorClear( lightcolor ); break; // default to no light default: radius = 0; VectorClear( lightcolor ); break; } // spawn light if not cleared if( radius ) CG_AddLightToScene( muzzle, radius, lightcolor[0], lightcolor[1], lightcolor[2], NULL ); //CG_AllocDlight( radius, muzzle, lightcolor ); if ( sound ) { if( ent->number == cg.chasedNum + 1 ) trap_S_StartSound ( NULL, ent->number, CHAN_AUTO, CG_MediaSfx (sound), cg_volume_effects->value, ATTN_NORM, 0 ); else trap_S_StartSound ( ent->origin, 0, CHAN_AUTO, CG_MediaSfx (sound), cg_volume_effects->value, ATTN_NORM, 0 ); } } void CG_WeaponSwitchSound( entity_state_t *ent, int parm ) { cgs_media_handle_t *sound = NULL; if( parm == 1 ) sound = cgs.media.sfxWeaponUp; else if( parm == 2 ) sound = cgs.media.sfxWeaponUpNoAmmo; if ( sound ) { if( ent->number == cg.chasedNum + 1 ) trap_S_StartSound ( NULL, ent->number, CHAN_AUTO, CG_MediaSfx (sound), cg_volume_effects->value, ATTN_NORM, 0 ); else trap_S_StartSound ( ent->origin, 0, CHAN_AUTO, CG_MediaSfx (sound), cg_volume_effects->value, ATTN_NORM, 0 ); } } /* ============== CG_FireLead Clientside prediction of gunshots. Must match fire_lead in g_weapon.c ============== */ static void CG_FireLead( int self, vec3_t start, vec3_t axis[3], int hspread, int vspread, int *seed, trace_t *trace ) { trace_t tr; vec3_t dir; vec3_t end; float r; float u; vec3_t water_start; qboolean water = qfalse; int content_mask = MASK_SHOT | MASK_WATER; #if 1 // circle double alpha=M_PI*Q_crandom(seed); // [-PI ..+PI] double s=fabs(Q_crandom(seed)); // [0..1] r= s*cos(alpha)*hspread; u= s*sin(alpha)*vspread; #else // square r = Q_crandom (seed) * hspread; u = Q_crandom (seed) * vspread; #endif VectorMA( start, 8192, axis[0], end ); VectorMA( end, r, axis[1], end ); VectorMA( end, u, axis[2], end ); if( CG_PointContents( start ) & MASK_WATER ) { water = qtrue; VectorCopy( start, water_start ); content_mask &= ~MASK_WATER; } CG_Trace( &tr, start, vec3_origin, vec3_origin, end, self, content_mask ); // see if we hit water if ( tr.contents & MASK_WATER ) { water = qtrue; VectorCopy( tr.endpos, water_start ); if( !VectorCompare( start, tr.endpos ) ) { vec3_t forward, right, up; if( tr.contents & CONTENTS_WATER ) CG_ParticleEffect( tr.endpos, tr.plane.normal, 0.47f, 0.48f, 0.8f, 8 ); else if ( tr.contents & CONTENTS_SLIME ) CG_ParticleEffect( tr.endpos, tr.plane.normal, 0.0f, 1.0f, 0.0f, 8 ); else if ( tr.contents & CONTENTS_LAVA ) CG_ParticleEffect( tr.endpos, tr.plane.normal, 1.0f, 0.67f, 0.0f, 8 ); // change bullet's course when it enters water VectorSubtract( end, start, dir ); VecToAngles( dir, dir ); AngleVectors( dir, forward, right, up ); r = Q_crandom( seed ) * hspread * 2; u = Q_crandom( seed ) * vspread * 2; VectorMA( water_start, 8192, forward, end ); VectorMA( end, r, right, end ); VectorMA( end, u, up, end ); } // re-trace ignoring water this time CG_Trace( &tr, water_start, vec3_origin, vec3_origin, end, self, MASK_SHOT ); } // save the final trace *trace = tr; // if went through water, determine where the end and make a bubble trail if ( water ) { vec3_t pos; VectorSubtract( tr.endpos, water_start, dir ); VectorNormalize( dir ); VectorMA( tr.endpos, -2, dir, pos ); if( CG_PointContents( pos ) & MASK_WATER ) VectorCopy( pos, tr.endpos ); else CG_Trace( &tr, pos, vec3_origin, vec3_origin, water_start, tr.ent ? cg_entities[tr.ent].current.number : 0, MASK_WATER ); VectorAdd( water_start, tr.endpos, pos ); VectorScale( pos, 0.5, pos ); CG_BubbleTrail( water_start, tr.endpos, 32 ); } } //============== //CG_FireBullet //============== void CG_FireBullet( int self, vec3_t start, vec3_t forward, int count, int vspread, int hspread, int seed, void (*impact) (trace_t *tr/*, int impactnum*/) ) { int i; trace_t tr; vec3_t dir, axis[3]; qboolean takedamage; // calculate normal vectors VecToAngles( forward, dir ); AngleVectors( dir, axis[0], axis[1], axis[2] ); for( i = 0; i < count; i++ ) { // wsw: fix hspread is the first param for that function CG_FireLead( self, start, axis, hspread, vspread, &seed, &tr ); takedamage = tr.ent && ( cg_entities[tr.ent].current.takedamage ); if( tr.fraction < 1.0f && !takedamage && !(tr.surfFlags & SURF_NOIMPACT) ) impact( &tr ); } } /* ============== CG_BulletImpact ============== */ void CG_BulletImpact( trace_t *tr ) { // bullet impact CG_BulletExplosion( tr->endpos, tr->plane.normal ); // spawn decal CG_SpawnDecal( tr->endpos, tr->plane.normal, random()*360, 8, 1, 1, 1, 1, 8, 1, qfalse, CG_MediaShader (cgs.media.shaderBulletMark) ); // throw particles on dust if( tr->surfFlags & SURF_DUST ) CG_ParticleEffect( tr->endpos, tr->plane.normal, 0.30f, 0.30f, 0.25f, 20 ); // impact sound trap_S_StartSound( tr->endpos, 0, 0, CG_MediaSfx( cgs.media.sfxRic[rand()&2] ), cg_volume_effects->value, ATTN_NORM, 0 ); } /* ============== CG_RiotGunImpactSound ============== */ void CG_RiotGunImpactSound( int self, vec3_t start, vec3_t dir, int count ) { trace_t trace; vec3_t end; VectorMA( start, 8192, dir, end ); CG_Trace( &trace, start, vec3_origin, vec3_origin, end, self, MASK_SHOT ); if( trace.fraction < 1.0f && !(trace.surfFlags & SURF_NOIMPACT) ) { if( count > 20 ) trap_S_StartSound ( trace.endpos, 0, 0, CG_MediaSfx (cgs.media.sfxRiotgunStrongHit), cg_volume_effects->value, ATTN_NORM, 0 ); else trap_S_StartSound ( trace.endpos, 0, 0, CG_MediaSfx (cgs.media.sfxRiotgunWeakHit), cg_volume_effects->value, ATTN_NORM, 0 ); } } /* ============== CG_RiotgunStrongImpact ============== */ void CG_RiotgunStrongImpact( trace_t *tr )//, int impactnum ) { // bullet impact CG_BulletExplosion ( tr->endpos, tr->plane.normal ); // throw particles on dust if ( tr->surfFlags & SURF_DUST ) { CG_ParticleEffect ( tr->endpos, tr->plane.normal, 0.30f, 0.30f, 0.25f, 20 ); } // spawn decal CG_SpawnDecal ( tr->endpos, tr->plane.normal, random()*360, 8, 1, 1, 1, 1, 8, 1, qfalse, CG_MediaShader (cgs.media.shaderBulletMark) ); //if ( !impactnum ) // trap_S_StartSound ( tr->endpos, 0, 0, CG_MediaSfx (cgs.media.sfxRiotgunStrongHit), cg_volume_effects->value, ATTN_NORM, 0 ); } //================================================================== //========================================================= #define CG_MAX_ANNOUNCER_EVENTS 32 #define CG_MAX_ANNOUNCER_EVENTS_MASK ( CG_MAX_ANNOUNCER_EVENTS - 1 ) #define CG_ANNOUNCER_EVENTS_FRAMETIME 1.5f // the announcer will speak each 1.5 seconds typedef struct cg_announcerevent_s { int soundindex; }cg_announcerevent_t; cg_announcerevent_t cg_announcerEvents[CG_MAX_ANNOUNCER_EVENTS]; static int cg_announcerEventsCurrent = 0; static int cg_announcerEventsHead = 0; static float cg_announcerEventsDelay = 0.0f; //================= //G_ClearAnnouncerEvents //================= void CG_ClearAnnouncerEvents( void ) { cg_announcerEventsCurrent = cg_announcerEventsHead = 0; } //================= //G_AddAnnouncerEvent //================= void CG_AddAnnouncerEvent( int soundindex ) { if( cg_announcerEventsCurrent + CG_MAX_ANNOUNCER_EVENTS >= cg_announcerEventsHead ) { // full buffer (we do nothing, just let it overwrite the oldest } // add it cg_announcerEvents[cg_announcerEventsHead&CG_MAX_ANNOUNCER_EVENTS_MASK].soundindex = soundindex; cg_announcerEventsHead++; } //================= //G_ReleaseAnnouncerEvents //================= void CG_ReleaseAnnouncerEvents( void ) { // see if time enough has passed cg_announcerEventsDelay -= cg.frameTime; if( cg_announcerEventsDelay > 0.0f ) { return; } if( cg_announcerEventsCurrent < cg_announcerEventsHead ) { // play the event int soundindex = cg_announcerEvents[cg_announcerEventsCurrent&CG_MAX_ANNOUNCER_EVENTS_MASK].soundindex; trap_S_StartSound( NULL, cg.chasedNum + 1, CHAN_AUTO, cgs.soundPrecache[soundindex], cg_volume_announcer->value, ATTN_NONE, 0 ); cg_announcerEventsCurrent++; cg_announcerEventsDelay = CG_ANNOUNCER_EVENTS_FRAMETIME; // wait } else { cg_announcerEventsDelay = 0; // no wait } } //================================================================== //================================================================== /* ============== CG_EntityEvent An entity has just been parsed that has an event value ============== */ //wsw : jal : fixme (clean up into cg_local.h) void CG_PlasmaExplosion ( vec3_t pos, vec3_t dir, int fire_mode, float radius ); void CG_GrenadeExplosionMode ( vec3_t pos, vec3_t dir, int fire_mode, float radius ); void CG_RocketExplosionMode ( vec3_t pos, vec3_t dir, int fire_mode, float radius ); void CG_ElectroTrail2( vec3_t start, vec3_t end ); void CG_BoltExplosionMode ( vec3_t pos, vec3_t dir, int fire_mode ); void CG_BladeImpact ( vec3_t pos, vec3_t dir ); void CG_GunBladeBlastImpact ( vec3_t pos, vec3_t dir, float radius ); void CG_StartKickAnglesEffect( vec3_t source, float knockback, float radius, int time ); void CG_StartColorBlendEffect( float r, float g, float b, float a, int time ); struct sfx_s *CG_RegisterSexedSound( int entnum, char *name ); void CG_PModel_SetUpTeleportEffect( vec3_t origin, int entNum, int event ); void CG_EntityEvent( entity_state_t *ent ) { static orientation_t projection; int choose;//splitmodels int i; int parm; vec3_t dir; for( i = 0; i < 2; i++ ) { parm = ent->eventParms[i]; switch( ent->events[i] ) { case EV_FALL: if( parm == FALL_FAR ) { CG_SexedSound( ent->number, CHAN_VOICE, "*fall_2.wav", cg_volume_players->value ); } else if( parm == FALL_MEDIUM ) { CG_SexedSound( ent->number, CHAN_VOICE, "*fall_1.wav", cg_volume_players->value ); } else CG_SexedSound( ent->number, CHAN_VOICE, "*fall_0.wav", cg_volume_players->value ); //splitmodels jal[start] #ifndef _ANIM_PRESTEP CG_AddPModelAnimation( ent->number, LEGS_JUMP1ST, 0, 0, EVENT_CHANNEL); #endif //jal[end] break; case EV_SEXEDSOUND: if( parm == 2 ) CG_SexedSound( ent->number, CHAN_VOICE, S_PLAYER_GASP, cg_volume_players->value ); else if( parm == 1 ) CG_SexedSound( ent->number, CHAN_VOICE, S_PLAYER_DROWN, cg_volume_players->value ); break; case EV_PAIN: CG_SexedSound( ent->number, CHAN_VOICE, va( S_PLAYER_PAINS, 25*(parm+1) ), cg_volume_players->value ); //splitmodels jal[start] choose = random(); if (choose <= 0.33) CG_AddPModelAnimation( ent->number, 0, TORSO_PAIN1, 0, EVENT_CHANNEL); else if (choose <= 0.66) CG_AddPModelAnimation( ent->number, 0, TORSO_PAIN2, 0, EVENT_CHANNEL); else CG_AddPModelAnimation( ent->number, 0, TORSO_PAIN3, 0, EVENT_CHANNEL); //jal[end] break; case EV_DIE: CG_SexedSound( ent->number, CHAN_VOICE, "*death.wav", cg_volume_players->value ); //splitmodels jal[start] switch( parm ) { case 0: default: CG_AddPModelAnimation( ent->number, BOTH_DEATH1, BOTH_DEATH1, ANIM_NONE, EVENT_CHANNEL); break; case 1: CG_AddPModelAnimation( ent->number, BOTH_DEATH2, BOTH_DEATH2, ANIM_NONE, EVENT_CHANNEL); break; case 2: CG_AddPModelAnimation( ent->number, BOTH_DEATH3, BOTH_DEATH3, ANIM_NONE, EVENT_CHANNEL); break; } //jal[end] break; case EV_GIB: // trap_S_StartSound( NULL, ent->number, CHAN_VOICE, CG_MediaSfx( cgs.media.sfxGibSound ), cg_volume_effects->value, ATTN_NORM, 0 ); break; case EV_JUMP_PAD: CG_TouchJumpPad( ent->ownerNum/*, ent->targetNum*/ ); break; case EV_EXPLOSION1: CG_Explosion1( ent->origin ); break; case EV_EXPLOSION2: CG_Explosion2( ent->origin ); break; case EV_GREEN_LASER: CG_GreenLaser( ent->origin, ent->origin2 ); break; case EV_LIGHTNING: //CG_AddLightning( ent->ownerNum, ent->targetNum, ent->origin, ent->origin2, CG_MediaModel( cgs.media.modLightning ) ); //trap_S_StartSound( NULL, ent->ownerNum, CHAN_AUTO, CG_MediaSfx( cgs.media.sfxLightning ), cg_volume_effects->value, ATTN_NORM, 0 ); break; case EV_BLOOD: ByteToDir( parm, dir ); CG_ParticleEffect( ent->origin, dir, 0.61f, 0.1f, 0.0f, 60 ); break; case EV_SPARKS: ByteToDir( parm, dir ); CG_ParticleEffect( ent->origin, dir, 1.0f, 0.67f, 0.0f, 6 ); break; case EV_BULLET_SPARKS: ByteToDir( parm, dir ); CG_BulletExplosion( ent->origin, dir ); CG_ParticleEffect( ent->origin, dir, 1.0f, 0.67f, 0.0f, 6 ); trap_S_StartSound( ent->origin, 0, 0, CG_MediaSfx( cgs.media.sfxRic[rand()&2] ), cg_volume_effects->value, ATTN_NORM, 0 ); break; case EV_LASER_SPARKS: ByteToDir( parm, dir ); CG_ParticleEffect2( ent->origin, dir, COLOR_R (ent->colorRGBA) * (1.0 / 255.0), COLOR_G (ent->colorRGBA) * (1.0 / 255.0), COLOR_B (ent->colorRGBA) * (1.0 / 255.0), ent->eventCount ); break; //SPLITMODELS jal[start] case EV_GESTURE: CG_SexedSound( ent->number, CHAN_VOICE, "*taunt.wav", cg_volume_players->value ); CG_AddPModelAnimation( ent->number, 0, TORSO_GESTURE, 0, EVENT_CHANNEL); break; case EV_DROP: CG_AddPModelAnimation( ent->number, 0, TORSO_DROP, 0, EVENT_CHANNEL); break; case EV_WEAPONUP: CG_WeaponSwitchSound( ent, parm ); CG_AddPModelAnimation( ent->number, 0, TORSO_FLIPIN, 0, EVENT_CHANNEL); break; case EV_SPOG://debrisbounce CG_SmallPileOfGibs( ent->origin, parm, ent->origin2 ); break; //jal[end] //JALNEWWEAPONS : wsw events //------------------------------------------------------------------- case EV_JUMP: if( parm == 1 ) { CG_SexedSound( ent->number, CHAN_VOICE, va( S_PLAYER_WALLJUMP_1_to_2, (rand()&1)+1 ), cg_volume_players->value ); } else if( parm == 2 ) { CG_SexedSound( ent->number, CHAN_VOICE, va( S_PLAYER_DASH_1_to_2, (rand()&1)+1 ), cg_volume_players->value ); } else CG_SexedSound( ent->number, CHAN_VOICE, va( S_PLAYER_JUMP_1_to_2, (rand()&1)+1 ), cg_volume_players->value ); break; case EV_ITEM_RESPAWN: cg_entities[ent->number].respawnTime = cg.time; if( parm == POWERUP_QUAD ) trap_S_StartSound( NULL, ent->number, CHAN_AUTO, CG_MediaSfx (cgs.media.sfxItemQuadRespawn), cg_volume_effects->value, ATTN_IDLE, 0 ); else trap_S_StartSound( NULL, ent->number, CHAN_AUTO, CG_MediaSfx (cgs.media.sfxItemRespawn), cg_volume_effects->value, ATTN_IDLE, 0 ); break; case EV_PLAYER_TELEPORT_IN: if( ent->ownerNum == cg.chasedNum + 1 ) { trap_S_StartSound( NULL, ent->ownerNum, 0, CG_MediaSfx (cgs.media.sfxTeleportIn), cg_volume_effects->value, ATTN_NORM, 0 ); } else { trap_S_StartSound( ent->origin, 0, 0, CG_MediaSfx (cgs.media.sfxTeleportIn), cg_volume_effects->value, ATTN_NORM, 0 ); } if( ent->ownerNum && ent->ownerNum < MAX_CLIENTS ) { cg_entities[ent->ownerNum].localEffects[LOCALEFFECT_EV_PLAYER_TELEPORT_IN] = (unsigned int)cg.time; } else { CG_TeleportEffect( ent->origin ); } break; case EV_PLAYER_TELEPORT_OUT: if( ent->ownerNum == cg.chasedNum + 1 ) { trap_S_StartSound( NULL, ent->ownerNum, 0, CG_MediaSfx (cgs.media.sfxTeleportOut), cg_volume_effects->value, ATTN_NORM, 0 ); } else { trap_S_StartSound( ent->origin, 0, 0, CG_MediaSfx (cgs.media.sfxTeleportOut), cg_volume_effects->value, ATTN_NORM, 0 ); } if( ent->ownerNum && ent->ownerNum < MAX_CLIENTS ) { cg_entities[ent->ownerNum].localEffects[LOCALEFFECT_EV_PLAYER_TELEPORT_OUT] = (unsigned int)cg.time; VectorCopy( ent->origin, cg_entities[ent->ownerNum].teleportedFrom ); } else { CG_TeleportEffect( ent->origin ); } break; case EV_MUZZLEFLASH: //parm is fire mode CG_PlayerMuzzleFlash( ent, parm ); CG_PModels_AddFireEffects( ent ); break; case EV_ELECTROTRAIL: if( CG_PModel_GetProjectionSource( ent->ownerNum, &projection ) ) CG_ElectroTrail2( projection.origin, ent->origin2 ); else CG_ElectroTrail2( ent->origin, ent->origin2 ); break; case EV_PLASMA_EXPLOSION: ByteToDir( parm, dir ); if( ent->firemode == FIRE_MODE_WEAK ) { CG_PlasmaExplosion( ent->origin, dir, FIRE_MODE_WEAK, (float)ent->weapon * 8.0f ); trap_S_StartSound( ent->origin, 0, CHAN_AUTO, CG_MediaSfx( cgs.media.sfxPlasmaWeakHit), cg_volume_effects->value, ATTN_NORM, 0 ); } else { CG_PlasmaExplosion( ent->origin, dir, FIRE_MODE_STRONG, (float)ent->weapon * 8.0f ); trap_S_StartSound( ent->origin, 0, CHAN_AUTO, CG_MediaSfx( cgs.media.sfxPlasmaStrongHit), cg_volume_effects->value, ATTN_NORM, 0 ); } // add kickangles from explosion radius if( ent->firemode == FIRE_MODE_STRONG ) CG_StartKickAnglesEffect( ent->origin, 30, ent->weapon * 8, 50 ); else CG_StartKickAnglesEffect( ent->origin, 20, ent->weapon * 8, 30 ); break; case EV_BOLT_EXPLOSION: ByteToDir( parm, dir ); CG_BoltExplosionMode( ent->origin, dir, ent->firemode ); CG_BulletExplosion( ent->origin, dir ); break; case EV_GRENADE_EXPLOSION: // wsw: pb make grenades using the same explosion effect as RL if( parm ) { // we have a direction ByteToDir( parm, dir ); CG_GrenadeExplosionMode( ent->origin, dir, ent->firemode, (float)ent->weapon*8.0f ); } else { // no direction so lets use vec3_origin CG_GrenadeExplosionMode( ent->origin, vec3_origin, ent->firemode, (float)ent->weapon*8.0f ); } /* // old code if( parm ) { ByteToDir( parm, dir ); CG_GrenadeExplosionMode( ent->origin, dir, ent->firemode, (float)ent->weapon*8.0f ); } else CG_GrenadeExplosionMode( ent->origin, NULL, ent->firemode, (float)ent->weapon*8.0f ); */ if( ent->firemode == FIRE_MODE_WEAK ) trap_S_StartSound( ent->origin, 0, CHAN_AUTO, CG_MediaSfx( cgs.media.sfxGrenadeWeakExplosion), cg_volume_effects->value, ATTN_NORM, 0 ); else trap_S_StartSound( ent->origin, 0, CHAN_AUTO, CG_MediaSfx( cgs.media.sfxGrenadeStrongExplosion), cg_volume_effects->value, ATTN_NORM, 0 ); // add kickangles from explosion radius if( ent->firemode == FIRE_MODE_STRONG ) CG_StartKickAnglesEffect( ent->origin, 150, ent->weapon*8, 350 ); else CG_StartKickAnglesEffect( ent->origin, 130, ent->weapon*8, 300 ); break; case EV_ROCKET_EXPLOSION: ByteToDir( parm, dir ); if( ent->firemode == FIRE_MODE_WEAK ) { CG_RocketExplosionMode( ent->origin, dir, FIRE_MODE_WEAK, (float)ent->weapon * 8.0f ); trap_S_StartSound( ent->origin, 0, CHAN_AUTO, CG_MediaSfx( cgs.media.sfxRocketLauncherWeakHit), cg_volume_effects->value, ATTN_NORM, 0 ); } else { CG_RocketExplosionMode( ent->origin, dir, FIRE_MODE_STRONG, (float)ent->weapon * 8.0f ); trap_S_StartSound( ent->origin, 0, CHAN_AUTO, CG_MediaSfx( cgs.media.sfxRocketLauncherStrongHit), cg_volume_effects->value, ATTN_NORM, 0 ); } // add kickangles from explosion radius if( ent->firemode == FIRE_MODE_STRONG ) CG_StartKickAnglesEffect( ent->origin, 140, ent->weapon * 8, 300 ); else CG_StartKickAnglesEffect( ent->origin, 120, ent->weapon * 8, 250 ); break; case EV_FIRE_RIOTGUN: //spreads come inside the free shorts I found: //light = hspread //skinnum = vspread CG_FireBullet( ent->ownerNum, ent->origin, ent->origin2, ent->eventCount, ent->skinnum, ent->light, parm, CG_RiotgunStrongImpact ); //spawn a single sound in the impact CG_RiotGunImpactSound( ent->ownerNum, ent->origin, ent->origin2, ent->eventCount ); break; case EV_FIRE_BULLET: CG_FireBullet( ent->ownerNum, ent->origin, ent->origin2, 1, DEFAULT_BULLET_VSPREAD, DEFAULT_BULLET_HSPREAD, parm, CG_BulletImpact ); break; case EV_GRENADE_BOUNCE: if( parm == FIRE_MODE_STRONG ) trap_S_StartSound( NULL, ent->number, CHAN_AUTO, CG_MediaSfx (cgs.media.sfxGrenadeStrongBounce[rand()&1]), cg_volume_effects->value, ATTN_NORM, 0 ); else trap_S_StartSound( NULL, ent->number, CHAN_AUTO, CG_MediaSfx (cgs.media.sfxGrenadeWeakBounce[rand()&1]), cg_volume_effects->value, ATTN_NORM, 0 ); break; case EV_BLADE_IMPACT: CG_BladeImpact( ent->origin, ent->origin2 ); break; case EV_GUNBLADEBLAST_IMPACT: ByteToDir ( parm, dir ); CG_GunBladeBlastImpact( ent->origin, dir, (float)ent->weapon*8 ); //sfxGunbladeStrongHit if( ent->skinnum > 64 ) trap_S_StartSound( ent->origin, 0, CHAN_AUTO, CG_MediaSfx (cgs.media.sfxGunbladeStrongHit[2]), cg_volume_effects->value, ATTN_NORM, 0 ); else if( ent->skinnum > 34 ) trap_S_StartSound( ent->origin, 0, CHAN_AUTO, CG_MediaSfx (cgs.media.sfxGunbladeStrongHit[1]), cg_volume_effects->value, ATTN_NORM, 0 ); else trap_S_StartSound( ent->origin, 0, CHAN_AUTO, CG_MediaSfx (cgs.media.sfxGunbladeStrongHit[0]), cg_volume_effects->value, ATTN_NORM, 0 ); // add kickangles from explosion radius //ent->skinnum is knockback value CG_StartKickAnglesEffect( ent->origin, ent->skinnum*8, ent->weapon*8, 200 ); break; case EV_BLOOD2: // wsw: pb: filter own blood trail if ( cg_showBloodTrail->integer==2 && (cg.chasedNum+1 == ent->ownerNum)) return; ByteToDir( parm, dir ); CG_BloodDamageEffect( ent->origin, dir, ent->damage ); break; case EV_BLOOD_SAVED: //ByteToDir( parm, dir ); //CG_BloodDamageEffect( ent->origin, dir, ent->damage ); break; #ifdef VSAYS case EV_VSAY: CG_StartVoiceTokenEffect( ent->ownerNum, EV_VSAY, parm ); break; #endif #ifdef THIS_IS_DISABLED case EV_RAILTRAIL: //splitmodels jal[start] if( vweap.active && (cg.chasedNum+1 == ent->ownerNum) ) { orientation_t projection; if( CG_vWeap_CalcProjectionSource( &projection ) ) CG_RailTrail( projection.origin, ent->origin2 ); else CG_RailTrail( ent->origin, ent->origin2 ); } else { orientation_t projection; if( CG_PModel_GetProjectionSource( &cg_entPModels[ent->ownerNum], &projection) ) CG_RailTrail ( projection.origin, ent->origin2 ); else CG_RailTrail ( ent->origin, ent->origin2 ); } //jal[end] trap_S_StartSound( ent->origin2, 0, 0, CG_MediaSfx( cgs.media.sfxRailg ), cg_volume_effects->value, ATTN_NORM, 0 ); break; case EV_BFG_EXPLOSION: CG_BFGExplosion( ent->origin ); break; case EV_BFG_BIGEXPLOSION: CG_BFGBigExplosion( ent->origin ); break; #endif } } } //================== //CG_FirePlayerStateEvents //This events are only received by this client, and only affect it. //================== void CG_FirePlayerStateEvents( void ) { unsigned int event, parm; if( !cg.frame.playerState.event ) return; // first byte is event number, second is parm event = cg.frame.playerState.event & 0xFF; parm = (cg.frame.playerState.event>>8) & 0xFF; //CG_Printf( "GotPSEVENT:%i\n", event ); switch( event ) { case PSEV_HIT : // wsw : pb: hit sound volume trap_S_StartSound( cg.refdef.vieworg, 0, 0, CG_MediaSfx( cgs.media.sfxWeaponHit[parm] ), cg_volume_hitsound->value , ATTN_NONE, 0 ); if( parm == 4 && cg_showhelp->integer ) { if( random() <= 0.5f ) SCR_CenterPrint( "Don't shoot at members of your team!" ); else SCR_CenterPrint( "You are shooting at your team-mates!" ); } break; case PSEV_PICKUP : if( cg_pickup_flash->integer ) CG_StartColorBlendEffect( 1.0f, 1.0f, 1.0f, 0.25f, 150 ); break; case PSEV_DAMAGE_BLEND : if( cg_damage_blend->integer ) CG_StartColorBlendEffect( 1.0f, 0.0f, 0.0f, 0.4f, parm * 10 ); break; case PSEV_INDEXEDSOUND: trap_S_StartSound( NULL, cg.chasedNum + 1, CHAN_AUTO, cgs.soundPrecache[parm], cg_volume_effects->value, ATTN_NORM, 0 ); break; case PSEV_NOAMMO: if( parm == cg.frame.playerState.stats[STAT_WEAPON_ITEM] ) CG_NoAmmoWeaponChange(); break; case PSEV_ANNOUNCER: trap_S_StartSound( NULL, cg.chasedNum + 1, CHAN_AUTO, cgs.soundPrecache[parm], cg_volume_announcer->value, ATTN_NONE, 0 ); break; case PSEV_ANNOUNCER_QUEUED: CG_AddAnnouncerEvent( parm ); break; default: break; } }