/*** * * 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 EXPORT RespawnThink(); void EXPORT AngleThink(); void EXPORT DeployThink(); void EXPORT DieThink(); 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( 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; if( !pev->model ) pev->model = MAKE_STRING( "models/xash/barrel_brown.mdl" ); 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( pGibName ); PRECACHE_MODEL( 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 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() && ( m_attacker != NULL ) ) { ExplosionCreate( pev->origin, pev->angles, m_attacker, ExplosionMagnitude(), FALSE ); RadiusDamage( pev->origin, pev, VARS(m_attacker), ExplosionMagnitude(), ExplosionMagnitude() * 2.5 , CLASS_NONE, DMG_BLAST ); } UTIL_SetSize( pev, Vector( 0, 0, 0 ), Vector( 0, 0, 0 ) ); } 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 ); SetTouch( NULL ); SetUse( NULL ); } void CProp::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { if( pev->health <= 0 ) return; 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( pev->health <= 0 ) return; 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, "setH: %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; ALERT( at_console, "setV: %f %f %f\n", pev->angles.x, pev->angles.y, pev->angles.z ); // 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 ) { if( pev->health <= 0 ) return; //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( ( pOther->edict() != m_owner2 ) && ( pev->spawnflags & SF_PROP_BREAKABLE ) && ( pev->velocity.Length() > 900 ) ) { pev->nextthink = gpGlobals->time + 0.1; SetThink( &CProp::DieThink ); } 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( 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_flSpawnHealth <= 0 ) m_flSpawnHealth = 30; if( !m_flRespawnTime ) m_flRespawnTime = 20; pev->dmg = 100; PropRespawn(); } void CProp::PropRespawn() { UTIL_SetSize( pev, Vector( 0, 0, 0 ), Vector( 0, 0, 0 ) ); pev->effects &= ~EF_NODRAW; pev->movetype = MOVETYPE_BOUNCE; pev->solid = SOLID_SLIDEBOX; pev->takedamage = DAMAGE_YES; pev->health = m_flSpawnHealth; pev->velocity = pev->avelocity = g_vecZero; pev->angles = spawnAngles; pev->deadflag = DEAD_NO; SET_MODEL( ENT( pev ), STRING( pev->model ) ); m_oldshape = ( PropShape ) - 1; CheckRotate(); SetTouch( &CProp::BounceTouch ); SetUse( &CProp::Use ); pev->framerate = 1.0f; UTIL_SetOrigin( pev, spawnOrigin ); } void CProp::RespawnThink() { if( !( pev->spawnflags & SF_PROP_RESPAWN ) ) { if( pev->health <= 0 ) { pev->nextthink = gpGlobals->time + 0.1; SetThink( &CBaseEntity::SUB_Remove ); } 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 ); } void CProp::DieThink() { Killed( VARS( m_attacker ), GIB_NORMAL ); Die(); } int CProp::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) { Vector r = ( pevInflictor->origin - pev->origin ); if( ( !m_attacker || ( pev->velocity.Length() < 700 ) ) && ( (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 ); ALERT( at_console, "Takedmg: %s %s %f %f\n", STRING( pevInflictor->classname ), STRING( pevAttacker->classname ), flDamage, pev->health ); // now some func_breakable code if( !( pev->spawnflags & SF_PROP_BREAKABLE ) ) return 0; if( pev->health <= 0 ) return; // 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 ) { // delayed explode SetThink( &CProp::DieThink ); pev->nextthink = gpGlobals->time + 0.2; 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 ); }