Делаем Displacer а-ля Opposing Force
Beta 0.8
Делаем Displacer а-ля Opposing Force
Ну чтоже, соскучились вы по нашим туторам ? Если да то для вас приятная новость - сейчас туторы будут добавляться на сайт, поэтому следите ;)
Итак, я уверен 99.9% всех халферов ирали в Opposing Force и пользовались оружием-телепортером. С каждым годом все больше и больше увеличиваеться количество людей, желающих создать свой супер пупер мод под Халфу и довольно многие хотят видеть в своем моде это оружие. Однако жадные Gearbox не спешат поделиться кодом этого орудия( ровно как и всего остального добавочного оружия, монтсров и эффектов из OF) то модерам приходиться огорчаться и перекраивать свои планы...
Так было раньше, пока не был создан данный тутор с полным кодом данного орудия, так что со всех тех кто это читает причитается :Р Вообщем перед вами и есть тот самый дисплейсер со всеми функциями и поностью рабочий, поэтому если у вас что то не работает то проблема уже в вас :)
Опытным програмерам вставить недостающие строки не составит никакого труда. новичкам придется немного помучиться, но оно и к лучшему - научаться не только нажимать ctrl+c -> ctrl+v но и немного думать и причем головой, и что самое главное - своей ;)
Итак - добавляем дефайн в weapons.h
Добавляем файл displacer.cpp в проект
Добавляем строчку прекеша в weapons.cpp
Регаем эвент как на клиенте, так и на сервере (хотя кажись нужно тока на клиенте - не помню) - если не помните(или не знаете) как это делать смотрите тутор по созданию нового оружия.
Вот код эвента (спаисбо xwiderу за функцию доступа к аттачментам)
void EV_SpinDisplacer(event_args_t *args)//Original code idea by Xwider
{
if (EV_IsLocal(args->entindex))
{
gEngfuncs.pEventAPI->EV_WeaponAnimation(DISPLACER_SPINUP, 2);
cl_entity_t *view = gEngfuncs.GetViewModel();
if (view != NULL)
{
float life = 1.14;
gEngfuncs.pEfxAPI->R_BeamEnts(view->index | 0x1000, view->index | 0x2000, args->iparam2, life, 0.8, 0.5, 0.5, 0.6, 0, 10, 2, 10, 0);
gEngfuncs.pEfxAPI->R_BeamEnts(view->index | 0x1000, view->index | 0x3000, args->iparam2, life, 0.8, 0.5, 0.5, 0.6, 0, 10, 2, 10, 0);
gEngfuncs.pEfxAPI->R_BeamEnts(view->index | 0x1000, view->index | 0x4000, args->iparam2, life, 0.8, 0.5, 0.5, 0.6, 0, 10, 2, 10, 0);
}
}
if (args->bparam2 == 0)// sound mode
gEngfuncs.pEventAPI->EV_PlaySound(args->entindex, args->origin, CHAN_WEAPON, "weapons/displacer_spin.wav", 1.0, ATTN_NORM, 0, PITCH_NORM);
else
gEngfuncs.pEventAPI->EV_PlaySound(args->entindex, args->origin, CHAN_WEAPON, "weapons/displacer_spin2.wav", 1.0, ATTN_NORM, 0, PITCH_NORM);
пихаем его в ev_hldm.cpp и еще вот эту хрень в ev_hldm.h
enum displacer_e
{
DISPLACER_IDLE1 = 0,
DISPLACER_IDLE2,
DISPLACER_SPINUP,
DISPLACER_SPIN,
DISPLACER_FIRE,
DISPLACER_DRAW,
DISPLACER_HOLSTER,
};
называйте эвент как хотите, но на сервере он называется displacer.sc и точка! (ессно можете изменить имя если хоцца)
Bот код displacer.cpp
/***
*
* Copyright (c) 2004 Shambler Team. All rights reserved.
*
****/
#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "monsters.h"
#include "weapons.h"
#include "nodes.h"
#include "player.h"
#include "gamerules.h"
#include "shake.h"
#include "effects.h"
enum displacer_e
{
DISPLACER_IDLE1 = 0,
DISPLACER_IDLE2,
DISPLACER_SPINUP,
DISPLACER_SPIN,
DISPLACER_FIRE,
DISPLACER_DRAW,
DISPLACER_HOLSTER,
};
class CDisplacer : public CBasePlayerWeapon
{
public:
int Save( CSave &save );
int Restore( CRestore &restore );
static TYPEDESCRIPTION m_SaveData[];
void Spawn( void );
void Precache( void );
int iItemSlot( void ) { return 4; }
int GetItemInfo(ItemInfo *p);
int AddToPlayer( CBasePlayer *pPlayer );
BOOL Deploy( void );
void Holster( int skiplocal = 0 );
void PrimaryAttack( void );
void SecondaryAttack (void);
void EXPORT SpinUp( void );
void EXPORT Teleport( void );
void EXPORT Fire( void );
void WeaponIdle( void );
int m_iAttackMode;// no need save/restore this. g-cont
private:
unsigned short m_usDisplacer;
int m_iBeam;
int m_iPlace;
};
LINK_ENTITY_TO_CLASS( weapon_displacer, CDisplacer );
//===========================
//
// TeleBall code
//
//===========================
//class CDispBall : public CGrenade
class CDispBall : public CBaseEntity//Special for Ghoul [BB] :devil:
{
public:
void Spawn( void );
void Precache( void );
void Explode( TraceResult *pTrace );
void RemoveBall (void);
void EXPORT ExplodeTouch( CBaseEntity *pOther );
void EXPORT BallThink( void );
static CDispBall *CreateDispBall( Vector vecOrigin, Vector vecAngles, CBaseEntity *pOwner, CDisplacer *pLauncher );
int m_iDispRing;
BOOL LockRing;
};
LINK_ENTITY_TO_CLASS( dispball, CDispBall );
CDispBall *CDispBall::CreateDispBall( Vector vecOrigin, Vector vecAngles, CBaseEntity *pOwner, CDisplacer *pLauncher )
{
CDispBall *pDispBall = GetClassPtr( (CDispBall *)NULL );
UTIL_SetOrigin( pDispBall->pev, vecOrigin );
pDispBall->pev->angles = vecAngles;
pDispBall->Spawn();
pDispBall->SetTouch( CDispBall::ExplodeTouch );
pDispBall->pev->owner = pOwner->edict();
return pDispBall;
}
void CDispBall :: Spawn( void )
{
Precache( );
pev->movetype = MOVETYPE_FLY;
pev->solid = SOLID_BBOX;
SET_MODEL(ENT(pev), "sprites/exit1.spr");
pev->scale = .5;
UTIL_SetSize(pev, Vector ( 0, 0, 0 ), Vector ( 0, 0, 0 ) );
UTIL_SetOrigin( pev, pev->origin );
pev->rendermode = kRenderTransAdd;
pev->renderamt = 255;
pev->classname = MAKE_STRING("dispball");
SetThink( BallThink );
SetTouch( ExplodeTouch );
pev->angles.x -= 0;
UTIL_MakeVectors( pev->angles );
pev->angles.x = -(pev->angles.x + 0);
pev->velocity = gpGlobals->v_forward * 500;
pev->nextthink = 0.5;
pev->dmg = 1000;
}
void CDispBall :: Precache( void )
{
PRECACHE_MODEL("sprites/exit1.spr");
PRECACHE_MODEL ("sprites/plasma.spr");
PRECACHE_SOUND("weapons/displacer_teleport.wav");
m_iDispRing = PRECACHE_MODEL ("sprites/displacer_ring.spr");
}
void CDispBall :: BallThink( void )
{
CBeam *pBeam;
TraceResult tr;
Vector vecDest;
float flDist = 1.0;
for (int i = 0; i < 10; i++)
{
Vector vecDir = Vector( RANDOM_FLOAT( -1.0, 1.0 ), RANDOM_FLOAT( -1.0, 1.0 ),RANDOM_FLOAT( -1.0, 1.0 ) );
vecDir = vecDir.Normalize();
TraceResult tr1;
UTIL_TraceLine( pev->origin, pev->origin + vecDir * 1024, ignore_monsters, ENT(pev), &tr1 );
if (flDist > tr1.flFraction)
{
tr = tr1;
flDist = tr.flFraction;
}
}
if ( flDist == 1.0 ) return;
pBeam = CBeam::BeamCreate("sprites/plasma.spr",200);
pBeam->PointEntInit( tr.vecEndPos, entindex() );
pBeam->SetStartPos( tr.vecEndPos );
pBeam->SetEndEntity( entindex() );
pBeam->SetColor( 90, 170, 16 );
pBeam->SetNoise( 65 );
pBeam->SetBrightness( 255 );
pBeam->SetWidth( 30 );
pBeam->SetScrollRate( 35 );
pBeam->LiveForTime( 1 );
pev->frame += 1; //animate teleball
if(pev->frame > 24)
pev->frame = fmod( pev->frame, 24 );
pev->nextthink = gpGlobals->time + 0.1;
}
void CDispBall::ExplodeTouch( CBaseEntity *pOther )
{
TraceResult tr;
Vector vecSpot;// trace starts here!
pev->enemy = pOther->edict();
vecSpot = pev->origin - pev->velocity.Normalize() * 32;
UTIL_TraceLine( vecSpot, vecSpot + pev->velocity.Normalize() * 64, dont_ignore_monsters, ENT(pev), &tr );
Explode( &tr );
}
void CDispBall::Explode( TraceResult *pTrace )
{
if(!LockRing)//for partially fix strange bug in HL engine (for full fix teleball MUST be moved to client side). g-cont
{
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin );
WRITE_BYTE( TE_BEAMCYLINDER );
WRITE_COORD( pev->origin.x);
WRITE_COORD( pev->origin.y);
WRITE_COORD( pev->origin.z);
WRITE_COORD( pev->origin.x);
WRITE_COORD( pev->origin.y);
WRITE_COORD( pev->origin.z + 800);
WRITE_SHORT( m_iDispRing );
WRITE_BYTE( 0 ); // startframe
WRITE_BYTE( 10 ); // framerate
WRITE_BYTE( 3 ); // life
WRITE_BYTE( 20 ); // width
WRITE_BYTE( 0 ); // noise
WRITE_BYTE( 255 ); // r, g, b
WRITE_BYTE( 255 ); // r, g, b
WRITE_BYTE( 255 ); // r, g, b
WRITE_BYTE( 255 ); //brightness
WRITE_BYTE( 0 ); // speed
MESSAGE_END();
}
LockRing = TRUE;
pev->velocity = g_vecZero;
SetThink (RemoveBall);
pev->nextthink = gpGlobals->time + 0.6;
}
void CDispBall::RemoveBall( void )
{
EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/displacer_teleport.wav", 0.9, ATTN_NORM);
pev->effects |= EF_NODRAW;
entvars_t *pevOwner;
if ( pev->owner )
pevOwner = VARS( pev->owner );
else
pevOwner = NULL;
pev->owner = NULL;
UTIL_Remove( this );
::RadiusDamage( pev->origin, pev, pevOwner, pev->dmg, 200, CLASS_NONE, DMG_ENERGYBEAM );
}
//===========================
//
// Displacer code
//
//===========================
void CDisplacer::Spawn( )
{
Precache( );
m_iId = WEAPON_DISPLACER;
SET_MODEL(ENT(pev), "models/w_displacer.mdl");
m_iDefaultAmmo = 60;
FallInit();// get ready to fall down.
}
TYPEDESCRIPTION CDisplacer::m_SaveData[] =
{
DEFINE_FIELD( CDisplacer, m_iPlace, FIELD_INTEGER ),//Remember last teleportation point. g-cont
};
IMPLEMENT_SAVERESTORE( CDisplacer, CBasePlayerWeapon );
int CDisplacer::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 CDisplacer::Deploy( void )
{
return DefaultDeploy( "models/v_displacer.mdl", "models/p_displacer.mdl", DISPLACER_DRAW, "gauss" );
}
void CDisplacer::Holster( int skiplocal )
{
m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 1.5;
SendWeaponAnim( DISPLACER_HOLSTER );
}
int CDisplacer::GetItemInfo(ItemInfo *p)
{
p->pszName = STRING(pev->classname);
p->pszAmmo1 = "uranium";
p->iMaxAmmo1 = URANIUM_MAX_CARRY;
p->pszAmmo2 = NULL;
p->iMaxAmmo2 = -1;
p->iMaxClip = WEAPON_NOCLIP;
p->iSlot = 3;
p->iPosition = 4;
p->iId = m_iId = WEAPON_DISPLACER;
p->iFlags = 0;
p->iWeight = DISPLACER_WEIGHT;
return 1;
}
void CDisplacer::Precache( void )
{
PRECACHE_MODEL("models/w_displacer.mdl");
PRECACHE_MODEL("models/v_displacer.mdl");
PRECACHE_MODEL("models/p_displacer.mdl");
PRECACHE_SOUND("weapons/displacer_fire.wav");
PRECACHE_SOUND ("buttons/button10.wav");
PRECACHE_SOUND ("weapons/displacer_self.wav");
UTIL_PrecacheOther( "dispball" );
m_iBeam = PRECACHE_MODEL("sprites/plasma.spr");
m_usDisplacer = PRECACHE_EVENT( 1, "events/displacer.sc" );
}
void CDisplacer::SpinUp ( void )
{
int flags;
flags = 0;
if (!m_iAttackMode) SetThink (CDisplacer::Fire);
else SetThink (CDisplacer::Teleport);
//spinup event
PLAYBACK_EVENT_FULL( flags, m_pPlayer->edict(), m_usDisplacer, 0.0, (float *)&g_vecZero, (float *)&g_vecZero, 0.0, 0.0, 0, m_iBeam, 0, m_iAttackMode);
pev->nextthink = gpGlobals->time + 1.1;
m_flTimeWeaponIdle = gpGlobals->time + 1.15;
}
void CDisplacer::PrimaryAttack( void )
{
if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] < 20)//Check for ammo
{
EMIT_SOUND( edict(), CHAN_BODY, "buttons/button10.wav", 1, ATTN_NORM );
m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 1.0;
return;
}
m_iAttackMode = FALSE; //We set attack mode in first mode
SetThink (CDisplacer::SpinUp);
m_flNextPrimaryAttack = gpGlobals->time + 2.0;
m_flTimeWeaponIdle = gpGlobals->time + 1.5;
pev->nextthink = gpGlobals->time + 0.1;
}
void CDisplacer::SecondaryAttack ( void )
{
if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] < 60)
{
EMIT_SOUND( edict(), CHAN_BODY, "buttons/button10.wav", 1, ATTN_NORM );
m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 1.0;
return;
}
if (m_iAttackMode) return;
m_iAttackMode = TRUE; //We set attack mode in second mode
SetThink (CDisplacer::SpinUp);
m_flNextSecondaryAttack = gpGlobals->time + 2.0;
m_flTimeWeaponIdle = gpGlobals->time + 1.5;
pev->nextthink = gpGlobals->time + 0.1;
}
void CDisplacer::Fire (void)
{
SendWeaponAnim( DISPLACER_FIRE );
m_pPlayer->SetAnimation( PLAYER_ATTACK1 );
Vector vecSrc = m_pPlayer->GetGunPosition( ) + gpGlobals->v_forward * 16 + gpGlobals->v_right * 2 + gpGlobals->v_up * -5;
m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] -= 20;
EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/displacer_fire.wav", 0.9, ATTN_NORM );
CDispBall *pDispBall = CDispBall::CreateDispBall( vecSrc, m_pPlayer->pev->v_angle, m_pPlayer, this );
}
void CDisplacer:: Teleport ( void )
{
m_iPlace = !m_iPlace;
CBaseEntity *pSpot = NULL;
m_iAttackMode = FALSE;//reset firemode
if ( m_iPlace ) //Xen and earth target
pSpot = UTIL_FindEntityByClassname( pSpot, "info_xen" );
else
pSpot = UTIL_FindEntityByClassname( pSpot, "info_earth" );
if (pSpot)
{
UTIL_ScreenFade( m_pPlayer, Vector(0, 200, 0), 0.5, 0.5, 255, FFADE_IN );
m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] -= 60;
Vector tmp = pSpot->pev->origin;
tmp.z -= m_pPlayer->pev->mins.z;
tmp.z++;
UTIL_SetOrigin( m_pPlayer->pev, tmp );
m_pPlayer->pev->angles = pSpot->pev->angles;
m_pPlayer->pev->velocity = m_pPlayer->pev->basevelocity = g_vecZero;
EMIT_SOUND( edict(), CHAN_BODY, "weapons/displacer_self.wav", 1, ATTN_NORM );
SendWeaponAnim( DISPLACER_FIRE );
}
else
{
SendWeaponAnim( DISPLACER_SPINUP );
EMIT_SOUND( edict(), CHAN_BODY, "buttons/button10.wav", 1, ATTN_NORM );
m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.3;
}
m_flNextSecondaryAttack = gpGlobals->time + 2.0;
m_flTimeWeaponIdle = gpGlobals->time + 1.0;
}
void CDisplacer::WeaponIdle( void )
{
int iAnim;
m_pPlayer->GetAutoaimVector( AUTOAIM_5DEGREES );
if ( m_flTimeWeaponIdle > gpGlobals->time )
return;
float flRand = RANDOM_FLOAT(0,1);
if ( flRand <= 0.5 )
{
iAnim = DISPLACER_IDLE1;
m_flTimeWeaponIdle = gpGlobals->time + RANDOM_FLOAT(10,15);
}
else
{
iAnim = DISPLACER_IDLE2;
m_flTimeWeaponIdle = gpGlobals->time + 3;
}
SendWeaponAnim( iAnim );
}
K вящей радости любителей телепортнуться можно еще добавить две строчки в triggers.cpp дабы халфа не ругалась матом, а вела себя тихо и примерно:
LINK_ENTITY_TO_CLASS( info_xen, CPointEntity );//Two points teleporartion
LINK_ENTITY_TO_CLASS( info_earth, CPointEntity );
Xотя какая нафиг разница куда вы впишете эти две строчки ?
Да хоть в h_export.cpp, но тока чур в самый конец, если вам от этого легче.
Внимание! жалобы, типа "у меня не работает" не принимаються
Hа этом все, спасибо за внимание :)
g-cont