Rotating Pickups/Code: Difference between revisions

From Valve Developer Community
Jump to navigation Jump to search
No edit summary
m (clean up, added uncategorised, deadend tags)
 
(One intermediate revision by one other user not shown)
Line 1: Line 1:
<pre>
{{Dead end|date=January 2024}}
//================ Coder: Maestro ==============//
__TOC__
//
== Server ==
// Purpose: Make a healthkit that rotates
//
//=============================================================================//


#include "cbase.h" // The base for all entities
<source lang=cpp>
#include "player.h" // The player itself
#include "cbase.h"
#include "items.h" // Where we derive our class from
#include "items.h"
#include "engine/IEngineSound.h" // This takes care of the sounds


// memdbgon must be the last include file in a .cpp file!!!
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#include "tier0/memdbgon.h"


// This defines the size of a box around our pickup
#define PICKUP_DECAL "decals/item_base"
#define ITEM_PICKUP_BOX_BLOAT          24
#define PICKUP_MODEL "models/items/healthkit.mdl"
 
#define PICKUP_MIN_HEIGHT 50
// A full rotation divided by the number of seconds it takes to rotate
int PickupDecalIndex; // set by CRotatingPickup::Precache()
#define ITEM_ROTATION_RATE            ( 360.0f / 9.0f )
 
// Define what will be the default health inside
#define         DEFAULT_HEALTH_TO_GIVE        25
 
// Define what will be the default respawn time
#define        DEFAULT_RESPAWN_TIME          20


#define SF_SUPPRESS_PICKUP_DECAL 0x00000002


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Small rotating health kit. Heals the player when picked up.
// Rotating health kit. Heals the player when picked up.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
class CRotatingPickup : public CItem
class CRotatingPickup : public CItem
{
{
public:
DECLARE_CLASS( CRotatingPickup, CItem );
DECLARE_CLASS( CRotatingPickup, CItem );
DECLARE_DATADESC();
DECLARE_DATADESC();
DECLARE_SERVERCLASS();
public:


CRotatingPickup();
CRotatingPickup();


void Spawn( void );
void Spawn();
void Precache( void );
void Activate();
void Precache();


bool MyTouch( CBasePlayer *pPlayer );
bool MyTouch( CBasePlayer *pPlayer );
void FallThink( void ) { return; } // Override the function that makes items fall to the ground
void RotateThink( void );


CBaseEntity* Respawn( void );
CBaseEntity* Respawn();
void Materialize( void );
void Materialize();


protected:
int m_iHealthToGive;
 
float m_fRespawnTime;
int m_iHealthToGive;
CNetworkVar(bool, m_bRespawning);
int m_iRespawnTime;
Vector RespawnPosition;


private:
private:
 
Vector MdlTop;
void UpdateSpawnPosition( Vector originalSpawnPosition );
 
};
};


Line 65: Line 51:


BEGIN_DATADESC( CRotatingPickup )
BEGIN_DATADESC( CRotatingPickup )
DEFINE_KEYFIELD( m_iHealthToGive, FIELD_INTEGER, "givehealth"),
DEFINE_KEYFIELD( m_fRespawnTime, FIELD_FLOAT, "respawntime"),
END_DATADESC()


DEFINE_KEYFIELD( m_iHealthToGive, FIELD_INTEGER, "health"),
IMPLEMENT_SERVERCLASS_ST( CRotatingPickup, DT_RotatingPickup )
DEFINE_KEYFIELD( m_iRespawnTime, FIELD_INTEGER, "respawntime"),
SendPropBool( SENDINFO( m_bRespawning )),
 
SendPropExclude( "DT_BaseEntity", "m_angRotation" ),
DEFINE_THINKFUNC( RotateThink ),
END_SEND_TABLE()


END_DATADESC()
//-----------------------------------------------------------------------------
// Purpose: Initialize member variables
//-----------------------------------------------------------------------------
CRotatingPickup::CRotatingPickup()
CRotatingPickup::CRotatingPickup()
{
{
m_bShouldFall = false;
if ( m_iHealthToGive <= 0 )
if ( m_iHealthToGive <= 0 )
m_iHealthToGive = DEFAULT_HEALTH_TO_GIVE;
m_iHealthToGive = 25;


if ( m_iRespawnTime <= 0 )
if ( m_fRespawnTime <= 0 )
m_iRespawnTime = DEFAULT_RESPAWN_TIME;
m_fRespawnTime = 20;
}
}


