Rotating Pickups/Code: Difference between revisions

From Valve Developer Community
Jump to navigation Jump to search
(New page: <pre> //================ Coder: Maestro ==============// // // Purpose: Make a healthkit that rotates // //=============================================================================...)
 
m (clean up, added uncategorised, deadend tags)
 
(2 intermediate revisions by 2 users 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 );
void FallThink( void ) { return; } // Override the function that makes items fall to the ground
void RotateThink( void );


CBaseEntity* Respawn( void );
bool MyTouch( CBasePlayer *pPlayer );
void Materialize( void );


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


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


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" ),
END_SEND_TABLE()


DEFINE_THINKFUNC( RotateThink ),
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();
SetAbsOrigin(OriginalLocation);


SetMoveType( MOVETYPE_NONE ); // It will only rotate, not move
UseClientSideAnimation();
SetSolid( SOLID_BBOX ); // It is solid
SetModel(PICKUP_MODEL);
SetCollisionGroup( COLLISION_GROUP_WEAPON ); // And it can collide with stuff


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


UpdateSpawnPosition( GetAbsOrigin() ); // We update our position relative to the ground
// Grab the highest point on the model before we change the bounding box
 
MdlTop = GetAbsOrigin();
// Set the x angle, since it will never change
MdlTop.z += GetModelPtr()->hull_max().z;
QAngle angle = GetAbsAngles();
angle.x = 45;
SetSolid(SOLID_NONE);
SetAbsAngles( angle );
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
m_takedamage = DAMAGE_EVENTS_ONLY;
SetSize(-OBBSize,OBBSize); // Resize the bounding box
 
SetModel( "models/items/healthkit.mdl" ); // Set the model we'll use
AddEffects(EF_NOSHADOW);
 
// Start thinking in 0.01 seconds
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" );
return true;
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
return false;
}


Respawn(); // Respawn our pickup
// Disappear
CBaseEntity* CRotatingPickup::Respawn()
{
SetTouch(NULL);
m_bRespawning = true;
SetThink ( &CRotatingPickup::Materialize );
SetNextThink( gpGlobals->curtime + m_fRespawnTime );


return true;
return this;
}
}


return false;
// Reappear
void CRotatingPickup::Materialize()
{
EmitSound("AlyxEmp.Charge");
m_bRespawning = false;
SetTouch(&CItem::ItemTouch);
}
}
</source>
== Client ==
<source lang=cpp>
#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


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Purpose: Initiate the respawning process
// Rotating health kit. Heals the player when picked up.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
CBaseEntity* CRotatingPickup::Respawn( void )
class C_RotatingPickup : public C_BaseAnimating
{
{
// It can't be touched and it can't be seen
DECLARE_CLASS( C_RotatingPickup, C_BaseAnimating );
SetTouch( NULL );
DECLARE_CLIENTCLASS();
AddEffects( EF_NODRAW );
public:
C_RotatingPickup() {
ClientRotAng = QAngle(45,0,0);
m_bRespawning = m_bRespawning_Cache = false;
}
void Spawn() { ClientThink(); }


//Reset the movetypes
bool IsRespawning();
SetMoveType( MOVETYPE_NONE );
void ClientThink();
SetSolid( SOLID_BBOX );
void PostDataUpdate(DataUpdateType_t updateType);
SetCollisionGroup( COLLISION_GROUP_WEAPON );
bool ShouldDraw();


UTIL_SetOrigin( this, RespawnPosition ); //Get our respawn position from earlier
bool m_bRespawning;
bool m_bRespawning_Cache;


// Reset the angles
private:
QAngle angle = GetAbsAngles();
QAngle ClientRotAng; // m_angRotation is stomped sometimes (CItem returning the ent to spawn position?)
angle.x = 45;
};
SetAbsAngles( angle );


RemoveAllDecals(); //Remove any decals
LINK_ENTITY_TO_CLASS( item_rotating, C_RotatingPickup );


//Start thinking when the pickup should appear again
IMPLEMENT_CLIENTCLASS_DT( C_RotatingPickup, DT_RotatingPickup,CRotatingPickup )
SetThink ( &CRotatingPickup::Materialize );
RecvPropBool( RECVINFO(m_bRespawning) ),
SetNextThink( gpGlobals->curtime + m_iRespawnTime );
END_RECV_TABLE()


return this;
inline bool C_RotatingPickup::IsRespawning()
{
return m_bRespawning;
}
}


//-----------------------------------------------------------------------------
void C_RotatingPickup::ClientThink()
// Purpose: Finalize the respawning process
//-----------------------------------------------------------------------------
void CRotatingPickup::Materialize( void )
{
{
if ( IsEffectActive( EF_NODRAW ) )
if (IsAbsQueriesValid())
{
{
//Changing from invisible state to visible.
// Rotate
RemoveEffects( EF_NODRAW );
ClientRotAng.y += ITEM_ROTATION_RATE * gpGlobals->frametime;
if ( ClientRotAng.y >= 360 )
ClientRotAng.y -= 360;
 
SetAbsAngles( ClientRotAng );
}
}


EmitSound( "AlyxEmp.Charge" ); //Emit a sound
SetNextClientThink(CLIENT_THINK_ALWAYS);
}


SetTouch( &CRotatingPickup::ItemTouch ); //Reset our think functions
void C_RotatingPickup::PostDataUpdate(DataUpdateType_t updateType)
{
if (m_bRespawning_Cache != m_bRespawning)
{
// Appear/disappear
UpdateVisibility();
ClientRotAng.y = 0;
m_bRespawning_Cache = m_bRespawning;
}


SetThink( &CRotatingPickup::RotateThink ); // Start rotating again
return BaseClass::PostDataUpdate(updateType);
SetNextThink( gpGlobals->curtime + 0.01f ); // Think in 0.01 sec
}
}


//-----------------------------------------------------------------------------
bool C_RotatingPickup::ShouldDraw()
// Purpose: Make our model rotate
//-----------------------------------------------------------------------------
void CRotatingPickup::RotateThink( void )
{
{
// This makes sure the model rotates independent of the fps
return !IsRespawning() && BaseClass::ShouldDraw();
float dt = gpGlobals->curtime - GetLastThink();  
}
 
</source>
QAngle angles = GetAbsAngles(); //Get the current angles


// Set the angles according to the rotation rate and fps
== FGD ==
angles.y += ( ITEM_ROTATION_RATE * dt );


if ( angles.y >= 360 ) // If the rotation is more than 360,
<source lang=php>
angles.y -= 360; // subtract 360 to avoid large variables
@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>


SetAbsAngles( angles ); // Set the angles now
{{Uncategorized|date=January 2024}}
SetNextThink( gpGlobals->curtime + 0.01f ); // Think again in 0.01 sec
}
</pre>

Latest revision as of 09: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