Rotating Pickups: Difference between revisions
		
		
		
		
		
		Jump to navigation
		Jump to search
		
				
		
		
	
Islandstone (talk | contribs) No edit summary  | 
				Islandstone (talk | contribs)  mNo edit summary  | 
				||
| Line 126: | Line 126: | ||
	trace_t tr; // The trace  | 	trace_t tr; // The trace  | ||
	Vector end, dir, final; // The vectors  | 	Vector end, dir, final; // The vectors  | ||
	QAngle down; //   | 	QAngle down; // The angles  | ||
	down.y = -90; //Make angle point down  | 	down.y = -90; //Make angle point down  | ||
| Line 142: | Line 142: | ||
	SetAbsOrigin( final ); // Update our position to be 50 units above the ground  | 	SetAbsOrigin( final ); // Update our position to be 50 units above the ground  | ||
	RespawnPosition = final; // Store our position for easy access  | 	RespawnPosition = final; // Store our position for easy access later  | ||
}  | }  | ||
| Line 150: | Line 150: | ||
void CRotatingPickup::Precache( void )  | void CRotatingPickup::Precache( void )  | ||
{  | {  | ||
	PrecacheModel("models/items/healthkit.mdl"); // Change this to get another model  | 	PrecacheModel( "models/items/healthkit.mdl" ); // Change this to get another model  | ||
	PrecacheScriptSound( "HealthKit.Touch" );  // scripts/game_sounds_items.txt  | 	PrecacheScriptSound( "HealthKit.Touch" );  // scripts/game_sounds_items.txt  | ||
}  | }  | ||
| Line 156: | Line 156: | ||
//-----------------------------------------------------------------------------  | //-----------------------------------------------------------------------------  | ||
// Purpose: Give the player health and   | // Purpose: Give the player health and plays a sound  | ||
// Input  : *pPlayer -    | // Input  : *pPlayer -    | ||
// Output :    | // Output :    | ||
Revision as of 07:19, 26 December 2008
This tutorial will show you how to make a rotating pickup, like the ones in TF2. It can easily be modified to use other models, rotate at other speeds and angles.
//================ Coder: Islandstone		Mod: Operation Nexus ==============//
//
// Purpose: Make a healthkit that rotates
//
//=============================================================================//
#include "cbase.h" // The base for all entities
#include "player.h" // The player itself
#include "items.h" // Where we derive our class from
#include "engine/IEngineSound.h" // This takes care of the sounds
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
// This defines the size of a box around our pickup
#define		ITEM_PICKUP_BOX_BLOAT           24
// A full rotation divided by the number of seconds it takes to rotate
#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
//-----------------------------------------------------------------------------
// Small rotating health kit. Heals the player when picked up.
//-----------------------------------------------------------------------------
class CRotatingPickup : public CItem
{
public:
	DECLARE_CLASS( CRotatingPickup, CItem );
	DECLARE_DATADESC();
	CRotatingPickup();
	void			Spawn( void );
	void			Precache( void );
	bool			MyTouch( CBasePlayer *pPlayer );
	void			FallThink( void ) { return; } // Override the function that makes items fall to the ground
	void			RotateThink( void );
	CBaseEntity*		Respawn( void );
	void			Materialize( void );
protected:
	int			m_iHealthToGive;
	int			m_iRespawnTime;
	Vector			RespawnPosition;
private:
	void			UpdateSpawnPosition( Vector originalSpawnPosition );
};
LINK_ENTITY_TO_CLASS( item_rotating, CRotatingPickup );
PRECACHE_REGISTER( item_rotating );
BEGIN_DATADESC( CRotatingPickup )
DEFINE_KEYFIELD( m_iHealthToGive, FIELD_INTEGER, "health"),
DEFINE_KEYFIELD( m_iRespawnTime, FIELD_INTEGER, "respawntime"),
DEFINE_THINKFUNC( RotateThink ),
END_DATADESC()
//-----------------------------------------------------------------------------
// Purpose: Initialize member variables
//-----------------------------------------------------------------------------
CRotatingPickup::CRotatingPickup()
{
	m_bShouldFall = false;
	if ( m_iHealthToGive <= 0 )
		m_iHealthToGive = DEFAULT_HEALTH_TO_GIVE;
	if ( m_iRespawnTime <= 0 )
		m_iRespawnTime = DEFAULT_RESPAWN_TIME;
}
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CRotatingPickup::Spawn( void )
{
	BaseClass::Spawn(); //Spawn the baseclass
	Precache();	// Make sure the assets are loaded
	SetMoveType( MOVETYPE_NONE ); // It will only rotate, not move
	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
	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
	// Set the x angle, since it will never change
	QAngle angle = GetAbsAngles();
	angle.x = 45;
	SetAbsAngles( angle );
	m_takedamage = DAMAGE_EVENTS_ONLY;
	SetModel( "models/items/healthkit.mdl" ); // Set the model we'll use
	// Start thinking in 0.01 seconds
	SetThink( &CRotatingPickup::RotateThink );
	SetNextThink( gpGlobals->curtime + 0.01f );
}
void CRotatingPickup::UpdateSpawnPosition( Vector originalPosition )
{
	// Create local variables
	trace_t tr; // The trace
	Vector end, dir, final; // The vectors
	QAngle down; // The angles
	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
}
//-----------------------------------------------------------------------------
// 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
	PrecacheScriptSound( "HealthKit.Touch" );  // scripts/game_sounds_items.txt
}
//-----------------------------------------------------------------------------
// Purpose: Give the player health and plays a sound
// Input  : *pPlayer - 
// Output : 
//-----------------------------------------------------------------------------
bool CRotatingPickup::MyTouch( CBasePlayer *pPlayer )
{
	//Check the pointer and check if the player needs more health
	if ( pPlayer && pPlayer->GetHealth() < pPlayer->GetMaxHealth() ) 
	{
		pPlayer->TakeHealth( m_iHealthToGive, DMG_GENERIC );
		// This code is related to the hud
		CSingleUserRecipientFilter user( pPlayer );
		user.MakeReliable();
		UserMessageBegin( user, "ItemPickup" );
		WRITE_STRING( GetClassname() );
		MessageEnd();
		// Output the sound sound
		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 false;
}
//-----------------------------------------------------------------------------
// Purpose: Initiate the respawning process
//-----------------------------------------------------------------------------
CBaseEntity* CRotatingPickup::Respawn( void )
{
	// It can't be touched and it can't be seen
	SetTouch( NULL );
	AddEffects( EF_NODRAW );
	//Reset the movetypes
	SetMoveType( MOVETYPE_NONE );
	SetSolid( SOLID_BBOX );
	SetCollisionGroup( COLLISION_GROUP_WEAPON );
	UTIL_SetOrigin( this, RespawnPosition ); //Get our respawn position from earlier
	// Reset the angles
	QAngle angle = GetAbsAngles();
	angle.x = 45;
	SetAbsAngles( angle );
	RemoveAllDecals(); //Remove any decals
	//Start thinking when the pickup should appear again
	SetThink ( &CRotatingPickup::Materialize );
	SetNextThink( gpGlobals->curtime + m_iRespawnTime );
	return this;
}
//-----------------------------------------------------------------------------
// Purpose: Finalize the respawning process
//-----------------------------------------------------------------------------
void CRotatingPickup::Materialize( void )
{
	if ( IsEffectActive( EF_NODRAW ) )
	{
		//Changing from invisible state to visible.
		RemoveEffects( EF_NODRAW );
	}
	EmitSound( "AlyxEmp.Charge" ); //Emit a sound
	SetTouch( &CRotatingPickup::ItemTouch ); //Reset our think functions
	SetThink( &CRotatingPickup::RotateThink ); // Start rotating again
	SetNextThink( gpGlobals->curtime + 0.01f ); // Think in 0.01 sec
}
//-----------------------------------------------------------------------------
// Purpose: Make our model rotate
//-----------------------------------------------------------------------------
void CRotatingPickup::RotateThink( void )
{
	// This makes sure the model rotates independent of the fps
	float dt = gpGlobals->curtime - GetLastThink(); 
	QAngle angles = GetAbsAngles(); //Get the current angles
	// Set the angles according to the rotation rate and fps
	angles.y += ( ITEM_ROTATION_RATE * dt ); 
	if ( angles.y >= 360 )	// If the rotation is more than 360, 
		angles.y -= 360;	// subtract 360 to avoid large variables
	SetAbsAngles( angles ); // Set the angles now
	SetNextThink( gpGlobals->curtime + 0.01f ); // Think again in 0.01 sec
}
Todo: Make a tutorial/article that explains the code