You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
725 lines
17 KiB
725 lines
17 KiB
/*** |
|
* |
|
* Copyright (c) 1996-2001, 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 "extdll.h" |
|
#include "util.h" |
|
#include "cbase.h" |
|
#include "monsters.h" |
|
#include "weapons.h" |
|
#include "nodes.h" |
|
#include "player.h" |
|
#include "gamerules.h" |
|
|
|
#ifndef CLIENT_DLL |
|
#include "grapple_tonguetip.h" |
|
#endif |
|
|
|
LINK_ENTITY_TO_CLASS(weapon_grapple, CGrapple); |
|
|
|
enum bgrap_e { |
|
BGRAP_BREATHE = 0, |
|
BGRAP_LONGIDLE, |
|
BGRAP_SHORTIDLE, |
|
BGRAP_COUGH, |
|
BGRAP_DOWN, |
|
BGRAP_UP, |
|
BGRAP_FIRE, |
|
BGRAP_FIREWAITING, |
|
BGRAP_FIREREACHED, |
|
BGRAP_FIRETRAVEL, |
|
BGRAP_FIRERELEASE, |
|
}; |
|
|
|
//========================================================= |
|
// Purpose: |
|
//========================================================= |
|
void CGrapple::Spawn() |
|
{ |
|
Precache(); |
|
m_iId = WEAPON_GRAPPLE; |
|
SET_MODEL(ENT(pev), "models/w_bgrap.mdl"); |
|
m_iClip = -1; |
|
|
|
m_pBeam = NULL; |
|
m_pTongueTip = NULL; |
|
m_fTipHit = FALSE; |
|
m_fPlayPullSound = FALSE; |
|
|
|
m_iHitFlags = 0; |
|
m_iFirestate = FIRESTATE_NONE; |
|
|
|
m_flNextPullSoundTime = 0.0f; |
|
|
|
FallInit();// get ready to fall down. |
|
} |
|
|
|
//========================================================= |
|
// Purpose: |
|
//========================================================= |
|
void CGrapple::Precache(void) |
|
{ |
|
PRECACHE_MODEL("models/v_bgrap.mdl"); |
|
PRECACHE_MODEL("models/v_bgrap_tonguetip.mdl"); |
|
|
|
PRECACHE_MODEL("models/w_bgrap.mdl"); |
|
PRECACHE_MODEL("models/p_bgrap.mdl"); |
|
|
|
PRECACHE_SOUND("weapons/alienweap_draw.wav"); |
|
|
|
PRECACHE_SOUND("weapons/bgrapple_cough.wav"); |
|
PRECACHE_SOUND("weapons/bgrapple_fire.wav"); |
|
PRECACHE_SOUND("weapons/bgrapple_impact.wav"); |
|
PRECACHE_SOUND("weapons/bgrapple_pull.wav"); |
|
PRECACHE_SOUND("weapons/bgrapple_release.wav"); |
|
PRECACHE_SOUND("weapons/bgrapple_wait.wav"); |
|
|
|
PRECACHE_MODEL("sprites/smoke_blk.spr"); |
|
} |
|
|
|
//========================================================= |
|
// Purpose: |
|
//========================================================= |
|
int CGrapple::GetItemInfo(ItemInfo *p) |
|
{ |
|
p->pszName = STRING(pev->classname); |
|
p->pszAmmo1 = NULL; |
|
p->iMaxAmmo1 = -1; |
|
p->pszAmmo2 = NULL; |
|
p->iMaxAmmo2 = -1; |
|
p->iMaxClip = WEAPON_GRAPPLE; |
|
p->iSlot = 0; |
|
p->iPosition = 3; |
|
p->iId = WEAPON_GRAPPLE; |
|
p->iWeight = GRAPPLE_WEIGHT; |
|
return 1; |
|
} |
|
|
|
//========================================================= |
|
// Purpose: |
|
//========================================================= |
|
BOOL CGrapple::Deploy() |
|
{ |
|
m_flNextPrimaryAttack = m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 0.9f; |
|
m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 0.9f; |
|
|
|
return DefaultDeploy("models/v_bgrap.mdl", "models/p_bgrap.mdl", BGRAP_UP, "bgrap"); |
|
} |
|
|
|
//========================================================= |
|
// Purpose: |
|
//========================================================= |
|
void CGrapple::Holster(int skiplocal /* = 0 */) |
|
{ |
|
FireRelease(); |
|
|
|
m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.5; |
|
SendWeaponAnim(BGRAP_DOWN); |
|
} |
|
|
|
//========================================================= |
|
// Purpose: |
|
//========================================================= |
|
void CGrapple::PrimaryAttack(void) |
|
{ |
|
if (m_iFirestate != FIRESTATE_NONE) |
|
return; |
|
|
|
m_iFirestate = FIRESTATE_FIRE; |
|
|
|
m_flNextPrimaryAttack = m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + RANDOM_FLOAT(10, 15); |
|
m_flTimeWeaponIdle = UTIL_WeaponTimeBase(); |
|
} |
|
|
|
//========================================================= |
|
// Purpose: `` |
|
//========================================================= |
|
void CGrapple::ItemPostFrame(void) |
|
{ |
|
if (!(m_pPlayer->pev->button & IN_ATTACK)) |
|
{ |
|
m_flLastFireTime = 0.0f; |
|
} |
|
|
|
if (m_iFirestate != FIRESTATE_NONE) |
|
{ |
|
if (m_fTipHit) |
|
{ |
|
Pull(); |
|
} |
|
|
|
// Check if fire is still eligible. |
|
CheckFireEligibility(); |
|
|
|
// Check if the current tip is attached to a monster or a wall. |
|
// If the tongue tip move type is MOVETYPE_FOLLOW, then it |
|
// implies that we are targetting a monster, so it will kill |
|
// the monster when close enough. |
|
CheckTargetProximity(); |
|
|
|
// Update the tip velocity and position. |
|
UpdateTongueTip(); |
|
|
|
// Update the tongue beam. |
|
UpdateBeam(); |
|
|
|
// Update the pull sound. |
|
UpdatePullSound(); |
|
} |
|
|
|
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; |
|
} |
|
|
|
#ifndef CLIENT_DLL |
|
m_pPlayer->TabulateAmmo(); |
|
#endif |
|
PrimaryAttack(); |
|
m_pPlayer->pev->button &= ~IN_ATTACK; |
|
} |
|
else if (!(m_pPlayer->pev->button & (IN_ATTACK | IN_ATTACK2))) |
|
{ |
|
if (m_iFirestate != FIRESTATE_NONE) |
|
{ |
|
FireRelease(); |
|
} |
|
|
|
#ifndef CLIENT_DLL |
|
// 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; |
|
} |
|
} |
|
#endif |
|
|
|
WeaponIdle(); |
|
return; |
|
} |
|
|
|
// catch all |
|
if (ShouldWeaponIdle()) |
|
{ |
|
WeaponIdle(); |
|
} |
|
} |
|
|
|
//========================================================= |
|
// Purpose: |
|
//========================================================= |
|
void CGrapple::WeaponIdle(void) |
|
{ |
|
ResetEmptySound(); |
|
|
|
if (m_flTimeWeaponIdle > UTIL_WeaponTimeBase()) |
|
return; |
|
|
|
if (m_iFirestate != FIRESTATE_NONE) |
|
{ |
|
switch (m_iFirestate) |
|
{ |
|
case FIRESTATE_FIRE: |
|
Fire(); |
|
break; |
|
|
|
case FIRESTATE_FIRE2: |
|
Fire2(); |
|
break; |
|
|
|
case FIRESTATE_WAIT: |
|
FireWait(); |
|
break; |
|
|
|
case FIRESTATE_REACH: |
|
FireReach(); |
|
break; |
|
|
|
case FIRESTATE_TRAVEL: |
|
FireTravel(); |
|
break; |
|
|
|
case FIRESTATE_RELEASE: |
|
FireRelease(); |
|
break; |
|
|
|
default: |
|
break; |
|
} |
|
return; |
|
} |
|
|
|
int iAnim; |
|
float flRand = UTIL_SharedRandomFloat(m_pPlayer->random_seed, 0, 1); |
|
if (flRand <= 0.8f) |
|
{ |
|
iAnim = BGRAP_BREATHE; |
|
m_flTimeWeaponIdle = 2.6f; |
|
} |
|
else if (flRand <= 0.7f) |
|
{ |
|
iAnim = BGRAP_LONGIDLE; |
|
m_flTimeWeaponIdle = 10.0f; |
|
} |
|
else if (flRand <= 0.95) |
|
{ |
|
iAnim = BGRAP_SHORTIDLE; |
|
m_flTimeWeaponIdle = 1.3f; |
|
} |
|
else |
|
{ |
|
iAnim = BGRAP_COUGH; |
|
m_flTimeWeaponIdle = 4.6f; |
|
|
|
EMIT_SOUND(ENT(pev), CHAN_BODY, "weapons/bgrapple_cough.wav", 1, ATTN_NORM); |
|
} |
|
|
|
SendWeaponAnim(iAnim, UseDecrement()); |
|
} |
|
|
|
//========================================================= |
|
// Purpose: |
|
//========================================================= |
|
void CGrapple::Fire(void) |
|
{ |
|
EMIT_SOUND(ENT(pev), CHAN_BODY, "weapons/bgrapple_fire.wav", 1, ATTN_NORM); |
|
|
|
SendWeaponAnim(BGRAP_FIRE, UseDecrement()); |
|
m_iFirestate = FIRESTATE_FIRE2; |
|
|
|
m_flNextPrimaryAttack = m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + RANDOM_FLOAT(15, 20); |
|
m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 0.45f; |
|
} |
|
|
|
//========================================================= |
|
// Purpose: |
|
//========================================================= |
|
void CGrapple::Fire2(void) |
|
{ |
|
// Create the tongue tip. |
|
CreateTongueTip(); |
|
|
|
// Start the pull sound. |
|
StartPullSound(); |
|
|
|
m_iFirestate = FIRESTATE_WAIT; |
|
|
|
m_flNextPrimaryAttack = m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + RANDOM_FLOAT(15, 20); |
|
m_flTimeWeaponIdle = UTIL_WeaponTimeBase(); |
|
} |
|
|
|
//========================================================= |
|
// Purpose: |
|
//========================================================= |
|
void CGrapple::FireWait(void) |
|
{ |
|
SendWeaponAnim(BGRAP_FIREWAITING, UseDecrement()); |
|
|
|
m_flNextPrimaryAttack = m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + RANDOM_FLOAT(15, 20); |
|
m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 0.5f; |
|
} |
|
|
|
//========================================================= |
|
// Purpose: |
|
//========================================================= |
|
void CGrapple::FireReach(void) |
|
{ |
|
SendWeaponAnim(BGRAP_FIREREACHED, UseDecrement()); |
|
m_iFirestate = FIRESTATE_TRAVEL; |
|
|
|
// Start pulling the owner toward tongue tip. |
|
StartPull(); |
|
|
|
m_flNextPrimaryAttack = m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + RANDOM_FLOAT(15, 20); |
|
//m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 4.7; |
|
m_flTimeWeaponIdle = UTIL_WeaponTimeBase(); |
|
} |
|
|
|
//========================================================= |
|
// Purpose: |
|
//========================================================= |
|
void CGrapple::FireTravel(void) |
|
{ |
|
SendWeaponAnim(BGRAP_FIRETRAVEL, UseDecrement()); |
|
|
|
m_flNextPrimaryAttack = m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + RANDOM_FLOAT(15, 20); |
|
m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 0.6f; |
|
} |
|
|
|
//========================================================= |
|
// Purpose: |
|
//========================================================= |
|
void CGrapple::FireRelease(void) |
|
{ |
|
// Stop pull sound. |
|
// STOP_SOUND(ENT(pev), CHAN_VOICE, "weapons/bgrapple_pull.wav"); |
|
|
|
// Play release sound. |
|
EMIT_SOUND(ENT(pev), CHAN_BODY, "weapons/bgrapple_release.wav", 1, ATTN_NORM); |
|
|
|
SendWeaponAnim(BGRAP_FIRERELEASE, UseDecrement()); |
|
m_iFirestate = FIRESTATE_NONE; |
|
|
|
// Stop the pulling. |
|
StopPull(); |
|
|
|
// Reset pull sound. |
|
ResetPullSound(); |
|
|
|
// Destroy the tongue tip. |
|
DestroyTongueTip(); |
|
|
|
m_fTipHit = FALSE; |
|
m_iHitFlags = 0; |
|
|
|
m_flNextPrimaryAttack = m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 0.75f; |
|
m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 0.1; |
|
} |
|
|
|
//========================================================= |
|
// Purpose: |
|
//========================================================= |
|
void CGrapple::OnTongueTipHitSurface(const Vector& vecTarget) |
|
{ |
|
#ifndef CLIENT_DLL |
|
ALERT(at_console, "Hit surface at: (%f,%f,%f).\n", vecTarget.x, vecTarget.y, vecTarget.z); |
|
#endif |
|
FireReach(); |
|
} |
|
|
|
//========================================================= |
|
// Purpose: |
|
//========================================================= |
|
void CGrapple::OnTongueTipHitEntity(CBaseEntity* pEntity) |
|
{ |
|
#ifndef CLIENT_DLL |
|
ALERT(at_console, "Hit entity with classname %s.\n", (char*)STRING(pEntity->pev->classname)); |
|
#endif |
|
FireReach(); |
|
} |
|
//========================================================= |
|
// Purpose: |
|
//========================================================= |
|
void CGrapple::StartPull(void) |
|
{ |
|
#ifndef CLIENT_DLL |
|
Vector vecOrigin = m_pPlayer->pev->origin; |
|
|
|
vecOrigin.z++; |
|
UTIL_SetOrigin(m_pPlayer->pev, vecOrigin); |
|
|
|
m_pPlayer->pev->movetype = MOVETYPE_FLY; |
|
m_pPlayer->pev->gravity = 0.0f; |
|
|
|
m_pPlayer->pev->flags &= ~FL_ONGROUND; |
|
|
|
m_pPlayer->m_afPhysicsFlags |= PFLAG_LATCHING; |
|
#endif |
|
} |
|
|
|
//========================================================= |
|
// Purpose: |
|
//========================================================= |
|
void CGrapple::StopPull(void) |
|
{ |
|
#ifndef CLIENT_DLL |
|
m_pPlayer->pev->movetype = MOVETYPE_WALK; |
|
m_pPlayer->pev->gravity = 1.0f; |
|
|
|
m_pPlayer->m_afPhysicsFlags &= ~PFLAG_LATCHING; |
|
#endif |
|
} |
|
|
|
//========================================================= |
|
// Purpose: |
|
//========================================================= |
|
void CGrapple::Pull( void ) |
|
{ |
|
#ifndef CLIENT_DLL |
|
|
|
Vector vecOwnerPos, vecTipPos, vecDirToTip; |
|
vecOwnerPos = m_pPlayer->pev->origin; |
|
vecTipPos = m_pTongueTip->pev->origin; |
|
vecDirToTip = (vecTipPos - vecOwnerPos).Normalize(); |
|
|
|
m_pPlayer->pev->velocity = vecDirToTip * 300; |
|
|
|
m_pPlayer->m_afPhysicsFlags |= PFLAG_LATCHING; |
|
#endif |
|
} |
|
|
|
//========================================================= |
|
// Purpose: |
|
//========================================================= |
|
void CGrapple::CreateTongueTip( void ) |
|
{ |
|
#ifndef CLIENT_DLL |
|
Vector vecSrc, vecAiming, vecVel; |
|
UTIL_MakeVectors( m_pPlayer->pev->v_angle ); |
|
|
|
vecSrc = m_pPlayer->GetGunPosition(); |
|
vecSrc = vecSrc + gpGlobals->v_forward * 8; |
|
vecSrc = vecSrc + gpGlobals->v_right * 8; |
|
vecSrc = vecSrc + gpGlobals->v_up; |
|
|
|
vecVel = gpGlobals->v_forward * 300; |
|
|
|
//GetAttachment(0, vecSrc, vecAiming); |
|
|
|
m_pTongueTip = CGrappleTonguetip::CreateTip(pev, vecSrc, vecVel); |
|
|
|
CreateBeam(m_pTongueTip); |
|
#endif |
|
|
|
} |
|
|
|
//========================================================= |
|
// Purpose: |
|
//========================================================= |
|
void CGrapple::DestroyTongueTip(void) |
|
{ |
|
#ifndef CLIENT_DLL |
|
DestroyBeam(); |
|
|
|
if (m_pTongueTip) |
|
{ |
|
UTIL_Remove(m_pTongueTip); |
|
m_pTongueTip = NULL; |
|
} |
|
#endif |
|
} |
|
|
|
//========================================================= |
|
// Purpose: |
|
//========================================================= |
|
void CGrapple::UpdateTongueTip(void) |
|
{ |
|
#ifndef CLIENT_DLL |
|
if (m_pTongueTip) |
|
{ |
|
Vector tipVel = m_pTongueTip->pev->velocity; |
|
Vector ownerVel = VARS(pev->owner)->velocity; |
|
|
|
Vector newVel; |
|
|
|
newVel.x = tipVel.x + (ownerVel.x * 0.02f); |
|
newVel.y = tipVel.y + (ownerVel.y * 0.05f); |
|
newVel.z = tipVel.z + (ownerVel.z * 0.01f); |
|
|
|
m_pTongueTip->pev->velocity = newVel; |
|
} |
|
#endif |
|
} |
|
|
|
//========================================================= |
|
// Purpose: |
|
//========================================================= |
|
void CGrapple::CreateBeam( CBaseEntity* pTongueTip ) |
|
{ |
|
#ifndef CLIENT_DLL |
|
m_pBeam = CBeam::BeamCreate("sprites/smoke_blk.spr", 16); |
|
|
|
if (m_pBeam) |
|
{ |
|
m_pBeam->PointsInit(pTongueTip->pev->origin, pev->origin); |
|
m_pBeam->SetColor(255, 255, 255); |
|
m_pBeam->SetBrightness(255); |
|
m_pBeam->SetNoise(0); |
|
m_pBeam->SetFrame(0); |
|
m_pBeam->pev->rendermode = kRenderNormal; |
|
m_pBeam->pev->renderamt = 255; |
|
} |
|
#endif |
|
} |
|
|
|
//========================================================= |
|
// Purpose: |
|
//========================================================= |
|
void CGrapple::DestroyBeam(void) |
|
{ |
|
#ifndef CLIENT_DLL |
|
if (m_pBeam) |
|
{ |
|
UTIL_Remove(m_pBeam); |
|
m_pBeam = NULL; |
|
} |
|
#endif |
|
} |
|
|
|
//========================================================= |
|
// Purpose: |
|
//========================================================= |
|
void CGrapple::UpdateBeam(void) |
|
{ |
|
#ifndef CLIENT_DLL |
|
if (m_pBeam) |
|
{ |
|
Vector vecSrc; |
|
UTIL_MakeVectors(m_pPlayer->pev->v_angle); |
|
|
|
vecSrc = m_pPlayer->GetGunPosition(); |
|
vecSrc = vecSrc + gpGlobals->v_forward * 16; |
|
vecSrc = vecSrc + gpGlobals->v_right * 6; |
|
vecSrc = vecSrc + gpGlobals->v_up * -8; |
|
m_pBeam->SetStartPos(vecSrc); |
|
m_pBeam->SetEndPos(m_pTongueTip->pev->origin); |
|
m_pBeam->RelinkBeam(); |
|
} |
|
#endif |
|
} |
|
|
|
//========================================================= |
|
// Purpose: |
|
//========================================================= |
|
BOOL CGrapple::CanAttack(float attack_time, float curtime, BOOL isPredicted) |
|
{ |
|
#if defined( CLIENT_WEAPONS ) |
|
if (!isPredicted) |
|
#else |
|
if (1) |
|
#endif |
|
{ |
|
return (attack_time <= curtime) ? TRUE : FALSE; |
|
} |
|
else |
|
{ |
|
return (attack_time <= 0.0) ? TRUE : FALSE; |
|
} |
|
} |
|
|
|
|
|
void CGrapple::StartPullSound(void) |
|
{ |
|
m_flNextPullSoundTime = UTIL_WeaponTimeBase(); |
|
m_fPlayPullSound = TRUE; |
|
} |
|
|
|
void CGrapple::UpdatePullSound(void) |
|
{ |
|
if (!m_fPlayPullSound) |
|
return; |
|
|
|
if (m_flNextPullSoundTime <= UTIL_WeaponTimeBase()) |
|
{ |
|
EMIT_SOUND(ENT(pev), CHAN_BODY, "weapons/bgrapple_pull.wav", 1, ATTN_NORM); |
|
m_flNextPullSoundTime = UTIL_WeaponTimeBase() + 0.6f; |
|
} |
|
} |
|
|
|
void CGrapple::ResetPullSound(void) |
|
{ |
|
STOP_SOUND(ENT(pev), CHAN_BODY, "weapons/bgrapple_pull.wav"); |
|
m_flNextPullSoundTime = 0.0f; |
|
m_fPlayPullSound = FALSE; |
|
} |
|
|
|
BOOL CGrapple::IsTongueColliding(const Vector& vecShootOrigin, const Vector& vecTipPos) |
|
{ |
|
#ifndef CLIENT_DLL |
|
TraceResult tr; |
|
UTIL_TraceLine(vecShootOrigin, vecTipPos, dont_ignore_monsters, ENT(m_pPlayer->pev), &tr); |
|
|
|
if (tr.flFraction != 1.0) |
|
{ |
|
if (m_pTongueTip->pev->movetype == MOVETYPE_FOLLOW) |
|
{ |
|
if (tr.pHit && m_pTongueTip->pev->aiment && m_pTongueTip->pev->aiment == tr.pHit) |
|
return FALSE; |
|
} |
|
|
|
return TRUE; |
|
} |
|
|
|
return FALSE; |
|
#else |
|
return TRUE; |
|
#endif |
|
} |
|
|
|
void CGrapple::CheckFireEligibility(void) |
|
{ |
|
#ifndef CLIENT_DLL |
|
// Do not check for this if the tongue is not valid. |
|
if (!m_pTongueTip) |
|
return; |
|
|
|
// Check if the tongue is through walls or if other things |
|
// such as entities are between the owner and the tip. |
|
if (IsTongueColliding(m_pPlayer->GetGunPosition(), m_pTongueTip->pev->origin)) |
|
{ |
|
// Stop firing! |
|
FireRelease(); |
|
} |
|
#endif |
|
} |
|
|
|
BOOL CGrapple::CheckTargetProximity(void) |
|
{ |
|
#ifndef CLIENT_DLL |
|
// Do not check for this if the tongue is not valid. |
|
if (!m_pTongueTip) |
|
return FALSE; |
|
|
|
if (m_pTongueTip->pev->movetype == MOVETYPE_FOLLOW) |
|
{ |
|
// Trace a hull around to see if we hit something within 20 units. |
|
TraceResult tr; |
|
UTIL_TraceHull( |
|
pev->origin, |
|
pev->origin + VARS(pev->owner)->velocity.Normalize() * 20, |
|
dont_ignore_monsters, |
|
head_hull, |
|
edict(), |
|
&tr); |
|
|
|
|
|
// Check to see if we are close enough to our target. |
|
// In the case o a monster, attempt to get a pointer to |
|
// the entity, gib it, release grappler fire. |
|
edict_t* pHit = ENT(tr.pHit); |
|
if ( pHit && |
|
(VARS(pHit)->flags & FL_MONSTER) && |
|
(m_pTongueTip->pev->aiment == tr.pHit)) |
|
{ |
|
// Get a pointer to the entity. |
|
CBaseEntity* pEnt = GetClassPtr((CBaseEntity*)VARS(tr.pHit)); |
|
if (pEnt) |
|
{ |
|
// Since FL_MONSTER is set, it can only be a monster entity. |
|
ASSERT(pEnt->MyMonsterPointer() != NULL); |
|
|
|
// Gib monster. |
|
((CBaseMonster*)pEnt)->GibMonster(); |
|
} |
|
else |
|
{ |
|
ALERT(at_console, "ERROR: %s attempted to reference an unlisted monster.\n", STRING(m_pTongueTip->pev->classname)); |
|
} |
|
|
|
// Release fire. |
|
FireRelease(); |
|
} |
|
} |
|
#endif |
|
return FALSE; |
|
} |