Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
SNMetamorph committed Dec 5, 2024
1 parent ce71b17 commit 077a372
Show file tree
Hide file tree
Showing 15 changed files with 577 additions and 396 deletions.
2 changes: 2 additions & 0 deletions server/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ list(APPEND SVDLL_SOURCES
"user_messages.cpp"
"util.cpp"
"weapons.cpp"
"weapon_logic.cpp"
"weapon_logic_funcs_impl.cpp"
"world.cpp"
)

Expand Down
22 changes: 22 additions & 0 deletions server/item_info.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#pragma once

#define ITEM_FLAG_SELECTONEMPTY 1
#define ITEM_FLAG_NOAUTORELOAD 2
#define ITEM_FLAG_NOAUTOSWITCHEMPTY 4
#define ITEM_FLAG_LIMITINWORLD 8
#define ITEM_FLAG_EXHAUSTIBLE 16 // A player can totally exhaust their ammo supply and lose this weapon

typedef struct
{
int iSlot;
int iPosition;
const char *pszAmmo1; // ammo 1 type
int iMaxAmmo1; // max ammo 1
const char *pszAmmo2; // ammo 2 type
int iMaxAmmo2; // max ammo 2
const char *pszName;
int iMaxClip;
int iId;
int iFlags;
int iWeight;// this value used to determine this weapon's importance in autoselection.
} ItemInfo;
2 changes: 1 addition & 1 deletion server/player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4915,7 +4915,7 @@ void CBasePlayer::DropPlayerItem ( char *pszItemName )

UTIL_MakeVectors ( GetAbsAngles() );

RemoveWeapon( pWeapon->m_iId ); // take item off hud
RemoveWeapon( pWeapon->iWeaponID() ); // take item off hud

CWeaponBox *pWeaponBox = (CWeaponBox *)CBaseEntity::Create( "weaponbox", GetAbsOrigin() + gpGlobals->v_forward * 10, GetAbsAngles(), edict() );
Vector vecAngles = pWeaponBox->GetAbsAngles();
Expand Down
2 changes: 1 addition & 1 deletion server/stats.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ void UpdateStats( CBasePlayer *pPlayer )

int index = pPlayer->GetAmmoIndex(II.pszAmmo1);
if ( index >= 0 )
ammoCount[ index ] += ((CBasePlayerWeapon *)p)->m_iClip;
ammoCount[ index ] += ((CBasePlayerWeapon *)p)->m_pWeaponLogic->m_iClip;

p = p->m_pNext;
}
Expand Down
249 changes: 249 additions & 0 deletions server/weapon_logic.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
#include "weapon_logic.h"
#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "player.h"
#include "monsters.h"
#include "weapons.h"
#include "nodes.h"
#include "soundent.h"
#include "decals.h"
#include "gamerules.h"

BOOL CanAttack( float attack_time, float curtime, BOOL isPredicted )
{
return ( attack_time <= curtime ) ? TRUE : FALSE;
}

CBaseWeaponLogic::CBaseWeaponLogic(IWeaponLogicFuncs *funcs) :
m_pFuncs(funcs),
m_pPlayer(nullptr)
{
}

CBaseWeaponLogic::~CBaseWeaponLogic()
{
if (m_pFuncs) {
delete m_pFuncs;
}
}

void CBaseWeaponLogic::ItemPostFrame()
{
if ((m_fInReload) && ( m_pPlayer->m_flNextAttack <= gpGlobals->time ))
{
// complete the reload.
int j = Q_min( iMaxClip() - m_iClip, m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]);

// Add them to the clip
m_iClip += j;
m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] -= j;

m_fInReload = FALSE;
}

if ((m_pPlayer->pev->button & IN_ATTACK2) && CanAttack( m_flNextSecondaryAttack, gpGlobals->time, UseDecrement() ) )
{
if ( pszAmmo2() && !m_pPlayer->m_rgAmmo[SecondaryAmmoIndex()] )
{
m_fFireOnEmpty = TRUE;
}

SecondaryAttack();
m_pPlayer->pev->button &= ~IN_ATTACK2;
}
else if ((m_pPlayer->pev->button & IN_ATTACK) && CanAttack( m_flNextPrimaryAttack, gpGlobals->time, UseDecrement() ) )
{
if ( (m_iClip == 0 && pszAmmo1()) || (iMaxClip() == -1 && !m_pPlayer->m_rgAmmo[PrimaryAmmoIndex()] ) )
{
m_fFireOnEmpty = TRUE;
}

PrimaryAttack();
}
else if ( m_pPlayer->pev->button & IN_RELOAD && iMaxClip() != WEAPON_NOCLIP && !m_fInReload )
{
// reload when reload is pressed, or if no buttons are down and weapon is empty.
Reload();
}
else if ( !(m_pPlayer->pev->button & (IN_ATTACK|IN_ATTACK2) ) )
{
// no fire buttons down

m_fFireOnEmpty = FALSE;

if ( !IsUseable() && m_flNextPrimaryAttack < ( UseDecrement() ? 0.0 : gpGlobals->time ) )
{
// weapon isn't useable, switch.
if ( !(iFlags() & ITEM_FLAG_NOAUTOSWITCHEMPTY) && m_pFuncs->GetNextBestWeapon() )
{
m_flNextPrimaryAttack = ( UseDecrement() ? 0.0 : gpGlobals->time ) + 0.3;
return;
}
}
else
{
// weapon is useable. Reload if empty and weapon has waited as long as it has to after firing
if ( m_iClip == 0 && !(iFlags() & ITEM_FLAG_NOAUTORELOAD) && m_flNextPrimaryAttack < ( UseDecrement() ? 0.0 : gpGlobals->time ) )
{
Reload();
return;
}
}

WeaponIdle( );
return;
}

