|
|
|
/***
|
|
|
|
*
|
|
|
|
* 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 );
|
|
|
|
}
|