//-----------------------------------------------------------------------------
void CRotatingPickup::Spawn()
// Purpose:
//-----------------------------------------------------------------------------
void CRotatingPickup::Spawn( void )
{
{
BaseClass::Spawn(); //Spawn the baseclass
// CItem is designed for Vphys objects, so we need to undo a couple of things its spawn() does
 
Vector OriginalLocation = GetAbsOrigin();
Precache(); // Make sure the assets are loaded
BaseClass::Spawn();
 
VPhysicsDestroyObject();
SetMoveType( MOVETYPE_NONE ); // It will only rotate, not move
SetAbsOrigin(OriginalLocation);
SetSolid( SOLID_BBOX ); // It is solid
SetCollisionGroup( COLLISION_GROUP_WEAPON ); // And it can collide with stuff


CollisionProp()->UseTriggerBounds( true, ITEM_PICKUP_BOX_BLOAT ); // Create a collision trigger around the object
UseClientSideAnimation();
SetTouch(&CRotatingPickup::ItemTouch); // ItemTouch is a function in our base class that takes care of touches
SetModel(PICKUP_MODEL);


UpdateSpawnPosition( GetAbsOrigin() ); // We update our position relative to the ground
SetMoveType(MOVETYPE_NONE);


// Set the x angle, since it will never change
// Grab the highest point on the model before we change the bounding box
QAngle angle = GetAbsAngles();
MdlTop = GetAbsOrigin();
angle.x = 45;
MdlTop.z += GetModelPtr()->hull_max().z;
SetAbsAngles( angle );
 
SetSolid(SOLID_NONE);
m_takedamage = DAMAGE_EVENTS_ONLY;
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
SetModel( "models/items/healthkit.mdl" ); // Set the model we'll use
SetSize(-OBBSize,OBBSize); // Resize the bounding box
 
// Start thinking in 0.01 seconds
AddEffects(EF_NOSHADOW);
SetThink( &CRotatingPickup::RotateThink );
SetNextThink( gpGlobals->curtime + 0.01f );
}
}


