Tutorial: Add a new simple weapon (straightforward way) Last edited 11 months ago2023-12-28 06:41:58 UTC

Disclaimer
This is not a proper way of adding new weapons because it does not use client side network prediction and stuff, but it is still reliable for single player only mods. For a more complete guide, refer to Weapons Programming - Standard Weapons.
Are you programming for a single-player Half-Life mod and looking for an easy way to add new, normal hitscan, shooting weapons? You are at the right place. In this tutorial, we will look into a way to add AK-47 from Counter-Strike 1.6 to Half-Life.

Before we start

This tutorial requires you to use:

Get the files

Get the models, sound and sprites from Counter-Strike 1.6 and put in the respective folders in your mod folder. Assets to be taken:

Files to edit

As a bare minimum, these are the files needed to be edited to get a working weapon (minus client side prediction). They are: As a matter of fact, ak47.cpp needs to be manually created by right clicking dlls folder in hldll/Source Files, click "Add, New Item", select "C/C++ source file", set the name to ak47.cpp and make sure it's created inside the dlls folder (not inside projects). This file needs to be added to hl_cdll as well by finding its location (alongside other weapons, search for mp5.cpp for example). Right click on the folder, click "Add" and click "Existing Items" and find the ak47.cpp in the dlls folder.

weapons.h

Scroll down and paste this code
// Enumeration of AK-47 animations in the view model file (v_ak47.mdl)
// Must match the same order as the model itself (look through a model viewer)
enum ak47_e
{
    AK47_IDLE,
    AK47_RELOAD,
    AK47_DRAW,
    AK47_SHOOT1,
    AK47_SHOOT2,
    AK47_SHOOT3,
};

// Main weapon class
class CAK47 : public CBasePlayerWeapon
{
    void Spawn() override;
    void Precache() override;
    // Which "slot" (column) in the HUD this weapon is located
    int iItemSlot() override { return 2; }
    bool GetItemInfo(ItemInfo* p) override;
    void PrimaryAttack() override;
    void Reload() override;
    bool Deploy() override;
    void WeaponIdle() override;
    bool UseDecrement() override
    {
#if defined(CLIENT_WEAPONS)
        return true;
#else
        return false;
#endif
    }

    int m_iShell;
};

ak47.cpp

Paste this code
#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "monsters.h"
#include "weapons.h"
#include "player.h"
#include "soundent.h"
#include "gamerules.h"
#include "UserMessages.h"

LINK_ENTITY_TO_CLASS(weapon_ak47, CAK47);

void CAK47::Spawn()
{
    Precache();
    SET_MODEL(ENT(pev), "models/w_ak47.mdl");
    m_iId = WEAPON_AK47;
    m_iDefaultAmmo = 30; // How much ammo this weapon has on spawn
    FallInit(); // get ready to fall down.
}

void CAK47::Precache()
{
    PRECACHE_MODEL("models/v_ak47.mdl");
    PRECACHE_MODEL("models/w_ak47.mdl");
    PRECACHE_MODEL("models/p_ak47.mdl");

    m_iShell = PRECACHE_MODEL("models/shell.mdl"); // brass shell

    PRECACHE_SOUND("weapons/ak47-1.wav");
}

bool CAK47::GetItemInfo(ItemInfo* p)
{
    p->pszName = STRING(pev->classname);
    p->pszAmmo1 = "762"; // Which ammo type this weapon use
    p->iMaxAmmo1 = 90; // What's the max ammo quantity for that kind of ammo
    p->pszAmmo2 = NULL;
    p->iMaxAmmo2 = NULL;
    p->iMaxClip = 30; // How many ammo this weapon's clip or magazine has
    p->iSlot = 2; // Which "slot" (column) in the HUD this weapon is located (2 = same slot as HL1 MP5, shotgun, crossbow)
    p->iPosition = 3; // Which "position" (row) in the HUD this weapon is located (3 = after HL1 crossbow)
    p->iFlags = 0; // Special flags this weapon has
    p->iId = m_iId = WEAPON_AK47;
    p->iWeight = MP5_WEIGHT; // How much "priority" this weapon has when auto-switch is triggered

    return true;
}

bool CAK47::Deploy()
{
    //  The last parameter is the animation set for the player model in thirdperson to use
    return DefaultDeploy("models/v_ak47.mdl", "models/p_ak47.mdl", AK47_DRAW, "mp5");
}

