/***
*
*	Copyright (c) 1996-2001, Valve LLC. All rights reserved.
*
*	This product contains software technology licensed from Id
*	Software, Inc. ("Id Technology").  Id Technology (c) 1996 Id Software, Inc.
*	All Rights Reserved.
*
*   Use, distribution, and modification of this source code and/or resulting
*   object code is restricted to non-commercial enhancements to products from
*   Valve LLC.  All other use, distribution, or modification is prohibited
*   without written permission from Valve LLC.
*
****/
/*

===== generic grenade.cpp ========================================================

*/

#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "monsters.h"
#include "weapons.h"
#include "nodes.h"
#include "soundent.h"
#include "decals.h"
#include "player.h"
#include "explode.h"
#include "gamerules.h"

#define SF_PROP_RESPAWN		8 // enable autorespawn
#define SF_PROP_BREAKABLE		16 // enable break/explode
#define SF_PROP_FIXED		32 // don't move untill touch
typedef enum { expRandom, expDirected} Explosions;
typedef enum { matGlass = 0, matWood, matMetal, matFlesh, matCinderBlock, matCeilingTile, matComputer, matUnbreakableGlass, matRocks, matNone, matLastMaterial } Materials;

//extern "C" void AngleVectors (const vec3_t angles, vec3_t forward, vec3_t right, vec3_t up);
Vector UTIL_AngleVectorsF(const Vector &angles)
{
    float rgflVecOut[3];
    float rgflVecIn[3];
    angles.CopyToArray(rgflVecIn);
    g_engfuncs.pfnAngleVectors(rgflVecIn, rgflVecOut, NULL, NULL);
    return Vector(rgflVecOut);
}
Vector UTIL_AngleVectorsR(const Vector &angles)
{
    float rgflVecOut[3];
    float rgflVecIn[3];
    angles.CopyToArray(rgflVecIn);
    g_engfuncs.pfnAngleVectors(rgflVecIn, NULL, rgflVecOut, NULL);
    return Vector(rgflVecOut);
}
Vector UTIL_AngleVectorsU(const Vector &angles)
{
    float rgflVecOut[3];
    float rgflVecIn[3];
    angles.CopyToArray(rgflVecIn);
    g_engfuncs.pfnAngleVectors(rgflVecIn, NULL, rgflVecOut, NULL);
    return Vector(rgflVecOut);
}
//===================grenade

enum PropShape
{
    SHAPE_CYL_H = 0,
    SHAPE_CYL_V,
    SHAPE_BOX,
    SHAPE_GENERIC,
    SHAPE_SPHERE,
    SHAPE_NOROTATE
};

class CProp : public CBaseEntity
{
public:
    void Spawn(void);
    void Precache();

    void EXPORT BounceTouch(CBaseEntity *pOther);
    //void EXPORT SlideTouch(CBaseEntity *pOther);
    virtual void Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value);
    virtual void Force(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value);
    int TakeDamage(entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType);
    virtual int	ObjectCaps(void) { return (CBaseEntity::ObjectCaps() & ~FCAP_ACROSS_TRANSITION) | FCAP_IMPULSE_USE | FCAP_CONTINUOUS_USE; }
    virtual void BounceSound(void);
    virtual int	BloodColor(void) { return DONT_BLEED; }
    virtual void Killed(entvars_t *pevAttacker, int iGib);
    void CheckRotate();
    void RespawnThink();
    void AngleThink();
    void DeployThink();
    void DamageSound( void );
    void PropRespawn();
    void KeyValue( KeyValueData* pkvd);

    static const char *pSoundsWood[];
    static const char *pSoundsFlesh[];
    static const char *pSoundsGlass[];
    static const char *pSoundsMetal[];
    static const char *pSoundsConcrete[];
    static const char *pSpawnObjects[];

    inline BOOL		Explodable( void ) { return ExplosionMagnitude() > 0; }
    inline int		ExplosionMagnitude( void ) { return pev->impulse; }
    inline void		ExplosionSetMagnitude( int magnitude ) { pev->impulse = magnitude; }


    static void MaterialSoundPrecache( Materials precacheMaterial );
    static void MaterialSoundRandom( edict_t *pEdict, Materials soundMaterial, float volume );
    static const char **MaterialSoundList( Materials precacheMaterial, int &soundCount );
    void EXPORT		Die( void );

    BOOL m_bBarrel;
    float m_flFloorFriction;
    float m_flCollideFriction;

    // hull sizes
    Vector minsH, maxsH;
    Vector minsV, maxsV;

    // spawn backup;
    Vector spawnOrigin;
    Vector spawnAngles;

    edict_t *m_owner2;
    edict_t *m_attacker;
    float m_flNextAttack;
    float m_flRespawnTime;
    PropShape m_shape;
    PropShape m_oldshape;
    CBasePlayer *m_pHolstered;
    float m_flSpawnHealth;
    int			m_idShard;
    float		m_angle;
    int			m_iszGibModel;
    Materials	m_Material;
    Explosions	m_Explosion;
    int m_iaCustomAnglesX[10];
    int m_iaCustomAnglesZ[10];
};
LINK_ENTITY_TO_CLASS(prop, CProp);

