Authoring an ammo resupply entity

From Valve Developer Community
Jump to navigation Jump to search

This code is for the Source SDK 2013 Multiplayer legacy build. Go to your source sdk projects src/game/server/ folder and create item_ammo_cache.cpp

#include "cbase.h"
#include "baseanimating.h"
#include "ammodef.h"
#include "player.h"
#include "entitylist.h"

class CItemAmmoCache : public CBaseAnimating
{
public:
    DECLARE_CLASS(CItemAmmoCache, CBaseAnimating);
    DECLARE_DATADESC();

    // Required for HL2MP +USE detection
    virtual int ObjectCaps() { return BaseClass::ObjectCaps() | FCAP_IMPULSE_USE; }

    void Spawn(void);
    void Precache(void);
    void Use(CBaseEntity* pActivator, CBaseEntity* pCaller, USE_TYPE useType, float value);

    void RefillPlayerAmmo(CBasePlayer* pPlayer);
    void UpdateVisualState();

    void InputRefillAmmo(inputdata_t& inputData);
    void InputRefillAllPlayers(inputdata_t& inputData);
    void InputEnable(inputdata_t& inputData);
    void InputDisable(inputdata_t& inputData);

private:
    float m_flCooldown;
    float m_flNextUseTime;

    int   m_iMaxUses;
    int   m_iUsesRemaining;

    int   m_iTeamNumber;
    bool  m_bDisabled;
    bool  m_bUseColorOverlay;

    COutputEvent m_OnUsed;
    COutputEvent m_OnOutOfUses;
};

LINK_ENTITY_TO_CLASS(item_ammo_cache, CItemAmmoCache);

BEGIN_DATADESC(CItemAmmoCache)

DEFINE_KEYFIELD(m_flCooldown, FIELD_FLOAT, "Cooldown"),
DEFINE_KEYFIELD(m_iMaxUses, FIELD_INTEGER, "MaxUses"),
DEFINE_KEYFIELD(m_iTeamNumber, FIELD_INTEGER, "TeamNumber"),
DEFINE_KEYFIELD(m_bDisabled, FIELD_BOOLEAN, "StartDisabled"),
DEFINE_KEYFIELD(m_bUseColorOverlay, FIELD_BOOLEAN, "UseColorOverlay"),

DEFINE_FIELD(m_flNextUseTime, FIELD_TIME),
DEFINE_FIELD(m_iUsesRemaining, FIELD_INTEGER),

DEFINE_USEFUNC(Use),

DEFINE_INPUTFUNC(FIELD_VOID, "RefillAmmo", InputRefillAmmo),
DEFINE_INPUTFUNC(FIELD_VOID, "RefillAllPlayers", InputRefillAllPlayers),
DEFINE_INPUTFUNC(FIELD_VOID, "Enable", InputEnable),
DEFINE_INPUTFUNC(FIELD_VOID, "Disable", InputDisable),

DEFINE_OUTPUT(m_OnUsed, "OnUsed"),
DEFINE_OUTPUT(m_OnOutOfUses, "OnOutOfUses"),

END_DATADESC()


//-----------------------------------------------------------------------------
// Precache
//-----------------------------------------------------------------------------
void CItemAmmoCache::Precache(void)
{
    if (GetModelName() != NULL_STRING)
        PrecacheModel(STRING(GetModelName()));
    else
        PrecacheModel("models/props_junk/wood_crate001a.mdl");

    PrecacheScriptSound("AmmoCrate.Open");

    BaseClass::Precache();
}


//-----------------------------------------------------------------------------
// Spawn
//-----------------------------------------------------------------------------
void CItemAmmoCache::Spawn(void)
{
    Precache();

    if (GetModelName() == NULL_STRING)
        SetModel("models/props_junk/wood_crate001a.mdl");
    else
        SetModel(STRING(GetModelName()));

    SetSolid(SOLID_BBOX);
    SetMoveType(MOVETYPE_NONE);
    SetCollisionGroup(COLLISION_GROUP_NONE);

    UTIL_SetSize(this, Vector(-16, -16, 0), Vector(16, 16, 32));

    SetUse(&CItemAmmoCache::Use);

    m_flNextUseTime = 0.0f;
    m_iUsesRemaining = (m_iMaxUses <= 0) ? -1 : m_iMaxUses;

    UpdateVisualState();
}