// catch all
if ( ShouldWeaponIdle() )
{
WeaponIdle();
}
}

void CBaseWeaponLogic::Holster( void )
{
m_fInReload = FALSE; // cancel any reload in progress.
m_pPlayer->pev->viewmodel = 0;
m_pPlayer->pev->weaponmodel = 0;
}

//=========================================================
// IsUseable - this function determines whether or not a
// weapon is useable by the player in its current state.
// (does it have ammo loaded? do I have any ammo for the
// weapon?, etc)
//=========================================================
BOOL CBaseWeaponLogic :: IsUseable( void )
{
if ( m_iClip <= 0 )
{
if ( m_pPlayer->m_rgAmmo[ PrimaryAmmoIndex() ] <= 0 && iMaxAmmo1() != -1 )
{
// clip is empty (or nonexistant) and the player has no more ammo of this type.
return FALSE;
}
}

return TRUE;
}

BOOL CBaseWeaponLogic :: CanDeploy( void )
{
BOOL bHasAmmo = 0;

if ( !pszAmmo1() )
{
// this weapon doesn't use ammo, can always deploy.
return TRUE;
}

if ( pszAmmo1() )
{
bHasAmmo |= (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] != 0);
}
if ( pszAmmo2() )
{
bHasAmmo |= (m_pPlayer->m_rgAmmo[m_iSecondaryAmmoType] != 0);
}
if (m_iClip > 0)
{
bHasAmmo |= 1;
}
if (!bHasAmmo)
{
return FALSE;
}

return TRUE;
}

BOOL CBaseWeaponLogic :: DefaultDeploy( char *szViewModel, char *szWeaponModel, int iAnim, char *szAnimExt, int skiplocal /* = 0 */, int body )
{
if (!CanDeploy( ))
return FALSE;

m_pPlayer->pev->viewmodel = MAKE_STRING(szViewModel);
m_pPlayer->pev->weaponmodel = MAKE_STRING(szWeaponModel);
strcpy( m_pPlayer->m_szAnimExtention, szAnimExt );
SendWeaponAnim( iAnim, skiplocal, body );

m_pPlayer->m_flNextAttack = gpGlobals->time + 0.5;
m_flTimeWeaponIdle = gpGlobals->time + 1.0;

return TRUE;
}

BOOL CBaseWeaponLogic :: DefaultReload( int iClipSize, int iAnim, float fDelay, int body )
{
if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0)
return FALSE;

int j = Q_min(iClipSize - m_iClip, m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]);

if (j == 0)
return FALSE;

m_pPlayer->m_flNextAttack = gpGlobals->time + fDelay;

//!!UNDONE -- reload sound goes here !!!
SendWeaponAnim( iAnim, UseDecrement() ? 1 : 0 );

m_fInReload = TRUE;

m_flTimeWeaponIdle = gpGlobals->time + 3;
return TRUE;
}

void CBaseWeaponLogic::SendWeaponAnim( int iAnim, int skiplocal, int body )
{
if ( UseDecrement() )
skiplocal = 1;
else
skiplocal = 0;

m_pPlayer->pev->weaponanim = iAnim;

MESSAGE_BEGIN( MSG_ONE, SVC_WEAPONANIM, NULL, m_pPlayer->pev );
WRITE_BYTE( iAnim ); // sequence number
WRITE_BYTE( m_pFuncs->GetWeaponBodygroup() ); // weaponmodel bodygroup.
MESSAGE_END();
}

BOOL CBaseWeaponLogic :: PlayEmptySound( void )
{
if (m_iPlayEmptySound)
{
EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/357_cock1.wav", 0.8, ATTN_NORM);
m_iPlayEmptySound = 0;
return 0;
}
return 0;
}

void CBaseWeaponLogic :: ResetEmptySound( void )
{
m_iPlayEmptySound = 1;
}

int CBaseWeaponLogic::PrimaryAmmoIndex( void )
{
return m_iPrimaryAmmoType;
}

int CBaseWeaponLogic::SecondaryAmmoIndex( void )
{
return -1;
}