const char *CProp::pSoundsWood[] =
{
    "debris/wood1.wav",
    "debris/wood2.wav",
    "debris/wood3.wav",
};

const char *CProp::pSoundsFlesh[] =
{
    "debris/flesh1.wav",
    "debris/flesh2.wav",
    "debris/flesh3.wav",
    "debris/flesh5.wav",
    "debris/flesh6.wav",
    "debris/flesh7.wav",
};

const char *CProp::pSoundsMetal[] =
{
    "debris/metal1.wav",
    "debris/metal2.wav",
    "debris/metal3.wav",
};

const char *CProp::pSoundsConcrete[] =
{
    "debris/concrete1.wav",
    "debris/concrete2.wav",
    "debris/concrete3.wav",
};


const char *CProp::pSoundsGlass[] =
{
    "debris/glass1.wav",
    "debris/glass2.wav",
    "debris/glass3.wav",
};

const char **CProp::MaterialSoundList( Materials precacheMaterial, int &soundCount )
{
    const char	**pSoundList = NULL;

    switch ( precacheMaterial )
    {
    case matWood:
        pSoundList = pSoundsWood;
        soundCount = ARRAYSIZE(pSoundsWood);
        break;
    case matFlesh:
        pSoundList = pSoundsFlesh;
        soundCount = ARRAYSIZE(pSoundsFlesh);
        break;
    case matComputer:
    case matUnbreakableGlass:
    case matGlass:
        pSoundList = pSoundsGlass;
        soundCount = ARRAYSIZE(pSoundsGlass);
        break;

    case matMetal:
        pSoundList = pSoundsMetal;
        soundCount = ARRAYSIZE(pSoundsMetal);
        break;

    case matCinderBlock:
    case matRocks:
        pSoundList = pSoundsConcrete;
        soundCount = ARRAYSIZE(pSoundsConcrete);
        break;


    case matCeilingTile:
    case matNone:
    default:
        soundCount = 0;
        break;
    }

    return pSoundList;
}

void CProp::MaterialSoundPrecache( Materials precacheMaterial )
{
    const char	**pSoundList;
    int			i, soundCount = 0;

    pSoundList = MaterialSoundList( precacheMaterial, soundCount );

    for ( i = 0; i < soundCount; i++ )
    {
        PRECACHE_SOUND( (char *)pSoundList[i] );
    }
}

void CProp::MaterialSoundRandom( edict_t *pEdict, Materials soundMaterial, float volume )
{
    const char	**pSoundList;
    int			soundCount = 0;

    pSoundList = MaterialSoundList( soundMaterial, soundCount );

    if ( soundCount )
        EMIT_SOUND( pEdict, CHAN_BODY, pSoundList[ RANDOM_LONG(0,soundCount-1) ], volume, 1.0 );
}

void CProp::Precache( void )
{
    const char *pGibName;

    switch (m_Material)
    {
    case matWood:
        pGibName = "models/woodgibs.mdl";

        PRECACHE_SOUND("debris/bustcrate1.wav");
        PRECACHE_SOUND("debris/bustcrate2.wav");
        break;
    case matFlesh:
        pGibName = "models/fleshgibs.mdl";

        PRECACHE_SOUND("debris/bustflesh1.wav");
        PRECACHE_SOUND("debris/bustflesh2.wav");
        break;
    case matComputer:
        PRECACHE_SOUND("buttons/spark5.wav");
        PRECACHE_SOUND("buttons/spark6.wav");
        pGibName = "models/computergibs.mdl";

        PRECACHE_SOUND("debris/bustmetal1.wav");
        PRECACHE_SOUND("debris/bustmetal2.wav");
        break;

    case matUnbreakableGlass:
    case matGlass:
        pGibName = "models/glassgibs.mdl";

        PRECACHE_SOUND("debris/bustglass1.wav");
        PRECACHE_SOUND("debris/bustglass2.wav");
        break;
    case matMetal:
        pGibName = "models/metalplategibs.mdl";

        PRECACHE_SOUND("debris/bustmetal1.wav");
        PRECACHE_SOUND("debris/bustmetal2.wav");
        break;
    case matCinderBlock:
        pGibName = "models/cindergibs.mdl";

        PRECACHE_SOUND("debris/bustconcrete1.wav");
        PRECACHE_SOUND("debris/bustconcrete2.wav");
        break;
    case matRocks:
        pGibName = "models/rockgibs.mdl";

        PRECACHE_SOUND("debris/bustconcrete1.wav");
        PRECACHE_SOUND("debris/bustconcrete2.wav");
        break;
    case matCeilingTile:
        pGibName = "models/ceilinggibs.mdl";

        PRECACHE_SOUND ("debris/bustceiling.wav");
        break;
    }
    MaterialSoundPrecache( m_Material );
    if ( m_iszGibModel )
        pGibName = STRING(m_iszGibModel);

    m_idShard = PRECACHE_MODEL( (char *)pGibName );
    PRECACHE_MODEL( (char *)STRING(pev->model) );
}