//-----------------------------------------------------------------------------
// Visual State
//-----------------------------------------------------------------------------
void CItemAmmoCache::UpdateVisualState()
{
    if (!m_bUseColorOverlay)
    {
        // Force true model appearance (no Hammer tint)
        SetRenderColor(255, 255, 255);
        return;
    }

    if (m_bDisabled || m_iUsesRemaining == 0)
        SetRenderColor(255, 50, 50);   // Red (inactive)
    else
        SetRenderColor(50, 255, 50);   // Green (active)
}


//-----------------------------------------------------------------------------
// Core Ammo Refill Logic
//-----------------------------------------------------------------------------
void CItemAmmoCache::RefillPlayerAmmo(CBasePlayer* pPlayer)
{
    if (!pPlayer)
        return;

    CAmmoDef* pAmmoDef = GetAmmoDef();
    if (!pAmmoDef)
        return;

    for (int i = 0; i < MAX_AMMO_TYPES; ++i)
    {
        int maxCarry = pAmmoDef->MaxCarry(i);
        if (maxCarry > 0)
            pPlayer->SetAmmoCount(maxCarry, i);
    }

    EmitSound("AmmoCrate.Open");
}


//-----------------------------------------------------------------------------
// Use
//-----------------------------------------------------------------------------
void CItemAmmoCache::Use(CBaseEntity* pActivator, CBaseEntity* pCaller, USE_TYPE useType, float value)
{
    if (m_bDisabled)
        return;

    if (gpGlobals->curtime < m_flNextUseTime)
        return;

    if (!pActivator || !pActivator->IsPlayer())
        return;

    CBasePlayer* pPlayer = static_cast<CBasePlayer*>(pActivator);

    if (m_iTeamNumber > 0 && pPlayer->GetTeamNumber() != m_iTeamNumber)
        return;

    if (m_iUsesRemaining == 0)
        return;

    RefillPlayerAmmo(pPlayer);

    m_OnUsed.FireOutput(pPlayer, this);

    if (m_iUsesRemaining > 0)
    {
        m_iUsesRemaining--;

        if (m_iUsesRemaining == 0)
            m_OnOutOfUses.FireOutput(pPlayer, this);
    }

    m_flNextUseTime = gpGlobals->curtime + m_flCooldown;

    UpdateVisualState();
}


//-----------------------------------------------------------------------------
// Inputs
//-----------------------------------------------------------------------------
void CItemAmmoCache::InputRefillAmmo(inputdata_t& inputData)
{
    if (inputData.pActivator && inputData.pActivator->IsPlayer())
        RefillPlayerAmmo(static_cast<CBasePlayer*>(inputData.pActivator));
}

void CItemAmmoCache::InputRefillAllPlayers(inputdata_t& inputData)
{
    for (int i = 1; i <= gpGlobals->maxClients; ++i)
    {
        CBasePlayer* pPlayer = UTIL_PlayerByIndex(i);
        if (pPlayer)
            RefillPlayerAmmo(pPlayer);
    }
}

void CItemAmmoCache::InputEnable(inputdata_t&)
{
    m_bDisabled = false;
    UpdateVisualState();
}

void CItemAmmoCache::InputDisable(inputdata_t&)
{
    m_bDisabled = true;
    UpdateVisualState();
}

Add as an existing file to your server projects source file folder and recompile the entire solution. swap your client and server.dlls

in a mods own .fgd add

//-------------------------------------------------------------------------
//
// Items
//
//-------------------------------------------------------------------------
@PointClass base(Targetname, Angles, RenderFields) studio() = item_ammo_cache :
    "Ammo Cache - Advanced Ammo Refill Entity"
[
    model(studio) : "World Model" : ""
	
	UseColorOverlay(choices) : "Use Color Overlay" : 0 =
	[
		0 : "No"
		1 : "Yes"
	]
    Cooldown(float) : "Cooldown (seconds)" : 0
    MaxUses(integer) : "Max Uses (-1 = infinite)" : -1
    TeamNumber(integer) : "Team Filter (0 = all teams)" : 0

    StartDisabled(choices) : "Start Disabled" : 0 =
    [
        0 : "No"
        1 : "Yes"
    ]

    input RefillAmmo(void) : "Refill ammo for activator"
    input RefillAllPlayers(void) : "Refill ammo for all players"
    input Enable(void) : "Enable the ammo cache"
    input Disable(void) : "Disable the ammo cache"

    output OnUsed(void) : "Fired when used"
    output OnOutOfUses(void) : "Fired when last charge is consumed"
]

Open Hammer Editor and entity item_ammo_cache is created. This entity will supply users with maximum ammo for all available ammo types, including model assignment, cool down, usage limiting, team filtering, start disable support and hammer editor I/O support via func_buttons etc and color overlay with render handling. easily expandable..