forked from valve/halflife-sdk
695 lines
17 KiB
C++
695 lines
17 KiB
C++
|
/***
|
||
|
*
|
||
|
* Copyright (c) 1999, 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 )
|
||
|
|
||
|
#include "extdll.h"
|
||
|
#include "util.h"
|
||
|
#include "cbase.h"
|
||
|
#include "monsters.h"
|
||
|
#include "weapons.h"
|
||
|
#include "nodes.h"
|
||
|
#include "player.h"
|
||
|
#include "gamerules.h"
|
||
|
|
||
|
|
||
|
enum rpg_e {
|
||
|
RPG_IDLE = 0,
|
||
|
RPG_FIDGET,
|
||
|
RPG_RELOAD, // to reload
|
||
|
RPG_FIRE2, // to empty
|
||
|
RPG_HOLSTER1, // loaded
|
||
|
RPG_DRAW1, // loaded
|
||
|
RPG_HOLSTER2, // unloaded
|
||
|
RPG_DRAW_UL, // unloaded
|
||
|
RPG_IDLE_UL, // unloaded idle
|
||
|
RPG_FIDGET_UL, // unloaded fidget
|
||
|
};
|
||
|
|
||
|
|
||
|
class CLaserSpot : public CBaseEntity
|
||
|
{
|
||
|
void Spawn( void );
|
||
|
void Precache( void );
|
||
|
|
||
|
int ObjectCaps( void ) { return FCAP_DONT_SAVE; }
|
||
|
|
||
|
public:
|
||
|
void Suspend( float flSuspendTime );
|
||
|
void EXPORT Revive( void );
|
||
|
|
||
|
static CLaserSpot *CreateSpot( void );
|
||
|
};
|
||
|
LINK_ENTITY_TO_CLASS( laser_spot, CLaserSpot );
|
||
|
|
||
|
|
||
|
class CRpg : public CBasePlayerWeapon
|
||
|
{
|
||
|
public:
|
||
|
int Save( CSave &save );
|
||
|
int Restore( CRestore &restore );
|
||
|
static TYPEDESCRIPTION m_SaveData[];
|
||
|
|
||
|
void Spawn( void );
|
||
|
void Precache( void );
|
||
|
void Reload( void );
|
||
|
int iItemSlot( void ) { return 4; }
|
||
|
int GetItemInfo(ItemInfo *p);
|
||
|
int AddToPlayer( CBasePlayer *pPlayer );
|
||
|
|
||
|
BOOL Deploy( void );
|
||
|
BOOL CanHolster( void );
|
||
|
void Holster( void );
|
||
|
|
||
|
void PrimaryAttack( void );
|
||
|
void SecondaryAttack( void );
|
||
|
void WeaponIdle( void );
|
||
|
|
||
|
void UpdateSpot( void );
|
||
|
BOOL ShouldWeaponIdle( void ) { return TRUE; };
|
||
|
|
||
|
CLaserSpot *m_pSpot;
|
||
|
int m_fSpotActive;
|
||
|
int m_cActiveRockets;// how many missiles in flight from this launcher right now?
|
||
|
|
||
|
};
|
||
|
LINK_ENTITY_TO_CLASS( weapon_rpg, CRpg );
|
||
|
|
||
|
TYPEDESCRIPTION CRpg::m_SaveData[] =
|
||
|
{
|
||
|
DEFINE_FIELD( CRpg, m_fSpotActive, FIELD_INTEGER ),
|
||
|
DEFINE_FIELD( CRpg, m_cActiveRockets, FIELD_INTEGER ),
|
||
|
};
|
||
|
IMPLEMENT_SAVERESTORE( CRpg, CBasePlayerWeapon );
|
||
|
|
||
|
//=========================================================
|
||
|
//=========================================================
|
||
|
CLaserSpot *CLaserSpot::CreateSpot( void )
|
||
|
{
|
||
|
CLaserSpot *pSpot = GetClassPtr( (CLaserSpot *)NULL );
|
||
|
pSpot->Spawn();
|
||
|
|
||
|
pSpot->pev->classname = MAKE_STRING("laser_spot");
|
||
|
|
||
|
return pSpot;
|
||
|
}
|
||
|
|
||
|
//=========================================================
|
||
|
//=========================================================
|
||
|
void CLaserSpot::Spawn( void )
|
||
|
{
|
||
|
Precache( );
|
||
|
pev->movetype = MOVETYPE_NONE;
|
||
|
pev->solid = SOLID_NOT;
|
||
|
|
||
|
pev->rendermode = kRenderGlow;
|
||
|
pev->renderfx = kRenderFxNoDissipation;
|
||
|
pev->renderamt = 255;
|
||
|
|
||
|
SET_MODEL(ENT(pev), "sprites/laserdot.spr");
|
||
|
UTIL_SetOrigin( pev, pev->origin );
|
||
|
};
|
||
|
|
||
|
//=========================================================
|
||
|
// Suspend- make the laser sight invisible.
|
||
|
//=========================================================
|
||
|
void CLaserSpot::Suspend( float flSuspendTime )
|
||
|
{
|
||
|
pev->effects |= EF_NODRAW;
|
||
|
|
||
|
SetThink( Revive );
|
||
|
pev->nextthink = gpGlobals->time + flSuspendTime;
|
||
|
}
|
||
|
|
||
|
//=========================================================
|
||
|
// Revive - bring a suspended laser sight back.
|
||
|
//=========================================================
|
||
|
void CLaserSpot::Revive( void )
|
||
|
{
|
||
|
pev->effects &= ~EF_NODRAW;
|
||
|
|
||
|
SetThink( NULL );
|
||
|
}
|
||
|
|
||
|
void CLaserSpot::Precache( void )
|
||
|
{
|
||
|
PRECACHE_MODEL("sprites/laserdot.spr");
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
class CRpgRocket : public CGrenade
|
||
|
{
|
||
|
public:
|
||
|
int Save( CSave &save );
|
||
|
int Restore( CRestore &restore );
|
||
|
static TYPEDESCRIPTION m_SaveData[];
|
||
|
void Spawn( void );
|
||
|
void Precache( void );
|
||
|
void EXPORT FollowThink( void );
|
||
|
void EXPORT IgniteThink( void );
|
||
|
void EXPORT RocketTouch( CBaseEntity *pOther );
|
||
|
static CRpgRocket *CreateRpgRocket( Vector vecOrigin, Vector vecAngles, CBaseEntity *pOwner, CRpg *pLauncher );
|
||
|
|
||
|
int m_iTrail;
|
||
|
float m_flIgniteTime;
|
||
|
CRpg *m_pLauncher;// pointer back to the launcher that fired me.
|
||
|
};
|
||
|
LINK_ENTITY_TO_CLASS( rpg_rocket, CRpgRocket );
|
||
|
|
||
|
TYPEDESCRIPTION CRpgRocket::m_SaveData[] =
|
||
|
{
|
||
|
DEFINE_FIELD( CRpgRocket, m_flIgniteTime, FIELD_TIME ),
|
||
|
DEFINE_FIELD( CRpgRocket, m_pLauncher, FIELD_CLASSPTR ),
|
||
|
};
|
||
|
IMPLEMENT_SAVERESTORE( CRpgRocket, CGrenade );
|
||
|
|
||
|
//=========================================================
|
||
|
//=========================================================
|
||
|
CRpgRocket *CRpgRocket::CreateRpgRocket( Vector vecOrigin, Vector vecAngles, CBaseEntity *pOwner, CRpg *pLauncher )
|
||
|
{
|
||
|
CRpgRocket *pRocket = GetClassPtr( (CRpgRocket *)NULL );
|
||
|
|
||
|
UTIL_SetOrigin( pRocket->pev, vecOrigin );
|
||
|
pRocket->pev->angles = vecAngles;
|
||
|
pRocket->Spawn();
|
||
|
pRocket->SetTouch( CRpgRocket::RocketTouch );
|
||
|
pRocket->m_pLauncher = pLauncher;// remember what RPG fired me.
|
||
|
pRocket->m_pLauncher->m_cActiveRockets++;// register this missile as active for the launcher
|
||
|
pRocket->pev->owner = pOwner->edict();
|
||
|
|
||
|
return pRocket;
|
||
|
}
|
||
|
|
||
|
//=========================================================
|
||
|
//=========================================================
|
||
|
void CRpgRocket :: Spawn( void )
|
||
|
{
|
||
|
Precache( );
|
||
|
// motor
|
||
|
pev->movetype = MOVETYPE_BOUNCE;
|
||
|
pev->solid = SOLID_BBOX;
|
||
|
|
||
|
SET_MODEL(ENT(pev), "models/rpgrocket.mdl");
|
||
|
UTIL_SetSize(pev, Vector( 0, 0, 0), Vector(0, 0, 0));
|
||
|
UTIL_SetOrigin( pev, pev->origin );
|
||
|
|
||
|
pev->classname = MAKE_STRING("rpg_rocket");
|
||
|
|
||
|
SetThink( IgniteThink );
|
||
|
SetTouch( ExplodeTouch );
|
||
|
|
||
|
pev->angles.x -= 30;
|
||
|
UTIL_MakeVectors( pev->angles );
|
||
|
pev->angles.x = -(pev->angles.x + 30);
|
||
|
|
||
|
pev->velocity = gpGlobals->v_forward * 250;
|
||
|
pev->gravity = 0.5;
|
||
|
|
||
|
pev->nextthink = gpGlobals->time + 0.4;
|
||
|
|
||
|
pev->dmg = gSkillData.plrDmgRPG;
|
||
|
}
|
||
|
|
||
|
//=========================================================
|
||
|
//=========================================================
|
||
|
void CRpgRocket :: RocketTouch ( CBaseEntity *pOther )
|
||
|
{
|
||
|
if ( m_pLauncher )
|
||
|
{
|
||
|
// my launcher is still around, tell it I'm dead.
|
||
|
m_pLauncher->m_cActiveRockets--;
|
||
|
}
|
||
|
|
||
|
STOP_SOUND( edict(), CHAN_VOICE, "weapons/rocket1.wav" );
|
||
|
ExplodeTouch( pOther );
|
||
|
}
|
||
|
|
||
|
//=========================================================
|
||
|
//=========================================================
|
||
|
void CRpgRocket :: Precache( void )
|
||
|
{
|
||
|
PRECACHE_MODEL("models/rpgrocket.mdl");
|
||
|
m_iTrail = PRECACHE_MODEL("sprites/smoke.spr");
|
||
|
PRECACHE_SOUND ("weapons/rocket1.wav");
|
||
|
}
|
||
|
|
||
|
|
||
|
void CRpgRocket :: IgniteThink( void )
|
||
|
{
|
||
|
// pev->movetype = MOVETYPE_TOSS;
|
||
|
|
||
|
pev->movetype = MOVETYPE_FLY;
|
||
|
pev->effects |= EF_LIGHT;
|
||
|
|
||
|
// make rocket sound
|
||
|
EMIT_SOUND( ENT(pev), CHAN_VOICE, "weapons/rocket1.wav", 1, 0.5 );
|
||
|
|
||
|
// rocket trail
|
||
|
MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY );
|
||
|
|
||
|
WRITE_BYTE( TE_BEAMFOLLOW );
|
||
|
WRITE_SHORT(entindex()); // entity
|
||
|
WRITE_SHORT(m_iTrail ); // model
|
||
|
WRITE_BYTE( 40 ); // life
|
||
|
WRITE_BYTE( 5 ); // width
|
||
|
WRITE_BYTE( 224 ); // r, g, b
|
||
|
WRITE_BYTE( 224 ); // r, g, b
|
||
|
WRITE_BYTE( 255 ); // r, g, b
|
||
|
WRITE_BYTE( 255 ); // brightness
|
||
|
|
||
|
MESSAGE_END(); // move PHS/PVS data sending into here (SEND_ALL, SEND_PVS, SEND_PHS)
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
WRITE_BYTE( MSG_BROADCAST, SVC_TEMPENTITY );
|
||
|
WRITE_BYTE( MSG_BROADCAST, TE_BEAMFOLLOW);
|
||
|
WRITE_SHORT(entindex()); // entity
|
||
|
WRITE_SHORT(MSG_BROADCAST, m_iTrail ); // model
|
||
|
WRITE_BYTE( MSG_BROADCAST, 40 ); // life
|
||
|
WRITE_BYTE( MSG_BROADCAST, 5 ); // width
|
||
|
WRITE_BYTE( MSG_BROADCAST, 224 ); // r, g, b
|
||
|
WRITE_BYTE( MSG_BROADCAST, 224 ); // r, g, b
|
||
|
WRITE_BYTE( MSG_BROADCAST, 255 ); // r, g, b
|
||
|
WRITE_BYTE( MSG_BROADCAST, 255 ); // brightness
|
||
|
*/
|
||
|
m_flIgniteTime = gpGlobals->time;
|
||
|
|
||
|
// set to follow laser spot
|
||
|
SetThink( FollowThink );
|
||
|
pev->nextthink = gpGlobals->time + 0.1;
|
||
|
}
|
||
|
|
||
|
|
||
|
void CRpgRocket :: FollowThink( void )
|
||
|
{
|
||
|
CBaseEntity *pOther = NULL;
|
||
|
Vector vecTarget;
|
||
|
Vector vecDir;
|
||
|
float flDist, flMax, flDot;
|
||
|
TraceResult tr;
|
||
|
|
||
|
UTIL_MakeAimVectors( pev->angles );
|
||
|
|
||
|
vecTarget = gpGlobals->v_forward;
|
||
|
flMax = 4096;
|
||
|
|
||
|
// Examine all entities within a reasonable radius
|
||
|
while ((pOther = UTIL_FindEntityByClassname( pOther, "laser_spot" )) != NULL)
|
||
|
{
|
||
|
UTIL_TraceLine ( pev->origin, pOther->pev->origin, dont_ignore_monsters, ENT(pev), &tr );
|
||
|
// ALERT( at_console, "%f\n", tr.flFraction );
|
||
|
if (tr.flFraction >= 0.90)
|
||
|
{
|
||
|
vecDir = pOther->pev->origin - pev->origin;
|
||
|
flDist = vecDir.Length( );
|
||
|
vecDir = vecDir.Normalize( );
|
||
|
flDot = DotProduct( gpGlobals->v_forward, vecDir );
|
||
|
if ((flDot > 0) && (flDist * (1 - flDot) < flMax))
|
||
|
{
|
||
|
flMax = flDist * (1 - flDot);
|
||
|
vecTarget = vecDir;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pev->angles = UTIL_VecToAngles( vecTarget );
|
||
|
|
||
|
// this acceleration and turning math is totally wrong, but it seems to respond well so don't change it.
|
||
|
float flSpeed = pev->velocity.Length();
|
||
|
if (gpGlobals->time - m_flIgniteTime < 1.0)
|
||
|
{
|
||
|
pev->velocity = pev->velocity * 0.2 + vecTarget * (flSpeed * 0.8 + 400);
|
||
|
if (pev->waterlevel == 3)
|
||
|
{
|
||
|
// go slow underwater
|
||
|
if (pev->velocity.Length() > 300)
|
||
|
{
|
||
|
pev->velocity = pev->velocity.Normalize() * 300;
|
||
|
}
|
||
|
UTIL_BubbleTrail( pev->origin - pev->velocity * 0.1, pev->origin, 4 );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (pev->velocity.Length() > 2000)
|
||
|
{
|
||
|
pev->velocity = pev->velocity.Normalize() * 2000;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (pev->effects & EF_LIGHT)
|
||
|
{
|
||
|
pev->effects = 0;
|
||
|
STOP_SOUND( ENT(pev), CHAN_VOICE, "weapons/rocket1.wav" );
|
||
|
}
|
||
|
pev->velocity = pev->velocity * 0.2 + vecTarget * flSpeed * 0.798;
|
||
|
if (pev->waterlevel == 0 && pev->velocity.Length() < 1500)
|
||
|
{
|
||
|
Detonate( );
|
||
|
}
|
||
|
}
|
||
|
// ALERT( at_console, "%.0f\n", flSpeed );
|
||
|
|
||
|
pev->nextthink = gpGlobals->time + 0.1;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
void CRpg::Reload( void )
|
||
|
{
|
||
|
int iResult;
|
||
|
|
||
|
if ( m_iClip == 1 )
|
||
|
{
|
||
|
// don't bother with any of this if don't need to reload.
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// because the RPG waits to autoreload when no missiles are active while the LTD is on, the
|
||
|
// weapons code is constantly calling into this function, but is often denied because
|
||
|
// a) missiles are in flight, but the LTD is on
|
||
|
// or
|
||
|
// b) player is totally out of ammo and has nothing to switch to, and should be allowed to
|
||
|
// shine the designator around
|
||
|
//
|
||
|
// Set the next attack time into the future so that WeaponIdle will get called more often
|
||
|
// than reload, allowing the RPG LTD to be updated
|
||
|
|
||
|
m_flNextPrimaryAttack = gpGlobals->time + 0.5;
|
||
|
|
||
|
if ( m_cActiveRockets && m_fSpotActive )
|
||
|
{
|
||
|
// no reloading when there are active missiles tracking the designator.
|
||
|
// ward off future autoreload attempts by setting next attack time into the future for a bit.
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (m_pSpot && m_fSpotActive)
|
||
|
{
|
||
|
m_pSpot->Suspend( 2.1 );
|
||
|
m_flNextSecondaryAttack = gpGlobals->time + 2.1;
|
||
|
}
|
||
|
|
||
|
if (m_iClip == 0)
|
||
|
{
|
||
|
iResult = DefaultReload( RPG_MAX_CLIP, RPG_RELOAD, 2 );
|
||
|
}
|
||
|
|
||
|
if (iResult)
|
||
|
{
|
||
|
m_flTimeWeaponIdle = gpGlobals->time + RANDOM_FLOAT ( 10, 15 );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CRpg::Spawn( )
|
||
|
{
|
||
|
Precache( );
|
||
|
m_iId = WEAPON_RPG;
|
||
|
|
||
|
SET_MODEL(ENT(pev), "models/w_rpg.mdl");
|
||
|
m_fSpotActive = 1;
|
||
|
|
||
|
if ( g_pGameRules->IsMultiplayer() )
|
||
|
{
|
||
|
// more default ammo in multiplay.
|
||
|
m_iDefaultAmmo = RPG_DEFAULT_GIVE * 2;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m_iDefaultAmmo = RPG_DEFAULT_GIVE;
|
||
|
}
|
||
|
|
||
|
FallInit();// get ready to fall down.
|
||
|
}
|
||
|
|
||
|
|
||
|
void CRpg::Precache( void )
|
||
|
{
|
||
|
PRECACHE_MODEL("models/w_rpg.mdl");
|
||
|
PRECACHE_MODEL("models/v_rpg.mdl");
|
||
|
PRECACHE_MODEL("models/p_rpg.mdl");
|
||
|
|
||
|
PRECACHE_SOUND("items/9mmclip1.wav");
|
||
|
|
||
|
UTIL_PrecacheOther( "laser_spot" );
|
||
|
UTIL_PrecacheOther( "rpg_rocket" );
|
||
|
|
||
|
PRECACHE_SOUND("weapons/rocketfire1.wav");
|
||
|
PRECACHE_SOUND("weapons/glauncher.wav"); // alternative fire sound
|
||
|
}
|
||
|
|
||
|
|
||
|
int CRpg::GetItemInfo(ItemInfo *p)
|
||
|
{
|
||
|
p->pszName = STRING(pev->classname);
|
||
|
p->pszAmmo1 = "rockets";
|
||
|
p->iMaxAmmo1 = ROCKET_MAX_CARRY;
|
||
|
p->pszAmmo2 = NULL;
|
||
|
p->iMaxAmmo2 = -1;
|
||
|
p->iMaxClip = RPG_MAX_CLIP;
|
||
|
p->iSlot = 3;
|
||
|
p->iPosition = 0;
|
||
|
p->iId = m_iId = WEAPON_RPG;
|
||
|
p->iFlags = 0;
|
||
|
p->iWeight = RPG_WEIGHT;
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
int CRpg::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 CRpg::Deploy( )
|
||
|
{
|
||
|
if ( m_iClip == 0 )
|
||
|
{
|
||
|
return DefaultDeploy( "models/v_rpg.mdl", "models/p_rpg.mdl", RPG_DRAW_UL, "rpg" );
|
||
|
}
|
||
|
|
||
|
return DefaultDeploy( "models/v_rpg.mdl", "models/p_rpg.mdl", RPG_DRAW1, "rpg" );
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL CRpg::CanHolster( void )
|
||
|
{
|
||
|
if ( m_fSpotActive && m_cActiveRockets )
|
||
|
{
|
||
|
// can't put away while guiding a missile.
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
void CRpg::Holster( )
|
||
|
{
|
||
|
m_fInReload = FALSE;// cancel any reload in progress.
|
||
|
|
||
|
m_pPlayer->m_flNextAttack = gpGlobals->time + 0.5;
|
||
|
// m_flTimeWeaponIdle = gpGlobals->time + RANDOM_FLOAT ( 10, 15 );
|
||
|
SendWeaponAnim( RPG_HOLSTER1 );
|
||
|
if (m_pSpot)
|
||
|
{
|
||
|
m_pSpot->Killed( NULL, GIB_NEVER );
|
||
|
m_pSpot = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
void CRpg::PrimaryAttack()
|
||
|
{
|
||
|
if (m_iClip)
|
||
|
{
|
||
|
m_pPlayer->m_iWeaponVolume = LOUD_GUN_VOLUME;
|
||
|
m_pPlayer->m_iWeaponFlash = BRIGHT_GUN_FLASH;
|
||
|
|
||
|
SendWeaponAnim( RPG_FIRE2 );
|
||
|
|
||
|
// player "shoot" animation
|
||
|
m_pPlayer->SetAnimation( PLAYER_ATTACK1 );
|
||
|
|
||
|
UTIL_MakeVectors( m_pPlayer->pev->v_angle );
|
||
|
Vector vecSrc = m_pPlayer->GetGunPosition( ) + gpGlobals->v_forward * 16 + gpGlobals->v_right * 8 + gpGlobals->v_up * -8;
|
||
|
|
||
|
CRpgRocket *pRocket = CRpgRocket::CreateRpgRocket( vecSrc, m_pPlayer->pev->v_angle, m_pPlayer, this );
|
||
|
|
||
|
UTIL_MakeVectors( m_pPlayer->pev->v_angle );// RpgRocket::Create stomps on globals, so remake.
|
||
|
pRocket->pev->velocity = pRocket->pev->velocity + gpGlobals->v_forward * DotProduct( m_pPlayer->pev->velocity, gpGlobals->v_forward );
|
||
|
|
||
|
// firing RPG no longer turns on the designator. ALT fire is a toggle switch for the LTD.
|
||
|
// Ken signed up for this as a global change (sjb)
|
||
|
|
||
|
|
||
|
EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/rocketfire1.wav", 0.9, ATTN_NORM );
|
||
|
EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_ITEM, "weapons/glauncher.wav", 0.7, ATTN_NORM );
|
||
|
|
||
|
m_iClip--;
|
||
|
//m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]--;
|
||
|
|
||
|
m_flNextPrimaryAttack = gpGlobals->time + 1.5;
|
||
|
m_flTimeWeaponIdle = gpGlobals->time + 1.5;
|
||
|
m_pPlayer->pev->punchangle.x -= 5;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
PlayEmptySound( );
|
||
|
}
|
||
|
UpdateSpot( );
|
||
|
}
|
||
|
|
||
|
|
||
|
void CRpg::SecondaryAttack()
|
||
|
{
|
||
|
m_fSpotActive = ! m_fSpotActive;
|
||
|
|
||
|
if (!m_fSpotActive && m_pSpot)
|
||
|
{
|
||
|
m_pSpot->Killed( NULL, GIB_NORMAL );
|
||
|
m_pSpot = NULL;
|
||
|
}
|
||
|
|
||
|
m_flNextSecondaryAttack = gpGlobals->time + 0.2;
|
||
|
}
|
||
|
|
||
|
|
||
|
void CRpg::WeaponIdle( void )
|
||
|
{
|
||
|
UpdateSpot( );
|
||
|
|
||
|
ResetEmptySound( );
|
||
|
|
||
|
if (m_flTimeWeaponIdle > gpGlobals->time)
|
||
|
return;
|
||
|
|
||
|
if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType])
|
||
|
{
|
||
|
int iAnim;
|
||
|
float flRand = RANDOM_FLOAT(0, 1);
|
||
|
if (flRand <= 0.75 || m_fSpotActive)
|
||
|
{
|
||
|
if ( m_iClip == 0 )
|
||
|
iAnim = RPG_IDLE_UL;
|
||
|
else
|
||
|
iAnim = RPG_IDLE;
|
||
|
|
||
|
m_flTimeWeaponIdle = gpGlobals->time + 90.0 / 15.0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ( m_iClip == 0 )
|
||
|
iAnim = RPG_FIDGET_UL;
|
||
|
else
|
||
|
iAnim = RPG_FIDGET;
|
||
|
|
||
|
m_flTimeWeaponIdle = gpGlobals->time + 3.0;
|
||
|
}
|
||
|
|
||
|
SendWeaponAnim( iAnim );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m_flTimeWeaponIdle = gpGlobals->time + 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
void CRpg::UpdateSpot( void )
|
||
|
{
|
||
|
if (m_fSpotActive)
|
||
|
{
|
||
|
if (!m_pSpot)
|
||
|
{
|
||
|
m_pSpot = CLaserSpot::CreateSpot();
|
||
|
}
|
||
|
|
||
|
UTIL_MakeVectors( m_pPlayer->pev->v_angle );
|
||
|
Vector vecSrc = m_pPlayer->GetGunPosition( );;
|
||
|
Vector vecAiming = gpGlobals->v_forward;
|
||
|
|
||
|
TraceResult tr;
|
||
|
UTIL_TraceLine ( vecSrc, vecSrc + vecAiming * 8192, dont_ignore_monsters, ENT(m_pPlayer->pev), &tr );
|
||
|
|
||
|
// ALERT( "%f %f\n", gpGlobals->v_forward.y, vecAiming.y );
|
||
|
|
||
|
/*
|
||
|
float a = gpGlobals->v_forward.y * vecAiming.y + gpGlobals->v_forward.x * vecAiming.x;
|
||
|
m_pPlayer->pev->punchangle.y = acos( a ) * (180 / M_PI);
|
||
|
|
||
|
ALERT( at_console, "%f\n", a );
|
||
|
*/
|
||
|
|
||
|
UTIL_SetOrigin( m_pSpot->pev, tr.vecEndPos );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
class CRpgAmmo : public CBasePlayerAmmo
|
||
|
{
|
||
|
void Spawn( void )
|
||
|
{
|
||
|
Precache( );
|
||
|
SET_MODEL(ENT(pev), "models/w_rpgammo.mdl");
|
||
|
CBasePlayerAmmo::Spawn( );
|
||
|
}
|
||
|
void Precache( void )
|
||
|
{
|
||
|
PRECACHE_MODEL ("models/w_rpgammo.mdl");
|
||
|
PRECACHE_SOUND("items/9mmclip1.wav");
|
||
|
}
|
||
|
BOOL AddAmmo( CBaseEntity *pOther )
|
||
|
{
|
||
|
int iGive;
|
||
|
|
||
|
if ( g_pGameRules->IsMultiplayer() )
|
||
|
{
|
||
|
// hand out more ammo per rocket in multiplayer.
|
||
|
iGive = AMMO_RPGCLIP_GIVE * 2;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
iGive = AMMO_RPGCLIP_GIVE;
|
||
|
}
|
||
|
|
||
|
if (pOther->GiveAmmo( iGive, "rockets", ROCKET_MAX_CARRY ) != -1)
|
||
|
{
|
||
|
EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/9mmclip1.wav", 1, ATTN_NORM);
|
||
|
return TRUE;
|
||
|
}
|
||
|
return FALSE;
|
||
|
}
|
||
|
};
|
||
|
LINK_ENTITY_TO_CLASS( ammo_rpgclip, CRpgAmmo );
|
||
|
|
||
|
#endif
|