您的位置:首页 > Web前端

在 Half-Life MOD 中创建自己的武器及弹药

2006-03-12 14:52 471 查看
本文将介绍如何在 Half-Life MOD 中创建自己的武器及弹药, 本文所有源代码全部来自Codename HLDino, 你可以在Codename HLDino 开发者首页下载得到本文提到的源代码.
本文假设您有足够的C++知识及基本的HL MOD开发经验

首先, 在weapons.h中(没有硬性规定, 但是此为HL MOD开发惯例)添加武器类的声明以及一些宏的说明:

//weapons.h - 第81行
//武器的ID
#define WEAPON_AK47             16

//weapons.h - 第98行
//武器的重量(在拣到武器时, 重的武器会被自动切换)
#define AK47_WEIGHT			25

//weapons.h - 第114行
//最大载弹量
#define AK47_MAX_CARRY			250

//weapons.h - 第133行
//每个弹匣的最大载弹量
#define AK47_MAX_CLIP			30

//weapons.h - 第150行
//在拣到武器时给予的子弹数目
#define AK47_DEFAULT_GIVE			30

//weapons.h - 第169行
//在拣到弹药时给予的子弹数目
#define AMMO_AK47CLIP_GIVE		AK47_MAX_CLIP

//weapons.h - 第591行
//武器的类声明
class CAK47 : public CBasePlayerWeapon
{
public:
void Spawn( void );
void Precache( void );
int iItemSlot( void ) { return 3; }
int GetItemInfo(ItemInfo *p);
int AddToPlayer( CBasePlayer *pPlayer );

void PrimaryAttack( void );
BOOL Deploy( void );
void Reload( void );
void WeaponIdle( void );
float m_flNextAnimTime;
int m_iShell;

virtual BOOL UseDecrement( void )
{
#if defined( CLIENT_WEAPONS )
return TRUE;
#else
return FALSE;
#endif
}

private:
unsigned short m_usAK47;
};


然后, 当然是对武器进行编码啦:

/***
*
*	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.
*
****/

/**
* Codename HLDino
* Version: Beta 1.0
* Date:    Feb 2, 2005
* Author:  fiNAL.Y
* Description:
* The source code for weapon_ak47
* Copyright (C) 2000-2005 =M4CS= Development
* Copyright (C) 2000-2005 SoftBoys Entertainment
* Copyright (C) 2000-2005 MODChina.com
*/

#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "monsters.h"
#include "weapons.h"
#include "nodes.h"
#include "player.h"
#include "soundent.h"
#include "gamerules.h"

//定义武器的动作, 这些动作的顺序和模型(v_ak47.mdl)中是一致的
enum AK47_e
{
AK47_IDLE1 = 0,
AK47_RELOAD,
AK47_DRAW,
AK47_SHOOT1,
AK47_SHOOT2,
AK47_SHOOT3,
};

//定义武器的伤害值
const int AK47_DAMAGE = 25;

//使类与游戏中的实体建立连接
LINK_ENTITY_TO_CLASS( weapon_ak47, CAK47 );

//=========================================================
//=========================================================
//在武器产生时被调用
void CAK47::Spawn( )
{
pev->classname = MAKE_STRING("weapon_ak47"); //指定了武器的名称(最好与实体名称一致)
Precache( );
SET_MODEL(ENT(pev), "models/w_ak47.mdl");
m_iId = WEAPON_AK47;

m_iDefaultAmmo = AK47_DEFAULT_GIVE;

FallInit();// get ready to fall down.
}

void CAK47::Precache( void )
{
//告诉引擎对以下文件进行预缓存
PRECACHE_MODEL("models/v_ak47.mdl");
PRECACHE_MODEL("models/w_ak47.mdl");
PRECACHE_MODEL("models/p_ak47.mdl");

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

PRECACHE_SOUND ("weapons/ak47-1.wav");
PRECACHE_SOUND ("weapons/ak47-2.wav");
PRECACHE_SOUND ("weapons/ak47_clipin.wav");
PRECACHE_SOUND( "weapons/ak47_clipout.wav" );
PRECACHE_SOUND( "weapons/ak47_boltpull.wav" );

m_usAK47 = PRECACHE_EVENT( 1, "events/ak47.sc" );
}

