/*** * * 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( (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; 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( (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 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( (pev->spawnflags & SF_PROP_BREAKABLE) && (pev->velocity.Length() > 999) ) { 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 (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; 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 ); }