Need help with weapon coding Created 3 years ago2021-06-27 21:17:58 UTC by Alexis_of_Steel Alexis_of_Steel

Created 3 years ago2021-06-27 21:17:58 UTC by Alexis_of_Steel Alexis_of_Steel

Posted 3 years ago2021-06-27 21:17:58 UTC Post #345708
Hello, guys. I wonder if you can help me with this: I want to create a chaingun (minigun) but I'm stuck on the part about making the barrel start spinning, then firing and when I stop firing, the barrel stop spinning. I have the following animations:
enum minigun_e {
    MINIGUN_IDLE1 = 0,
    MINIGUN_IDLE2,
    MINIGUN_SPINUP,
    MINIGUN_SPINDOWN,
    MINIGUN_FIRE,
    MINIGUN_FIRE_EMPTY,
    MINIGUN_RELOAD,
    MINIGUN_DRAW,
    MINIGUN_HOLSTER
};
In other words, I need to display the MINIGUN_SPINUP animation first, then the MINIGUN_FIRE animation (after a few seconds), and finally the MINIGUN_SPINDOWN animation when I stop pressing the right mouse button or I'm out of bullets.

I think that the key must be in the PrimaryAttack method. :hecu:

What follows is everything I have done thus far:
#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "monsters.h"
#include "weapons.h"
#include "nodes.h"
#include "player.h"
#include "soundent.h"
#include "gamerules.h"

enum minigun_e {
    MINIGUN_IDLE1 = 0,
    MINIGUN_IDLE2,
    MINIGUN_SPINUP,
    MINIGUN_SPINDOWN,
    MINIGUN_FIRE,
    MINIGUN_FIRE_EMPTY,
    MINIGUN_RELOAD,
    MINIGUN_DRAW,
    MINIGUN_HOLSTER
};

LINK_ENTITY_TO_CLASS( weapon_minigun, CMinigun );

void CMinigun::Spawn()
{
    pev->classname = MAKE_STRING("weapon_minigun");
    Precache();
    m_iId = WEAPON_MINIGUN;
    SET_MODEL(ENT(pev), "models/w_minigun.mdl");
    m_iDefaultAmmo = MINIGUN_DEFAULT_GIVE;
    FallInit();
}

void CMinigun::Precache( void )
{
    PRECACHE_MODEL("models/v_minigun.mdl");
    PRECACHE_MODEL("models/w_minigun.mdl");
    PRECACHE_MODEL("models/p_minigun.mdl");
    PRECACHE_SOUND ("weapons/minigun_fire.wav");
    PRECACHE_SOUND ("weapons/minigun_spinup.wav");
    PRECACHE_SOUND ("weapons/minigun_spindown.wav");
    m_usFireMinigun = PRECACHE_EVENT( 1, "events/minigun.sc" );
    m_iShell = PRECACHE_MODEL("models/shell.mdl");
    PRECACHE_SOUND("weapons/357_cock1.wav");
}

int CMinigun::GetItemInfo(ItemInfo *p)
{
    p->pszName = STRING(pev->classname);
    p->pszAmmo1 = "9mm";
    p->iMaxAmmo1 = _9MM_MAX_CARRY;
    p->pszAmmo2 = NULL;
    p->iMaxAmmo2 = -1;
    p->iMaxClip = MINIGUN_MAX_CLIP;
    p->iSlot = 2;
    p->iPosition = 3;
    p->iFlags = 0;
    p->iId = m_iId = WEAPON_MINIGUN;
    p->iWeight = MINIGUN_WEIGHT;
    return 1;
}

int CMinigun::AddToPlayer( CBasePlayer *pPlayer )
{
    if ( CBasePlayerWeapon::AddToPlayer( pPlayer ) )
    {
        MESSAGE_BEGIN( MSG_ONE, gmsgWeapPickup, NULL, pPlayer->pev );
            WRITE_BYTE( m_iId );
        MESSAGE_END();
        return TRUE;
    }
    return FALSE;
}