void CProp::DamageSound( void )
{
    int pitch;
    float fvol;
    char *rgpsz[6];
    int i;
    int material = m_Material;

//	if (RANDOM_LONG(0,1))
//		return;

    if (RANDOM_LONG(0,2))
        pitch = PITCH_NORM;
    else
        pitch = 95 + RANDOM_LONG(0,34);

    fvol = RANDOM_FLOAT(0.75, 1.0);

    if (material == matComputer && RANDOM_LONG(0,1))
        material = matMetal;

    switch (material)
    {
    case matComputer:
    case matGlass:
    case matUnbreakableGlass:
        rgpsz[0] = "debris/glass1.wav";
        rgpsz[1] = "debris/glass2.wav";
        rgpsz[2] = "debris/glass3.wav";
        i = 3;
        break;

    case matWood:
        rgpsz[0] = "debris/wood1.wav";
        rgpsz[1] = "debris/wood2.wav";
        rgpsz[2] = "debris/wood3.wav";
        i = 3;
        break;

    case matMetal:
        rgpsz[0] = "debris/metal1.wav";
        rgpsz[1] = "debris/metal3.wav";
        rgpsz[2] = "debris/metal2.wav";
        i = 2;
        break;

    case matFlesh:
        rgpsz[0] = "debris/flesh1.wav";
        rgpsz[1] = "debris/flesh2.wav";
        rgpsz[2] = "debris/flesh3.wav";
        rgpsz[3] = "debris/flesh5.wav";
        rgpsz[4] = "debris/flesh6.wav";
        rgpsz[5] = "debris/flesh7.wav";
        i = 6;
        break;

    case matRocks:
    case matCinderBlock:
        rgpsz[0] = "debris/concrete1.wav";
        rgpsz[1] = "debris/concrete2.wav";
        rgpsz[2] = "debris/concrete3.wav";
        i = 3;
        break;

    case matCeilingTile:
        // UNDONE: no ceiling tile shard sound yet
        i = 0;
        break;
    }

    if (i)
        EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, rgpsz[RANDOM_LONG(0,i-1)], fvol, ATTN_NORM, 0, pitch);
}

void CProp::Die( void )
{
    Vector vecSpot;// shard origin
    Vector vecVelocity;// shard velocity
    CBaseEntity *pEntity = NULL;
    char cFlag = 0;
    int pitch;
    float fvol;

    pitch = 95 + RANDOM_LONG(0,29);

    if (pitch > 97 && pitch < 103)
        pitch = 100;

    // The more negative pev->health, the louder
    // the sound should be.

    fvol = RANDOM_FLOAT(0.85, 1.0) + (fabs(pev->health) / 100.0);

    if (fvol > 1.0)
        fvol = 1.0;


    switch (m_Material)
    {
    case matGlass:
        switch ( RANDOM_LONG(0,1) )
        {
        case 0:	EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustglass1.wav", fvol, ATTN_NORM, 0, pitch);
            break;
        case 1:	EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustglass2.wav", fvol, ATTN_NORM, 0, pitch);
            break;
        }
        cFlag = BREAK_GLASS;
        break;

    case matWood:
        switch ( RANDOM_LONG(0,1) )
        {
        case 0:	EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustcrate1.wav", fvol, ATTN_NORM, 0, pitch);
            break;
        case 1:	EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustcrate2.wav", fvol, ATTN_NORM, 0, pitch);
            break;
        }
        cFlag = BREAK_WOOD;
        break;

    case matComputer:
    case matMetal:
        switch ( RANDOM_LONG(0,1) )
        {
        case 0:	EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustmetal1.wav", fvol, ATTN_NORM, 0, pitch);
            break;
        case 1:	EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustmetal2.wav", fvol, ATTN_NORM, 0, pitch);
            break;
        }
        cFlag = BREAK_METAL;
        break;

    case matFlesh:
        switch ( RANDOM_LONG(0,1) )
        {
        case 0:	EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustflesh1.wav", fvol, ATTN_NORM, 0, pitch);
            break;
        case 1:	EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustflesh2.wav", fvol, ATTN_NORM, 0, pitch);
            break;
        }
        cFlag = BREAK_FLESH;
        break;

    case matRocks:
    case matCinderBlock:
        switch ( RANDOM_LONG(0,1) )
        {
        case 0:	EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustconcrete1.wav", fvol, ATTN_NORM, 0, pitch);
            break;
        case 1:	EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustconcrete2.wav", fvol, ATTN_NORM, 0, pitch);
            break;
        }
        cFlag = BREAK_CONCRETE;
        break;

    case matCeilingTile:
        EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustceiling.wav", fvol, ATTN_NORM, 0, pitch);
        break;
    }


    if (m_Explosion == expDirected)
        vecVelocity = g_vecAttackDir * 200;
    else
    {
        vecVelocity.x = 0;
        vecVelocity.y = 0;
        vecVelocity.z = 0;
    }

    vecSpot = pev->origin + (pev->mins + pev->maxs) * 0.5;
    MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSpot );
        WRITE_BYTE( TE_BREAKMODEL);

        // position
        WRITE_COORD( vecSpot.x );
        WRITE_COORD( vecSpot.y );
        WRITE_COORD( vecSpot.z );

        // size
        WRITE_COORD( pev->size.x);
        WRITE_COORD( pev->size.y);
        WRITE_COORD( pev->size.z);

        // velocity
        WRITE_COORD( vecVelocity.x );
        WRITE_COORD( vecVelocity.y );
        WRITE_COORD( vecVelocity.z );

        // randomization
        WRITE_BYTE( 10 );

        // Model
        WRITE_SHORT( m_idShard );	//model id#

        // # of shards
        WRITE_BYTE( 0 );	// let client decide

        // duration
        WRITE_BYTE( 25 );// 2.5 seconds

        // flags
        WRITE_BYTE( cFlag );
    MESSAGE_END();

    float size = pev->size.x;
    if ( size < pev->size.y )
        size = pev->size.y;
    if ( size < pev->size.z )
        size = pev->size.z;

    // !!! HACK  This should work!
    // Build a box above the entity that looks like an 8 pixel high sheet
    Vector mins = pev->absmin;
    Vector maxs = pev->absmax;
    mins.z = pev->absmax.z;
    maxs.z += 8;

    // BUGBUG -- can only find 256 entities on a breakable -- should be enough
    CBaseEntity *pList[256];
    int count = UTIL_EntitiesInBox( pList, 256, mins, maxs, FL_ONGROUND );
    if ( count )
    {
        for ( int i = 0; i < count; i++ )
        {
            ClearBits( pList[i]->pev->flags, FL_ONGROUND );
            pList[i]->pev->groundentity = NULL;
        }
    }

    // Don't fire something that could fire myself
    pev->targetname = 0;

    pev->solid = SOLID_NOT;
    // Fire targets on break
    SUB_UseTargets( NULL, USE_TOGGLE, 0 );

    if ( Explodable() )
    {
        ExplosionCreate( pev->origin, pev->angles, m_attacker, ExplosionMagnitude(), TRUE );
    }
}

