Rotating Pickups: Difference between revisions
Jump to navigation
Jump to search
Islandstone (talk | contribs) No edit summary |
Islandstone (talk | contribs) No edit summary |
||
| Line 2: | Line 2: | ||
<pre> | <pre> | ||
//================== Coder: Islandstone 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 picku | |||
#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; // Th angle | |||
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 | |||
} | |||
//----------------------------------------------------------------------------- | |||
// 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 play 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 | |||
} | |||
</pre> | </pre> | ||
{{todo| | {{todo|Make a tutorial/article that explains the code}} | ||
Revision as of 07:14, 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 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 picku
#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; // Th angle
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
}
//-----------------------------------------------------------------------------
// 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 play 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