Rotating Pickups/Code
Jump to navigation
Jump to search
Server
#include "cbase.h"
#include "items.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#define PICKUP_DECAL "decals/item_base"
#define PICKUP_MODEL "models/items/healthkit.mdl"
#define PICKUP_MIN_HEIGHT 50
int PickupDecalIndex; // set by CRotatingPickup::Precache()
#define SF_SUPPRESS_PICKUP_DECAL 0x00000002
//-----------------------------------------------------------------------------
// Rotating health kit. Heals the player when picked up.
//-----------------------------------------------------------------------------
class CRotatingPickup : public CItem
{
DECLARE_CLASS( CRotatingPickup, CItem );
DECLARE_DATADESC();
DECLARE_SERVERCLASS();
public:
CRotatingPickup();
void Spawn();
void Activate();
void Precache();
bool MyTouch( CBasePlayer *pPlayer );
CBaseEntity* Respawn();
void Materialize();
int m_iHealthToGive;
float m_fRespawnTime;
CNetworkVar(bool, m_bRespawning);
private:
Vector MdlTop;
};
LINK_ENTITY_TO_CLASS( item_rotating, CRotatingPickup );
PRECACHE_REGISTER( item_rotating );
BEGIN_DATADESC( CRotatingPickup )
DEFINE_KEYFIELD( m_iHealthToGive, FIELD_INTEGER, "givehealth"),
DEFINE_KEYFIELD( m_fRespawnTime, FIELD_FLOAT, "respawntime"),
END_DATADESC()
IMPLEMENT_SERVERCLASS_ST( CRotatingPickup, DT_RotatingPickup )
SendPropBool( SENDINFO( m_bRespawning )),
SendPropExclude( "DT_BaseEntity", "m_angRotation" ),
END_SEND_TABLE()
CRotatingPickup::CRotatingPickup()
{
if ( m_iHealthToGive <= 0 )
m_iHealthToGive = 25;
if ( m_fRespawnTime <= 0 )
m_fRespawnTime = 20;
}
void CRotatingPickup::Spawn()
{
// CItem is designed for Vphys objects, so we need to undo a couple of things its spawn() does
Vector OriginalLocation = GetAbsOrigin();
BaseClass::Spawn();
VPhysicsDestroyObject();
SetAbsOrigin(OriginalLocation);
UseClientSideAnimation();
SetModel(PICKUP_MODEL);
SetMoveType(MOVETYPE_NONE);
// Grab the highest point on the model before we change the bounding box
MdlTop = GetAbsOrigin();
MdlTop.z += GetModelPtr()->hull_max().z;
SetSolid(SOLID_NONE);
CollisionProp()->UseTriggerBounds(true,6); // Reign in the volume added to the trigger collision box
Vector OBBSize = Vector(CollisionProp()->OBBSize().Length() / 2); // need to use length as the model will be rotated at 45 degrees on clients
SetSize(-OBBSize,OBBSize); // Resize the bounding box
AddEffects(EF_NOSHADOW);
}
void CRotatingPickup::Activate()
{
BaseClass::Activate();
// Ensure minimum distance above a standable surfare
trace_t tr;
UTIL_TraceLine(MdlTop,MdlTop + Vector(0,0,-PICKUP_MIN_HEIGHT),MASK_PLAYERSOLID,this,COLLISION_GROUP_NONE,&tr); // measuring from MdlTop
if(tr.DidHit())
{
if ( !HasSpawnFlags( SF_SUPPRESS_PICKUP_DECAL ) )
engine->StaticDecal(tr.endpos,PickupDecalIndex,0,0,false); // mark the location of the pickup
SetAbsOrigin( GetAbsOrigin() + ( Vector(0,0,PICKUP_MIN_HEIGHT*(1-tr.fraction)) ) );
}
}
void CRotatingPickup::Precache()
{
PrecacheModel( PICKUP_MODEL );
PrecacheScriptSound( "HealthKit.Touch" );
PrecacheScriptSound( "AlyxEmp.Charge" );
PickupDecalIndex = UTIL_PrecacheDecal(PICKUP_DECAL, true );
}
// Called from CItem::ItemTouch()
bool CRotatingPickup::MyTouch( CBasePlayer *pPlayer )
{
if ( pPlayer && pPlayer->GetHealth() < pPlayer->GetMaxHealth() )
{
pPlayer->TakeHealth( m_iHealthToGive, DMG_GENERIC );
CSingleUserRecipientFilter PlayerFilter( pPlayer );
PlayerFilter.MakeReliable();
UserMessageBegin( PlayerFilter, "ItemPickup" );
WRITE_STRING( GetClassname() );
MessageEnd();
EmitSound( PlayerFilter, pPlayer->entindex(), "HealthKit.Touch" ); // this should be done by the HUD really
Respawn();
return true;
}
return false;
}
// Disappear
CBaseEntity* CRotatingPickup::Respawn()
{
SetTouch(NULL);
m_bRespawning = true;
SetThink ( &CRotatingPickup::Materialize );
SetNextThink( gpGlobals->curtime + m_fRespawnTime );
return this;
}
// Reappear
void CRotatingPickup::Materialize()
{
EmitSound("AlyxEmp.Charge");
m_bRespawning = false;
SetTouch(&CItem::ItemTouch);
}
Client
#include "cbase.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#define ITEM_ROTATION_RATE ( 360.0f / 4.0f )
#define PICKUP_MIN_HEIGHT 50
//-----------------------------------------------------------------------------
// Rotating health kit. Heals the player when picked up.
//-----------------------------------------------------------------------------
class C_RotatingPickup : public C_BaseAnimating
{
DECLARE_CLASS( C_RotatingPickup, C_BaseAnimating );
DECLARE_CLIENTCLASS();
public:
C_RotatingPickup() {
ClientRotAng = QAngle(45,0,0);
m_bRespawning = m_bRespawning_Cache = false;
}
void Spawn() { ClientThink(); }
bool IsRespawning();
void ClientThink();
void PostDataUpdate(DataUpdateType_t updateType);
bool ShouldDraw();
bool m_bRespawning;
bool m_bRespawning_Cache;
private:
QAngle ClientRotAng; // m_angRotation is stomped sometimes (CItem returning the ent to spawn position?)
};
LINK_ENTITY_TO_CLASS( item_rotating, C_RotatingPickup );
IMPLEMENT_CLIENTCLASS_DT( C_RotatingPickup, DT_RotatingPickup,CRotatingPickup )
RecvPropBool( RECVINFO(m_bRespawning) ),
END_RECV_TABLE()
inline bool C_RotatingPickup::IsRespawning()
{
return m_bRespawning;
}
void C_RotatingPickup::ClientThink()
{
if (IsAbsQueriesValid())
{
// Rotate
ClientRotAng.y += ITEM_ROTATION_RATE * gpGlobals->frametime;
if ( ClientRotAng.y >= 360 )
ClientRotAng.y -= 360;
SetAbsAngles( ClientRotAng );
}
SetNextClientThink(CLIENT_THINK_ALWAYS);
}
void C_RotatingPickup::PostDataUpdate(DataUpdateType_t updateType)
{
if (m_bRespawning_Cache != m_bRespawning)
{
// Appear/disappear
UpdateVisibility();
ClientRotAng.y = 0;
m_bRespawning_Cache = m_bRespawning;
}
return BaseClass::PostDataUpdate(updateType);
}
bool C_RotatingPickup::ShouldDraw()
{
return !IsRespawning() && BaseClass::ShouldDraw();
}
FGD
@PointClass base(Item) studio("models/items/healthkit.mdl") = item_rotating: "A spinning health kit"
[
givehealth(integer) : "Health to give" : 25
respawntime(float) : "Respawn time" : 20
spawnflags(Flags) =
[
2 : "Suppress location decal" : 0
]
]