Forum posts

Looks like I have a lot to learn in C++ besides the main syntax 😅

Anyway, I finally have nothing to ask. Thanks for your patience when answering my dumb questions :3
I found that this error doesn't appear in the new entity's file. However, it seems to appear when I'm trying to write such code in the same method override in my old entity's class. After finding this, I also tried to copy-paste includes (ones from items.cpp), and it solved the problem, so the reason is one of these includes. How...?
Okay, I'll post all the code anyway, I'm curious about the real reason, and I don't think my pathetic attempts in C++ are worth something xd
#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "UserMessages.h"
#include "cstring"

class CTargetChangeHudColor : public CBaseEntity
{
public:
    // make these variables private, probably?
    // nothing should change them directly
    int m_iRed;
    int m_iGreen;
    int m_iBlue;
    void Use(CBaseEntity* pActivator, CBaseEntity* pOther, USE_TYPE useType, float value) override;
    bool KeyValue(KeyValueData* pkvd) override;
};

LINK_ENTITY_TO_CLASS(target_change_hud_color, CTargetChangeHudColor);

bool CTargetChangeHudColor::KeyValue(KeyValueData* pkvd)
{
    if (FStrEq(pkvd->szKeyName, "red"))
    {
        m_iRed = atof(pkvd->szValue);
        return true;
    }
    else if (FStrEq(pkvd->szKeyName, "green"))
    {
        m_iGreen = atof(pkvd->szValue);
        return true;
    }
    else if (FStrEq(pkvd->szValue, "blue"))
    {
        m_iBlue = atof(pkvd->szValue);
        return true;
    }

    return CBaseEntity::KeyValue(pkvd);
}

void CTargetChangeHudColor::Use(CBaseEntity* pActivator, CBaseEntity* pOther, USE_TYPE useType, float value)
{
    CBasePlayer* player;

     // errors here
    if (pActivator->IsPlayer())
        player = static_cast<CBasePlayer*>(pActivator);
    else
        player = static_cast<CBasePlayer*>(pOther);

    // MESSAGE_BEGIN(MSG_ONE, gmsgHudColor, NULL, player->pev);
    MESSAGE_BEGIN(MSG_ALL, gmsgHudColor);
    WRITE_SHORT(m_iRed);
    WRITE_SHORT(m_iGreen);
    WRITE_SHORT(m_iBlue);
    MESSAGE_END();
}
ScreenshotScreenshot
I don't know which entity is calling player (who pressed the button), so I checked both of them. This article says something about pCaller, however, I don't see such variable in the method signature.
@Admer456 said:
It's probably an entvars_t*
Nope, it's exactly CBaseEntity*; at least, the method signature says so. All attempts were in the CBaseEntity::Use() method override. Maybe, some Unified SDK bug? I double-checked everything to be correct. Otherwise, I wouldn't be asking - I prefer to solve by myself mostly, without asking someone to do this instead of me, unless I don't have another option and Google can't help me.
@Admer456 said:
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.
Oh, it explains.
@Admer456 said:
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.
Sorry for asking without clarification.
I used a static cast like this:
if (pOther->IsPlayer())
    player = static_cast<CBasePlayer*>(pOther); // invalid type conversion
Have no idea why it gives an error. I read that this may also occur when inheritance is private, but it's public in SDK code.

And dynamic cast like this:
player = dynamic_cast<CBasePlayer*>(pOther); // the type in a dynamic_cast must be a pointer or reference to a complete class type, or void *
I believe I've already indicated that I need a pointer by using an asterisk. At least, when I used dynamic_cast multiple times trying to cast from CBaseEntity to CBasePlayer through the inheritance chain, it worked fine with all classes but CBasePlayer.

I also know the differences between these two methods, I tried both just in case, but thanks for the explanation anyway!

By the way, your code:
@Admer456 said:
if ( pEntity->IsPlayer() )
{
   CBasePlayer* player = static_cast<CBasePlayer*>( pEntity );
   ...
}
Is just like the one I used and caused the same error (invalid type conversion). The only difference is that I changed pEntity to pOther (since I used your snippet in the entity code), however, it does not look like the reason.
Oh, and... I don't know, maybe I should create a new thread for a new question (sorry if I do :3), but I also want to ask how to get CBasePlayer from CBaseEntity.
I tried both static_cast and dynamic_cast, but they failed with different errors. I also tried to cast step-by-step through the inheritance chain (CBaseEntity -> CBaseDelay -> CBaseAnimating -> CBaseToggle -> CBaseMonster -> CBasePlayer, if I'm not mistaken), and it still gives an error when trying to cast CBasePlayer from CBaseMonster. I (again) have no idea what causes these errors, so I am asking here.
Thanks! All worked. Thanks for the link too, I found a lot of interesting articles about working with HUD/VGUI there.

Also, for those who will find this thread when searching for the same question and encounter the same problem - Tried to create a message with a bogus message type ( 0 ) in my case popped up because of a message name longer than 12 characters (I somehow skipped it in tutorial 😅).
@Admer456 said:
And yes, gHUD is the HUD singleton. It represents the clientside as a whole, almost a bit of a god object I'm afraid..
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. Otherwise, they could at least use folders to group files and make working with code a little less hell. Or, maybe, it wasn't possible in C++ back then... I don't know for sure, so I won't judge.
First of all, I'm not a C++ programmer. Despite this, I have a lot of experience with C#, so after two days of working with the Half-Life SDK, I learned the main differences. However, for more experienced developers, some of the things I’ve done may seem odd or even wrong. I don’t mind if someone points them out. :3

What I want to achieve is to add the ability to change the HUD color by firing a map entity, because sometimes the player will be playing as different characters. After reading this amazing tutorial, I created a static class called HUDManager with static fields defining the RGB values, as well as a static method to change these values. I modified the UnpackRGB() method to use the values from this class, and so far, it has worked with the default values (though I haven’t yet tried to use this method at runtime).

After a few more hours spent reading the SDK code and tutorials, I created an entity class, linked it, and... suddenly realized that entities are defined and declared on the server side, while my HUDManager is on the client side. So now I need a way to call a client-side method from the server.

I found this tutorial on temporary entity messages, which seems like what I need (or at least something very similar). However, it doesn’t fully solve my problem - none of the constants defined in const.h (from hl_cdll) seem to help.

I also found a file called UserMessages.cpp in hldll, which seems to use the REG_USER_MSG method. This is a macro for g_engfuncs.pfnRegUserMsg, which I presume, based on the file name and the comment above enginefuncs_s, is an interface to an engine function of the same name. Since the engine’s code is not open-source, I can’t dig deeper to verify if this is the function I need. That’s why I’m asking for help.

Some possibly useful information: I’m using the Half-Life Unified SDK, and my mod is intended to be entirely single-player.

I feel like this might be a very basic question that's already been answered somewhere, or there may be a tutorial about it that I just haven't found yet. Forgive my dumb C# brain xd

Oh, and if I’ve already posted this… I assume that after changing static variables of HUDManager at runtime (which I haven’t yet tested), I’ll need to manually redraw the HUD to apply the new colors. My second question is: how can I do this? I found the gHUD variable in cdll_int.h, which I assume is the main HUD variable on the client side? The CHud class (to which gHUD belongs) has a Redraw method with two arguments: flTime and intermission. What exactly does flTime refer to (specifically the m_flTime variable, which is assigned in the Redraw method)? The comment says it's "the current client time," but the current time in what context and in what format? m_flTime is used in so many places that I can’t quite grasp its meaning.