Rotating Pickups/Code

From Valve Developer Community
Jump to: navigation, search
Dead End - Icon.png
This article has no links to other VDC articles. Please help improve this article by adding links that are relevant to the context within the existing text.
January 2024

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
	]
]
Wikipedia - Letter.png
This article has not been added to any content categories. Please help out by adding categories.
January 2024