void CProp::Killed(entvars_t *pevAttacker, int iGib)
{
    pev->takedamage = DAMAGE_NO;
    pev->deadflag = DEAD_DEAD;
    pev->solid = SOLID_NOT;
    pev->effects |= EF_NODRAW;
    pev->nextthink = gpGlobals->time + m_flRespawnTime;
    SetThink( CProp::RespawnThink );
    ResetTouch( );
    ResetUse( );
}

void CProp::Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value)
{
    if (m_owner2 != pActivator->edict())
    {
        if (pev->velocity.Length() < 100 && pActivator->IsPlayer())
        {
            m_owner2 = m_attacker = pActivator->edict();
        }
        else
            return;
    }
    if( pActivator->IsPlayer() )
    {
        m_pHolstered = (CBasePlayer *) pActivator;
        if( m_pHolstered )
        {

            if ( m_pHolstered->m_pActiveItem )
            {
                CBasePlayerWeapon *weapon = (CBasePlayerWeapon *) m_pHolstered->m_pActiveItem->GetWeaponPtr();


                //m_Holstered->m_pActiveItem->Holster(); // strange bug here. ValveWHY?

                // HACK: prevent attack
                if( weapon )
                {
                    weapon->m_flNextPrimaryAttack += 0.1;
                    weapon->m_flNextSecondaryAttack += 0.1;
                }
                m_pHolstered->m_iHideHUD |= HIDEHUD_WEAPONS;
                m_pHolstered->pev->weaponmodel = 0;
                m_pHolstered->pev->viewmodel = 0;
            }
            SetThink( CProp::DeployThink );
            pev->nextthink = gpGlobals->time + 0.2;
        }
    }
    Vector target = pActivator->pev->origin + UTIL_GetAimVector(m_owner2, 1000) * 50;
    target.z = target.z + 32;
    pev->velocity = (target - VecBModelOrigin(pev)) * 10;
    Vector atarget = UTIL_VecToAngles(UTIL_GetAimVector(m_owner2, 1000));
    pev->angles.x = UTIL_AngleMod(pev->angles.x);
    pev->angles.y = UTIL_AngleMod(pev->angles.y);
    pev->angles.z = UTIL_AngleMod(pev->angles.z);
    atarget.x = UTIL_AngleMod(atarget.x);
    atarget.y = UTIL_AngleMod(atarget.y);
    atarget.z = UTIL_AngleMod(atarget.z);
    pev->avelocity.x = UTIL_AngleDiff(atarget.x, pev->angles.x) * 10;
    pev->avelocity.y = UTIL_AngleDiff(atarget.y, pev->angles.y) * 10;
    pev->avelocity.z = UTIL_AngleDiff(atarget.z, pev->angles.z) * 10;
    //pev->angles.z += (0 - pev->angles.z) * 0.06;
    if ((pActivator->pev->button & (IN_ATTACK)))
    {
        pev->velocity = UTIL_GetAimVector(m_owner2, 1000) * 1000;
        pev->avelocity.y = pev->avelocity.y*1.5 + RANDOM_FLOAT(100, -100);
        pev->avelocity.x = pev->avelocity.x*1.5 + RANDOM_FLOAT(100, -100);
        //pev->avelocity.z = pev->avelocity.z*0.5 + RANDOM_FLOAT ( 100, -100 );
    }
    if ((pActivator->pev->button & (IN_ATTACK2)))
    {
        //m_Horizontal = false;
        //pev->angles.z = 0;
    }
    //	m_Horizontal = (fabs(UTIL_AngleDiff(pev->angles.z, 90)) < 20) || ( sin(pev->angles.x/180*M_PI) > 0.1);
    //	CheckRotate();
    //ALERT( at_console, "Prop use!\n");
}

