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 08: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