Rotating Pickups
Jump to navigation
Jump to search
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