Винтовка Генри
Небольшое руководство по созданию многозарядной винтовки.
Решил внести свой вклад в развитие этого сайта.
Код снайперской винтовки уже есть, но она с отъемным магазином. Поэтому я решил сделать руководство по созданию магазинной винтовки с заряжанием по одному патрону. Винтовка не снайперская, но переделать ее в снайперскую не составит труда. Всем фанатам Дикого Запада посвящается! Хотя при желании, если немного подправить код, она подойдет и для Второй Мировой.
Итак, я решил показать вам, как сделать магазинную винтовку с заряжанием по одному патрону. За образец возьму винтовку Генри образца 1860 года. Почему ее? Она мне просто нравится. Хотя, при желании этот код, как уже говорилось выше, подойдет для любой неавтоматической винтовки с заряжанием по одному патрону – надо просто изменить количество патронов. Поскольку винтовки со скобой Генри часто выпускались под револьверный патрон, а также, чтобы избавить вас от лишнего геморроя, мы не будем создавать новый тип боеприпасов, а сделаем ее на основе патронов 357 Magnum.
Итак, начали.
Для начала создаем на сервере файл henry.cpp. Копируем туда следующий код:
C / C++:
/*** * * 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. * ****/ #if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD )
#include "extdll.h" #include "util.h" #include "cbase.h" #include "weapons.h" #include "monsters.h" #include "player.h" #include "gamerules.h"
enum henry_e { HENRY_IDLE1 = 0, HENRY_FIRE1, HENRY_RELOAD, HENRY_PUMP, HENRY_START_RELOAD, HENRY_DRAW, HENRY_IDLE2, HENRY_IDLE3 };
LINK_ENTITY_TO_CLASS( weapon_henry, CHenry );
int CHenry::GetItemInfo(ItemInfo *p) { p->pszName = STRING(pev->classname); p->pszAmmo1 = "357"; p->iMaxAmmo1 = HENRY_MAX_CARRY; p->pszAmmo2 = NULL; p->iMaxAmmo2 = -1; p->iMaxClip = HENRY_MAX_CLIP; p->iFlags = 0; p->iSlot = 2; p->iPosition = 3; p->iId = m_iId = WEAPON_HENRY; p->iWeight = PYTHON_WEIGHT;
return 1; }
int CHenry::AddToPlayer( CBasePlayer *pPlayer ) { if ( CBasePlayerWeapon::AddToPlayer( pPlayer ) ) { MESSAGE_BEGIN( MSG_ONE, gmsgWeapPickup, NULL, pPlayer->pev ); WRITE_BYTE( m_iId ); MESSAGE_END(); return TRUE; } return FALSE; }
void CHenry::Spawn( ) { pev->classname = MAKE_STRING("weapon_henry"); // hack to allow for old names Precache( ); m_iId = WEAPON_HENRY; SET_MODEL(ENT(pev), "models/w_henry.mdl");
m_iDefaultAmmo = HENRY_DEFAULT_GIVE;
FallInit();// get ready to fall down. }
void CHenry::Precache( void ) { PRECACHE_MODEL("models/v_henry.mdl"); PRECACHE_MODEL("models/w_henry.mdl"); PRECACHE_MODEL("models/p_henry.mdl");
PRECACHE_MODEL("models/w_357ammobox.mdl"); PRECACHE_SOUND("items/9mmclip1.wav");
PRECACHE_SOUND ("weapons/357_cock1.wav"); PRECACHE_SOUND ("weapons/henry_shot1.WAV"); PRECACHE_SOUND ("weapons/henry_shot2.WAV");
m_usFireHenry = PRECACHE_EVENT( 1, "events/henry.sc" ); }
BOOL CHenry::Deploy( ) { #ifdef CLIENT_DLL if ( bIsMultiplayer() ) #else if ( g_pGameRules->IsMultiplayer() ) #endif { // enable laser sight geometry. pev->body = 1; } else { pev->body = 0; }
return DefaultDeploy( "models/v_henry.mdl", "models/p_henry.mdl", HENRY_DRAW, "henry", UseDecrement(), pev->body ); }
void CHenry::PrimaryAttack() { // don't fire underwater if (m_pPlayer->pev->waterlevel == 3) { PlayEmptySound( ); m_flNextPrimaryAttack = 0.15; return; }
if (m_iClip <= 0) { if (!m_fFireOnEmpty) Reload( ); else { EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/357_cock1.wav", 0.8, ATTN_NORM); m_flNextPrimaryAttack = 0.15; }
return; }
m_pPlayer->m_iWeaponVolume = LOUD_GUN_VOLUME; m_pPlayer->m_iWeaponFlash = BRIGHT_GUN_FLASH;
m_iClip--;
m_pPlayer->pev->effects = (int)(m_pPlayer->pev->effects) | EF_MUZZLEFLASH;
// player "shoot" animation m_pPlayer->SetAnimation( PLAYER_ATTACK1 );
UTIL_MakeVectors( m_pPlayer->pev->v_angle + m_pPlayer->pev->punchangle );
Vector vecSrc = m_pPlayer->GetGunPosition( ); Vector vecAiming = m_pPlayer->GetAutoaimVector( AUTOAIM_10DEGREES );
Vector vecDir; vecDir = m_pPlayer->FireBulletsPlayer( 1, vecSrc, vecAiming, VECTOR_CONE_1DEGREES, 8192, BULLET_PLAYER_357, 0, 0, m_pPlayer->pev, m_pPlayer->random_seed );
int flags; #if defined( CLIENT_WEAPONS ) flags = FEV_NOTHOST; #else flags = 0; #endif
PLAYBACK_EVENT_FULL( flags, m_pPlayer->edict(), m_usFireHenry, 0.0, (float *)&g_vecZero, (float *)&g_vecZero, vecDir.x, vecDir.y, 0, 0, 0, 0 );
if (!m_iClip && m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0) // HEV suit - indicate out of ammo condition m_pPlayer->SetSuitUpdate("!HEV_AMO0", FALSE, 0);
if (m_iClip != 0) m_flPumpTime = gpGlobals->time + 0.5;
m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 1.1; if (m_iClip != 0) m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 5.0; else m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 0.75; m_fInSpecialReload = 0; }
void CHenry::Reload( void ) { if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0 || m_iClip == HENRY_MAX_CLIP) return;
// don't reload until recoil is done if (m_flNextPrimaryAttack > UTIL_WeaponTimeBase()) return;
// check to see if we're ready to reload if (m_fInSpecialReload == 0) { SendWeaponAnim( HENRY_START_RELOAD ); m_fInSpecialReload = 1; m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.6; m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 0.6; m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 1.0; return; } else if (m_fInSpecialReload == 1) { if (m_flTimeWeaponIdle > UTIL_WeaponTimeBase()) return; // was waiting for gun to move to side m_fInSpecialReload = 2;
SendWeaponAnim( HENRY_RELOAD );
m_flNextReload = UTIL_WeaponTimeBase() + 0.8; m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 0.8; } else { // Add them to the clip m_iClip += 1; m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] -= 1; m_fInSpecialReload = 1; } }
void CHenry::WeaponIdle( void ) { ResetEmptySound( );
m_pPlayer->GetAutoaimVector( AUTOAIM_5DEGREES );
if ( m_flPumpTime && m_flPumpTime < gpGlobals->time ) { m_flPumpTime = 0; }
if (m_flTimeWeaponIdle < UTIL_WeaponTimeBase() ) { if (m_iClip == 0 && m_fInSpecialReload == 0 && m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]) { Reload( ); } else if (m_fInSpecialReload != 0) { if (m_iClip != 15 && m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]) { Reload( ); } else { // reload debounce has timed out SendWeaponAnim( HENRY_PUMP ); m_fInSpecialReload = 0; m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 2.6; } } else { int iAnim; float flRand = UTIL_SharedRandomFloat( m_pPlayer->random_seed, 0, 1 ); if (flRand <= 0.5) { iAnim = HENRY_IDLE1; m_flTimeWeaponIdle = (70.0/30.0); } else if (flRand <= 0.7) { iAnim = HENRY_IDLE2; m_flTimeWeaponIdle = (70.0/30.0); } else { iAnim = HENRY_IDLE3; m_flTimeWeaponIdle = (88.0/30.0); }
int bUseScope = FALSE; #ifdef CLIENT_DLL bUseScope = bIsMultiplayer(); #else bUseScope = g_pGameRules->IsMultiplayer(); #endif SendWeaponAnim( iAnim, UseDecrement() ? 1 : 0, bUseScope ); } } }
class CHenryAmmo : public CBasePlayerAmmo { void Spawn( void ) { Precache( ); SET_MODEL(ENT(pev), "models/w_357ammobox.mdl"); CBasePlayerAmmo::Spawn( ); } void Precache( void ) { PRECACHE_MODEL ("models/w_357ammobox.mdl"); PRECACHE_SOUND("items/9mmclip1.wav"); } BOOL AddAmmo( CBaseEntity *pOther ) { if (pOther->GiveAmmo( AMMO_357BOX_GIVE, "357", _357_MAX_CARRY ) != -1) { EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/9mmclip1.wav", 1, ATTN_NORM); return TRUE; } return FALSE; } }; LINK_ENTITY_TO_CLASS( ammo_henry, CHenryAmmo );
#endif
В этом коде все адаптировано под прилагаемую ниже модель, но при желании можно адаптировать его под нужную вам модель.
|