int CAK47::GetItemInfo(ItemInfo *p)
{
p->pszName = STRING(pev->classname);
p->pszAmmo1 = "ak47"; //指定弹药的名称
p->iMaxAmmo1 = AK47_MAX_CARRY;
p->pszAmmo2 = NULL;
p->iMaxAmmo2 = -1;
p->iMaxClip = AK47_MAX_CLIP;
p->iSlot = 2;
p->iPosition = 4;  //这两行指定了武器在HUD中的位置, 不要与其他武器重复
p->iFlags = 0;
p->iId = m_iId = WEAPON_AK47;
p->iWeight = AK47_WEIGHT;

return 1;
}

int CAK47::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;
}

BOOL CAK47::Deploy( )
{
return DefaultDeploy( "models/v_ak47.mdl", "models/p_ak47.mdl", AK47_DRAW, "AK47" );
}

void CAK47::PrimaryAttack()
{

if (m_iClip <= 0)
{
PlayEmptySound();
m_flNextPrimaryAttack = 0.15;
return;
}

m_pPlayer->m_iWeaponVolume = NORMAL_GUN_VOLUME;
m_pPlayer->m_iWeaponFlash = NORMAL_GUN_FLASH;

m_iClip--;

m_pPlayer->pev->effects = (int)(m_pPlayer->pev->effects) | EF_MUZZLEFLASH;

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

Vector vecSrc	 = m_pPlayer->GetGunPosition( );
Vector vecAiming = m_pPlayer->GetAutoaimVector( AUTOAIM_5DEGREES );
Vector vecDir;

#ifdef CLIENT_DLL
if ( !bIsMultiplayer() )
#else
if ( !g_pGameRules->IsMultiplayer() )
#endif
{
// optimized multiplayer. Widened to make it easier to hit a moving player
vecDir = m_pPlayer->FireBulletsPlayer( 1, vecSrc, vecAiming, VECTOR_CONE_6DEGREES,
8192, BULLET_PLAYER_MP5, 2, AK47_DAMAGE, m_pPlayer->pev, m_pPlayer->random_seed );
}
else
{
// single player spread
vecDir = m_pPlayer->FireBulletsPlayer( 1, vecSrc, vecAiming, VECTOR_CONE_3DEGREES,
8192, BULLET_PLAYER_MP5, 2, AK47_DAMAGE, 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_usAK47, 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);

m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 0.1;

if ( m_flNextPrimaryAttac
4000
k < UTIL_WeaponTimeBase() )
m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 0.1;

m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + UTIL_SharedRandomFloat( m_pPlayer->random_seed, 10, 15 );
}

void CAK47::Reload( void )
{
if ( m_pPlayer->ammo_ak47 <= 0 )
return;

DefaultReload( AK47_MAX_CLIP, AK47_RELOAD, 1.5 );
}

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

if ( m_flTimeWeaponIdle > UTIL_WeaponTimeBase() )
return;

SendWeaponAnim( AK47_IDLE1 );

m_flTimeWeaponIdle = UTIL_SharedRandomFloat( m_pPlayer->random_seed, 10, 15 );
}

//弹药的类定义
class CAK47Ammo : public CBasePlayerAmmo
{
void Spawn( void )
{
Precache( );
SET_MODEL(ENT(pev), "models/w_9mmARclip.mdl");
CBasePlayerAmmo::Spawn( );
}
void Precache( void )
{
PRECACHE_MODEL ("models/w_9mmARclip.mdl");
PRECACHE_SOUND("items/9mmclip1.wav");
}
BOOL AddAmmo( CBaseEntity *pOther )
{
int bResult = (pOther->GiveAmmo( AMMO_AK47CLIP_GIVE, "ak47", AK47_MAX_CARRY) != -1);
if (bResult)
{
EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/9mmclip1.wav", 1, ATTN_NORM);
}
return bResult;
}
};
LINK_ENTITY_TO_CLASS( ammo_ak47, CAK47Ammo );


