From c7c87b78cb1b507f06feda9606bd8e20487572a1 Mon Sep 17 00:00:00 2001 From: SNMetamorph Date: Mon, 6 Jan 2025 16:13:36 +0400 Subject: [PATCH] WIP --- client/CMakeLists.txt | 7 + game_shared/item_info.h | 75 +++++ game_shared/weapon_context.cpp | 297 +++++++++++++++++++ game_shared/weapon_context.h | 85 ++++++ game_shared/weapon_layer.h | 109 +++++++ game_shared/weapons/glock.cpp | 206 +++++++++++++ game_shared/weapons/glock.h | 43 +++ server/CMakeLists.txt | 7 +- server/entities/ammo_9mmclip.cpp | 3 +- server/gamerules/multiplay_gamerules.cpp | 4 +- server/player.cpp | 99 +------ server/player.h | 6 - server/server_weapon_layer_impl.cpp | 259 +++++++++++++++++ server/server_weapon_layer_impl.h | 42 +++ server/stats.cpp | 4 +- server/weapons.cpp | 321 ++++----------------- server/weapons.h | 202 ++++--------- server/weapons/crossbow.cpp | 40 +-- server/weapons/crossbow.h | 26 +- server/weapons/glock.cpp | 224 -------------- server/weapons/weapon_glock.cpp | 50 ++++ server/weapons/{glock.h => weapon_glock.h} | 14 +- 22 files changed, 1354 insertions(+), 769 deletions(-) create mode 100644 game_shared/item_info.h create mode 100644 game_shared/weapon_context.cpp create mode 100644 game_shared/weapon_context.h create mode 100644 game_shared/weapon_layer.h create mode 100644 game_shared/weapons/glock.cpp create mode 100644 game_shared/weapons/glock.h create mode 100644 server/server_weapon_layer_impl.cpp create mode 100644 server/server_weapon_layer_impl.h delete mode 100644 server/weapons/glock.cpp create mode 100644 server/weapons/weapon_glock.cpp rename server/weapons/{glock.h => weapon_glock.h} (73%) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 8a4ea000c..6b0063d4d 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -57,6 +57,8 @@ list(APPEND CLDLL_SOURCES "${CMAKE_SOURCE_DIR}/game_shared/stringlib.cpp" "${CMAKE_SOURCE_DIR}/game_shared/virtualfs.cpp" "${CMAKE_SOURCE_DIR}/game_shared/trace.cpp" + "${CMAKE_SOURCE_DIR}/game_shared/weapon_context.cpp" + "${CMAKE_SOURCE_DIR}/game_shared/seeded_random_generator.cpp" "${CMAKE_SOURCE_DIR}/game_shared/filesystem_utils.cpp" "${CMAKE_SOURCE_DIR}/game_shared/filesystem_manager.cpp" "${CMAKE_SOURCE_DIR}/public/crclib.cpp" @@ -70,6 +72,10 @@ file(GLOB ENTITIES_SOURCES "entities/*.cpp") # game events source files file(GLOB GAME_EVENTS_SOURCES "events/*.cpp") + +# weapon shared code source files +file(GLOB WEAPONS_SHARED_SOURCES "${CMAKE_SOURCE_DIR}/game_shared/weapons/*.cpp") + # ImGui source files if(NOT ENABLE_VGUI_COMPATIBILITY) list(APPEND IMGUI_SOURCES @@ -87,6 +93,7 @@ endif() list(APPEND CLDLL_SOURCES ${RENDER_SOURCES}) list(APPEND CLDLL_SOURCES ${ENTITIES_SOURCES}) list(APPEND CLDLL_SOURCES ${GAME_EVENTS_SOURCES}) +list(APPEND CLDLL_SOURCES ${WEAPONS_SHARED_SOURCES}) add_library(${PROJECT_NAME} SHARED ${CLDLL_SOURCES}) target_include_directories(${PROJECT_NAME} PRIVATE diff --git a/game_shared/item_info.h b/game_shared/item_info.h new file mode 100644 index 000000000..7f5e5d44e --- /dev/null +++ b/game_shared/item_info.h @@ -0,0 +1,75 @@ +/*** +* +* Copyright (c) 1996-2002, Valve LLC. All rights reserved. +* +* This product contains software technology licensed from Id +* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. +* All Rights Reserved. +* +* Use, distribution, and modification of this source code and/or resulting +* object code is restricted to non-commercial enhancements to products from +* Valve LLC. All other use, distribution, or modification is prohibited +* without written permission from Valve LLC. +* +****/ + +#pragma once +#include "cdll_dll.h" + +#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 + +#define WEAPON_NOCLIP -1 +#define WEAPON_ALLWEAPONS (~(1<GetPlayerNextAttackTime() <= m_pLayer->GetWeaponTimeBase()) + { + // complete the reload. + int j = Q_min( iMaxClip() - m_iClip, m_pLayer->GetPlayerAmmo(m_iPrimaryAmmoType) ); + + // Add them to the clip + m_iClip += j; + m_pLayer->SetPlayerAmmo( m_iPrimaryAmmoType, m_pLayer->GetPlayerAmmo(m_iPrimaryAmmoType) - j ); + + m_fInReload = FALSE; + } + + if (m_pLayer->CheckPlayerButtonFlag(IN_ATTACK2) && m_flNextSecondaryAttack <= m_pLayer->GetWeaponTimeBase() ) + { + if ( pszAmmo2() && !m_pLayer->GetPlayerAmmo(SecondaryAmmoIndex()) ) + { + m_fFireOnEmpty = TRUE; + } + + SecondaryAttack(); + m_pLayer->ClearPlayerButtonFlag(IN_ATTACK2); + } + else if (m_pLayer->CheckPlayerButtonFlag(IN_ATTACK) && m_flNextPrimaryAttack <= m_pLayer->GetWeaponTimeBase() ) + { + if ( (m_iClip == 0 && pszAmmo1()) || (iMaxClip() == -1 && !m_pLayer->GetPlayerAmmo(PrimaryAmmoIndex())) ) + { + m_fFireOnEmpty = TRUE; + } + + PrimaryAttack(); + } + else if ( m_pLayer->CheckPlayerButtonFlag(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_pLayer->CheckPlayerButtonFlag(IN_ATTACK|IN_ATTACK2) ) ) + { + // no fire buttons down + + m_fFireOnEmpty = FALSE; +#ifndef CLIENT_DLL // we don't need this branch on client side, because client is not responsible for changing weapons + if ( !IsUseable() && m_flNextPrimaryAttack < m_pLayer->GetWeaponTimeBase() ) + { + // weapon isn't useable, switch. GetNextBestWeapon exactly does weapon switching + if ( !(iFlags() & ITEM_FLAG_NOAUTOSWITCHEMPTY) && g_pGameRules->GetNextBestWeapon( m_pLayer->GetWeaponEntity()->m_pPlayer, m_pLayer->GetWeaponEntity() )) + { + m_flNextPrimaryAttack = m_pLayer->GetWeaponTimeBase() + 0.3; + return; + } + } + else +#endif + { + // 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 < m_pLayer->GetWeaponTimeBase() ) + { + Reload(); + return; + } + } + + WeaponIdle( ); + return; + } + + // catch all + if ( ShouldWeaponIdle() ) + { + WeaponIdle(); + } +} + +void CBaseWeaponContext::Holster() +{ + m_fInReload = FALSE; // cancel any reload in progress. + m_pLayer->SetPlayerViewmodel(0); +#ifndef CLIENT_DLL + m_pLayer->GetWeaponEntity()->m_pPlayer->pev->weaponmodel = 0; +#endif +} + +//========================================================= +// 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 CBaseWeaponContext :: IsUseable() +{ + if ( m_iClip <= 0 ) + { + if ( m_pLayer->GetPlayerAmmo( PrimaryAmmoIndex() ) <= 0 && iMaxAmmo1() != -1 ) + { + // clip is empty (or nonexistant) and the player has no more ammo of this type. + return FALSE; + } + } + + return TRUE; +} + +bool CBaseWeaponContext :: CanDeploy() +{ + BOOL bHasAmmo = 0; + + if ( !pszAmmo1() ) + { + // this weapon doesn't use ammo, can always deploy. + return TRUE; + } + + if ( pszAmmo1() ) + { + bHasAmmo |= (m_pLayer->GetPlayerAmmo(m_iPrimaryAmmoType) != 0); + } + if ( pszAmmo2() ) + { + bHasAmmo |= (m_pLayer->GetPlayerAmmo(m_iSecondaryAmmoType) != 0); + } + if (m_iClip > 0) + { + bHasAmmo |= 1; + } + if (!bHasAmmo) + { + return FALSE; + } + + return TRUE; +} + +bool CBaseWeaponContext :: DefaultDeploy( char *szViewModel, char *szWeaponModel, int iAnim, char *szAnimExt, int skiplocal /* = 0 */, int body ) +{ + if (!CanDeploy( )) + return FALSE; + +#ifndef CLIENT_DLL + //m_pLayer->GetWeaponEntity()->m_pPlayer->TabulateAmmo(); + m_pLayer->GetWeaponEntity()->m_pPlayer->pev->viewmodel = MAKE_STRING(szViewModel); + m_pLayer->GetWeaponEntity()->m_pPlayer->pev->weaponmodel = MAKE_STRING(szWeaponModel); + strcpy( m_pLayer->GetWeaponEntity()->m_pPlayer->m_szAnimExtention, szAnimExt ); +#else + // gEngfuncs.CL_LoadModel( szViewModel, &m_pPlayer->pev->viewmodel ); +#endif + SendWeaponAnim( iAnim, skiplocal, body ); + + m_pLayer->SetPlayerNextAttackTime(m_pLayer->GetWeaponTimeBase() + 0.5); + m_flTimeWeaponIdle = m_pLayer->GetWeaponTimeBase() + 1.0; + return TRUE; +} + +BOOL CBaseWeaponContext :: DefaultReload( int iClipSize, int iAnim, float fDelay, int body ) +{ + if (m_pLayer->GetPlayerAmmo(m_iPrimaryAmmoType) <= 0) + return FALSE; + + int j = Q_min(iClipSize - m_iClip, m_pLayer->GetPlayerAmmo(m_iPrimaryAmmoType)); + + if (j == 0) + return FALSE; + + m_pLayer->SetPlayerNextAttackTime(m_pLayer->GetWeaponTimeBase() + fDelay); + + //!!UNDONE -- reload sound goes here !!! + SendWeaponAnim( iAnim, UseDecrement() ? 1 : 0, body ); + + m_fInReload = TRUE; + + m_flTimeWeaponIdle = m_pLayer->GetWeaponTimeBase() + 3; + return TRUE; +} + +void CBaseWeaponContext::SendWeaponAnim( int iAnim, int skiplocal, int body ) +{ + m_pLayer->SetPlayerWeaponAnim(iAnim); + +#ifdef CLIENT_DLL + // HUD_SendWeaponAnim( iAnim, body, 0 ); +#else + if ( UseDecrement() ) + skiplocal = 1; + else + skiplocal = 0; + + if ( skiplocal && ENGINE_CANSKIP( m_pLayer->GetWeaponEntity()->m_pPlayer->edict() ) ) + return; + + MESSAGE_BEGIN( MSG_ONE, SVC_WEAPONANIM, NULL, m_pLayer->GetWeaponEntity()->m_pPlayer->pev ); + WRITE_BYTE( iAnim ); // sequence number + WRITE_BYTE( m_pLayer->GetWeaponBodygroup() ); // weaponmodel bodygroup. + MESSAGE_END(); +#endif +} + +bool CBaseWeaponContext :: PlayEmptySound() +{ + if (m_iPlayEmptySound) + { +#ifdef CLIENT_DLL + // HUD_PlaySound( "weapons/357_cock1.wav", 0.8 ); +#else + EMIT_SOUND(ENT(m_pLayer->GetWeaponEntity()->m_pPlayer->pev), CHAN_WEAPON, "weapons/357_cock1.wav", 0.8, ATTN_NORM); +#endif + m_iPlayEmptySound = 0; + return 0; + } + return 0; +} + +void CBaseWeaponContext :: ResetEmptySound() +{ + m_iPlayEmptySound = 1; +} + +int CBaseWeaponContext::PrimaryAmmoIndex() +{ + return m_iPrimaryAmmoType; +} + +int CBaseWeaponContext::SecondaryAmmoIndex() +{ + return -1; +} + +int CBaseWeaponContext::iItemSlot() { return 0; } // return 0 to MAX_ITEMS_SLOTS, used in hud +int CBaseWeaponContext::iItemPosition() { return CBaseWeaponContext::ItemInfoArray[ m_iId ].iPosition; } +const char *CBaseWeaponContext::pszAmmo1() { return CBaseWeaponContext::ItemInfoArray[ m_iId ].pszAmmo1; } +int CBaseWeaponContext::iMaxAmmo1() { return CBaseWeaponContext::ItemInfoArray[ m_iId ].iMaxAmmo1; } +const char *CBaseWeaponContext::pszAmmo2() { return CBaseWeaponContext::ItemInfoArray[ m_iId ].pszAmmo2; } +int CBaseWeaponContext::iMaxAmmo2() { return CBaseWeaponContext::ItemInfoArray[ m_iId ].iMaxAmmo2; } +const char *CBaseWeaponContext::pszName() { return CBaseWeaponContext::ItemInfoArray[ m_iId ].pszName; } +int CBaseWeaponContext::iMaxClip() { return CBaseWeaponContext::ItemInfoArray[ m_iId ].iMaxClip; } +int CBaseWeaponContext::iWeight() { return CBaseWeaponContext::ItemInfoArray[ m_iId ].iWeight; } +int CBaseWeaponContext::iFlags() { return CBaseWeaponContext::ItemInfoArray[ m_iId ].iFlags; } diff --git a/game_shared/weapon_context.h b/game_shared/weapon_context.h new file mode 100644 index 000000000..1cf6827af --- /dev/null +++ b/game_shared/weapon_context.h @@ -0,0 +1,85 @@ +/* +weapon_context.h - part of weapons implementation common for client & server +Copyright (C) 2024 SNMetamorph + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#pragma once +#include "vector.h" +#include "item_info.h" +#include "cdll_dll.h" +#include "weapon_layer.h" +#include + +class CBaseWeaponContext +{ +public: + CBaseWeaponContext(IWeaponLayer *layer); + virtual ~CBaseWeaponContext(); + + // called by CBasePlayerWeapons ItemPostFrame() + virtual void PrimaryAttack() {} // do "+ATTACK" + virtual void SecondaryAttack() {} // do "+ATTACK2" + virtual void Reload() {} // do "+RELOAD" + virtual void WeaponIdle() {} // called when no buttons pressed + + void ItemPostFrame(); + + virtual bool ShouldWeaponIdle() { return false; }; + virtual bool CanDeploy(); + virtual bool Deploy() { return true; }; // returns is deploy was successful + virtual bool CanHolster() { return true; }; // can this weapon be put away right nxow? + virtual void Holster(); + virtual bool IsUseable(); + virtual bool UseDecrement() { return false; }; // always true because weapon prediction enabled regardless of anything + + virtual int GetItemInfo(ItemInfo *p) { return 0; }; // returns 0 if struct not filled out + virtual int PrimaryAmmoIndex(); + virtual int SecondaryAmmoIndex(); + + virtual int iItemSlot(); + virtual int iItemPosition(); + virtual const char *pszAmmo1(); + virtual int iMaxAmmo1(); + virtual const char *pszAmmo2(); + virtual int iMaxAmmo2(); + virtual const char *pszName(); + virtual int iMaxClip(); + virtual int iWeight(); + virtual int iFlags(); + + 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 ResetEmptySound(); + + static ItemInfo ItemInfoArray[ MAX_WEAPONS ]; + static AmmoInfo AmmoInfoArray[ MAX_AMMO_SLOTS ]; + + 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. + IWeaponLayer *m_pLayer; +}; diff --git a/game_shared/weapon_layer.h b/game_shared/weapon_layer.h new file mode 100644 index 000000000..117b8d33a --- /dev/null +++ b/game_shared/weapon_layer.h @@ -0,0 +1,109 @@ +/* +weapon_layer.h - interface for abstracting client & server weapons implementation differences +Copyright (C) 2024 SNMetamorph + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#pragma once +#include "vector.h" +#include "matrix.h" +#include + +#define _CLASSNAME_STR(s) (#s) +#define CLASSNAME_STR(s) _CLASSNAME_STR(s) + +#define AUTOAIM_2DEGREES 0.0348994967025 +#define AUTOAIM_5DEGREES 0.08715574274766 +#define AUTOAIM_8DEGREES 0.1391731009601 +#define AUTOAIM_10DEGREES 0.1736481776669 + +// do not change, these flags are shared with engine +enum class WeaponEventFlags : int +{ + // Empty flag + None = 0, + + // Skip local host for event send. + NotHost = (1<<0), + + // Send the event reliably. You must specify the origin and angles and use + // PLAYBACK_EVENT_FULL for this to work correctly on the server for anything + // that depends on the event origin/angles. I.e., the origin/angles are not + // taken from the invoking edict for reliable events. + Reliable = (1<<1), + + // Don't restrict to PAS/PVS, send this event to _everybody_ on the server ( useful for stopping CHAN_STATIC + // sounds started by client event when client is not in PVS anymore ( hwguy in TFC e.g. ). + Global = (1<<2), + + // If this client already has one of these events in its queue, just update the event instead of sending it as a duplicate + Update = (1<<3), + + // Only send to entity specified as the invoker + HostOnly = (1<<4), + + // Only send if the event was created on the server. + Server = (1<<5), + + // Only issue event client side ( from shared code ) + Client = (1<<6) +}; + +struct WeaponEventParams +{ + WeaponEventFlags flags; + float *origin; + float *angles; + float delay; + float fparam1; + float fparam2; + int iparam1; + int iparam2; + int bparam1; + int bparam2; + uint16_t eventindex; +}; + +// forward declaration, client should not know anything about server entities +class CBasePlayerWeapon; + +class IWeaponLayer +{ +public: + virtual ~IWeaponLayer() {}; + + // accessing weapon entity state + virtual int GetWeaponBodygroup() = 0; + virtual Vector GetGunPosition() = 0; + virtual matrix3x3 GetCameraOrientation() = 0; + virtual Vector GetAutoaimVector(float delta) = 0; + virtual Vector FireBullets(int bullets, Vector origin, matrix3x3 orientation, float distance, float spread, int bulletType, uint32_t seed, int damage = 0) = 0; + virtual CBasePlayerWeapon* GetWeaponEntity() = 0; + + // modifying/accessing player state + virtual int GetPlayerAmmo(int ammoType) = 0; + virtual void SetPlayerAmmo(int ammoType, int count) = 0; + virtual void SetPlayerWeaponAnim(int anim) = 0; + virtual void SetPlayerViewmodel(int model) = 0; + virtual bool CheckPlayerButtonFlag(int buttonMask) = 0; + virtual void ClearPlayerButtonFlag(int buttonMask) = 0; + virtual float GetPlayerNextAttackTime() = 0; + virtual void SetPlayerNextAttackTime(float value) = 0; + + // miscellaneous things + virtual float GetWeaponTimeBase() = 0; + virtual uint32_t GetRandomSeed() = 0; + virtual uint32_t GetRandomInt(uint32_t seed, int32_t min, int32_t max) = 0; + virtual float GetRandomFloat(uint32_t seed, float min, float max) = 0; + virtual uint16_t PrecacheEvent(const char *eventName) = 0; + virtual void PlaybackWeaponEvent(const WeaponEventParams ¶ms) = 0; +}; diff --git a/game_shared/weapons/glock.cpp b/game_shared/weapons/glock.cpp new file mode 100644 index 000000000..8af9433a7 --- /dev/null +++ b/game_shared/weapons/glock.cpp @@ -0,0 +1,206 @@ +/*** +* +* Copyright (c) 1996-2002, Valve LLC. All rights reserved. +* +* This product contains software technology licensed from Id +* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. +* All Rights Reserved. +* +* Use, distribution, and modification of this source code and/or resulting +* object code is restricted to non-commercial enhancements to products from +* Valve LLC. All other use, distribution, or modification is prohibited +* without written permission from Valve LLC. +* +****/ + +#include "glock.h" + +#ifdef CLIENT_DLL + +#else +#include "extdll.h" +#include "util.h" +#include "cbase.h" +#include "monsters.h" +#include "weapons.h" +#include "nodes.h" +#include "player.h" +#endif + +enum glock_e +{ + GLOCK_IDLE1 = 0, + GLOCK_IDLE2, + GLOCK_IDLE3, + GLOCK_SHOOT, + GLOCK_SHOOT_EMPTY, + GLOCK_RELOAD, + GLOCK_RELOAD_NOT_EMPTY, + GLOCK_DRAW, + GLOCK_HOLSTER, + GLOCK_ADD_SILENCER +}; + +CGlockWeaponLogic::CGlockWeaponLogic(IWeaponLayer *layer) : + CBaseWeaponContext(layer) +{ + m_iDefaultAmmo = GLOCK_DEFAULT_GIVE; + m_iId = WEAPON_GLOCK; + m_usFireGlock1 = m_pLayer->PrecacheEvent("events/glock1.sc"); + m_usFireGlock2 = m_pLayer->PrecacheEvent("events/glock2.sc"); +} + +int CGlockWeaponLogic::GetItemInfo(ItemInfo *p) +{ + p->pszName = CLASSNAME_STR(GLOCK_CLASSNAME); + p->pszAmmo1 = "9mm"; + p->iMaxAmmo1 = _9MM_MAX_CARRY; + p->pszAmmo2 = NULL; + p->iMaxAmmo2 = -1; + p->iMaxClip = GLOCK_MAX_CLIP; + p->iSlot = 1; + p->iPosition = 0; + p->iFlags = 0; + p->iId = m_iId; + p->iWeight = GLOCK_WEIGHT; + + return 1; +} + +bool CGlockWeaponLogic::Deploy( ) +{ + // pev->body = 1; + return DefaultDeploy( "models/v_9mmhandgun.mdl", "models/p_9mmhandgun.mdl", GLOCK_DRAW, "onehanded" ); +} + +void CGlockWeaponLogic::SecondaryAttack( void ) +{ + GlockFire( 0.1, 0.2, FALSE ); +} + +void CGlockWeaponLogic::PrimaryAttack( void ) +{ + GlockFire( 0.01, 0.3, TRUE ); +} + +void CGlockWeaponLogic::GlockFire( float flSpread , float flCycleTime, bool fUseAutoAim ) +{ + if (m_iClip <= 0) + { + if (m_fFireOnEmpty) + { + PlayEmptySound(); + m_flNextPrimaryAttack = m_pLayer->GetWeaponTimeBase() + 0.2; + } + + return; + } + + m_iClip--; + + if (m_iClip != 0) + SendWeaponAnim( GLOCK_SHOOT ); + else + SendWeaponAnim( GLOCK_SHOOT_EMPTY ); + +#ifndef CLIENT_DLL + // player "shoot" animation + m_pLayer->GetWeaponEntity()->m_pPlayer->SetAnimation( PLAYER_ATTACK1 ); + m_pLayer->GetWeaponEntity()->m_pPlayer->pev->effects = (int)(m_pLayer->GetWeaponEntity()->m_pPlayer->pev->effects) | EF_MUZZLEFLASH; + + // silenced + if (m_pLayer->GetWeaponBodygroup() == 1) + { + m_pLayer->GetWeaponEntity()->m_pPlayer->m_iWeaponVolume = QUIET_GUN_VOLUME; + m_pLayer->GetWeaponEntity()->m_pPlayer->m_iWeaponFlash = DIM_GUN_FLASH; + } + else + { + // non-silenced + m_pLayer->GetWeaponEntity()->m_pPlayer->m_iWeaponVolume = NORMAL_GUN_VOLUME; + m_pLayer->GetWeaponEntity()->m_pPlayer->m_iWeaponFlash = NORMAL_GUN_FLASH; + } +#endif + + Vector vecSrc = m_pLayer->GetGunPosition(); + matrix3x3 aimMatrix = m_pLayer->GetCameraOrientation(); + + if (fUseAutoAim) { + aimMatrix.SetForward(m_pLayer->GetAutoaimVector(AUTOAIM_10DEGREES)); + } + + Vector vecDir = m_pLayer->FireBullets(1, vecSrc, aimMatrix, 8192, flSpread, BULLET_PLAYER_9MM, m_pLayer->GetRandomSeed()); + m_flNextPrimaryAttack = m_flNextSecondaryAttack = m_pLayer->GetWeaponTimeBase() + flCycleTime; + + WeaponEventParams params; + params.flags = WeaponEventFlags::None; //WeaponEventFlags::NotHost; + params.eventindex = fUseAutoAim ? m_usFireGlock1 : m_usFireGlock2; + params.delay = 0.0f; + params.origin = vecSrc; + params.angles = aimMatrix.GetAngles(); + params.fparam1 = vecDir.x; + params.fparam2 = vecDir.y; + params.iparam1 = 0; + params.iparam2 = 0; + params.bparam1 = (m_iClip == 0) ? 1 : 0; + params.bparam2 = 0; + m_pLayer->PlaybackWeaponEvent(params); + + // PLAYBACK_EVENT_FULL( flags, m_pPlayer->edict(), fUseAutoAim ? m_usFireGlock1 : m_usFireGlock2, 0.0, (float *)&g_vecZero, (float *)&g_vecZero, vecDir.x, vecDir.y, 0, 0, ( m_iClip == 0 ) ? 1 : 0, 0 ); + +#ifndef CLIENT_DLL + if (!m_iClip && m_pLayer->GetPlayerAmmo(m_iPrimaryAmmoType) <= 0) + // HEV suit - indicate out of ammo condition + m_pLayer->GetWeaponEntity()->m_pPlayer->SetSuitUpdate("!HEV_AMO0", FALSE, 0); +#endif + m_flTimeWeaponIdle = m_pLayer->GetWeaponTimeBase() + m_pLayer->GetRandomFloat(m_pLayer->GetRandomSeed(), 10.f, 15.f); + //m_pPlayer->pev->punchangle.x -= 2; +} + +void CGlockWeaponLogic::Reload( void ) +{ + int iResult; + + if (m_iClip == 0) + iResult = DefaultReload( 17, GLOCK_RELOAD, 1.5 ); + else + iResult = DefaultReload( 17, GLOCK_RELOAD_NOT_EMPTY, 1.5 ); + + if (iResult) + { + m_flTimeWeaponIdle = m_pLayer->GetWeaponTimeBase() + m_pLayer->GetRandomFloat(m_pLayer->GetRandomSeed(), 10.0f, 15.0f); + } +} + +void CGlockWeaponLogic::WeaponIdle( void ) +{ + ResetEmptySound( ); + + m_pLayer->GetAutoaimVector( AUTOAIM_10DEGREES ); + + if (m_flTimeWeaponIdle > m_pLayer->GetWeaponTimeBase()) + return; + + // only idle if the slid isn't back + if (m_iClip != 0) + { + int iAnim; + float flRand = m_pLayer->GetRandomFloat(m_pLayer->GetRandomSeed(), 0.0f, 1.0f); + if (flRand <= 0.3 + 0 * 0.75) + { + iAnim = GLOCK_IDLE3; + m_flTimeWeaponIdle = m_pLayer->GetWeaponTimeBase() + 49.0 / 16; + } + else if (flRand <= 0.6 + 0 * 0.875) + { + iAnim = GLOCK_IDLE1; + m_flTimeWeaponIdle = m_pLayer->GetWeaponTimeBase() + 60.0 / 16.0; + } + else + { + iAnim = GLOCK_IDLE2; + m_flTimeWeaponIdle = m_pLayer->GetWeaponTimeBase() + 40.0 / 16.0; + } + SendWeaponAnim( iAnim ); + } +} diff --git a/game_shared/weapons/glock.h b/game_shared/weapons/glock.h new file mode 100644 index 000000000..06fff3738 --- /dev/null +++ b/game_shared/weapons/glock.h @@ -0,0 +1,43 @@ +/*** +* +* Copyright (c) 1996-2002, Valve LLC. All rights reserved. +* +* This product contains software technology licensed from Id +* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. +* All Rights Reserved. +* +* Use, distribution, and modification of this source code and/or resulting +* object code is restricted to non-commercial enhancements to products from +* Valve LLC. All other use, distribution, or modification is prohibited +* without written permission from Valve LLC. +* +****/ + +#pragma once +#include "weapon_context.h" +#include "weapon_layer.h" + +#define WEAPON_GLOCK 2 +#define GLOCK_WEIGHT 10 +#define GLOCK_MAX_CLIP 17 +#define GLOCK_DEFAULT_GIVE 17 +#define GLOCK_CLASSNAME weapon_glock + +class CGlockWeaponLogic : public CBaseWeaponContext +{ +public: + CGlockWeaponLogic() = delete; + CGlockWeaponLogic(IWeaponLayer *layer); + + int iItemSlot() override { return 2; } + int GetItemInfo(ItemInfo *p) override; + void PrimaryAttack() override; + void SecondaryAttack() override; + bool Deploy() override; + void Reload() override; + void WeaponIdle() override; + void GlockFire( float flSpread, float flCycleTime, bool fUseAutoAim ); + + uint16_t m_usFireGlock1; + uint16_t m_usFireGlock2; +}; diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index e4fda15ae..ba3cb7178 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -48,6 +48,7 @@ list(APPEND SVDLL_SOURCES "user_messages.cpp" "util.cpp" "weapons.cpp" + "server_weapon_layer_impl.cpp" "world.cpp" ) @@ -65,6 +66,8 @@ list(APPEND SVDLL_SOURCES "${CMAKE_SOURCE_DIR}/game_shared/vector.cpp" "${CMAKE_SOURCE_DIR}/game_shared/virtualfs.cpp" "${CMAKE_SOURCE_DIR}/game_shared/trace.cpp" + "${CMAKE_SOURCE_DIR}/game_shared/weapon_context.cpp" + "${CMAKE_SOURCE_DIR}/game_shared/seeded_random_generator.cpp" "${CMAKE_SOURCE_DIR}/game_shared/meshdesc.cpp" "${CMAKE_SOURCE_DIR}/game_shared/meshdesc_factory.cpp" "${CMAKE_SOURCE_DIR}/game_shared/filesystem_utils.cpp" @@ -89,8 +92,9 @@ endif() file(GLOB ENTITIES_SOURCES "entities/*.cpp") file(GLOB MONSTERS_SOURCES "monsters/*.cpp") -file(GLOB WEAPONS_SOURCES "weapons/*.cpp") +file(GLOB WEAPONS_SOURCES "weapons/weapon_*.cpp") file(GLOB GAMERULES_SOURCES "gamerules/*.cpp") +file(GLOB WEAPONS_SHARED_SOURCES "${CMAKE_SOURCE_DIR}/game_shared/weapons/*.cpp") # add .def file to sources if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") @@ -101,6 +105,7 @@ list(APPEND SVDLL_SOURCES ${ENTITIES_SOURCES}) list(APPEND SVDLL_SOURCES ${MONSTERS_SOURCES}) list(APPEND SVDLL_SOURCES ${WEAPONS_SOURCES}) list(APPEND SVDLL_SOURCES ${GAMERULES_SOURCES}) +list(APPEND SVDLL_SOURCES ${WEAPONS_SHARED_SOURCES}) add_library(${PROJECT_NAME} SHARED ${SVDLL_SOURCES}) target_include_directories(${PROJECT_NAME} PRIVATE diff --git a/server/entities/ammo_9mmclip.cpp b/server/entities/ammo_9mmclip.cpp index 8d93d0116..ca74d6626 100644 --- a/server/entities/ammo_9mmclip.cpp +++ b/server/entities/ammo_9mmclip.cpp @@ -14,6 +14,7 @@ ****/ #include "ammo_9mmclip.h" +#include "weapons/glock.h" LINK_ENTITY_TO_CLASS( ammo_glockclip, CGlockAmmo ); LINK_ENTITY_TO_CLASS( ammo_9mmclip, CGlockAmmo ); @@ -33,7 +34,7 @@ void CGlockAmmo::Precache() BOOL CGlockAmmo::AddAmmo( CBaseEntity *pOther ) { - if (pOther->GiveAmmo( AMMO_GLOCKCLIP_GIVE, "9mm", _9MM_MAX_CARRY ) != -1) + if (pOther->GiveAmmo( GLOCK_MAX_CLIP, "9mm", _9MM_MAX_CARRY ) != -1) { EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/9mmclip1.wav", 1, ATTN_NORM); return TRUE; diff --git a/server/gamerules/multiplay_gamerules.cpp b/server/gamerules/multiplay_gamerules.cpp index ac28c586d..680668478 100644 --- a/server/gamerules/multiplay_gamerules.cpp +++ b/server/gamerules/multiplay_gamerules.cpp @@ -874,7 +874,7 @@ float CHalfLifeMultiplay :: FlWeaponRespawnTime( CBasePlayerItem *pWeapon ) //========================================================= float CHalfLifeMultiplay :: FlWeaponTryRespawn( CBasePlayerItem *pWeapon ) { - if ( pWeapon && pWeapon->m_iId && (pWeapon->iFlags() & ITEM_FLAG_LIMITINWORLD) ) + if ( pWeapon && pWeapon->iWeaponID() && (pWeapon->iFlags() & ITEM_FLAG_LIMITINWORLD)) { if ( NUMBER_OF_ENTITIES() < (gpGlobals->maxEntities - ENTITY_INTOLERANCE) ) return 0; @@ -927,7 +927,7 @@ BOOL CHalfLifeMultiplay::CanHavePlayerItem( CBasePlayer *pPlayer, CBasePlayerIte while ( it != NULL ) { - if ( it->m_iId == pItem->m_iId ) + if ( it->iWeaponID() == pItem->iWeaponID()) { return FALSE; } diff --git a/server/player.cpp b/server/player.cpp index 579f48702..89e974134 100644 --- a/server/player.cpp +++ b/server/player.cpp @@ -668,7 +668,7 @@ void CBasePlayer::PackDeadPlayerItems( void ) // pack the ammo while ( iPackAmmo[iPA] != -1 ) { - pWeaponBox->PackAmmo( MAKE_STRING( CBasePlayerItem::AmmoInfoArray[iPackAmmo[iPA]].pszName ), m_rgAmmo[iPackAmmo[iPA]] ); + pWeaponBox->PackAmmo( MAKE_STRING( CBaseWeaponContext::AmmoInfoArray[iPackAmmo[iPA]].pszName ), m_rgAmmo[iPackAmmo[iPA]] ); iPA++; } @@ -2623,7 +2623,7 @@ void CBasePlayer::PostThink() } // do weapon stuff - ItemPostFrame( ); + ItemPostFrame(); // check to see if player landed hard enough to make a sound // falling farther than half of the maximum safe distance, but not as far a max safe distance will @@ -4068,7 +4068,7 @@ void CBasePlayer::ItemPostFrame() if (!m_pActiveItem) return; - m_pActiveItem->ItemPostFrame( ); + m_pActiveItem->ItemPostFrame(); } int CBasePlayer::AmmoInventory( int iAmmoIndex ) @@ -4090,10 +4090,10 @@ int CBasePlayer::GetAmmoIndex(const char *psz) for (i = 1; i < MAX_AMMO_SLOTS; i++) { - if ( !CBasePlayerItem::AmmoInfoArray[i].pszName ) + if ( !CBaseWeaponContext::AmmoInfoArray[i].pszName ) continue; - if (stricmp( psz, CBasePlayerItem::AmmoInfoArray[i].pszName ) == 0) + if (stricmp( psz, CBaseWeaponContext::AmmoInfoArray[i].pszName ) == 0) return i; } @@ -4449,7 +4449,7 @@ void CBasePlayer :: UpdateClientData( void ) for (i = 0; i < MAX_WEAPONS; i++) { - ItemInfo& II = CBasePlayerItem::ItemInfoArray[i]; + ItemInfo& II = CBaseWeaponContext::ItemInfoArray[i]; if ( !II.iId ) continue; @@ -4605,91 +4605,6 @@ void CBasePlayer :: HideWeapons( BOOL fHideWeapons ) #define DOT_20DEGREE 0.9396926207859 #define DOT_25DEGREE 0.9063077870367 -//========================================================= -// Autoaim -// set crosshair position to point to enemey -//========================================================= -Vector CBasePlayer :: GetAutoaimVector( float flDelta ) -{ - if (g_iSkillLevel == SKILL_HARD) - { - UTIL_MakeVectors( pev->v_angle + pev->punchangle ); - return gpGlobals->v_forward; - } - - Vector vecSrc = GetGunPosition( ); - float flDist = 8192; - - // always use non-sticky autoaim - // UNDONE: use server variable to chose! - if (1 || g_iSkillLevel == SKILL_MEDIUM) - { - m_vecAutoAim = Vector( 0, 0, 0 ); - // flDelta *= 0.5; - } - - BOOL m_fOldTargeting = m_fOnTarget; - Vector angles = AutoaimDeflection(vecSrc, flDist, flDelta ); - - // update ontarget if changed - if ( !g_pGameRules->AllowAutoTargetCrosshair() ) - m_fOnTarget = 0; - else if (m_fOldTargeting != m_fOnTarget) - { - m_pActiveItem->UpdateItemInfo( ); - } - - if (angles.x > 180) - angles.x -= 360; - if (angles.x < -180) - angles.x += 360; - if (angles.y > 180) - angles.y -= 360; - if (angles.y < -180) - angles.y += 360; - - if (angles.x > 25) - angles.x = 25; - if (angles.x < -25) - angles.x = -25; - if (angles.y > 12) - angles.y = 12; - if (angles.y < -12) - angles.y = -12; - - - // always use non-sticky autoaim - // UNDONE: use sever variable to chose! - if (0 || g_iSkillLevel == SKILL_EASY) - { - m_vecAutoAim = m_vecAutoAim * 0.67 + angles * 0.33; - } - else - { - m_vecAutoAim = angles * 0.9; - } - - // m_vecAutoAim = m_vecAutoAim * 0.99; - - // Don't send across network if sv_aim is 0 - if ( g_psv_aim->value != 0 ) - { - if ( m_vecAutoAim.x != m_lastx || m_vecAutoAim.y != m_lasty ) - { - SET_CROSSHAIRANGLE( edict(), -m_vecAutoAim.x, m_vecAutoAim.y ); - - m_lastx = m_vecAutoAim.x; - m_lasty = m_vecAutoAim.y; - } - } - - // ALERT( at_console, "%f %f\n", angles.x, angles.y ); - - UTIL_MakeVectors( pev->v_angle + pev->punchangle + m_vecAutoAim ); - return gpGlobals->v_forward; -} - - Vector CBasePlayer :: AutoaimDeflection( Vector &vecSrc, float flDist, float flDelta ) { edict_t *pEdict = INDEXENT( 1 ); @@ -4915,7 +4830,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(); diff --git a/server/player.h b/server/player.h index 2839364ac..c9a0fca10 100644 --- a/server/player.h +++ b/server/player.h @@ -328,7 +328,6 @@ class CBasePlayer : public CBaseMonster int Illumination( void ); void ResetAutoaim( void ); - Vector GetAutoaimVector( float flDelta ); Vector AutoaimDeflection( Vector &vecSrc, float flDist, float flDelta ); void ForceClientDllUpdate( void ); // Forces all client .dll specific data to be resent to client. @@ -409,11 +408,6 @@ class CBasePlayer : public CBaseMonster bool m_bRainNeedsUpdate; // don't save\restore this }; -#define AUTOAIM_2DEGREES 0.0348994967025 -#define AUTOAIM_5DEGREES 0.08715574274766 -#define AUTOAIM_8DEGREES 0.1391731009601 -#define AUTOAIM_10DEGREES 0.1736481776669 - extern BOOL gInitHUD; diff --git a/server/server_weapon_layer_impl.cpp b/server/server_weapon_layer_impl.cpp new file mode 100644 index 000000000..04a6e818b --- /dev/null +++ b/server/server_weapon_layer_impl.cpp @@ -0,0 +1,259 @@ +#include "server_weapon_layer_impl.h" +#include "gamerules.h" +#include "game.h" + +CServerWeaponLayerImpl::CServerWeaponLayerImpl(CBasePlayerWeapon *weaponEntity) : + m_pWeapon(weaponEntity) +{ +} + +int CServerWeaponLayerImpl::GetWeaponBodygroup() +{ + return m_pWeapon->pev->body; +} + +Vector CServerWeaponLayerImpl::GetGunPosition() +{ + return m_pWeapon->m_pPlayer->pev->origin + m_pWeapon->m_pPlayer->pev->view_ofs; +} + +matrix3x3 CServerWeaponLayerImpl::GetCameraOrientation() +{ + CBasePlayer *player = m_pWeapon->m_pPlayer; + return matrix3x3(player->pev->v_angle + player->pev->punchangle); +} + +Vector CServerWeaponLayerImpl::GetAutoaimVector(float delta) +{ + CBasePlayer *player = m_pWeapon->m_pPlayer; + + if (g_iSkillLevel == SKILL_HARD) + { + UTIL_MakeVectors( player->pev->v_angle + player->pev->punchangle ); + return gpGlobals->v_forward; + } + + Vector vecSrc = GetGunPosition( ); + float flDist = 8192; + + // always use non-sticky autoaim + // UNDONE: use sever variable to chose! + if (1 || g_iSkillLevel == SKILL_MEDIUM) + { + player->m_vecAutoAim = Vector( 0, 0, 0 ); + // flDelta *= 0.5; + } + + BOOL m_fOldTargeting = player->m_fOnTarget; + Vector angles = player->AutoaimDeflection(vecSrc, flDist, delta ); + + // update ontarget if changed + if ( !g_pGameRules->AllowAutoTargetCrosshair() ) + player->m_fOnTarget = 0; + else if (m_fOldTargeting != player->m_fOnTarget) + { + player->m_pActiveItem->UpdateItemInfo( ); + } + + if (angles.x > 180) + angles.x -= 360; + if (angles.x < -180) + angles.x += 360; + if (angles.y > 180) + angles.y -= 360; + if (angles.y < -180) + angles.y += 360; + + if (angles.x > 25) + angles.x = 25; + if (angles.x < -25) + angles.x = -25; + if (angles.y > 12) + angles.y = 12; + if (angles.y < -12) + angles.y = -12; + + + // always use non-sticky autoaim + // UNDONE: use sever variable to chose! + if (0 || g_iSkillLevel == SKILL_EASY) + { + player->m_vecAutoAim = player->m_vecAutoAim * 0.67 + angles * 0.33; + } + else + { + player->m_vecAutoAim = angles * 0.9; + } + + // m_vecAutoAim = m_vecAutoAim * 0.99; + + // Don't send across network if sv_aim is 0 + if ( g_psv_aim->value != 0 ) + { + if ( player->m_vecAutoAim.x != player->m_lastx || + player->m_vecAutoAim.y != player->m_lasty ) + { + SET_CROSSHAIRANGLE( player->edict(), -player->m_vecAutoAim.x, player->m_vecAutoAim.y ); + + player->m_lastx = player->m_vecAutoAim.x; + player->m_lasty = player->m_vecAutoAim.y; + } + } + + // ALERT( at_console, "%f %f\n", angles.x, angles.y ); + + UTIL_MakeVectors( player->pev->v_angle + player->pev->punchangle + player->m_vecAutoAim ); + return gpGlobals->v_forward; +} + +Vector CServerWeaponLayerImpl::FireBullets(int bullets, Vector origin, matrix3x3 orientation, float distance, float spread, int bulletType, uint32_t seed, int damage) +{ + float x, y, z; + TraceResult tr; + CBasePlayer *player = m_pWeapon->m_pPlayer; + + ClearMultiDamage(); + gMultiDamage.type = DMG_BULLET | DMG_NEVERGIB; + + for (uint32_t i = 1; i <= bullets; i++) + { + // use player's random seed, get circular gaussian spread + x = m_randomGenerator.GetFloat(seed + i, -0.5f, 0.5f) + m_randomGenerator.GetFloat(seed + 1 + i, -0.5f, 0.5f); + y = m_randomGenerator.GetFloat(seed + 2 + i, -0.5f, 0.5f) + m_randomGenerator.GetFloat(seed + 3 + i, -0.5f, 0.5f); + z = x * x + y * y; + + Vector vecDir = orientation.GetForward() + + x * spread * orientation.GetRight() + + y * spread * orientation.GetUp(); + Vector vecEnd = origin + vecDir * distance; + + SetBits(gpGlobals->trace_flags, FTRACE_MATERIAL_TRACE); + UTIL_TraceLine(origin, vecEnd, dont_ignore_monsters, ENT(player->pev), &tr); + ClearBits(gpGlobals->trace_flags, FTRACE_MATERIAL_TRACE); + + // do damage, paint decals + if (tr.flFraction != 1.0) + { + CBaseEntity *pEntity = CBaseEntity::Instance(tr.pHit); + + if ( damage ) + { + pEntity->TraceAttack(player->pev, damage, vecDir, &tr, DMG_BULLET | ((damage > 16) ? DMG_ALWAYSGIB : DMG_NEVERGIB) ); + + TEXTURETYPE_PlaySound(&tr, origin, vecEnd, bulletType); + DecalGunshot( &tr, bulletType, origin, vecEnd ); + } + else switch(bulletType) + { + default: + case BULLET_PLAYER_9MM: + pEntity->TraceAttack(player->pev, gSkillData.plrDmg9MM, vecDir, &tr, DMG_BULLET); + break; + + case BULLET_PLAYER_MP5: + pEntity->TraceAttack(player->pev, gSkillData.plrDmgMP5, vecDir, &tr, DMG_BULLET); + break; + + case BULLET_PLAYER_BUCKSHOT: + // make distance based! + pEntity->TraceAttack(player->pev, gSkillData.plrDmgBuckshot, vecDir, &tr, DMG_BULLET); + break; + + case BULLET_PLAYER_357: + pEntity->TraceAttack(player->pev, gSkillData.plrDmg357, vecDir, &tr, DMG_BULLET); + break; + + case BULLET_NONE: // FIX + pEntity->TraceAttack(player->pev, 50, vecDir, &tr, DMG_CLUB); + TEXTURETYPE_PlaySound(&tr, origin, vecEnd, bulletType); + if ( !FNullEnt(tr.pHit) && VARS(tr.pHit)->rendermode != 0) // only decal glass + { + UTIL_DecalTrace( &tr, "{break1" ); + } + + break; + } + } + // make bullet trails + UTIL_BubbleTrail( origin, tr.vecEndPos, (distance * tr.flFraction) / 64.0 ); + } + + ApplyMultiDamage(player->pev, player->pev); + return Vector( x * spread, y * spread, 0.0 ); +} + +int CServerWeaponLayerImpl::GetPlayerAmmo(int ammoType) +{ + return m_pWeapon->m_pPlayer->m_rgAmmo[ammoType]; +} + +void CServerWeaponLayerImpl::SetPlayerAmmo(int ammoType, int count) +{ + m_pWeapon->m_pPlayer->m_rgAmmo[ammoType] = count; +} + +void CServerWeaponLayerImpl::SetPlayerWeaponAnim(int anim) +{ + m_pWeapon->m_pPlayer->pev->weaponanim = anim; +} + +void CServerWeaponLayerImpl::SetPlayerViewmodel(int model) +{ + m_pWeapon->m_pPlayer->pev->viewmodel = model; +} + +bool CServerWeaponLayerImpl::CheckPlayerButtonFlag(int buttonMask) +{ + return FBitSet(m_pWeapon->m_pPlayer->pev->button, buttonMask); +} + +void CServerWeaponLayerImpl::ClearPlayerButtonFlag(int buttonMask) +{ + ClearBits(m_pWeapon->m_pPlayer->pev->button, buttonMask); +} + +float CServerWeaponLayerImpl::GetPlayerNextAttackTime() +{ + return m_pWeapon->m_pPlayer->m_flNextAttack; +} + +void CServerWeaponLayerImpl::SetPlayerNextAttackTime(float value) +{ + m_pWeapon->m_pPlayer->m_flNextAttack = value; +} + +float CServerWeaponLayerImpl::GetWeaponTimeBase() +{ + return gpGlobals->time; + //return 0.0f; // zero because we're using predicting +} + +uint32_t CServerWeaponLayerImpl::GetRandomSeed() +{ + return m_pWeapon->m_pPlayer->random_seed; +} + +uint32_t CServerWeaponLayerImpl::GetRandomInt(uint32_t seed, int32_t min, int32_t max) +{ + return m_randomGenerator.GetInteger(seed, min, max); +} + +float CServerWeaponLayerImpl::GetRandomFloat(uint32_t seed, float min, float max) +{ + return m_randomGenerator.GetFloat(seed, min, max); +} + +uint16_t CServerWeaponLayerImpl::PrecacheEvent(const char *eventName) +{ + return g_engfuncs.pfnPrecacheEvent(1, eventName); +} + +void CServerWeaponLayerImpl::PlaybackWeaponEvent(const WeaponEventParams ¶ms) +{ + g_engfuncs.pfnPlaybackEvent(static_cast(params.flags), m_pWeapon->m_pPlayer->edict(), + params.eventindex, params.delay, + params.origin, params.angles, + params.fparam1, params.fparam2, + params.iparam1, params.iparam2, + params.bparam1, params.bparam2); +} diff --git a/server/server_weapon_layer_impl.h b/server/server_weapon_layer_impl.h new file mode 100644 index 000000000..96caa15ef --- /dev/null +++ b/server/server_weapon_layer_impl.h @@ -0,0 +1,42 @@ +#pragma once +#include "weapon_layer.h" +#include "extdll.h" +#include "util.h" +#include "cbase.h" +#include "player.h" +#include "monsters.h" +#include "weapons.h" +#include "seeded_random_generator.h" + +class CServerWeaponLayerImpl : public IWeaponLayer +{ +public: + CServerWeaponLayerImpl(CBasePlayerWeapon *weaponEntity); + + int GetWeaponBodygroup() override; + Vector GetGunPosition() override; + matrix3x3 GetCameraOrientation() override; + Vector GetAutoaimVector(float delta) override; + Vector FireBullets(int bullets, Vector origin, matrix3x3 orientation, float distance, float spread, int bulletType, uint32_t seed, int damage = 0) override; + CBasePlayerWeapon* GetWeaponEntity() override { return m_pWeapon; }; + + int GetPlayerAmmo(int ammoType) override; + void SetPlayerAmmo(int ammoType, int count) override; + void SetPlayerWeaponAnim(int anim) override; + void SetPlayerViewmodel(int model) override; + bool CheckPlayerButtonFlag(int buttonMask) override; + void ClearPlayerButtonFlag(int buttonMask) override; + float GetPlayerNextAttackTime() override; + void SetPlayerNextAttackTime(float value) override; + + float GetWeaponTimeBase() override; + uint32_t GetRandomSeed() override; + uint32_t GetRandomInt(uint32_t seed, int32_t min, int32_t max) override; + float GetRandomFloat(uint32_t seed, float min, float max) override; + uint16_t PrecacheEvent(const char *eventName) override; + void PlaybackWeaponEvent(const WeaponEventParams ¶ms) override; + +private: + CBasePlayerWeapon *m_pWeapon; + CSeededRandomGenerator m_randomGenerator; +}; diff --git a/server/stats.cpp b/server/stats.cpp index 5650336d3..2cc0ef87f 100644 --- a/server/stats.cpp +++ b/server/stats.cpp @@ -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_pWeaponContext->m_iClip; p = p->m_pNext; } @@ -118,7 +118,7 @@ void UpdateStats( CBasePlayer *pPlayer ) float ammo = 0; for (i = 1; i < MAX_AMMO_SLOTS; i++) { - ammo += ammoCount[i] * AmmoDamage( CBasePlayerItem::AmmoInfoArray[i].pszName ); + ammo += ammoCount[i] * AmmoDamage( CBaseWeaponContext::AmmoInfoArray[i].pszName ); } float health = pPlayer->pev->health + pPlayer->pev->armorvalue * 2; // Armor is 2X health diff --git a/server/weapons.cpp b/server/weapons.cpp index 54ec7477d..1b9b01a29 100644 --- a/server/weapons.cpp +++ b/server/weapons.cpp @@ -47,9 +47,6 @@ DLL_GLOBAL short g_sModelIndexBubbles;// holds the index for the bubbles model DLL_GLOBAL short g_sModelIndexBloodDrop;// holds the sprite index for the initial blood DLL_GLOBAL short g_sModelIndexBloodSpray;// holds the sprite index for splattered blood -ItemInfo CBasePlayerItem::ItemInfoArray[MAX_WEAPONS]; -AmmoInfo CBasePlayerItem::AmmoInfoArray[MAX_AMMO_SLOTS]; - MULTIDAMAGE gMultiDamage; #define TRACER_FREQ 4 // Tracers fire every fourth bullet @@ -64,10 +61,10 @@ int MaxAmmoCarry( int iszName ) { for ( int i = 0; i < MAX_WEAPONS; i++ ) { - if ( CBasePlayerItem::ItemInfoArray[i].pszAmmo1 && !strcmp( STRING(iszName), CBasePlayerItem::ItemInfoArray[i].pszAmmo1 ) ) - return CBasePlayerItem::ItemInfoArray[i].iMaxAmmo1; - if ( CBasePlayerItem::ItemInfoArray[i].pszAmmo2 && !strcmp( STRING(iszName), CBasePlayerItem::ItemInfoArray[i].pszAmmo2 ) ) - return CBasePlayerItem::ItemInfoArray[i].iMaxAmmo2; + if ( CBaseWeaponContext::ItemInfoArray[i].pszAmmo1 && !strcmp( STRING(iszName), CBaseWeaponContext::ItemInfoArray[i].pszAmmo1 ) ) + return CBaseWeaponContext::ItemInfoArray[i].iMaxAmmo1; + if ( CBaseWeaponContext::ItemInfoArray[i].pszAmmo2 && !strcmp( STRING(iszName), CBaseWeaponContext::ItemInfoArray[i].pszAmmo2 ) ) + return CBaseWeaponContext::ItemInfoArray[i].iMaxAmmo2; } ALERT( at_console, "MaxAmmoCarry() doesn't recognize '%s'!\n", STRING( iszName ) ); @@ -241,10 +238,10 @@ void AddAmmoNameToAmmoRegistry( const char *szAmmoname ) // make sure it's not already in the registry for ( int i = 0; i < MAX_AMMO_SLOTS; i++ ) { - if ( !CBasePlayerItem::AmmoInfoArray[i].pszName) + if ( !CBaseWeaponContext::AmmoInfoArray[i].pszName) continue; - if ( stricmp( CBasePlayerItem::AmmoInfoArray[i].pszName, szAmmoname ) == 0 ) + if ( stricmp( CBaseWeaponContext::AmmoInfoArray[i].pszName, szAmmoname ) == 0 ) return; // ammo already in registry, just quite } @@ -254,8 +251,8 @@ void AddAmmoNameToAmmoRegistry( const char *szAmmoname ) if ( giAmmoIndex >= MAX_AMMO_SLOTS ) giAmmoIndex = 0; - CBasePlayerItem::AmmoInfoArray[giAmmoIndex].pszName = szAmmoname; - CBasePlayerItem::AmmoInfoArray[giAmmoIndex].iId = giAmmoIndex; // yes, this info is redundant + CBaseWeaponContext::AmmoInfoArray[giAmmoIndex].pszName = szAmmoname; + CBaseWeaponContext::AmmoInfoArray[giAmmoIndex].iId = giAmmoIndex; // yes, this info is redundant } @@ -276,7 +273,7 @@ void UTIL_PrecacheOtherWeapon( const char *szClassname ) if ( ((CBasePlayerItem*)pEntity)->GetItemInfo( &II ) ) { - CBasePlayerItem::ItemInfoArray[II.iId] = II; + CBaseWeaponContext::ItemInfoArray[II.iId] = II; if ( II.pszAmmo1 && *II.pszAmmo1 ) { @@ -297,8 +294,8 @@ void UTIL_PrecacheOtherWeapon( const char *szClassname ) // called by worldspawn void W_Precache(void) { - memset( CBasePlayerItem::ItemInfoArray, 0, sizeof(CBasePlayerItem::ItemInfoArray) ); - memset( CBasePlayerItem::AmmoInfoArray, 0, sizeof(CBasePlayerItem::AmmoInfoArray) ); + memset( CBaseWeaponContext::ItemInfoArray, 0, sizeof(CBaseWeaponContext::ItemInfoArray) ); + memset( CBaseWeaponContext::AmmoInfoArray, 0, sizeof(CBaseWeaponContext::AmmoInfoArray) ); giAmmoIndex = 0; // custom items... @@ -398,7 +395,6 @@ void W_Precache(void) BEGIN_DATADESC( CBasePlayerItem ) DEFINE_FIELD( m_pPlayer, FIELD_CLASSPTR ), DEFINE_FIELD( m_pNext, FIELD_CLASSPTR ), - DEFINE_FIELD( m_iId, FIELD_INTEGER ), DEFINE_FUNCTION( DestroyItem ), DEFINE_FUNCTION( DefaultTouch ), DEFINE_FUNCTION( FallThink ), @@ -407,13 +403,14 @@ BEGIN_DATADESC( CBasePlayerItem ) END_DATADESC() BEGIN_DATADESC( CBasePlayerWeapon ) - DEFINE_FIELD( m_flNextPrimaryAttack, FIELD_TIME ), - DEFINE_FIELD( m_flNextSecondaryAttack, FIELD_TIME ), - DEFINE_FIELD( m_flTimeWeaponIdle, FIELD_TIME ), - DEFINE_FIELD( m_iPrimaryAmmoType, FIELD_INTEGER ), - DEFINE_FIELD( m_iSecondaryAmmoType, FIELD_INTEGER ), - DEFINE_FIELD( m_iClip, FIELD_INTEGER ), - DEFINE_FIELD( m_iDefaultAmmo, FIELD_INTEGER ), + //DEFINE_FIELD( m_flNextPrimaryAttack, FIELD_TIME ), + //DEFINE_FIELD( m_flNextSecondaryAttack, FIELD_TIME ), + //DEFINE_FIELD( m_flTimeWeaponIdle, FIELD_TIME ), + //DEFINE_FIELD( m_iPrimaryAmmoType, FIELD_INTEGER ), + //DEFINE_FIELD( m_iSecondaryAmmoType, FIELD_INTEGER ), + //DEFINE_FIELD( m_iClip, FIELD_INTEGER ), + //DEFINE_FIELD( m_iDefaultAmmo, FIELD_INTEGER ), + //DEFINE_FIELD( m_iId, FIELD_INTEGER ), END_DATADESC() void CBasePlayerItem :: SetObjectCollisionBox( void ) @@ -584,84 +581,7 @@ void CBasePlayerItem::DefaultTouch( CBaseEntity *pOther ) SUB_UseTargets( pOther, USE_TOGGLE, 0 ); // UNDONE: when should this happen? } -BOOL CanAttack( float attack_time, float curtime, BOOL isPredicted ) -{ - return ( attack_time <= curtime ) ? TRUE : FALSE; -} - -void CBasePlayerWeapon::ItemPostFrame( void ) -{ - 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) && g_pGameRules->GetNextBestWeapon( m_pPlayer, this ) ) - { - 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 CBasePlayerItem::DestroyItem( void ) { @@ -720,7 +640,7 @@ void CBasePlayerItem::AttachToPlayer ( CBasePlayer *pPlayer ) // CALLED THROUGH the newly-touched weapon's instance. The existing player weapon is pOriginal int CBasePlayerWeapon::AddDuplicate( CBasePlayerItem *pOriginal ) { - if ( m_iDefaultAmmo ) + if ( m_pWeaponContext->m_iDefaultAmmo ) { return ExtractAmmo( (CBasePlayerWeapon *)pOriginal ); } @@ -731,22 +651,33 @@ int CBasePlayerWeapon::AddDuplicate( CBasePlayerItem *pOriginal ) } } +CBasePlayerWeapon::CBasePlayerWeapon() : + m_pWeaponContext(nullptr) +{ +} + +CBasePlayerWeapon::~CBasePlayerWeapon() +{ + if (m_pWeaponContext) { + delete m_pWeaponContext; + } +} int CBasePlayerWeapon::AddToPlayer( CBasePlayer *pPlayer ) { int bResult = CBasePlayerItem::AddToPlayer( pPlayer ); - pPlayer->AddWeapon( m_iId ); + pPlayer->AddWeapon( m_pWeaponContext->m_iId ); - if ( !m_iPrimaryAmmoType ) + if ( !m_pWeaponContext->m_iPrimaryAmmoType ) { - m_iPrimaryAmmoType = pPlayer->GetAmmoIndex( pszAmmo1() ); - m_iSecondaryAmmoType = pPlayer->GetAmmoIndex( pszAmmo2() ); + m_pWeaponContext->m_iPrimaryAmmoType = pPlayer->GetAmmoIndex( pszAmmo1() ); + m_pWeaponContext->m_iSecondaryAmmoType = pPlayer->GetAmmoIndex( pszAmmo2() ); } - if (bResult) return AddWeapon( ); + return FALSE; } @@ -779,8 +710,8 @@ int CBasePlayerWeapon::UpdateClientData( CBasePlayer *pPlayer ) } // If the ammo, state, or fov has changed, update the weapon - if ( m_iClip != m_iClientClip || - state != m_iClientWeaponState || + if ( m_pWeaponContext->m_iClip != m_pWeaponContext->m_iClientClip || + state != m_pWeaponContext->m_iClientWeaponState || pPlayer->m_iFOV != pPlayer->m_iClientFOV ) { bSend = TRUE; @@ -790,12 +721,12 @@ int CBasePlayerWeapon::UpdateClientData( CBasePlayer *pPlayer ) { MESSAGE_BEGIN( MSG_ONE, gmsgCurWeapon, NULL, pPlayer->pev ); WRITE_BYTE( state ); - WRITE_BYTE( m_iId ); - WRITE_BYTE( m_iClip ); + WRITE_BYTE( m_pWeaponContext->m_iId ); + WRITE_BYTE( m_pWeaponContext->m_iClip ); MESSAGE_END(); - m_iClientClip = m_iClip; - m_iClientWeaponState = state; + m_pWeaponContext->m_iClientClip = m_pWeaponContext->m_iClip; + m_pWeaponContext->m_iClientWeaponState = state; pPlayer->m_fWeapon = TRUE; } @@ -805,20 +736,9 @@ int CBasePlayerWeapon::UpdateClientData( CBasePlayer *pPlayer ) return 1; } - -void CBasePlayerWeapon::SendWeaponAnim( int iAnim, int skiplocal, int body ) +void CBasePlayerWeapon::ItemPostFrame() { - 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( pev->body ); // weaponmodel bodygroup. - MESSAGE_END(); + m_pWeaponContext->ItemPostFrame(); } BOOL CBasePlayerWeapon :: AddPrimaryAmmo( int iCount, char *szName, int iMaxClip, int iMaxCarry ) @@ -827,14 +747,14 @@ BOOL CBasePlayerWeapon :: AddPrimaryAmmo( int iCount, char *szName, int iMaxClip if (iMaxClip < 1) { - m_iClip = -1; + m_pWeaponContext->m_iClip = -1; iIdAmmo = m_pPlayer->GiveAmmo( iCount, szName, iMaxCarry ); } - else if (m_iClip == 0) + else if (m_pWeaponContext->m_iClip == 0) { int i; - i = Q_min( m_iClip + iCount, iMaxClip ) - m_iClip; - m_iClip += i; + i = Q_min( m_pWeaponContext->m_iClip + iCount, iMaxClip ) - m_pWeaponContext->m_iClip; + m_pWeaponContext->m_iClip += i; iIdAmmo = m_pPlayer->GiveAmmo( iCount - i, szName, iMaxCarry ); } else @@ -846,7 +766,7 @@ BOOL CBasePlayerWeapon :: AddPrimaryAmmo( int iCount, char *szName, int iMaxClip if (iIdAmmo > 0) { - m_iPrimaryAmmoType = iIdAmmo; + m_pWeaponContext->m_iPrimaryAmmoType = iIdAmmo; if (m_pPlayer->HasPlayerItem( this ) ) { // play the "got ammo" sound only if we gave some ammo to a player that already had this gun. @@ -869,137 +789,12 @@ BOOL CBasePlayerWeapon :: AddSecondaryAmmo( int iCount, char *szName, int iMax ) if (iIdAmmo > 0) { - m_iSecondaryAmmoType = iIdAmmo; + m_pWeaponContext->m_iSecondaryAmmoType = iIdAmmo; EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/9mmclip1.wav", 1, ATTN_NORM); } return iIdAmmo > 0 ? TRUE : FALSE; } -//========================================================= -// 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 CBasePlayerWeapon :: 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 CBasePlayerWeapon :: 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 CBasePlayerWeapon :: 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 CBasePlayerWeapon :: 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; -} - -BOOL CBasePlayerWeapon :: 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 CBasePlayerWeapon :: ResetEmptySound( void ) -{ - m_iPlayEmptySound = 1; -} - -//========================================================= -//========================================================= -int CBasePlayerWeapon::PrimaryAmmoIndex( void ) -{ - return m_iPrimaryAmmoType; -} - -//========================================================= -//========================================================= -int CBasePlayerWeapon::SecondaryAmmoIndex( void ) -{ - return -1; -} - -void CBasePlayerWeapon::Holster( void ) -{ - m_fInReload = FALSE; // cancel any reload in progress. - m_pPlayer->pev->viewmodel = 0; - m_pPlayer->pev->weaponmodel = 0; -} - BEGIN_DATADESC( CBasePlayerAmmo ) DEFINE_FUNCTION( DefaultTouch ), DEFINE_FUNCTION( Materialize ), @@ -1083,8 +878,8 @@ int CBasePlayerWeapon::ExtractAmmo( CBasePlayerWeapon *pWeapon ) { // blindly call with m_iDefaultAmmo. It's either going to be a value or zero. If it is zero, // we only get the ammo in the weapon's clip, which is what we want. - iReturn = pWeapon->AddPrimaryAmmo( m_iDefaultAmmo, (char *)pszAmmo1(), iMaxClip(), iMaxAmmo1() ); - m_iDefaultAmmo = 0; + iReturn = pWeapon->AddPrimaryAmmo( m_pWeaponContext->m_iDefaultAmmo, (char *)pszAmmo1(), iMaxClip(), iMaxAmmo1() ); + m_pWeaponContext->m_iDefaultAmmo = 0; } if ( pszAmmo2() != NULL ) @@ -1102,13 +897,13 @@ int CBasePlayerWeapon::ExtractClipAmmo( CBasePlayerWeapon *pWeapon ) { int iAmmo; - if ( m_iClip == WEAPON_NOCLIP ) + if ( m_pWeaponContext->m_iClip == WEAPON_NOCLIP ) { iAmmo = 0;// guns with no clips always come empty if they are second-hand } else { - iAmmo = m_iClip; + iAmmo = m_pWeaponContext->m_iClip; } return pWeapon->m_pPlayer->GiveAmmo( iAmmo, (char *)pszAmmo1(), iMaxAmmo1() ); // , &m_iPrimaryAmmoType @@ -1465,17 +1260,17 @@ void CWeaponBox::SetObjectCollisionBox( void ) void CBasePlayerWeapon::PrintState( void ) { - ALERT( at_console, "primary: %f\n", m_flNextPrimaryAttack ); - ALERT( at_console, "idle : %f\n", m_flTimeWeaponIdle ); + ALERT( at_console, "primary: %f\n", m_pWeaponContext->m_flNextPrimaryAttack ); + ALERT( at_console, "idle : %f\n", m_pWeaponContext->m_flTimeWeaponIdle ); // ALERT( at_console, "nextrl : %f\n", m_flNextReload ); // ALERT( at_console, "nextpum: %f\n", m_flPumpTime ); // ALERT( at_console, "m_frt : %f\n", m_fReloadTime ); - ALERT( at_console, "m_finre: %i\n", m_fInReload ); + ALERT( at_console, "m_finre: %i\n", m_pWeaponContext->m_fInReload ); // ALERT( at_console, "m_finsr: %i\n", m_fInSpecialReload ); - ALERT( at_console, "m_iclip: %i\n", m_iClip ); + ALERT( at_console, "m_iclip: %i\n", m_pWeaponContext->m_iClip ); } //========================================================= diff --git a/server/weapons.h b/server/weapons.h index 70379ad89..0aceff606 100644 --- a/server/weapons.h +++ b/server/weapons.h @@ -15,7 +15,10 @@ #ifndef WEAPONS_H #define WEAPONS_H +#include "cbase.h" #include "effects.h" +#include "item_info.h" +#include "weapon_context.h" class CBasePlayer; @@ -29,7 +32,6 @@ void DeactivateSatchels( CBasePlayer *pOwner ); #define WEAPON_NONE 0 #define WEAPON_CROWBAR 1 -#define WEAPON_GLOCK 2 #define WEAPON_PYTHON 3 #define WEAPON_MP5 4 #define WEAPON_CYCLER 5 @@ -44,14 +46,10 @@ void DeactivateSatchels( CBasePlayer *pOwner ); #define WEAPON_SATCHEL 14 #define WEAPON_SNARK 15 -#define WEAPON_ALLWEAPONS (~(1<PrimaryAmmoIndex(); }; // forward to weapon logic + int SecondaryAmmoIndex() override { return m_pWeaponContext->SecondaryAmmoIndex(); }; // forward to weapon logic + + // forward all this to weapon logic + virtual int GetItemInfo(ItemInfo *p) override { return m_pWeaponContext->GetItemInfo(p); }; // returns 0 if struct not filled out + virtual BOOL CanDeploy( void ) override { return m_pWeaponContext->CanDeploy(); }; + virtual BOOL Deploy() override { return m_pWeaponContext->Deploy(); }; // returns is deploy was successful + virtual BOOL CanHolster( void ) override { return m_pWeaponContext->CanHolster(); }; // can this weapon be put away right nxow? + virtual void Holster(void) override { m_pWeaponContext->Holster(); }; + + void UpdateItemInfo( void ) override {}; // updates HUD state + CBasePlayerItem *GetWeaponPtr( void ) override { return (CBasePlayerItem *)this; }; + + // forward all of them to weapon logic + int iItemSlot() override { return m_pWeaponContext->iItemSlot(); } + int iItemPosition() override { return m_pWeaponContext->iItemPosition(); } + const char *pszAmmo1() override { return m_pWeaponContext->pszAmmo1(); } + int iMaxAmmo1() override { return m_pWeaponContext->iMaxAmmo1(); } + const char *pszAmmo2() override { return m_pWeaponContext->pszAmmo2(); } + int iMaxAmmo2() override { return m_pWeaponContext->iMaxAmmo2(); } + const char *pszName() override { return m_pWeaponContext->pszName(); } + int iMaxClip() override { return m_pWeaponContext->iMaxClip(); } + int iWeight() override { return m_pWeaponContext->iWeight(); } + int iFlags() override { return m_pWeaponContext->iFlags(); } + int iWeaponID() override { return m_pWeaponContext->m_iId; } + +protected: + int ExtractAmmo( CBasePlayerWeapon *pWeapon ); // Return TRUE if you can add ammo to yourself when picked up + int ExtractClipAmmo( CBasePlayerWeapon *pWeapon ); // Return TRUE if you can add ammo to yourself when picked up + + int AddWeapon( void ) { ExtractAmmo( this ); return TRUE; }; // Return TRUE if you want to add yourself to the player + void RetireWeapon( void ); // generic "shared" ammo handlers BOOL AddPrimaryAmmo( int iCount, char *szName, int iMaxClip, int iMaxCarry ); BOOL AddSecondaryAmmo( int iCount, char *szName, int iMaxCarry ); - - virtual void UpdateItemInfo( void ) {}; // updates HUD state - - int m_iPlayEmptySound; - int m_fFireOnEmpty; // True when the gun is empty and the player is still holding down the - // attack key(s) - virtual BOOL PlayEmptySound( void ); - virtual void ResetEmptySound( void ); - - virtual void SendWeaponAnim( int iAnim, int skiplocal = 1, int body = 0 ); // skiplocal is 1 if client is predicting weapon animations - - virtual BOOL CanDeploy( void ); - virtual BOOL IsUseable( 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 ); - - virtual void ItemPostFrame( void ); // called each frame by the player PostThink - // 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 int UpdateClientData( CBasePlayer *pPlayer ); // sends hud info to client dll, if things have changed - virtual void RetireWeapon( void ); - virtual BOOL ShouldWeaponIdle( void ) {return FALSE; }; - virtual void Holster( void ); - virtual BOOL UseDecrement( void ) { return FALSE; }; - - int PrimaryAmmoIndex(); - int SecondaryAmmoIndex(); - void PrintState( void ); - - virtual CBasePlayerItem *GetWeaponPtr( void ) { return (CBasePlayerItem *)this; }; - - 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. - }; class CBasePlayerAmmo : public CBaseEntity diff --git a/server/weapons/crossbow.cpp b/server/weapons/crossbow.cpp index 0f37a018a..b0b48e739 100644 --- a/server/weapons/crossbow.cpp +++ b/server/weapons/crossbow.cpp @@ -15,6 +15,7 @@ #include "crossbow.h" #include "crossbow_bolt.h" +#include "weapon_logic_funcs_impl.h" #define BOLT_AIR_VELOCITY 2000 #define BOLT_WATER_VELOCITY 1000 @@ -38,19 +39,17 @@ enum crossbow_e LINK_ENTITY_TO_CLASS( weapon_crossbow, CCrossbow ); BEGIN_DATADESC( CCrossbow ) - DEFINE_FIELD( m_fInZoom, FIELD_BOOLEAN ), - DEFINE_FIELD( m_fZoomInUse, FIELD_BOOLEAN ), + //DEFINE_FIELD( m_fInZoom, FIELD_BOOLEAN ), + //DEFINE_FIELD( m_fZoomInUse, FIELD_BOOLEAN ), END_DATADESC() void CCrossbow::Spawn( void ) { + pev->classname = MAKE_STRING( "weapon_crossbow" ); Precache( ); - m_iId = WEAPON_CROSSBOW; SET_MODEL(ENT(pev), "models/w_crossbow.mdl"); - - m_iDefaultAmmo = CROSSBOW_DEFAULT_GIVE; - FallInit();// get ready to fall down. + m_pWeaponLogic = new CCrossbowWeaponLogic(new CServerWeaponLayerImpl(this)); } int CCrossbow::AddToPlayer( CBasePlayer *pPlayer ) @@ -58,7 +57,7 @@ int CCrossbow::AddToPlayer( CBasePlayer *pPlayer ) if ( CBasePlayerWeapon::AddToPlayer( pPlayer ) ) { MESSAGE_BEGIN( MSG_ONE, gmsgWeapPickup, NULL, pPlayer->pev ); - WRITE_BYTE( m_iId ); + WRITE_BYTE( m_pWeaponLogic->m_iId ); MESSAGE_END(); return TRUE; } @@ -77,9 +76,16 @@ void CCrossbow::Precache( void ) UTIL_PrecacheOther( "crossbow_bolt" ); } -int CCrossbow::GetItemInfo(ItemInfo *p) +CCrossbowWeaponLogic::CCrossbowWeaponLogic(IWeaponLayer *funcs) : + CBaseWeaponContext(funcs) +{ + m_iId = WEAPON_CROSSBOW; + m_iDefaultAmmo = CROSSBOW_DEFAULT_GIVE; +} + +int CCrossbowWeaponLogic::GetItemInfo(ItemInfo *p) { - p->pszName = STRING(pev->classname); + p->pszName = m_pFuncs->GetWeaponClassname(); //STRING(pev->classname); p->pszAmmo1 = "bolts"; p->iMaxAmmo1 = BOLT_MAX_CARRY; p->pszAmmo2 = NULL; @@ -93,14 +99,14 @@ int CCrossbow::GetItemInfo(ItemInfo *p) return 1; } -BOOL CCrossbow::Deploy( ) +bool CCrossbowWeaponLogic::Deploy( ) { if (m_iClip) return DefaultDeploy( "models/v_crossbow.mdl", "models/p_crossbow.mdl", CROSSBOW_DRAW1, "bow" ); return DefaultDeploy( "models/v_crossbow.mdl", "models/p_crossbow.mdl", CROSSBOW_DRAW2, "bow" ); } -void CCrossbow::Holster( void ) +void CCrossbowWeaponLogic::Holster( void ) { m_fInReload = FALSE;// cancel any reload in progress. @@ -117,7 +123,7 @@ void CCrossbow::Holster( void ) SendWeaponAnim( CROSSBOW_HOLSTER2 ); } -void CCrossbow::PrimaryAttack( void ) +void CCrossbowWeaponLogic::PrimaryAttack( void ) { if ( m_fInZoom && g_pGameRules->IsMultiplayer() ) { @@ -129,7 +135,7 @@ void CCrossbow::PrimaryAttack( void ) } // this function only gets called in multiplayer -void CCrossbow::FireSniperBolt() +void CCrossbowWeaponLogic::FireSniperBolt() { m_flNextPrimaryAttack = gpGlobals->time + 0.75; @@ -210,7 +216,7 @@ void CCrossbow::FireSniperBolt() } } -void CCrossbow::FireBolt( void ) +void CCrossbowWeaponLogic::FireBolt( void ) { TraceResult tr; @@ -292,7 +298,7 @@ void CCrossbow::FireBolt( void ) m_pPlayer->pev->punchangle.x -= 2; } -void CCrossbow::SecondaryAttack( void ) +void CCrossbowWeaponLogic::SecondaryAttack( void ) { // do not switch zoom when player stay button is pressed if (m_fZoomInUse) @@ -315,7 +321,7 @@ void CCrossbow::SecondaryAttack( void ) m_flTimeWeaponIdle = gpGlobals->time + 5.0; } -void CCrossbow::Reload( void ) +void CCrossbowWeaponLogic::Reload( void ) { if ( m_fInZoom ) { @@ -329,7 +335,7 @@ void CCrossbow::Reload( void ) } } -void CCrossbow::WeaponIdle( void ) +void CCrossbowWeaponLogic::WeaponIdle( void ) { m_pPlayer->GetAutoaimVector( AUTOAIM_2DEGREES ); // get the autoaim vector but ignore it; used for autoaim crosshair in DM diff --git a/server/weapons/crossbow.h b/server/weapons/crossbow.h index bb9dc2a3c..0923c3077 100644 --- a/server/weapons/crossbow.h +++ b/server/weapons/crossbow.h @@ -23,13 +23,14 @@ #include "player.h" #include "gamerules.h" #include "user_messages.h" +#include "weapon_layer.h" -class CCrossbow : public CBasePlayerWeapon +class CCrossbowWeaponLogic : public CBaseWeaponContext { - DECLARE_CLASS( CCrossbow, CBasePlayerWeapon ); public: - void Spawn( void ); - void Precache( void ); + CCrossbowWeaponLogic() = delete; + CCrossbowWeaponLogic(IWeaponLayer *funcs); + int iItemSlot( ) { return 3; } int GetItemInfo(ItemInfo *p); @@ -37,11 +38,7 @@ class CCrossbow : public CBasePlayerWeapon void FireSniperBolt( void ); void PrimaryAttack( void ); void SecondaryAttack( void ); - int AddToPlayer( CBasePlayer *pPlayer ); - - DECLARE_DATADESC(); - - BOOL Deploy( void ); + bool Deploy( void ); void Holster( void ); void Reload( void ); void WeaponIdle( void ); @@ -49,3 +46,14 @@ class CCrossbow : public CBasePlayerWeapon int m_fInZoom; // don't save this int m_fZoomInUse; }; + +class CCrossbow : public CBasePlayerWeapon +{ + DECLARE_CLASS( CCrossbow, CBasePlayerWeapon ); +public: + void Spawn( void ); + void Precache( void ); + int AddToPlayer( CBasePlayer *pPlayer ); + + DECLARE_DATADESC(); +}; diff --git a/server/weapons/glock.cpp b/server/weapons/glock.cpp deleted file mode 100644 index bb20271fc..000000000 --- a/server/weapons/glock.cpp +++ /dev/null @@ -1,224 +0,0 @@ -/*** -* -* Copyright (c) 1996-2002, Valve LLC. All rights reserved. -* -* This product contains software technology licensed from Id -* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. -* All Rights Reserved. -* -* Use, distribution, and modification of this source code and/or resulting -* object code is restricted to non-commercial enhancements to products from -* Valve LLC. All other use, distribution, or modification is prohibited -* without written permission from Valve LLC. -* -****/ - -#include "glock.h" - -enum glock_e -{ - GLOCK_IDLE1 = 0, - GLOCK_IDLE2, - GLOCK_IDLE3, - GLOCK_SHOOT, - GLOCK_SHOOT_EMPTY, - GLOCK_RELOAD, - GLOCK_RELOAD_NOT_EMPTY, - GLOCK_DRAW, - GLOCK_HOLSTER, - GLOCK_ADD_SILENCER -}; - -LINK_ENTITY_TO_CLASS( weapon_glock, CGlock ); -LINK_ENTITY_TO_CLASS( weapon_9mmhandgun, CGlock ); - -void CGlock::Spawn( ) -{ - pev->classname = MAKE_STRING( "weapon_9mmhandgun" ); // hack to allow for old names - Precache( ); - m_iId = WEAPON_GLOCK; - SET_MODEL( edict(), "models/w_9mmhandgun.mdl" ); - - m_iDefaultAmmo = GLOCK_DEFAULT_GIVE; - - FallInit();// get ready to fall down. -} - -void CGlock::Precache( void ) -{ - PRECACHE_MODEL("models/v_9mmhandgun.mdl"); - PRECACHE_MODEL("models/w_9mmhandgun.mdl"); - PRECACHE_MODEL("models/p_9mmhandgun.mdl"); - - m_iShell = PRECACHE_MODEL ("models/shell.mdl");// brass shell - - PRECACHE_SOUND("items/9mmclip1.wav"); - PRECACHE_SOUND("items/9mmclip2.wav"); - - PRECACHE_SOUND ("weapons/pl_gun1.wav");//silenced handgun - PRECACHE_SOUND ("weapons/pl_gun2.wav");//silenced handgun - PRECACHE_SOUND ("weapons/pl_gun3.wav");//handgun -} - -int CGlock::GetItemInfo(ItemInfo *p) -{ - p->pszName = STRING(pev->classname); - p->pszAmmo1 = "9mm"; - p->iMaxAmmo1 = _9MM_MAX_CARRY; - p->pszAmmo2 = NULL; - p->iMaxAmmo2 = -1; - p->iMaxClip = GLOCK_MAX_CLIP; - p->iSlot = 1; - p->iPosition = 0; - p->iFlags = 0; - p->iId = m_iId = WEAPON_GLOCK; - p->iWeight = GLOCK_WEIGHT; - - return 1; -} - -BOOL CGlock::Deploy( ) -{ - // pev->body = 1; - return DefaultDeploy( "models/v_9mmhandgun.mdl", "models/p_9mmhandgun.mdl", GLOCK_DRAW, "onehanded" ); -} - -void CGlock::SecondaryAttack( void ) -{ - GlockFire( 0.1, 0.2, FALSE ); -} - -void CGlock::PrimaryAttack( void ) -{ - GlockFire( 0.01, 0.3, TRUE ); -} - -void CGlock::GlockFire( float flSpread , float flCycleTime, BOOL fUseAutoAim ) -{ - if (m_iClip <= 0) - { - if (m_fFireOnEmpty) - { - PlayEmptySound(); - m_flNextPrimaryAttack = gpGlobals->time + 0.2; - } - - return; - } - - m_iClip--; - - m_pPlayer->pev->effects = (int)(m_pPlayer->pev->effects) | EF_MUZZLEFLASH; - - if (m_iClip != 0) - SendWeaponAnim( GLOCK_SHOOT ); - else - SendWeaponAnim( GLOCK_SHOOT_EMPTY ); - - // player "shoot" animation - m_pPlayer->SetAnimation( PLAYER_ATTACK1 ); - - UTIL_MakeVectors( m_pPlayer->pev->v_angle + m_pPlayer->pev->punchangle ); - - Vector vecShellVelocity = m_pPlayer->GetAbsVelocity() - + gpGlobals->v_right * RANDOM_FLOAT(50,70) - + gpGlobals->v_up * RANDOM_FLOAT(100,150) - + gpGlobals->v_forward * 25; - EjectBrass ( m_pPlayer->EyePosition() + gpGlobals->v_up * -12 + gpGlobals->v_forward * 32 + gpGlobals->v_right * 6, - vecShellVelocity, m_pPlayer->GetAbsAngles().y, m_iShell, TE_BOUNCE_SHELL ); - - // silenced - if (pev->body == 1) - { - m_pPlayer->m_iWeaponVolume = QUIET_GUN_VOLUME; - m_pPlayer->m_iWeaponFlash = DIM_GUN_FLASH; - - switch(RANDOM_LONG(0,1)) - { - case 0: - EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/pl_gun1.wav", RANDOM_FLOAT(0.9, 1.0), ATTN_NORM); - break; - case 1: - EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/pl_gun2.wav", RANDOM_FLOAT(0.9, 1.0), ATTN_NORM); - break; - } - } - else - { - // non-silenced - m_pPlayer->m_iWeaponVolume = NORMAL_GUN_VOLUME; - m_pPlayer->m_iWeaponFlash = NORMAL_GUN_FLASH; - EMIT_SOUND_DYN(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/pl_gun3.wav", RANDOM_FLOAT(0.92, 1.0), ATTN_NORM, 0, 98 + RANDOM_LONG(0,3)); - } - - Vector vecSrc = m_pPlayer->GetGunPosition( ); - Vector vecAiming; - - if ( fUseAutoAim ) - { - vecAiming = m_pPlayer->GetAutoaimVector( AUTOAIM_10DEGREES ); - } - else - { - vecAiming = gpGlobals->v_forward; - } - - m_pPlayer->FireBullets( 1, vecSrc, vecAiming, Vector( flSpread, flSpread, flSpread ), 8192, BULLET_PLAYER_9MM, 0 ); - m_flNextPrimaryAttack = m_flNextSecondaryAttack = gpGlobals->time + flCycleTime; - - if (!m_iClip && m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0) - // HEV suit - indicate out of ammo condition - m_pPlayer->SetSuitUpdate("!HEV_AMO0", FALSE, 0); - - m_flTimeWeaponIdle = gpGlobals->time + RANDOM_FLOAT ( 10, 15 ); - - m_pPlayer->pev->punchangle.x -= 2; -} - -void CGlock::Reload( void ) -{ - int iResult; - - if (m_iClip == 0) - iResult = DefaultReload( 17, GLOCK_RELOAD, 1.5 ); - else - iResult = DefaultReload( 17, GLOCK_RELOAD_NOT_EMPTY, 1.5 ); - - if (iResult) - { - m_flTimeWeaponIdle = gpGlobals->time + RANDOM_FLOAT ( 10, 15 ); - } -} - -void CGlock::WeaponIdle( void ) -{ - ResetEmptySound( ); - - m_pPlayer->GetAutoaimVector( AUTOAIM_10DEGREES ); - - if (m_flTimeWeaponIdle > gpGlobals->time) - return; - - // only idle if the slid isn't back - if (m_iClip != 0) - { - int iAnim; - float flRand = RANDOM_FLOAT(0, 1); - if (flRand <= 0.3 + 0 * 0.75) - { - iAnim = GLOCK_IDLE3; - m_flTimeWeaponIdle = gpGlobals->time + 49.0 / 16; - } - else if (flRand <= 0.6 + 0 * 0.875) - { - iAnim = GLOCK_IDLE1; - m_flTimeWeaponIdle = gpGlobals->time + 60.0 / 16.0; - } - else - { - iAnim = GLOCK_IDLE2; - m_flTimeWeaponIdle = gpGlobals->time + 40.0 / 16.0; - } - SendWeaponAnim( iAnim ); - } -} diff --git a/server/weapons/weapon_glock.cpp b/server/weapons/weapon_glock.cpp new file mode 100644 index 000000000..d17a8e3fb --- /dev/null +++ b/server/weapons/weapon_glock.cpp @@ -0,0 +1,50 @@ +/*** +* +* Copyright (c) 1996-2002, Valve LLC. All rights reserved. +* +* This product contains software technology licensed from Id +* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. +* All Rights Reserved. +* +* Use, distribution, and modification of this source code and/or resulting +* object code is restricted to non-commercial enhancements to products from +* Valve LLC. All other use, distribution, or modification is prohibited +* without written permission from Valve LLC. +* +****/ + +#include "weapon_glock.h" +#include "weapon_layer.h" +#include "weapons/glock.h" +#include "server_weapon_layer_impl.h" + +LINK_ENTITY_TO_CLASS( GLOCK_CLASSNAME, CGlock ); +LINK_ENTITY_TO_CLASS( weapon_9mmhandgun, CGlock ); + +CGlock::CGlock() +{ + m_pWeaponContext = new CGlockWeaponLogic(new CServerWeaponLayerImpl(this)); +} + +void CGlock::Spawn( ) +{ + pev->classname = MAKE_STRING(CLASSNAME_STR(GLOCK_CLASSNAME)); // hack to allow for old names + Precache( ); + SET_MODEL( edict(), "models/w_9mmhandgun.mdl" ); + FallInit();// get ready to fall down. +} + +void CGlock::Precache( void ) +{ + PRECACHE_MODEL("models/v_9mmhandgun.mdl"); + PRECACHE_MODEL("models/w_9mmhandgun.mdl"); + PRECACHE_MODEL("models/p_9mmhandgun.mdl"); + PRECACHE_MODEL("models/shell.mdl"); // brass shell + + PRECACHE_SOUND("items/9mmclip1.wav"); + PRECACHE_SOUND("items/9mmclip2.wav"); + + PRECACHE_SOUND("weapons/pl_gun1.wav"); //silenced handgun + PRECACHE_SOUND("weapons/pl_gun2.wav"); //silenced handgun + PRECACHE_SOUND("weapons/pl_gun3.wav"); //handgun +} diff --git a/server/weapons/glock.h b/server/weapons/weapon_glock.h similarity index 73% rename from server/weapons/glock.h rename to server/weapons/weapon_glock.h index 4448beac3..8c1dc78f2 100644 --- a/server/weapons/glock.h +++ b/server/weapons/weapon_glock.h @@ -25,18 +25,10 @@ class CGlock : public CBasePlayerWeapon { DECLARE_CLASS( CGlock, CBasePlayerWeapon ); + public: + CGlock(); + void Spawn( void ); void Precache( void ); - int iItemSlot( void ) { return 2; } - int GetItemInfo(ItemInfo *p); - - void PrimaryAttack( void ); - void SecondaryAttack( void ); - void GlockFire( float flSpread, float flCycleTime, BOOL fUseAutoAim ); - BOOL Deploy( void ); - void Reload( void ); - void WeaponIdle( void ); -private: - int m_iShell; };