void CProp::Force(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value)
{
    if (m_owner2 != pActivator->edict())
    {
        if (pev->velocity.Length() < 100 && pActivator->IsPlayer())
            m_attacker = pActivator->edict();
        else
            return;
    }

    if ((pActivator->pev->button & (IN_ATTACK)))
    {
        pev->velocity = UTIL_GetAimVector(m_owner2, 3000) * 1000;
        pev->avelocity.y = pev->avelocity.y*1.5 + RANDOM_FLOAT(100, -100);
        pev->avelocity.x = pev->avelocity.x*1.5 + RANDOM_FLOAT(100, -100);
        //pev->avelocity.z = pev->avelocity.z*0.5 + RANDOM_FLOAT ( 100, -100 );
    }
    if ((pActivator->pev->button & (IN_ATTACK2)))
    {
        //m_Horizontal = false;
        //pev->angles.z = 0;
    }

    pev->nextthink = gpGlobals->time + m_flRespawnTime;
    SetThink(CProp::RespawnThink);
}

void CProp::CheckRotate()
{
    if( m_shape != SHAPE_CYL_H && m_shape != SHAPE_CYL_V )
    {
        UTIL_SetSize(pev, minsH, maxsH);
        return;
    }
    if( (fabs(UTIL_AngleDiff(pev->angles.z, 90)) < 20) ||
        (fabs(sin(pev->angles.x / 180 * M_PI)) > 0.3) )
        m_shape = SHAPE_CYL_H;
    else
        m_shape = SHAPE_CYL_V;

    if (m_oldshape != m_shape)
    {

        if (m_shape == SHAPE_CYL_H)
        {
            pev->angles.y += 90;

            ALERT(at_console, "setHorizontal: %f %f %f\n", pev->angles.x, pev->angles.y, pev->angles.z);

            UTIL_SetSize(pev, minsH, maxsH);
        }
        else if (m_shape == SHAPE_CYL_V)
        {
            Vector mins = pev->absmin;
            Vector maxs = pev->absmax;

            mins.z = pev->absmax.z;
            maxs.z += 10;

            // BUGBUG -- can only find 256 entities on a prop -- should be enough
            CBaseEntity *pList[256];
            int count = UTIL_EntitiesInBox( pList, 256, mins, maxs, FL_ONGROUND );
            if ( count )
            {
                for ( int i = 0; i < count; i++ )
                {
                    pList[i]->pev->origin.z += 10;
                }
            }
            pev->origin.z += 10;
            //pev->angles.y -= 90;
            UTIL_SetSize(pev, minsV, maxsV);
        }
        //DROP_TO_FLOOR(edict());
        //pev->origin.z += 0.5;
        m_oldshape = m_shape;
    }
}

void CProp::DeployThink( void )
{
    if( m_pHolstered )
    {
        if( m_pHolstered->m_pActiveItem )
        {
            m_pHolstered->m_pActiveItem->Deploy();
            CBasePlayerWeapon *weapon = (CBasePlayerWeapon *) m_pHolstered->m_pActiveItem->GetWeaponPtr();
            if( weapon )
            {
                weapon->m_flNextPrimaryAttack = 0;
                weapon->m_flNextSecondaryAttack = 0;
            }
        }
        m_pHolstered ->m_iHideHUD &= ~HIDEHUD_WEAPONS;
        m_pHolstered = NULL;
    }
    if( m_pfnThink == &CProp::DeployThink )
    {
        pev->nextthink = gpGlobals->time + m_flRespawnTime;
        SetThink( CProp::RespawnThink );
    }
}