int CBaseWeaponLogic::iItemSlot(void) { return 0; } // return 0 to MAX_ITEMS_SLOTS, used in hud
int CBaseWeaponLogic::iItemPosition( void ) { return CBasePlayerItem::ItemInfoArray[ m_iId ].iPosition; }
const char *CBaseWeaponLogic::pszAmmo1( void ) { return CBasePlayerItem::ItemInfoArray[ m_iId ].pszAmmo1; }
int CBaseWeaponLogic::iMaxAmmo1( void ) { return CBasePlayerItem::ItemInfoArray[ m_iId ].iMaxAmmo1; }
const char *CBaseWeaponLogic::pszAmmo2( void ) { return CBasePlayerItem::ItemInfoArray[ m_iId ].pszAmmo2; }
int CBaseWeaponLogic::iMaxAmmo2( void ) { return CBasePlayerItem::ItemInfoArray[ m_iId ].iMaxAmmo2; }
const char *CBaseWeaponLogic::pszName( void ) { return CBasePlayerItem::ItemInfoArray[ m_iId ].pszName; }
int CBaseWeaponLogic::iMaxClip( void ) { return CBasePlayerItem::ItemInfoArray[ m_iId ].iMaxClip; }
int CBaseWeaponLogic::iWeight( void ) { return CBasePlayerItem::ItemInfoArray[ m_iId ].iWeight; }
int CBaseWeaponLogic::iFlags( void ) { return CBasePlayerItem::ItemInfoArray[ m_iId ].iFlags; }
68 changes: 68 additions & 0 deletions server/weapon_logic.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#pragma once
#include "weapon_logic_funcs.h"
#include "vector.h"
#include "item_info.h"

class CBasePlayer;

class CBaseWeaponLogic
{
public:
CBaseWeaponLogic(IWeaponLogicFuncs *funcs);
virtual ~CBaseWeaponLogic();
void ItemPostFrame();

// called by CBasePlayerWeapons ItemPostFrame()
virtual void PrimaryAttack( void ) { return; } // do "+ATTACK"
virtual void SecondaryAttack( void ) { return; } // do "+ATTACK2"
virtual void Reload( void ) { return; } // do "+RELOAD"
virtual void WeaponIdle( void ) { return; } // called when no buttons pressed

virtual BOOL ShouldWeaponIdle( void ) {return FALSE; };
virtual BOOL CanDeploy( void );
virtual BOOL Deploy( ) { return TRUE; }; // returns is deploy was successful
virtual BOOL CanHolster( void ) { return TRUE; }; // can this weapon be put away right nxow?
virtual void Holster(void);
virtual BOOL IsUseable( void );
virtual BOOL UseDecrement( void ) { return FALSE; };

virtual int GetItemInfo(ItemInfo *p) { return 0; }; // returns 0 if struct not filled out
virtual int PrimaryAmmoIndex();
virtual int SecondaryAmmoIndex();

virtual int iItemSlot(void);
virtual int iItemPosition(void);
virtual const char *pszAmmo1(void);
virtual int iMaxAmmo1(void);
virtual const char *pszAmmo2(void);
virtual int iMaxAmmo2(void);
virtual const char *pszName(void);
virtual int iMaxClip(void);
virtual int iWeight(void);
virtual int iFlags(void);

BOOL DefaultDeploy( char *szViewModel, char *szWeaponModel, int iAnim, char *szAnimExt, int skiplocal = 0, int body = 0 );
int DefaultReload( int iClipSize, int iAnim, float fDelay, int body = 0 );
void SendWeaponAnim( int iAnim, int skiplocal = 1, int body = 0 ); // skiplocal is 1 if client is predicting weapon animations
BOOL PlayEmptySound( void );
void ResetEmptySound( void );

int m_iId; // WEAPON_???
int m_iPlayEmptySound;
int m_fFireOnEmpty; // True when the gun is empty and the player is still holding down the attack key(s)
float m_flPumpTime;
int m_fInSpecialReload; // Are we in the middle of a reload for the shotguns
float m_flNextPrimaryAttack; // soonest time ItemPostFrame will call PrimaryAttack
float m_flNextSecondaryAttack; // soonest time ItemPostFrame will call SecondaryAttack
float m_flTimeWeaponIdle; // soonest time ItemPostFrame will call WeaponIdle
int m_iPrimaryAmmoType; // "primary" ammo index into players m_rgAmmo[]
int m_iSecondaryAmmoType; // "secondary" ammo index into players m_rgAmmo[]
int m_iClip; // number of shots left in the primary weapon clip, -1 it not used
int m_iClientClip; // the last version of m_iClip sent to hud dll
int m_iClientWeaponState; // the last version of the weapon state sent to hud dll (is current weapon, is on target)
int m_fInReload; // Are we in the middle of a reload;
int m_iDefaultAmmo;// how much ammo you get when you pick up this weapon as placed by a level designer.
CBasePlayer *m_pPlayer;
IWeaponLogicFuncs *m_pFuncs;
};

10 changes: 10 additions & 0 deletions server/weapon_logic_funcs.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#pragma once

class IWeaponLogicFuncs
{
public:
virtual ~IWeaponLogicFuncs() {};
virtual const char *GetWeaponClassname() = 0;
virtual int GetWeaponBodygroup() = 0;
virtual bool GetNextBestWeapon() = 0;
};
Loading

0 comments on commit 077a372

Please sign in to comment.