BOOL CMinigun::Deploy()
{
    return DefaultDeploy( "models/v_minigun.mdl", "models/p_minigun.mdl", MINIGUN_DRAW, "mp5" );
}
Thanks for your time! :biggrin:
Posted 3 years ago2021-06-28 09:24:36 UTC Post #345709
Take a look at the Gauss gun's code, its secondary attack works a lot like that. It mostly involves handling timing in WeaponIdle.
Posted 3 years ago2021-07-03 22:07:33 UTC Post #345726
Hey, Solokiller! I took a look at the code you told me. And, well, based on that code I made my own to create a minigun:
void CMinigun::PrimaryAttack( void )
{
    if ( m_iClip <= 0 )
    {
        PlayEmptySound();
        m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 0.5;
        return;
    }
    if ( m_fInAttack == 0 )
    {
        m_flTimeWeaponIdle = m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 0.6;
        SendWeaponAnim( MINIGUN_SPINUP );
        EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/minigun_spinup.wav", 1, ATTN_NORM);
        m_fInAttack = 1;
        return;
    }
    else if ( m_fInAttack == 1 )
    {
        MinigunFire();
        return;
    }
}

void CMinigun::MinigunFire()
{
    m_pPlayer->pev->effects = (int)(m_pPlayer->pev->effects) | EF_MUZZLEFLASH;
    SendWeaponAnim( MINIGUN_FIRE );
    m_pPlayer->SetAnimation( PLAYER_ATTACK1 );
    m_iClip--;
    UTIL_MakeVectors( m_pPlayer->pev->v_angle + m_pPlayer->pev->punchangle );
    Vector vecShellVelocity = m_pPlayer->pev->velocity + gpGlobals->v_right * RANDOM_FLOAT(50,70) + gpGlobals->v_up * RANDOM_FLOAT(100,150) + gpGlobals->v_forward * 25;
    EjectBrass( pev->origin + m_pPlayer->pev->view_ofs + gpGlobals->v_up * -12 + gpGlobals->v_forward * 32 + gpGlobals->v_right * 6 , vecShellVelocity, pev->angles.y, m_iShell, TE_BOUNCE_SHELL );
    m_pPlayer->m_iWeaponVolume = LOUD_GUN_VOLUME;
    m_pPlayer->m_iWeaponFlash = BRIGHT_GUN_FLASH;
    EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/minigun_fire.wav", 1, ATTN_NORM);
    Vector vecSrc = m_pPlayer->GetGunPosition();
    Vector vecAiming = m_pPlayer->GetAutoaimVector( AUTOAIM_10DEGREES );
    Vector vecDir;
#ifdef CLIENT_DLL
    if ( !bIsMultiplayer() )
#else
    if ( !g_pGameRules->IsMultiplayer() )
#endif
    {
        vecDir = m_pPlayer->FireBulletsPlayer( 1, vecSrc, vecAiming, VECTOR_CONE_6DEGREES, 8192, BULLET_PLAYER_MINIGUN, 0, 0, m_pPlayer->pev, m_pPlayer->random_seed );
    }
    else
    {
        vecDir = m_pPlayer->FireBulletsPlayer( 1, vecSrc, vecAiming, VECTOR_CONE_3DEGREES, 8192, BULLET_PLAYER_MINIGUN, 0, 0, m_pPlayer->pev, m_pPlayer->random_seed );
    }
    int flags;
#if defined( CLIENT_WEAPONS )
    flags = FEV_NOTHOST;
#else
    flags = 0;
#endif
    PLAYBACK_EVENT_FULL( flags, m_pPlayer->edict(), m_usFireMinigun, 0.0, (float *)&g_vecZero, (float *)&g_vecZero, vecDir.x, vecDir.y, 0, 0, 0, 0 );
    m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 0.15;
}

