char *CG_GetLocationMsg(int clientNum, vec3_t origin)
{
	location_t *bestLoc = CG_GetLocation(clientNum, origin);

	// Fast way out, there are no locations for the map
	if (cgs.numLocations < 1)
	{
		return "Unknown";
	}

	if (bestLoc != NULL && strlen(bestLoc->message) > 1)
	{
		return va("%s", bestLoc->message);
	}

	return "Unknown";
}
Exemple #2
0
/*
==============
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 )
    {
    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 );
    }
}