Need help with Activities and NPC coding Created 4 years ago2020-11-19 12:25:06 UTC by Alexis_of_Steel Alexis_of_Steel

Created 4 years ago2020-11-19 12:25:06 UTC by Alexis_of_Steel Alexis_of_Steel

Posted 4 years ago2020-11-19 12:25:06 UTC Post #344889
Hello people. Will you believe me if I tell you that I've been trying for months the following coding issue with no satisfactory results? Yes, it took me several months of headaches, and I'm getting sad :(
So for dat reason I'm appealing to TWHL, specifically to Half-Life's Lords of Coding. :D

I'm trying out a new NPC that consists in hevscientist with multiple faces and weapons to choose (yeah, like those ones saw in Sweet Half-Life).
I want to assign a specific idling animation to every weapon the model has. For example, if the model has a RPG, the idling animation will be those that hold up an RPG. If the model has a crowbar, the idling animation will be the one that carry a crowbar, and so on.

The challenge, I guess, is make the NPC choose a certain animation when it has a given weapon. Note that if all idling sequences has ACT_IDLE attached to them, the NPC plays all idling animations without distinction.

But I can't get what I'm looking for. Is there something I gotta do in the code?
Thanks in advance, and have a nice day :cool:
Posted 4 years ago2020-11-19 22:28:35 UTC Post #344895
Posted 4 years ago2020-11-20 01:31:36 UTC Post #344897
Hey Shepard. I tried that method from hgrunt.cpp, but my NPC still plays all idling animations :(
In the QC file, I assigned to every idling sequence (glock, magnum, shotgun, etc.) the ACT_IDLE tag. Is that right?
Posted 4 years ago2020-11-20 08:37:24 UTC Post #344899
Did you look at the ACT_IDLE part in that grunt code? That's using LookupActivity(NewActivity), which will randomly pick one of the animations that are marked with that activity tag (if I understand things correctly). That's not what you want.

Instead, look at the ACT_RANGE_ATTACK1 and ACT_RANGE_ATTACK2 parts - those use LookupSequence("sequence name") to pick a specific animation based on the grunt's weapon and posture. That's what you want to do. So if the new activity is ACT_IDLE, and your NPC is holding an RPG, do iSequence = LookupSequence("idle_rpg"), if the NPC is holding a crowbar, do iSequence = LookupSequence("idle_crowbar"), and so on.
Posted 4 years ago2020-11-20 13:28:16 UTC Post #344906
Yep, I just did what you said. And here's the bunch of code I wrote:
case ACT_IDLE:
if (FBitSet(pev->weapons, HEVSCI_GLOCK)) // 9mm handgun hevscientist
{
    if ( m_fStanding )
    {
        iSequence = LookupSequence( "ref_aim_onehanded" );
    }
    else
    {
        iSequence = LookupSequence( "crouch_aim_onehanded" );
    }
}
else if (FBitSet(pev->weapons, HEVSCI_PYTHON)) // .357 Magnum hevscientist
{
    if ( m_fStanding )
    {
        iSequence = LookupSequence( "ref_aim_python" );
    }
    else
    {
        iSequence = LookupSequence( "crouch_aim_python" );
    }
}
else if (FBitSet(pev->weapons, HEVSCI_SHOTGUN)) // Shotgun hevscientist
{
    if ( m_fStanding )
    {
        iSequence = LookupSequence( "ref_aim_shotgun" );
    }
    else
    {
        iSequence = LookupSequence( "crouch_aim_shotgun" );
    }
}
else if (FBitSet(pev->weapons, HEVSCI_MP5)) // MP5 hevscientist
{
    if ( m_fStanding )
    {
        iSequence = LookupSequence( "ref_aim_mp5" );
    }
    else
    {
        iSequence = LookupSequence( "crouch_aim_mp5" );
    }
}
else if (FBitSet(pev->weapons, HEVSCI_CROSSBOW)) // Crossbow hevscientist
{
    if ( m_fStanding )
    {
        iSequence = LookupSequence( "ref_aim_bow" );
    }
    else
    {
        iSequence = LookupSequence( "crouch_aim_bow" );
    }
}
else if (FBitSet(pev->weapons, HEVSCI_RPG)) // RPG hevscientist
{
    if ( m_fStanding )
    {
        iSequence = LookupSequence( "ref_aim_rpg" );
    }
    else
    {
        iSequence = LookupSequence( "crouch_aim_rpg" );
    }
}
else if (FBitSet(pev->weapons, HEVSCI_GAUSS)) // Gauss hevscientist
{
    if ( m_fStanding )
    {
        iSequence = LookupSequence( "ref_aim_gauss" );
    }
    else
    {
        iSequence = LookupSequence( "crouch_aim_gauss" );
    }
}
iSequence = LookupActivity ( NewActivity );
break;
And my NPC still plays all the idling sequences without select one specifically. But I guess this is the way! And it seem that something's still missing...
Posted 4 years ago2020-11-20 14:02:00 UTC Post #344908
The problem is that iSequence = LookupActivity ( NewActivity ); line at the bottom - that's overwriting the animation you've selected with a random idle animation. If I were you I would also simplify this code by creating a function that returns the name of the NPC's weapon, so you only need a single if/else statement that decides between "ref_aim_" + WeaponName() and "crouch_aim_" + WeaponName() (that's not exactly how you concatenate strings in C or C++ - right now I don't have time to look that up - but you should get the idea).

(I've also edited your post to fix the code formatting - for large code blocks, use triple ticks: ` ` ` code goes here ` ` `)
Posted 4 years ago2020-11-20 18:21:24 UTC Post #344909
Hello Captain P, thanks for your correction. :D
I just deleted that part (iSequence = LookupActivity ( NewActivity );), but unfortunately my NPC still plays all the idle animations :(
I'm starting to think it's more complicated than I thought...
Posted 4 years ago2020-11-20 18:30:19 UTC Post #344910
I second Captain P advice, the way you are doing it right now will involve a lot of code duplication and that will put a lot of burden when maintaining it.

Sometimes in situations like these, it's best to make methods that solve small but repetitive problems. In your case, one small problem is "how do I convert the pev->weapons value to a C string that would match the animations names" and "how do I handle the standing up/crouched difference".

Then you combine both solutions to get the "final answer" to your original problem which was "how do I get the proper value to use in LookupSequence ". In the end, you have something like this:
/**
 * Returns the name of a sequence for this monster based on his stance (standing up or crouched), a specific name and his current weapon's name.
 * @param szName The name of the sequence without the stance ("ref_" and "crouch_"), weapon's name ("python"...) and any underscore.
 * @return The corresponding sequence name.
 */
const char *CMyMonster::GetSequenceName( const char *szName ) const
{
    static char szResult[32];

    strncpy( szResult, m_fStanding ? "ref_" : "crouch_", sizeof( szResult ) );
    strncat( szResult, szName, sizeof( szResult ) );
    strncat( szResult, "_", sizeof( szResult ) );
    strncat( szResult, GetWeaponIDAsCStr(), sizeof( szResult ) );

    return szResult;
}

/**
 * Returns the name of the weapon this monster is currently using as a "C string".
 * This is used to select the proper sequence when performing activities.
 * @return This monster's current weapon name.
 */
const char *CMyMonster::GetWeaponIDAsCStr() const
{
    if ( pev->weapons & HEVSCI_PYTHON )
        return "python";
    else if ( pev->weapons & HEVSCI_SHOTGUN )
        return "shotgun";
    else if ( pev->weapons & HEVSCI_MP5 )
        return "mp5";
    else if ( pev->weapons & HEVSCI_CROSSBOW )
        return "bow";
    else if ( pev->weapons & HEVSCI_RPG )
        return "rpg";
    else if ( pev->weapons & HEVSCI_GAUSS )
        return "gauss";
    else
        return "onehanded";
}
Which you can use like this:
void CMyMonster::SetActivity( Activity NewActivity )
{
    int iSequence = ACTIVITY_NOT_AVAILABLE;
    void *pModel = GET_MODEL_PTR( ENT( pev ) );

    switch ( NewActivity )
    {
    case ACT_IDLE:
        iSequence = LookupSequence( GetSequenceName( "aim" ) );
        break;
    default:
        iSequence = LookupActivity( NewActivity );
    }

    if ( iSequence <= ACTIVITY_NOT_AVAILABLE )
    {
        // Not available try to get default anim
        ALERT( at_console, "%s has no sequence for act:%d\n", STRING( pev->classname ), NewActivity );
        pev->sequence = 0; // Set to the reset anim (if it's there)
        return;
    }

    // Set to the desired anim, or default anim if the desired is not present
    if ( pev->sequence != iSequence || !m_fSequenceLoops )
        pev->frame = 0;

    pev->sequence = iSequence; // Set to the reset anim (if it's there)
    ResetSequenceInfo();
    SetYawSpeed();
}
Assuming your monster is crouched, uses the Python and you want the corresponding aim sequence, GetSequenceName( "aim" ) will return crouched_aim_python.
Posted 4 years ago2020-11-21 23:34:33 UTC Post #344919
I just tested your method. It compiled correctly, but for some reason I don't know the NPC animations are bugged. The NPC stay freeze.
Anyway, I really appreciate your help, guys. But I think it would be better if I create the NPC with all weapons separately. :confused:
You must be logged in to post a response.