void CProp::BounceTouch(CBaseEntity *pOther)
{
    //ALERT( at_console, "BounceTouch: %f %f %f\n", pev->angles.x, pev->angles.y, pev->angles.z );
    // only do damage if we're moving fairly fast
    DeployThink();

    if ( m_flNextAttack < gpGlobals->time && pev->velocity.Length() > 300)
    {
        entvars_t *pevOwner = VARS(m_attacker);
        if (pevOwner)
        {
            float dmg = 50 + pev->velocity.Length() / 40;
            if (pOther->edict() == m_owner2)
            {
                dmg = 5;
                if (pOther->pev->button & (IN_USE))
                {
                    dmg = 1;
                }
            }
            TraceResult tr = UTIL_GetGlobalTrace();
            ClearMultiDamage();
            pOther->TraceAttack(pevOwner, dmg, gpGlobals->v_forward, &tr, DMG_CLUB);
            ApplyMultiDamage(pev, pevOwner);
        }
        m_flNextAttack = gpGlobals->time + 1.0; // debounce
    }
    if( (pev->spawnflags & SF_PROP_BREAKABLE) && (pev->velocity.Length() > 700) )
    {
        Killed( VARS(m_attacker), GIB_NORMAL );
        Die();
    }

    pev->velocity = pev->velocity + pOther->pev->velocity;
    float dp = cos(M_PI / 180 * UTIL_AngleDiff(UTIL_VecToAngles(pev->velocity).y, pev->angles.y));
    if (pev->flags & FL_ONGROUND || fabs(pev->velocity.z) < 40)
    {
        CheckRotate();
        if (m_shape == SHAPE_CYL_H)
        {

            pev->velocity.x *= fabs(dp) * 0.8 + 0.2;
            pev->velocity.y *= fabs(dp) * 0.8 + 0.2;
            pev->velocity.z -= 20;
            pev->avelocity.x = -dp*pev->velocity.Length()* 1.5;
            pev->avelocity.y = 0;
            pev->avelocity.z = 0;
            pev->angles.z += UTIL_AngleDiff(90, pev->angles.z) * 0.7;
            //AngleThink();
        }
        else if (m_shape == SHAPE_CYL_V)
        {
            // pev->angles.z *= 0.3;
            //pev->angles.x *= 0.3;
            //AngleThink();
            //CheckRotate();
            pev->velocity.z *= m_flFloorFriction;
            pev->velocity.x *= m_flFloorFriction;
            pev->velocity.y *= m_flFloorFriction;
            pev->velocity.z -= 10;
            pev->avelocity.y = pev->avelocity.y*0.4 + RANDOM_FLOAT(30, -30);
        }
        else if( m_shape == SHAPE_SPHERE )
        {
            pev->velocity.z -= 20;
            pev->avelocity.x = -cos(M_PI / 180 * UTIL_AngleDiff(UTIL_VecToAngles(pev->velocity).y, pev->angles.y))*pev->velocity.Length()* 1.5;
            pev->avelocity.y =  -sin(M_PI / 180 * UTIL_AngleDiff(UTIL_VecToAngles(pev->velocity).y, pev->angles.y))*pev->velocity.Length()* 1.5;;
            pev->avelocity.z = 0;
        }
        else if( m_shape == SHAPE_BOX || m_shape == SHAPE_GENERIC )
        {
            pev->velocity.z *= m_flFloorFriction;
            pev->velocity.x *= m_flFloorFriction;
            pev->velocity.y *= m_flFloorFriction;
            pev->velocity.z -= 10;
        }

    }
    else
    {
        {
            pev->velocity.z *= 0.3;
            pev->velocity.y *= m_flCollideFriction;
            pev->velocity.x *= m_flCollideFriction;
            if( m_shape != SHAPE_SPHERE )
            {
                pev->avelocity.y = pev->avelocity.y*0.4 + RANDOM_FLOAT(100, -100);
                pev->avelocity.x = pev->avelocity.x*0.5 + RANDOM_FLOAT(100, -100);
            }
        }
        //pev->avelocity.z = pev->avelocity.z*0.5 + RANDOM_FLOAT ( 1, -1 );
        BounceSound();
    }
    pev->framerate = pev->velocity.Length() / 200.0;
    if (pev->framerate > 1.0)
        pev->framerate = 1;
    else if (pev->framerate < 0.2)
    {
        CheckRotate();
        AngleThink();
        if (pev->angles.z == 0 || pev->angles.z == 90)
            pev->framerate = 0;
        else
            pev->framerate = 0.2;
    }
}

void CProp::BounceSound(void)
{
    switch (RANDOM_LONG(0, 2))
    {
    case 0:	EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/grenade_hit1.wav", 0.25, ATTN_NORM);	break;
    case 1:	EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/grenade_hit2.wav", 0.25, ATTN_NORM);	break;
    case 2:	EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/grenade_hit3.wav", 0.25, ATTN_NORM);	break;
    }
}

void CProp::Spawn(void)
{

    Precache();

    if( !pev->model )
        pev->model = MAKE_STRING( "models/xash/barrel_brown.mdl" );

    if( minsH == g_vecZero )
    {
        // default barrel parameters
        minsV = Vector(-10, -10, -17);
        maxsV = Vector(10, 10, 18);
        minsH = Vector(-10, -10, -10);
        maxsH = Vector(10, 10, 13);
    }
    m_flCollideFriction = 0.7;
    m_flFloorFriction = 0.5;
    spawnOrigin = pev->origin;
    spawnAngles = pev->angles;
    m_flSpawnHealth = pev->health;
    if( !m_flRespawnTime )
        m_flRespawnTime = 20;
    pev->dmg = 100;
    PropRespawn();
}

void CProp::PropRespawn()
{
    pev->movetype = MOVETYPE_BOUNCE;
    pev->solid = SOLID_SLIDEBOX;
    pev->angles = spawnAngles;
    pev->takedamage = DAMAGE_YES;
    pev->velocity = pev->avelocity = g_vecZero;
    SET_MODEL( ENT(pev), STRING(pev->model) );
    UTIL_SetOrigin( pev, spawnOrigin );
    m_oldshape = (PropShape)-1;
    CheckRotate();
    pev->health = m_flSpawnHealth;
    SetTouch(CProp::BounceTouch);
    SetUse(CProp::Use);
    pev->effects &= ~EF_NODRAW;
    pev->framerate = 1.0f;
}

