Rotating Pickups/Code: Difference between revisions
Jump to navigation
Jump to search
Islandstone (talk | contribs) No edit summary |
TomEdwards (talk | contribs) No edit summary |
||
Line 1: | Line 1: | ||
__TOC__ | |||
== Server == | |||
#include "cbase.h" | <source lang=cpp> | ||
#include "cbase.h" | |||
#include "items.h" | #include "items.h" | ||
// 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" | ||
#define PICKUP_DECAL "decals/item_base" | |||
#define | #define PICKUP_MODEL "models/items/healthkit.mdl" | ||
#define PICKUP_MIN_HEIGHT 50 | |||
/ | int PickupDecalIndex; // set by CRotatingPickup::Precache() | ||
#define | |||
#define | |||
// | |||
#define SF_SUPPRESS_PICKUP_DECAL 0x00000002 | |||
//----------------------------------------------------------------------------- | //----------------------------------------------------------------------------- | ||
// | // Rotating health kit. Heals the player when picked up. | ||
//----------------------------------------------------------------------------- | //----------------------------------------------------------------------------- | ||
class CRotatingPickup : public CItem | class CRotatingPickup : public CItem | ||
{ | { | ||
DECLARE_CLASS( CRotatingPickup, CItem ); | DECLARE_CLASS( CRotatingPickup, CItem ); | ||
DECLARE_DATADESC(); | DECLARE_DATADESC(); | ||
DECLARE_SERVERCLASS(); | |||
public: | |||
CRotatingPickup(); | CRotatingPickup(); | ||
void | void Spawn(); | ||
void | void Activate(); | ||
void Precache(); | |||
bool | bool MyTouch( CBasePlayer *pPlayer ); | ||
CBaseEntity* | CBaseEntity* Respawn(); | ||
void Materialize( | void Materialize(); | ||
int m_iHealthToGive; | |||
float m_fRespawnTime; | |||
int | CNetworkVar(bool, m_bRespawning); | ||
private: | private: | ||
Vector MdlTop; | |||
}; | }; | ||
Line 65: | Line 50: | ||
BEGIN_DATADESC( CRotatingPickup ) | 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() | CRotatingPickup::CRotatingPickup() | ||
{ | { | ||
if ( m_iHealthToGive <= 0 ) | if ( m_iHealthToGive <= 0 ) | ||
m_iHealthToGive = | m_iHealthToGive = 25; | ||
if ( | if ( m_fRespawnTime <= 0 ) | ||
m_fRespawnTime = 20; | |||
} | } | ||
void CRotatingPickup::Spawn() | |||
void CRotatingPickup::Spawn( | |||
{ | { | ||
BaseClass::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 ); | 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() | |||
void CRotatingPickup:: | |||
{ | { | ||
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:: | 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 ) | bool CRotatingPickup::MyTouch( CBasePlayer *pPlayer ) | ||
{ | { | ||
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(); | |||
UserMessageBegin( PlayerFilter, "ItemPickup" ); | |||
UserMessageBegin( | |||
WRITE_STRING( GetClassname() ); | WRITE_STRING( GetClassname() ); | ||
MessageEnd(); | MessageEnd(); | ||
EmitSound( PlayerFilter, pPlayer->entindex(), "HealthKit.Touch" ); // this should be done by the HUD really | |||
Respawn(); | |||
Respawn(); | |||
return true; | return true; | ||
} | } | ||
Line 186: | Line 138: | ||
} | } | ||
// | // Disappear | ||
CBaseEntity* CRotatingPickup::Respawn() | |||
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); | |||
} | |||
</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 | |||
//----------------------------------------------------------------------------- | //----------------------------------------------------------------------------- | ||
// | // 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() | |||
void | |||
{ | { | ||
// | 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(); | |||
} | } | ||
</ | </source> | ||
== | == FGD == | ||
<source lang=php> | |||
@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> |
Revision as of 14:53, 3 April 2011
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
]
]