//-----------------------------------------------------------------------------
void CRotatingPickup::Activate()
// Purpose: Make sure the engine loads the sounds and models before they are used
//-----------------------------------------------------------------------------
void CRotatingPickup::Precache( void )
{
{
PrecacheModel( "models/items/healthkit.mdl" ); // Change this to get another model
BaseClass::Activate();
PrecacheScriptSound( "HealthKit.Touch" ); // scripts/game_sounds_items.txt
 
// 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::UpdateSpawnPosition( Vector originalPosition )
void CRotatingPickup::Precache()
{
{
// Create local variables
PrecacheModel( PICKUP_MODEL );
trace_t tr; // The trace
PrecacheScriptSound( "HealthKit.Touch" );
Vector end, dir, final; // The vectors
PrecacheScriptSound( "AlyxEmp.Charge" );
QAngle down; // The angles
PickupDecalIndex = UTIL_PrecacheDecal(PICKUP_DECAL, true );
 
down.y = -90; //Make angle point down
 
AngleVectors( down, &dir); //Make the vector point to the angle
 
end = originalPosition + dir * MAX_TRACE_LENGTH; // Get the end point
 
// Trace a line down to the ground
UTIL_TraceLine( originalPosition, end, MASK_SOLID, NULL, COLLISION_GROUP_NONE, &tr );
 
final = tr.endpos; // final is now the position of the ground right beneath our entity
 
final.z += 50; // Add 50 units in height
 
SetAbsOrigin( final ); // Update our position to be 50 units above the ground
RespawnPosition = final; // Store our position for easy access later
}
}


//-----------------------------------------------------------------------------
// Called from CItem::ItemTouch()
// Purpose: Give the player health and plays a sound
// Input  : *pPlayer -
// Output :
//-----------------------------------------------------------------------------
bool CRotatingPickup::MyTouch( CBasePlayer *pPlayer )
bool CRotatingPickup::MyTouch( CBasePlayer *pPlayer )
{
{
//Check the pointer and check if the player needs more health
if ( pPlayer && pPlayer->GetHealth() < pPlayer->GetMaxHealth() )  
if ( pPlayer && pPlayer->GetHealth() < pPlayer->GetMaxHealth() )  
{
{
pPlayer->TakeHealth( m_iHealthToGive, DMG_GENERIC );
pPlayer->TakeHealth( m_iHealthToGive, DMG_GENERIC );
CSingleUserRecipientFilter PlayerFilter( pPlayer );
PlayerFilter.MakeReliable();


// This code is related to the hud
UserMessageBegin( PlayerFilter, "ItemPickup" );
CSingleUserRecipientFilter user( pPlayer );
user.MakeReliable();
 
UserMessageBegin( user, "ItemPickup" );
WRITE_STRING( GetClassname() );
WRITE_STRING( GetClassname() );
MessageEnd();
MessageEnd();
EmitSound( PlayerFilter, pPlayer->entindex(), "HealthKit.Touch" ); // this should be done by the HUD really


// Output the sound sound
Respawn();
CPASAttenuationFilter filter( pPlayer, "HealthKit.Touch" );
EmitSound( filter, pPlayer->entindex(), "HealthKit.Touch" );
 
//Msg("A player picked up something!\n" ); //Uncomment this line to get a note in the console when picked up
 
Respawn(); // Respawn our pickup
 
return true;
return true;
}
}
Line 186: Line 139:
}
}


//-----------------------------------------------------------------------------
// Disappear
// Purpose: Initiate the respawning process
CBaseEntity* CRotatingPickup::Respawn()
//-----------------------------------------------------------------------------
CBaseEntity* CRotatingPickup::Respawn( void )
{
{
// It can't be touched and it can't be seen
SetTouch(NULL);
SetTouch( NULL );
m_bRespawning = true;
AddEffects( EF_NODRAW );
SetThink ( &CRotatingPickup::Materialize );
SetNextThink( gpGlobals->curtime + m_fRespawnTime );


//Reset the movetypes
return this;
SetMoveType( MOVETYPE_NONE );
}
SetSolid( SOLID_BBOX );
SetCollisionGroup( COLLISION_GROUP_WEAPON );


UTIL_SetOrigin( this, RespawnPosition ); //Get our respawn position from earlier
// Reappear
void CRotatingPickup::Materialize()
{
EmitSound("AlyxEmp.Charge");
m_bRespawning = false;
SetTouch(&CItem::ItemTouch);
}
</source>


// Reset the angles
== Client ==
QAngle angle = GetAbsAngles();
angle.x = 45;
SetAbsAngles( angle );


RemoveAllDecals(); //Remove any decals
<source lang=cpp>
#include "cbase.h"


//Start thinking when the pickup should appear again
// memdbgon must be the last include file in a .cpp file!!!
SetThink ( &CRotatingPickup::Materialize );
#include "tier0/memdbgon.h"
SetNextThink( gpGlobals->curtime + m_iRespawnTime );


return this;
#define ITEM_ROTATION_RATE ( 360.0f / 4.0f )
}
#define PICKUP_MIN_HEIGHT 50


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Purpose: Finalize the respawning process
// Rotating health kit. Heals the player when picked up.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CRotatingPickup::Materialize( void )
class C_RotatingPickup : public C_BaseAnimating
{
{
if ( IsEffectActive( EF_NODRAW ) )
DECLARE_CLASS( C_RotatingPickup, C_BaseAnimating );
{
DECLARE_CLIENTCLASS();
//Changing from invisible state to visible.
public:
RemoveEffects( EF_NODRAW );
C_RotatingPickup() {
ClientRotAng = QAngle(45,0,0);
m_bRespawning = m_bRespawning_Cache = false;
}
}
void Spawn() { ClientThink(); }


EmitSound( "AlyxEmp.Charge" ); //Emit a sound
bool IsRespawning();
void ClientThink();
void PostDataUpdate(DataUpdateType_t updateType);
bool ShouldDraw();


SetTouch( &CRotatingPickup::ItemTouch ); //Reset our think functions
bool m_bRespawning;
bool m_bRespawning_Cache;


SetThink( &CRotatingPickup::RotateThink ); // Start rotating again
private:
SetNextThink( gpGlobals->curtime + 0.01f ); // Think in 0.01 sec
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()
// Purpose: Make our model rotate
//-----------------------------------------------------------------------------
void CRotatingPickup::RotateThink( void )
{
{
// This makes sure the model rotates independent of the fps
if (IsAbsQueriesValid())
float dt = gpGlobals->curtime - GetLastThink();  
{
// Rotate
ClientRotAng.y += ITEM_ROTATION_RATE * gpGlobals->frametime;
if ( ClientRotAng.y >= 360 )
ClientRotAng.y -= 360;


QAngle angles = GetAbsAngles(); //Get the current angles
SetAbsAngles( ClientRotAng );
}


// Set the angles according to the rotation rate and fps
SetNextClientThink(CLIENT_THINK_ALWAYS);
angles.y += ( ITEM_ROTATION_RATE * dt );  
}
 
void C_RotatingPickup::PostDataUpdate(DataUpdateType_t updateType)
{
if (m_bRespawning_Cache != m_bRespawning)
{
// Appear/disappear
UpdateVisibility();
ClientRotAng.y = 0;
m_bRespawning_Cache = m_bRespawning;
}


if ( angles.y >= 360 ) // If the rotation is more than 360,
return BaseClass::PostDataUpdate(updateType);
angles.y -= 360; // subtract 360 to avoid large variables
}


SetAbsAngles( angles ); // Set the angles now
bool C_RotatingPickup::ShouldDraw()
SetNextThink( gpGlobals->curtime + 0.01f ); // Think again in 0.01 sec
{
return !IsRespawning() && BaseClass::ShouldDraw();
}
}
</pre>
</source>
 
== FGD ==


== See Also ==
<source lang=php>
@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
]
]
</source>


*[[Rotating Pickups|Rotating Pickups]]
{{Uncategorized|date=January 2024}}

Latest revision as of 10:09, 21 January 2024

Dead End - Icon.png
This article has no Wikipedia icon links to other VDC articles. Please help improve this article by adding links Wikipedia icon 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 Wikipedia icon categories. Please help out by Wikipedia icon adding categories.
January 2024