/* ========================= 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_BuildableParticleEffects ================== */ static void CG_BuildableParticleEffects( centity_t *cent ) { entityState_t *es = ¢->currentState; team_t team = BG_Buildable( es->modelindex )->team; int health = es->generic1; float healthFrac = (float)health / BG_Buildable( es->modelindex )->health; if( !( es->eFlags & EF_B_SPAWNED ) ) return; //ORGANIC BULB LIGHT / BA_H_LIGHT - VIA PS if( es->modelindex == BA_A_ORGANIC_BULB ) { if( !CG_IsParticleSystemValid( ¢->buildablePS ) ) { cent->buildablePS = CG_SpawnNewParticleSystem( cgs.media.organicbulbPS ); if( CG_IsParticleSystemValid( ¢->buildablePS ) ) { CG_SetAttachmentCent( ¢->buildablePS->attachment, cent ); CG_AttachToCent( ¢->buildablePS->attachment ); } } } else if( team == TEAM_HUMANS ) { if( healthFrac < 0.33f && !CG_IsParticleSystemValid( ¢->buildablePS ) ) { cent->buildablePS = CG_SpawnNewParticleSystem( cgs.media.humanBuildableDamagedPS ); if( CG_IsParticleSystemValid( ¢->buildablePS ) ) { CG_SetAttachmentCent( ¢->buildablePS->attachment, cent ); CG_AttachToCent( ¢->buildablePS->attachment ); } } else if( healthFrac >= 0.33f && CG_IsParticleSystemValid( ¢->buildablePS ) ) CG_DestroyParticleSystem( ¢->buildablePS ); } else if( team == TEAM_ALIENS ) { if( healthFrac < 0.33f && !CG_IsParticleSystemValid( ¢->buildablePS ) ) { cent->buildablePS = CG_SpawnNewParticleSystem( cgs.media.alienBuildableDamagedPS ); if( CG_IsParticleSystemValid( ¢->buildablePS ) ) { CG_SetAttachmentCent( ¢->buildablePS->attachment, cent ); CG_SetParticleSystemNormal( cent->buildablePS, es->origin2 ); CG_AttachToCent( ¢->buildablePS->attachment ); } } else if( healthFrac >= 0.33f && CG_IsParticleSystemValid( ¢->buildablePS ) ) CG_DestroyParticleSystem( ¢->buildablePS ); } }
/* =============== CG_TestTS_f Test a trail system =============== */ void CG_TestTS_f() { char tsName[ MAX_QPATH ]; if ( trap_Argc() < 2 ) { return; } Q_strncpyz( tsName, CG_Argv( 1 ), MAX_QPATH ); testTSHandle = CG_RegisterTrailSystem( tsName ); if ( testTSHandle ) { CG_DestroyTestTS_f(); testTS = CG_SpawnNewTrailSystem( testTSHandle ); if ( CG_IsTrailSystemValid( &testTS ) ) { CG_SetAttachmentCent( &testTS->frontAttachment, &cg_entities[ 0 ] ); CG_AttachToCent( &testTS->frontAttachment ); } } }
/* ================== CG_BuildableParticleEffects ================== */ static void CG_BuildableParticleEffects( centity_t *cent ) { entityState_t *es = ¢->currentState; buildableTeam_t team = BG_FindTeamForBuildable( es->modelindex ); int health = es->generic1 & B_HEALTH_MASK; float healthFrac = (float)health / B_HEALTH_MASK; if( !( es->generic1 & B_SPAWNED_TOGGLEBIT ) ) return; if( team == BIT_HUMANS ) { if( healthFrac < 0.33f && !CG_IsParticleSystemValid( ¢->buildablePS ) ) { cent->buildablePS = CG_SpawnNewParticleSystem( cgs.media.humanBuildableDamagedPS ); if( CG_IsParticleSystemValid( ¢->buildablePS ) ) { CG_SetAttachmentCent( ¢->buildablePS->attachment, cent ); CG_AttachToCent( ¢->buildablePS->attachment ); } } else if( healthFrac >= 0.33f && CG_IsParticleSystemValid( ¢->buildablePS ) ) CG_DestroyParticleSystem( ¢->buildablePS ); } else if( team == BIT_ALIENS ) { if( healthFrac < 0.33f && !CG_IsParticleSystemValid( ¢->buildablePS ) ) { cent->buildablePS = CG_SpawnNewParticleSystem( cgs.media.alienBuildableDamagedPS ); if( CG_IsParticleSystemValid( ¢->buildablePS ) ) { CG_SetAttachmentCent( ¢->buildablePS->attachment, cent ); CG_SetParticleSystemNormal( cent->buildablePS, es->origin2 ); CG_AttachToCent( ¢->buildablePS->attachment ); } } else if( healthFrac >= 0.33f && CG_IsParticleSystemValid( ¢->buildablePS ) ) CG_DestroyParticleSystem( ¢->buildablePS ); } }
/* ================= CG_PoisonCloud_f ================= */ static void CG_PoisonCloud_f( void ) { cg.poisonedTime = cg.time; if ( CG_IsParticleSystemValid( &cg.poisonCloudPS ) ) { cg.poisonCloudPS = CG_SpawnNewParticleSystem( cgs.media.poisonCloudPS ); CG_SetAttachmentCent( &cg.poisonCloudPS->attachment, &cg.predictedPlayerEntity ); CG_AttachToCent( &cg.poisonCloudPS->attachment ); } }
/* ============== 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 = ¢->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 ) { 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 a general 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 rotAxis; 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, rotAxis ); VectorNormalize( rotAxis ); //add the op CG_addSmoothOp( rotAxis, 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_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; case EV_JETPACK_ENABLE: // TODO: Trigger jetpack enable animation break; case EV_JETPACK_DISABLE: // TODO: Trigger jetpack disable animation break; case EV_JETPACK_START: // TODO: Start jetpack gfx/sfx break; case EV_JETPACK_STOP: // TODO: Stop jetpack gfx/sfx break; 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_HandleFireWeapon( cent, WPM_PRIMARY ); break; case EV_FIRE_WEAPON2: CG_HandleFireWeapon( cent, WPM_SECONDARY ); break; case EV_FIRE_WEAPON3: CG_HandleFireWeapon( cent, WPM_TERTIARY ); break; case EV_WEAPON_RELOAD: if ( cg_weapons[ es->eventParm ].wim[ WPM_PRIMARY ].reloadSound ) { trap_S_StartSound( NULL, es->number, CHAN_WEAPON, cg_weapons[ es->eventParm ].wim[ WPM_PRIMARY ].reloadSound ); } break; case EV_PLAYER_TELEPORT_IN: //deprecated break; case EV_PLAYER_TELEPORT_OUT: CG_PlayerDisconnect( position ); break; case EV_BUILD_CONSTRUCT: break; case EV_BUILD_DESTROY: break; case EV_AMMO_REFILL: case EV_CLIPS_REFILL: case EV_FUEL_REFILL: // TODO: Add different sounds for EV_AMMO_REFILL, EV_CLIPS_REFILL, EV_FUEL_REFILL 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_WEAPON_HIT_ENTITY: CG_HandleWeaponHitEntity( es, position ); break; case EV_WEAPON_HIT_ENVIRONMENT: CG_HandleWeaponHitWall( es, position ); break; case EV_MISSILE_HIT_ENTITY: CG_HandleMissileHitEntity( es, position ); break; // currently there is no support for metal sounds case EV_MISSILE_HIT_ENVIRONMENT: case EV_MISSILE_HIT_METAL: CG_HandleMissileHitWall( es, position ); break; case EV_SHOTGUN: CG_HandleFireShotgun( es ); break; case EV_HUMAN_BUILDABLE_DYING: CG_HumanBuildableDying( (buildable_t) es->modelindex, position ); break; case EV_HUMAN_BUILDABLE_EXPLOSION: ByteToDir( es->eventParm, dir ); CG_HumanBuildableExplosion( (buildable_t) es->modelindex, position, dir ); break; case EV_ALIEN_BUILDABLE_EXPLOSION: ByteToDir( es->eventParm, dir ); CG_AlienBuildableExplosion( position, dir ); 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_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_1: case EV_OVERMIND_ATTACK_2: if ( cg.predictedPlayerState.persistant[ PERS_TEAM ] == TEAM_ALIENS ) { trap_S_StartLocalSound( cgs.media.alienOvermindAttack, CHAN_ANNOUNCER ); CG_CenterPrint( va( "^%c%s", "31"[ event - EV_OVERMIND_ATTACK_1 ], _( "The Overmind is under attack!" ) ), 200, GIANTCHAR_WIDTH * 4 ); } break; case EV_OVERMIND_DYING: if ( cg.predictedPlayerState.persistant[ PERS_TEAM ] == TEAM_ALIENS ) { trap_S_StartLocalSound( cgs.media.alienOvermindDying, CHAN_ANNOUNCER ); CG_CenterPrint( _( "^1The Overmind is dying!" ), 200, GIANTCHAR_WIDTH * 4 ); } break; case EV_REACTOR_ATTACK_1: case EV_REACTOR_ATTACK_2: if ( cg.predictedPlayerState.persistant[ PERS_TEAM ] == TEAM_HUMANS ) { CG_CenterPrint( va( "^%c%s", "31"[ event - EV_REACTOR_ATTACK_1 ], _( "The reactor is under attack!" ) ), 200, GIANTCHAR_WIDTH * 4 ); } break; case EV_REACTOR_DYING: if ( cg.predictedPlayerState.persistant[ PERS_TEAM ] == TEAM_HUMANS ) { CG_CenterPrint( _( "^1The reactor is about to explode!" ), 200, GIANTCHAR_WIDTH * 4 ); } break; case EV_WARN_ATTACK: // if eventParm is non-zero, this is for humans and there's a nearby reactor or repeater, otherwise it's for aliens if ( es->eventParm >= MAX_CLIENTS && es->eventParm < MAX_GENTITIES ) { const char *location; qboolean base = cg_entities[ es->eventParm ].currentState.modelindex == BA_H_REACTOR; centity_t *locent = CG_GetLocation( cg_entities[ es->eventParm ].currentState.origin ); CG_CenterPrint( base ? _( "Our base is under attack!" ) : _( "A forward base is under attack!" ), 200, GIANTCHAR_WIDTH * 4 ); if ( locent ) { location = CG_ConfigString( CS_LOCATIONS + locent->currentState.generic1 ); } else { location = CG_ConfigString( CS_LOCATIONS ); } if ( location && *location ) { Com_Printf( _( "%s Under attack – %s\n" ), base ? "[reactor]" : "[repeater]", location ); } else { Com_Printf( _( "%s Under attack\n" ), base ? "[reactor]" : "[repeater]" ); } } else // this is for aliens, and the overmind is in range { CG_CenterPrint( _( "Our base is under attack!" ), 200, GIANTCHAR_WIDTH * 4 ); } break; case EV_MGTURRET_SPINUP: trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.turretSpinupSound ); break; case EV_OVERMIND_SPAWNS: if ( cg.predictedPlayerState.persistant[ PERS_TEAM ] == TEAM_ALIENS ) { trap_S_StartLocalSound( cgs.media.alienOvermindSpawns, CHAN_ANNOUNCER ); CG_CenterPrint( "The 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_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; case EV_HIT: cg.hitTime = cg.time; break; case EV_MOMENTUM: CG_Momentum( es ); break; default: CG_Error( "Unknown event: %i", event ); } }
/* ============== 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 = ¢->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 rotAxis; 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, rotAxis ); VectorNormalize( rotAxis ); //add the op CG_addSmoothOp( rotAxis, 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_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; //ROTAXfun 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 );//ROTAXfun break; case EV_ALIEN_BUILDABLE_EXPLOSION: ByteToDir( es->eventParm, dir ); CG_AlienBuildableExplosion( position, dir, es->modelindex );//ROTAXfun if ( es->modelindex == BA_A_BUSH ) CG_AlienBushExplosion( position, dir ); 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 ) { //trap_S_StartLocalSound( cgs.media.humanDCCAttack, CHAN_ANNOUNCER ); CG_CenterPrint( "Our base is under attack!", 200, GIANTCHAR_WIDTH * 4 ); } 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( "The 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_MEDKIT_USED: // the parameter is the healer's entity number { const int healerNum = es->eventParm; const char *configstring; const char *name; if( healerNum != clientNum ) { if( healerNum == cg.clientNum ) { configstring = CG_ConfigString( clientNum + CS_PLAYERS ); // isolate the player's name name = Info_ValueForKey( configstring, "n" ); CG_Printf( S_COLOR_CYAN "You bandaged " S_COLOR_WHITE "%s" S_COLOR_CYAN "'s wounds.\n", name ); } else if( clientNum == cg.clientNum ) { configstring = CG_ConfigString( healerNum + CS_PLAYERS ); // isolate the player's name name = Info_ValueForKey( configstring, "n" ); CG_Printf( S_COLOR_WHITE "%s" S_COLOR_CYAN " bandaged your wounds.\n", name ); } } } 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_ServerCommand The string has been tokenized and can be retrieved with Cmd_Argc() / Cmd_Argv() ================= */ static void CG_ServerCommand( void ) { const char *cmd; char text[ MAX_SAY_TEXT ]; cmd = CG_Argv( 0 ); if ( !cmd[ 0 ] ) { // server claimed the command return; } if ( !strcmp( cmd, "cp" ) ) { CG_CenterPrint( CG_Argv( 1 ), SCREEN_HEIGHT * 0.30, BIGCHAR_WIDTH ); return; } if ( !strcmp( cmd, "cs" ) ) { CG_ConfigStringModified(); return; } if ( !strcmp( cmd, "print" ) ) { CG_Printf( "%s", CG_Argv( 1 ) ); return; } if ( !strcmp( cmd, "chat" ) ) { if ( !cg_teamChatsOnly.integer ) { trap_S_StartLocalSound( cgs.media.talkSound, CHAN_LOCAL_SOUND ); Q_strncpyz( text, CG_Argv( 1 ), MAX_SAY_TEXT ); CG_RemoveChatEscapeChar( text ); CG_Printf( "%s\n", text ); } return; } if ( !strcmp( cmd, "tchat" ) ) { if ( cg.snap->ps.stats[ STAT_PTEAM ] == PTE_ALIENS ) { trap_S_StartLocalSound( cgs.media.alienTalkSound, CHAN_LOCAL_SOUND ); } else if ( cg.snap->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) { trap_S_StartLocalSound( cgs.media.humanTalkSound, CHAN_LOCAL_SOUND ); } else { trap_S_StartLocalSound( cgs.media.talkSound, CHAN_LOCAL_SOUND ); } Q_strncpyz( text, CG_Argv( 1 ), MAX_SAY_TEXT ); CG_RemoveChatEscapeChar( text ); CG_AddToTeamChat( text ); CG_Printf( "%s\n", text ); return; } if ( !strcmp( cmd, "scores" ) ) { CG_ParseScores(); return; } if ( !strcmp( cmd, "tinfo" ) ) { CG_ParseTeamInfo(); return; } if ( !strcmp( cmd, "map_restart" ) ) { CG_MapRestart(); return; } if ( Q_stricmp( cmd, "remapShader" ) == 0 ) { if ( trap_Argc() == 4 ) { trap_R_RemapShader( CG_Argv( 1 ), CG_Argv( 2 ), CG_Argv( 3 ) ); } } //the server has triggered a menu if ( !strcmp( cmd, "servermenu" ) ) { if ( trap_Argc() == 2 && !cg.demoPlayback ) { CG_Menu( atoi( CG_Argv( 1 ) ) ); } return; } //the server thinks this client should close all menus if ( !strcmp( cmd, "serverclosemenus" ) ) { trap_SendConsoleCommand( "closemenus\n" ); return; } //poison cloud effect needs to be reliable if ( !strcmp( cmd, "poisoncloud" ) ) { cg.poisonedTime = cg.time; if ( CG_IsParticleSystemValid( &cg.poisonCloudPS ) ) { cg.poisonCloudPS = CG_SpawnNewParticleSystem( cgs.media.poisonCloudPS ); CG_SetAttachmentCent( &cg.poisonCloudPS->attachment, &cg.predictedPlayerEntity ); CG_AttachToCent( &cg.poisonCloudPS->attachment ); } return; } if ( !strcmp( cmd, "weaponswitch" ) ) { CG_Printf( "client weaponswitch\n" ); if ( trap_Argc() == 2 ) { cg.weaponSelect = atoi( CG_Argv( 1 ) ); cg.weaponSelectTime = cg.time; } return; } // server requests a ptrc if ( !strcmp( cmd, "ptrcrequest" ) ) { int code = CG_ReadPTRCode(); trap_SendClientCommand( va( "ptrcverify %d", code ) ); return; } // server issues a ptrc if ( !strcmp( cmd, "ptrcissue" ) ) { if ( trap_Argc() == 2 ) { int code = atoi( CG_Argv( 1 ) ); CG_WritePTRCode( code ); } return; } // reply to ptrcverify if ( !strcmp( cmd, "ptrcconfirm" ) ) { trap_SendConsoleCommand( "menu ptrc_popmenu\n" ); return; } CG_Printf( "Unknown client game command: %s\n", cmd ); }