void CProp::RespawnThink()
{
    if( !(pev->spawnflags & SF_PROP_RESPAWN))
        return;
    PropRespawn();
}



void CProp::AngleThink()
{
    pev->nextthink = gpGlobals->time + m_flRespawnTime;
    SetThink(CProp::RespawnThink);
    if (!(pev->flags & FL_ONGROUND || fabs(pev->velocity.z) < 40))
    {
        m_owner2 = m_attacker = 0;
        return;
    }
    if (m_shape == SHAPE_CYL_H)
    {
        pev->angles.z += UTIL_AngleDiff(90, pev->angles.z) * 0.7;
        if (fabs(UTIL_AngleDiff(90, pev->angles.z)) > 0.1)
        {
            SetThink(CProp::AngleThink);
            pev->nextthink = gpGlobals->time + 0.1;
        }
        //ALERT( at_console, "AngleThink: %f %f %f\n", pev->angles.x, pev->angles.y, pev->angles.z );
        pev->avelocity.y = pev->avelocity.z = 0;
    }
    else if (m_shape == SHAPE_CYL_V)
    {
        if (fabs(UTIL_AngleDiff(90, pev->angles.z)) > 0.1)
        {
            SetThink(CProp::AngleThink);
            pev->nextthink = gpGlobals->time + 0.1;
        }
        pev->angles.z += UTIL_AngleDiff(0, pev->angles.z) * 0.7;
        //pev->angles.x += UTIL_AngleDiff( 0, pev->angles.x ) * 0.3;
        pev->avelocity.x = pev->avelocity.y = pev->avelocity.z = 0;
    }
    else if (m_shape == SHAPE_BOX)
    {
        Vector iangles;
        iangles.x = round( pev->angles.x / 90 ) * 90;
        iangles.y = round( pev->angles.y / 90 ) * 90;
        iangles.z = round( pev->angles.z / 90 ) * 90;
        if (fabs(UTIL_AngleDiff(iangles.x, pev->angles.x)) > 0.1 ||
            //fabs(UTIL_AngleDiff(iangles.y, pev->angles.y)) > 0.1 ||
            fabs(UTIL_AngleDiff(iangles.z, pev->angles.z)) > 0.1)
        {
            SetThink(CProp::AngleThink);
            pev->nextthink = gpGlobals->time + 0.1;
        }
        pev->angles.x += UTIL_AngleDiff(iangles.x, pev->angles.x) * 0.6;
        //pev->angles.y += UTIL_AngleDiff(iangles.y, pev->angles.y) * 0.6;
        pev->angles.z += UTIL_AngleDiff(iangles.z, pev->angles.z) * 0.6;

        pev->avelocity.x = pev->avelocity.y = pev->avelocity.z = 0;
    }
    else if (m_shape == SHAPE_NOROTATE)
    {
        pev->avelocity.x = pev->avelocity.y = pev->avelocity.z = 0;
        Vector iangles = spawnAngles;
        if (fabs(UTIL_AngleDiff(iangles.x, pev->angles.x)) > 0.1 ||
            fabs(UTIL_AngleDiff(iangles.y, pev->angles.y)) > 0.1 ||
            fabs(UTIL_AngleDiff(iangles.z, pev->angles.z)) > 0.1)
        {
            SetThink(CProp::AngleThink);
            pev->nextthink = gpGlobals->time + 0.1;
        }
        pev->angles.x += UTIL_AngleDiff(iangles.x, pev->angles.x) * 0.6;
        pev->angles.y += UTIL_AngleDiff(iangles.y, pev->angles.y) * 0.6;
        pev->angles.z += UTIL_AngleDiff(iangles.z, pev->angles.z) * 0.6;
    }
    else if (m_shape == SHAPE_GENERIC)
    {
        float ianglex = 0, ianglez = 0, imaxanglediff=360.0f;
        //  if first number is zero, it is angle
        // all other zeroes is array end
        for( int i = 0; (i < 10) && ( (i == 0 ) || m_iaCustomAnglesX[i] ); i++)
        {
            float anglediff = fabs(UTIL_AngleDiff(pev->angles.x, m_iaCustomAnglesX[i]));
            if( imaxanglediff > anglediff )
            {
                ianglex = m_iaCustomAnglesX[i];
                imaxanglediff = anglediff;
            }
        }
        imaxanglediff=360.0f;
        for( int i = 0; (i < 10) && ( (i == 0 ) || m_iaCustomAnglesZ[i] ); i++)
        {
            float anglediff = fabs(UTIL_AngleDiff(pev->angles.z, m_iaCustomAnglesZ[i]));
            if( imaxanglediff > anglediff )
            {
                ianglez = m_iaCustomAnglesZ[i];
                imaxanglediff = anglediff;
            }
        }
        if (fabs(UTIL_AngleDiff(ianglex, pev->angles.x)) > 0.1 ||
            fabs(UTIL_AngleDiff(ianglez, pev->angles.z)) > 0.1 )
        {
            SetThink(CProp::AngleThink);
            pev->nextthink = gpGlobals->time + 0.1;
        }
        pev->angles.x += UTIL_AngleDiff(ianglex, pev->angles.x) * 0.6;
        pev->angles.z += UTIL_AngleDiff(ianglez, pev->angles.z) * 0.6;
        pev->avelocity.x = pev->avelocity.y = pev->avelocity.z = 0;
    }
    pev->angles.x = UTIL_AngleMod(pev->angles.x);
    pev->angles.y = UTIL_AngleMod(pev->angles.y);
    pev->angles.z = UTIL_AngleMod(pev->angles.z);
}


