Ah, You caught me, good prank?Thanks for wasting my time I guess.
class Class
{
public:
void Method()
{
// Avoid doing this if possible
}
};
// Class.h
class Class
{
public:
void Method();
}
// Class.cpp
void Class::Method()
{
// Do prefer doing this in .cpp files - sometimes it may not be possible
}
This is because of how declarations work in C and C++. When a header is #include
-ed into a .cpp file, it is literally copy-pasted there, together with other headers. If a datatype is declared above some code, then that code is quite simply "aware" that it exists.CBasePlayer
wasn't declared in one of these situations. So you gotta move that code into a .cpp and include player.h
to get the CBasePlayer
declaration. Or, if you absolutely have to do it in a header, you can use forward-declaration:
class CBasePlayer; // this class is more fully declared elsewhere
pOther
. It's probably an entvars_t*
which you first must convert into a CBaseEntity*
via some utility.Yeah, after a few days working with SDK, I'm starting to think that guys from Valve didn't know about any good coding practices or principles back in 1998.They knew about good principles, they just didn't really have the time to apply em. Between 1996 and 1998, the game spec changed so rapidly and so much, nobody could really keep up. Game code is very patchy, very volatile, and rapidly evolves. It's simply like that by nature.
but I also want to ask how to getCBasePlayer
fromCBaseEntity
if ( pEntity->IsPlayer() )
{
CBasePlayer* player = static_cast<CBasePlayer*>( pEntity );
...
}
It is possible you were getting errors because you were casting to CBasePlayer
and not CBasePlayer*
. This is just C++'s value and reference semantics. In C#, a value type is a struct. In C++, a value type is anything that isn't a pointer. So you were trying to, essentially, convert a reference type into a value type there. Possibly, I'm just guessing.IsPlayer
or checking pev->classname
), you use static_cast
. static_cast
will throw if the cast fails.dynamic_cast
(a bit like the is
keyword in C# but simultaneously it's an as
)template<class T>
T* As()
{
return static_cast<T*>( this );
}
template<class T>
T* TryAs()
{
if ( Is<T>() )
{
return As<T>();
}
return nullptr;
}
template<class T>
bool Is()
{
return dynamic_cast<T*>( this ) != nullptr;
}
Usage:
if ( pEntity->Is<CBasePlayer>() )
{
pEntity->As<CBasePlayer>()->DoPlayerSpecificStuff();
// or
CBasePlayer* player = pEntity->As<CBasePlayer>();
}
flTime
and others, that is the absolute time (in seconds) since the client started... something, I am not 100% sure if it's since joining the server or starting up the game altogether, but it's likely the latter.gHUD
is the HUD singleton. It represents the clientside as a whole, almost a bit of a god object I'm afraid..inline int gmsgMyMessage
) and register it, i.e. link it to a name: gmsgMyMessage = REGISTER_USER_MSG( "MyMessage", -1 )
, the last number saying how many bytes are sent in this message, -1 if unknownDECLARE_MESSAGE
and HOOK_MESSAGE
It is a hybrid server that runs both mods so both HL and CS clients can connect.This is gonna be interesting.
GetScheduleOfType
, probably an SDK difference of some sort? In either case you can also edit the tutorial itself.Personally, i strongly believe they are developing somethingI'm sure they're always developing something. I'm guessing that, right now, the gamedev work is mainly distributed across Deadlock (production and testing) and Counter-Strike 2 (maintenance and hopefully content updates), with maybe, maybe just a little bit of wiggle room? For an unannounced game in an early prototyping stage?
CGameRules
and the code around it.InstallGameRules
(assuming they'd be implemented as gamerules) instead of mangling with different DLLs. The latter could be pretty messy.CVAR_SET_FLOAT
:
// Gravity will vary +/- 400
#define GRAVITY_VARIATION 400
int myRandomGravity = 800 + rand() % GRAVITY_VARIATION;
CVAR_SET_FLOAT( "sv_gravity", myRandomGravity );
But of course, there are ways to convert numbers into strings (the values in quotation marks are called "strings").battery.cpp
starts with:
//
// battery.cpp
//
// implementation of CHudBattery class
//
Implementation of CHudBattery
huh... what's that?hud.h
:
class CHudBattery : public CHudBase
{
public:
bool Init() override;
bool VidInit() override;
bool Draw(float flTime) override;
bool MsgFunc_Battery(const char* pszName, int iSize, void* pbuf);
private:
HSPRITE m_hSprite1;
HSPRITE m_hSprite2;
Rect* m_prc1;
Rect* m_prc2;
int m_iBat;
int m_iBatMax;
float m_fFade;
int m_iHeight; // width of the battery innards
};
Each of these HUD elements are C++ classes, which inherit from CHudBase
. The HUD system contains a collection of these CHudBase
based objects.Draw
functions are for. If you want to find what coordinates are used, you would simply find, for instance, CHudBattery::Draw
, like so:
bool CHudBattery::Draw(float flTime)
{
if ((gHUD.m_iHideHUDDisplay & HIDEHUD_HEALTH) != 0)
return true;
int r, g, b, x, y, a;
Rect rc;
rc = *m_prc2;
rc.top += m_iHeight * ((float)(100 - (V_min(100, m_iBat))) * 0.01); // battery can go from 0 to 100 so * 0.01 goes from 0 to 1
UnpackRGB(r, g, b, RGB_YELLOWISH);
if (!gHUD.HasSuit())
return true;
... // rest of the code
Scrolling down in the same function, we see this code:
y = ScreenHeight - gHUD.m_iFontHeight - gHUD.m_iFontHeight / 2;
x = ScreenWidth / 4;
// make sure we have the right sprite handles
if (0 == m_hSprite1)
m_hSprite1 = gHUD.GetSprite(gHUD.GetSpriteIndex("suit_empty"));
if (0 == m_hSprite2)
m_hSprite2 = gHUD.GetSprite(gHUD.GetSpriteIndex("suit_full"));
SPR_Set(m_hSprite1, r, g, b);
SPR_DrawAdditive(0, x, y - iOffset, m_prc1);
if (rc.bottom > rc.top)
{
SPR_Set(m_hSprite2, r, g, b);
SPR_DrawAdditive(0, x, y - iOffset + (rc.top - m_prc2->top), &rc);
}
This says "Hey GoldSRC, please render a sprite at these coordinates here". SPR_Set
tells the engine to use a certain sprite, and SPR_DrawAdditive
tells the engine to draw it. So, now you might be wondering what's up with these x
and y
variable things. You can declare your own temporary variables to essentially "chain" HUD elements one after another, so you don't have to manually specify every single coordinate yourself. You can use it to say "the next one will be 5 pixels more" etc.