void CMinigun::WeaponIdle( void )
{
    if ( m_flTimeWeaponIdle > 0.6 )
    {
        if ( m_fInAttack == 1 )
        {
            m_flTimeWeaponIdle = m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 1.4;
            SendWeaponAnim( MINIGUN_SPINDOWN );
            EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/minigun_spindown.wav", 1, ATTN_NORM);
            m_fInAttack = 0;
            return;
        }
    }
    ResetEmptySound();
    if ( m_flTimeWeaponIdle > UTIL_WeaponTimeBase() )
        return;
    int iAnim;
    switch ( RANDOM_LONG( 0, 1 ) )
    {
    case 0:
        iAnim = MINIGUN_IDLE1;
        break;
    default:
    case 1:
        iAnim = MINIGUN_IDLE2;
        break;
    }
    SendWeaponAnim( iAnim );
    m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + UTIL_SharedRandomFloat( m_pPlayer->random_seed, 10, 15 );
}
This code that I wrote does not work so well that let's say: there are times when the gun fires without spinning up the barrel.
I wanted to know if you guys could help me improve it. :D
Posted 3 years ago2021-07-03 23:41:50 UTC Post #345727
Forget it, I've already figured it out, haha. I was supposed to create an event for the barrel spinning. And I did it and now it works perfectly!
Posted 3 years ago2021-07-05 14:31:26 UTC Post #345732
Post full code please!!! :)
Posted 3 years ago2021-07-05 18:22:37 UTC Post #345733
First of all, this code was implemented in the official Half-Life SDK released by Valve.
As a final comment, please excuse me if my code contains unnecessary or redundant lines (I'm not really an expert). I hope someone takes the initiative to even make a wiki page with this humble contribution. :D

First, I added the following lines of code in weapons.h:
// NEW MINIGUN
class CMinigun : public CBasePlayerWeapon
{
public:
    void Spawn( void );
    void Precache( void );
    int iItemSlot( void ) { return 3; }
    int GetItemInfo(ItemInfo *p);
    int AddToPlayer(CBasePlayer *pPlayer);
    BOOL Deploy( void );
    void PrimaryAttack( void );
    void MinigunFire( void );
    void Reload( void );
    void WeaponIdle( void );
    void Holster( int skiplocal = 0 );
    int m_iShell;

    virtual BOOL UseDecrement( void )
    {
#if defined( CLIENT_WEAPONS )
        return TRUE;
#else
        return FALSE;
#endif
    }

private:
    unsigned short m_usSpinUpMinigun;
    unsigned short m_usFireMinigun;
};
Then, I created an element called minigun.cpp in hldll. All the code it contains is as follows:
// NEW MINIGUN
#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "monsters.h"
#include "weapons.h"
#include "nodes.h"
#include "player.h"
#include "soundent.h"
#include "gamerules.h"

enum minigun_e {
    MINIGUN_IDLE1 = 0,
    MINIGUN_IDLE2,
    MINIGUN_SPINUP,
    MINIGUN_SPINDOWN,
    MINIGUN_FIRE,
    MINIGUN_FIRE_EMPTY,
    MINIGUN_RELOAD,
    MINIGUN_DRAW,
    MINIGUN_HOLSTER
};

LINK_ENTITY_TO_CLASS( weapon_minigun, CMinigun );

void CMinigun::Spawn()
{
    pev->classname = MAKE_STRING("weapon_minigun");
    Precache();
    m_iId = WEAPON_MINIGUN;
    SET_MODEL(ENT(pev), "models/w_minigun.mdl");
    m_iDefaultAmmo = MINIGUN_DEFAULT_GIVE;
    FallInit();
}

void CMinigun::Precache( void )
{
    PRECACHE_MODEL("models/v_minigun.mdl");
    PRECACHE_MODEL("models/w_minigun.mdl");
    PRECACHE_MODEL("models/p_minigun.mdl");
    PRECACHE_SOUND("weapons/minigun_fire.wav");
    PRECACHE_SOUND("weapons/minigun_spinup.wav");
    PRECACHE_SOUND("weapons/minigun_spindown.wav");
    m_usSpinUpMinigun = PRECACHE_EVENT( 1, "events/minigun1.sc" );
    m_usFireMinigun = PRECACHE_EVENT( 1, "events/minigun2.sc" );
    m_iShell = PRECACHE_MODEL("models/shell.mdl");
    PRECACHE_SOUND("weapons/357_cock1.wav");
}

int CMinigun::GetItemInfo(ItemInfo *p)
{
    p->pszName = STRING(pev->classname);
    p->pszAmmo1 = "chaingun";
    p->iMaxAmmo1 = MINIGUN_MAX_CARRY;
    p->pszAmmo2 = NULL;
    p->iMaxAmmo2 = -1;
    p->iMaxClip = MINIGUN_MAX_CLIP;
    p->iSlot = 2;
    p->iPosition = 4;
    p->iFlags = 0;
    p->iId = m_iId = WEAPON_MINIGUN;
    p->iWeight = MINIGUN_WEIGHT;
    return 1;
}

int CMinigun::AddToPlayer( CBasePlayer *pPlayer )
{
    if ( CBasePlayerWeapon::AddToPlayer( pPlayer ) )
    {
        MESSAGE_BEGIN( MSG_ONE, gmsgWeapPickup, NULL, pPlayer->pev );
            WRITE_BYTE( m_iId );
        MESSAGE_END();
        return TRUE;
    }
    return FALSE;
}

BOOL CMinigun::Deploy()
{
    return DefaultDeploy( "models/v_minigun.mdl", "models/p_minigun.mdl", MINIGUN_DRAW, "mp5" );
}

void CMinigun::Holster( int skiplocal /* = 0 */ )
{
    m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.5;
    SendWeaponAnim( MINIGUN_HOLSTER );
    m_fInAttack = 0;
}

void CMinigun::PrimaryAttack( void )
{
    if ( m_iClip <= 0 )
    {
        PlayEmptySound();
        m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 0.5;
        return;
    }
    if ( m_fInAttack == 0 )
    {
        PLAYBACK_EVENT_FULL( FEV_NOTHOST, m_pPlayer->edict(), m_usSpinUpMinigun, 0.0, (float *)&g_vecZero, (float *)&g_vecZero, 0.0, 0.0, 0, 0, 0, 0 );
        m_flTimeWeaponIdle = m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 0.6;
        SendWeaponAnim( MINIGUN_SPINUP );
        EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/minigun_spinup.wav", 1, ATTN_NORM);
        m_fInAttack = 1;
        return;
    }
    else if ( m_fInAttack == 1 )
    {
        MinigunFire();
        return;
    }
}

void CMinigun::MinigunFire()
{
    m_pPlayer->pev->effects = (int)(m_pPlayer->pev->effects) | EF_MUZZLEFLASH;
    SendWeaponAnim( MINIGUN_FIRE );
    m_pPlayer->SetAnimation( PLAYER_ATTACK1 );
    m_iClip--;
    UTIL_MakeVectors( m_pPlayer->pev->v_angle + m_pPlayer->pev->punchangle );
    Vector vecShellVelocity = m_pPlayer->pev->velocity + gpGlobals->v_right * RANDOM_FLOAT(50,70) + gpGlobals->v_up * RANDOM_FLOAT(100,150) + gpGlobals->v_forward * 25;
    EjectBrass( pev->origin + m_pPlayer->pev->view_ofs + gpGlobals->v_up * -12 + gpGlobals->v_forward * 32 + gpGlobals->v_right * 6 , vecShellVelocity, pev->angles.y, m_iShell, TE_BOUNCE_SHELL );
    m_pPlayer->m_iWeaponVolume = LOUD_GUN_VOLUME;
    m_pPlayer->m_iWeaponFlash = BRIGHT_GUN_FLASH;
    EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/minigun_fire.wav", 1, ATTN_NORM);
    Vector vecSrc = m_pPlayer->GetGunPosition();
    Vector vecAiming = m_pPlayer->GetAutoaimVector( AUTOAIM_10DEGREES );
    Vector vecDir;
#ifdef CLIENT_DLL
    if ( !bIsMultiplayer() )
#else
    if ( !g_pGameRules->IsMultiplayer() )
#endif
    {
        vecDir = m_pPlayer->FireBulletsPlayer( 1, vecSrc, vecAiming, VECTOR_CONE_6DEGREES, 8192, BULLET_PLAYER_MINIGUN, 0, 0, m_pPlayer->pev, m_pPlayer->random_seed );
    }
    else
    {
        vecDir = m_pPlayer->FireBulletsPlayer( 1, vecSrc, vecAiming, VECTOR_CONE_3DEGREES, 8192, BULLET_PLAYER_MINIGUN, 0, 0, m_pPlayer->pev, m_pPlayer->random_seed );
    }
    int flags;
#if defined( CLIENT_WEAPONS )
    flags = FEV_NOTHOST;
#else
    flags = 0;
#endif
    PLAYBACK_EVENT_FULL( flags, m_pPlayer->edict(), m_usFireMinigun, 0.0, (float *)&g_vecZero, (float *)&g_vecZero, vecDir.x, vecDir.y, 0, 0, 0, 0 );
    m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 0.1;
}

void CMinigun::WeaponIdle( void )
{
    if ( m_flTimeWeaponIdle >= 0.6 )
    {
        if ( m_fInAttack == 1 )
        {
            m_flTimeWeaponIdle = m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 1.4;
            SendWeaponAnim( MINIGUN_SPINDOWN );
            EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/minigun_spindown.wav", 1, ATTN_NORM);
            m_fInAttack = 0;
            return;
        }
    }
    ResetEmptySound();
    if ( m_flTimeWeaponIdle > UTIL_WeaponTimeBase() )
        return;
    int iAnim;
    switch ( RANDOM_LONG( 0, 1 ) )
    {
    case 0:
        iAnim = MINIGUN_IDLE1;
        break;
    default:
    case 1:
        iAnim = MINIGUN_IDLE2;
        break;
    }
    SendWeaponAnim( iAnim );
    m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + UTIL_SharedRandomFloat( m_pPlayer->random_seed, 10, 15 );
}

void CMinigun::Reload( void )
{
    if ( m_pPlayer->ammo_9mm <= 0 )
         return;
    DefaultReload( MINIGUN_MAX_CLIP, MINIGUN_RELOAD, 1.5 );
}
Everything previously written would have been useless if I had not written the following in ev_hldm.cpp (hl_cdll):
// NEW MINIGUN
//======================
// MINIGUN START
//======================
enum minigun_e {
    MINIGUN_IDLE1 = 0,
    MINIGUN_IDLE2,
    MINIGUN_SPINUP,
    MINIGUN_SPINDOWN,
    MINIGUN_FIRE,
    MINIGUN_FIRE_EMPTY,
    MINIGUN_RELOAD,
    MINIGUN_DRAW,
    MINIGUN_HOLSTER
};

void EV_SpinUpMinigun( event_args_t *args )
{
    int idx;
    vec3_t origin;
    vec3_t angles;
    vec3_t velocity;

    idx = args->entindex;
    VectorCopy( args->origin, origin );
    VectorCopy( args->angles, angles );
    VectorCopy( args->velocity, velocity );

    gEngfuncs.pEventAPI->EV_PlaySound( idx, origin, CHAN_WEAPON, "weapons/minigun_spinup.wav", 1, ATTN_NORM, 0, PITCH_NORM);

    if ( EV_IsLocal( idx ) )
    {
        gEngfuncs.pEventAPI->EV_WeaponAnimation( MINIGUN_SPINUP, 1 );
    }
}

void EV_FireMinigun( event_args_t *args )
{
    int idx;
    vec3_t origin;
    vec3_t angles;
    vec3_t velocity;
    int empty;

    vec3_t ShellVelocity;
    vec3_t ShellOrigin;
    int shell;
    vec3_t vecSrc, vecAiming;
    vec3_t up, right, forward;
    float flSpread = 0.01;

    idx = args->entindex;
    VectorCopy( args->origin, origin );
    VectorCopy( args->angles, angles );
    VectorCopy( args->velocity, velocity );

    empty = args->bparam1;
    AngleVectors( angles, forward, right, up );

    shell = gEngfuncs.pEventAPI->EV_FindModelIndex ("models/shell.mdl");

    if ( EV_IsLocal( idx ) )
    {
        EV_MuzzleFlash();
        gEngfuncs.pEventAPI->EV_WeaponAnimation( empty ? MINIGUN_FIRE_EMPTY : MINIGUN_FIRE, 2 );
        V_PunchAxis( -2, 0 );
    }

    EV_GetDefaultShellInfo( args, origin, velocity, ShellVelocity, ShellOrigin, forward, right, up, 20, -12, 4 );

    EV_EjectBrass( ShellOrigin, ShellVelocity, angles[ YAW ], shell, TE_BOUNCE_SHELL );

    gEngfuncs.pEventAPI->EV_PlaySound( idx, origin, CHAN_WEAPON, "weapons/minigun_fire.wav", 1, ATTN_NORM, 0, 94 + gEngfuncs.pfnRandomLong( 0, 0xf ) );

    EV_GetGunPosition( args, vecSrc, origin );
    VectorCopy( forward, vecAiming );

    if ( gEngfuncs.GetMaxClients() > 1 )
    {
        EV_HLDM_FireBullets( idx, forward, right, up, 1, vecSrc, vecAiming, 8192, BULLET_PLAYER_MINIGUN, 2, &tracerCount[idx-1], args->fparam1, args->fparam2 );
    }
    else
    {
        EV_HLDM_FireBullets( idx, forward, right, up, 1, vecSrc, vecAiming, 8192, BULLET_PLAYER_MINIGUN, 2, &tracerCount[idx-1], args->fparam1, args->fparam2 );
    }
}

//======================
// MINIGUN END
//======================
For the other details that I did not comment on here, I suggest reviewing this article written by Penguinboy about weapons programming, I relied entirely on that article to make my new weapon. :hecu:
You must be logged in to post a response.