int CProp::TakeDamage(entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType)
{
    Vector r = (pevInflictor->origin - pev->origin);
    if (flDamage > 200 && ((CBaseEntity*)GET_PRIVATE(ENT(pevAttacker)))->IsPlayer())
        m_attacker = ENT(pevAttacker);
    DeployThink();

    pev->velocity = r * flDamage / -7;
    pev->avelocity.x = pev->avelocity.x*0.5 + RANDOM_FLOAT(100, -100);

    // now some func_breakable code

    if ( !(pev->spawnflags & SF_PROP_BREAKABLE ) )
        return 0;
    // Breakables take double damage from the crowbar
    if ( bitsDamageType & DMG_CLUB )
        flDamage *= 2;

    // Boxes / glass / etc. don't take much poison damage, just the impact of the dart - consider that 10%
    if ( bitsDamageType & DMG_POISON )
        flDamage *= 0.1;
    g_vecAttackDir = r.Normalize();

    // do the damage
    pev->health -= flDamage;
    if (pev->health <= 0)
    {
        Killed( VARS(m_attacker), GIB_NORMAL );
        Die();
        return 0;
    }

    // Make a shard noise each time func breakable is hit.
    // Don't play shard noise if cbreakable actually died.

    DamageSound();
    return 1;
}
void CProp::KeyValue( KeyValueData* pkvd )
{
    ALERT( at_console, "%s %s\n", pkvd->szKeyName, pkvd->szValue);
    // UNDONE_WC: explicitly ignoring these fields, but they shouldn't be in the map file!
    if (FStrEq(pkvd->szKeyName, "explosion"))
    {
        if (!stricmp(pkvd->szValue, "directed"))
            m_Explosion = expDirected;
        else if (!stricmp(pkvd->szValue, "random"))
            m_Explosion = expRandom;
        else
            m_Explosion = expRandom;

        pkvd->fHandled = TRUE;
    }
    else if (FStrEq(pkvd->szKeyName, "material"))
    {
        int i = atoi( pkvd->szValue);

        // 0:glass, 1:metal, 2:flesh, 3:wood

        if ((i < 0) || (i >= matLastMaterial))
            m_Material = matWood;
        else
            m_Material = (Materials)i;

        pkvd->fHandled = TRUE;
    }
    else if (FStrEq(pkvd->szKeyName, "shape"))
    {
        int i = atoi( pkvd->szValue);

        if ((i < 0) || (i >= SHAPE_NOROTATE))
            m_shape = SHAPE_NOROTATE;
        else
            m_shape = (PropShape)i;

        pkvd->fHandled = TRUE;
    }
    else if (FStrEq(pkvd->szKeyName, "gibmodel") )
    {
        m_iszGibModel = ALLOC_STRING(pkvd->szValue);
        pkvd->fHandled = TRUE;
    }
    else if (FStrEq(pkvd->szKeyName, "explodemagnitude") )
    {
        ExplosionSetMagnitude( atoi( pkvd->szValue ) );
        pkvd->fHandled = TRUE;
    }
    else if (FStrEq(pkvd->szKeyName, "respawntime") )
    {
        m_flRespawnTime = atof( pkvd->szValue );
        pkvd->fHandled = TRUE;
    }
    else if (FStrEq(pkvd->szKeyName, "customanglesx"))
    {
        UTIL_StringToIntArray( m_iaCustomAnglesX, ARRAYSIZE( m_iaCustomAnglesX ), pkvd->szValue );
        pkvd->fHandled = TRUE;
    }
    else if (FStrEq(pkvd->szKeyName, "customanglesz"))
    {
        UTIL_StringToIntArray( m_iaCustomAnglesZ, ARRAYSIZE( m_iaCustomAnglesZ ), pkvd->szValue );
        pkvd->fHandled = TRUE;
    }
    else if (FStrEq(pkvd->szKeyName, "hmin"))
    {
        UTIL_StringToVector( minsH, pkvd->szValue );
        pkvd->fHandled = TRUE;
    }
    else if (FStrEq(pkvd->szKeyName, "hmax"))
    {
        UTIL_StringToVector( maxsH, pkvd->szValue );
        pkvd->fHandled = TRUE;
    }
    else if (FStrEq(pkvd->szKeyName, "vmin"))
    {
        UTIL_StringToVector( minsV, pkvd->szValue );
        pkvd->fHandled = TRUE;
    }
    else if (FStrEq(pkvd->szKeyName, "vmax"))
    {
        UTIL_StringToVector( maxsV, pkvd->szValue );
        pkvd->fHandled = TRUE;
    }
    else
        CBaseEntity::KeyValue( pkvd );
}