关于弹药的说明:
注意这一行:
p->pszAmmo1 = "ak47"; //指定弹药的名称
这一行中指定的就是弹药的名称了.然后要在CBaseEntity中添加声明来存储你的弹药:
int ammo_ak47; //cbase.h - 第350行
并在CBasePlayer::TabulateAmmo()中注册弹药:
ammo_ak47 = AmmoInventory( GetAmmoIndex( "ak47" ) ); //player.cpp - 第1141行
在你的代码中访问m_pPlayer->ammo_ak47即可得到弹药的数量.
弹药类 (CAK47Ammo)产生的实体是在地图中可以捡起的弹药, 如果你不需要这种功能( 如在CS中就没有这种功能 ), 就可以不写弹药类.

如果你想要武器可以打碎物品( 指func_breakable实体 ), 在CBreakable::pSpawnObjects[]数组中加入你的武器的名称:

//func_break.cpp - 第61行
"weapon_ak47",      // 22
"ammo_ak47",        // 23


然后, 在全局函数void W_Precache(void)中初始化你的武器:

//weapons.cpp - 第337行
// ak47
UTIL_PrecacheOtherWeapon( "weapon_ak47" );
UTIL_PrecacheOther( "ammo_ak47" );


这样, 在server端的代码就完成了.

现在, 打开你的client端代码, 并将 ak47.cpp 添加到工程的hl目录中:
在client端的主要任务是对武器击发是产生事件的处理, 注意刚才的server端代码中的这两句:

m_usAK47 = PRECACHE_EVENT( 1, "events/ak47.sc" );

PLAYBACK_EVENT_FULL( flags, m_pPlayer->edict(), m_usAK47, 0.0, (float *)&g_vecZero, (float *)&g_vecZero, vecDir.x, vecDir.y, 0, 0, 0, 0 );

它们分别初始化事件和触发事件, 在client端中要响应这个事件, 联系它们的纽带就是 "events/ak47.sc" 这个文件, 它可以是空的, 但是必须存在.

在ev_hldm.cpp中声明并完成事件函数:

//ev_hldm.cpp - 第83行
void EV_AK47Fire( struct event_args_s *args );

//ev_hldm.cpp - 第1604行
void EV_AK47Fire( event_args_t *args )
{
//...
}


在hl/hl_weapons.cpp中完成关于HUD的设置:

//hl/hl_weapons.cpp - 第69行
CAK47 g_Ak47;

//hl/hl_weapons.cpp - 第639行
void HUD_InitClientWeapons( void )
{
//...
HUD_PrepEntity( &g_Ak47     , &player );
//...
}

//hl/hl_weapons.cpp - 第757行
void HUD_WeaponsPostThink( local_state_s *from, local_state_s *to, usercmd_t *cmd,
double time, unsigned int random_seed )
{
//...
switch ( from->client.m_iId )
{
//...
case WEAPON_AK47:
pWeapon = &g_Ak47;
break;
//...
}
//...
}


最后一步, 在hl/hl_events.cpp中给事件挂上钩子:

//hl/hl_events.cpp - 第39行, 与ev_hldm中的声明保持一致
void EV_AK47Fire( struct event_args_s *args );

//hl/hl_events.cpp - 第79行
gEngfuncs.pfnHookEvent( "events/ak47.sc", EV_AK47Fire );


编译你的MOD, 配置sprite中的文件( sprite/weapon_ak47.txt )指定准星, HUD图标等, 新的武器就完成了.
Copyright © 2000-2005 =M4CS=
ALL RIGHTS RESERVED
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息