void CAK47::PrimaryAttack()
{
    // don't fire underwater
    if (m_pPlayer->pev->waterlevel == 3)
    {
        PlayEmptySound();
        m_flNextPrimaryAttack = 0.15;
        return;
    }

    // Don't fire if empty
    if (m_iClip <= 0)
    {
        PlayEmptySound();
        m_flNextPrimaryAttack = 0.15;
        return;
    }

    // Size of the muzzle flash and how much volume in the world the firing sound produces
    m_pPlayer->m_iWeaponVolume = NORMAL_GUN_VOLUME;
    m_pPlayer->m_iWeaponFlash = NORMAL_GUN_FLASH;
    m_pPlayer->pev->effects = (int)(m_pPlayer->pev->effects) | EF_MUZZLEFLASH;

    // player "shoot" animation
    m_pPlayer->SetAnimation(PLAYER_ATTACK1);

    Vector vecSrc = m_pPlayer->GetGunPosition();
    Vector vecAiming = m_pPlayer->GetAutoaimVector(AUTOAIM_5DEGREES);
    // Bullet firing code. By order:
    // 1. No. of bullet: 1 for other guns, multiple for shotgun)
    // 2. Firing source: its firing from player 3rd person mdl's gun attachment point
    // 3. Firing direction: The autoaim vector is mainly for autoaim feature, however it's still used to pinpoint end position
    // 4. Spread: How big the randomness of the bullet spread. To make 100% accuracy, make it Vector(0, 0, 0). Otherwise, you can use vector cone
        // 5. Trace line end position: 8192 is the max world length in HL
    // 6. Bullet type: Control end point effects and damage. If damage is not specified in slot number 8 below, it will use default value of MP5 damage.
    // 7. Frequency of tracer effect. Has a number between 0-4. 0 means no tracer at all (like in CS 1.6), 1 is the highest frequency.
    // 8. Damage. If left empty, it will use the default of value of bullet type.
    m_pPlayer->FireBullets(1, vecSrc, vecAiming, VECTOR_CONE_3DEGREES, 8192, BULLET_PLAYER_MP5, 1, 30);

    // Play view model animation and firing sound
    SendWeaponAnim(AK47_SHOOT1 + RANDOM_LONG(0, 2));
    // the 3rd entry defines the firing sound path. Make sure to precache first
    EMIT_SOUND(m_pPlayer->edict(), CHAN_WEAPON, "weapons/ak47-1.wav", 1, ATTN_NORM);

    // Eject the brass
    Vector vecShellVelocity = m_pPlayer->pev->velocity + gpGlobals->v_right * RANDOM_FLOAT(100, 200) +
        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 * 20 +
        gpGlobals->v_right * -8, vecShellVelocity, pev->angles.y, m_iShell, TE_BOUNCE_SHELL);

    // Punch the camera to simulate recoil
    m_pPlayer->pev->punchangle.x -= 2;
    // Remove a bullet
    m_iClip--;
    // Next time for attack and weapon idling
    m_flNextPrimaryAttack = 0.1;
    m_flTimeWeaponIdle = 0.85;
}

void CAK47::Reload()
{
    // If ammo is not 30, reload using AK47_RELOAD animation and disable firing and idling for 2.5 seconds
    DefaultReload(30, AK47_RELOAD, 2.5);
}


void CAK47::WeaponIdle()
{
    ResetEmptySound();

    // Not the time to idle
    if (m_flTimeWeaponIdle > UTIL_WeaponTimeBase())
        return;

    // Play idle animation
    SendWeaponAnim(AK47_IDLE);
    // When to call idle animation again in seconds. Change this value if your mdl has a certain value for idle animation for smooth transitioning
    m_flTimeWeaponIdle = 10;
}

cdll_dll.h

Add WEAPON_AK47 in the same WeaponID enum.

hl_weapons.cpp

case WEAPON_SNARK:
        pWeapon = &g_Snark;
        break;
and paste
case WEAPON_AK47:
    pWeapon = &g_AK47;
    break;
below it.

weapons.cpp

Find UTIL_PrecacheOtherWeapon("weapon_hornetgun"); and paste UTIL_PrecacheOtherWeapon("weapon_ak47"); below it.

player.cpp (Optional)

This is to add ak47 to the impulse 101 cheat command. Find GiveNamedItem("weapon_hornetgun"); and paste GiveNamedItem("weapon_ak47"); below it.

Finale

Save and compile your code. It should give you a working AK-47 under the Crossbow slot.

Edits

If you want to edit some of the attributes, you can do as below.

Enjoy!

You now have a working AK-47!

6 Comments

Commented 1 year ago2023-06-25 12:29:38 UTC Comment #105376
Works 100%. Thx. but my I ask you question, how can I use desert eagle sprites? I already have code for it but I can't bcz of sprites
Commented 1 year ago2023-07-16 04:08:33 UTC Comment #105403
@Draxler: What sprites? Do you mean the laser?
Commented 5 months ago2024-07-19 14:43:44 UTC Comment #106258
Great Tutorial.
I can get the gun in game, but it wont pick up any ammo so only get 30 shots.
says p->pszAmmo1 = "762"; I tried changing that to 9mm but the gun does not appear.
Commented 3 months ago2024-08-26 16:26:22 UTC Comment #106335
i got the gun in the game, but seems like the fire and reload animations don't work. the weapon just stays idle while it fires or reloads. i've checked the animation order in a model viewer and they're the same as in the animation enum. is this something to do with the half-life updated sdk?
Commented 3 months ago2024-08-28 14:20:56 UTC Comment #106341
im getting this error Host_Error: PF_precache_model_I: 'models/v_ak47.mdl' Precache can only be done in spawn functions
i copied all needed files and checked the sourcecode several times
any idea?
Commented 1 week ago2024-12-15 04:13:35 UTC Comment #106559
im getting this error Host_Error: PF_precache_model_I: 'models/v_ak47.mdl' Precache can only be done in spawn functions
To fix this, make sure you precache the model inside the Precache function of the weapon. If it's still happening, make sure to precache AK-47 in weapons.cpp.
i got the gun in the game, but seems like the fire and reload animations don't work.
Probably you forgot to add the ak47.cpp to hl_dlls
I tried changing that to 9mm but the gun does not appear.
That's very weird. Changing ammo type should not do that.

You must log in to post a comment. You can login or register a new account.