mirror of
https://github.com/nillerusr/source-engine.git
synced 2026-08-08 01:39:36 +00:00
add hl1,portal,dod source code
This commit is contained in:
@@ -0,0 +1,215 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
#include "cbase.h"
|
||||
#include "hl1_ai_basenpc.h"
|
||||
#include "scripted.h"
|
||||
#include "soundent.h"
|
||||
#include "animation.h"
|
||||
#include "entitylist.h"
|
||||
#include "ai_navigator.h"
|
||||
#include "ai_motor.h"
|
||||
#include "player.h"
|
||||
#include "vstdlib/random.h"
|
||||
#include "engine/IEngineSound.h"
|
||||
#include "npcevent.h"
|
||||
|
||||
#include "effect_dispatch_data.h"
|
||||
#include "te_effect_dispatch.h"
|
||||
#include "cplane.h"
|
||||
#include "ai_squad.h"
|
||||
|
||||
#define HUMAN_GIBS 1
|
||||
#define ALIEN_GIBS 2
|
||||
|
||||
//=========================================================
|
||||
// NoFriendlyFire - checks for possibility of friendly fire
|
||||
//
|
||||
// Builds a large box in front of the grunt and checks to see
|
||||
// if any squad members are in that box.
|
||||
//=========================================================
|
||||
bool CHL1BaseNPC::NoFriendlyFire( void )
|
||||
{
|
||||
if ( !m_pSquad )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
CPlane backPlane;
|
||||
CPlane leftPlane;
|
||||
CPlane rightPlane;
|
||||
|
||||
Vector vecLeftSide;
|
||||
Vector vecRightSide;
|
||||
Vector v_left;
|
||||
|
||||
Vector vForward, vRight, vUp;
|
||||
QAngle vAngleToEnemy;
|
||||
|
||||
if ( GetEnemy() != NULL )
|
||||
{
|
||||
//!!!BUGBUG - to fix this, the planes must be aligned to where the monster will be firing its gun, not the direction it is facing!!!
|
||||
VectorAngles( ( GetEnemy()->WorldSpaceCenter() - GetAbsOrigin() ), vAngleToEnemy );
|
||||
|
||||
AngleVectors ( vAngleToEnemy, &vForward, &vRight, &vUp );
|
||||
}
|
||||
else
|
||||
{
|
||||
// if there's no enemy, pretend there's a friendly in the way, so the grunt won't shoot.
|
||||
return false;
|
||||
}
|
||||
|
||||
vecLeftSide = GetAbsOrigin() - ( vRight * ( WorldAlignSize().x * 1.5 ) );
|
||||
vecRightSide = GetAbsOrigin() + ( vRight * ( WorldAlignSize().x * 1.5 ) );
|
||||
v_left = vRight * -1;
|
||||
|
||||
leftPlane.InitializePlane ( vRight, vecLeftSide );
|
||||
rightPlane.InitializePlane ( v_left, vecRightSide );
|
||||
backPlane.InitializePlane ( vForward, GetAbsOrigin() );
|
||||
|
||||
AISquadIter_t iter;
|
||||
for ( CAI_BaseNPC *pSquadMember = m_pSquad->GetFirstMember( &iter ); pSquadMember; pSquadMember = m_pSquad->GetNextMember( &iter ) )
|
||||
{
|
||||
if ( pSquadMember == NULL )
|
||||
continue;
|
||||
|
||||
if ( pSquadMember == this )
|
||||
continue;
|
||||
|
||||
if ( backPlane.PointInFront ( pSquadMember->GetAbsOrigin() ) &&
|
||||
leftPlane.PointInFront ( pSquadMember->GetAbsOrigin() ) &&
|
||||
rightPlane.PointInFront ( pSquadMember->GetAbsOrigin()) )
|
||||
{
|
||||
// this guy is in the check volume! Don't shoot!
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CHL1BaseNPC::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
|
||||
{
|
||||
if ( info.GetDamage() >= 1.0 && !(info.GetDamageType() & DMG_SHOCK ) )
|
||||
{
|
||||
UTIL_BloodSpray( ptr->endpos, vecDir, BloodColor(), 4, FX_BLOODSPRAY_ALL );
|
||||
}
|
||||
|
||||
BaseClass::TraceAttack( info, vecDir, ptr, pAccumulator );
|
||||
}
|
||||
|
||||
|
||||
bool CHL1BaseNPC::ShouldGib( const CTakeDamageInfo &info )
|
||||
{
|
||||
if ( info.GetDamageType() & DMG_NEVERGIB )
|
||||
return false;
|
||||
|
||||
if ( ( g_pGameRules->Damage_ShouldGibCorpse( info.GetDamageType() ) && m_iHealth < GIB_HEALTH_VALUE ) || ( info.GetDamageType() & DMG_ALWAYSGIB ) )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
bool CHL1BaseNPC::HasHumanGibs( void )
|
||||
{
|
||||
Class_T myClass = Classify();
|
||||
|
||||
if ( myClass == CLASS_HUMAN_MILITARY ||
|
||||
myClass == CLASS_PLAYER_ALLY ||
|
||||
myClass == CLASS_HUMAN_PASSIVE ||
|
||||
myClass == CLASS_PLAYER )
|
||||
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool CHL1BaseNPC::HasAlienGibs( void )
|
||||
{
|
||||
Class_T myClass = Classify();
|
||||
|
||||
if ( myClass == CLASS_ALIEN_MILITARY ||
|
||||
myClass == CLASS_ALIEN_MONSTER ||
|
||||
myClass == CLASS_INSECT ||
|
||||
myClass == CLASS_ALIEN_PREDATOR ||
|
||||
myClass == CLASS_ALIEN_PREY )
|
||||
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void CHL1BaseNPC::Precache( void )
|
||||
{
|
||||
PrecacheModel( "models/gibs/agibs.mdl" );
|
||||
PrecacheModel( "models/gibs/hgibs.mdl" );
|
||||
|
||||
BaseClass::Precache();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Output : Returns true on success, false on failure.
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CHL1BaseNPC::CorpseGib( const CTakeDamageInfo &info )
|
||||
{
|
||||
CEffectData data;
|
||||
|
||||
data.m_vOrigin = WorldSpaceCenter();
|
||||
data.m_vNormal = data.m_vOrigin - info.GetDamagePosition();
|
||||
VectorNormalize( data.m_vNormal );
|
||||
|
||||
data.m_flScale = RemapVal( m_iHealth, 0, -500, 1, 3 );
|
||||
data.m_flScale = clamp( data.m_flScale, 1, 3 );
|
||||
|
||||
if ( HasAlienGibs() )
|
||||
data.m_nMaterial = ALIEN_GIBS;
|
||||
else if ( HasHumanGibs() )
|
||||
data.m_nMaterial = HUMAN_GIBS;
|
||||
|
||||
data.m_nColor = BloodColor();
|
||||
|
||||
DispatchEffect( "HL1Gib", data );
|
||||
|
||||
CSoundEnt::InsertSound( SOUND_MEAT, GetAbsOrigin(), 256, 0.5f, this );
|
||||
|
||||
/// BaseClass::CorpseGib( info );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int CHL1BaseNPC::IRelationPriority( CBaseEntity *pTarget )
|
||||
{
|
||||
return BaseClass::IRelationPriority( pTarget );
|
||||
}
|
||||
|
||||
void CHL1BaseNPC::EjectShell( const Vector &vecOrigin, const Vector &vecVelocity, float rotation, int iType )
|
||||
{
|
||||
CEffectData data;
|
||||
data.m_vStart = vecVelocity;
|
||||
data.m_vOrigin = vecOrigin;
|
||||
data.m_vAngles = QAngle( 0, rotation, 0 );
|
||||
data.m_fFlags = iType;
|
||||
|
||||
DispatchEffect( "HL1ShellEject", data );
|
||||
}
|
||||
|
||||
// HL1 version - never return Ragdoll as the automatic schedule at the end of a
|
||||
// scripted sequence
|
||||
int CHL1BaseNPC::SelectDeadSchedule()
|
||||
{
|
||||
// Alread dead (by animation event maybe?)
|
||||
// Is it safe to set it to SCHED_NONE?
|
||||
if ( m_lifeState == LIFE_DEAD )
|
||||
return SCHED_NONE;
|
||||
|
||||
CleanupOnDeath();
|
||||
return SCHED_DIE;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Base combat character with no AI
|
||||
//
|
||||
// $Workfile: $
|
||||
// $Date: $
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef HL1_AI_BASENPC_H
|
||||
#define HL1_AI_BASENPC_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
#include "ai_basenpc.h"
|
||||
#include "ai_motor.h"
|
||||
//=============================================================================
|
||||
// >> CHL1NPCTalker
|
||||
//=============================================================================
|
||||
|
||||
class CHL1BaseNPC : public CAI_BaseNPC
|
||||
{
|
||||
DECLARE_CLASS( CHL1BaseNPC, CAI_BaseNPC );
|
||||
|
||||
public:
|
||||
CHL1BaseNPC( void )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator );
|
||||
bool ShouldGib( const CTakeDamageInfo &info );
|
||||
bool CorpseGib( const CTakeDamageInfo &info );
|
||||
|
||||
bool HasAlienGibs( void );
|
||||
bool HasHumanGibs( void );
|
||||
|
||||
void Precache( void );
|
||||
|
||||
int IRelationPriority( CBaseEntity *pTarget );
|
||||
bool NoFriendlyFire( void );
|
||||
|
||||
void EjectShell( const Vector &vecOrigin, const Vector &vecVelocity, float rotation, int iType );
|
||||
|
||||
virtual int SelectDeadSchedule();
|
||||
};
|
||||
|
||||
#endif //HL1_AI_BASENPC_H
|
||||
@@ -0,0 +1,86 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
|
||||
#include "cbase.h"
|
||||
#include "hl1_basecombatweapon_shared.h"
|
||||
#include "effect_dispatch_data.h"
|
||||
#include "te_effect_dispatch.h"
|
||||
|
||||
|
||||
BEGIN_DATADESC( CBaseHL1CombatWeapon )
|
||||
DEFINE_THINKFUNC( FallThink ),
|
||||
END_DATADESC();
|
||||
|
||||
|
||||
void CBaseHL1CombatWeapon::Precache()
|
||||
{
|
||||
BaseClass::Precache();
|
||||
|
||||
PrecacheScriptSound( "BaseCombatWeapon.WeaponDrop" );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CBaseHL1CombatWeapon::FallInit( void )
|
||||
{
|
||||
SetModel( GetWorldModel() );
|
||||
SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE );
|
||||
SetSolid( SOLID_BBOX );
|
||||
AddSolidFlags( FSOLID_TRIGGER );
|
||||
AddSolidFlags( FSOLID_NOT_SOLID );
|
||||
|
||||
SetPickupTouch();
|
||||
|
||||
SetThink( &CBaseHL1CombatWeapon::FallThink );
|
||||
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
|
||||
// HACKHACK - On ground isn't always set, so look for ground underneath
|
||||
trace_t tr;
|
||||
UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() - Vector(0,0,2), MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr );
|
||||
|
||||
if ( tr.fraction < 1.0 )
|
||||
{
|
||||
SetGroundEntity( tr.m_pEnt );
|
||||
}
|
||||
|
||||
SetViewOffset( Vector(0,0,8) );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Items that have just spawned run this think to catch them when
|
||||
// they hit the ground. Once we're sure that the object is grounded,
|
||||
// we change its solid type to trigger and set it in a large box that
|
||||
// helps the player get it.
|
||||
//-----------------------------------------------------------------------------
|
||||
void CBaseHL1CombatWeapon::FallThink ( void )
|
||||
{
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
|
||||
if ( GetFlags() & FL_ONGROUND )
|
||||
{
|
||||
// clatter if we have an owner (i.e., dropped by someone)
|
||||
// don't clatter if the gun is waiting to respawn (if it's waiting, it is invisible!)
|
||||
if ( GetOwnerEntity() )
|
||||
{
|
||||
EmitSound( "BaseCombatWeapon.WeaponDrop" );
|
||||
}
|
||||
|
||||
// lie flat
|
||||
QAngle ang = GetAbsAngles();
|
||||
ang.x = 0;
|
||||
ang.z = 0;
|
||||
SetAbsAngles( ang );
|
||||
|
||||
Materialize();
|
||||
|
||||
SetSize( Vector( -24, -24, 0 ), Vector( 24, 24, 16 ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
|
||||
#include "cbase.h"
|
||||
#include "decals.h"
|
||||
#include "basecombatcharacter.h"
|
||||
#include "shake.h"
|
||||
#include "engine/IEngineSound.h"
|
||||
#include "soundent.h"
|
||||
#include "entitylist.h"
|
||||
#include "hl1_basegrenade.h"
|
||||
|
||||
|
||||
extern short g_sModelIndexFireball; // (in combatweapon.cpp) holds the index for the fireball
|
||||
extern short g_sModelIndexWExplosion; // (in combatweapon.cpp) holds the index for the underwater explosion
|
||||
|
||||
unsigned int CHL1BaseGrenade::PhysicsSolidMaskForEntity( void ) const
|
||||
{
|
||||
return BaseClass::PhysicsSolidMaskForEntity() | CONTENTS_HITBOX;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CHL1BaseGrenade::Precache()
|
||||
{
|
||||
BaseClass::Precache();
|
||||
|
||||
PrecacheScriptSound( "BaseGrenade.Explode" );
|
||||
}
|
||||
|
||||
|
||||
void CHL1BaseGrenade::Explode( trace_t *pTrace, int bitsDamageType )
|
||||
{
|
||||
float flRndSound;// sound randomizer
|
||||
|
||||
SetModelName( NULL_STRING );//invisible
|
||||
AddSolidFlags( FSOLID_NOT_SOLID );
|
||||
|
||||
m_takedamage = DAMAGE_NO;
|
||||
|
||||
// Pull out of the wall a bit
|
||||
if ( pTrace->fraction != 1.0 )
|
||||
{
|
||||
SetLocalOrigin( pTrace->endpos + (pTrace->plane.normal * 0.6) );
|
||||
}
|
||||
|
||||
Vector vecAbsOrigin = GetAbsOrigin();
|
||||
int contents = UTIL_PointContents ( vecAbsOrigin );
|
||||
|
||||
if ( pTrace->fraction != 1.0 )
|
||||
{
|
||||
Vector vecNormal = pTrace->plane.normal;
|
||||
const surfacedata_t *pdata = physprops->GetSurfaceData( pTrace->surface.surfaceProps );
|
||||
CPASFilter filter( vecAbsOrigin );
|
||||
te->Explosion( filter, 0.0,
|
||||
&vecAbsOrigin,
|
||||
!( contents & MASK_WATER ) ? g_sModelIndexFireball : g_sModelIndexWExplosion,
|
||||
m_DmgRadius * .03,
|
||||
25,
|
||||
TE_EXPLFLAG_NONE,
|
||||
m_DmgRadius,
|
||||
m_flDamage,
|
||||
&vecNormal,
|
||||
(char) pdata->game.material );
|
||||
}
|
||||
else
|
||||
{
|
||||
CPASFilter filter( vecAbsOrigin );
|
||||
te->Explosion( filter, 0.0,
|
||||
&vecAbsOrigin,
|
||||
!( contents & MASK_WATER ) ? g_sModelIndexFireball : g_sModelIndexWExplosion,
|
||||
m_DmgRadius * .03,
|
||||
25,
|
||||
TE_EXPLFLAG_NONE,
|
||||
m_DmgRadius,
|
||||
m_flDamage );
|
||||
}
|
||||
|
||||
CSoundEnt::InsertSound ( SOUND_COMBAT, GetAbsOrigin(), BASEGRENADE_EXPLOSION_VOLUME, 3.0 );
|
||||
|
||||
// Use the owner's position as the reported position
|
||||
Vector vecReported = GetThrower() ? GetThrower()->GetAbsOrigin() : vec3_origin;
|
||||
|
||||
CTakeDamageInfo info( this, GetThrower(), GetBlastForce(), GetAbsOrigin(), m_flDamage, bitsDamageType, 0, &vecReported );
|
||||
|
||||
RadiusDamage( info, GetAbsOrigin(), m_DmgRadius, CLASS_NONE, NULL );
|
||||
|
||||
UTIL_DecalTrace( pTrace, "Scorch" );
|
||||
|
||||
flRndSound = random->RandomFloat( 0 , 1 );
|
||||
|
||||
EmitSound( "BaseGrenade.Explode" );
|
||||
|
||||
SetTouch( NULL );
|
||||
|
||||
AddEffects( EF_NODRAW );
|
||||
SetAbsVelocity( vec3_origin );
|
||||
|
||||
SetThink( &CBaseGrenade::Smoke );
|
||||
SetNextThink( gpGlobals->curtime + 0.3);
|
||||
|
||||
if ( GetWaterLevel() == 0 )
|
||||
{
|
||||
int sparkCount = random->RandomInt( 0,3 );
|
||||
QAngle angles;
|
||||
VectorAngles( pTrace->plane.normal, angles );
|
||||
|
||||
for ( int i = 0; i < sparkCount; i++ )
|
||||
Create( "spark_shower", GetAbsOrigin(), angles, NULL );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef HL1_BASEGRENADE_H
|
||||
#define HL1_BASEGRENADE_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
#include "basegrenade_shared.h"
|
||||
|
||||
|
||||
class CHL1BaseGrenade : public CBaseGrenade
|
||||
{
|
||||
DECLARE_CLASS( CHL1BaseGrenade, CBaseGrenade );
|
||||
public:
|
||||
|
||||
virtual void Precache();
|
||||
|
||||
void Explode( trace_t *pTrace, int bitsDamageType );
|
||||
unsigned int PhysicsSolidMaskForEntity( void ) const;
|
||||
};
|
||||
|
||||
class CHandGrenade : public CHL1BaseGrenade
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CHandGrenade, CHL1BaseGrenade );
|
||||
DECLARE_DATADESC();
|
||||
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void BounceSound( void );
|
||||
void BounceTouch( CBaseEntity *pOther );
|
||||
|
||||
void ShootTimed( CBaseCombatCharacter *pOwner, Vector vecVelocity, float flTime );
|
||||
};
|
||||
|
||||
#endif // HL1_BASEGRENADE_H
|
||||
@@ -0,0 +1,199 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
/*
|
||||
|
||||
===== tf_client.cpp ========================================================
|
||||
|
||||
HL1 client/server game specific stuff
|
||||
|
||||
*/
|
||||
|
||||
#include "cbase.h"
|
||||
#include "hl1_player.h"
|
||||
#include "hl1mp_player.h"
|
||||
#include "hl1_gamerules.h"
|
||||
#include "gamerules.h"
|
||||
#include "teamplay_gamerules.h"
|
||||
#include "entitylist.h"
|
||||
#include "physics.h"
|
||||
#include "game.h"
|
||||
#include "player_resource.h"
|
||||
#include "engine/IEngineSound.h"
|
||||
|
||||
#include "tier0/vprof.h"
|
||||
|
||||
void Host_Say( edict_t *pEdict, bool teamonly );
|
||||
|
||||
extern CBaseEntity* FindPickerEntityClass( CBasePlayer *pPlayer, char *classname );
|
||||
extern bool g_fGameOver;
|
||||
|
||||
/*
|
||||
===========
|
||||
ClientPutInServer
|
||||
|
||||
called each time a player is spawned into the game
|
||||
============
|
||||
*/
|
||||
void ClientPutInServer( edict_t *pEdict, const char *playername )
|
||||
{
|
||||
CHL1_Player *pPlayer = NULL;
|
||||
|
||||
// Allocate a CBasePlayer for pev, and call spawn
|
||||
if ( g_pGameRules->IsMultiplayer() )
|
||||
pPlayer = CHL1_Player::CreatePlayer( "player_mp", pEdict );
|
||||
else
|
||||
pPlayer = CHL1_Player::CreatePlayer( "player", pEdict );
|
||||
|
||||
pPlayer->SetPlayerName( playername );
|
||||
}
|
||||
|
||||
|
||||
void ClientActive( edict_t *pEdict, bool bLoadGame )
|
||||
{
|
||||
CHL1_Player *pPlayer = dynamic_cast< CHL1_Player* >( CBaseEntity::Instance( pEdict ) );
|
||||
|
||||
pPlayer->InitialSpawn();
|
||||
|
||||
if ( !bLoadGame )
|
||||
{
|
||||
pPlayer->Spawn();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===============
|
||||
const char *GetGameDescription()
|
||||
|
||||
Returns the descriptive name of this .dll. E.g., Half-Life, or Team Fortress 2
|
||||
===============
|
||||
*/
|
||||
const char *GetGameDescription()
|
||||
{
|
||||
if ( g_pGameRules ) // this function may be called before the world has spawned, and the game rules initialized
|
||||
return g_pGameRules->GetGameDescription();
|
||||
else
|
||||
return "Half-Life 1";
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Given a player and optional name returns the entity of that
|
||||
// classname that the player is nearest facing
|
||||
//
|
||||
// Input :
|
||||
// Output :
|
||||
//-----------------------------------------------------------------------------
|
||||
CBaseEntity* FindEntity( edict_t *pEdict, char *classname)
|
||||
{
|
||||
// If no name was given set bits based on the picked
|
||||
if (FStrEq(classname,""))
|
||||
{
|
||||
return (FindPickerEntityClass( static_cast<CBasePlayer*>(GetContainingEntity(pEdict)), classname ));
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Precache game-specific models & sounds
|
||||
//-----------------------------------------------------------------------------
|
||||
void ClientGamePrecache( void )
|
||||
{
|
||||
// Multiplayer uses different models, and more of them.
|
||||
if ( g_pGameRules->IsMultiplayer() )
|
||||
{
|
||||
CBaseEntity::PrecacheModel("models/player/mp/barney/barney.mdl");
|
||||
CBaseEntity::PrecacheModel("models/player/mp/gina/gina.mdl");
|
||||
CBaseEntity::PrecacheModel("models/player/mp/gman/gman.mdl");
|
||||
CBaseEntity::PrecacheModel("models/player/mp/gordon/gordon.mdl");
|
||||
CBaseEntity::PrecacheModel("models/player/mp/helmet/helmet.mdl");
|
||||
CBaseEntity::PrecacheModel("models/player/mp/hgrunt/hgrunt.mdl");
|
||||
CBaseEntity::PrecacheModel("models/player/mp/robo/robo.mdl");
|
||||
CBaseEntity::PrecacheModel("models/player/mp/scientist/scientist.mdl");
|
||||
CBaseEntity::PrecacheModel("models/player/mp/zombie/zombie.mdl");
|
||||
CBaseEntity::PrecacheModel("models/player.mdl" );
|
||||
}
|
||||
else
|
||||
{
|
||||
CBaseEntity::PrecacheModel("models/player.mdl" );
|
||||
}
|
||||
|
||||
CBaseEntity::PrecacheModel( "models/gibs/agibs.mdl" );
|
||||
|
||||
CBaseEntity::PrecacheScriptSound( "Player.UseDeny" );
|
||||
}
|
||||
|
||||
|
||||
// called by ClientKill and DeadThink
|
||||
void respawn( CBaseEntity *pEdict, bool fCopyCorpse )
|
||||
{
|
||||
if (gpGlobals->coop || gpGlobals->deathmatch)
|
||||
{
|
||||
if ( fCopyCorpse )
|
||||
{
|
||||
// make a copy of the dead body for appearances sake
|
||||
((CHL1MP_Player *)pEdict)->CreateCorpse();
|
||||
}
|
||||
|
||||
// respawn player
|
||||
pEdict->Spawn();
|
||||
}
|
||||
else
|
||||
{ // restart the entire server
|
||||
engine->ServerCommand("reload\n");
|
||||
}
|
||||
}
|
||||
|
||||
void GameStartFrame( void )
|
||||
{
|
||||
VPROF("GameStartFrame()");
|
||||
|
||||
if ( g_fGameOver )
|
||||
return;
|
||||
|
||||
gpGlobals->teamplay = (teamplay.GetInt() != 0);
|
||||
|
||||
#ifdef DEBUG
|
||||
extern void Bot_RunAll();
|
||||
Bot_RunAll();
|
||||
#endif
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// instantiate the proper game rules object
|
||||
//=========================================================
|
||||
void InstallGameRules()
|
||||
{
|
||||
engine->ServerCommand( "exec game.cfg\n" );
|
||||
engine->ServerExecute( );
|
||||
|
||||
if ( !gpGlobals->deathmatch )
|
||||
{
|
||||
// generic half-life
|
||||
CreateGameRulesObject( "CHalfLife1" );
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
CreateGameRulesObject( "CHL1MPRules" );
|
||||
return;
|
||||
|
||||
if ( teamplay.GetInt() > 0 )
|
||||
{
|
||||
// teamplay
|
||||
CreateGameRulesObject( "CTeamplayRules" );
|
||||
return;
|
||||
}
|
||||
|
||||
// vanilla deathmatch
|
||||
CreateGameRulesObject( "CMultiplayRules" );
|
||||
return;
|
||||
}
|
||||
|
||||
CreateGameRulesObject( "CHalfLife1" );
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,73 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef HL1_ENTS_H
|
||||
#define HL1_ENTS_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
/**********************
|
||||
Pendulum
|
||||
/**********************/
|
||||
|
||||
class CPendulum : public CBaseToggle
|
||||
{
|
||||
DECLARE_CLASS( CPendulum, CBaseToggle );
|
||||
public:
|
||||
void Spawn ( void );
|
||||
void KeyValue( KeyValueData *pkvd );
|
||||
void Swing( void );
|
||||
void PendulumUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
void Stop( void );
|
||||
void Touch( CBaseEntity *pOther );
|
||||
void RopeTouch ( CBaseEntity *pOther );// this touch func makes the pendulum a rope
|
||||
void Blocked( CBaseEntity *pOther );
|
||||
|
||||
// Input handlers.
|
||||
void InputActivate( inputdata_t &inputdata );
|
||||
|
||||
DECLARE_DATADESC();
|
||||
|
||||
float m_flAccel; // Acceleration
|
||||
float m_flTime;
|
||||
float m_flDamp;
|
||||
float m_flMaxSpeed;
|
||||
float m_flDampSpeed;
|
||||
QAngle m_vCenter;
|
||||
QAngle m_vStart;
|
||||
float m_flBlockDamage;
|
||||
|
||||
EHANDLE m_hEnemy;
|
||||
};
|
||||
|
||||
class CHL1Gib : public CBaseEntity
|
||||
{
|
||||
DECLARE_CLASS( CHL1Gib, CBaseEntity );
|
||||
|
||||
public:
|
||||
void Spawn( const char *szGibModel );
|
||||
void BounceGibTouch ( CBaseEntity *pOther );
|
||||
void StickyGibTouch ( CBaseEntity *pOther );
|
||||
void WaitTillLand( void );
|
||||
void LimitVelocity( void );
|
||||
|
||||
virtual int ObjectCaps( void ) { return (CBaseEntity::ObjectCaps() & ~FCAP_ACROSS_TRANSITION) | FCAP_DONT_SAVE; }
|
||||
static void SpawnHeadGib( CBaseEntity *pVictim );
|
||||
static void SpawnRandomGibs( CBaseEntity *pVictim, int cGibs, int human );
|
||||
static void SpawnStickyGibs( CBaseEntity *pVictim, Vector vecOrigin, int cGibs );
|
||||
|
||||
int m_bloodColor;
|
||||
int m_cBloodDecals;
|
||||
int m_material;
|
||||
float m_lifeTime;
|
||||
|
||||
DECLARE_DATADESC();
|
||||
};
|
||||
|
||||
|
||||
#endif // HL1_ENTS_H
|
||||
@@ -0,0 +1,219 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "player.h"
|
||||
#include "mathlib/mathlib.h"
|
||||
#include "ai_speech.h"
|
||||
#include "stringregistry.h"
|
||||
#include "gamerules.h"
|
||||
#include "game.h"
|
||||
#include <ctype.h>
|
||||
#include "entitylist.h"
|
||||
#include "vstdlib/random.h"
|
||||
#include "engine/IEngineSound.h"
|
||||
#include "ndebugoverlay.h"
|
||||
#include "soundscape.h"
|
||||
|
||||
#define SPEAKER_START_SILENT 1 // wait for trigger 'on' to start announcements
|
||||
|
||||
// ===================================================================================
|
||||
//
|
||||
// Speaker class. Used for announcements per level, for door lock/unlock spoken voice.
|
||||
//
|
||||
|
||||
class CSpeaker : public CPointEntity
|
||||
{
|
||||
DECLARE_CLASS( CSpeaker, CPointEntity );
|
||||
public:
|
||||
bool KeyValue( const char *szKeyName, const char *szValue );
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void ToggleUse ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
void SpeakerThink( void );
|
||||
|
||||
virtual int ObjectCaps( void ) { return (CBaseEntity::ObjectCaps() & ~FCAP_ACROSS_TRANSITION); }
|
||||
|
||||
int m_preset; // preset number
|
||||
string_t m_iszMessage;
|
||||
|
||||
DECLARE_DATADESC();
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( speaker, CSpeaker );
|
||||
|
||||
BEGIN_DATADESC( CSpeaker )
|
||||
DEFINE_FIELD( m_preset, FIELD_INTEGER ),
|
||||
DEFINE_KEYFIELD( m_iszMessage, FIELD_STRING, "message" ),
|
||||
DEFINE_THINKFUNC( SpeakerThink ),
|
||||
DEFINE_USEFUNC( ToggleUse ),
|
||||
END_DATADESC()
|
||||
|
||||
//
|
||||
// ambient_generic - general-purpose user-defined static sound
|
||||
//
|
||||
void CSpeaker::Spawn( void )
|
||||
{
|
||||
char* szSoundFile = (char*) STRING( m_iszMessage );
|
||||
|
||||
if ( !m_preset && ( m_iszMessage == NULL_STRING || strlen( szSoundFile ) < 1 ) )
|
||||
{
|
||||
Msg( "SPEAKER with no Level/Sentence! at: %f, %f, %f\n", GetAbsOrigin().x, GetAbsOrigin().y, GetAbsOrigin().z );
|
||||
SetNextThink( gpGlobals->curtime + 0.1 );
|
||||
SetThink( &CSpeaker::SUB_Remove );
|
||||
return;
|
||||
}
|
||||
SetSolid( SOLID_NONE );
|
||||
SetMoveType( MOVETYPE_NONE );
|
||||
|
||||
|
||||
SetThink(&CSpeaker::SpeakerThink);
|
||||
SetNextThink( TICK_NEVER_THINK );
|
||||
|
||||
// allow on/off switching via 'use' function.
|
||||
SetUse ( &CSpeaker::ToggleUse );
|
||||
|
||||
Precache( );
|
||||
}
|
||||
|
||||
#define ANNOUNCE_MINUTES_MIN 0.25
|
||||
#define ANNOUNCE_MINUTES_MAX 2.25
|
||||
|
||||
void CSpeaker::Precache( void )
|
||||
{
|
||||
if ( !FBitSet ( GetSpawnFlags(), SPEAKER_START_SILENT ) )
|
||||
// set first announcement time for random n second
|
||||
SetNextThink( gpGlobals->curtime + random->RandomFloat( 5.0, 15.0 ) );
|
||||
}
|
||||
void CSpeaker::SpeakerThink( void )
|
||||
{
|
||||
char* szSoundFile = NULL;
|
||||
float flvolume = m_iHealth * 0.1;
|
||||
int flags = 0;
|
||||
int pitch = 100;
|
||||
|
||||
|
||||
// Wait for the talking characters to finish first.
|
||||
if ( !g_AIFriendliesTalkSemaphore.IsAvailable( this ) || !g_AIFoesTalkSemaphore.IsAvailable( this ) )
|
||||
{
|
||||
float releaseTime = MAX( g_AIFriendliesTalkSemaphore.GetReleaseTime(), g_AIFoesTalkSemaphore.GetReleaseTime() );
|
||||
SetNextThink( gpGlobals->curtime + releaseTime + random->RandomFloat( 5, 10 ) );
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_preset)
|
||||
{
|
||||
// go lookup preset text, assign szSoundFile
|
||||
switch (m_preset)
|
||||
{
|
||||
case 1: szSoundFile = "C1A0_"; break;
|
||||
case 2: szSoundFile = "C1A1_"; break;
|
||||
case 3: szSoundFile = "C1A2_"; break;
|
||||
case 4: szSoundFile = "C1A3_"; break;
|
||||
case 5: szSoundFile = "C1A4_"; break;
|
||||
case 6: szSoundFile = "C2A1_"; break;
|
||||
case 7: szSoundFile = "C2A2_"; break;
|
||||
case 8: szSoundFile = "C2A3_"; break;
|
||||
case 9: szSoundFile = "C2A4_"; break;
|
||||
case 10: szSoundFile = "C2A5_"; break;
|
||||
case 11: szSoundFile = "C3A1_"; break;
|
||||
case 12: szSoundFile = "C3A2_"; break;
|
||||
}
|
||||
} else
|
||||
szSoundFile = (char*) STRING( m_iszMessage );
|
||||
|
||||
if (szSoundFile[0] == '!')
|
||||
{
|
||||
// play single sentence, one shot
|
||||
UTIL_EmitAmbientSound ( GetSoundSourceIndex(), GetAbsOrigin(), szSoundFile,
|
||||
flvolume, SNDLVL_120dB, flags, pitch);
|
||||
|
||||
// shut off and reset
|
||||
SetNextThink( TICK_NEVER_THINK );
|
||||
}
|
||||
else
|
||||
{
|
||||
// make random announcement from sentence group
|
||||
|
||||
if ( SENTENCEG_PlayRndSz( edict(), szSoundFile, flvolume, SNDLVL_120dB, flags, pitch) < 0 )
|
||||
Msg( "Level Design Error!\nSPEAKER has bad sentence group name: %s\n",szSoundFile);
|
||||
|
||||
// set next announcement time for random 5 to 10 minute delay
|
||||
SetNextThink ( gpGlobals->curtime +
|
||||
random->RandomFloat( ANNOUNCE_MINUTES_MIN * 60.0, ANNOUNCE_MINUTES_MAX * 60.0 ) );
|
||||
|
||||
// time delay until it's ok to speak: used so that two NPCs don't talk at once
|
||||
g_AIFriendliesTalkSemaphore.Acquire( 5, this );
|
||||
g_AIFoesTalkSemaphore.Acquire( 5, this );
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// ToggleUse - if an announcement is pending, cancel it. If no announcement is pending, start one.
|
||||
//
|
||||
void CSpeaker::ToggleUse ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
int fActive = (GetNextThink() > 0.0);
|
||||
|
||||
// fActive is TRUE only if an announcement is pending
|
||||
|
||||
if ( useType != USE_TOGGLE )
|
||||
{
|
||||
// ignore if we're just turning something on that's already on, or
|
||||
// turning something off that's already off.
|
||||
if ( (fActive && useType == USE_ON) || (!fActive && useType == USE_OFF) )
|
||||
return;
|
||||
}
|
||||
|
||||
if ( useType == USE_ON )
|
||||
{
|
||||
// turn on announcements
|
||||
SetNextThink( gpGlobals->curtime + 0.1 );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( useType == USE_OFF )
|
||||
{
|
||||
// turn off announcements
|
||||
SetNextThink( TICK_NEVER_THINK );
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
// Toggle announcements
|
||||
|
||||
|
||||
if ( fActive )
|
||||
{
|
||||
// turn off announcements
|
||||
SetNextThink( TICK_NEVER_THINK );
|
||||
}
|
||||
else
|
||||
{
|
||||
// turn on announcements
|
||||
SetNextThink( gpGlobals->curtime + 0.1 );
|
||||
}
|
||||
}
|
||||
|
||||
// KeyValue - load keyvalue pairs into member data
|
||||
// NOTE: called BEFORE spawn!
|
||||
|
||||
bool CSpeaker::KeyValue( const char *szKeyName, const char *szValue )
|
||||
{
|
||||
// preset
|
||||
if (FStrEq(szKeyName, "preset"))
|
||||
{
|
||||
m_preset = atoi(szValue);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return BaseClass::KeyValue( szKeyName, szValue );
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
#include "cbase.h"
|
||||
#include "../EventLog.h"
|
||||
|
||||
class CHL1EventLog : public CEventLog
|
||||
{
|
||||
private:
|
||||
typedef CEventLog BaseClass;
|
||||
|
||||
public:
|
||||
virtual ~CHL1EventLog() {};
|
||||
|
||||
public:
|
||||
bool PrintEvent( IGameEvent * event ) // override virtual function
|
||||
{
|
||||
if ( BaseClass::PrintEvent( event ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( Q_strcmp(event->GetName(), "hl1_") == 0 )
|
||||
{
|
||||
return PrintHL1Event( event );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
bool PrintHL1Event( IGameEvent * event ) // print Mod specific logs
|
||||
{
|
||||
// const char * name = event->GetName() + Q_strlen("hl1_"); // remove prefix
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
CHL1EventLog g_HL1EventLog;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Singleton access
|
||||
//-----------------------------------------------------------------------------
|
||||
IGameSystem* GameLogSystem()
|
||||
{
|
||||
return &g_HL1EventLog;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,239 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
/*
|
||||
|
||||
===== h_battery.cpp ========================================================
|
||||
|
||||
battery-related code
|
||||
|
||||
*/
|
||||
|
||||
#include "cbase.h"
|
||||
#include "gamerules.h"
|
||||
#include "player.h"
|
||||
#include "engine/IEngineSound.h"
|
||||
#include "in_buttons.h"
|
||||
|
||||
ConVar sk_suitcharger( "sk_suitcharger","0" );
|
||||
#define HL1_MAX_ARMOR 100
|
||||
|
||||
class CRecharge : public CBaseToggle
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CRecharge, CBaseToggle );
|
||||
|
||||
void Spawn( );
|
||||
|
||||
virtual void Precache();
|
||||
|
||||
bool CreateVPhysics();
|
||||
void Off(void);
|
||||
void Recharge(void);
|
||||
bool KeyValue( const char *szKeyName, const char *szValue );
|
||||
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
virtual int ObjectCaps( void ) { return (BaseClass::ObjectCaps() | m_iCaps ); }
|
||||
|
||||
DECLARE_DATADESC();
|
||||
|
||||
float m_flNextCharge;
|
||||
int m_iReactivate ; // DeathMatch Delay until reactvated
|
||||
int m_iJuice;
|
||||
int m_iOn; // 0 = off, 1 = startup, 2 = going
|
||||
float m_flSoundTime;
|
||||
|
||||
int m_iCaps;
|
||||
|
||||
COutputFloat m_OutRemainingCharge;
|
||||
};
|
||||
|
||||
BEGIN_DATADESC( CRecharge )
|
||||
|
||||
DEFINE_FIELD( m_flNextCharge, FIELD_TIME ),
|
||||
DEFINE_FIELD( m_iReactivate, FIELD_INTEGER),
|
||||
DEFINE_FIELD( m_iJuice, FIELD_INTEGER),
|
||||
DEFINE_FIELD( m_iOn, FIELD_INTEGER),
|
||||
DEFINE_FIELD( m_flSoundTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( m_iCaps, FIELD_INTEGER ),
|
||||
|
||||
// Function Pointers
|
||||
DEFINE_FUNCTION( Off ),
|
||||
DEFINE_FUNCTION( Recharge ),
|
||||
|
||||
DEFINE_OUTPUT(m_OutRemainingCharge, "OutRemainingCharge"),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
|
||||
LINK_ENTITY_TO_CLASS(func_recharge, CRecharge);
|
||||
|
||||
|
||||
bool CRecharge::KeyValue( const char *szKeyName, const char *szValue )
|
||||
{
|
||||
if ( FStrEq(szKeyName, "style") ||
|
||||
FStrEq(szKeyName, "height") ||
|
||||
FStrEq(szKeyName, "value1") ||
|
||||
FStrEq(szKeyName, "value2") ||
|
||||
FStrEq(szKeyName, "value3"))
|
||||
{
|
||||
}
|
||||
else if (FStrEq(szKeyName, "dmdelay"))
|
||||
{
|
||||
m_iReactivate = atoi(szValue);
|
||||
}
|
||||
else
|
||||
return BaseClass::KeyValue( szKeyName, szValue );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CRecharge::Spawn()
|
||||
{
|
||||
Precache( );
|
||||
|
||||
SetSolid( SOLID_BSP );
|
||||
SetMoveType( MOVETYPE_PUSH );
|
||||
|
||||
SetModel( STRING( GetModelName() ) );
|
||||
m_iJuice = sk_suitcharger.GetFloat();
|
||||
SetTextureFrameIndex( 0 );
|
||||
|
||||
m_iCaps = FCAP_CONTINUOUS_USE;
|
||||
|
||||
CreateVPhysics();
|
||||
}
|
||||
|
||||
void CRecharge::Precache()
|
||||
{
|
||||
BaseClass::Precache();
|
||||
|
||||
PrecacheScriptSound( "SuitRecharge.Deny" );
|
||||
PrecacheScriptSound( "SuitRecharge.Start" );
|
||||
PrecacheScriptSound( "SuitRecharge.ChargingLoop" );
|
||||
}
|
||||
|
||||
bool CRecharge::CreateVPhysics()
|
||||
{
|
||||
VPhysicsInitStatic();
|
||||
return true;
|
||||
}
|
||||
|
||||
void CRecharge::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
// Make sure that we have a caller
|
||||
if (!pActivator)
|
||||
return;
|
||||
|
||||
// if it's not a player, ignore
|
||||
if ( !pActivator->IsPlayer() )
|
||||
return;
|
||||
|
||||
CBasePlayer *pPlayer = dynamic_cast<CBasePlayer *>( pActivator );
|
||||
|
||||
if ( pPlayer == NULL )
|
||||
return;
|
||||
|
||||
// Reset to a state of continuous use.
|
||||
m_iCaps = FCAP_CONTINUOUS_USE;
|
||||
|
||||
// if there is no juice left, turn it off
|
||||
if (m_iJuice <= 0)
|
||||
{
|
||||
SetTextureFrameIndex( 1 );
|
||||
Off();
|
||||
}
|
||||
|
||||
// if the player doesn't have the suit, or there is no juice left, make the deny noise
|
||||
if ( m_iJuice <= 0 )
|
||||
{
|
||||
if (m_flSoundTime <= gpGlobals->curtime)
|
||||
{
|
||||
m_flSoundTime = gpGlobals->curtime + 0.62;
|
||||
EmitSound( "SuitRecharge.Deny" );
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// If we're over our limit, debounce our keys
|
||||
if ( pPlayer->ArmorValue() >= HL1_MAX_ARMOR)
|
||||
{
|
||||
// Make the user re-use me to get started drawing health.
|
||||
pPlayer->m_afButtonPressed &= ~IN_USE;
|
||||
m_iCaps = FCAP_IMPULSE_USE;
|
||||
|
||||
EmitSound( "SuitRecharge.Deny" );
|
||||
return;
|
||||
}
|
||||
|
||||
SetNextThink( gpGlobals->curtime + 0.25 );
|
||||
SetThink(&CRecharge::Off);
|
||||
|
||||
// Time to recharge yet?
|
||||
|
||||
if (m_flNextCharge >= gpGlobals->curtime)
|
||||
return;
|
||||
|
||||
m_hActivator = pActivator;
|
||||
|
||||
|
||||
// Play the on sound or the looping charging sound
|
||||
if (!m_iOn)
|
||||
{
|
||||
m_iOn++;
|
||||
EmitSound( "SuitRecharge.Start" );
|
||||
m_flSoundTime = 0.56 + gpGlobals->curtime;
|
||||
}
|
||||
if ((m_iOn == 1) && (m_flSoundTime <= gpGlobals->curtime))
|
||||
{
|
||||
m_iOn++;
|
||||
CPASAttenuationFilter filter( this, "SuitRecharge.ChargingLoop" );
|
||||
filter.MakeReliable();
|
||||
EmitSound( filter, entindex(), "SuitRecharge.ChargingLoop" );
|
||||
}
|
||||
|
||||
CBasePlayer *pl = (CBasePlayer *) m_hActivator.Get();
|
||||
|
||||
// charge the player
|
||||
if (pl->ArmorValue() < HL1_MAX_ARMOR)
|
||||
{
|
||||
m_iJuice--;
|
||||
pl->IncrementArmorValue( 1, HL1_MAX_ARMOR );
|
||||
}
|
||||
|
||||
// Send the output.
|
||||
float flRemaining = m_iJuice / sk_suitcharger.GetFloat();
|
||||
m_OutRemainingCharge.Set(flRemaining, pActivator, this);
|
||||
|
||||
// govern the rate of charge
|
||||
m_flNextCharge = gpGlobals->curtime + 0.1;
|
||||
}
|
||||
|
||||
void CRecharge::Recharge(void)
|
||||
{
|
||||
m_iJuice = sk_suitcharger.GetFloat();
|
||||
SetTextureFrameIndex( 0 );
|
||||
SetThink( &CBaseEntity::SUB_DoNothing );
|
||||
}
|
||||
|
||||
void CRecharge::Off(void)
|
||||
{
|
||||
// Stop looping sound.
|
||||
if (m_iOn > 1)
|
||||
{
|
||||
StopSound( "SuitRecharge.ChargingLoop" );
|
||||
}
|
||||
|
||||
m_iOn = 0;
|
||||
|
||||
if ((!m_iJuice) && ( ( m_iReactivate = g_pGameRules->FlHEVChargerRechargeTime() ) > 0) )
|
||||
{
|
||||
SetNextThink( gpGlobals->curtime + m_iReactivate );
|
||||
SetThink(&CRecharge::Recharge);
|
||||
}
|
||||
else
|
||||
SetThink( NULL );
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,160 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "hl1_grenade_mp5.h"
|
||||
#include "hl1mp_weapon_mp5.h"
|
||||
#include "soundent.h"
|
||||
#include "decals.h"
|
||||
#include "shake.h"
|
||||
#include "smoke_trail.h"
|
||||
#include "vstdlib/random.h"
|
||||
#include "engine/IEngineSound.h"
|
||||
#include "world.h"
|
||||
|
||||
extern short g_sModelIndexFireball;
|
||||
extern short g_sModelIndexWExplosion;
|
||||
|
||||
extern ConVar sk_plr_dmg_mp5_grenade;
|
||||
extern ConVar sk_max_mp5_grenade;
|
||||
extern ConVar sk_mp5_grenade_radius;
|
||||
|
||||
BEGIN_DATADESC( CGrenadeMP5 )
|
||||
// SR-BUGBUG: These are borked!!!!
|
||||
// float m_fSpawnTime;
|
||||
|
||||
// Function pointers
|
||||
DEFINE_ENTITYFUNC( GrenadeMP5Touch ),
|
||||
|
||||
DEFINE_FIELD( m_fSpawnTime, FIELD_TIME ),
|
||||
END_DATADESC()
|
||||
|
||||
LINK_ENTITY_TO_CLASS( grenade_mp5, CGrenadeMP5 );
|
||||
|
||||
void CGrenadeMP5::Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SetSolid( SOLID_BBOX );
|
||||
SetMoveType( MOVETYPE_FLY );
|
||||
AddFlag( FL_GRENADE );
|
||||
|
||||
SetModel( "models/grenade.mdl" );
|
||||
//UTIL_SetSize(this, Vector(-3, -3, -3), Vector(3, 3, 3));
|
||||
UTIL_SetSize(this, Vector(0, 0, 0), Vector(0, 0, 0));
|
||||
|
||||
SetUse( &CBaseGrenade::DetonateUse );
|
||||
SetTouch( &CGrenadeMP5::GrenadeMP5Touch );
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
|
||||
m_flDamage = sk_plr_dmg_mp5_grenade.GetFloat();
|
||||
m_DmgRadius = sk_mp5_grenade_radius.GetFloat();
|
||||
m_takedamage = DAMAGE_YES;
|
||||
m_bIsLive = true;
|
||||
m_iHealth = 1;
|
||||
|
||||
SetGravity( UTIL_ScaleForGravity( 400 ) ); // use a lower gravity for grenades to make them easier to see
|
||||
SetFriction( 0.8 );
|
||||
|
||||
SetSequence( 0 );
|
||||
|
||||
m_fSpawnTime = gpGlobals->curtime;
|
||||
}
|
||||
|
||||
|
||||
void CGrenadeMP5::Event_Killed( CBaseEntity *pInflictor, CBaseEntity *pAttacker, float flDamage, int bitsDamageType )
|
||||
{
|
||||
Detonate( );
|
||||
}
|
||||
|
||||
|
||||
void CGrenadeMP5::GrenadeMP5Touch( CBaseEntity *pOther )
|
||||
{
|
||||
if ( !pOther->IsSolid() )
|
||||
return;
|
||||
|
||||
// If I'm live go ahead and blow up
|
||||
if (m_bIsLive)
|
||||
{
|
||||
Detonate();
|
||||
}
|
||||
else
|
||||
{
|
||||
// If I'm not live, only blow up if I'm hitting an chacter that
|
||||
// is not the owner of the weapon
|
||||
CBaseCombatCharacter *pBCC = ToBaseCombatCharacter( pOther );
|
||||
if (pBCC && GetThrower() != pBCC)
|
||||
{
|
||||
m_bIsLive = true;
|
||||
Detonate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CGrenadeMP5::Detonate(void)
|
||||
{
|
||||
if (!m_bIsLive)
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_bIsLive = false;
|
||||
m_takedamage = DAMAGE_NO;
|
||||
|
||||
CPASFilter filter( GetAbsOrigin() );
|
||||
|
||||
te->Explosion( filter, 0.0,
|
||||
&GetAbsOrigin(),
|
||||
GetWaterLevel() == 0 ? g_sModelIndexFireball : g_sModelIndexWExplosion,
|
||||
(m_flDamage - 50) * .60,
|
||||
15,
|
||||
TE_EXPLFLAG_NONE,
|
||||
m_DmgRadius,
|
||||
m_flDamage );
|
||||
|
||||
trace_t tr;
|
||||
tr = CBaseEntity::GetTouchTrace();
|
||||
|
||||
if ( (tr.m_pEnt != GetWorldEntity()) || (tr.hitbox != 0) )
|
||||
{
|
||||
// non-world needs smaller decals
|
||||
UTIL_DecalTrace( &tr, "SmallScorch");
|
||||
}
|
||||
else
|
||||
{
|
||||
UTIL_DecalTrace( &tr, "Scorch" );
|
||||
}
|
||||
|
||||
CSoundEnt::InsertSound ( SOUND_COMBAT, GetAbsOrigin(), BASEGRENADE_EXPLOSION_VOLUME, 3.0 );
|
||||
|
||||
RadiusDamage ( CTakeDamageInfo( this, GetThrower(), m_flDamage, DMG_BLAST ), GetAbsOrigin(), m_flDamage * 2.5, CLASS_NONE, NULL );
|
||||
|
||||
CPASAttenuationFilter filter2( this );
|
||||
EmitSound( filter2, entindex(), "GrenadeMP5.Detonate" );
|
||||
|
||||
if ( GetWaterLevel() == 0 )
|
||||
{
|
||||
int sparkCount = random->RandomInt( 0,3 );
|
||||
QAngle angles;
|
||||
VectorAngles( tr.plane.normal, angles );
|
||||
|
||||
for ( int i = 0; i < sparkCount; i++ )
|
||||
Create( "spark_shower", GetAbsOrigin(), angles, NULL );
|
||||
}
|
||||
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CGrenadeMP5::Precache( void )
|
||||
{
|
||||
BaseClass::Precache();
|
||||
|
||||
PrecacheModel( "models/grenade.mdl" );
|
||||
|
||||
PrecacheScriptSound( "GrenadeMP5.Detonate" );
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Projectile shot from the MP5
|
||||
//
|
||||
// $Workfile: $
|
||||
// $Date: $
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// $Log: $
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef GRENADEMP5_H
|
||||
#define GRENADEMP5_H
|
||||
|
||||
#include "hl1_basegrenade.h"
|
||||
|
||||
#define MAX_MP5_NO_COLLIDE_TIME 0.2
|
||||
|
||||
class SmokeTrail;
|
||||
class CWeaponMP5;
|
||||
|
||||
class CGrenadeMP5 : public CHL1BaseGrenade
|
||||
{
|
||||
DECLARE_CLASS( CGrenadeMP5, CHL1BaseGrenade );
|
||||
public:
|
||||
|
||||
float m_fSpawnTime;
|
||||
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void GrenadeMP5Touch( CBaseEntity *pOther );
|
||||
void Event_Killed( CBaseEntity *pInflictor, CBaseEntity *pAttacker, float flDamage, int bitsDamageType );
|
||||
|
||||
public:
|
||||
void EXPORT Detonate(void);
|
||||
|
||||
DECLARE_DATADESC();
|
||||
};
|
||||
|
||||
#endif //GRENADEMP5_H
|
||||
@@ -0,0 +1,171 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $Workfile: $
|
||||
// $Date: $
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "hl1_grenade_spit.h"
|
||||
#include "soundent.h"
|
||||
#include "decals.h"
|
||||
|
||||
#include "smoke_trail.h"
|
||||
#include "hl2_shareddefs.h"
|
||||
#include "vstdlib/random.h"
|
||||
#include "engine/IEngineSound.h"
|
||||
|
||||
ConVar sk_bullsquid_dmg_spit ( "sk_bullsquid_dmg_spit", "0" );
|
||||
|
||||
BEGIN_DATADESC( CGrenadeSpit )
|
||||
|
||||
// Function pointers
|
||||
DEFINE_THINKFUNC( SpitThink ),
|
||||
DEFINE_ENTITYFUNC( GrenadeSpitTouch ),
|
||||
|
||||
//DEFINE_FIELD( m_nSquidSpitSprite, FIELD_INTEGER ),
|
||||
|
||||
DEFINE_FIELD( m_fSpitDeathTime, FIELD_TIME ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
LINK_ENTITY_TO_CLASS( grenade_spit, CGrenadeSpit );
|
||||
|
||||
void CGrenadeSpit::Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SetSolid( SOLID_BBOX );
|
||||
SetMoveType( MOVETYPE_FLYGRAVITY );
|
||||
|
||||
// FIXME, if these is a sprite, then we need a base class derived from CSprite rather than
|
||||
// CBaseAnimating. pev->scale becomes m_flSpriteScale in that case.
|
||||
SetModel( "models/spitball_large.mdl" );
|
||||
UTIL_SetSize(this, Vector(-3, -3, -3), Vector(3, 3, 3));
|
||||
|
||||
m_nRenderMode = kRenderTransAdd;
|
||||
SetRenderColor( 255, 255, 255, 255 );
|
||||
m_nRenderFX = kRenderFxNone;
|
||||
|
||||
SetThink( &CGrenadeSpit::SpitThink );
|
||||
SetUse( &CBaseGrenade::DetonateUse );
|
||||
SetTouch( &CGrenadeSpit::GrenadeSpitTouch );
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
|
||||
m_flDamage = sk_bullsquid_dmg_spit.GetFloat();
|
||||
m_DmgRadius = 60.0f;
|
||||
m_takedamage = DAMAGE_YES;
|
||||
m_iHealth = 1;
|
||||
|
||||
SetGravity( SPIT_GRAVITY );
|
||||
SetFriction( 0.8 );
|
||||
SetSequence( 1 );
|
||||
|
||||
SetCollisionGroup( HL2COLLISION_GROUP_SPIT );
|
||||
}
|
||||
|
||||
|
||||
void CGrenadeSpit::SetSpitSize(int nSize)
|
||||
{
|
||||
switch (nSize)
|
||||
{
|
||||
case SPIT_LARGE:
|
||||
{
|
||||
SetModel( "models/spitball_large.mdl" );
|
||||
break;
|
||||
}
|
||||
case SPIT_MEDIUM:
|
||||
{
|
||||
SetModel( "models/spitball_medium.mdl" );
|
||||
break;
|
||||
}
|
||||
case SPIT_SMALL:
|
||||
{
|
||||
SetModel( "models/spitball_small.mdl" );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CGrenadeSpit::Event_Killed( const CTakeDamageInfo &info )
|
||||
{
|
||||
Detonate( );
|
||||
}
|
||||
|
||||
void CGrenadeSpit::GrenadeSpitTouch( CBaseEntity *pOther )
|
||||
{
|
||||
if (m_fSpitDeathTime != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if ( pOther->GetCollisionGroup() == HL2COLLISION_GROUP_SPIT)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if ( !pOther->m_takedamage )
|
||||
{
|
||||
|
||||
// make a splat on the wall
|
||||
trace_t tr;
|
||||
UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + GetAbsVelocity() * 10, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
|
||||
UTIL_DecalTrace(&tr, "BeerSplash" );
|
||||
|
||||
// make some flecks
|
||||
CPVSFilter filter( tr.endpos );
|
||||
te->SpriteSpray( filter, 0.0,
|
||||
&tr.endpos, &tr.plane.normal, m_nSquidSpitSprite, random->RandomInt( 90, 160 ), 50, random->RandomInt ( 5, 15 ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
RadiusDamage ( CTakeDamageInfo( this, GetThrower(), m_flDamage, DMG_BLAST ), GetAbsOrigin(), m_DmgRadius, CLASS_NONE, NULL );
|
||||
}
|
||||
|
||||
Detonate();
|
||||
}
|
||||
|
||||
void CGrenadeSpit::SpitThink( void )
|
||||
{
|
||||
if (m_fSpitDeathTime != 0 &&
|
||||
m_fSpitDeathTime < gpGlobals->curtime)
|
||||
{
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
}
|
||||
|
||||
void CGrenadeSpit::Detonate(void)
|
||||
{
|
||||
m_takedamage = DAMAGE_NO;
|
||||
|
||||
int iPitch;
|
||||
|
||||
// splat sound
|
||||
iPitch = random->RandomFloat( 90, 110 );
|
||||
|
||||
EmitSound( "GrenadeSpit.Acid" );
|
||||
EmitSound( "GrenadeSpit.Hit" );
|
||||
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CGrenadeSpit::Precache( void )
|
||||
{
|
||||
m_nSquidSpitSprite = PrecacheModel("sprites/bigspit.vmt");// client side spittle.
|
||||
|
||||
PrecacheModel("models/spitball_large.mdl");
|
||||
PrecacheModel("models/spitball_medium.mdl");
|
||||
PrecacheModel("models/spitball_small.mdl");
|
||||
|
||||
PrecacheScriptSound( "GrenadeSpit.Acid" );
|
||||
PrecacheScriptSound( "GrenadeSpit.Hit" );
|
||||
|
||||
}
|
||||
|
||||
|
||||
CGrenadeSpit::CGrenadeSpit(void)
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Projectile shot by bullsquid
|
||||
//
|
||||
// $Workfile: $
|
||||
// $Date: $
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// $Log: $
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef GRENADESPIT_H
|
||||
#define GRENADESPIT_H
|
||||
|
||||
#include "hl1_basegrenade.h"
|
||||
|
||||
enum SpitSize_e
|
||||
{
|
||||
SPIT_SMALL,
|
||||
SPIT_MEDIUM,
|
||||
SPIT_LARGE,
|
||||
};
|
||||
|
||||
#define SPIT_GRAVITY 0.9
|
||||
|
||||
class CGrenadeSpit : public CHL1BaseGrenade
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CGrenadeSpit, CHL1BaseGrenade );
|
||||
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void SpitThink( void );
|
||||
void GrenadeSpitTouch( CBaseEntity *pOther );
|
||||
void Event_Killed( const CTakeDamageInfo &info );
|
||||
void SetSpitSize(int nSize);
|
||||
|
||||
int m_nSquidSpitSprite;
|
||||
float m_fSpitDeathTime; // If non-zero won't detonate
|
||||
|
||||
void EXPORT Detonate(void);
|
||||
CGrenadeSpit(void);
|
||||
|
||||
DECLARE_DATADESC();
|
||||
};
|
||||
|
||||
#endif //GRENADESPIT_H
|
||||
@@ -0,0 +1,410 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Ammo boxes for HL1
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
|
||||
#include "cbase.h"
|
||||
#include "player.h"
|
||||
#include "gamerules.h"
|
||||
#include "items.h"
|
||||
#include "hl1_items.h"
|
||||
|
||||
|
||||
// ========================================================================
|
||||
// >> Crossbow bolts
|
||||
// ========================================================================
|
||||
#define AMMO_CROSSBOW_GIVE 5
|
||||
#define AMMO_CROSSBOW_MODEL "models/w_crossbow_clip.mdl"
|
||||
|
||||
class CCrossbowAmmo : public CHL1Item
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CCrossbowAmmo, CHL1Item );
|
||||
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache();
|
||||
SetModel( AMMO_CROSSBOW_MODEL );
|
||||
BaseClass::Spawn();
|
||||
}
|
||||
void Precache( void )
|
||||
{
|
||||
PrecacheModel( AMMO_CROSSBOW_MODEL );
|
||||
}
|
||||
bool MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
if (pPlayer->GiveAmmo( AMMO_CROSSBOW_GIVE, "XBowBolt" ) )
|
||||
{
|
||||
if ( g_pGameRules->ItemShouldRespawn( this ) == GR_ITEM_RESPAWN_NO )
|
||||
{
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS(ammo_crossbow, CCrossbowAmmo);
|
||||
PRECACHE_REGISTER(ammo_crossbow);
|
||||
|
||||
|
||||
// ========================================================================
|
||||
// >> Egon ammo
|
||||
// ========================================================================
|
||||
#define AMMO_EGON_GIVE 20
|
||||
#define AMMO_EGON_MODEL "models/w_chainammo.mdl"
|
||||
|
||||
class CEgonAmmo : public CHL1Item
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CEgonAmmo, CHL1Item );
|
||||
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache();
|
||||
SetModel( AMMO_EGON_MODEL );
|
||||
BaseClass::Spawn();
|
||||
}
|
||||
void Precache( void )
|
||||
{
|
||||
PrecacheModel ( AMMO_EGON_MODEL );
|
||||
}
|
||||
bool MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
if (pPlayer->GiveAmmo( AMMO_EGON_GIVE, "Uranium" ) )
|
||||
{
|
||||
if ( g_pGameRules->ItemShouldRespawn( this ) == GR_ITEM_RESPAWN_NO )
|
||||
{
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS(ammo_egonclip, CEgonAmmo);
|
||||
PRECACHE_REGISTER(ammo_egonclip);
|
||||
|
||||
|
||||
// ========================================================================
|
||||
// >> Gauss ammo
|
||||
// ========================================================================
|
||||
#define AMMO_GAUSS_GIVE 20
|
||||
#define AMMO_GAUSS_MODEL "models/w_gaussammo.mdl"
|
||||
|
||||
class CGaussAmmo : public CHL1Item
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CGaussAmmo, CHL1Item );
|
||||
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache();
|
||||
SetModel( AMMO_GAUSS_MODEL );
|
||||
BaseClass::Spawn();
|
||||
}
|
||||
void Precache( void )
|
||||
{
|
||||
PrecacheModel ( AMMO_GAUSS_MODEL );
|
||||
}
|
||||
bool MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
if (pPlayer->GiveAmmo( AMMO_GAUSS_GIVE, "Uranium" ) )
|
||||
{
|
||||
if ( g_pGameRules->ItemShouldRespawn( this ) == GR_ITEM_RESPAWN_NO )
|
||||
{
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS(ammo_gaussclip, CGaussAmmo);
|
||||
PRECACHE_REGISTER(ammo_gaussclip);
|
||||
|
||||
|
||||
// ========================================================================
|
||||
// >> Glock ammo
|
||||
// ========================================================================
|
||||
#define AMMO_GLOCK_GIVE 18
|
||||
#define AMMO_GLOCK_MODEL "models/w_9mmclip.mdl"
|
||||
|
||||
class CGlockAmmo : public CHL1Item
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CGlockAmmo, CHL1Item );
|
||||
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache();
|
||||
SetModel( AMMO_GLOCK_MODEL );
|
||||
BaseClass::Spawn();
|
||||
}
|
||||
void Precache( void )
|
||||
{
|
||||
PrecacheModel ( AMMO_GLOCK_MODEL );
|
||||
}
|
||||
bool MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
if (pPlayer->GiveAmmo( AMMO_GLOCK_GIVE, "9mmRound" ) )
|
||||
{
|
||||
if ( g_pGameRules->ItemShouldRespawn( this ) == GR_ITEM_RESPAWN_NO )
|
||||
{
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS(ammo_glockclip, CGlockAmmo);
|
||||
PRECACHE_REGISTER(ammo_glockclip);
|
||||
LINK_ENTITY_TO_CLASS(ammo_9mmclip, CGlockAmmo);
|
||||
PRECACHE_REGISTER(ammo_9mmclip);
|
||||
|
||||
|
||||
// ========================================================================
|
||||
// >> MP5 ammo
|
||||
// ========================================================================
|
||||
#define AMMO_MP5_GIVE 50
|
||||
#define AMMO_MP5_MODEL "models/w_9mmARclip.mdl"
|
||||
|
||||
class CMP5AmmoClip : public CHL1Item
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CMP5AmmoClip, CHL1Item );
|
||||
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache();
|
||||
SetModel( AMMO_MP5_MODEL );
|
||||
BaseClass::Spawn();
|
||||
}
|
||||
void Precache( void )
|
||||
{
|
||||
PrecacheModel ( AMMO_MP5_MODEL );
|
||||
}
|
||||
bool MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
if (pPlayer->GiveAmmo( AMMO_MP5_GIVE, "9mmRound" ) )
|
||||
{
|
||||
if ( g_pGameRules->ItemShouldRespawn( this ) == GR_ITEM_RESPAWN_NO )
|
||||
{
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS(ammo_mp5clip, CMP5AmmoClip);
|
||||
PRECACHE_REGISTER(ammo_mp5clip);
|
||||
LINK_ENTITY_TO_CLASS(ammo_9mmar, CMP5AmmoClip);
|
||||
PRECACHE_REGISTER(ammo_9mmar);
|
||||
|
||||
|
||||
// ========================================================================
|
||||
// >> MP5Chain (?) ammo
|
||||
// ========================================================================
|
||||
#define AMMO_MP5CHAIN_GIVE 200
|
||||
#define AMMO_MP5CHAIN_MODEL "models/w_chainammo.mdl"
|
||||
|
||||
class CMP5Chainammo : public CHL1Item
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CMP5Chainammo, CHL1Item );
|
||||
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache();
|
||||
SetModel( AMMO_MP5CHAIN_MODEL );
|
||||
BaseClass::Spawn();
|
||||
}
|
||||
void Precache( void )
|
||||
{
|
||||
PrecacheModel ( AMMO_MP5CHAIN_MODEL );
|
||||
}
|
||||
bool MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
if (pPlayer->GiveAmmo( AMMO_MP5CHAIN_GIVE, "9mmRound" ) )
|
||||
{
|
||||
if ( g_pGameRules->ItemShouldRespawn( this ) == GR_ITEM_RESPAWN_NO )
|
||||
{
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS(ammo_9mmbox, CMP5Chainammo);
|
||||
PRECACHE_REGISTER(ammo_9mmbox);
|
||||
|
||||
|
||||
// ========================================================================
|
||||
// >> MP5 grenades
|
||||
// ========================================================================
|
||||
#define AMMO_MP5GRENADE_GIVE 2
|
||||
#define AMMO_MP5GRENADE_MODEL "models/w_ARgrenade.mdl"
|
||||
|
||||
class CMP5AmmoGrenade : public CHL1Item
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CMP5AmmoGrenade, CHL1Item );
|
||||
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache();
|
||||
SetModel( AMMO_MP5GRENADE_MODEL );
|
||||
BaseClass::Spawn();
|
||||
}
|
||||
void Precache( void )
|
||||
{
|
||||
PrecacheModel ( AMMO_MP5GRENADE_MODEL );
|
||||
}
|
||||
bool MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
if (pPlayer->GiveAmmo( AMMO_MP5GRENADE_GIVE, "MP5_Grenade" ) )
|
||||
{
|
||||
if ( g_pGameRules->ItemShouldRespawn( this ) == GR_ITEM_RESPAWN_NO )
|
||||
{
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS(ammo_mp5grenades, CMP5AmmoGrenade);
|
||||
PRECACHE_REGISTER(ammo_mp5grenades);
|
||||
LINK_ENTITY_TO_CLASS(ammo_argrenades, CMP5AmmoGrenade);
|
||||
PRECACHE_REGISTER(ammo_argrenades);
|
||||
|
||||
|
||||
// ========================================================================
|
||||
// >> 357 ammo
|
||||
// ========================================================================
|
||||
#define AMMO_357_GIVE 6
|
||||
#define AMMO_357_MODEL "models/w_357ammobox.mdl"
|
||||
|
||||
class CPythonAmmo : public CHL1Item
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CPythonAmmo, CHL1Item );
|
||||
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache();
|
||||
SetModel( AMMO_357_MODEL );
|
||||
BaseClass::Spawn();
|
||||
}
|
||||
void Precache( void )
|
||||
{
|
||||
PrecacheModel ( AMMO_357_MODEL );
|
||||
}
|
||||
bool MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
if (pPlayer->GiveAmmo( AMMO_357_GIVE, "357Round" ) )
|
||||
{
|
||||
if ( g_pGameRules->ItemShouldRespawn( this ) == GR_ITEM_RESPAWN_NO )
|
||||
{
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS(ammo_357, CPythonAmmo);
|
||||
PRECACHE_REGISTER(ammo_357);
|
||||
|
||||
|
||||
// ========================================================================
|
||||
// >> RPG rockets
|
||||
// ========================================================================
|
||||
#define AMMO_RPG_GIVE 1
|
||||
#define AMMO_RPG_MODEL "models/w_rpgammo.mdl"
|
||||
|
||||
class CRpgAmmo : public CHL1Item
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CRpgAmmo, CHL1Item );
|
||||
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache();
|
||||
SetModel( AMMO_RPG_MODEL );
|
||||
BaseClass::Spawn();
|
||||
}
|
||||
void Precache( void )
|
||||
{
|
||||
PrecacheModel ( AMMO_RPG_MODEL );
|
||||
}
|
||||
bool MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
int nGive;
|
||||
|
||||
if ( g_pGameRules->IsMultiplayer() )
|
||||
{
|
||||
// hand out more ammo per rocket in multiplayer.
|
||||
nGive = AMMO_RPG_GIVE * 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
nGive = AMMO_RPG_GIVE;
|
||||
}
|
||||
|
||||
if (pPlayer->GiveAmmo( nGive, "RPG_Rocket" ) )
|
||||
{
|
||||
if ( g_pGameRules->ItemShouldRespawn( this ) == GR_ITEM_RESPAWN_NO )
|
||||
{
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS(ammo_rpgclip, CRpgAmmo);
|
||||
PRECACHE_REGISTER(ammo_rpgclip);
|
||||
|
||||
|
||||
// ========================================================================
|
||||
// >> Shotgun ammo
|
||||
// ========================================================================
|
||||
#define AMMO_SHOTGUN_GIVE 12
|
||||
#define AMMO_SHOTGUN_MODEL "models/w_shotbox.mdl"
|
||||
|
||||
class CShotgunAmmo : public CHL1Item
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CShotgunAmmo, CHL1Item );
|
||||
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache();
|
||||
SetModel( AMMO_SHOTGUN_MODEL );
|
||||
BaseClass::Spawn();
|
||||
}
|
||||
void Precache( void )
|
||||
{
|
||||
PrecacheModel ( AMMO_SHOTGUN_MODEL );
|
||||
}
|
||||
bool MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
if (pPlayer->GiveAmmo( AMMO_SHOTGUN_GIVE, "Buckshot" ) )
|
||||
{
|
||||
if ( g_pGameRules->ItemShouldRespawn( this ) == GR_ITEM_RESPAWN_NO )
|
||||
{
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS(ammo_buckshot, CShotgunAmmo);
|
||||
PRECACHE_REGISTER(ammo_buckshot);
|
||||
@@ -0,0 +1,78 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Handling for the suit batteries.
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "player.h"
|
||||
#include "basecombatweapon.h"
|
||||
#include "gamerules.h"
|
||||
#include "items.h"
|
||||
#include "engine/IEngineSound.h"
|
||||
#include "hl1_items.h"
|
||||
|
||||
|
||||
#define BATTERY_MODEL "models/w_battery.mdl"
|
||||
|
||||
ConVar sk_battery( "sk_battery","0" );
|
||||
|
||||
class CItemBattery : public CHL1Item
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CItemBattery, CHL1Item );
|
||||
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SetModel( BATTERY_MODEL );
|
||||
BaseClass::Spawn( );
|
||||
}
|
||||
void Precache( void )
|
||||
{
|
||||
PrecacheModel( BATTERY_MODEL );
|
||||
|
||||
PrecacheScriptSound( "Item.Pickup" );
|
||||
}
|
||||
|
||||
bool MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
if ((pPlayer->ArmorValue() < MAX_NORMAL_BATTERY) && pPlayer->IsSuitEquipped())
|
||||
{
|
||||
int pct;
|
||||
char szcharge[64];
|
||||
|
||||
pPlayer->IncrementArmorValue( sk_battery.GetFloat(), MAX_NORMAL_BATTERY );
|
||||
|
||||
CPASAttenuationFilter filter( pPlayer, "Item.Pickup" );
|
||||
EmitSound( filter, pPlayer->entindex(), "Item.Pickup" );
|
||||
|
||||
CSingleUserRecipientFilter user( pPlayer );
|
||||
user.MakeReliable();
|
||||
|
||||
UserMessageBegin( user, "ItemPickup" );
|
||||
WRITE_STRING( GetClassname() );
|
||||
MessageEnd();
|
||||
|
||||
|
||||
// Suit reports new power level
|
||||
// For some reason this wasn't working in release build -- round it.
|
||||
pct = (int)( (float)(pPlayer->ArmorValue() * 100.0) * (1.0/MAX_NORMAL_BATTERY) + 0.5);
|
||||
pct = (pct / 5);
|
||||
if (pct > 0)
|
||||
pct--;
|
||||
|
||||
Q_snprintf( szcharge,sizeof(szcharge),"!HEV_%1dP", pct );
|
||||
|
||||
//UTIL_EmitSoundSuit(edict(), szcharge);
|
||||
pPlayer->SetSuitUpdate(szcharge, FALSE, SUIT_NEXT_IN_30SEC);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS(item_battery, CItemBattery);
|
||||
PRECACHE_REGISTER(item_battery);
|
||||
|
||||
@@ -0,0 +1,384 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "gamerules.h"
|
||||
#include "player.h"
|
||||
#include "items.h"
|
||||
#include "engine/IEngineSound.h"
|
||||
#include "hl1_items.h"
|
||||
#include "in_buttons.h"
|
||||
|
||||
|
||||
ConVar sk_healthkit( "sk_healthkit","0" );
|
||||
ConVar sk_healthvial( "sk_healthvial","0" );
|
||||
ConVar sk_healthcharger( "sk_healthcharger","0" );
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Small health kit. Heals the player when picked up.
|
||||
//-----------------------------------------------------------------------------
|
||||
class CHealthKit : public CHL1Item
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CHealthKit, CHL1Item );
|
||||
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
bool MyTouch( CBasePlayer *pPlayer );
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( item_healthkit, CHealthKit );
|
||||
PRECACHE_REGISTER(item_healthkit);
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CHealthKit::Spawn( void )
|
||||
{
|
||||
Precache();
|
||||
SetModel( "models/w_medkit.mdl" );
|
||||
|
||||
BaseClass::Spawn();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CHealthKit::Precache( void )
|
||||
{
|
||||
PrecacheModel("models/w_medkit.mdl");
|
||||
|
||||
PrecacheScriptSound( "HealthKit.Touch" );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : *pPlayer -
|
||||
// Output :
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CHealthKit::MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
if ( pPlayer->TakeHealth( sk_healthkit.GetFloat(), DMG_GENERIC ) )
|
||||
{
|
||||
CSingleUserRecipientFilter user( pPlayer );
|
||||
user.MakeReliable();
|
||||
|
||||
UserMessageBegin( user, "ItemPickup" );
|
||||
WRITE_STRING( GetClassname() );
|
||||
MessageEnd();
|
||||
|
||||
CPASAttenuationFilter filter( pPlayer, "HealthKit.Touch" );
|
||||
EmitSound( filter, pPlayer->entindex(), "HealthKit.Touch" );
|
||||
|
||||
if ( g_pGameRules->ItemShouldRespawn( this ) )
|
||||
{
|
||||
Respawn();
|
||||
}
|
||||
else
|
||||
{
|
||||
UTIL_Remove(this);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Small dynamically dropped health kit
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class CHealthVial : public CHL1Item
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CHealthVial, CHL1Item );
|
||||
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache();
|
||||
SetModel( "models/healthvial.mdl" );
|
||||
|
||||
BaseClass::Spawn();
|
||||
}
|
||||
|
||||
void Precache( void )
|
||||
{
|
||||
PrecacheModel("models/healthvial.mdl");
|
||||
|
||||
PrecacheScriptSound( "HealthVial.Touch" );
|
||||
}
|
||||
|
||||
bool MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
if ( pPlayer->TakeHealth( sk_healthvial.GetFloat(), DMG_GENERIC ) )
|
||||
{
|
||||
CSingleUserRecipientFilter user( pPlayer );
|
||||
user.MakeReliable();
|
||||
|
||||
UserMessageBegin( user, "ItemPickup" );
|
||||
WRITE_STRING( GetClassname() );
|
||||
MessageEnd();
|
||||
|
||||
CPASAttenuationFilter filter( pPlayer, "HealthVial.Touch" );
|
||||
EmitSound( filter, pPlayer->entindex(), "HealthVial.Touch" );
|
||||
|
||||
if ( g_pGameRules->ItemShouldRespawn( this ) )
|
||||
{
|
||||
Respawn();
|
||||
}
|
||||
else
|
||||
{
|
||||
UTIL_Remove(this);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( item_healthvial, CHealthVial );
|
||||
PRECACHE_REGISTER( item_healthvial );
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Wall mounted health kit. Heals the player when used.
|
||||
//-----------------------------------------------------------------------------
|
||||
class CWallHealth : public CBaseToggle
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CWallHealth, CBaseToggle );
|
||||
|
||||
void Spawn( );
|
||||
void Precache( void );
|
||||
bool CreateVPhysics(void);
|
||||
void Off(void);
|
||||
void Recharge(void);
|
||||
bool KeyValue( const char *szKeyName, const char *szValue );
|
||||
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
virtual int ObjectCaps( void ) { return BaseClass::ObjectCaps() | m_iCaps; }
|
||||
|
||||
float m_flNextCharge;
|
||||
int m_iReactivate ; // DeathMatch Delay until reactvated
|
||||
int m_iJuice;
|
||||
int m_iOn; // 0 = off, 1 = startup, 2 = going
|
||||
float m_flSoundTime;
|
||||
int m_iCaps;
|
||||
|
||||
DECLARE_DATADESC();
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS(func_healthcharger, CWallHealth);
|
||||
|
||||
|
||||
BEGIN_DATADESC( CWallHealth )
|
||||
|
||||
DEFINE_FIELD( m_flNextCharge, FIELD_TIME),
|
||||
DEFINE_FIELD( m_iReactivate, FIELD_INTEGER),
|
||||
DEFINE_FIELD( m_iJuice, FIELD_INTEGER),
|
||||
DEFINE_FIELD( m_iOn, FIELD_INTEGER),
|
||||
DEFINE_FIELD( m_flSoundTime, FIELD_TIME),
|
||||
DEFINE_FIELD( m_iCaps, FIELD_INTEGER ),
|
||||
|
||||
// Function Pointers
|
||||
DEFINE_FUNCTION( Off ),
|
||||
DEFINE_FUNCTION( Recharge ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : *pkvd -
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CWallHealth::KeyValue( const char *szKeyName, const char *szValue )
|
||||
{
|
||||
if (FStrEq(szKeyName, "style") ||
|
||||
FStrEq(szKeyName, "height") ||
|
||||
FStrEq(szKeyName, "value1") ||
|
||||
FStrEq(szKeyName, "value2") ||
|
||||
FStrEq(szKeyName, "value3"))
|
||||
{
|
||||
return(true);
|
||||
}
|
||||
else if (FStrEq(szKeyName, "dmdelay"))
|
||||
{
|
||||
m_iReactivate = atoi(szValue);
|
||||
return(true);
|
||||
}
|
||||
|
||||
return(BaseClass::KeyValue( szKeyName, szValue ));
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CWallHealth::Spawn(void)
|
||||
{
|
||||
Precache( );
|
||||
|
||||
SetSolid( SOLID_BSP );
|
||||
SetMoveType( MOVETYPE_PUSH );
|
||||
|
||||
SetModel( STRING( GetModelName() ) );
|
||||
|
||||
m_iJuice = sk_healthcharger.GetFloat();
|
||||
SetTextureFrameIndex( 0 );
|
||||
|
||||
m_iCaps = FCAP_CONTINUOUS_USE;
|
||||
|
||||
CreateVPhysics();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool CWallHealth::CreateVPhysics(void)
|
||||
{
|
||||
VPhysicsInitStatic();
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CWallHealth::Precache(void)
|
||||
{
|
||||
PrecacheScriptSound( "WallHealth.Deny" );
|
||||
PrecacheScriptSound( "WallHealth.Start" );
|
||||
PrecacheScriptSound( "WallHealth.LoopingContinueCharge" );
|
||||
PrecacheScriptSound( "WallHealth.Recharge" );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : *pActivator -
|
||||
// *pCaller -
|
||||
// useType -
|
||||
// value -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CWallHealth::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
// Make sure that we have a caller
|
||||
if (!pActivator)
|
||||
return;
|
||||
// if it's not a player, ignore
|
||||
if ( !pActivator->IsPlayer() )
|
||||
return;
|
||||
|
||||
// Reset to a state of continuous use.
|
||||
m_iCaps = FCAP_CONTINUOUS_USE;
|
||||
|
||||
// if there is no juice left, turn it off
|
||||
if (m_iJuice <= 0)
|
||||
{
|
||||
Off();
|
||||
SetTextureFrameIndex( 1 );
|
||||
}
|
||||
|
||||
CBasePlayer *pPlayer = ToBasePlayer( pActivator );
|
||||
|
||||
// if the player doesn't have the suit, or there is no juice left, make the deny noise.
|
||||
if ((m_iJuice <= 0) || (!(pPlayer->m_Local.m_bWearingSuit)))
|
||||
{
|
||||
if (m_flSoundTime <= gpGlobals->curtime)
|
||||
{
|
||||
m_flSoundTime = gpGlobals->curtime + 0.62;
|
||||
EmitSound( "WallHealth.Deny" );
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if( pActivator->GetHealth() >= pActivator->GetMaxHealth() )
|
||||
{
|
||||
CBasePlayer *pPlayer = dynamic_cast<CBasePlayer *>(pActivator);
|
||||
|
||||
if( pPlayer )
|
||||
{
|
||||
pPlayer->m_afButtonPressed &= ~IN_USE;
|
||||
}
|
||||
|
||||
// Make the user re-use me to get started drawing health.
|
||||
m_iCaps = FCAP_IMPULSE_USE;
|
||||
|
||||
EmitSound( "WallHealth.Deny" );
|
||||
return;
|
||||
}
|
||||
|
||||
SetNextThink( gpGlobals->curtime + 0.25 );
|
||||
SetThink(&CWallHealth::Off);
|
||||
|
||||
// Time to recharge yet?
|
||||
|
||||
if (m_flNextCharge >= gpGlobals->curtime)
|
||||
return;
|
||||
|
||||
// Play the on sound or the looping charging sound
|
||||
if (!m_iOn)
|
||||
{
|
||||
m_iOn++;
|
||||
EmitSound( "WallHealth.Start" );
|
||||
m_flSoundTime = 0.56 + gpGlobals->curtime;
|
||||
}
|
||||
if ((m_iOn == 1) && (m_flSoundTime <= gpGlobals->curtime))
|
||||
{
|
||||
m_iOn++;
|
||||
CPASAttenuationFilter filter( this, "WallHealth.LoopingContinueCharge" );
|
||||
filter.MakeReliable();
|
||||
EmitSound( filter, entindex(), "WallHealth.LoopingContinueCharge" );
|
||||
}
|
||||
|
||||
|
||||
// charge the player
|
||||
if ( pActivator->TakeHealth( 1, DMG_GENERIC ) )
|
||||
{
|
||||
m_iJuice--;
|
||||
}
|
||||
|
||||
// govern the rate of charge
|
||||
m_flNextCharge = gpGlobals->curtime + 0.1;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CWallHealth::Recharge(void)
|
||||
{
|
||||
EmitSound( "WallHealth.Recharge" );
|
||||
m_iJuice = sk_healthcharger.GetFloat();
|
||||
SetTextureFrameIndex( 0 );
|
||||
SetThink( NULL );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CWallHealth::Off(void)
|
||||
{
|
||||
// Stop looping sound.
|
||||
if (m_iOn > 1)
|
||||
StopSound( "WallHealth.LoopingContinueCharge" );
|
||||
|
||||
m_iOn = 0;
|
||||
|
||||
if ((!m_iJuice) && ( ( m_iReactivate = g_pGameRules->FlHealthChargerRechargeTime() ) > 0) )
|
||||
{
|
||||
SetNextThink( gpGlobals->curtime + m_iReactivate );
|
||||
SetThink(&CWallHealth::Recharge);
|
||||
}
|
||||
else
|
||||
SetThink( NULL );
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
|
||||
#include "cbase.h"
|
||||
#include "player.h"
|
||||
#include "gamerules.h"
|
||||
#include "items.h"
|
||||
#include "hl1_items.h"
|
||||
#include "hl1_player.h"
|
||||
|
||||
|
||||
class CItemLongJump : public CHL1Item
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CItemLongJump, CHL1Item );
|
||||
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SetModel( "models/w_longjump.mdl" );
|
||||
BaseClass::Spawn( );
|
||||
|
||||
CollisionProp()->UseTriggerBounds( true, 16.0f );
|
||||
}
|
||||
void Precache( void )
|
||||
{
|
||||
PrecacheModel ("models/w_longjump.mdl");
|
||||
}
|
||||
bool MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
CHL1_Player *pHL1Player = (CHL1_Player*)pPlayer;
|
||||
|
||||
if ( pHL1Player->m_bHasLongJump == true )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( pHL1Player->IsSuitEquipped() )
|
||||
{
|
||||
pHL1Player->m_bHasLongJump = true;// player now has longjump module
|
||||
|
||||
CSingleUserRecipientFilter user( pHL1Player );
|
||||
user.MakeReliable();
|
||||
|
||||
UserMessageBegin( user, "ItemPickup" );
|
||||
WRITE_STRING( STRING(m_iClassname) );
|
||||
MessageEnd();
|
||||
|
||||
UTIL_EmitSoundSuit( pHL1Player->edict(), "!HEV_A1" ); // Play the longjump sound UNDONE: Kelly? correct sound?
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( item_longjump, CItemLongJump );
|
||||
PRECACHE_REGISTER(item_longjump);
|
||||
@@ -0,0 +1,61 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
/*
|
||||
===== item_suit.cpp ========================================================
|
||||
|
||||
handling for the player's suit.
|
||||
*/
|
||||
|
||||
#include "cbase.h"
|
||||
#include "player.h"
|
||||
#include "gamerules.h"
|
||||
#include "items.h"
|
||||
#include "hl1_items.h"
|
||||
|
||||
|
||||
#define SF_SUIT_SHORTLOGON 0x0001
|
||||
|
||||
#define SUIT_MODEL "models/w_suit.mdl"
|
||||
|
||||
extern int gEvilImpulse101;
|
||||
|
||||
class CItemSuit : public CHL1Item
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CItemSuit, CHL1Item );
|
||||
|
||||
void Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SetModel( SUIT_MODEL );
|
||||
BaseClass::Spawn( );
|
||||
|
||||
CollisionProp()->UseTriggerBounds( true, 12.0f );
|
||||
}
|
||||
void Precache( void )
|
||||
{
|
||||
PrecacheModel( SUIT_MODEL );
|
||||
}
|
||||
bool MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
if ( pPlayer->IsSuitEquipped() )
|
||||
return false;
|
||||
|
||||
if( !gEvilImpulse101 )
|
||||
{
|
||||
if ( HasSpawnFlags( SF_SUIT_SHORTLOGON ) )
|
||||
UTIL_EmitSoundSuit(pPlayer->edict(), "!HEV_A0"); // short version of suit logon,
|
||||
else
|
||||
UTIL_EmitSoundSuit(pPlayer->edict(), "!HEV_AAx"); // long version of suit logon
|
||||
}
|
||||
|
||||
pPlayer->EquipSuit();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS(item_suit, CItemSuit);
|
||||
PRECACHE_REGISTER(item_suit);
|
||||
@@ -0,0 +1,45 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
|
||||
#include "cbase.h"
|
||||
#include "player.h"
|
||||
#include "items.h"
|
||||
#include "gamerules.h"
|
||||
#include "hl1_items.h"
|
||||
|
||||
|
||||
void CHL1Item::Spawn( void )
|
||||
{
|
||||
SetMoveType( MOVETYPE_FLYGRAVITY );
|
||||
SetSolid( SOLID_BBOX );
|
||||
AddSolidFlags( FSOLID_NOT_STANDABLE | FSOLID_TRIGGER );
|
||||
CollisionProp()->UseTriggerBounds( true, 24.0f );
|
||||
|
||||
SetCollisionGroup( COLLISION_GROUP_DEBRIS );
|
||||
|
||||
SetTouch( &CItem::ItemTouch );
|
||||
|
||||
#ifdef HL1_DLL
|
||||
if ( g_pGameRules->IsMultiplayer() )
|
||||
AddEffects( EF_NOSHADOW );
|
||||
#endif
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
void CHL1Item::Activate( void )
|
||||
{
|
||||
BaseClass::Activate();
|
||||
|
||||
if ( UTIL_DropToFloor( this, MASK_SOLID ) == 0 )
|
||||
{
|
||||
Warning( "Item %s fell out of level at %f,%f,%f\n", GetClassname(), GetAbsOrigin().x, GetAbsOrigin().y, GetAbsOrigin().z);
|
||||
UTIL_Remove( this );
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef HL1_ITEMS_H
|
||||
#define HL1_ITEMS_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
#include "items.h"
|
||||
|
||||
class CHL1Item : public CItem
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CHL1Item, CItem );
|
||||
|
||||
void Spawn( void );
|
||||
void Activate( void );
|
||||
};
|
||||
|
||||
|
||||
#endif // HL1_ITEMS_H
|
||||
@@ -0,0 +1,294 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: An entity that creates NPCs in the game.
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "entityapi.h"
|
||||
#include "entityoutput.h"
|
||||
#include "ai_basenpc.h"
|
||||
#include "hl1_monstermaker.h"
|
||||
#include "mapentities.h"
|
||||
|
||||
|
||||
BEGIN_DATADESC( CNPCMaker )
|
||||
|
||||
DEFINE_KEYFIELD( m_iMaxNumNPCs, FIELD_INTEGER, "monstercount" ),
|
||||
DEFINE_KEYFIELD( m_iMaxLiveChildren, FIELD_INTEGER, "MaxLiveChildren" ),
|
||||
DEFINE_KEYFIELD( m_flSpawnFrequency, FIELD_FLOAT, "delay" ),
|
||||
DEFINE_KEYFIELD( m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ),
|
||||
DEFINE_KEYFIELD( m_iszNPCClassname, FIELD_STRING, "monstertype" ),
|
||||
|
||||
DEFINE_FIELD( m_cLiveChildren, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( m_flGround, FIELD_FLOAT ),
|
||||
|
||||
// Inputs
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "Spawn", InputSpawnNPC ),
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ),
|
||||
|
||||
// Outputs
|
||||
DEFINE_OUTPUT( m_OnSpawnNPC, "OnSpawnNPC" ),
|
||||
|
||||
// Function Pointers
|
||||
DEFINE_THINKFUNC( MakerThink ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
|
||||
LINK_ENTITY_TO_CLASS( monstermaker, CNPCMaker );
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Spawn
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNPCMaker::Spawn( void )
|
||||
{
|
||||
SetSolid( SOLID_NONE );
|
||||
m_cLiveChildren = 0;
|
||||
Precache();
|
||||
|
||||
// If I can make an infinite number of NPC, force them to fade
|
||||
if ( m_spawnflags & SF_NPCMAKER_INF_CHILD )
|
||||
{
|
||||
m_spawnflags |= SF_NPCMAKER_FADE;
|
||||
}
|
||||
|
||||
//Start on?
|
||||
if ( m_bDisabled == false )
|
||||
{
|
||||
SetThink ( &CNPCMaker::MakerThink );
|
||||
SetNextThink( gpGlobals->curtime + m_flSpawnFrequency );
|
||||
}
|
||||
else
|
||||
{
|
||||
//wait to be activated.
|
||||
SetThink ( &CBaseEntity::SUB_DoNothing );
|
||||
}
|
||||
m_flGround = 0;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Returns whether or not it is OK to make an NPC at this instant.
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CNPCMaker::CanMakeNPC( void )
|
||||
{
|
||||
if ( m_iMaxLiveChildren > 0 && m_cLiveChildren >= m_iMaxLiveChildren )
|
||||
{// not allowed to make a new one yet. Too many live ones out right now.
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( !m_flGround )
|
||||
{
|
||||
// set altitude. Now that I'm activated, any breakables, etc should be out from under me.
|
||||
trace_t tr;
|
||||
|
||||
UTIL_TraceLine ( GetAbsOrigin(), GetAbsOrigin() - Vector ( 0, 0, 2048 ), MASK_NPCSOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr );
|
||||
m_flGround = tr.endpos.z;
|
||||
}
|
||||
|
||||
Vector mins = GetAbsOrigin() - Vector( 34, 34, 0 );
|
||||
Vector maxs = GetAbsOrigin() + Vector( 34, 34, 0 );
|
||||
maxs.z = GetAbsOrigin().z;
|
||||
|
||||
//Only adjust for the ground if we want it
|
||||
if ( ( m_spawnflags & SF_NPCMAKER_NO_DROP ) == false )
|
||||
{
|
||||
mins.z = m_flGround;
|
||||
}
|
||||
|
||||
CBaseEntity *pList[128];
|
||||
|
||||
int count = UTIL_EntitiesInBox( pList, 128, mins, maxs, FL_CLIENT|FL_NPC );
|
||||
if ( count )
|
||||
{
|
||||
//Iterate through the list and check the results
|
||||
for ( int i = 0; i < count; i++ )
|
||||
{
|
||||
//Don't build on top of another entity
|
||||
if ( pList[i] == NULL )
|
||||
continue;
|
||||
|
||||
//If one of the entities is solid, then we can't spawn now
|
||||
if ( ( pList[i]->GetSolidFlags() & FSOLID_NOT_SOLID ) == false )
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: If this had a finite number of children, return true if they've all
|
||||
// been created.
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CNPCMaker::IsDepleted()
|
||||
{
|
||||
if ( (m_spawnflags & SF_NPCMAKER_INF_CHILD) || m_iMaxNumNPCs > 0 )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Toggle the spawner's state
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNPCMaker::Toggle( void )
|
||||
{
|
||||
if ( m_bDisabled )
|
||||
{
|
||||
Enable();
|
||||
}
|
||||
else
|
||||
{
|
||||
Disable();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Start the spawner
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNPCMaker::Enable( void )
|
||||
{
|
||||
// can't be enabled once depleted
|
||||
if ( IsDepleted() )
|
||||
return;
|
||||
|
||||
m_bDisabled = false;
|
||||
SetThink ( &CNPCMaker::MakerThink );
|
||||
SetNextThink( gpGlobals->curtime );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Stop the spawner
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNPCMaker::Disable( void )
|
||||
{
|
||||
m_bDisabled = true;
|
||||
SetThink ( NULL );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Input handler that spawns an NPC.
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNPCMaker::InputSpawnNPC( inputdata_t &inputdata )
|
||||
{
|
||||
MakeNPC();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Input hander that starts the spawner
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNPCMaker::InputEnable( inputdata_t &inputdata )
|
||||
{
|
||||
Enable();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Input hander that stops the spawner
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNPCMaker::InputDisable( inputdata_t &inputdata )
|
||||
{
|
||||
Disable();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Input hander that toggles the spawner
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNPCMaker::InputToggle( inputdata_t &inputdata )
|
||||
{
|
||||
Toggle();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Precache the target NPC
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNPCMaker::Precache( void )
|
||||
{
|
||||
BaseClass::Precache();
|
||||
UTIL_PrecacheOther( STRING( m_iszNPCClassname ) );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Creates the NPC.
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNPCMaker::MakeNPC( void )
|
||||
{
|
||||
if (!CanMakeNPC())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CBaseEntity *pent = (CBaseEntity*)CreateEntityByName( STRING(m_iszNPCClassname) );
|
||||
|
||||
if ( !pent )
|
||||
{
|
||||
Warning("NULL Ent in NPCMaker!\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
m_OnSpawnNPC.FireOutput( this, this );
|
||||
|
||||
pent->SetLocalOrigin( GetAbsOrigin() );
|
||||
pent->SetLocalAngles( GetAbsAngles() );
|
||||
|
||||
pent->AddSpawnFlags( SF_NPC_FALL_TO_GROUND );
|
||||
|
||||
if ( m_spawnflags & SF_NPCMAKER_FADE )
|
||||
{
|
||||
pent->AddSpawnFlags( SF_NPC_FADE_CORPSE );
|
||||
}
|
||||
|
||||
|
||||
DispatchSpawn( pent );
|
||||
pent->SetOwnerEntity( this );
|
||||
|
||||
m_cLiveChildren++;// count this NPC
|
||||
|
||||
if (!(m_spawnflags & SF_NPCMAKER_INF_CHILD))
|
||||
{
|
||||
m_iMaxNumNPCs--;
|
||||
|
||||
if ( IsDepleted() )
|
||||
{
|
||||
// Disable this forever. Don't kill it because it still gets death notices
|
||||
SetThink( NULL );
|
||||
SetUse( NULL );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Creates a new NPC every so often.
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNPCMaker::MakerThink ( void )
|
||||
{
|
||||
SetNextThink( gpGlobals->curtime + m_flSpawnFrequency );
|
||||
|
||||
MakeNPC();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : *pVictim -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNPCMaker::DeathNotice( CBaseEntity *pVictim )
|
||||
{
|
||||
// ok, we've gotten the deathnotice from our child, now clear out its owner if we don't want it to fade.
|
||||
m_cLiveChildren--;
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef MONSTERMAKER_H
|
||||
#define MONSTERMAKER_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "cbase.h"
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Spawnflags
|
||||
//-----------------------------------------------------------------------------
|
||||
#define SF_NPCMAKER_START_ON 1 // start active ( if has targetname )
|
||||
#define SF_NPCMAKER_NPCCLIP 8 // Children are blocked by NPCclip
|
||||
#define SF_NPCMAKER_FADE 16 // Children's corpses fade
|
||||
#define SF_NPCMAKER_INF_CHILD 32 // Infinite number of children
|
||||
#define SF_NPCMAKER_NO_DROP 64 // Do not adjust for the ground's position when checking for spawn
|
||||
|
||||
|
||||
class CNPCMaker : public CBaseEntity
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CNPCMaker, CBaseEntity );
|
||||
|
||||
CNPCMaker(void) {}
|
||||
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
|
||||
void MakerThink( void );
|
||||
bool CanMakeNPC( void );
|
||||
|
||||
void DeathNotice( CBaseEntity *pChild );// NPC maker children use this to tell the NPC maker that they have died.
|
||||
void MakeNPC( void );
|
||||
|
||||
// Input handlers
|
||||
void InputSpawnNPC( inputdata_t &inputdata );
|
||||
void InputEnable( inputdata_t &inputdata );
|
||||
void InputDisable( inputdata_t &inputdata );
|
||||
void InputToggle( inputdata_t &inputdata );
|
||||
|
||||
// State changers
|
||||
void Toggle( void );
|
||||
void Enable( void );
|
||||
void Disable( void );
|
||||
|
||||
bool IsDepleted();
|
||||
|
||||
DECLARE_DATADESC();
|
||||
|
||||
int m_iMaxNumNPCs; // max number of NPCs this ent can create
|
||||
float m_flSpawnFrequency; // delay (in secs) between spawns
|
||||
int m_iMaxLiveChildren; // max number of NPCs that this maker may have out at one time.
|
||||
string_t m_iszNPCClassname; // classname of the NPC(s) that will be created.
|
||||
|
||||
COutputEvent m_OnSpawnNPC;
|
||||
|
||||
int m_cLiveChildren;// how many NPCs made by this NPC maker that are currently alive
|
||||
|
||||
float m_flGround; // z coord of the ground under me, used to make sure no NPCs are under the maker when it drops a new child
|
||||
bool m_bDisabled;
|
||||
};
|
||||
|
||||
|
||||
#endif // MONSTERMAKER_H
|
||||
@@ -0,0 +1,858 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Bullseyes act as targets for other NPC's to attack and to trigger
|
||||
// events
|
||||
//
|
||||
// $Workfile: $
|
||||
// $Date: $
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// $Log: $
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "ai_default.h"
|
||||
#include "ai_task.h"
|
||||
#include "ai_schedule.h"
|
||||
#include "ai_node.h"
|
||||
#include "ai_hull.h"
|
||||
#include "ai_hint.h"
|
||||
#include "ai_route.h"
|
||||
#include "soundent.h"
|
||||
#include "game.h"
|
||||
#include "npcevent.h"
|
||||
#include "entitylist.h"
|
||||
#include "activitylist.h"
|
||||
#include "animation.h"
|
||||
#include "basecombatweapon.h"
|
||||
#include "IEffects.h"
|
||||
#include "vstdlib/random.h"
|
||||
#include "engine/IEngineSound.h"
|
||||
#include "ammodef.h"
|
||||
#include "hl1_ai_basenpc.h"
|
||||
|
||||
#define AFLOCK_MAX_RECRUIT_RADIUS 1024
|
||||
#define AFLOCK_FLY_SPEED 125
|
||||
#define AFLOCK_TURN_RATE 75
|
||||
#define AFLOCK_ACCELERATE 10
|
||||
#define AFLOCK_CHECK_DIST 192
|
||||
#define AFLOCK_TOO_CLOSE 100
|
||||
#define AFLOCK_TOO_FAR 256
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
class CNPC_FlockingFlyerFlock : public CHL1BaseNPC
|
||||
{
|
||||
DECLARE_CLASS( CNPC_FlockingFlyerFlock, CHL1BaseNPC );
|
||||
public:
|
||||
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
bool KeyValue( const char *szKeyName, const char *szValue );
|
||||
void SpawnFlock( void );
|
||||
|
||||
// Sounds are shared by the flock
|
||||
static void PrecacheFlockSounds( void );
|
||||
|
||||
DECLARE_DATADESC();
|
||||
|
||||
int m_cFlockSize;
|
||||
float m_flFlockRadius;
|
||||
};
|
||||
|
||||
BEGIN_DATADESC( CNPC_FlockingFlyerFlock )
|
||||
DEFINE_FIELD( m_cFlockSize, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( m_flFlockRadius, FIELD_FLOAT ),
|
||||
END_DATADESC()
|
||||
|
||||
class CNPC_FlockingFlyer : public CHL1BaseNPC
|
||||
{
|
||||
DECLARE_CLASS( CNPC_FlockingFlyer, CHL1BaseNPC );
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void SpawnCommonCode( void );
|
||||
void IdleThink( void );
|
||||
void BoidAdvanceFrame( void );
|
||||
void Start( void );
|
||||
bool FPathBlocked( void );
|
||||
void FlockLeaderThink( void );
|
||||
void SpreadFlock( void );
|
||||
void SpreadFlock2( void );
|
||||
void MakeSound( void );
|
||||
void FlockFollowerThink( void );
|
||||
void Event_Killed( const CTakeDamageInfo &info );
|
||||
void FallHack( void );
|
||||
//void Poop ( void ); Adrian - wtf?!
|
||||
|
||||
|
||||
|
||||
int IsLeader( void ) { return m_pSquadLeader == this; }
|
||||
int InSquad( void ) { return m_pSquadLeader != NULL; }
|
||||
int SquadCount( void );
|
||||
void SquadRemove( CNPC_FlockingFlyer *pRemove );
|
||||
void SquadUnlink( void );
|
||||
void SquadAdd( CNPC_FlockingFlyer *pAdd );
|
||||
void SquadDisband( void );
|
||||
|
||||
CNPC_FlockingFlyer *m_pSquadLeader;
|
||||
CNPC_FlockingFlyer *m_pSquadNext;
|
||||
bool m_fTurning;// is this boid turning?
|
||||
bool m_fCourseAdjust;// followers set this flag TRUE to override flocking while they avoid something
|
||||
bool m_fPathBlocked;// TRUE if there is an obstacle ahead
|
||||
Vector m_vecReferencePoint;// last place we saw leader
|
||||
Vector m_vecAdjustedVelocity;// adjusted velocity (used when fCourseAdjust is TRUE)
|
||||
float m_flGoalSpeed;
|
||||
float m_flLastBlockedTime;
|
||||
float m_flFakeBlockedTime;
|
||||
float m_flAlertTime;
|
||||
float m_flFlockNextSoundTime;
|
||||
float m_flTempVar;
|
||||
|
||||
DECLARE_DATADESC();
|
||||
};
|
||||
|
||||
BEGIN_DATADESC( CNPC_FlockingFlyer )
|
||||
DEFINE_FIELD( m_pSquadLeader, FIELD_CLASSPTR ),
|
||||
DEFINE_FIELD( m_pSquadNext, FIELD_CLASSPTR ),
|
||||
DEFINE_FIELD( m_fTurning, FIELD_BOOLEAN ),
|
||||
DEFINE_FIELD( m_fCourseAdjust, FIELD_BOOLEAN ),
|
||||
DEFINE_FIELD( m_fPathBlocked, FIELD_BOOLEAN ),
|
||||
DEFINE_FIELD( m_vecReferencePoint, FIELD_POSITION_VECTOR ),
|
||||
DEFINE_FIELD( m_vecAdjustedVelocity, FIELD_VECTOR ),
|
||||
DEFINE_FIELD( m_flGoalSpeed, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( m_flLastBlockedTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( m_flFakeBlockedTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( m_flAlertTime, FIELD_TIME ),
|
||||
DEFINE_THINKFUNC( IdleThink ),
|
||||
DEFINE_THINKFUNC( Start ),
|
||||
DEFINE_THINKFUNC( FlockLeaderThink ),
|
||||
DEFINE_THINKFUNC( FlockFollowerThink ),
|
||||
DEFINE_THINKFUNC( FallHack ),
|
||||
|
||||
DEFINE_FIELD( m_flFlockNextSoundTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( m_flTempVar, FIELD_FLOAT ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
LINK_ENTITY_TO_CLASS( monster_flyer, CNPC_FlockingFlyer );
|
||||
LINK_ENTITY_TO_CLASS( monster_flyer_flock, CNPC_FlockingFlyerFlock );
|
||||
|
||||
bool CNPC_FlockingFlyerFlock::KeyValue( const char *szKeyName, const char *szValue )
|
||||
{
|
||||
if ( FStrEq( szKeyName, "iFlockSize" ) )
|
||||
{
|
||||
m_cFlockSize = atoi( szValue );
|
||||
return true;
|
||||
}
|
||||
else if ( FStrEq( szKeyName, "flFlockRadius" ) )
|
||||
{
|
||||
m_flFlockRadius = atof( szValue );
|
||||
return true;
|
||||
}
|
||||
else
|
||||
BaseClass::KeyValue( szKeyName, szValue );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
void CNPC_FlockingFlyerFlock::Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
|
||||
SetRenderColor( 255, 255, 255, 255 );
|
||||
SpawnFlock();
|
||||
|
||||
|
||||
SetThink( &CBaseEntity::SUB_Remove );
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
void CNPC_FlockingFlyerFlock::Precache( void )
|
||||
{
|
||||
//PRECACHE_MODEL("models/aflock.mdl");
|
||||
PrecacheModel("models/boid.mdl");
|
||||
|
||||
PrecacheFlockSounds();
|
||||
}
|
||||
|
||||
void CNPC_FlockingFlyerFlock::SpawnFlock( void )
|
||||
{
|
||||
float R = m_flFlockRadius;
|
||||
int iCount;
|
||||
Vector vecSpot;
|
||||
CNPC_FlockingFlyer *pBoid, *pLeader;
|
||||
|
||||
pLeader = pBoid = NULL;
|
||||
|
||||
for ( iCount = 0 ; iCount < m_cFlockSize ; iCount++ )
|
||||
{
|
||||
pBoid = CREATE_ENTITY( CNPC_FlockingFlyer, "monster_flyer" );
|
||||
|
||||
if ( !pLeader )
|
||||
{
|
||||
// make this guy the leader.
|
||||
pLeader = pBoid;
|
||||
|
||||
pLeader->m_pSquadLeader = pLeader;
|
||||
pLeader->m_pSquadNext = NULL;
|
||||
}
|
||||
|
||||
vecSpot.x = random->RandomFloat( -R, R );
|
||||
vecSpot.y = random->RandomFloat( -R, R );
|
||||
vecSpot.z = random->RandomFloat( 0, 16 );
|
||||
vecSpot = GetAbsOrigin() + vecSpot;
|
||||
|
||||
UTIL_SetOrigin( pBoid, vecSpot);
|
||||
pBoid->SetMoveType( MOVETYPE_FLY );
|
||||
pBoid->SpawnCommonCode();
|
||||
pBoid->SetGroundEntity( NULL );
|
||||
pBoid->SetAbsVelocity( Vector ( 0, 0, 0 ) );
|
||||
pBoid->SetAbsAngles( GetAbsAngles() );
|
||||
|
||||
pBoid->SetCycle( 0 );
|
||||
pBoid->SetThink( &CNPC_FlockingFlyer::IdleThink );
|
||||
pBoid->SetNextThink( gpGlobals->curtime + 0.2 );
|
||||
|
||||
if ( pBoid != pLeader )
|
||||
{
|
||||
pLeader->SquadAdd( pBoid );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CNPC_FlockingFlyerFlock::PrecacheFlockSounds( void )
|
||||
{
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
void CNPC_FlockingFlyer::Spawn( )
|
||||
{
|
||||
Precache( );
|
||||
SpawnCommonCode();
|
||||
|
||||
SetCycle( 0 );
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
SetThink( &CNPC_FlockingFlyer::IdleThink );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
void CNPC_FlockingFlyer::SpawnCommonCode( )
|
||||
{
|
||||
m_lifeState = LIFE_ALIVE;
|
||||
SetClassname( "monster_flyer" );
|
||||
SetSolid( SOLID_BBOX );
|
||||
AddSolidFlags( FSOLID_NOT_STANDABLE );
|
||||
SetMoveType( MOVETYPE_FLY );
|
||||
m_takedamage = DAMAGE_NO;
|
||||
m_iHealth = 1;
|
||||
|
||||
m_fPathBlocked = FALSE;// obstacles will be detected
|
||||
m_flFieldOfView = 0.2;
|
||||
m_flTempVar = 0;
|
||||
|
||||
//SET_MODEL(ENT(pev), "models/aflock.mdl");
|
||||
SetModel( "models/boid.mdl" );
|
||||
|
||||
// UTIL_SetSize(this, Vector(0,0,0), Vector(0,0,0));
|
||||
UTIL_SetSize(this, Vector(-5,-5,0), Vector(5,5,2));
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
void CNPC_FlockingFlyer::Precache( )
|
||||
{
|
||||
//PRECACHE_MODEL("models/aflock.mdl");
|
||||
PrecacheModel("models/boid.mdl");
|
||||
CNPC_FlockingFlyerFlock::PrecacheFlockSounds();
|
||||
|
||||
PrecacheScriptSound( "FlockingFlyer.Alert" );
|
||||
PrecacheScriptSound( "FlockingFlyer.Idle" );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
void CNPC_FlockingFlyer::IdleThink( void )
|
||||
{
|
||||
SetNextThink( gpGlobals->curtime + 0.2 );
|
||||
|
||||
// see if there's a client in the same pvs as the monster
|
||||
if ( !FNullEnt( UTIL_FindClientInPVS( edict() ) ) )
|
||||
{
|
||||
SetThink( &CNPC_FlockingFlyer::Start );
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//
|
||||
// SquadUnlink(), Unlink the squad pointers.
|
||||
//
|
||||
//=========================================================
|
||||
void CNPC_FlockingFlyer::SquadUnlink( void )
|
||||
{
|
||||
m_pSquadLeader = NULL;
|
||||
m_pSquadNext = NULL;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//
|
||||
// SquadAdd(), add pAdd to my squad
|
||||
//
|
||||
//=========================================================
|
||||
void CNPC_FlockingFlyer::SquadAdd( CNPC_FlockingFlyer *pAdd )
|
||||
{
|
||||
ASSERT( pAdd!=NULL );
|
||||
ASSERT( !pAdd->InSquad() );
|
||||
ASSERT( this->IsLeader() );
|
||||
|
||||
pAdd->m_pSquadNext = m_pSquadNext;
|
||||
m_pSquadNext = pAdd;
|
||||
pAdd->m_pSquadLeader = this;
|
||||
}
|
||||
//=========================================================
|
||||
//
|
||||
// SquadRemove(), remove pRemove from my squad.
|
||||
// If I am pRemove, promote m_pSquadNext to leader
|
||||
//
|
||||
//=========================================================
|
||||
void CNPC_FlockingFlyer::SquadRemove( CNPC_FlockingFlyer *pRemove )
|
||||
{
|
||||
ASSERT( pRemove!=NULL );
|
||||
ASSERT( this->IsLeader() );
|
||||
ASSERT( pRemove->m_pSquadLeader == this );
|
||||
|
||||
if ( SquadCount() > 2 )
|
||||
{
|
||||
// Removing the leader, promote m_pSquadNext to leader
|
||||
if ( pRemove == this )
|
||||
{
|
||||
CNPC_FlockingFlyer *pLeader = m_pSquadNext;
|
||||
|
||||
// copy the enemy LKP to the new leader
|
||||
|
||||
// if ( GetEnemy() )
|
||||
// pLeader->m_vecEnemyLKP = m_vecEnemyLKP;
|
||||
|
||||
if ( pLeader )
|
||||
{
|
||||
CNPC_FlockingFlyer *pList = pLeader;
|
||||
|
||||
while ( pList )
|
||||
{
|
||||
pList->m_pSquadLeader = pLeader;
|
||||
pList = pList->m_pSquadNext;
|
||||
}
|
||||
|
||||
}
|
||||
SquadUnlink();
|
||||
}
|
||||
else // removing a node
|
||||
{
|
||||
CNPC_FlockingFlyer *pList = this;
|
||||
|
||||
// Find the node before pRemove
|
||||
while ( pList->m_pSquadNext != pRemove )
|
||||
{
|
||||
// assert to test valid list construction
|
||||
ASSERT( pList->m_pSquadNext != NULL );
|
||||
pList = pList->m_pSquadNext;
|
||||
}
|
||||
// List validity
|
||||
ASSERT( pList->m_pSquadNext == pRemove );
|
||||
|
||||
// Relink without pRemove
|
||||
pList->m_pSquadNext = pRemove->m_pSquadNext;
|
||||
|
||||
// Unlink pRemove
|
||||
pRemove->SquadUnlink();
|
||||
}
|
||||
}
|
||||
else
|
||||
SquadDisband();
|
||||
}
|
||||
//=========================================================
|
||||
//
|
||||
// SquadCount(), return the number of members of this squad
|
||||
// callable from leaders & followers
|
||||
//
|
||||
//=========================================================
|
||||
int CNPC_FlockingFlyer::SquadCount( void )
|
||||
{
|
||||
CNPC_FlockingFlyer *pList = m_pSquadLeader;
|
||||
int squadCount = 0;
|
||||
while ( pList )
|
||||
{
|
||||
squadCount++;
|
||||
pList = pList->m_pSquadNext;
|
||||
}
|
||||
|
||||
return squadCount;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//
|
||||
// SquadDisband(), Unlink all squad members
|
||||
//
|
||||
//=========================================================
|
||||
void CNPC_FlockingFlyer::SquadDisband( void )
|
||||
{
|
||||
CNPC_FlockingFlyer *pList = m_pSquadLeader;
|
||||
CNPC_FlockingFlyer *pNext;
|
||||
|
||||
while ( pList )
|
||||
{
|
||||
pNext = pList->m_pSquadNext;
|
||||
pList->SquadUnlink();
|
||||
pList = pNext;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Start - player enters the pvs, so get things going.
|
||||
//=========================================================
|
||||
void CNPC_FlockingFlyer::Start( void )
|
||||
{
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
|
||||
if ( IsLeader() )
|
||||
{
|
||||
SetThink( &CNPC_FlockingFlyer::FlockLeaderThink );
|
||||
}
|
||||
else
|
||||
{
|
||||
SetThink( &CNPC_FlockingFlyer::FlockFollowerThink );
|
||||
}
|
||||
|
||||
SetActivity ( ACT_FLY );
|
||||
ResetSequenceInfo( );
|
||||
BoidAdvanceFrame( );
|
||||
|
||||
m_flSpeed = AFLOCK_FLY_SPEED;// no delay!
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
void CNPC_FlockingFlyer::BoidAdvanceFrame ( void )
|
||||
{
|
||||
float flapspeed = ( m_flSpeed - m_flTempVar ) / AFLOCK_ACCELERATE;
|
||||
m_flTempVar = m_flTempVar * .8 + m_flSpeed * .2;
|
||||
|
||||
if (flapspeed < 0) flapspeed = -flapspeed;
|
||||
if (flapspeed < 0.25) flapspeed = 0.25;
|
||||
if (flapspeed > 1.9) flapspeed = 1.9;
|
||||
|
||||
m_flPlaybackRate = flapspeed;
|
||||
|
||||
QAngle angVel = GetLocalAngularVelocity();
|
||||
|
||||
// lean
|
||||
angVel.x = - GetAbsAngles().x + flapspeed * 5;
|
||||
|
||||
// bank
|
||||
angVel.z = - GetAbsAngles().z + angVel.y;
|
||||
|
||||
SetLocalAngularVelocity( angVel );
|
||||
|
||||
// pev->framerate = flapspeed;
|
||||
StudioFrameAdvance();
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Leader boids use this think every tenth
|
||||
//=========================================================
|
||||
void CNPC_FlockingFlyer::FlockLeaderThink( void )
|
||||
{
|
||||
trace_t tr;
|
||||
Vector vecDist;// used for general measurements
|
||||
Vector vecDir;// used for general measurements
|
||||
float flLeftSide;
|
||||
float flRightSide;
|
||||
Vector vForward, vRight, vUp;
|
||||
|
||||
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
|
||||
AngleVectors ( GetAbsAngles(), &vForward, &vRight, &vUp );
|
||||
|
||||
// is the way ahead clear?
|
||||
if ( !FPathBlocked () )
|
||||
{
|
||||
// if the boid is turning, stop the trend.
|
||||
if ( m_fTurning )
|
||||
{
|
||||
m_fTurning = FALSE;
|
||||
|
||||
QAngle angVel = GetLocalAngularVelocity();
|
||||
angVel.y = 0;
|
||||
SetLocalAngularVelocity( angVel );
|
||||
}
|
||||
|
||||
m_fPathBlocked = FALSE;
|
||||
|
||||
if ( m_flSpeed <= AFLOCK_FLY_SPEED )
|
||||
m_flSpeed += 5;
|
||||
|
||||
SetAbsVelocity( vForward * m_flSpeed );
|
||||
|
||||
BoidAdvanceFrame( );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// IF we get this far in the function, the leader's path is blocked!
|
||||
m_fPathBlocked = TRUE;
|
||||
|
||||
if ( !m_fTurning)// something in the way and boid is not already turning to avoid
|
||||
{
|
||||
// measure clearance on left and right to pick the best dir to turn
|
||||
UTIL_TraceLine(GetAbsOrigin(), GetAbsOrigin() + vRight * AFLOCK_CHECK_DIST, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr);
|
||||
vecDist = (tr.endpos - GetAbsOrigin());
|
||||
flRightSide = vecDist.Length();
|
||||
|
||||
UTIL_TraceLine(GetAbsOrigin(), GetAbsOrigin() - vRight * AFLOCK_CHECK_DIST, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr);
|
||||
vecDist = (tr.endpos - GetAbsOrigin());
|
||||
flLeftSide = vecDist.Length();
|
||||
|
||||
// turn right if more clearance on right side
|
||||
if ( flRightSide > flLeftSide )
|
||||
{
|
||||
QAngle angVel = GetLocalAngularVelocity();
|
||||
angVel.y = -AFLOCK_TURN_RATE;
|
||||
SetLocalAngularVelocity( angVel );
|
||||
|
||||
m_fTurning = TRUE;
|
||||
}
|
||||
// default to left turn :)
|
||||
else if ( flLeftSide > flRightSide )
|
||||
{
|
||||
QAngle angVel = GetLocalAngularVelocity();
|
||||
angVel.y = AFLOCK_TURN_RATE;
|
||||
SetLocalAngularVelocity( angVel );
|
||||
|
||||
m_fTurning = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
// equidistant. Pick randomly between left and right.
|
||||
m_fTurning = TRUE;
|
||||
|
||||
QAngle angVel = GetLocalAngularVelocity();
|
||||
|
||||
if ( random->RandomInt( 0, 1 ) == 0 )
|
||||
{
|
||||
angVel.y = AFLOCK_TURN_RATE;
|
||||
}
|
||||
else
|
||||
{
|
||||
angVel.y = -AFLOCK_TURN_RATE;
|
||||
}
|
||||
|
||||
SetLocalAngularVelocity( angVel );
|
||||
}
|
||||
}
|
||||
|
||||
SpreadFlock( );
|
||||
|
||||
SetAbsVelocity( vForward * m_flSpeed );
|
||||
|
||||
// check and make sure we aren't about to plow into the ground, don't let it happen
|
||||
UTIL_TraceLine(GetAbsOrigin(), GetAbsOrigin() - vUp * 16, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr);
|
||||
if (tr.fraction != 1.0 && GetAbsVelocity().z < 0 )
|
||||
{
|
||||
Vector vecVel = GetAbsVelocity();
|
||||
vecVel.z = 0;
|
||||
SetAbsVelocity( vecVel );
|
||||
}
|
||||
|
||||
// maybe it did, though.
|
||||
if ( GetFlags() & FL_ONGROUND )
|
||||
{
|
||||
UTIL_SetOrigin( this, GetAbsOrigin() + Vector ( 0 , 0 , 1 ) );
|
||||
Vector vecVel = GetAbsVelocity();
|
||||
vecVel.z = 0;
|
||||
SetAbsVelocity( vecVel );
|
||||
}
|
||||
|
||||
if ( m_flFlockNextSoundTime < gpGlobals->curtime )
|
||||
{
|
||||
// MakeSound();
|
||||
m_flFlockNextSoundTime = gpGlobals->curtime + random->RandomFloat( 1, 3 );
|
||||
}
|
||||
|
||||
BoidAdvanceFrame( );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// FBoidPathBlocked - returns TRUE if there is an obstacle ahead
|
||||
//=========================================================
|
||||
bool CNPC_FlockingFlyer::FPathBlocked( void )
|
||||
{
|
||||
trace_t tr;
|
||||
Vector vecDist;// used for general measurements
|
||||
Vector vecDir;// used for general measurements
|
||||
bool fBlocked;
|
||||
Vector vForward, vRight, vUp;
|
||||
|
||||
if ( m_flFakeBlockedTime > gpGlobals->curtime )
|
||||
{
|
||||
m_flLastBlockedTime = gpGlobals->curtime;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// use VELOCITY, not angles, not all boids point the direction they are flying
|
||||
//vecDir = UTIL_VecToAngles( pevBoid->velocity );
|
||||
AngleVectors ( GetAbsAngles(), &vForward, &vRight, &vUp );
|
||||
|
||||
fBlocked = FALSE;// assume the way ahead is clear
|
||||
|
||||
// check for obstacle ahead
|
||||
UTIL_TraceLine(GetAbsOrigin(), GetAbsOrigin() + vForward * AFLOCK_CHECK_DIST, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr);
|
||||
|
||||
if (tr.fraction != 1.0)
|
||||
{
|
||||
m_flLastBlockedTime = gpGlobals->curtime;
|
||||
fBlocked = TRUE;
|
||||
}
|
||||
|
||||
// extra wide checks
|
||||
UTIL_TraceLine(GetAbsOrigin() + vRight * 12, GetAbsOrigin() + vRight * 12 + vForward * AFLOCK_CHECK_DIST, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr);
|
||||
|
||||
if (tr.fraction != 1.0)
|
||||
{
|
||||
m_flLastBlockedTime = gpGlobals->curtime;
|
||||
fBlocked = TRUE;
|
||||
}
|
||||
|
||||
UTIL_TraceLine(GetAbsOrigin() - vRight * 12, GetAbsOrigin() - vRight * 12 + vForward * AFLOCK_CHECK_DIST, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr);
|
||||
|
||||
if (tr.fraction != 1.0)
|
||||
{
|
||||
m_flLastBlockedTime = gpGlobals->curtime;
|
||||
fBlocked = TRUE;
|
||||
}
|
||||
|
||||
if ( !fBlocked && gpGlobals->curtime - m_flLastBlockedTime > 6 )
|
||||
{
|
||||
// not blocked, and it's been a few seconds since we've actually been blocked.
|
||||
m_flFakeBlockedTime = gpGlobals->curtime + random->RandomInt(1, 3);
|
||||
}
|
||||
|
||||
return fBlocked;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Searches for boids that are too close and pushes them away
|
||||
//=========================================================
|
||||
void CNPC_FlockingFlyer::SpreadFlock( )
|
||||
{
|
||||
Vector vecDir;
|
||||
float flSpeed;// holds vector magnitude while we fiddle with the direction
|
||||
|
||||
CNPC_FlockingFlyer *pList = m_pSquadLeader;
|
||||
while ( pList )
|
||||
{
|
||||
if ( pList != this && ( GetAbsOrigin() - pList->GetAbsOrigin() ).Length() <= AFLOCK_TOO_CLOSE )
|
||||
{
|
||||
// push the other away
|
||||
vecDir = ( pList->GetAbsOrigin() - GetAbsOrigin() );
|
||||
VectorNormalize( vecDir );
|
||||
|
||||
// store the magnitude of the other boid's velocity, and normalize it so we
|
||||
// can average in a course that points away from the leader.
|
||||
flSpeed = pList->GetAbsVelocity().Length();
|
||||
|
||||
Vector vecVel = pList->GetAbsVelocity();
|
||||
VectorNormalize( vecVel );
|
||||
pList->SetAbsVelocity( ( vecVel + vecDir ) * 0.5 * flSpeed );
|
||||
}
|
||||
|
||||
pList = pList->m_pSquadNext;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Alters the caller's course if he's too close to others
|
||||
//
|
||||
// This function should **ONLY** be called when Caller's velocity is normalized!!
|
||||
//=========================================================
|
||||
void CNPC_FlockingFlyer::SpreadFlock2 ( )
|
||||
{
|
||||
Vector vecDir;
|
||||
|
||||
CNPC_FlockingFlyer *pList = m_pSquadLeader;
|
||||
|
||||
while ( pList )
|
||||
{
|
||||
if ( pList != this && ( GetAbsOrigin() - pList->GetAbsOrigin() ).Length() <= AFLOCK_TOO_CLOSE )
|
||||
{
|
||||
vecDir = ( GetAbsOrigin() - pList->GetAbsOrigin() );
|
||||
VectorNormalize( vecDir );
|
||||
|
||||
SetAbsVelocity( ( GetAbsVelocity() + vecDir ) );
|
||||
}
|
||||
|
||||
pList = pList->m_pSquadNext;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
void CNPC_FlockingFlyer::MakeSound( void )
|
||||
{
|
||||
if ( m_flAlertTime > gpGlobals->curtime )
|
||||
{
|
||||
CPASAttenuationFilter filter1( this );
|
||||
|
||||
// make agitated sounds
|
||||
EmitSound( filter1, entindex(), "FlockingFlyer.Alert" );
|
||||
return;
|
||||
}
|
||||
|
||||
// make normal sound
|
||||
CPASAttenuationFilter filter2( this );
|
||||
|
||||
EmitSound( filter2, entindex(), "FlockingFlyer.Idle" );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// follower boids execute this code when flocking
|
||||
//=========================================================
|
||||
void CNPC_FlockingFlyer::FlockFollowerThink( void )
|
||||
{
|
||||
Vector vecDist;
|
||||
Vector vecDir;
|
||||
Vector vecDirToLeader;
|
||||
float flDistToLeader;
|
||||
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
|
||||
if ( IsLeader() || !InSquad() )
|
||||
{
|
||||
// the leader has been killed and this flyer suddenly finds himself the leader.
|
||||
SetThink ( &CNPC_FlockingFlyer::FlockLeaderThink );
|
||||
return;
|
||||
}
|
||||
|
||||
vecDirToLeader = ( m_pSquadLeader->GetAbsOrigin() - GetAbsOrigin() );
|
||||
flDistToLeader = vecDirToLeader.Length();
|
||||
|
||||
// match heading with leader
|
||||
SetAbsAngles( m_pSquadLeader->GetAbsAngles() );
|
||||
|
||||
//
|
||||
// We can see the leader, so try to catch up to it
|
||||
//
|
||||
if ( FInViewCone ( m_pSquadLeader ) )
|
||||
{
|
||||
// if we're too far away, speed up
|
||||
if ( flDistToLeader > AFLOCK_TOO_FAR )
|
||||
{
|
||||
m_flGoalSpeed = m_pSquadLeader->GetAbsVelocity().Length() * 1.5;
|
||||
}
|
||||
|
||||
// if we're too close, slow down
|
||||
else if ( flDistToLeader < AFLOCK_TOO_CLOSE )
|
||||
{
|
||||
m_flGoalSpeed = m_pSquadLeader->GetAbsVelocity().Length() * 0.5;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// wait up! the leader isn't out in front, so we slow down to let him pass
|
||||
m_flGoalSpeed = m_pSquadLeader->GetAbsVelocity().Length() * 0.5;
|
||||
}
|
||||
|
||||
SpreadFlock2();
|
||||
|
||||
Vector vecVel = GetAbsVelocity();
|
||||
m_flSpeed = vecVel.Length();
|
||||
VectorNormalize( vecVel );
|
||||
|
||||
// if we are too far from leader, average a vector towards it into our current velocity
|
||||
if ( flDistToLeader > AFLOCK_TOO_FAR )
|
||||
{
|
||||
VectorNormalize( vecDirToLeader );
|
||||
vecVel = (vecVel + vecDirToLeader) * 0.5;
|
||||
}
|
||||
|
||||
// clamp speeds and handle acceleration
|
||||
if ( m_flGoalSpeed > AFLOCK_FLY_SPEED * 2 )
|
||||
{
|
||||
m_flGoalSpeed = AFLOCK_FLY_SPEED * 2;
|
||||
}
|
||||
|
||||
if ( m_flSpeed < m_flGoalSpeed )
|
||||
{
|
||||
m_flSpeed += AFLOCK_ACCELERATE;
|
||||
}
|
||||
else if ( m_flSpeed > m_flGoalSpeed )
|
||||
{
|
||||
m_flSpeed -= AFLOCK_ACCELERATE;
|
||||
}
|
||||
|
||||
SetAbsVelocity( vecVel * m_flSpeed );
|
||||
|
||||
BoidAdvanceFrame( );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
void CNPC_FlockingFlyer::Event_Killed( const CTakeDamageInfo &info )
|
||||
{
|
||||
CNPC_FlockingFlyer *pSquad;
|
||||
|
||||
pSquad = (CNPC_FlockingFlyer *)m_pSquadLeader;
|
||||
|
||||
while ( pSquad )
|
||||
{
|
||||
pSquad->m_flAlertTime = gpGlobals->curtime + 15;
|
||||
pSquad = (CNPC_FlockingFlyer *)pSquad->m_pSquadNext;
|
||||
}
|
||||
|
||||
if ( m_pSquadLeader )
|
||||
{
|
||||
m_pSquadLeader->SquadRemove( this );
|
||||
}
|
||||
|
||||
m_lifeState = LIFE_DEAD;
|
||||
|
||||
m_flPlaybackRate = 0;
|
||||
IncrementInterpolationFrame();
|
||||
|
||||
UTIL_SetSize( this, Vector(0,0,0), Vector(0,0,0) );
|
||||
SetMoveType( MOVETYPE_FLYGRAVITY );
|
||||
|
||||
SetThink ( &CNPC_FlockingFlyer::FallHack );
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
}
|
||||
|
||||
void CNPC_FlockingFlyer::FallHack( void )
|
||||
{
|
||||
if ( GetFlags() & FL_ONGROUND )
|
||||
{
|
||||
CBaseEntity *groundentity = GetContainingEntity( GetGroundEntity()->edict() );
|
||||
|
||||
if ( !FClassnameIs ( groundentity, "worldspawn" ) )
|
||||
{
|
||||
SetGroundEntity( NULL );
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
}
|
||||
else
|
||||
{
|
||||
SetAbsVelocity( Vector( 0, 0, 0 ) );
|
||||
SetThink( NULL );
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,766 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
#include "cbase.h"
|
||||
#include "ai_default.h"
|
||||
#include "ai_task.h"
|
||||
#include "ai_schedule.h"
|
||||
#include "ai_node.h"
|
||||
#include "ai_hull.h"
|
||||
#include "ai_hint.h"
|
||||
#include "ai_memory.h"
|
||||
#include "ai_route.h"
|
||||
#include "ai_motor.h"
|
||||
#include "soundent.h"
|
||||
#include "game.h"
|
||||
#include "npcevent.h"
|
||||
#include "entitylist.h"
|
||||
#include "activitylist.h"
|
||||
#include "hl1_basegrenade.h"
|
||||
#include "animation.h"
|
||||
#include "IEffects.h"
|
||||
#include "vstdlib/random.h"
|
||||
#include "engine/IEngineSound.h"
|
||||
#include "ammodef.h"
|
||||
#include "soundenvelope.h"
|
||||
#include "hl1_CBaseHelicopter.h"
|
||||
#include "ndebugoverlay.h"
|
||||
#include "smoke_trail.h"
|
||||
#include "beam_shared.h"
|
||||
#include "grenade_homer.h"
|
||||
|
||||
#define HOMER_TRAIL0_LIFE 0.1
|
||||
#define HOMER_TRAIL1_LIFE 0.2
|
||||
#define HOMER_TRAIL2_LIFE 3.0// 1.0
|
||||
|
||||
#define SF_NOTRANSITION 128
|
||||
|
||||
extern short g_sModelIndexFireball;
|
||||
|
||||
class CNPC_Apache : public CBaseHelicopter
|
||||
{
|
||||
DECLARE_CLASS( CNPC_Apache, CBaseHelicopter );
|
||||
public:
|
||||
DECLARE_DATADESC();
|
||||
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
|
||||
int BloodColor( void ) { return DONT_BLEED; }
|
||||
Class_T Classify( void ) { return CLASS_HUMAN_MILITARY; };
|
||||
void InitializeRotorSound( void );
|
||||
void LaunchRocket( Vector &viewDir, int damage, int radius, Vector vecLaunchPoint );
|
||||
|
||||
void Flight( void );
|
||||
|
||||
bool FireGun( void );
|
||||
void AimRocketGun( void );
|
||||
void FireRocket( void );
|
||||
void DyingThink( void );
|
||||
|
||||
int ObjectCaps( void );
|
||||
|
||||
void TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator );
|
||||
|
||||
/* bool OnInternalDrawModel( ClientModelRenderInfo_t *pInfo )
|
||||
{
|
||||
BaseClass::OnInteralDrawModel( pInfo );
|
||||
Vector origin = GetAbsOrigin();
|
||||
origin.z += 32;
|
||||
SetAbsOrigin( origin );
|
||||
}*/
|
||||
|
||||
/*( void SetAbsOrigin( const Vector& absOrigin )
|
||||
{
|
||||
((Vector&)absOrigin).z += 32;
|
||||
BaseClass::SetAbsOrigin( absOrigin );
|
||||
}*/
|
||||
|
||||
|
||||
|
||||
/* int Save( CSave &save );
|
||||
int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
|
||||
|
||||
void Killed( entvars_t *pevAttacker, int iGib );
|
||||
void GibMonster( void );
|
||||
|
||||
void EXPORT HuntThink( void );
|
||||
void EXPORT FlyTouch( CBaseEntity *pOther );
|
||||
void EXPORT CrashTouch( CBaseEntity *pOther );
|
||||
void EXPORT DyingThink( void );
|
||||
void EXPORT StartupUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
void EXPORT NullThink( void );
|
||||
|
||||
void ShowDamage( void );
|
||||
void Flight( void );
|
||||
void FireRocket( void );
|
||||
BOOL FireGun( void );
|
||||
|
||||
int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType );
|
||||
void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType);*/
|
||||
|
||||
int m_iRockets;
|
||||
float m_flForce;
|
||||
float m_flNextRocket;
|
||||
|
||||
int m_iAmmoType;
|
||||
|
||||
Vector m_vecTarget;
|
||||
Vector m_posTarget;
|
||||
|
||||
Vector m_vecDesired;
|
||||
Vector m_posDesired;
|
||||
|
||||
Vector m_vecGoal;
|
||||
|
||||
QAngle m_angGun;
|
||||
|
||||
int m_iSoundState; // don't save this
|
||||
|
||||
int m_iExplode;
|
||||
int m_iBodyGibs;
|
||||
int m_nDebrisModel;
|
||||
|
||||
float m_flGoalSpeed;
|
||||
|
||||
CHandle<SmokeTrail> m_hSmoke;
|
||||
CBeam *m_pBeam;
|
||||
};
|
||||
|
||||
BEGIN_DATADESC( CNPC_Apache )
|
||||
DEFINE_FIELD( m_iRockets, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( m_flForce, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( m_flNextRocket, FIELD_TIME ),
|
||||
DEFINE_FIELD( m_vecTarget, FIELD_VECTOR ),
|
||||
DEFINE_FIELD( m_posTarget, FIELD_POSITION_VECTOR ),
|
||||
DEFINE_FIELD( m_vecDesired, FIELD_VECTOR ),
|
||||
DEFINE_FIELD( m_posDesired, FIELD_POSITION_VECTOR ),
|
||||
DEFINE_FIELD( m_vecGoal, FIELD_VECTOR ),
|
||||
DEFINE_FIELD( m_angGun, FIELD_VECTOR ),
|
||||
DEFINE_FIELD( m_flLastSeen, FIELD_TIME ),
|
||||
DEFINE_FIELD( m_flPrevSeen, FIELD_TIME ),
|
||||
// DEFINE_FIELD( m_iSoundState, FIELD_INTEGER ),
|
||||
// DEFINE_FIELD( m_iSpriteTexture, FIELD_INTEGER ),
|
||||
// DEFINE_FIELD( m_iExplode, FIELD_INTEGER ),
|
||||
// DEFINE_FIELD( m_iBodyGibs, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( m_pBeam, FIELD_CLASSPTR ),
|
||||
DEFINE_FIELD( m_flGoalSpeed, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( m_hSmoke, FIELD_EHANDLE ),
|
||||
END_DATADESC()
|
||||
|
||||
ConVar sk_apache_health( "sk_apache_health","100");
|
||||
|
||||
static Vector s_vecSurroundingMins( -300, -300, -172);
|
||||
static Vector s_vecSurroundingMaxs(300, 300, 8);
|
||||
|
||||
|
||||
void CNPC_Apache::Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
SetModel( "models/apache.mdl" );
|
||||
|
||||
BaseClass::Spawn();
|
||||
|
||||
AddFlag( FL_NPC );
|
||||
SetSolid( SOLID_BBOX );
|
||||
SetMoveType( MOVETYPE_STEP );
|
||||
AddFlag( FL_FLY );
|
||||
|
||||
|
||||
m_iHealth = sk_apache_health.GetFloat();
|
||||
|
||||
m_flFieldOfView = -0.707; // 270 degrees
|
||||
|
||||
m_fHelicopterFlags = BITS_HELICOPTER_MISSILE_ON | BITS_HELICOPTER_GUN_ON;
|
||||
|
||||
InitBoneControllers();
|
||||
|
||||
SetRenderColor( 255, 255, 255, 255 );
|
||||
|
||||
m_iRockets = 10;
|
||||
|
||||
UTIL_SetSize( this, Vector( -32, -32, -32 ), Vector( 32, 32, 32 ) );
|
||||
|
||||
//CollisionProp()->SetSurroundingBoundsType( USE_SPECIFIED_BOUNDS, &s_vecSurroundingMins, &s_vecSurroundingMaxs );
|
||||
//AddSolidFlags( FSOLID_CUSTOMRAYTEST | FSOLID_CUSTOMBOXTEST );
|
||||
|
||||
m_hSmoke = NULL;
|
||||
}
|
||||
|
||||
LINK_ENTITY_TO_CLASS ( monster_apache, CNPC_Apache );
|
||||
|
||||
int CNPC_Apache::ObjectCaps( void )
|
||||
{
|
||||
if ( GetSpawnFlags() & SF_NOTRANSITION )
|
||||
return BaseClass::ObjectCaps() & ~FCAP_ACROSS_TRANSITION;
|
||||
else
|
||||
return BaseClass::ObjectCaps();
|
||||
}
|
||||
|
||||
void CNPC_Apache::Precache( void )
|
||||
{
|
||||
// Get to tha chopper!
|
||||
PrecacheModel( "models/apache.mdl" );
|
||||
PrecacheScriptSound( "Apache.Rotor" );
|
||||
m_nDebrisModel = PrecacheModel( "models/metalplategibs_green.mdl" );
|
||||
|
||||
// Gun
|
||||
PrecacheScriptSound( "Apache.FireGun" );
|
||||
m_iAmmoType = GetAmmoDef()->Index("9mmRound");
|
||||
|
||||
// Rockets
|
||||
UTIL_PrecacheOther( "grenade_homer" );
|
||||
PrecacheScriptSound( "Apache.RPG" );
|
||||
PrecacheModel( "models/weapons/w_missile.mdl" );
|
||||
|
||||
BaseClass::Precache();
|
||||
}
|
||||
|
||||
void CNPC_Apache::InitializeRotorSound( void )
|
||||
{
|
||||
CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
|
||||
|
||||
CPASAttenuationFilter filter( this );
|
||||
m_pRotorSound = controller.SoundCreate( filter, entindex(), "Apache.Rotor" );
|
||||
|
||||
BaseClass::InitializeRotorSound();
|
||||
}
|
||||
|
||||
void CNPC_Apache::Flight( void )
|
||||
{
|
||||
StudioFrameAdvance( );
|
||||
|
||||
float flDistToDesiredPosition = (GetAbsOrigin() - m_vecDesiredPosition).Length();
|
||||
// NDebugOverlay::Line(GetAbsOrigin(), m_vecDesiredPosition, 0,0,255, true, 0.1);
|
||||
|
||||
if (m_flGoalSpeed < 800 )
|
||||
m_flGoalSpeed += GetAcceleration();
|
||||
|
||||
|
||||
// Vector vecGoalOrientation;
|
||||
if (flDistToDesiredPosition > 250) // 500
|
||||
{
|
||||
Vector v1 = (m_vecTargetPosition - GetAbsOrigin());
|
||||
Vector v2 = (m_vecDesiredPosition - GetAbsOrigin());
|
||||
|
||||
VectorNormalize( v1 );
|
||||
VectorNormalize( v2 );
|
||||
|
||||
if (m_flLastSeen + 90 > gpGlobals->curtime && DotProduct( v1, v2 ) > 0.25)
|
||||
{
|
||||
m_vecGoalOrientation = ( m_vecTargetPosition - GetAbsOrigin());
|
||||
}
|
||||
else
|
||||
{
|
||||
m_vecGoalOrientation = (m_vecDesiredPosition - GetAbsOrigin());
|
||||
}
|
||||
VectorNormalize( m_vecGoalOrientation );
|
||||
}
|
||||
else
|
||||
{
|
||||
AngleVectors( GetGoalEnt()->GetAbsAngles(), &m_vecGoalOrientation );
|
||||
}
|
||||
// SetGoalOrientation( vecGoalOrientation );
|
||||
|
||||
|
||||
if (GetGoalEnt())
|
||||
{
|
||||
// ALERT( at_console, "%.0f\n", flLength );
|
||||
if ( HasReachedTarget() )
|
||||
{
|
||||
// If we get this close to the desired position, it's assumed that we've reached
|
||||
// the desired position, so move on.
|
||||
|
||||
// Fire target that I've reached my goal
|
||||
m_AtTarget.FireOutput( GetGoalEnt(), this );
|
||||
|
||||
OnReachedTarget( GetGoalEnt() );
|
||||
|
||||
SetGoalEnt( gEntList.FindEntityByName( NULL, GetGoalEnt()->m_target ) );
|
||||
|
||||
if (GetGoalEnt())
|
||||
{
|
||||
m_vecDesiredPosition = GetGoalEnt()->GetAbsOrigin();
|
||||
|
||||
// Vector vecGoalOrientation;
|
||||
AngleVectors( GetGoalEnt()->GetAbsAngles(), &m_vecGoalOrientation );
|
||||
|
||||
// SetGoalOrientation( vecGoalOrientation );
|
||||
|
||||
flDistToDesiredPosition = (GetAbsOrigin() - m_vecDesiredPosition).Length();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we can't find a new target, just stay where we are.
|
||||
m_vecDesiredPosition = GetAbsOrigin();
|
||||
}
|
||||
|
||||
// tilt model 5 degrees
|
||||
QAngle angAdj = QAngle( 5.0, 0, 0 );
|
||||
|
||||
// estimate where I'll be facing in one seconds
|
||||
Vector forward, right, up;
|
||||
AngleVectors( GetAbsAngles() + GetLocalAngularVelocity() * 2 + angAdj, &forward, &right, &up );
|
||||
// Vector vecEst1 = GetAbsOrigin() + pev->velocity + gpGlobals->v_up * m_flForce - Vector( 0, 0, 384 );
|
||||
// float flSide = DotProduct( m_posDesired - vecEst1, gpGlobals->v_right );
|
||||
|
||||
|
||||
QAngle angVel = GetLocalAngularVelocity();
|
||||
float flSide = DotProduct( m_vecGoalOrientation, right );
|
||||
|
||||
if (flSide < 0)
|
||||
{
|
||||
if ( angVel.y < 60)
|
||||
{
|
||||
angVel.y += 8; // 9 * (3.0/2.0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( angVel.y > -60)
|
||||
{
|
||||
angVel.y -= 8; // 9 * (3.0/2.0);
|
||||
}
|
||||
}
|
||||
angVel.y *= 0.98;
|
||||
SetLocalAngularVelocity( angVel );
|
||||
|
||||
Vector vecVel = GetAbsVelocity();
|
||||
|
||||
// estimate where I'll be in two seconds
|
||||
AngleVectors( GetAbsAngles() + GetLocalAngularVelocity() * 1 + angAdj, &forward, &right, &up );
|
||||
Vector vecEst = GetAbsOrigin() + vecVel * 2.0 + up * m_flForce * 20 - Vector( 0, 0, 384 * 2 );
|
||||
|
||||
// add immediate force
|
||||
AngleVectors( GetAbsAngles() + angAdj, &forward, &right, &up );
|
||||
|
||||
vecVel.x += up.x * m_flForce;
|
||||
vecVel.y += up.y * m_flForce;
|
||||
vecVel.z += up.z * m_flForce;
|
||||
// add gravity
|
||||
vecVel.z -= 38.4; // 32ft/sec
|
||||
|
||||
float flSpeed = vecVel.Length();
|
||||
float flDir = DotProduct( Vector( forward.x, forward.y, 0 ), Vector( vecVel.x, vecVel.y, 0 ) );
|
||||
if (flDir < 0)
|
||||
flSpeed = -flSpeed;
|
||||
|
||||
float flDist = DotProduct( m_vecDesiredPosition - vecEst, forward );
|
||||
|
||||
// float flSlip = DotProduct( pev->velocity, gpGlobals->v_right );
|
||||
float flSlip = -DotProduct( m_vecDesiredPosition - vecEst, right );
|
||||
|
||||
angVel = GetLocalAngularVelocity();
|
||||
// fly sideways
|
||||
if (flSlip > 0)
|
||||
{
|
||||
if (GetAbsAngles().z > -30 && angVel.z > -15)
|
||||
angVel.z -= 4;
|
||||
else
|
||||
angVel.z += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if (GetAbsAngles().z < 30 && angVel.z < 15)
|
||||
angVel.z += 4;
|
||||
else
|
||||
angVel.z -= 2;
|
||||
}
|
||||
SetLocalAngularVelocity( angVel );
|
||||
|
||||
// sideways drag
|
||||
vecVel.x = vecVel.x * (1.0 - fabs( right.x ) * 0.05);
|
||||
vecVel.y = vecVel.y * (1.0 - fabs( right.y ) * 0.05);
|
||||
vecVel.z = vecVel.z * (1.0 - fabs( right.z ) * 0.05);
|
||||
|
||||
// general drag
|
||||
vecVel = vecVel * 0.995;
|
||||
|
||||
// Set final computed velocity
|
||||
SetAbsVelocity( vecVel );
|
||||
|
||||
// apply power to stay correct height
|
||||
if (m_flForce < 80 && vecEst.z < m_vecDesiredPosition.z)
|
||||
{
|
||||
m_flForce += 12;
|
||||
}
|
||||
else if (m_flForce > 30)
|
||||
{
|
||||
if (vecEst.z > m_vecDesiredPosition.z)
|
||||
m_flForce -= 8;
|
||||
}
|
||||
|
||||
|
||||
angVel = GetLocalAngularVelocity();
|
||||
// pitch forward or back to get to target
|
||||
if (flDist > 0 && flSpeed < m_flGoalSpeed && GetAbsAngles().x + angVel.x < 40)
|
||||
{
|
||||
// ALERT( at_console, "F " );
|
||||
// lean forward
|
||||
angVel.x += 12.0;
|
||||
}
|
||||
else if (flDist < 0 && flSpeed > -50 && GetAbsAngles().x + angVel.x > -20)
|
||||
{
|
||||
// ALERT( at_console, "B " );
|
||||
// lean backward
|
||||
angVel.x -= 12.0;
|
||||
}
|
||||
else if (GetAbsAngles().x + angVel.x < 0)
|
||||
{
|
||||
// ALERT( at_console, "f " );
|
||||
angVel.x += 4.0;
|
||||
}
|
||||
else if (GetAbsAngles().x + angVel.x > 0)
|
||||
{
|
||||
// ALERT( at_console, "b " );
|
||||
angVel.x -= 4.0;
|
||||
}
|
||||
|
||||
// Set final computed angular velocity
|
||||
SetLocalAngularVelocity( angVel );
|
||||
|
||||
// ALERT( at_console, "%.0f %.0f : %.0f %.0f : %.0f %.0f : %.0f\n", GetAbsOrigin().x, pev->velocity.x, flDist, flSpeed, GetAbsAngles().x, pev->avelocity.x, m_flForce );
|
||||
// ALERT( at_console, "%.0f %.0f : %.0f %0.f : %.0f\n", GetAbsOrigin().z, pev->velocity.z, vecEst.z, m_posDesired.z, m_flForce );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose :
|
||||
// Input :
|
||||
// Output :
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#define CHOPPER_AP_GUN_TIP 0
|
||||
#define CHOPPER_AP_GUN_BASE 1
|
||||
|
||||
#define CHOPPER_BC_GUN_YAW 0
|
||||
#define CHOPPER_BC_GUN_PITCH 1
|
||||
#define CHOPPER_BC_POD_PITCH 2
|
||||
|
||||
bool CNPC_Apache::FireGun( )
|
||||
{
|
||||
if ( !GetEnemy() )
|
||||
return false;
|
||||
|
||||
Vector vForward, vRight, vUp;
|
||||
|
||||
AngleVectors( GetAbsAngles(), &vForward, &vUp, &vRight );
|
||||
|
||||
Vector posGun;
|
||||
QAngle angGun;
|
||||
GetAttachment( 1, posGun, angGun );
|
||||
|
||||
Vector vecTarget = (m_vecTargetPosition - posGun);
|
||||
|
||||
VectorNormalize( vecTarget );
|
||||
|
||||
Vector vecOut;
|
||||
|
||||
vecOut.x = DotProduct( vForward, vecTarget );
|
||||
vecOut.y = -DotProduct( vUp, vecTarget );
|
||||
vecOut.z = DotProduct( vRight, vecTarget );
|
||||
|
||||
QAngle angles;
|
||||
|
||||
VectorAngles( vecOut, angles );
|
||||
|
||||
angles.y = AngleNormalize(angles.y);
|
||||
angles.x = AngleNormalize(angles.x);
|
||||
|
||||
if (angles.x > m_angGun.x)
|
||||
m_angGun.x = MIN( angles.x, m_angGun.x + 12 );
|
||||
if (angles.x < m_angGun.x)
|
||||
m_angGun.x = MAX( angles.x, m_angGun.x - 12 );
|
||||
if (angles.y > m_angGun.y)
|
||||
m_angGun.y = MIN( angles.y, m_angGun.y + 12 );
|
||||
if (angles.y < m_angGun.y)
|
||||
m_angGun.y = MAX( angles.y, m_angGun.y - 12 );
|
||||
|
||||
// hacks - shouldn't be hardcoded, oh well.
|
||||
// limit it so it doesn't pop if you try to set it to the max value
|
||||
m_angGun.y = clamp( m_angGun.y, -89.9, 89.9 );
|
||||
m_angGun.x = clamp( m_angGun.x, -9.9, 44.9 );
|
||||
|
||||
m_angGun.y = SetBoneController( 0, m_angGun.y );
|
||||
m_angGun.x = SetBoneController( 1, m_angGun.x );
|
||||
|
||||
Vector posBarrel;
|
||||
QAngle angBarrel;
|
||||
GetAttachment( 0, posBarrel, angBarrel );
|
||||
|
||||
Vector forward;
|
||||
AngleVectors( angBarrel + m_angGun, &forward );
|
||||
|
||||
Vector2D vec2LOS = ( GetEnemy()->GetAbsOrigin() - GetAbsOrigin() ).AsVector2D();
|
||||
vec2LOS.NormalizeInPlace();
|
||||
|
||||
float flDot = vec2LOS.Dot( forward.AsVector2D() );
|
||||
|
||||
//forward
|
||||
// NDebugOverlay::Line( GetAbsOrigin(), GetAbsOrigin() + ( forward * 200 ), 255,0,0, false, 0.1);
|
||||
//LOS
|
||||
// NDebugOverlay::Line( posGun, m_vecTargetPosition , 0,0,255, false, 0.1);
|
||||
// NDebugOverlay::Box( GetAbsOrigin(), s_vecSurroundingMins, s_vecSurroundingMaxs, 0, 255,0, false, 0.1);
|
||||
|
||||
if ( flDot > 0.98 )
|
||||
{
|
||||
CPASAttenuationFilter filter( this, 0.2f );
|
||||
|
||||
EmitSound( filter, entindex(), "Apache.FireGun" );//<<TEMP>>temp sound
|
||||
|
||||
// gun is a bit dodgy, just fire at the target if we are close
|
||||
FireBullets( 1, posGun, vecTarget, VECTOR_CONE_4DEGREES, 8192, m_iAmmoType, 2 );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CNPC_Apache::FireRocket( void )
|
||||
{
|
||||
static float side = 1.0;
|
||||
static int count;
|
||||
Vector vForward, vRight, vUp;
|
||||
|
||||
|
||||
AngleVectors( GetAbsAngles(), &vForward, &vRight, &vUp );
|
||||
Vector vecSrc = GetAbsOrigin() + 1.5 * ( vForward * 21 + vRight * 70 * side + vUp * -79 );
|
||||
|
||||
// pick firing pod to launch from
|
||||
switch( m_iRockets % 5)
|
||||
{
|
||||
case 0: vecSrc = vecSrc + vRight * 10; break;
|
||||
case 1: vecSrc = vecSrc - vRight * 10; break;
|
||||
case 2: vecSrc = vecSrc + vUp * 10; break;
|
||||
case 3: vecSrc = vecSrc - vUp * 10; break;
|
||||
case 4: break;
|
||||
}
|
||||
|
||||
Vector vTargetDir = m_vecTargetPosition - GetAbsOrigin();
|
||||
VectorNormalize ( vTargetDir );
|
||||
LaunchRocket( vTargetDir, 100, 150, vecSrc);
|
||||
|
||||
m_iRockets--;
|
||||
|
||||
side = - side;
|
||||
}
|
||||
void CNPC_Apache::AimRocketGun( void )
|
||||
{
|
||||
Vector vForward, vRight, vUp;
|
||||
|
||||
if (m_iRockets <= 0)
|
||||
return;
|
||||
|
||||
Vector vTargetDir = m_vecTargetPosition - GetAbsOrigin();
|
||||
VectorNormalize ( vTargetDir );
|
||||
|
||||
AngleVectors( GetAbsAngles(), &vForward, &vRight, &vUp );
|
||||
Vector vecEst = ( vForward * 800 + GetAbsVelocity());
|
||||
VectorNormalize ( vecEst );
|
||||
|
||||
trace_t tr1;
|
||||
UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + vecEst * 4096, MASK_ALL, this, COLLISION_GROUP_NONE, &tr1);
|
||||
|
||||
// NDebugOverlay::Line(GetAbsOrigin(), tr1.endpos, 255,255,0, false, 0.1);
|
||||
|
||||
UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + vTargetDir * 4096, MASK_ALL, this, COLLISION_GROUP_NONE, &tr1);
|
||||
|
||||
// NDebugOverlay::Line(GetAbsOrigin(), tr1.endpos, 0,255,0, false, 0.1);
|
||||
|
||||
// ALERT( at_console, "%d %d %d %4.2f\n", GetAbsAngles().x < 0, DotProduct( pev->velocity, gpGlobals->v_forward ) > -100, m_flNextRocket < gpGlobals->curtime, DotProduct( m_vecTarget, vecEst ) );
|
||||
|
||||
if ((m_iRockets % 2) == 1)
|
||||
{
|
||||
FireRocket( );
|
||||
m_flNextRocket = gpGlobals->curtime + 0.5;
|
||||
if (m_iRockets <= 0)
|
||||
{
|
||||
m_flNextRocket = gpGlobals->curtime + 10;
|
||||
m_iRockets = 10;
|
||||
}
|
||||
}
|
||||
else if (DotProduct( GetAbsVelocity(), vForward ) > -100 && m_flNextRocket < gpGlobals->curtime)
|
||||
{
|
||||
if (m_flLastSeen + 60 > gpGlobals->curtime)
|
||||
{
|
||||
if (GetEnemy() != NULL)
|
||||
{
|
||||
// make sure it's a good shot
|
||||
//if (DotProduct( vTargetDir, vecEst) > 0.5)
|
||||
{
|
||||
trace_t tr;
|
||||
|
||||
UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + vecEst * 4096, MASK_ALL, this, COLLISION_GROUP_NONE, &tr);
|
||||
|
||||
// NDebugOverlay::Line(GetAbsOrigin(), tr.endpos, 255,0,255, false, 5);
|
||||
|
||||
// if ((tr.endpos - m_vecTargetPosition).Length() < 512)
|
||||
if ((tr.endpos - m_vecTargetPosition).Length() < 1024)
|
||||
FireRocket( );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
trace_t tr;
|
||||
|
||||
UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + vecEst * 4096, MASK_ALL, this, COLLISION_GROUP_NONE, &tr);
|
||||
// just fire when close
|
||||
if ((tr.endpos - m_vecTargetPosition).Length() < 512)
|
||||
FireRocket( );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define MISSILE_HOMING_STRENGTH 0.3
|
||||
#define MISSILE_HOMING_DELAY 5.0
|
||||
#define MISSILE_HOMING_RAMP_UP 0.5
|
||||
#define MISSILE_HOMING_DURATION 1.0
|
||||
#define MISSILE_HOMING_RAMP_DOWN 0.5
|
||||
|
||||
void CNPC_Apache::LaunchRocket( Vector &viewDir, int damage, int radius, Vector vecLaunchPoint )
|
||||
{
|
||||
|
||||
CGrenadeHomer *pGrenade = CGrenadeHomer::CreateGrenadeHomer(
|
||||
MAKE_STRING("models/weapons/w_missile.mdl"),
|
||||
MAKE_STRING( "Apache.RPG" ),
|
||||
vecLaunchPoint, vec3_angle, edict() );
|
||||
pGrenade->Spawn( );
|
||||
pGrenade->SetHoming(MISSILE_HOMING_STRENGTH, MISSILE_HOMING_DELAY,
|
||||
MISSILE_HOMING_RAMP_UP, MISSILE_HOMING_DURATION,
|
||||
MISSILE_HOMING_RAMP_DOWN);
|
||||
pGrenade->SetDamage( damage );
|
||||
pGrenade->m_DmgRadius = radius;
|
||||
|
||||
pGrenade->Launch( this, GetEnemy(), viewDir * 1500, 500, 0, HOMER_SMOKE_TRAIL_ON );
|
||||
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Lame, temporary death
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNPC_Apache::DyingThink( void )
|
||||
{
|
||||
StudioFrameAdvance( );
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
|
||||
if( gpGlobals->curtime > m_flNextCrashExplosion )
|
||||
{
|
||||
CPASFilter pasFilter( GetAbsOrigin() );
|
||||
Vector pos;
|
||||
|
||||
pos = GetAbsOrigin();
|
||||
pos.x += random->RandomFloat( -150, 150 );
|
||||
pos.y += random->RandomFloat( -150, 150 );
|
||||
pos.z += random->RandomFloat( -150, -50 );
|
||||
|
||||
te->Explosion( pasFilter, 0.0, &pos, g_sModelIndexFireball, 10, 15, TE_EXPLFLAG_NONE, 100, 0 );
|
||||
|
||||
Vector vecSize = Vector( 500, 500, 60 );
|
||||
CPVSFilter pvsFilter( GetAbsOrigin() );
|
||||
|
||||
te->BreakModel( pvsFilter, 0.0, GetAbsOrigin(), vec3_angle, vecSize, vec3_origin,
|
||||
m_nDebrisModel, 100, 0, 2.5, BREAK_METAL );
|
||||
|
||||
m_flNextCrashExplosion = gpGlobals->curtime + random->RandomFloat( 0.3, 0.5 );
|
||||
}
|
||||
|
||||
QAngle angVel = GetLocalAngularVelocity();
|
||||
if( angVel.y < 400 )
|
||||
{
|
||||
angVel.y *= 1.1;
|
||||
SetLocalAngularVelocity( angVel );
|
||||
}
|
||||
|
||||
Vector vecImpulse( 0, 0, -38.4 ); // gravity - 32ft/sec
|
||||
ApplyAbsVelocityImpulse( vecImpulse );
|
||||
|
||||
if( m_hSmoke )
|
||||
{
|
||||
m_hSmoke->SetLifetime(0.1f);
|
||||
m_hSmoke = NULL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CNPC_Apache::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
|
||||
{
|
||||
|
||||
CTakeDamageInfo dmgInfo = info;
|
||||
|
||||
// HITGROUPS don't work currently.
|
||||
// ignore blades
|
||||
//if (ptr->hitgroup == 6 && (info.GetDamageType() & (DMG_ENERGYBEAM|DMG_BULLET|DMG_CLUB)))
|
||||
// return;
|
||||
|
||||
// hit hard, hits cockpit
|
||||
if (info.GetDamage() > 50 || ptr->hitgroup == 1 || ptr->hitgroup == 2 || ptr->hitgroup == 3 )
|
||||
{
|
||||
// ALERT( at_console, "%map .0f\n", flDamage );
|
||||
AddMultiDamage( dmgInfo, this );
|
||||
|
||||
if ( info.GetDamage() > 50 )
|
||||
{
|
||||
if ( m_hSmoke == NULL && (m_hSmoke = SmokeTrail::CreateSmokeTrail()) != NULL )
|
||||
{
|
||||
m_hSmoke->m_Opacity = 1.0f;
|
||||
m_hSmoke->m_SpawnRate = 60;
|
||||
m_hSmoke->m_ParticleLifetime = 1.3f;
|
||||
m_hSmoke->m_StartColor.Init( 0.65f, 0.65f , 0.65f );
|
||||
m_hSmoke->m_EndColor.Init( 0.65f, 0.65f, 0.65f );
|
||||
m_hSmoke->m_StartSize = 12;
|
||||
m_hSmoke->m_EndSize = 64;
|
||||
m_hSmoke->m_SpawnRadius = 8;
|
||||
m_hSmoke->m_MinSpeed = 2;
|
||||
m_hSmoke->m_MaxSpeed = 24;
|
||||
|
||||
m_hSmoke->SetLifetime( 1e6 );
|
||||
m_hSmoke->FollowEntity( this );
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// do half damage in the body
|
||||
dmgInfo.ScaleDamage(0.5);
|
||||
AddMultiDamage( dmgInfo, this );
|
||||
g_pEffects->Ricochet( ptr->endpos, ptr->plane.normal );
|
||||
}
|
||||
|
||||
if ( m_iHealth < 10 )
|
||||
{
|
||||
if ( m_hSmoke == NULL && (m_hSmoke = SmokeTrail::CreateSmokeTrail()) != NULL )
|
||||
{
|
||||
m_hSmoke->m_Opacity = 1.0f;
|
||||
m_hSmoke->m_SpawnRate = 60;
|
||||
m_hSmoke->m_ParticleLifetime = 1.3f;
|
||||
m_hSmoke->m_StartColor.Init( 0.65f, 0.65f , 0.65f );
|
||||
m_hSmoke->m_EndColor.Init( 0.65f, 0.65f, 0.65f );
|
||||
m_hSmoke->m_StartSize = 12;
|
||||
m_hSmoke->m_EndSize = 64;
|
||||
m_hSmoke->m_SpawnRadius = 8;
|
||||
m_hSmoke->m_MinSpeed = 2;
|
||||
m_hSmoke->m_MaxSpeed = 24;
|
||||
|
||||
m_hSmoke->SetLifetime( 1e6 );
|
||||
m_hSmoke->FollowEntity( this );
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,516 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: barnacle - stationary ceiling mounted 'fishing' monster
|
||||
//
|
||||
// $Workfile: $
|
||||
// $Date: $
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "hl1_npc_barnacle.h"
|
||||
#include "npcevent.h"
|
||||
#include "gib.h"
|
||||
#include "ai_default.h"
|
||||
#include "activitylist.h"
|
||||
#include "hl2_player.h"
|
||||
#include "vstdlib/random.h"
|
||||
#include "physics_saverestore.h"
|
||||
#include "vcollide_parse.h"
|
||||
#include "engine/IEngineSound.h"
|
||||
|
||||
ConVar sk_barnacle_health( "sk_barnacle_health","25");
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Private activities.
|
||||
//-----------------------------------------------------------------------------
|
||||
static int ACT_EAT = 0;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Interactions
|
||||
//-----------------------------------------------------------------------------
|
||||
int g_interactionBarnacleVictimDangle = 0;
|
||||
int g_interactionBarnacleVictimReleased = 0;
|
||||
int g_interactionBarnacleVictimGrab = 0;
|
||||
|
||||
LINK_ENTITY_TO_CLASS( monster_barnacle, CNPC_Barnacle );
|
||||
IMPLEMENT_CUSTOM_AI( monster_barnacle, CNPC_Barnacle );
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Initialize the custom schedules
|
||||
// Input :
|
||||
// Output :
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNPC_Barnacle::InitCustomSchedules(void)
|
||||
{
|
||||
INIT_CUSTOM_AI(CNPC_Barnacle);
|
||||
|
||||
ADD_CUSTOM_ACTIVITY(CNPC_Barnacle, ACT_EAT);
|
||||
|
||||
g_interactionBarnacleVictimDangle = CBaseCombatCharacter::GetInteractionID();
|
||||
g_interactionBarnacleVictimReleased = CBaseCombatCharacter::GetInteractionID();
|
||||
g_interactionBarnacleVictimGrab = CBaseCombatCharacter::GetInteractionID();
|
||||
}
|
||||
|
||||
|
||||
BEGIN_DATADESC( CNPC_Barnacle )
|
||||
|
||||
DEFINE_FIELD( m_flAltitude, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( m_flKillVictimTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( m_cGibs, FIELD_INTEGER ),// barnacle loads up on gibs each time it kills something.
|
||||
DEFINE_FIELD( m_fLiftingPrey, FIELD_BOOLEAN ),
|
||||
DEFINE_FIELD( m_flTongueAdj, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( m_flIgnoreTouchesUntil, FIELD_TIME ),
|
||||
|
||||
// Function pointers
|
||||
DEFINE_THINKFUNC( BarnacleThink ),
|
||||
DEFINE_THINKFUNC( WaitTillDead ),
|
||||
END_DATADESC()
|
||||
|
||||
|
||||
//=========================================================
|
||||
// Classify - indicates this monster's place in the
|
||||
// relationship table.
|
||||
//=========================================================
|
||||
Class_T CNPC_Barnacle::Classify ( void )
|
||||
{
|
||||
return CLASS_ALIEN_MONSTER;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// HandleAnimEvent - catches the monster-specific messages
|
||||
// that occur when tagged animation frames are played.
|
||||
//
|
||||
// Returns number of events handled, 0 if none.
|
||||
//=========================================================
|
||||
void CNPC_Barnacle::HandleAnimEvent( animevent_t *pEvent )
|
||||
{
|
||||
switch( pEvent->event )
|
||||
{
|
||||
case BARNACLE_AE_PUKEGIB:
|
||||
CGib::SpawnRandomGibs( this, 1, GIB_HUMAN );
|
||||
break;
|
||||
default:
|
||||
BaseClass::HandleAnimEvent( pEvent );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Spawn
|
||||
//=========================================================
|
||||
void CNPC_Barnacle::Spawn()
|
||||
{
|
||||
Precache( );
|
||||
|
||||
SetModel( "models/barnacle.mdl" );
|
||||
UTIL_SetSize( this, Vector(-16, -16, -32), Vector(16, 16, 0) );
|
||||
|
||||
SetSolid( SOLID_BBOX );
|
||||
AddSolidFlags( FSOLID_NOT_STANDABLE );
|
||||
SetMoveType( MOVETYPE_NONE );
|
||||
SetBloodColor( BLOOD_COLOR_GREEN );
|
||||
m_iHealth = sk_barnacle_health.GetFloat();
|
||||
m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result )
|
||||
m_NPCState = NPC_STATE_NONE;
|
||||
m_flKillVictimTime = 0;
|
||||
m_cGibs = 0;
|
||||
m_fLiftingPrey = FALSE;
|
||||
m_takedamage = DAMAGE_YES;
|
||||
|
||||
InitBoneControllers();
|
||||
InitTonguePosition();
|
||||
|
||||
// set eye position
|
||||
SetDefaultEyeOffset();
|
||||
|
||||
SetActivity ( ACT_IDLE );
|
||||
|
||||
SetThink ( &CNPC_Barnacle::BarnacleThink );
|
||||
SetNextThink( gpGlobals->curtime + 0.5f );
|
||||
//Do not have a shadow
|
||||
AddEffects( EF_NOSHADOW );
|
||||
|
||||
m_flIgnoreTouchesUntil = gpGlobals->curtime;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input :
|
||||
// Output :
|
||||
//-----------------------------------------------------------------------------
|
||||
int CNPC_Barnacle::OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo )
|
||||
{
|
||||
CTakeDamageInfo info = inputInfo;
|
||||
if ( info.GetDamageType() & DMG_CLUB )
|
||||
{
|
||||
info.SetDamage( m_iHealth );
|
||||
}
|
||||
|
||||
return BaseClass::OnTakeDamage_Alive( info );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Initialize tongue position when first spawned
|
||||
// Input :
|
||||
// Output :
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNPC_Barnacle::InitTonguePosition( void )
|
||||
{
|
||||
CBaseEntity *pTouchEnt;
|
||||
float flLength;
|
||||
|
||||
pTouchEnt = TongueTouchEnt( &flLength );
|
||||
m_flAltitude = flLength;
|
||||
|
||||
Vector origin;
|
||||
QAngle angle;
|
||||
|
||||
GetAttachment( "TongueEnd", origin, angle );
|
||||
|
||||
m_flTongueAdj = origin.z - GetAbsOrigin().z;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNPC_Barnacle::BarnacleThink ( void )
|
||||
{
|
||||
CBaseEntity *pTouchEnt;
|
||||
float flLength;
|
||||
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
|
||||
if (CAI_BaseNPC::m_nDebugBits & bits_debugDisableAI)
|
||||
{
|
||||
// AI Disabled, don't do anything
|
||||
}
|
||||
else if ( GetEnemy() != NULL )
|
||||
{
|
||||
// barnacle has prey.
|
||||
|
||||
if ( !GetEnemy()->IsAlive() )
|
||||
{
|
||||
// someone (maybe even the barnacle) killed the prey. Reset barnacle.
|
||||
m_fLiftingPrey = FALSE;// indicate that we're not lifting prey.
|
||||
SetEnemy( NULL );
|
||||
return;
|
||||
}
|
||||
|
||||
CBaseCombatCharacter* pVictim = GetEnemyCombatCharacterPointer();
|
||||
Assert( pVictim );
|
||||
|
||||
if ( m_fLiftingPrey )
|
||||
{
|
||||
|
||||
if ( GetEnemy() != NULL && pVictim->m_lifeState == LIFE_DEAD )
|
||||
{
|
||||
// crap, someone killed the prey on the way up.
|
||||
SetEnemy( NULL );
|
||||
m_fLiftingPrey = FALSE;
|
||||
return;
|
||||
}
|
||||
|
||||
// still pulling prey.
|
||||
Vector vecNewEnemyOrigin = GetEnemy()->GetLocalOrigin();
|
||||
vecNewEnemyOrigin.x = GetLocalOrigin().x;
|
||||
vecNewEnemyOrigin.y = GetLocalOrigin().y;
|
||||
|
||||
// guess as to where their neck is
|
||||
// FIXME: remove, ask victim where their neck is
|
||||
vecNewEnemyOrigin.x -= 6 * cos(GetEnemy()->GetLocalAngles().y * M_PI/180.0);
|
||||
vecNewEnemyOrigin.y -= 6 * sin(GetEnemy()->GetLocalAngles().y * M_PI/180.0);
|
||||
|
||||
m_flAltitude -= BARNACLE_PULL_SPEED;
|
||||
vecNewEnemyOrigin.z += BARNACLE_PULL_SPEED;
|
||||
|
||||
if ( fabs( GetLocalOrigin().z - ( vecNewEnemyOrigin.z + GetEnemy()->GetViewOffset().z ) ) < BARNACLE_BODY_HEIGHT )
|
||||
{
|
||||
// prey has just been lifted into position ( if the victim origin + eye height + 8 is higher than the bottom of the barnacle, it is assumed that the head is within barnacle's body )
|
||||
m_fLiftingPrey = FALSE;
|
||||
|
||||
CPASAttenuationFilter filter( this );
|
||||
EmitSound( filter, entindex(), "Barnacle.Bite");
|
||||
|
||||
// Take a while to kill the player
|
||||
m_flKillVictimTime = gpGlobals->curtime + 10;
|
||||
|
||||
if ( pVictim )
|
||||
{
|
||||
pVictim->DispatchInteraction( g_interactionBarnacleVictimDangle, NULL, this );
|
||||
SetActivity ( (Activity)ACT_EAT );
|
||||
}
|
||||
}
|
||||
|
||||
CBaseEntity *pEnemy = GetEnemy();
|
||||
|
||||
trace_t trace;
|
||||
UTIL_TraceEntity( pEnemy, pEnemy->GetAbsOrigin(), vecNewEnemyOrigin, MASK_SOLID_BRUSHONLY, pEnemy, COLLISION_GROUP_NONE, &trace );
|
||||
|
||||
if( trace.fraction != 1.0 )
|
||||
{
|
||||
// The victim cannot be moved from their current origin to this new origin. So drop them.
|
||||
SetEnemy( NULL );
|
||||
m_fLiftingPrey = FALSE;
|
||||
|
||||
if( pEnemy->MyCombatCharacterPointer() )
|
||||
{
|
||||
pEnemy->MyCombatCharacterPointer()->DispatchInteraction( g_interactionBarnacleVictimReleased, NULL, this );
|
||||
}
|
||||
|
||||
// Ignore touches long enough to let the victim move away.
|
||||
m_flIgnoreTouchesUntil = gpGlobals->curtime + 1.5;
|
||||
|
||||
SetActivity( ACT_IDLE );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
UTIL_SetOrigin ( GetEnemy(), vecNewEnemyOrigin );
|
||||
}
|
||||
else
|
||||
{
|
||||
// prey is lifted fully into feeding position and is dangling there.
|
||||
|
||||
if ( m_flKillVictimTime != -1 && gpGlobals->curtime > m_flKillVictimTime )
|
||||
{
|
||||
// kill!
|
||||
if ( pVictim )
|
||||
{
|
||||
// DMG_CRUSH added so no physics force is generated
|
||||
pVictim->TakeDamage( CTakeDamageInfo( this, this, pVictim->m_iHealth, DMG_SLASH | DMG_ALWAYSGIB | DMG_CRUSH ) );
|
||||
m_cGibs = 3;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// bite prey every once in a while
|
||||
if ( pVictim && ( random->RandomInt( 0, 49 ) == 0 ) )
|
||||
{
|
||||
CPASAttenuationFilter filter( this );
|
||||
EmitSound( filter, entindex(), "Barnacle.Chew" );
|
||||
|
||||
if ( pVictim )
|
||||
{
|
||||
pVictim->DispatchInteraction( g_interactionBarnacleVictimDangle, NULL, this );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// barnacle has no prey right now, so just idle and check to see if anything is touching the tongue.
|
||||
|
||||
// If idle and no nearby client, don't think so often. Client should be out of PVS and not within 50 feet.
|
||||
if ( !UTIL_FindClientInPVS(edict()) )
|
||||
{
|
||||
CBasePlayer *pPlayer = UTIL_PlayerByIndex(1);
|
||||
|
||||
if( pPlayer )
|
||||
{
|
||||
Vector vecDist = pPlayer->GetAbsOrigin() - GetAbsOrigin();
|
||||
|
||||
if( vecDist.Length2DSqr() >= Square(600.0f) )
|
||||
{
|
||||
SetNextThink( gpGlobals->curtime + 1.5f );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( IsActivityFinished() )
|
||||
{// this is done so barnacle will fidget.
|
||||
SetActivity ( ACT_IDLE );
|
||||
}
|
||||
|
||||
if ( m_cGibs && random->RandomInt(0,99) == 1 )
|
||||
{
|
||||
// cough up a gib.
|
||||
CGib::SpawnRandomGibs( this, 1, GIB_HUMAN );
|
||||
m_cGibs--;
|
||||
|
||||
CPASAttenuationFilter filter( this );
|
||||
EmitSound( filter, entindex(), "Barnacle.Chew" );
|
||||
}
|
||||
|
||||
pTouchEnt = TongueTouchEnt( &flLength );
|
||||
|
||||
//NDebugOverlay::Box( GetAbsOrigin() - Vector( 0, 0, flLength ), Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), 255,0,0, 0, 0.1 );
|
||||
|
||||
if ( pTouchEnt != NULL )
|
||||
{
|
||||
// tongue is fully extended, and is touching someone.
|
||||
CBaseCombatCharacter* pBCC = (CBaseCombatCharacter *)pTouchEnt;
|
||||
|
||||
// FIXME: humans should return neck position
|
||||
Vector vecGrabPos = pTouchEnt->GetAbsOrigin();
|
||||
|
||||
if ( pBCC && pBCC->DispatchInteraction( g_interactionBarnacleVictimGrab, &vecGrabPos, this ) )
|
||||
{
|
||||
CPASAttenuationFilter filter( this );
|
||||
EmitSound( filter, entindex(), "Barnacle.Alert" );
|
||||
|
||||
SetSequenceByName ( "attack1" );
|
||||
|
||||
SetEnemy( pTouchEnt );
|
||||
|
||||
pTouchEnt->SetMoveType( MOVETYPE_FLY );
|
||||
pTouchEnt->SetAbsVelocity( vec3_origin );
|
||||
pTouchEnt->SetBaseVelocity( vec3_origin );
|
||||
Vector origin = GetAbsOrigin();
|
||||
origin.z = pTouchEnt->GetAbsOrigin().z;
|
||||
pTouchEnt->SetLocalOrigin( origin );
|
||||
|
||||
m_fLiftingPrey = TRUE;// indicate that we should be lifting prey.
|
||||
m_flKillVictimTime = -1;// set this to a bogus time while the victim is lifted.
|
||||
|
||||
m_flAltitude = (GetAbsOrigin().z - vecGrabPos.z);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// calculate a new length for the tongue to be clear of anything else that moves under it.
|
||||
if ( m_flAltitude < flLength )
|
||||
{
|
||||
// if tongue is higher than is should be, lower it kind of slowly.
|
||||
m_flAltitude += BARNACLE_PULL_SPEED;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_flAltitude = flLength;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ALERT( at_console, "tounge %f\n", m_flAltitude + m_flTongueAdj );
|
||||
//NDebugOverlay::Box( GetAbsOrigin() - Vector( 0, 0, m_flAltitude ), Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), 255,255,255, 0, 0.1 );
|
||||
|
||||
SetBoneController( 0, -(m_flAltitude + m_flTongueAdj) );
|
||||
StudioFrameAdvance();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNPC_Barnacle::Event_Killed( const CTakeDamageInfo &info )
|
||||
{
|
||||
AddSolidFlags( FSOLID_NOT_SOLID );
|
||||
m_takedamage = DAMAGE_NO;
|
||||
m_lifeState = LIFE_DEAD;
|
||||
if ( GetEnemy() != NULL )
|
||||
{
|
||||
CBaseCombatCharacter *pVictim = GetEnemyCombatCharacterPointer();
|
||||
|
||||
if ( pVictim )
|
||||
{
|
||||
pVictim->DispatchInteraction( g_interactionBarnacleVictimReleased, NULL, this );
|
||||
}
|
||||
}
|
||||
|
||||
CGib::SpawnRandomGibs( this, 4, GIB_HUMAN );
|
||||
|
||||
CPASAttenuationFilter filter( this );
|
||||
EmitSound( filter, entindex(), "Barnacle.Die" );
|
||||
|
||||
SetActivity ( ACT_DIESIMPLE );
|
||||
SetBoneController( 0, 0 );
|
||||
|
||||
StudioFrameAdvance();
|
||||
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
SetThink ( &CNPC_Barnacle::WaitTillDead );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNPC_Barnacle::WaitTillDead ( void )
|
||||
{
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
|
||||
StudioFrameAdvance();
|
||||
DispatchAnimEvents ( this );
|
||||
|
||||
if ( IsActivityFinished() )
|
||||
{
|
||||
// death anim finished.
|
||||
StopAnimation();
|
||||
SetThink ( NULL );
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Precache - precaches all resources this monster needs
|
||||
//=========================================================
|
||||
void CNPC_Barnacle::Precache()
|
||||
{
|
||||
PrecacheModel("models/barnacle.mdl");
|
||||
|
||||
PrecacheScriptSound( "Barnacle.Bite" );
|
||||
PrecacheScriptSound( "Barnacle.Chew" );
|
||||
PrecacheScriptSound( "Barnacle.Alert" );
|
||||
PrecacheScriptSound( "Barnacle.Die" );
|
||||
|
||||
BaseClass::Precache();
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// TongueTouchEnt - does a trace along the barnacle's tongue
|
||||
// to see if any entity is touching it. Also stores the length
|
||||
// of the trace in the int pointer provided.
|
||||
//=========================================================
|
||||
#define BARNACLE_CHECK_SPACING 8
|
||||
CBaseEntity *CNPC_Barnacle::TongueTouchEnt ( float *pflLength )
|
||||
{
|
||||
trace_t tr;
|
||||
float length;
|
||||
|
||||
// trace once to hit architecture and see if the tongue needs to change position.
|
||||
UTIL_TraceLine ( GetAbsOrigin(), GetAbsOrigin() - Vector ( 0 , 0 , 2048 ),
|
||||
MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr );
|
||||
|
||||
length = fabs( GetAbsOrigin().z - tr.endpos.z );
|
||||
// Pull it up a tad
|
||||
length -= 16;
|
||||
if ( pflLength )
|
||||
{
|
||||
*pflLength = length;
|
||||
}
|
||||
|
||||
// Don't try to touch any prey.
|
||||
if ( m_flIgnoreTouchesUntil > gpGlobals->curtime )
|
||||
return NULL;
|
||||
|
||||
Vector delta = Vector( BARNACLE_CHECK_SPACING, BARNACLE_CHECK_SPACING, 0 );
|
||||
Vector mins = GetAbsOrigin() - delta;
|
||||
Vector maxs = GetAbsOrigin() + delta;
|
||||
maxs.z = GetAbsOrigin().z;
|
||||
|
||||
// Take our current tongue's length or a point higher if we hit a wall
|
||||
// NOTENOTE: (this relieves the need to know if the tongue is currently moving)
|
||||
mins.z -= MIN( m_flAltitude, length );
|
||||
|
||||
CBaseEntity *pList[10];
|
||||
int count = UTIL_EntitiesInBox( pList, 10, mins, maxs, (FL_CLIENT|FL_NPC) );
|
||||
if ( count )
|
||||
{
|
||||
for ( int i = 0; i < count; i++ )
|
||||
{
|
||||
CBaseCombatCharacter *pVictim = ToBaseCombatCharacter( pList[ i ] );
|
||||
|
||||
bool bCanHurt = false;
|
||||
|
||||
if ( IRelationType( pList[i] ) == D_HT || IRelationType( pList[i] ) == D_FR )
|
||||
bCanHurt = true;
|
||||
|
||||
if ( pList[i] != this && bCanHurt == true && pVictim->m_lifeState == LIFE_ALIVE )
|
||||
{
|
||||
return pList[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Base combat character with no AI
|
||||
//
|
||||
// $Workfile: $
|
||||
// $Date: $
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// $Log: $
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef HL1_NPC_BARNACLE_H
|
||||
#define HL1_NPC_BARNACLE_H
|
||||
|
||||
#include "hl1_ai_basenpc.h"
|
||||
#include "rope_physics.h"
|
||||
|
||||
#define BARNACLE_BODY_HEIGHT 44 // how 'tall' the barnacle's model is.
|
||||
#define BARNACLE_PULL_SPEED 8
|
||||
#define BARNACLE_KILL_VICTIM_DELAY 5 // how many seconds after pulling prey in to gib them.
|
||||
|
||||
//=========================================================
|
||||
// Monster's Anim Events Go Here
|
||||
//=========================================================
|
||||
#define BARNACLE_AE_PUKEGIB 2
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
class CNPC_Barnacle : public CHL1BaseNPC
|
||||
{
|
||||
DECLARE_CLASS( CNPC_Barnacle, CHL1BaseNPC );
|
||||
|
||||
public:
|
||||
void Spawn( void );
|
||||
void InitTonguePosition( void );
|
||||
void Precache( void );
|
||||
CBaseEntity* TongueTouchEnt ( float *pflLength );
|
||||
Class_T Classify ( void );
|
||||
void HandleAnimEvent( animevent_t *pEvent );
|
||||
void BarnacleThink ( void );
|
||||
void WaitTillDead ( void );
|
||||
void Event_Killed( const CTakeDamageInfo &info );
|
||||
int OnTakeDamage_Alive( const CTakeDamageInfo &info );
|
||||
|
||||
DECLARE_DATADESC();
|
||||
|
||||
float m_flAltitude;
|
||||
|
||||
float m_flKillVictimTime;
|
||||
int m_cGibs;// barnacle loads up on gibs each time it kills something.
|
||||
bool m_fLiftingPrey;
|
||||
float m_flTongueAdj;
|
||||
float m_flIgnoreTouchesUntil;
|
||||
|
||||
public:
|
||||
DEFINE_CUSTOM_AI;
|
||||
};
|
||||
|
||||
#endif //HL1_NPC_BARNACLE_H
|
||||
@@ -0,0 +1,938 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Bullseyes act as targets for other NPC's to attack and to trigger
|
||||
// events
|
||||
//
|
||||
// $Workfile: $
|
||||
// $Date: $
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// $Log: $
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "ai_default.h"
|
||||
#include "ai_task.h"
|
||||
#include "ai_schedule.h"
|
||||
#include "ai_node.h"
|
||||
#include "ai_hull.h"
|
||||
#include "ai_hint.h"
|
||||
#include "ai_memory.h"
|
||||
#include "ai_route.h"
|
||||
#include "ai_motor.h"
|
||||
#include "hl1_npc_barney.h"
|
||||
#include "soundent.h"
|
||||
#include "game.h"
|
||||
#include "npcevent.h"
|
||||
#include "entitylist.h"
|
||||
#include "activitylist.h"
|
||||
#include "animation.h"
|
||||
#include "basecombatweapon.h"
|
||||
#include "IEffects.h"
|
||||
#include "vstdlib/random.h"
|
||||
#include "engine/IEngineSound.h"
|
||||
#include "ammodef.h"
|
||||
#include "ai_behavior_follow.h"
|
||||
#include "AI_Criteria.h"
|
||||
#include "SoundEmitterSystem/isoundemittersystembase.h"
|
||||
|
||||
#define BA_ATTACK "BA_ATTACK"
|
||||
#define BA_MAD "BA_MAD"
|
||||
#define BA_SHOT "BA_SHOT"
|
||||
#define BA_KILL "BA_KILL"
|
||||
#define BA_POK "BA_POK"
|
||||
|
||||
ConVar sk_barney_health( "sk_barney_health","35");
|
||||
|
||||
//=========================================================
|
||||
// Monster's Anim Events Go Here
|
||||
//=========================================================
|
||||
// first flag is barney dying for scripted sequences?
|
||||
#define BARNEY_AE_DRAW ( 2 )
|
||||
#define BARNEY_AE_SHOOT ( 3 )
|
||||
#define BARNEY_AE_HOLSTER ( 4 )
|
||||
|
||||
#define BARNEY_BODY_GUNHOLSTERED 0
|
||||
#define BARNEY_BODY_GUNDRAWN 1
|
||||
#define BARNEY_BODY_GUNGONE 2
|
||||
|
||||
|
||||
//---------------------------------------------------------
|
||||
// Save/Restore
|
||||
//---------------------------------------------------------
|
||||
BEGIN_DATADESC( CNPC_Barney )
|
||||
DEFINE_FIELD( m_fGunDrawn, FIELD_BOOLEAN ),
|
||||
DEFINE_FIELD( m_flPainTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( m_flCheckAttackTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( m_fLastAttackCheck, FIELD_BOOLEAN ),
|
||||
|
||||
DEFINE_THINKFUNC( SUB_LVFadeOut ),
|
||||
|
||||
//DEFINE_FIELD( m_iAmmoType, FIELD_INTEGER ),
|
||||
END_DATADESC()
|
||||
|
||||
|
||||
LINK_ENTITY_TO_CLASS( monster_barney, CNPC_Barney );
|
||||
|
||||
|
||||
static BOOL IsFacing( CBaseEntity *pevTest, const Vector &reference )
|
||||
{
|
||||
Vector vecDir = (reference - pevTest->GetAbsOrigin());
|
||||
vecDir.z = 0;
|
||||
VectorNormalize( vecDir );
|
||||
Vector forward;
|
||||
QAngle angle;
|
||||
angle = pevTest->GetAbsAngles();
|
||||
angle.x = 0;
|
||||
AngleVectors( angle, &forward );
|
||||
// He's facing me, he meant it
|
||||
if ( DotProduct( forward, vecDir ) > 0.96 ) // +/- 15 degrees or so
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Spawn
|
||||
//=========================================================
|
||||
void CNPC_Barney::Spawn()
|
||||
{
|
||||
Precache( );
|
||||
|
||||
SetModel( "models/barney.mdl");
|
||||
|
||||
SetRenderColor( 255, 255, 255, 255 );
|
||||
|
||||
SetHullType(HULL_HUMAN);
|
||||
SetHullSizeNormal();
|
||||
|
||||
SetSolid( SOLID_BBOX );
|
||||
AddSolidFlags( FSOLID_NOT_STANDABLE );
|
||||
SetMoveType( MOVETYPE_STEP );
|
||||
m_bloodColor = BLOOD_COLOR_RED;
|
||||
m_iHealth = sk_barney_health.GetFloat();
|
||||
SetViewOffset( Vector ( 0, 0, 100 ) );// position of the eyes relative to monster's origin.
|
||||
m_flFieldOfView = VIEW_FIELD_WIDE; // NOTE: we need a wide field of view so npc will notice player and say hello
|
||||
m_NPCState = NPC_STATE_NONE;
|
||||
|
||||
SetBodygroup( 1, 0 );
|
||||
|
||||
m_fGunDrawn = false;
|
||||
|
||||
CapabilitiesClear();
|
||||
CapabilitiesAdd( bits_CAP_MOVE_GROUND | bits_CAP_OPEN_DOORS | bits_CAP_AUTO_DOORS | bits_CAP_USE | bits_CAP_DOORS_GROUP);
|
||||
CapabilitiesAdd( bits_CAP_INNATE_RANGE_ATTACK1 | bits_CAP_TURN_HEAD | bits_CAP_ANIMATEDFACE );
|
||||
|
||||
NPCInit();
|
||||
|
||||
SetUse( &CNPC_Barney::FollowerUse );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Precache - precaches all resources this monster needs
|
||||
//=========================================================
|
||||
void CNPC_Barney::Precache()
|
||||
{
|
||||
m_iAmmoType = GetAmmoDef()->Index("9mmRound");
|
||||
|
||||
PrecacheModel("models/barney.mdl");
|
||||
|
||||
PrecacheScriptSound( "Barney.FirePistol" );
|
||||
PrecacheScriptSound( "Barney.Pain" );
|
||||
PrecacheScriptSound( "Barney.Die" );
|
||||
|
||||
// every new barney must call this, otherwise
|
||||
// when a level is loaded, nobody will talk (time is reset to 0)
|
||||
TalkInit();
|
||||
BaseClass::Precache();
|
||||
}
|
||||
|
||||
void CNPC_Barney::ModifyOrAppendCriteria( AI_CriteriaSet& criteriaSet )
|
||||
{
|
||||
BaseClass::ModifyOrAppendCriteria( criteriaSet );
|
||||
|
||||
bool predisaster = FBitSet( m_spawnflags, SF_NPC_PREDISASTER ) ? true : false;
|
||||
|
||||
criteriaSet.AppendCriteria( "disaster", predisaster ? "[disaster::pre]" : "[disaster::post]" );
|
||||
}
|
||||
|
||||
// Init talk data
|
||||
void CNPC_Barney::TalkInit()
|
||||
{
|
||||
BaseClass::TalkInit();
|
||||
|
||||
// get voice for head - just one barney voice for now
|
||||
GetExpresser()->SetVoicePitch( 100 );
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// GetSoundInterests - returns a bit mask indicating which types
|
||||
// of sounds this monster regards.
|
||||
//=========================================================
|
||||
int CNPC_Barney::GetSoundInterests ( void)
|
||||
{
|
||||
return SOUND_WORLD |
|
||||
SOUND_COMBAT |
|
||||
SOUND_CARCASS |
|
||||
SOUND_MEAT |
|
||||
SOUND_GARBAGE |
|
||||
SOUND_DANGER |
|
||||
SOUND_PLAYER;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Classify - indicates this monster's place in the
|
||||
// relationship table.
|
||||
//=========================================================
|
||||
Class_T CNPC_Barney::Classify ( void )
|
||||
{
|
||||
return CLASS_PLAYER_ALLY;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// ALertSound - barney says "Freeze!"
|
||||
//=========================================================
|
||||
void CNPC_Barney::AlertSound( void )
|
||||
{
|
||||
if ( GetEnemy() != NULL )
|
||||
{
|
||||
if ( IsOkToSpeak() )
|
||||
{
|
||||
Speak( BA_ATTACK );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
//=========================================================
|
||||
// SetYawSpeed - allows each sequence to have a different
|
||||
// turn rate associated with it.
|
||||
//=========================================================
|
||||
void CNPC_Barney::SetYawSpeed ( void )
|
||||
{
|
||||
int ys;
|
||||
|
||||
ys = 0;
|
||||
|
||||
switch ( GetActivity() )
|
||||
{
|
||||
case ACT_IDLE:
|
||||
ys = 70;
|
||||
break;
|
||||
case ACT_WALK:
|
||||
ys = 70;
|
||||
break;
|
||||
case ACT_RUN:
|
||||
ys = 90;
|
||||
break;
|
||||
default:
|
||||
ys = 70;
|
||||
break;
|
||||
}
|
||||
|
||||
GetMotor()->SetYawSpeed( ys );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// CheckRangeAttack1
|
||||
//=========================================================
|
||||
bool CNPC_Barney::CheckRangeAttack1 ( float flDot, float flDist )
|
||||
{
|
||||
if ( gpGlobals->curtime > m_flCheckAttackTime )
|
||||
{
|
||||
trace_t tr;
|
||||
|
||||
Vector shootOrigin = GetAbsOrigin() + Vector( 0, 0, 55 );
|
||||
CBaseEntity *pEnemy = GetEnemy();
|
||||
Vector shootTarget = ( (pEnemy->BodyTarget( shootOrigin ) - pEnemy->GetAbsOrigin()) + GetEnemyLKP() );
|
||||
|
||||
UTIL_TraceLine ( shootOrigin, shootTarget, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr);
|
||||
m_flCheckAttackTime = gpGlobals->curtime + 1;
|
||||
if ( tr.fraction == 1.0 || ( tr.m_pEnt != NULL && tr.m_pEnt == pEnemy) )
|
||||
m_fLastAttackCheck = TRUE;
|
||||
else
|
||||
m_fLastAttackCheck = FALSE;
|
||||
|
||||
m_flCheckAttackTime = gpGlobals->curtime + 1.5;
|
||||
}
|
||||
|
||||
return m_fLastAttackCheck;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose : For innate range attack
|
||||
// Input :
|
||||
// Output :
|
||||
//------------------------------------------------------------------------------
|
||||
int CNPC_Barney::RangeAttack1Conditions( float flDot, float flDist )
|
||||
{
|
||||
if (GetEnemy() == NULL)
|
||||
{
|
||||
return( COND_NONE );
|
||||
}
|
||||
|
||||
else if ( flDist > 1024 )
|
||||
{
|
||||
return( COND_TOO_FAR_TO_ATTACK );
|
||||
}
|
||||
else if ( flDot < 0.5 )
|
||||
{
|
||||
return( COND_NOT_FACING_ATTACK );
|
||||
}
|
||||
|
||||
if ( CheckRangeAttack1 ( flDot, flDist ) )
|
||||
return( COND_CAN_RANGE_ATTACK1 );
|
||||
|
||||
return COND_NONE;
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// BarneyFirePistol - shoots one round from the pistol at
|
||||
// the enemy barney is facing.
|
||||
//=========================================================
|
||||
void CNPC_Barney::BarneyFirePistol ( void )
|
||||
{
|
||||
Vector vecShootOrigin;
|
||||
|
||||
vecShootOrigin = GetAbsOrigin() + Vector( 0, 0, 55 );
|
||||
Vector vecShootDir = GetShootEnemyDir( vecShootOrigin );
|
||||
|
||||
QAngle angDir;
|
||||
|
||||
VectorAngles( vecShootDir, angDir );
|
||||
// SetBlending( 0, angDir.x );
|
||||
DoMuzzleFlash();
|
||||
|
||||
FireBullets(1, vecShootOrigin, vecShootDir, VECTOR_CONE_2DEGREES, 1024, m_iAmmoType );
|
||||
|
||||
int pitchShift = random->RandomInt( 0, 20 );
|
||||
|
||||
// Only shift about half the time
|
||||
if ( pitchShift > 10 )
|
||||
pitchShift = 0;
|
||||
else
|
||||
pitchShift -= 5;
|
||||
|
||||
CPASAttenuationFilter filter( this );
|
||||
|
||||
EmitSound_t params;
|
||||
params.m_pSoundName = "Barney.FirePistol";
|
||||
params.m_flVolume = 1;
|
||||
params.m_nChannel= CHAN_WEAPON;
|
||||
params.m_SoundLevel = SNDLVL_NORM;
|
||||
params.m_nPitch = 100 + pitchShift;
|
||||
EmitSound( filter, entindex(), params );
|
||||
|
||||
CSoundEnt::InsertSound ( SOUND_COMBAT, GetAbsOrigin(), 384, 0.3 );
|
||||
|
||||
// UNDONE: Reload?
|
||||
m_cAmmoLoaded--;// take away a bullet!
|
||||
}
|
||||
|
||||
int CNPC_Barney::OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo )
|
||||
{
|
||||
// make sure friends talk about it if player hurts talkmonsters...
|
||||
int ret = BaseClass::OnTakeDamage_Alive( inputInfo );
|
||||
|
||||
if ( !IsAlive() || m_lifeState == LIFE_DYING )
|
||||
return ret;
|
||||
|
||||
if ( m_NPCState != NPC_STATE_PRONE && ( inputInfo.GetAttacker()->GetFlags() & FL_CLIENT ) )
|
||||
{
|
||||
// This is a heurstic to determine if the player intended to harm me
|
||||
// If I have an enemy, we can't establish intent (may just be crossfire)
|
||||
if ( GetEnemy() == NULL )
|
||||
{
|
||||
// If the player was facing directly at me, or I'm already suspicious, get mad
|
||||
if ( HasMemory( bits_MEMORY_SUSPICIOUS ) || IsFacing( inputInfo.GetAttacker(), GetAbsOrigin() ) )
|
||||
{
|
||||
// Alright, now I'm pissed!
|
||||
Speak( BA_MAD );
|
||||
|
||||
Remember( bits_MEMORY_PROVOKED );
|
||||
StopFollowing();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Hey, be careful with that
|
||||
Speak( BA_SHOT );
|
||||
Remember( bits_MEMORY_SUSPICIOUS );
|
||||
}
|
||||
}
|
||||
else if ( !(GetEnemy()->IsPlayer()) && m_lifeState == LIFE_ALIVE )
|
||||
{
|
||||
Speak( BA_SHOT );
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// PainSound
|
||||
//=========================================================
|
||||
void CNPC_Barney::PainSound( const CTakeDamageInfo &info )
|
||||
{
|
||||
if (gpGlobals->curtime < m_flPainTime)
|
||||
return;
|
||||
|
||||
m_flPainTime = gpGlobals->curtime + random->RandomFloat( 0.5, 0.75 );
|
||||
|
||||
CPASAttenuationFilter filter( this );
|
||||
|
||||
CSoundParameters params;
|
||||
if ( GetParametersForSound( "Barney.Pain", params, NULL ) )
|
||||
{
|
||||
params.pitch = GetExpresser()->GetVoicePitch();
|
||||
|
||||
EmitSound_t ep( params );
|
||||
|
||||
EmitSound( filter, entindex(), ep );
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// DeathSound
|
||||
//=========================================================
|
||||
void CNPC_Barney::DeathSound( const CTakeDamageInfo &info )
|
||||
{
|
||||
CPASAttenuationFilter filter( this );
|
||||
|
||||
CSoundParameters params;
|
||||
if ( GetParametersForSound( "Barney.Die", params, NULL ) )
|
||||
{
|
||||
params.pitch = GetExpresser()->GetVoicePitch();
|
||||
|
||||
EmitSound_t ep( params );
|
||||
|
||||
EmitSound( filter, entindex(), ep );
|
||||
}
|
||||
}
|
||||
|
||||
void CNPC_Barney::TraceAttack( const CTakeDamageInfo &inputInfo, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
|
||||
{
|
||||
CTakeDamageInfo info = inputInfo;
|
||||
|
||||
switch( ptr->hitgroup )
|
||||
{
|
||||
case HITGROUP_CHEST:
|
||||
case HITGROUP_STOMACH:
|
||||
if ( info.GetDamageType() & (DMG_BULLET | DMG_SLASH | DMG_BLAST) )
|
||||
{
|
||||
info.ScaleDamage( 0.5f );
|
||||
}
|
||||
break;
|
||||
case 10:
|
||||
if ( info.GetDamageType() & (DMG_BULLET | DMG_SLASH | DMG_CLUB) )
|
||||
{
|
||||
info.SetDamage( info.GetDamage() - 20 );
|
||||
if ( info.GetDamage() <= 0 )
|
||||
{
|
||||
g_pEffects->Ricochet( ptr->endpos, ptr->plane.normal );
|
||||
info.SetDamage( 0.01 );
|
||||
}
|
||||
}
|
||||
// always a head shot
|
||||
ptr->hitgroup = HITGROUP_HEAD;
|
||||
break;
|
||||
}
|
||||
|
||||
BaseClass::TraceAttack( info, vecDir, ptr, pAccumulator );
|
||||
}
|
||||
|
||||
void CNPC_Barney::Event_Killed( const CTakeDamageInfo &info )
|
||||
{
|
||||
if ( m_nBody < BARNEY_BODY_GUNGONE )
|
||||
{
|
||||
// drop the gun!
|
||||
Vector vecGunPos;
|
||||
QAngle angGunAngles;
|
||||
CBaseEntity *pGun = NULL;
|
||||
|
||||
SetBodygroup( 1, BARNEY_BODY_GUNGONE);
|
||||
|
||||
GetAttachment( "0", vecGunPos, angGunAngles );
|
||||
|
||||
angGunAngles.y += 180;
|
||||
pGun = DropItem( "weapon_glock", vecGunPos, angGunAngles );
|
||||
}
|
||||
|
||||
SetUse( NULL );
|
||||
BaseClass::Event_Killed( info );
|
||||
|
||||
if ( UTIL_IsLowViolence() )
|
||||
{
|
||||
SUB_StartLVFadeOut( 0.0f );
|
||||
}
|
||||
}
|
||||
|
||||
void CNPC_Barney::SUB_StartLVFadeOut( float delay, bool notSolid )
|
||||
{
|
||||
SetThink( &CNPC_Barney::SUB_LVFadeOut );
|
||||
SetNextThink( gpGlobals->curtime + delay );
|
||||
SetRenderColorA( 255 );
|
||||
m_nRenderMode = kRenderNormal;
|
||||
|
||||
if ( notSolid )
|
||||
{
|
||||
AddSolidFlags( FSOLID_NOT_SOLID );
|
||||
SetLocalAngularVelocity( vec3_angle );
|
||||
}
|
||||
}
|
||||
|
||||
void CNPC_Barney::SUB_LVFadeOut( void )
|
||||
{
|
||||
if( VPhysicsGetObject() )
|
||||
{
|
||||
if( VPhysicsGetObject()->GetGameFlags() & FVPHYSICS_PLAYER_HELD || GetEFlags() & EFL_IS_BEING_LIFTED_BY_BARNACLE )
|
||||
{
|
||||
// Try again in a few seconds.
|
||||
SetNextThink( gpGlobals->curtime + 5 );
|
||||
SetRenderColorA( 255 );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
float dt = gpGlobals->frametime;
|
||||
if ( dt > 0.1f )
|
||||
{
|
||||
dt = 0.1f;
|
||||
}
|
||||
m_nRenderMode = kRenderTransTexture;
|
||||
int speed = MAX(3,256*dt); // fade out over 3 seconds
|
||||
SetRenderColorA( UTIL_Approach( 0, m_clrRender->a, speed ) );
|
||||
NetworkStateChanged();
|
||||
|
||||
if ( m_clrRender->a == 0 )
|
||||
{
|
||||
UTIL_Remove(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetNextThink( gpGlobals->curtime );
|
||||
}
|
||||
}
|
||||
|
||||
void CNPC_Barney::StartTask( const Task_t *pTask )
|
||||
{
|
||||
BaseClass::StartTask( pTask );
|
||||
}
|
||||
|
||||
void CNPC_Barney::RunTask( const Task_t *pTask )
|
||||
{
|
||||
switch ( pTask->iTask )
|
||||
{
|
||||
case TASK_RANGE_ATTACK1:
|
||||
if (GetEnemy() != NULL && (GetEnemy()->IsPlayer()))
|
||||
{
|
||||
m_flPlaybackRate = 1.5;
|
||||
}
|
||||
BaseClass::RunTask( pTask );
|
||||
break;
|
||||
default:
|
||||
BaseClass::RunTask( pTask );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// HandleAnimEvent - catches the monster-specific messages
|
||||
// that occur when tagged animation frames are played.
|
||||
//
|
||||
// Returns number of events handled, 0 if none.
|
||||
//=========================================================
|
||||
void CNPC_Barney::HandleAnimEvent( animevent_t *pEvent )
|
||||
{
|
||||
switch( pEvent->event )
|
||||
{
|
||||
case BARNEY_AE_SHOOT:
|
||||
BarneyFirePistol();
|
||||
break;
|
||||
|
||||
case BARNEY_AE_DRAW:
|
||||
// barney's bodygroup switches here so he can pull gun from holster
|
||||
SetBodygroup( 1, BARNEY_BODY_GUNDRAWN);
|
||||
m_fGunDrawn = true;
|
||||
break;
|
||||
|
||||
case BARNEY_AE_HOLSTER:
|
||||
// change bodygroup to replace gun in holster
|
||||
SetBodygroup( 1, BARNEY_BODY_GUNHOLSTERED);
|
||||
m_fGunDrawn = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
BaseClass::HandleAnimEvent( pEvent );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// AI Schedules Specific to this monster
|
||||
//=========================================================
|
||||
|
||||
int CNPC_Barney::TranslateSchedule( int scheduleType )
|
||||
{
|
||||
switch( scheduleType )
|
||||
{
|
||||
case SCHED_ARM_WEAPON:
|
||||
if ( GetEnemy() != NULL )
|
||||
{
|
||||
// face enemy, then draw.
|
||||
return SCHED_BARNEY_ENEMY_DRAW;
|
||||
}
|
||||
break;
|
||||
|
||||
// Hook these to make a looping schedule
|
||||
case SCHED_TARGET_FACE:
|
||||
{
|
||||
int baseType;
|
||||
|
||||
// call base class default so that scientist will talk
|
||||
// when 'used'
|
||||
baseType = BaseClass::TranslateSchedule( scheduleType );
|
||||
|
||||
if ( baseType == SCHED_IDLE_STAND )
|
||||
return SCHED_BARNEY_FACE_TARGET;
|
||||
else
|
||||
return baseType;
|
||||
}
|
||||
break;
|
||||
|
||||
case SCHED_TARGET_CHASE:
|
||||
{
|
||||
return SCHED_BARNEY_FOLLOW;
|
||||
break;
|
||||
}
|
||||
|
||||
case SCHED_IDLE_STAND:
|
||||
{
|
||||
int baseType;
|
||||
|
||||
// call base class default so that scientist will talk
|
||||
// when 'used'
|
||||
baseType = BaseClass::TranslateSchedule( scheduleType );
|
||||
|
||||
if ( baseType == SCHED_IDLE_STAND )
|
||||
return SCHED_BARNEY_IDLE_STAND;
|
||||
else
|
||||
return baseType;
|
||||
}
|
||||
break;
|
||||
|
||||
case SCHED_TAKE_COVER_FROM_ENEMY:
|
||||
case SCHED_CHASE_ENEMY:
|
||||
{
|
||||
if ( HasCondition( COND_HEAVY_DAMAGE ) )
|
||||
return SCHED_TAKE_COVER_FROM_ENEMY;
|
||||
|
||||
// No need to take cover since I can see him
|
||||
// SHOOT!
|
||||
if ( HasCondition( COND_CAN_RANGE_ATTACK1 ) && m_fGunDrawn )
|
||||
return SCHED_RANGE_ATTACK1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return BaseClass::TranslateSchedule( scheduleType );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// SelectSchedule - Decides which type of schedule best suits
|
||||
// the monster's current state and conditions. Then calls
|
||||
// monster's member function to get a pointer to a schedule
|
||||
// of the proper type.
|
||||
//=========================================================
|
||||
int CNPC_Barney::SelectSchedule( void )
|
||||
{
|
||||
if ( m_NPCState == NPC_STATE_COMBAT || GetEnemy() != NULL )
|
||||
{
|
||||
// Priority action!
|
||||
if (!m_fGunDrawn )
|
||||
return SCHED_ARM_WEAPON;
|
||||
}
|
||||
|
||||
if ( GetFollowTarget() == NULL )
|
||||
{
|
||||
if ( HasCondition( COND_PLAYER_PUSHING ) && !(GetSpawnFlags() & SF_NPC_PREDISASTER ) ) // Player wants me to move
|
||||
return SCHED_HL1TALKER_FOLLOW_MOVE_AWAY;
|
||||
}
|
||||
|
||||
if ( BehaviorSelectSchedule() )
|
||||
return BaseClass::SelectSchedule();
|
||||
|
||||
if ( HasCondition( COND_HEAR_DANGER ) )
|
||||
{
|
||||
CSound *pSound;
|
||||
pSound = GetBestSound();
|
||||
|
||||
ASSERT( pSound != NULL );
|
||||
|
||||
if ( pSound && pSound->IsSoundType( SOUND_DANGER ) )
|
||||
return SCHED_TAKE_COVER_FROM_BEST_SOUND;
|
||||
}
|
||||
if ( HasCondition( COND_ENEMY_DEAD ) && IsOkToSpeak() )
|
||||
{
|
||||
Speak( BA_KILL );
|
||||
}
|
||||
|
||||
switch( m_NPCState )
|
||||
{
|
||||
case NPC_STATE_COMBAT:
|
||||
{
|
||||
// dead enemy
|
||||
if ( HasCondition( COND_ENEMY_DEAD ) )
|
||||
return BaseClass::SelectSchedule(); // call base class, all code to handle dead enemies is centralized there.
|
||||
|
||||
// always act surprized with a new enemy
|
||||
if ( HasCondition( COND_NEW_ENEMY ) && HasCondition( COND_LIGHT_DAMAGE) )
|
||||
return SCHED_SMALL_FLINCH;
|
||||
|
||||
if ( HasCondition( COND_HEAVY_DAMAGE ) )
|
||||
return SCHED_TAKE_COVER_FROM_ENEMY;
|
||||
|
||||
if ( !HasCondition(COND_SEE_ENEMY) )
|
||||
{
|
||||
// we can't see the enemy
|
||||
if ( !HasCondition(COND_ENEMY_OCCLUDED) )
|
||||
{
|
||||
// enemy is unseen, but not occluded!
|
||||
// turn to face enemy
|
||||
return SCHED_COMBAT_FACE;
|
||||
}
|
||||
else
|
||||
{
|
||||
return SCHED_CHASE_ENEMY;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case NPC_STATE_ALERT:
|
||||
case NPC_STATE_IDLE:
|
||||
if ( HasCondition( COND_LIGHT_DAMAGE ) || HasCondition( COND_HEAVY_DAMAGE ) )
|
||||
{
|
||||
// flinch if hurt
|
||||
return SCHED_SMALL_FLINCH;
|
||||
}
|
||||
|
||||
if ( GetEnemy() == NULL && GetFollowTarget() )
|
||||
{
|
||||
if ( !GetFollowTarget()->IsAlive() )
|
||||
{
|
||||
// UNDONE: Comment about the recently dead player here?
|
||||
StopFollowing();
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
return SCHED_TARGET_FACE;
|
||||
}
|
||||
}
|
||||
|
||||
// try to say something about smells
|
||||
TrySmellTalk();
|
||||
break;
|
||||
}
|
||||
|
||||
return BaseClass::SelectSchedule();
|
||||
}
|
||||
|
||||
NPC_STATE CNPC_Barney::SelectIdealState ( void )
|
||||
{
|
||||
return BaseClass::SelectIdealState();
|
||||
}
|
||||
|
||||
void CNPC_Barney::DeclineFollowing( void )
|
||||
{
|
||||
if ( CanSpeakAfterMyself() )
|
||||
{
|
||||
Speak( BA_POK );
|
||||
}
|
||||
}
|
||||
|
||||
bool CNPC_Barney::CanBecomeRagdoll( void )
|
||||
{
|
||||
if ( UTIL_IsLowViolence() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return BaseClass::CanBecomeRagdoll();
|
||||
}
|
||||
|
||||
bool CNPC_Barney::ShouldGib( const CTakeDamageInfo &info )
|
||||
{
|
||||
if ( UTIL_IsLowViolence() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return BaseClass::ShouldGib( info );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Schedules
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
AI_BEGIN_CUSTOM_NPC( monster_barney, CNPC_Barney )
|
||||
|
||||
//=========================================================
|
||||
// > SCHED_BARNEY_FOLLOW
|
||||
//=========================================================
|
||||
DEFINE_SCHEDULE
|
||||
(
|
||||
SCHED_BARNEY_FOLLOW,
|
||||
|
||||
" Tasks"
|
||||
// " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_BARNEY_STOP_FOLLOWING"
|
||||
" TASK_GET_PATH_TO_TARGET 0"
|
||||
" TASK_MOVE_TO_TARGET_RANGE 180"
|
||||
" TASK_SET_SCHEDULE SCHEDULE:SCHED_TARGET_FACE"
|
||||
" "
|
||||
" Interrupts"
|
||||
" COND_NEW_ENEMY"
|
||||
" COND_LIGHT_DAMAGE"
|
||||
" COND_HEAVY_DAMAGE"
|
||||
" COND_HEAR_DANGER"
|
||||
" COND_PROVOKED"
|
||||
)
|
||||
|
||||
//=========================================================
|
||||
// > SCHED_BARNEY_ENEMY_DRAW
|
||||
//=========================================================
|
||||
DEFINE_SCHEDULE
|
||||
(
|
||||
SCHED_BARNEY_ENEMY_DRAW,
|
||||
|
||||
" Tasks"
|
||||
" TASK_STOP_MOVING 0"
|
||||
" TASK_FACE_ENEMY 0"
|
||||
" TASK_PLAY_SEQUENCE_FACE_ENEMY ACTIVITY:ACT_ARM"
|
||||
" "
|
||||
" Interrupts"
|
||||
)
|
||||
|
||||
//=========================================================
|
||||
// > SCHED_BARNEY_FACE_TARGET
|
||||
//=========================================================
|
||||
DEFINE_SCHEDULE
|
||||
(
|
||||
SCHED_BARNEY_FACE_TARGET,
|
||||
|
||||
" Tasks"
|
||||
" TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
|
||||
" TASK_FACE_TARGET 0"
|
||||
" TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
|
||||
" TASK_SET_SCHEDULE SCHEDULE:SCHED_BARNEY_FOLLOW"
|
||||
" "
|
||||
" Interrupts"
|
||||
" COND_GIVE_WAY"
|
||||
" COND_NEW_ENEMY"
|
||||
" COND_LIGHT_DAMAGE"
|
||||
" COND_HEAVY_DAMAGE"
|
||||
" COND_PROVOKED"
|
||||
" COND_HEAR_DANGER"
|
||||
)
|
||||
|
||||
//=========================================================
|
||||
// > SCHED_BARNEY_IDLE_STAND
|
||||
//=========================================================
|
||||
DEFINE_SCHEDULE
|
||||
(
|
||||
SCHED_BARNEY_IDLE_STAND,
|
||||
|
||||
" Tasks"
|
||||
" TASK_STOP_MOVING 0"
|
||||
" TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
|
||||
" TASK_WAIT 2"
|
||||
" TASK_TALKER_HEADRESET 0"
|
||||
" "
|
||||
" Interrupts"
|
||||
" COND_NEW_ENEMY"
|
||||
" COND_LIGHT_DAMAGE"
|
||||
" COND_HEAVY_DAMAGE"
|
||||
" COND_PROVOKED"
|
||||
" COND_HEAR_COMBAT"
|
||||
" COND_SMELL"
|
||||
)
|
||||
|
||||
AI_END_CUSTOM_NPC()
|
||||
|
||||
|
||||
//=========================================================
|
||||
// DEAD BARNEY PROP
|
||||
//
|
||||
// Designer selects a pose in worldcraft, 0 through num_poses-1
|
||||
// this value is added to what is selected as the 'first dead pose'
|
||||
// among the monster's normal animations. All dead poses must
|
||||
// appear sequentially in the model file. Be sure and set
|
||||
// the m_iFirstPose properly!
|
||||
//
|
||||
//=========================================================
|
||||
class CNPC_DeadBarney : public CAI_BaseNPC
|
||||
{
|
||||
DECLARE_CLASS( CNPC_DeadBarney, CAI_BaseNPC );
|
||||
public:
|
||||
|
||||
void Spawn( void );
|
||||
Class_T Classify ( void ) { return CLASS_NONE; }
|
||||
|
||||
bool KeyValue( const char *szKeyName, const char *szValue );
|
||||
float MaxYawSpeed ( void ) { return 8.0f; }
|
||||
|
||||
int m_iPose;// which sequence to display -- temporary, don't need to save
|
||||
int m_iDesiredSequence;
|
||||
static char *m_szPoses[3];
|
||||
|
||||
DECLARE_DATADESC();
|
||||
};
|
||||
|
||||
char *CNPC_DeadBarney::m_szPoses[] = { "lying_on_back", "lying_on_side", "lying_on_stomach" };
|
||||
|
||||
bool CNPC_DeadBarney::KeyValue( const char *szKeyName, const char *szValue )
|
||||
{
|
||||
if ( FStrEq( szKeyName, "pose" ) )
|
||||
m_iPose = atoi( szValue );
|
||||
else
|
||||
BaseClass::KeyValue( szKeyName, szValue );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
LINK_ENTITY_TO_CLASS( monster_barney_dead, CNPC_DeadBarney );
|
||||
|
||||
BEGIN_DATADESC( CNPC_DeadBarney )
|
||||
END_DATADESC()
|
||||
|
||||
//=========================================================
|
||||
// ********** DeadBarney SPAWN **********
|
||||
//=========================================================
|
||||
void CNPC_DeadBarney::Spawn( void )
|
||||
{
|
||||
PrecacheModel("models/barney.mdl");
|
||||
SetModel( "models/barney.mdl");
|
||||
|
||||
ClearEffects();
|
||||
SetSequence( 0 );
|
||||
m_bloodColor = BLOOD_COLOR_RED;
|
||||
|
||||
SetRenderColor( 255, 255, 255, 255 );
|
||||
|
||||
SetSequence( m_iDesiredSequence = LookupSequence( m_szPoses[m_iPose] ) );
|
||||
if ( GetSequence() == -1 )
|
||||
{
|
||||
Msg ( "Dead barney with bad pose\n" );
|
||||
}
|
||||
// Corpses have less health
|
||||
m_iHealth = 0.0;//gSkillData.barneyHealth;
|
||||
|
||||
NPCInitDead();
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
#ifndef NPC_BARNEY_H
|
||||
#define NPC_BARNEY_H
|
||||
|
||||
#include "hl1_npc_talker.h"
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
class CNPC_Barney : public CHL1NPCTalker
|
||||
{
|
||||
DECLARE_CLASS( CNPC_Barney, CHL1NPCTalker );
|
||||
public:
|
||||
|
||||
DECLARE_DATADESC();
|
||||
|
||||
virtual void ModifyOrAppendCriteria( AI_CriteriaSet& set );
|
||||
|
||||
void Precache( void );
|
||||
void Spawn( void );
|
||||
void TalkInit( void );
|
||||
|
||||
void StartTask( const Task_t *pTask );
|
||||
void RunTask( const Task_t *pTask );
|
||||
|
||||
int GetSoundInterests ( void );
|
||||
Class_T Classify ( void );
|
||||
void AlertSound( void );
|
||||
void SetYawSpeed ( void );
|
||||
|
||||
bool CheckRangeAttack1 ( float flDot, float flDist );
|
||||
void BarneyFirePistol ( void );
|
||||
|
||||
int OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo );
|
||||
void TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator );
|
||||
void Event_Killed( const CTakeDamageInfo &info );
|
||||
|
||||
void PainSound( const CTakeDamageInfo &info );
|
||||
void DeathSound( const CTakeDamageInfo &info );
|
||||
|
||||
void HandleAnimEvent( animevent_t *pEvent );
|
||||
int TranslateSchedule( int scheduleType );
|
||||
int SelectSchedule( void );
|
||||
|
||||
void DeclineFollowing( void );
|
||||
|
||||
bool CanBecomeRagdoll( void );
|
||||
bool ShouldGib( const CTakeDamageInfo &info );
|
||||
|
||||
int RangeAttack1Conditions( float flDot, float flDist );
|
||||
|
||||
void SUB_StartLVFadeOut( float delay = 10.0f, bool bNotSolid = true );
|
||||
void SUB_LVFadeOut( void );
|
||||
|
||||
NPC_STATE SelectIdealState ( void );
|
||||
|
||||
bool m_fGunDrawn;
|
||||
float m_flPainTime;
|
||||
float m_flCheckAttackTime;
|
||||
bool m_fLastAttackCheck;
|
||||
|
||||
int m_iAmmoType;
|
||||
|
||||
enum
|
||||
{
|
||||
SCHED_BARNEY_FOLLOW = BaseClass::NEXT_SCHEDULE,
|
||||
SCHED_BARNEY_ENEMY_DRAW,
|
||||
SCHED_BARNEY_FACE_TARGET,
|
||||
SCHED_BARNEY_IDLE_STAND,
|
||||
SCHED_BARNEY_STOP_FOLLOWING,
|
||||
};
|
||||
|
||||
DEFINE_CUSTOM_AI;
|
||||
};
|
||||
|
||||
#endif //NPC_BARNEY_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,86 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
#include "cbase.h"
|
||||
#include "ai_default.h"
|
||||
#include "ai_task.h"
|
||||
#include "ai_schedule.h"
|
||||
#include "ai_node.h"
|
||||
#include "ai_hull.h"
|
||||
#include "ai_hint.h"
|
||||
#include "ai_memory.h"
|
||||
#include "ai_route.h"
|
||||
#include "ai_motor.h"
|
||||
#include "soundent.h"
|
||||
#include "game.h"
|
||||
#include "npcevent.h"
|
||||
#include "entitylist.h"
|
||||
#include "activitylist.h"
|
||||
#include "animation.h"
|
||||
#include "basecombatweapon.h"
|
||||
#include "IEffects.h"
|
||||
#include "vstdlib/random.h"
|
||||
#include "engine/IEngineSound.h"
|
||||
#include "ammodef.h"
|
||||
#include "hl1_ai_basenpc.h"
|
||||
|
||||
class CNPC_Bloater : public CHL1BaseNPC
|
||||
{
|
||||
DECLARE_CLASS( CNPC_Bloater, CHL1BaseNPC );
|
||||
public:
|
||||
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
/*void SetYawSpeed( void );
|
||||
int Classify ( void );
|
||||
void HandleAnimEvent( MonsterEvent_t *pEvent );
|
||||
|
||||
void PainSound( const CTakeDamageInfo &info );
|
||||
void AlertSound( void );
|
||||
void IdleSound( void );
|
||||
void AttackSnd( void );
|
||||
|
||||
// No range attacks
|
||||
BOOL CheckRangeAttack1 ( float flDot, float flDist ) { return FALSE; }
|
||||
BOOL CheckRangeAttack2 ( float flDot, float flDist ) { return FALSE; }
|
||||
int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType );*/
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( monster_bloater, CNPC_Bloater );
|
||||
|
||||
//=========================================================
|
||||
// Spawn
|
||||
//=========================================================
|
||||
void CNPC_Bloater::Spawn()
|
||||
{
|
||||
Precache( );
|
||||
|
||||
SetModel( "models/floater.mdl");
|
||||
// UTIL_SetSize( this, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX );
|
||||
|
||||
SetSolid( SOLID_BBOX );
|
||||
AddSolidFlags( FSOLID_NOT_STANDABLE );
|
||||
SetMoveType( MOVETYPE_FLY );
|
||||
m_spawnflags |= FL_FLY;
|
||||
m_bloodColor = BLOOD_COLOR_GREEN;
|
||||
m_iHealth = 40;
|
||||
// pev->view_ofs = VEC_VIEW;// position of the eyes relative to monster's origin.
|
||||
m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result )
|
||||
m_NPCState = NPC_STATE_NONE;
|
||||
|
||||
SetRenderColor( 255, 255, 255, 255 );
|
||||
|
||||
NPCInit();
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Precache - precaches all resources this monster needs
|
||||
//=========================================================
|
||||
void CNPC_Bloater::Precache()
|
||||
{
|
||||
PrecacheModel("models/floater.mdl");
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,68 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
#ifndef NPC_BULLSQUID_H
|
||||
#define NPC_BULLSQUID_H
|
||||
|
||||
#include "hl1_ai_basenpc.h"
|
||||
|
||||
|
||||
class CNPC_Bullsquid : public CHL1BaseNPC
|
||||
{
|
||||
DECLARE_CLASS( CNPC_Bullsquid, CHL1BaseNPC );
|
||||
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
Class_T Classify( void );
|
||||
|
||||
void IdleSound( void );
|
||||
void PainSound( const CTakeDamageInfo &info );
|
||||
void AlertSound( void );
|
||||
void DeathSound( const CTakeDamageInfo &info );
|
||||
void AttackSound( void );
|
||||
|
||||
float MaxYawSpeed( void );
|
||||
|
||||
void HandleAnimEvent( animevent_t *pEvent );
|
||||
|
||||
int RangeAttack1Conditions( float flDot, float flDist );
|
||||
int MeleeAttack1Conditions( float flDot, float flDist );
|
||||
int MeleeAttack2Conditions( float flDot, float flDist );
|
||||
|
||||
bool FValidateHintType ( CAI_Hint *pHint );
|
||||
void RemoveIgnoredConditions( void );
|
||||
Disposition_t IRelationType( CBaseEntity *pTarget );
|
||||
int OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo );
|
||||
|
||||
int GetSoundInterests ( void );
|
||||
void RunAI ( void );
|
||||
virtual void OnListened ( void );
|
||||
|
||||
int SelectSchedule( void );
|
||||
int TranslateSchedule( int scheduleType );
|
||||
|
||||
bool FInViewCone ( Vector pOrigin );
|
||||
bool FVisible ( Vector vecOrigin );
|
||||
|
||||
void StartTask ( const Task_t *pTask );
|
||||
void RunTask ( const Task_t *pTask );
|
||||
|
||||
NPC_STATE SelectIdealState ( void );
|
||||
|
||||
DEFINE_CUSTOM_AI;
|
||||
DECLARE_DATADESC()
|
||||
|
||||
private:
|
||||
|
||||
bool m_fCanThreatDisplay;// this is so the squid only does the "I see a headcrab!" dance one time.
|
||||
float m_flLastHurtTime;// we keep track of this, because if something hurts a squid, it will forget about its love of headcrabs for a while.
|
||||
float m_flNextSpitTime;// last time the bullsquid used the spit attack.
|
||||
float m_flHungryTime;// set this is a future time to stop the monster from eating for a while.
|
||||
|
||||
};
|
||||
#endif // NPC_BULLSQUID_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,170 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
#ifndef NPC_CONTROLLER_H
|
||||
#define NPC_CONTROLLER_H
|
||||
#pragma once
|
||||
|
||||
#include "ai_basenpc_flyer.h"
|
||||
|
||||
class CSprite;
|
||||
class CNPC_Controller;
|
||||
|
||||
enum
|
||||
{
|
||||
TASK_CONTROLLER_CHASE_ENEMY = LAST_SHARED_TASK,
|
||||
TASK_CONTROLLER_STRAFE,
|
||||
TASK_CONTROLLER_TAKECOVER,
|
||||
TASK_CONTROLLER_FAIL,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
SCHED_CONTROLLER_CHASE_ENEMY = LAST_SHARED_SCHEDULE,
|
||||
SCHED_CONTROLLER_STRAFE,
|
||||
SCHED_CONTROLLER_TAKECOVER,
|
||||
SCHED_CONTROLLER_FAIL,
|
||||
};
|
||||
|
||||
class CControllerNavigator : public CAI_ComponentWithOuter<CNPC_Controller, CAI_Navigator>
|
||||
{
|
||||
typedef CAI_ComponentWithOuter<CNPC_Controller, CAI_Navigator> BaseClass;
|
||||
public:
|
||||
CControllerNavigator( CNPC_Controller *pOuter )
|
||||
: BaseClass( pOuter )
|
||||
{
|
||||
}
|
||||
|
||||
bool ActivityIsLocomotive( Activity activity ) { return true; }
|
||||
};
|
||||
|
||||
class CNPC_Controller : public CAI_BaseFlyingBot
|
||||
{
|
||||
public:
|
||||
|
||||
DECLARE_CLASS( CNPC_Controller, CAI_BaseFlyingBot );
|
||||
DEFINE_CUSTOM_AI;
|
||||
DECLARE_DATADESC();
|
||||
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
|
||||
float MaxYawSpeed( void ) { return 120.0f; }
|
||||
Class_T Classify ( void ) { return CLASS_ALIEN_MILITARY; }
|
||||
|
||||
void HandleAnimEvent( animevent_t *pEvent );
|
||||
|
||||
void RunAI( void );
|
||||
|
||||
int RangeAttack1Conditions ( float flDot, float flDist ); // balls
|
||||
int RangeAttack2Conditions ( float flDot, float flDist ); // head
|
||||
int MeleeAttack1Conditions ( float flDot, float flDist ) { return COND_NONE; }
|
||||
int MeleeAttack2Conditions ( float flDot, float flDist ) { return COND_NONE; }
|
||||
|
||||
int TranslateSchedule( int scheduleType );
|
||||
void StartTask ( const Task_t *pTask );
|
||||
void RunTask ( const Task_t *pTask );
|
||||
|
||||
void Stop( void );
|
||||
bool OverridePathMove( float flInterval );
|
||||
bool OverrideMove( float flInterval );
|
||||
|
||||
void MoveToTarget( float flInterval, const Vector &vecMoveTarget );
|
||||
|
||||
void SetActivity ( Activity NewActivity );
|
||||
bool ShouldAdvanceRoute( float flWaypointDist );
|
||||
int LookupFloat( );
|
||||
|
||||
friend class CControllerNavigator;
|
||||
CAI_Navigator *CreateNavigator()
|
||||
{
|
||||
return new CControllerNavigator( this );
|
||||
}
|
||||
|
||||
bool ShouldGib( const CTakeDamageInfo &info );
|
||||
bool HasAlienGibs( void ) { return true; }
|
||||
bool HasHumanGibs( void ) { return false; }
|
||||
|
||||
float m_flNextFlinch;
|
||||
|
||||
float m_flShootTime;
|
||||
float m_flShootEnd;
|
||||
|
||||
void PainSound( void );
|
||||
void AlertSound( void );
|
||||
void IdleSound( void );
|
||||
void AttackSound( void );
|
||||
void DeathSound( void );
|
||||
|
||||
static const char *pAttackSounds[];
|
||||
static const char *pIdleSounds[];
|
||||
static const char *pAlertSounds[];
|
||||
static const char *pPainSounds[];
|
||||
static const char *pDeathSounds[];
|
||||
|
||||
int OnTakeDamage_Alive( const CTakeDamageInfo &info );
|
||||
void Event_Killed( const CTakeDamageInfo &info );
|
||||
|
||||
CSprite *m_pBall[2]; // hand balls
|
||||
int m_iBall[2]; // how bright it should be
|
||||
float m_iBallTime[2]; // when it should be that color
|
||||
int m_iBallCurrent[2]; // current brightness
|
||||
|
||||
Vector m_vecEstVelocity;
|
||||
|
||||
Vector m_velocity;
|
||||
bool m_fInCombat;
|
||||
|
||||
void SetSequence( int nSequence );
|
||||
};
|
||||
|
||||
class CNPC_ControllerHeadBall : public CAI_BaseNPC
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CNPC_ControllerHeadBall, CAI_BaseNPC );
|
||||
|
||||
DECLARE_DATADESC();
|
||||
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
|
||||
void EXPORT HuntThink( void );
|
||||
void EXPORT KillThink( void );
|
||||
void EXPORT BounceTouch( CBaseEntity *pOther );
|
||||
void MovetoTarget( Vector vecTarget );
|
||||
|
||||
int m_iTrail;
|
||||
int m_flNextAttack;
|
||||
float m_flSpawnTime;
|
||||
Vector m_vecIdeal;
|
||||
EHANDLE m_hOwner;
|
||||
|
||||
CSprite *m_pSprite;
|
||||
};
|
||||
|
||||
class CNPC_ControllerZapBall : public CAI_BaseNPC
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CNPC_ControllerHeadBall, CAI_BaseNPC );
|
||||
|
||||
DECLARE_DATADESC();
|
||||
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
|
||||
void EXPORT AnimateThink( void );
|
||||
void EXPORT ExplodeTouch( CBaseEntity *pOther );
|
||||
|
||||
void Kill( void );
|
||||
|
||||
EHANDLE m_hOwner;
|
||||
float m_flSpawnTime;
|
||||
|
||||
CSprite *m_pSprite;
|
||||
};
|
||||
|
||||
#endif //NPC_CONTROLLER_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,86 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
#ifndef NPC_GARGANTUA_H
|
||||
#define NPC_GARGANTUA_H
|
||||
|
||||
#include "hl1_ai_basenpc.h"
|
||||
|
||||
class CNPC_Gargantua : public CHL1BaseNPC
|
||||
{
|
||||
DECLARE_CLASS( CNPC_Gargantua, CHL1BaseNPC );
|
||||
public:
|
||||
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
Class_T Classify ( void );
|
||||
|
||||
float MaxYawSpeed ( void );
|
||||
|
||||
int MeleeAttack1Conditions( float flDot, float flDist );
|
||||
int MeleeAttack2Conditions( float flDot, float flDist );
|
||||
int RangeAttack1Conditions( float flDot, float flDist );
|
||||
|
||||
void HandleAnimEvent( animevent_t *pEvent );
|
||||
int TranslateSchedule( int scheduleType );
|
||||
|
||||
void StartTask( const Task_t *pTask );
|
||||
void RunTask ( const Task_t *pTask );
|
||||
|
||||
bool CanBecomeRagdoll() { return false; }
|
||||
|
||||
void TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator );
|
||||
int OnTakeDamage_Alive( const CTakeDamageInfo &info );
|
||||
|
||||
/* int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType );
|
||||
void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType );
|
||||
|
||||
Schedule_t *GetScheduleOfType( int Type );
|
||||
void StartTask( Task_t *pTask );
|
||||
void RunTask( Task_t *pTask );
|
||||
*/
|
||||
void PrescheduleThink( void );
|
||||
void Event_Killed( const CTakeDamageInfo &info );
|
||||
void DeathEffect( void );
|
||||
|
||||
bool ShouldGib( const CTakeDamageInfo &info );
|
||||
|
||||
void EyeOff( void );
|
||||
void EyeOn( int level );
|
||||
void EyeUpdate( void );
|
||||
// void Leap( void );
|
||||
void StompAttack( void );
|
||||
void FlameCreate( void );
|
||||
void FlameUpdate( void );
|
||||
void FlameControls( float angleX, float angleY );
|
||||
void FlameDestroy( void );
|
||||
inline BOOL FlameIsOn( void ) { return m_pFlame[0] != NULL; }
|
||||
|
||||
void FlameDamage( Vector vecStart, Vector vecEnd, CBaseEntity *pevInflictor, CBaseEntity *pevAttacker, float flDamage, int iClassIgnore, int bitsDamageType );
|
||||
|
||||
|
||||
DEFINE_CUSTOM_AI;
|
||||
DECLARE_DATADESC();
|
||||
|
||||
private:
|
||||
CBaseEntity* GargantuaCheckTraceHullAttack(float flDist, int iDamage, int iDmgType);
|
||||
|
||||
CSprite *m_pEyeGlow; // Glow around the eyes
|
||||
CBeam *m_pFlame[4]; // Flame beams
|
||||
|
||||
int m_eyeBrightness; // Brightness target
|
||||
float m_seeTime; // Time to attack (when I see the enemy, I set this)
|
||||
float m_flameTime; // Time of next flame attack
|
||||
float m_painSoundTime; // Time of next pain sound
|
||||
float m_streakTime; // streak timer (don't send too many)
|
||||
float m_flameX; // Flame thrower aim
|
||||
float m_flameY;
|
||||
|
||||
float m_flDmgTime;
|
||||
};
|
||||
|
||||
#endif //NPC_GARGANTUA_H
|
||||
@@ -0,0 +1,232 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
//=========================================================
|
||||
// GMan - misunderstood servant of the people
|
||||
//=========================================================
|
||||
|
||||
#include "cbase.h"
|
||||
#include "ai_default.h"
|
||||
#include "ai_task.h"
|
||||
#include "ai_schedule.h"
|
||||
#include "ai_node.h"
|
||||
#include "ai_hull.h"
|
||||
#include "ai_hint.h"
|
||||
#include "ai_memory.h"
|
||||
#include "ai_route.h"
|
||||
#include "ai_motor.h"
|
||||
#include "soundent.h"
|
||||
#include "game.h"
|
||||
#include "npcevent.h"
|
||||
#include "entitylist.h"
|
||||
#include "activitylist.h"
|
||||
#include "animation.h"
|
||||
#include "IEffects.h"
|
||||
#include "vstdlib/random.h"
|
||||
#include "ai_baseactor.h"
|
||||
|
||||
//=========================================================
|
||||
// Monster's Anim Events Go Here
|
||||
//=========================================================
|
||||
|
||||
class CNPC_GMan : public CAI_BaseActor
|
||||
{
|
||||
DECLARE_CLASS( CNPC_GMan, CAI_BaseActor );
|
||||
public:
|
||||
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
float MaxYawSpeed( void ){ return 90.0f; }
|
||||
Class_T Classify ( void );
|
||||
void HandleAnimEvent( animevent_t *pEvent );
|
||||
int GetSoundInterests ( void );
|
||||
|
||||
bool IsInC5A1();
|
||||
|
||||
void StartTask( const Task_t *pTask );
|
||||
void RunTask( const Task_t *pTask );
|
||||
int OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo );
|
||||
void TraceAttack( CBaseEntity *pAttacker, float flDamage, const Vector &vecDir, trace_t *ptr, int bitsDamageType);
|
||||
|
||||
virtual int PlayScriptedSentence( const char *pszSentence, float duration, float volume, soundlevel_t soundlevel, bool bConcurrent, CBaseEntity *pListener );
|
||||
|
||||
EHANDLE m_hPlayer;
|
||||
EHANDLE m_hTalkTarget;
|
||||
float m_flTalkTime;
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( monster_gman, CNPC_GMan );
|
||||
|
||||
//=========================================================
|
||||
// Hack that tells us whether the GMan is in the final map
|
||||
//=========================================================
|
||||
bool CNPC_GMan::IsInC5A1()
|
||||
{
|
||||
const char *pMapName = STRING(gpGlobals->mapname);
|
||||
|
||||
if( pMapName )
|
||||
{
|
||||
return !Q_strnicmp( pMapName, "c5a1", 4 );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Classify - indicates this monster's place in the
|
||||
// relationship table.
|
||||
//=========================================================
|
||||
Class_T CNPC_GMan::Classify ( void )
|
||||
{
|
||||
return CLASS_NONE;
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// HandleAnimEvent - catches the monster-specific messages
|
||||
// that occur when tagged animation frames are played.
|
||||
//=========================================================
|
||||
void CNPC_GMan::HandleAnimEvent( animevent_t *pEvent )
|
||||
{
|
||||
switch( pEvent->event )
|
||||
{
|
||||
case 1:
|
||||
default:
|
||||
BaseClass::HandleAnimEvent( pEvent );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// GetSoundInterests - generic monster can't hear.
|
||||
//=========================================================
|
||||
int CNPC_GMan::GetSoundInterests ( void )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Spawn
|
||||
//=========================================================
|
||||
void CNPC_GMan::Spawn()
|
||||
{
|
||||
Precache();
|
||||
|
||||
BaseClass::Spawn();
|
||||
|
||||
SetModel( "models/gman.mdl" );
|
||||
|
||||
SetHullType(HULL_HUMAN);
|
||||
SetHullSizeNormal();
|
||||
|
||||
SetSolid( SOLID_BBOX );
|
||||
AddSolidFlags( FSOLID_NOT_STANDABLE );
|
||||
SetMoveType( MOVETYPE_STEP );
|
||||
SetBloodColor( BLOOD_COLOR_MECH );
|
||||
m_iHealth = 8;
|
||||
m_flFieldOfView = 0.5;// indicates the width of this NPC's forward view cone ( as a dotproduct result )
|
||||
m_NPCState = NPC_STATE_NONE;
|
||||
|
||||
CapabilitiesAdd( bits_CAP_MOVE_GROUND | bits_CAP_OPEN_DOORS | bits_CAP_USE_WEAPONS | bits_CAP_ANIMATEDFACE | bits_CAP_TURN_HEAD);
|
||||
|
||||
NPCInit();
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Precache - precaches all resources this monster needs
|
||||
//=========================================================
|
||||
void CNPC_GMan::Precache()
|
||||
{
|
||||
PrecacheModel( "models/gman.mdl" );
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// AI Schedules Specific to this monster
|
||||
//=========================================================
|
||||
|
||||
|
||||
void CNPC_GMan::StartTask( const Task_t *pTask )
|
||||
{
|
||||
switch( pTask->iTask )
|
||||
{
|
||||
case TASK_WAIT:
|
||||
if (m_hPlayer == NULL)
|
||||
{
|
||||
m_hPlayer = gEntList.FindEntityByClassname( NULL, "player" );
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
BaseClass::StartTask( pTask );
|
||||
}
|
||||
|
||||
void CNPC_GMan::RunTask( const Task_t *pTask )
|
||||
{
|
||||
switch( pTask->iTask )
|
||||
{
|
||||
case TASK_WAIT:
|
||||
// look at who I'm talking to
|
||||
if (m_flTalkTime > gpGlobals->curtime && m_hTalkTarget != NULL)
|
||||
{
|
||||
AddLookTarget( m_hTalkTarget->GetAbsOrigin(), 1.0, 2.0 );
|
||||
}
|
||||
// look at player, but only if playing a "safe" idle animation
|
||||
else if (m_hPlayer != NULL && (GetSequence() == 0 || IsInC5A1()) )
|
||||
{
|
||||
AddLookTarget( m_hPlayer->EyePosition(), 1.0, 3.0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Just center the head forward.
|
||||
Vector forward;
|
||||
GetVectors( &forward, NULL, NULL );
|
||||
|
||||
AddLookTarget( GetAbsOrigin() + forward * 12.0f, 1.0, 1.0 );
|
||||
SetBoneController( 0, 0 );
|
||||
}
|
||||
BaseClass::RunTask( pTask );
|
||||
break;
|
||||
}
|
||||
|
||||
SetBoneController( 0, 0 );
|
||||
BaseClass::RunTask( pTask );
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// Override all damage
|
||||
//=========================================================
|
||||
int CNPC_GMan::OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo )
|
||||
{
|
||||
m_iHealth = m_iMaxHealth / 2; // always trigger the 50% damage aitrigger
|
||||
|
||||
if ( inputInfo.GetDamage() > 0 )
|
||||
SetCondition( COND_LIGHT_DAMAGE );
|
||||
|
||||
if ( inputInfo.GetDamage() >= 20 )
|
||||
SetCondition( COND_HEAVY_DAMAGE );
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
void CNPC_GMan::TraceAttack( CBaseEntity *pAttacker, float flDamage, const Vector &vecDir, trace_t *ptr, int bitsDamageType)
|
||||
{
|
||||
g_pEffects->Ricochet( ptr->endpos, ptr->plane.normal );
|
||||
// AddMultiDamage( pevAttacker, this, flDamage, bitsDamageType );
|
||||
}
|
||||
|
||||
int CNPC_GMan::PlayScriptedSentence( const char *pszSentence, float delay, float volume, soundlevel_t soundlevel, bool bConcurrent, CBaseEntity *pListener )
|
||||
{
|
||||
BaseClass::PlayScriptedSentence( pszSentence, delay, volume, soundlevel, bConcurrent, pListener );
|
||||
|
||||
m_flTalkTime = gpGlobals->curtime + delay;
|
||||
m_hTalkTarget = pListener;
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,951 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
#include "cbase.h"
|
||||
#include "ai_default.h"
|
||||
#include "ai_task.h"
|
||||
#include "ai_schedule.h"
|
||||
#include "ai_node.h"
|
||||
#include "ai_hull.h"
|
||||
#include "ai_hint.h"
|
||||
#include "ai_memory.h"
|
||||
#include "ai_route.h"
|
||||
#include "ai_motor.h"
|
||||
#include "soundent.h"
|
||||
#include "game.h"
|
||||
#include "npcevent.h"
|
||||
#include "entitylist.h"
|
||||
#include "activitylist.h"
|
||||
#include "animation.h"
|
||||
#include "basecombatweapon.h"
|
||||
#include "IEffects.h"
|
||||
#include "vstdlib/random.h"
|
||||
#include "engine/IEngineSound.h"
|
||||
#include "ammodef.h"
|
||||
#include "util.h"
|
||||
#include "hl1_ai_basenpc.h"
|
||||
#include "hl1_basegrenade.h"
|
||||
#include "movevars_shared.h"
|
||||
#include "ai_basenpc.h"
|
||||
|
||||
|
||||
ConVar sk_hassassin_health( "sk_hassassin_health", "50" );
|
||||
|
||||
//=========================================================
|
||||
// monster-specific schedule types
|
||||
//=========================================================
|
||||
enum
|
||||
{
|
||||
SCHED_ASSASSIN_EXPOSED = LAST_SHARED_SCHEDULE,// cover was blown.
|
||||
SCHED_ASSASSIN_JUMP, // fly through the air
|
||||
SCHED_ASSASSIN_JUMP_ATTACK, // fly through the air and shoot
|
||||
SCHED_ASSASSIN_JUMP_LAND, // hit and run away
|
||||
SCHED_ASSASSIN_FAIL,
|
||||
SCHED_ASSASSIN_TAKE_COVER_FROM_ENEMY1,
|
||||
SCHED_ASSASSIN_TAKE_COVER_FROM_ENEMY2,
|
||||
SCHED_ASSASSIN_TAKE_COVER_FROM_BEST_SOUND,
|
||||
SCHED_ASSASSIN_HIDE,
|
||||
SCHED_ASSASSIN_HUNT,
|
||||
};
|
||||
|
||||
Activity ACT_ASSASSIN_FLY_UP;
|
||||
Activity ACT_ASSASSIN_FLY_ATTACK;
|
||||
Activity ACT_ASSASSIN_FLY_DOWN;
|
||||
|
||||
//=========================================================
|
||||
// monster-specific tasks
|
||||
//=========================================================
|
||||
|
||||
enum
|
||||
{
|
||||
TASK_ASSASSIN_FALL_TO_GROUND = LAST_SHARED_TASK + 1, // falling and waiting to hit ground
|
||||
};
|
||||
|
||||
|
||||
//=========================================================
|
||||
// Monster's Anim Events Go Here
|
||||
//=========================================================
|
||||
#define ASSASSIN_AE_SHOOT1 1
|
||||
#define ASSASSIN_AE_TOSS1 2
|
||||
#define ASSASSIN_AE_JUMP 3
|
||||
|
||||
|
||||
#define MEMORY_BADJUMP bits_MEMORY_CUSTOM1
|
||||
|
||||
class CNPC_HAssassin : public CHL1BaseNPC
|
||||
{
|
||||
DECLARE_CLASS( CNPC_HAssassin, CHL1BaseNPC );
|
||||
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
|
||||
int TranslateSchedule( int scheduleType );
|
||||
|
||||
void HandleAnimEvent( animevent_t *pEvent );
|
||||
float MaxYawSpeed() { return 360.0f; }
|
||||
|
||||
void Shoot ( void );
|
||||
|
||||
int MeleeAttack1Conditions ( float flDot, float flDist );
|
||||
int RangeAttack1Conditions ( float flDot, float flDist );
|
||||
int RangeAttack2Conditions ( float flDot, float flDist );
|
||||
|
||||
int SelectSchedule ( void );
|
||||
|
||||
void RunTask ( const Task_t *pTask );
|
||||
void StartTask ( const Task_t *pTask );
|
||||
|
||||
Class_T Classify ( void );
|
||||
|
||||
int GetSoundInterests( void );
|
||||
|
||||
void RunAI( void );
|
||||
|
||||
float m_flLastShot;
|
||||
float m_flDiviation;
|
||||
|
||||
float m_flNextJump;
|
||||
Vector m_vecJumpVelocity;
|
||||
|
||||
float m_flNextGrenadeCheck;
|
||||
Vector m_vecTossVelocity;
|
||||
bool m_fThrowGrenade;
|
||||
|
||||
int m_iTargetRanderamt;
|
||||
|
||||
int m_iFrustration;
|
||||
|
||||
int m_iAmmoType;
|
||||
|
||||
public:
|
||||
DECLARE_DATADESC();
|
||||
DEFINE_CUSTOM_AI;
|
||||
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( monster_human_assassin, CNPC_HAssassin );
|
||||
|
||||
BEGIN_DATADESC( CNPC_HAssassin )
|
||||
DEFINE_FIELD( m_flLastShot, FIELD_TIME ),
|
||||
DEFINE_FIELD( m_flDiviation, FIELD_FLOAT ),
|
||||
|
||||
DEFINE_FIELD( m_flNextJump, FIELD_TIME ),
|
||||
DEFINE_FIELD( m_vecJumpVelocity, FIELD_VECTOR ),
|
||||
|
||||
DEFINE_FIELD( m_flNextGrenadeCheck, FIELD_TIME ),
|
||||
DEFINE_FIELD( m_vecTossVelocity, FIELD_VECTOR ),
|
||||
DEFINE_FIELD( m_fThrowGrenade, FIELD_BOOLEAN ),
|
||||
|
||||
DEFINE_FIELD( m_iTargetRanderamt, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( m_iFrustration, FIELD_INTEGER ),
|
||||
|
||||
//DEFINE_FIELD( m_iAmmoType, FIELD_INTEGER ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
//=========================================================
|
||||
// Spawn
|
||||
//=========================================================
|
||||
void CNPC_HAssassin::Spawn()
|
||||
{
|
||||
Precache( );
|
||||
|
||||
SetModel( "models/hassassin.mdl");
|
||||
|
||||
SetHullType(HULL_HUMAN);
|
||||
SetHullSizeNormal();
|
||||
|
||||
|
||||
SetNavType ( NAV_GROUND );
|
||||
SetSolid( SOLID_BBOX );
|
||||
AddSolidFlags( FSOLID_NOT_STANDABLE );
|
||||
SetMoveType( MOVETYPE_STEP );
|
||||
m_bloodColor = BLOOD_COLOR_RED;
|
||||
ClearEffects();
|
||||
m_iHealth = sk_hassassin_health.GetFloat();
|
||||
m_flFieldOfView = VIEW_FIELD_WIDE; // indicates the width of this monster's forward view cone ( as a dotproduct result )
|
||||
m_NPCState = NPC_STATE_NONE;
|
||||
|
||||
m_HackedGunPos = Vector( 0, 24, 48 );
|
||||
|
||||
m_iTargetRanderamt = 20;
|
||||
SetRenderColor( 255, 255, 255, 20 );
|
||||
m_nRenderMode = kRenderTransTexture;
|
||||
|
||||
CapabilitiesClear();
|
||||
CapabilitiesAdd( bits_CAP_MOVE_GROUND );
|
||||
CapabilitiesAdd( bits_CAP_INNATE_RANGE_ATTACK1 | bits_CAP_INNATE_RANGE_ATTACK2 | bits_CAP_INNATE_MELEE_ATTACK1 );
|
||||
|
||||
NPCInit();
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Precache - precaches all resources this monster needs
|
||||
//=========================================================
|
||||
void CNPC_HAssassin::Precache()
|
||||
{
|
||||
m_iAmmoType = GetAmmoDef()->Index("9mmRound");
|
||||
|
||||
PrecacheModel("models/hassassin.mdl");
|
||||
|
||||
UTIL_PrecacheOther( "npc_handgrenade" );
|
||||
|
||||
PrecacheScriptSound( "HAssassin.Shot" );
|
||||
PrecacheScriptSound( "HAssassin.Beamsound" );
|
||||
PrecacheScriptSound( "HAssassin.Footstep" );
|
||||
}
|
||||
|
||||
int CNPC_HAssassin::GetSoundInterests( void )
|
||||
{
|
||||
return SOUND_WORLD |
|
||||
SOUND_COMBAT |
|
||||
SOUND_PLAYER |
|
||||
SOUND_DANGER;
|
||||
}
|
||||
|
||||
Class_T CNPC_HAssassin::Classify ( void )
|
||||
{
|
||||
return CLASS_HUMAN_MILITARY;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// CheckMeleeAttack1 - jump like crazy if the enemy gets too close.
|
||||
//=========================================================
|
||||
int CNPC_HAssassin::MeleeAttack1Conditions ( float flDot, float flDist )
|
||||
{
|
||||
if ( m_flNextJump < gpGlobals->curtime && ( flDist <= 128 || HasMemory( MEMORY_BADJUMP )) && GetEnemy() != NULL )
|
||||
{
|
||||
trace_t tr;
|
||||
|
||||
Vector vecMin = Vector( random->RandomFloat( 0, -64), random->RandomFloat( 0, -64 ), 0 );
|
||||
Vector vecMax = Vector( random->RandomFloat( 0, 64), random->RandomFloat( 0, 64 ), 160 );
|
||||
|
||||
Vector vecDest = GetAbsOrigin() + Vector( random->RandomFloat( -64, 64), random->RandomFloat( -64, 64 ), 160 );
|
||||
|
||||
UTIL_TraceHull( GetAbsOrigin() + Vector( 0, 0, 36 ), GetAbsOrigin() + Vector( 0, 0, 36 ), vecMin, vecMax, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
|
||||
|
||||
//NDebugOverlay::Box( GetAbsOrigin() + Vector( 0, 0, 36 ), vecMin, vecMax, 0,0, 255, 0, 2.0 );
|
||||
|
||||
if ( tr.startsolid || tr.fraction < 1.0)
|
||||
{
|
||||
return COND_TOO_CLOSE_TO_ATTACK;
|
||||
}
|
||||
|
||||
float flGravity = GetCurrentGravity();
|
||||
|
||||
float time = sqrt( 160 / (0.5 * flGravity));
|
||||
float speed = flGravity * time / 160;
|
||||
m_vecJumpVelocity = ( vecDest - GetAbsOrigin() ) * speed;
|
||||
|
||||
return COND_CAN_MELEE_ATTACK1;
|
||||
}
|
||||
|
||||
if ( flDist > 128 )
|
||||
return COND_TOO_FAR_TO_ATTACK;
|
||||
|
||||
return COND_NONE;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// CheckRangeAttack1 - drop a cap in their ass
|
||||
//
|
||||
//=========================================================
|
||||
int CNPC_HAssassin::RangeAttack1Conditions ( float flDot, float flDist )
|
||||
{
|
||||
if ( !HasCondition( COND_ENEMY_OCCLUDED ) && flDist > 64 && flDist <= 2048 )
|
||||
{
|
||||
trace_t tr;
|
||||
|
||||
Vector vecSrc = GetAbsOrigin() + m_HackedGunPos;
|
||||
|
||||
// verify that a bullet fired from the gun will hit the enemy before the world.
|
||||
UTIL_TraceLine( vecSrc, GetEnemy()->BodyTarget(vecSrc), MASK_SOLID, this, COLLISION_GROUP_NONE, &tr);
|
||||
|
||||
if ( tr.fraction == 1.0 || tr.m_pEnt == GetEnemy() )
|
||||
{
|
||||
return COND_CAN_RANGE_ATTACK1;
|
||||
}
|
||||
}
|
||||
|
||||
return COND_NONE;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// CheckRangeAttack2 - toss grenade is enemy gets in the way and is too close.
|
||||
//=========================================================
|
||||
int CNPC_HAssassin::RangeAttack2Conditions ( float flDot, float flDist )
|
||||
{
|
||||
m_fThrowGrenade = false;
|
||||
if ( !FBitSet ( GetEnemy()->GetFlags(), FL_ONGROUND ) )
|
||||
{
|
||||
// don't throw grenades at anything that isn't on the ground!
|
||||
return COND_NONE;
|
||||
}
|
||||
|
||||
// don't get grenade happy unless the player starts to piss you off
|
||||
if ( m_iFrustration <= 2)
|
||||
return COND_NONE;
|
||||
|
||||
if ( m_flNextGrenadeCheck < gpGlobals->curtime && !HasCondition( COND_ENEMY_OCCLUDED ) && flDist <= 512 )
|
||||
{
|
||||
Vector vTossPos;
|
||||
QAngle vAngles;
|
||||
|
||||
GetAttachment( "grenadehand", vTossPos, vAngles );
|
||||
|
||||
Vector vecToss = VecCheckThrow( this, vTossPos, GetEnemy()->WorldSpaceCenter(), flDist, 0.5 ); // use dist as speed to get there in 1 second
|
||||
|
||||
if ( vecToss != vec3_origin )
|
||||
{
|
||||
m_vecTossVelocity = vecToss;
|
||||
|
||||
// throw a hand grenade
|
||||
m_fThrowGrenade = TRUE;
|
||||
|
||||
return COND_CAN_RANGE_ATTACK2;
|
||||
}
|
||||
}
|
||||
|
||||
return COND_NONE;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// StartTask
|
||||
//=========================================================
|
||||
void CNPC_HAssassin::StartTask ( const Task_t *pTask )
|
||||
{
|
||||
switch ( pTask->iTask )
|
||||
{
|
||||
case TASK_RANGE_ATTACK2:
|
||||
if (!m_fThrowGrenade)
|
||||
{
|
||||
TaskComplete( );
|
||||
}
|
||||
else
|
||||
{
|
||||
BaseClass::StartTask ( pTask );
|
||||
}
|
||||
break;
|
||||
case TASK_ASSASSIN_FALL_TO_GROUND:
|
||||
m_flWaitFinished = gpGlobals->curtime + 2.0f;
|
||||
break;
|
||||
default:
|
||||
BaseClass::StartTask ( pTask );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// RunTask
|
||||
//=========================================================
|
||||
void CNPC_HAssassin::RunTask ( const Task_t *pTask )
|
||||
{
|
||||
switch ( pTask->iTask )
|
||||
{
|
||||
case TASK_ASSASSIN_FALL_TO_GROUND:
|
||||
GetMotor()->SetIdealYawAndUpdate( GetEnemyLKP() );
|
||||
|
||||
if ( IsSequenceFinished() )
|
||||
{
|
||||
if ( GetAbsVelocity().z > 0)
|
||||
{
|
||||
SetActivity( ACT_ASSASSIN_FLY_UP );
|
||||
}
|
||||
else if ( HasCondition ( COND_SEE_ENEMY ))
|
||||
{
|
||||
SetActivity( ACT_ASSASSIN_FLY_ATTACK );
|
||||
SetCycle( 0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
SetActivity( ACT_ASSASSIN_FLY_DOWN );
|
||||
SetCycle( 0 );
|
||||
}
|
||||
|
||||
ResetSequenceInfo( );
|
||||
}
|
||||
|
||||
if ( GetFlags() & FL_ONGROUND)
|
||||
{
|
||||
TaskComplete( );
|
||||
}
|
||||
else if( gpGlobals->curtime > m_flWaitFinished || GetAbsVelocity().z == 0.0 )
|
||||
{
|
||||
// I've waited two seconds and haven't hit the ground. Try to force it.
|
||||
trace_t trace;
|
||||
UTIL_TraceEntity( this, GetAbsOrigin(), GetAbsOrigin() - Vector( 0, 0, 1 ), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &trace );
|
||||
|
||||
if( trace.DidHitWorld() )
|
||||
{
|
||||
SetGroundEntity( trace.m_pEnt );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Try again in a couple of seconds.
|
||||
m_flWaitFinished = gpGlobals->curtime + 2.0f;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
BaseClass::RunTask ( pTask );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// GetSchedule - Decides which type of schedule best suits
|
||||
// the monster's current state and conditions. Then calls
|
||||
// monster's member function to get a pointer to a schedule
|
||||
// of the proper type.
|
||||
//=========================================================
|
||||
int CNPC_HAssassin::SelectSchedule ( void )
|
||||
{
|
||||
switch ( m_NPCState )
|
||||
{
|
||||
case NPC_STATE_IDLE:
|
||||
case NPC_STATE_ALERT:
|
||||
{
|
||||
if ( HasCondition ( COND_HEAR_DANGER ) || HasCondition ( COND_HEAR_COMBAT ) )
|
||||
{
|
||||
if ( HasCondition ( COND_HEAR_DANGER ) )
|
||||
return SCHED_TAKE_COVER_FROM_BEST_SOUND;
|
||||
|
||||
else
|
||||
return SCHED_INVESTIGATE_SOUND;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case NPC_STATE_COMBAT:
|
||||
{
|
||||
// dead enemy
|
||||
if ( HasCondition( COND_ENEMY_DEAD ) )
|
||||
{
|
||||
// call base class, all code to handle dead enemies is centralized there.
|
||||
return BaseClass::SelectSchedule();
|
||||
}
|
||||
|
||||
// flying?
|
||||
if ( GetMoveType() == MOVETYPE_FLYGRAVITY )
|
||||
{
|
||||
if ( GetFlags() & FL_ONGROUND )
|
||||
{
|
||||
//Msg( "landed\n" );
|
||||
// just landed
|
||||
SetMoveType( MOVETYPE_STEP );
|
||||
return SCHED_ASSASSIN_JUMP_LAND;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Msg("jump\n");
|
||||
// jump or jump/shoot
|
||||
if ( m_NPCState == NPC_STATE_COMBAT )
|
||||
return SCHED_ASSASSIN_JUMP;
|
||||
else
|
||||
return SCHED_ASSASSIN_JUMP_ATTACK;
|
||||
}
|
||||
}
|
||||
|
||||
if ( HasCondition ( COND_HEAR_DANGER ) )
|
||||
{
|
||||
return SCHED_TAKE_COVER_FROM_BEST_SOUND;
|
||||
}
|
||||
|
||||
if ( HasCondition ( COND_LIGHT_DAMAGE ) )
|
||||
{
|
||||
m_iFrustration++;
|
||||
}
|
||||
if ( HasCondition ( COND_HEAVY_DAMAGE ) )
|
||||
{
|
||||
m_iFrustration++;
|
||||
}
|
||||
|
||||
// jump player!
|
||||
if ( HasCondition ( COND_CAN_MELEE_ATTACK1 ) )
|
||||
{
|
||||
//Msg( "melee attack 1\n");
|
||||
return SCHED_MELEE_ATTACK1;
|
||||
}
|
||||
|
||||
// throw grenade
|
||||
if ( HasCondition ( COND_CAN_RANGE_ATTACK2 ) )
|
||||
{
|
||||
//Msg( "range attack 2\n");
|
||||
return SCHED_RANGE_ATTACK2;
|
||||
}
|
||||
|
||||
// spotted
|
||||
if ( HasCondition ( COND_SEE_ENEMY ) && HasCondition ( COND_ENEMY_FACING_ME ) )
|
||||
{
|
||||
//Msg("exposed\n");
|
||||
m_iFrustration++;
|
||||
return SCHED_ASSASSIN_EXPOSED;
|
||||
}
|
||||
|
||||
// can attack
|
||||
if ( HasCondition ( COND_CAN_RANGE_ATTACK1 ) )
|
||||
{
|
||||
//Msg( "range attack 1\n" );
|
||||
m_iFrustration = 0;
|
||||
return SCHED_RANGE_ATTACK1;
|
||||
}
|
||||
|
||||
if ( HasCondition ( COND_SEE_ENEMY ) )
|
||||
{
|
||||
//Msg( "face\n");
|
||||
return SCHED_COMBAT_FACE;
|
||||
}
|
||||
|
||||
// new enemy
|
||||
if ( HasCondition ( COND_NEW_ENEMY ) )
|
||||
{
|
||||
//Msg( "take cover\n");
|
||||
return SCHED_TAKE_COVER_FROM_ENEMY;
|
||||
}
|
||||
|
||||
// ALERT( at_console, "stand\n");
|
||||
return SCHED_ALERT_STAND;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return BaseClass::SelectSchedule();
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// HandleAnimEvent - catches the monster-specific messages
|
||||
// that occur when tagged animation frames are played.
|
||||
//
|
||||
// Returns number of events handled, 0 if none.
|
||||
//=========================================================
|
||||
void CNPC_HAssassin::HandleAnimEvent( animevent_t *pEvent )
|
||||
{
|
||||
switch( pEvent->event )
|
||||
{
|
||||
case ASSASSIN_AE_SHOOT1:
|
||||
Shoot( );
|
||||
break;
|
||||
case ASSASSIN_AE_TOSS1:
|
||||
{
|
||||
Vector vTossPos;
|
||||
QAngle vAngles;
|
||||
|
||||
GetAttachment( "grenadehand", vTossPos, vAngles );
|
||||
|
||||
CHandGrenade *pGrenade = (CHandGrenade*)Create( "grenade_hand", vTossPos, vec3_angle );
|
||||
if ( pGrenade )
|
||||
{
|
||||
pGrenade->ShootTimed( this, m_vecTossVelocity, 2.0 );
|
||||
}
|
||||
|
||||
m_flNextGrenadeCheck = gpGlobals->curtime + 6;// wait six seconds before even looking again to see if a grenade can be thrown.
|
||||
m_fThrowGrenade = FALSE;
|
||||
// !!!LATER - when in a group, only try to throw grenade if ordered.
|
||||
}
|
||||
break;
|
||||
case ASSASSIN_AE_JUMP:
|
||||
{
|
||||
SetMoveType( MOVETYPE_FLYGRAVITY );
|
||||
SetGroundEntity( NULL );
|
||||
SetAbsVelocity( m_vecJumpVelocity );
|
||||
m_flNextJump = gpGlobals->curtime + 3.0;
|
||||
}
|
||||
return;
|
||||
default:
|
||||
BaseClass::HandleAnimEvent( pEvent );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=========================================================
|
||||
// Shoot
|
||||
//=========================================================
|
||||
void CNPC_HAssassin::Shoot ( void )
|
||||
{
|
||||
Vector vForward, vRight, vUp;
|
||||
Vector vecShootOrigin;
|
||||
QAngle vAngles;
|
||||
|
||||
if ( GetEnemy() == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GetAttachment( "guntip", vecShootOrigin, vAngles );
|
||||
|
||||
Vector vecShootDir = GetShootEnemyDir( vecShootOrigin );
|
||||
|
||||
if (m_flLastShot + 2 < gpGlobals->curtime)
|
||||
{
|
||||
m_flDiviation = 0.10;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_flDiviation -= 0.01;
|
||||
if (m_flDiviation < 0.02)
|
||||
m_flDiviation = 0.02;
|
||||
}
|
||||
m_flLastShot = gpGlobals->curtime;
|
||||
|
||||
AngleVectors( GetAbsAngles(), &vForward, &vRight, &vUp );
|
||||
|
||||
Vector vecShellVelocity = vRight * random->RandomFloat(40,90) + vUp * random->RandomFloat(75,200) + vForward * random->RandomFloat(-40, 40);
|
||||
EjectShell( GetAbsOrigin() + vUp * 32 + vForward * 12, vecShellVelocity, GetAbsAngles().y, 0 );
|
||||
FireBullets( 1, vecShootOrigin, vecShootDir, Vector( m_flDiviation, m_flDiviation, m_flDiviation ), 2048, m_iAmmoType ); // shoot +-8 degrees
|
||||
|
||||
//NDebugOverlay::Line( vecShootOrigin, vecShootOrigin + vecShootDir * 2048, 255, 0, 0, true, 2.0 );
|
||||
|
||||
CPASAttenuationFilter filter( this );
|
||||
EmitSound( filter, entindex(), "HAssassin.Shot" );
|
||||
|
||||
DoMuzzleFlash();
|
||||
|
||||
VectorAngles( vecShootDir, vAngles );
|
||||
SetPoseParameter( "shoot", vecShootDir.x );
|
||||
|
||||
m_cAmmoLoaded--;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
int CNPC_HAssassin::TranslateSchedule ( int scheduleType )
|
||||
{
|
||||
// Msg( "%d\n", m_iFrustration );
|
||||
switch ( scheduleType )
|
||||
{
|
||||
case SCHED_TAKE_COVER_FROM_ENEMY:
|
||||
|
||||
if ( m_iHealth > 30 )
|
||||
return SCHED_ASSASSIN_TAKE_COVER_FROM_ENEMY1;
|
||||
else
|
||||
return SCHED_ASSASSIN_TAKE_COVER_FROM_ENEMY2;
|
||||
|
||||
case SCHED_TAKE_COVER_FROM_BEST_SOUND:
|
||||
return SCHED_ASSASSIN_TAKE_COVER_FROM_BEST_SOUND;
|
||||
case SCHED_FAIL:
|
||||
|
||||
if ( m_NPCState == NPC_STATE_COMBAT )
|
||||
return SCHED_ASSASSIN_FAIL;
|
||||
|
||||
break;
|
||||
case SCHED_ALERT_STAND:
|
||||
|
||||
if ( m_NPCState == NPC_STATE_COMBAT )
|
||||
return SCHED_ASSASSIN_HIDE;
|
||||
|
||||
break;
|
||||
//case SCHED_CHASE_ENEMY:
|
||||
// return SCHED_ASSASSIN_HUNT;
|
||||
|
||||
case SCHED_MELEE_ATTACK1:
|
||||
|
||||
if ( GetFlags() & FL_ONGROUND)
|
||||
{
|
||||
if (m_flNextJump > gpGlobals->curtime)
|
||||
{
|
||||
// can't jump yet, go ahead and fail
|
||||
return SCHED_ASSASSIN_FAIL;
|
||||
}
|
||||
else
|
||||
{
|
||||
return SCHED_ASSASSIN_JUMP;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return SCHED_ASSASSIN_JUMP_ATTACK;
|
||||
}
|
||||
}
|
||||
|
||||
return BaseClass::TranslateSchedule( scheduleType );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// RunAI
|
||||
//=========================================================
|
||||
void CNPC_HAssassin::RunAI( void )
|
||||
{
|
||||
BaseClass::RunAI();
|
||||
|
||||
// always visible if moving
|
||||
// always visible is not on hard
|
||||
if (g_iSkillLevel != SKILL_HARD || GetEnemy() == NULL || m_lifeState == LIFE_DEAD || GetActivity() == ACT_RUN || GetActivity() == ACT_WALK || !(GetFlags() & FL_ONGROUND))
|
||||
m_iTargetRanderamt = 255;
|
||||
else
|
||||
m_iTargetRanderamt = 20;
|
||||
|
||||
CPASAttenuationFilter filter( this );
|
||||
|
||||
if ( GetRenderColor().a > m_iTargetRanderamt)
|
||||
{
|
||||
if ( GetRenderColor().a == 255)
|
||||
{
|
||||
EmitSound( filter, entindex(), "HAssassin.Beamsound" );
|
||||
}
|
||||
|
||||
SetRenderColorA( MAX( GetRenderColor().a - 50, m_iTargetRanderamt ) );
|
||||
m_nRenderMode = kRenderTransTexture;
|
||||
}
|
||||
else if ( GetRenderColor().a < m_iTargetRanderamt)
|
||||
{
|
||||
SetRenderColorA ( MIN( GetRenderColor().a + 50, m_iTargetRanderamt ) );
|
||||
if (GetRenderColor().a == 255)
|
||||
m_nRenderMode = kRenderNormal;
|
||||
}
|
||||
|
||||
if ( GetActivity() == ACT_RUN || GetActivity() == ACT_WALK)
|
||||
{
|
||||
static int iStep = 0;
|
||||
iStep = ! iStep;
|
||||
if (iStep)
|
||||
{
|
||||
EmitSound( filter, entindex(), "HAssassin.Footstep" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AI_BEGIN_CUSTOM_NPC( monster_human_assassin, CNPC_HAssassin )
|
||||
|
||||
DECLARE_TASK( TASK_ASSASSIN_FALL_TO_GROUND )
|
||||
|
||||
DECLARE_ACTIVITY( ACT_ASSASSIN_FLY_UP )
|
||||
DECLARE_ACTIVITY( ACT_ASSASSIN_FLY_ATTACK )
|
||||
DECLARE_ACTIVITY( ACT_ASSASSIN_FLY_DOWN )
|
||||
|
||||
//=========================================================
|
||||
// AI Schedules Specific to this monster
|
||||
//=========================================================
|
||||
|
||||
//=========================================================
|
||||
// Enemy exposed assasin's cover
|
||||
//=========================================================
|
||||
|
||||
//=========================================================
|
||||
// > SCHED_ASSASSIN_EXPOSED
|
||||
//=========================================================
|
||||
DEFINE_SCHEDULE
|
||||
(
|
||||
SCHED_ASSASSIN_EXPOSED,
|
||||
|
||||
" Tasks"
|
||||
" TASK_STOP_MOVING 0"
|
||||
" TASK_RANGE_ATTACK1 0"
|
||||
" TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_ASSASSIN_JUMP"
|
||||
" TASK_SET_SCHEDULE SCHEDULE:SCHED_TAKE_COVER_FROM_ENEMY"
|
||||
" "
|
||||
" Interrupts"
|
||||
" COND_CAN_MELEE_ATTACK1"
|
||||
|
||||
)
|
||||
|
||||
//=========================================================
|
||||
// > SCHED_ASSASSIN_JUMP
|
||||
//=========================================================
|
||||
DEFINE_SCHEDULE
|
||||
(
|
||||
SCHED_ASSASSIN_JUMP,
|
||||
|
||||
" Tasks"
|
||||
" TASK_STOP_MOVING 0"
|
||||
" TASK_PLAY_SEQUENCE ACTIVITY:ACT_HOP"
|
||||
" TASK_SET_SCHEDULE SCHEDULE:SCHED_ASSASSIN_JUMP_ATTACK"
|
||||
" "
|
||||
" Interrupts"
|
||||
)
|
||||
|
||||
//=========================================================
|
||||
// > SCHED_ASSASSIN_JUMP_ATTACK
|
||||
//=========================================================
|
||||
DEFINE_SCHEDULE
|
||||
(
|
||||
SCHED_ASSASSIN_JUMP_ATTACK,
|
||||
|
||||
" Tasks"
|
||||
" TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_ASSASSIN_JUMP_LAND"
|
||||
" TASK_ASSASSIN_FALL_TO_GROUND 0"
|
||||
" "
|
||||
" Interrupts"
|
||||
)
|
||||
|
||||
//=========================================================
|
||||
// > SCHED_ASSASSIN_JUMP_LAND
|
||||
//=========================================================
|
||||
DEFINE_SCHEDULE
|
||||
(
|
||||
SCHED_ASSASSIN_JUMP_LAND,
|
||||
|
||||
" Tasks"
|
||||
" TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_ASSASSIN_EXPOSED"
|
||||
" TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
|
||||
" TASK_REMEMBER MEMORY:CUSTOM1"
|
||||
" TASK_FIND_NODE_COVER_FROM_ENEMY 0"
|
||||
" TASK_RUN_PATH 0"
|
||||
" TASK_FORGET MEMORY:CUSTOM1"
|
||||
" TASK_WAIT_FOR_MOVEMENT 0"
|
||||
" TASK_REMEMBER MEMORY:INCOVER"
|
||||
" TASK_FACE_ENEMY 0"
|
||||
" TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_RANGE_ATTACK1"
|
||||
|
||||
" "
|
||||
" Interrupts"
|
||||
)
|
||||
|
||||
//=========================================================
|
||||
// Fail Schedule
|
||||
//=========================================================
|
||||
|
||||
//=========================================================
|
||||
// > SCHED_ASSASSIN_FAIL
|
||||
//=========================================================
|
||||
DEFINE_SCHEDULE
|
||||
(
|
||||
SCHED_ASSASSIN_FAIL,
|
||||
|
||||
" Tasks"
|
||||
" TASK_STOP_MOVING 0"
|
||||
" TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
|
||||
" TASK_WAIT_FACE_ENEMY 2"
|
||||
" TASK_SET_SCHEDULE SCHEDULE:SCHED_CHASE_ENEMY"
|
||||
" "
|
||||
" Interrupts"
|
||||
" COND_LIGHT_DAMAGE"
|
||||
" COND_HEAVY_DAMAGE"
|
||||
" COND_CAN_RANGE_ATTACK1"
|
||||
" COND_CAN_RANGE_ATTACK2"
|
||||
" COND_CAN_MELEE_ATTACK1"
|
||||
" COND_HEAR_DANGER"
|
||||
" COND_HEAR_PLAYER"
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
//=========================================================
|
||||
// > SCHED_ASSASSIN_TAKE_COVER_FROM_ENEMY1
|
||||
//=========================================================
|
||||
DEFINE_SCHEDULE
|
||||
(
|
||||
SCHED_ASSASSIN_TAKE_COVER_FROM_ENEMY1,
|
||||
|
||||
" Tasks"
|
||||
" TASK_STOP_MOVING 0"
|
||||
" TASK_WAIT 0.2"
|
||||
" TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_RANGE_ATTACK1"
|
||||
" TASK_FIND_COVER_FROM_ENEMY 0"
|
||||
" TASK_RUN_PATH 0"
|
||||
" TASK_WAIT_FOR_MOVEMENT 0"
|
||||
" TASK_REMEMBER MEMORY:INCOVER"
|
||||
" TASK_FACE_ENEMY 0"
|
||||
" "
|
||||
" Interrupts"
|
||||
" COND_CAN_MELEE_ATTACK1"
|
||||
" COND_NEW_ENEMY"
|
||||
" COND_HEAR_DANGER"
|
||||
|
||||
)
|
||||
|
||||
//=========================================================
|
||||
// > SCHED_ASSASSIN_TAKE_COVER_FROM_ENEMY2
|
||||
//=========================================================
|
||||
DEFINE_SCHEDULE
|
||||
(
|
||||
SCHED_ASSASSIN_TAKE_COVER_FROM_ENEMY2,
|
||||
|
||||
" Tasks"
|
||||
" TASK_STOP_MOVING 0"
|
||||
" TASK_WAIT 0.2"
|
||||
" TASK_FACE_ENEMY 0"
|
||||
" TASK_RANGE_ATTACK1 0"
|
||||
" TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_RANGE_ATTACK1"
|
||||
" TASK_FIND_COVER_FROM_ENEMY 0"
|
||||
" TASK_RUN_PATH 0"
|
||||
" TASK_WAIT_FOR_MOVEMENT 0"
|
||||
" TASK_REMEMBER MEMORY:INCOVER"
|
||||
" TASK_FACE_ENEMY 0"
|
||||
" "
|
||||
" Interrupts"
|
||||
" COND_CAN_MELEE_ATTACK1"
|
||||
" COND_NEW_ENEMY"
|
||||
" COND_HEAR_DANGER"
|
||||
|
||||
)
|
||||
|
||||
|
||||
|
||||
//=========================================================
|
||||
// hide from the loudest sound source
|
||||
//=========================================================
|
||||
|
||||
//=========================================================
|
||||
// > SCHED_ASSASSIN_TAKE_COVER_FROM_BEST_SOUND
|
||||
//=========================================================
|
||||
DEFINE_SCHEDULE
|
||||
(
|
||||
SCHED_ASSASSIN_TAKE_COVER_FROM_BEST_SOUND,
|
||||
|
||||
" Tasks"
|
||||
" TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_MELEE_ATTACK1"
|
||||
" TASK_STOP_MOVING 0"
|
||||
" TASK_FIND_COVER_FROM_BEST_SOUND 0"
|
||||
" TASK_RUN_PATH 0"
|
||||
" TASK_WAIT_FOR_MOVEMENT 0"
|
||||
" TASK_REMEMBER MEMORY:INCOVER"
|
||||
" TASK_TURN_LEFT 179"
|
||||
" "
|
||||
" Interrupts"
|
||||
" COND_NEW_ENEMY"
|
||||
|
||||
)
|
||||
|
||||
//=========================================================
|
||||
// > SCHED_ASSASSIN_HIDE
|
||||
//=========================================================
|
||||
DEFINE_SCHEDULE
|
||||
(
|
||||
SCHED_ASSASSIN_HIDE,
|
||||
|
||||
" Tasks"
|
||||
" TASK_STOP_MOVING 0"
|
||||
" TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
|
||||
" TASK_WAIT 2.0"
|
||||
" TASK_SET_SCHEDULE SCHEDULE:SCHED_CHASE_ENEMY"
|
||||
|
||||
" Interrupts"
|
||||
" COND_NEW_ENEMY"
|
||||
" COND_SEE_ENEMY"
|
||||
" COND_SEE_FEAR"
|
||||
" COND_LIGHT_DAMAGE"
|
||||
" COND_HEAVY_DAMAGE"
|
||||
" COND_PROVOKED"
|
||||
" COND_HEAR_DANGER"
|
||||
)
|
||||
|
||||
//=========================================================
|
||||
// > SCHED_ASSASSIN_HUNT
|
||||
//=========================================================
|
||||
DEFINE_SCHEDULE
|
||||
(
|
||||
SCHED_ASSASSIN_HUNT,
|
||||
|
||||
" Tasks"
|
||||
" TASK_STOP_MOVING 0"
|
||||
" TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_ASSASSIN_TAKE_COVER_FROM_ENEMY2"
|
||||
" TASK_GET_PATH_TO_ENEMY 0"
|
||||
" TASK_RUN_PATH 0"
|
||||
" TASK_WAIT_FOR_MOVEMENT 0"
|
||||
|
||||
" Interrupts"
|
||||
" COND_NEW_ENEMY"
|
||||
" COND_CAN_RANGE_ATTACK1"
|
||||
" COND_HEAR_DANGER"
|
||||
)
|
||||
|
||||
AI_END_CUSTOM_NPC()
|
||||
@@ -0,0 +1,698 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Implements the headcrab, a tiny, jumpy alien parasite.
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "game.h"
|
||||
#include "ai_default.h"
|
||||
#include "ai_schedule.h"
|
||||
#include "ai_hull.h"
|
||||
#include "ai_route.h"
|
||||
#include "ai_motor.h"
|
||||
#include "npcevent.h"
|
||||
#include "hl1_npc_headcrab.h"
|
||||
#include "gib.h"
|
||||
//#include "AI_Interactions.h"
|
||||
#include "ndebugoverlay.h"
|
||||
#include "vstdlib/random.h"
|
||||
#include "engine/IEngineSound.h"
|
||||
#include "movevars_shared.h"
|
||||
#include "SoundEmitterSystem/isoundemittersystembase.h"
|
||||
|
||||
extern void ClearMultiDamage(void);
|
||||
extern void ApplyMultiDamage( void );
|
||||
|
||||
ConVar sk_headcrab_health( "sk_headcrab_health","20");
|
||||
ConVar sk_headcrab_dmg_bite( "sk_headcrab_dmg_bite","10");
|
||||
|
||||
#define CRAB_ATTN_IDLE (float)1.5
|
||||
#define HEADCRAB_GUTS_GIB_COUNT 1
|
||||
#define HEADCRAB_LEGS_GIB_COUNT 3
|
||||
#define HEADCRAB_ALL_GIB_COUNT 5
|
||||
|
||||
#define HEADCRAB_MAX_JUMP_DIST 256
|
||||
|
||||
#define HEADCRAB_RUNMODE_ACCELERATE 1
|
||||
#define HEADCRAB_RUNMODE_IDLE 2
|
||||
#define HEADCRAB_RUNMODE_DECELERATE 3
|
||||
#define HEADCRAB_RUNMODE_FULLSPEED 4
|
||||
#define HEADCRAB_RUNMODE_PAUSE 5
|
||||
|
||||
#define HEADCRAB_RUN_MINSPEED 0.5
|
||||
#define HEADCRAB_RUN_MAXSPEED 1.0
|
||||
|
||||
#define HC_AE_JUMPATTACK ( 2 )
|
||||
|
||||
BEGIN_DATADESC( CNPC_Headcrab )
|
||||
// m_nGibCount - don't save
|
||||
|
||||
// Function Pointers
|
||||
DEFINE_ENTITYFUNC( LeapTouch ),
|
||||
DEFINE_FIELD( m_vecJumpVel, FIELD_VECTOR ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
LINK_ENTITY_TO_CLASS( monster_headcrab, CNPC_Headcrab );
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
SCHED_HEADCRAB_RANGE_ATTACK1 = LAST_SHARED_SCHEDULE,
|
||||
SCHED_FAST_HEADCRAB_RANGE_ATTACK1,
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input :
|
||||
// Output :
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNPC_Headcrab::Spawn( void )
|
||||
{
|
||||
Precache();
|
||||
|
||||
SetRenderColor( 255, 255, 255, 255 );
|
||||
|
||||
SetModel( "models/headcrab.mdl" );
|
||||
m_iHealth = sk_headcrab_health.GetFloat();
|
||||
|
||||
SetHullType(HULL_TINY);
|
||||
SetHullSizeNormal();
|
||||
|
||||
SetSolid( SOLID_BBOX );
|
||||
AddSolidFlags( FSOLID_NOT_STANDABLE );
|
||||
SetMoveType( MOVETYPE_STEP );
|
||||
SetViewOffset( Vector(6, 0, 11) ); // Position of the eyes relative to NPC's origin.
|
||||
|
||||
m_bloodColor = BLOOD_COLOR_GREEN;
|
||||
m_flFieldOfView = 0.5;
|
||||
m_NPCState = NPC_STATE_NONE;
|
||||
m_nGibCount = HEADCRAB_ALL_GIB_COUNT;
|
||||
|
||||
CapabilitiesClear();
|
||||
CapabilitiesAdd( bits_CAP_MOVE_GROUND | bits_CAP_INNATE_RANGE_ATTACK1 );
|
||||
|
||||
NPCInit();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input :
|
||||
// Output :
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNPC_Headcrab::Precache( void )
|
||||
{
|
||||
PrecacheModel( "models/headcrab.mdl" );
|
||||
// PrecacheModel( "models/hc_squashed01.mdl" );
|
||||
// PrecacheModel( "models/gibs/hc_gibs.mdl" );
|
||||
|
||||
PrecacheScriptSound( "Headcrab.Bite" );
|
||||
PrecacheScriptSound( "Headcrab.Attack" );
|
||||
PrecacheScriptSound( "Headcrab.Idle" );
|
||||
PrecacheScriptSound( "Headcrab.Die" );
|
||||
PrecacheScriptSound( "Headcrab.Alert" );
|
||||
PrecacheScriptSound( "Headcrab.Pain" );
|
||||
|
||||
BaseClass::Precache();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNPC_Headcrab::IdleSound()
|
||||
{
|
||||
HeadCrabSound( "Headcrab.Idle" );
|
||||
}
|
||||
|
||||
|
||||
void CNPC_Headcrab::AlertSound()
|
||||
{
|
||||
HeadCrabSound( "Headcrab.Alert" );
|
||||
}
|
||||
|
||||
|
||||
void CNPC_Headcrab::PainSound( const CTakeDamageInfo &info )
|
||||
{
|
||||
HeadCrabSound( "Headcrab.Pain" );
|
||||
}
|
||||
|
||||
|
||||
void CNPC_Headcrab::DeathSound( const CTakeDamageInfo &info )
|
||||
{
|
||||
HeadCrabSound( "Headcrab.Die" );
|
||||
}
|
||||
|
||||
void CNPC_Headcrab::HeadCrabSound( const char *pchSound )
|
||||
{
|
||||
CPASAttenuationFilter filter( this, ATTN_IDLE );
|
||||
|
||||
CSoundParameters params;
|
||||
if ( GetParametersForSound( pchSound, params, NULL ) )
|
||||
{
|
||||
EmitSound_t ep( params );
|
||||
|
||||
ep.m_flVolume = GetSoundVolume();
|
||||
ep.m_nPitch = GetVoicePitch();
|
||||
|
||||
EmitSound( filter, entindex(), ep );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : pTask -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNPC_Headcrab::StartTask( const Task_t *pTask )
|
||||
{
|
||||
switch ( pTask->iTask )
|
||||
{
|
||||
case TASK_RANGE_ATTACK1:
|
||||
{
|
||||
SetIdealActivity( ACT_RANGE_ATTACK1 );
|
||||
SetTouch( &CNPC_Headcrab::LeapTouch );
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
BaseClass::StartTask( pTask );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : *pTask -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNPC_Headcrab::RunTask( const Task_t *pTask )
|
||||
{
|
||||
switch ( pTask->iTask )
|
||||
{
|
||||
case TASK_RANGE_ATTACK1:
|
||||
case TASK_RANGE_ATTACK2:
|
||||
{
|
||||
if ( IsSequenceFinished() )
|
||||
{
|
||||
TaskComplete();
|
||||
SetTouch( NULL );
|
||||
SetIdealActivity( ACT_IDLE );
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
CAI_BaseNPC::RunTask( pTask );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Output :
|
||||
//-----------------------------------------------------------------------------
|
||||
int CNPC_Headcrab::SelectSchedule( void )
|
||||
{
|
||||
switch ( m_NPCState )
|
||||
{
|
||||
case NPC_STATE_ALERT:
|
||||
{
|
||||
if (HasCondition( COND_LIGHT_DAMAGE ) || HasCondition( COND_HEAVY_DAMAGE ))
|
||||
{
|
||||
if ( fabs( GetMotor()->DeltaIdealYaw() ) < ( 1.0 - m_flFieldOfView) * 60 ) // roughly in the correct direction
|
||||
{
|
||||
return SCHED_TAKE_COVER_FROM_ORIGIN;
|
||||
}
|
||||
else if ( SelectWeightedSequence( ACT_SMALL_FLINCH ) != -1 )
|
||||
{
|
||||
return SCHED_SMALL_FLINCH;
|
||||
}
|
||||
}
|
||||
else if (HasCondition( COND_HEAR_DANGER ) ||
|
||||
HasCondition( COND_HEAR_PLAYER ) ||
|
||||
HasCondition( COND_HEAR_WORLD ) ||
|
||||
HasCondition( COND_HEAR_COMBAT ))
|
||||
{
|
||||
return SCHED_ALERT_FACE_BESTSOUND;
|
||||
}
|
||||
else
|
||||
{
|
||||
return SCHED_PATROL_WALK;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// no special cases here, call the base class
|
||||
return BaseClass::SelectSchedule();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose :
|
||||
// Input :
|
||||
// Output :
|
||||
//------------------------------------------------------------------------------
|
||||
void CNPC_Headcrab::Touch( CBaseEntity *pOther )
|
||||
{
|
||||
// If someone has smacked me into a wall then gib!
|
||||
/* if (m_NPCState == NPC_STATE_DEAD)
|
||||
{
|
||||
if (GetAbsVelocity().Length() > 250)
|
||||
{
|
||||
trace_t tr;
|
||||
Vector vecDir = GetAbsVelocity();
|
||||
VectorNormalize(vecDir);
|
||||
UTIL_TraceLine(GetAbsOrigin(), GetAbsOrigin() + vecDir * 100,
|
||||
MASK_SOLID_BRUSHONLY, pev, COLLISION_GROUP_NONE, &tr);
|
||||
float dotPr = DotProduct(vecDir,tr.plane.normal);
|
||||
if ((tr.fraction != 1.0) &&
|
||||
(dotPr < -0.8) )
|
||||
{
|
||||
Event_Gibbed();
|
||||
// Throw headcrab guts
|
||||
CGib::SpawnSpecificGibs( this, HEADCRAB_GUTS_GIB_COUNT, 300, 400, "models/gibs/hc_gibs.mdl");
|
||||
}
|
||||
|
||||
}
|
||||
}*/
|
||||
BaseClass::Touch(pOther);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : pevInflictor -
|
||||
// pevAttacker -
|
||||
// flDamage -
|
||||
// bitsDamageType -
|
||||
// Output :
|
||||
//-----------------------------------------------------------------------------
|
||||
int CNPC_Headcrab::OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo )
|
||||
{
|
||||
CTakeDamageInfo info = inputInfo;
|
||||
|
||||
//
|
||||
// Don't take any acid damage.
|
||||
//
|
||||
if ( info.GetDamageType() & DMG_ACID )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return BaseClass::OnTakeDamage_Alive( info );
|
||||
}
|
||||
|
||||
float CNPC_Headcrab::GetDamageAmount( void )
|
||||
{
|
||||
return sk_headcrab_dmg_bite.GetFloat();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : Type -
|
||||
// Output : CAI_Schedule *
|
||||
//-----------------------------------------------------------------------------
|
||||
int CNPC_Headcrab::TranslateSchedule( int scheduleType )
|
||||
{
|
||||
switch( scheduleType )
|
||||
{
|
||||
case SCHED_RANGE_ATTACK1:
|
||||
return SCHED_HEADCRAB_RANGE_ATTACK1;
|
||||
|
||||
case SCHED_FAIL_TAKE_COVER:
|
||||
return SCHED_ALERT_FACE;
|
||||
}
|
||||
|
||||
return BaseClass::TranslateSchedule( scheduleType );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNPC_Headcrab::PrescheduleThink( void )
|
||||
{
|
||||
BaseClass::PrescheduleThink();
|
||||
|
||||
//
|
||||
// Make the crab coo a little bit in combat state.
|
||||
//
|
||||
if (( m_NPCState == NPC_STATE_COMBAT ) && ( random->RandomFloat( 0, 5 ) < 0.1 ))
|
||||
{
|
||||
IdleSound();
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: For innate melee attack
|
||||
// Input :
|
||||
// Output :
|
||||
//-----------------------------------------------------------------------------
|
||||
int CNPC_Headcrab::RangeAttack1Conditions ( float flDot, float flDist )
|
||||
{
|
||||
if ( gpGlobals->curtime < m_flNextAttack )
|
||||
{
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
if ( !(GetFlags() & FL_ONGROUND) )
|
||||
{
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
if ( flDist > 256 )
|
||||
{
|
||||
return( COND_TOO_FAR_TO_ATTACK );
|
||||
}
|
||||
else if ( flDot < 0.65 )
|
||||
{
|
||||
return( COND_NOT_FACING_ATTACK );
|
||||
}
|
||||
|
||||
return( COND_CAN_RANGE_ATTACK1 );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Indicates this monster's place in the relationship table.
|
||||
// Output :
|
||||
//-----------------------------------------------------------------------------
|
||||
Class_T CNPC_Headcrab::Classify( void )
|
||||
{
|
||||
return CLASS_ALIEN_PREY;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Returns the real center of the monster. The bounding box is much larger
|
||||
// than the actual creature so this is needed for targetting.
|
||||
// Output : Vector
|
||||
//-----------------------------------------------------------------------------
|
||||
Vector CNPC_Headcrab::Center( void )
|
||||
{
|
||||
return Vector( GetAbsOrigin().x, GetAbsOrigin().y, GetAbsOrigin().z + 6 );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : &posSrc -
|
||||
// Output : Vector
|
||||
//-----------------------------------------------------------------------------
|
||||
Vector CNPC_Headcrab::BodyTarget( const Vector &posSrc, bool bNoisy )
|
||||
{
|
||||
return( Center() );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input :
|
||||
// Output :
|
||||
//-----------------------------------------------------------------------------
|
||||
float CNPC_Headcrab::MaxYawSpeed ( void )
|
||||
{
|
||||
switch ( GetActivity() )
|
||||
{
|
||||
case ACT_IDLE:
|
||||
return 30;
|
||||
break;
|
||||
|
||||
case ACT_RUN:
|
||||
case ACT_WALK:
|
||||
return 20;
|
||||
break;
|
||||
|
||||
case ACT_TURN_LEFT:
|
||||
case ACT_TURN_RIGHT:
|
||||
return 15;
|
||||
break;
|
||||
|
||||
case ACT_RANGE_ATTACK1:
|
||||
return 30;
|
||||
break;
|
||||
|
||||
default:
|
||||
return 30;
|
||||
break;
|
||||
}
|
||||
|
||||
return BaseClass::MaxYawSpeed();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: LeapTouch - this is the headcrab's touch function when it is in the air.
|
||||
// Input : *pOther -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNPC_Headcrab::LeapTouch( CBaseEntity *pOther )
|
||||
{
|
||||
if ( pOther->Classify() == Classify() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't hit if back on ground
|
||||
if ( !(GetFlags() & FL_ONGROUND) && ( pOther->IsNPC() || pOther->IsPlayer() ) )
|
||||
{
|
||||
BiteSound();
|
||||
TouchDamage( pOther );
|
||||
}
|
||||
|
||||
SetTouch( NULL );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Make the sound of this headcrab chomping a target.
|
||||
// Input :
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNPC_Headcrab::BiteSound( void )
|
||||
{
|
||||
HeadCrabSound( "Headcrab.Bite" );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Deal the damage from the headcrab's touch attack.
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNPC_Headcrab::TouchDamage( CBaseEntity *pOther )
|
||||
{
|
||||
CTakeDamageInfo info( this, this, GetDamageAmount(), DMG_SLASH );
|
||||
CalculateMeleeDamageForce( &info, GetAbsVelocity(), GetAbsOrigin() );
|
||||
pOther->TakeDamage( info );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Catches the monster-specific messages that occur when tagged
|
||||
// animation frames are played.
|
||||
// Input : *pEvent -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNPC_Headcrab::HandleAnimEvent( animevent_t *pEvent )
|
||||
{
|
||||
switch ( pEvent->event )
|
||||
{
|
||||
case HC_AE_JUMPATTACK:
|
||||
{
|
||||
SetGroundEntity( NULL );
|
||||
|
||||
//
|
||||
// Take him off ground so engine doesn't instantly reset FL_ONGROUND.
|
||||
//
|
||||
UTIL_SetOrigin( this, GetAbsOrigin() + Vector( 0 , 0 , 1 ));
|
||||
|
||||
Vector vecJumpDir;
|
||||
CBaseEntity *pEnemy = GetEnemy();
|
||||
if ( pEnemy )
|
||||
{
|
||||
Vector vecEnemyEyePos = pEnemy->EyePosition();
|
||||
|
||||
float gravity = GetCurrentGravity();
|
||||
if ( gravity <= 1 )
|
||||
{
|
||||
gravity = 1;
|
||||
}
|
||||
|
||||
//
|
||||
// How fast does the headcrab need to travel to reach my enemy's eyes given gravity?
|
||||
//
|
||||
float height = ( vecEnemyEyePos.z - GetAbsOrigin().z );
|
||||
if ( height < 16 )
|
||||
{
|
||||
height = 16;
|
||||
}
|
||||
else if ( height > 120 )
|
||||
{
|
||||
height = 120;
|
||||
}
|
||||
float speed = sqrt( 2 * gravity * height );
|
||||
float time = speed / gravity;
|
||||
|
||||
//
|
||||
// Scale the sideways velocity to get there at the right time
|
||||
//
|
||||
vecJumpDir = vecEnemyEyePos - GetAbsOrigin();
|
||||
vecJumpDir = vecJumpDir / time;
|
||||
|
||||
//
|
||||
// Speed to offset gravity at the desired height.
|
||||
//
|
||||
vecJumpDir.z = speed;
|
||||
|
||||
//
|
||||
// Don't jump too far/fast.
|
||||
//
|
||||
float distance = vecJumpDir.Length();
|
||||
if ( distance > 650 )
|
||||
{
|
||||
vecJumpDir = vecJumpDir * ( 650.0 / distance );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
// Jump hop, don't care where.
|
||||
//
|
||||
Vector forward, up;
|
||||
AngleVectors( GetAbsAngles(), &forward, NULL, &up );
|
||||
vecJumpDir = Vector( forward.x, forward.y, up.z ) * 350;
|
||||
}
|
||||
|
||||
int iSound = random->RandomInt( 0 , 2 );
|
||||
if ( iSound != 0 )
|
||||
{
|
||||
AttackSound();
|
||||
}
|
||||
|
||||
SetAbsVelocity( vecJumpDir );
|
||||
m_flNextAttack = gpGlobals->curtime + 2;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
CAI_BaseNPC::HandleAnimEvent( pEvent );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CNPC_Headcrab::AttackSound( void )
|
||||
{
|
||||
HeadCrabSound( "Headcrab.Attack" );
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Schedules
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
AI_BEGIN_CUSTOM_NPC( monster_headcrab, CNPC_Headcrab )
|
||||
|
||||
//=========================================================
|
||||
// > SCHED_HEADCRAB_RANGE_ATTACK1
|
||||
//=========================================================
|
||||
DEFINE_SCHEDULE
|
||||
(
|
||||
SCHED_HEADCRAB_RANGE_ATTACK1,
|
||||
|
||||
" Tasks"
|
||||
" TASK_STOP_MOVING 0"
|
||||
" TASK_FACE_IDEAL 0"
|
||||
" TASK_RANGE_ATTACK1 0"
|
||||
" TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
|
||||
" TASK_FACE_IDEAL 0"
|
||||
" TASK_WAIT_RANDOM 0.5"
|
||||
" "
|
||||
" Interrupts"
|
||||
" COND_ENEMY_OCCLUDED"
|
||||
" COND_NO_PRIMARY_AMMO"
|
||||
)
|
||||
|
||||
//=========================================================
|
||||
// > SCHED_FAST_HEADCRAB_RANGE_ATTACK1
|
||||
//=========================================================
|
||||
DEFINE_SCHEDULE
|
||||
(
|
||||
SCHED_FAST_HEADCRAB_RANGE_ATTACK1,
|
||||
|
||||
" Tasks"
|
||||
" TASK_STOP_MOVING 0"
|
||||
" TASK_FACE_IDEAL 0"
|
||||
" TASK_RANGE_ATTACK1 0"
|
||||
" TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
|
||||
" "
|
||||
" Interrupts"
|
||||
" COND_ENEMY_OCCLUDED"
|
||||
" COND_NO_PRIMARY_AMMO"
|
||||
)
|
||||
|
||||
AI_END_CUSTOM_NPC()
|
||||
|
||||
|
||||
class CNPC_BabyCrab : public CNPC_Headcrab
|
||||
{
|
||||
DECLARE_CLASS( CNPC_BabyCrab, CNPC_Headcrab );
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
|
||||
unsigned int PhysicsSolidMaskForEntity( void ) const;
|
||||
|
||||
int RangeAttack1Conditions ( float flDot, float flDist );
|
||||
float MaxYawSpeed( void ){ return 120.0f; }
|
||||
float GetDamageAmount( void );
|
||||
|
||||
virtual int GetVoicePitch( void ) { return PITCH_NORM + random->RandomInt( 40,50 ); }
|
||||
virtual float GetSoundVolume( void ) { return 0.8; }
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS( monster_babycrab, CNPC_BabyCrab );
|
||||
|
||||
unsigned int CNPC_BabyCrab::PhysicsSolidMaskForEntity( void ) const
|
||||
{
|
||||
unsigned int iMask = BaseClass::PhysicsSolidMaskForEntity();
|
||||
|
||||
iMask &= ~CONTENTS_MONSTERCLIP;
|
||||
|
||||
return iMask;
|
||||
}
|
||||
|
||||
void CNPC_BabyCrab::Spawn( void )
|
||||
{
|
||||
CNPC_Headcrab::Spawn();
|
||||
SetModel( "models/baby_headcrab.mdl" );
|
||||
m_nRenderMode = kRenderTransTexture;
|
||||
|
||||
SetRenderColor( 255, 255, 255, 192 );
|
||||
|
||||
UTIL_SetSize(this, Vector(-12, -12, 0), Vector(12, 12, 24));
|
||||
|
||||
m_iHealth = sk_headcrab_health.GetFloat() * 0.25; // less health than full grown
|
||||
}
|
||||
|
||||
void CNPC_BabyCrab::Precache( void )
|
||||
{
|
||||
PrecacheModel( "models/baby_headcrab.mdl" );
|
||||
CNPC_Headcrab::Precache();
|
||||
}
|
||||
|
||||
int CNPC_BabyCrab::RangeAttack1Conditions( float flDot, float flDist )
|
||||
{
|
||||
if ( GetFlags() & FL_ONGROUND )
|
||||
{
|
||||
if ( GetGroundEntity() && ( GetGroundEntity()->GetFlags() & ( FL_CLIENT | FL_NPC ) ) )
|
||||
return COND_CAN_RANGE_ATTACK1;
|
||||
|
||||
// A little less accurate, but jump from closer
|
||||
if ( flDist <= 180 && flDot >= 0.55 )
|
||||
return COND_CAN_RANGE_ATTACK1;
|
||||
}
|
||||
|
||||
return COND_NONE;
|
||||
}
|
||||
|
||||
float CNPC_BabyCrab::GetDamageAmount( void )
|
||||
{
|
||||
return sk_headcrab_dmg_bite.GetFloat() * 0.3;
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
#ifndef NPC_HEADCRAB_H
|
||||
#define NPC_HEADCRAB_H
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "hl1_ai_basenpc.h"
|
||||
|
||||
class CNPC_Headcrab : public CHL1BaseNPC
|
||||
{
|
||||
DECLARE_CLASS( CNPC_Headcrab, CHL1BaseNPC );
|
||||
public:
|
||||
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
|
||||
void RunTask ( const Task_t *pTask );
|
||||
void StartTask ( const Task_t *pTask );
|
||||
void SetYawSpeed ( void );
|
||||
Vector Center( void );
|
||||
Vector BodyTarget( const Vector &posSrc, bool bNoisy = true );
|
||||
|
||||
float MaxYawSpeed( void );
|
||||
Class_T Classify( void );
|
||||
|
||||
void LeapTouch ( CBaseEntity *pOther );
|
||||
void BiteSound( void );
|
||||
void AttackSound( void );
|
||||
void TouchDamage( CBaseEntity *pOther );
|
||||
void HandleAnimEvent( animevent_t *pEvent );
|
||||
int SelectSchedule( void );
|
||||
void Touch( CBaseEntity *pOther );
|
||||
int OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo );
|
||||
int TranslateSchedule( int scheduleType );
|
||||
void PrescheduleThink( void );
|
||||
int RangeAttack1Conditions ( float flDot, float flDist );
|
||||
float GetDamageAmount( void );
|
||||
virtual void PainSound( const CTakeDamageInfo &info );
|
||||
virtual void DeathSound( const CTakeDamageInfo &info );
|
||||
virtual void IdleSound();
|
||||
virtual void AlertSound();
|
||||
|
||||
virtual int GetVoicePitch( void ) { return 100; }
|
||||
virtual float GetSoundVolume( void ) { return 1.0; }
|
||||
|
||||
int m_nGibCount;
|
||||
|
||||
DEFINE_CUSTOM_AI;
|
||||
DECLARE_DATADESC();
|
||||
|
||||
protected:
|
||||
void HeadCrabSound( const char *pchSound );
|
||||
|
||||
Vector m_vecJumpVel;
|
||||
};
|
||||
|
||||
#endif //NPC_HEADCRAB_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,117 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
#ifndef NPC_HGRUNT_H
|
||||
#define NPC_HGRUNT_H
|
||||
|
||||
#include "ai_squad.h"
|
||||
#include "hl1_ai_basenpc.h"
|
||||
|
||||
class CNPC_HGrunt : public CHL1BaseNPC
|
||||
{
|
||||
DECLARE_CLASS( CNPC_HGrunt, CHL1BaseNPC );
|
||||
public:
|
||||
|
||||
void Precache( void );
|
||||
void Spawn( void );
|
||||
|
||||
void JustSpoke( void );
|
||||
void SpeakSentence( void );
|
||||
void PrescheduleThink ( void );
|
||||
|
||||
bool FOkToSpeak( void );
|
||||
|
||||
Class_T Classify ( void );
|
||||
int RangeAttack1Conditions ( float flDot, float flDist );
|
||||
int MeleeAttack1Conditions ( float flDot, float flDist );
|
||||
int RangeAttack2Conditions ( float flDot, float flDist );
|
||||
|
||||
Activity NPC_TranslateActivity( Activity eNewActivity );
|
||||
|
||||
void ClearAttackConditions( void );
|
||||
|
||||
int IRelationPriority( CBaseEntity *pTarget );
|
||||
|
||||
int GetGrenadeConditions ( float flDot, float flDist );
|
||||
|
||||
bool FCanCheckAttacks( void );
|
||||
|
||||
int GetSoundInterests ( void );
|
||||
|
||||
void TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator );
|
||||
int OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo );
|
||||
|
||||
float MaxYawSpeed( void );
|
||||
|
||||
void IdleSound( void );
|
||||
|
||||
void CheckAmmo ( void );
|
||||
|
||||
CBaseEntity *Kick( void );
|
||||
|
||||
Vector Weapon_ShootPosition( void );
|
||||
void HandleAnimEvent( animevent_t *pEvent );
|
||||
|
||||
void Shoot ( void );
|
||||
void Shotgun( void );
|
||||
|
||||
void StartTask ( const Task_t *pTask );
|
||||
void RunTask ( const Task_t *pTask );
|
||||
|
||||
int SelectSchedule( void );
|
||||
int TranslateSchedule( int scheduleType );
|
||||
|
||||
|
||||
void PainSound( const CTakeDamageInfo &info );
|
||||
void DeathSound( const CTakeDamageInfo &info );
|
||||
void SetAim( const Vector &aimDir );
|
||||
|
||||
bool HandleInteraction(int interactionType, void *data, CBaseCombatCharacter* sourceEnt);
|
||||
|
||||
void StartNPC ( void );
|
||||
|
||||
int SquadRecruit( int searchRadius, int maxMembers );
|
||||
|
||||
void Event_Killed( const CTakeDamageInfo &info );
|
||||
|
||||
|
||||
static const char *pGruntSentences[];
|
||||
|
||||
bool m_bInBarnacleMouth;
|
||||
|
||||
public:
|
||||
DECLARE_DATADESC();
|
||||
DEFINE_CUSTOM_AI;
|
||||
|
||||
private:
|
||||
|
||||
// checking the feasibility of a grenade toss is kind of costly, so we do it every couple of seconds,
|
||||
// not every server frame.
|
||||
float m_flNextGrenadeCheck;
|
||||
float m_flNextPainTime;
|
||||
float m_flLastEnemySightTime;
|
||||
float m_flTalkWaitTime;
|
||||
|
||||
Vector m_vecTossVelocity;
|
||||
|
||||
int m_iLastGrenadeCondition;
|
||||
bool m_fStanding;
|
||||
bool m_fFirstEncounter;// only put on the handsign show in the squad's first encounter.
|
||||
int m_iClipSize;
|
||||
|
||||
int m_voicePitch;
|
||||
|
||||
int m_iSentence;
|
||||
|
||||
float m_flCheckAttackTime;
|
||||
|
||||
int m_iAmmoType;
|
||||
|
||||
int m_iWeapons;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,400 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
#include "cbase.h"
|
||||
#include "ai_default.h"
|
||||
#include "ai_task.h"
|
||||
#include "ai_schedule.h"
|
||||
#include "ai_node.h"
|
||||
#include "ai_hull.h"
|
||||
#include "ai_hint.h"
|
||||
#include "ai_memory.h"
|
||||
#include "ai_route.h"
|
||||
#include "ai_motor.h"
|
||||
#include "ai_senses.h"
|
||||
#include "soundent.h"
|
||||
#include "game.h"
|
||||
#include "npcevent.h"
|
||||
#include "entitylist.h"
|
||||
#include "activitylist.h"
|
||||
#include "animation.h"
|
||||
#include "basecombatweapon.h"
|
||||
#include "IEffects.h"
|
||||
#include "vstdlib/random.h"
|
||||
#include "engine/IEngineSound.h"
|
||||
#include "ammodef.h"
|
||||
#include "te.h"
|
||||
#include "hl1_npc_hornet.h"
|
||||
|
||||
int iHornetTrail;
|
||||
int iHornetPuff;
|
||||
|
||||
LINK_ENTITY_TO_CLASS( hornet, CNPC_Hornet );
|
||||
|
||||
extern ConVar sk_npc_dmg_hornet;
|
||||
extern ConVar sk_plr_dmg_hornet;
|
||||
|
||||
BEGIN_DATADESC( CNPC_Hornet )
|
||||
DEFINE_FIELD( m_flStopAttack, FIELD_TIME ),
|
||||
DEFINE_FIELD( m_iHornetType, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( m_flFlySpeed, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( m_flDamage, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( m_vecEnemyLKP, FIELD_POSITION_VECTOR ),
|
||||
|
||||
|
||||
DEFINE_ENTITYFUNC( DieTouch ),
|
||||
DEFINE_THINKFUNC( StartDart ),
|
||||
DEFINE_THINKFUNC( StartTrack ),
|
||||
DEFINE_ENTITYFUNC( DartTouch ),
|
||||
DEFINE_ENTITYFUNC( TrackTouch ),
|
||||
DEFINE_THINKFUNC( TrackTarget ),
|
||||
END_DATADESC()
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
void CNPC_Hornet::Spawn( void )
|
||||
{
|
||||
Precache();
|
||||
|
||||
SetMoveType( MOVETYPE_FLY );
|
||||
SetSolid( SOLID_BBOX );
|
||||
m_takedamage = DAMAGE_YES;
|
||||
AddFlag( FL_NPC );
|
||||
m_iHealth = 1;// weak!
|
||||
m_bloodColor = DONT_BLEED;
|
||||
|
||||
if ( g_pGameRules->IsMultiplayer() )
|
||||
{
|
||||
// hornets don't live as long in multiplayer
|
||||
m_flStopAttack = gpGlobals->curtime + 3.5;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_flStopAttack = gpGlobals->curtime + 5.0;
|
||||
}
|
||||
|
||||
m_flFieldOfView = 0.9; // +- 25 degrees
|
||||
|
||||
if ( random->RandomInt ( 1, 5 ) <= 2 )
|
||||
{
|
||||
m_iHornetType = HORNET_TYPE_RED;
|
||||
m_flFlySpeed = HORNET_RED_SPEED;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_iHornetType = HORNET_TYPE_ORANGE;
|
||||
m_flFlySpeed = HORNET_ORANGE_SPEED;
|
||||
}
|
||||
|
||||
SetModel( "models/hornet.mdl" );
|
||||
UTIL_SetSize( this, Vector( -4, -4, -4 ), Vector( 4, 4, 4 ) );
|
||||
|
||||
SetTouch( &CNPC_Hornet::DieTouch );
|
||||
SetThink( &CNPC_Hornet::StartTrack );
|
||||
|
||||
if ( GetOwnerEntity() && (GetOwnerEntity()->GetFlags() & FL_CLIENT) )
|
||||
{
|
||||
m_flDamage = sk_plr_dmg_hornet.GetFloat();
|
||||
}
|
||||
else
|
||||
{
|
||||
// no real owner, or owner isn't a client.
|
||||
m_flDamage = sk_npc_dmg_hornet.GetFloat();
|
||||
}
|
||||
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
ResetSequenceInfo();
|
||||
|
||||
m_vecEnemyLKP = vec3_origin;
|
||||
}
|
||||
|
||||
|
||||
void CNPC_Hornet::Precache()
|
||||
{
|
||||
PrecacheModel("models/hornet.mdl");
|
||||
|
||||
iHornetPuff = PrecacheModel( "sprites/muz1.vmt" );
|
||||
iHornetTrail = PrecacheModel("sprites/laserbeam.vmt");
|
||||
|
||||
PrecacheScriptSound( "Hornet.Die" );
|
||||
PrecacheScriptSound( "Hornet.Buzz" );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// hornets will never get mad at each other, no matter who the owner is.
|
||||
//=========================================================
|
||||
Disposition_t CNPC_Hornet::IRelationType( CBaseEntity *pTarget )
|
||||
{
|
||||
if ( pTarget->GetModelIndex() == GetModelIndex() )
|
||||
{
|
||||
return D_NU;
|
||||
}
|
||||
|
||||
return BaseClass::IRelationType( pTarget );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// ID's Hornet as their owner
|
||||
//=========================================================
|
||||
Class_T CNPC_Hornet::Classify ( void )
|
||||
{
|
||||
if ( GetOwnerEntity() && (GetOwnerEntity()->GetFlags() & FL_CLIENT) )
|
||||
{
|
||||
return CLASS_PLAYER_BIOWEAPON;
|
||||
}
|
||||
|
||||
return CLASS_ALIEN_BIOWEAPON;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// StartDart - starts a hornet out just flying straight.
|
||||
//=========================================================
|
||||
void CNPC_Hornet::StartDart ( void )
|
||||
{
|
||||
IgniteTrail();
|
||||
|
||||
SetTouch( &CNPC_Hornet::DartTouch );
|
||||
|
||||
SetThink( &CBaseEntity::SUB_Remove );
|
||||
SetNextThink( gpGlobals->curtime + 4 );
|
||||
}
|
||||
|
||||
|
||||
void CNPC_Hornet::DieTouch ( CBaseEntity *pOther )
|
||||
{
|
||||
if ( !pOther || !pOther->IsSolid() || pOther->IsSolidFlagSet(FSOLID_VOLUME_CONTENTS) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CPASAttenuationFilter filter( this );
|
||||
EmitSound( filter, entindex(), "Hornet.Die" );
|
||||
|
||||
CTakeDamageInfo info( this, GetOwnerEntity(), m_flDamage, DMG_BULLET );
|
||||
CalculateBulletDamageForce( &info, GetAmmoDef()->Index("Hornet"), GetAbsVelocity(), GetAbsOrigin() );
|
||||
pOther->TakeDamage( info );
|
||||
|
||||
m_takedamage = DAMAGE_NO;
|
||||
|
||||
AddEffects( EF_NODRAW );
|
||||
|
||||
AddSolidFlags( FSOLID_NOT_SOLID );// intangible
|
||||
|
||||
UTIL_Remove( this );
|
||||
SetTouch( NULL );
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// StartTrack - starts a hornet out tracking its target
|
||||
//=========================================================
|
||||
void CNPC_Hornet:: StartTrack ( void )
|
||||
{
|
||||
IgniteTrail();
|
||||
|
||||
SetTouch( &CNPC_Hornet::TrackTouch );
|
||||
SetThink( &CNPC_Hornet::TrackTarget );
|
||||
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
}
|
||||
|
||||
void TE_BeamFollow( IRecipientFilter& filter, float delay,
|
||||
int iEntIndex, int modelIndex, int haloIndex, float life, float width, float endWidth,
|
||||
float fadeLength,float r, float g, float b, float a );
|
||||
|
||||
void CNPC_Hornet::IgniteTrail( void )
|
||||
{
|
||||
Vector vColor;
|
||||
|
||||
if ( m_iHornetType == HORNET_TYPE_RED )
|
||||
vColor = Vector ( 179, 39, 14 );
|
||||
else
|
||||
vColor = Vector ( 255, 128, 0 );
|
||||
|
||||
CBroadcastRecipientFilter filter;
|
||||
TE_BeamFollow( filter, 0.0,
|
||||
entindex(),
|
||||
iHornetTrail,
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
0.5,
|
||||
0.5,
|
||||
vColor.x,
|
||||
vColor.y,
|
||||
vColor.z,
|
||||
128 );
|
||||
}
|
||||
|
||||
|
||||
unsigned int CNPC_Hornet::PhysicsSolidMaskForEntity( void ) const
|
||||
{
|
||||
unsigned int iMask = BaseClass::PhysicsSolidMaskForEntity();
|
||||
|
||||
iMask &= ~CONTENTS_MONSTERCLIP;
|
||||
|
||||
return iMask;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Tracking Hornet hit something
|
||||
//=========================================================
|
||||
void CNPC_Hornet::TrackTouch ( CBaseEntity *pOther )
|
||||
{
|
||||
if ( !pOther->IsSolid() || pOther->IsSolidFlagSet(FSOLID_VOLUME_CONTENTS) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( pOther == GetOwnerEntity() || pOther->GetModelIndex() == GetModelIndex() )
|
||||
{// bumped into the guy that shot it.
|
||||
//SetSolid( SOLID_NOT );
|
||||
return;
|
||||
}
|
||||
|
||||
int nRelationship = IRelationType( pOther );
|
||||
if ( (nRelationship == D_FR || nRelationship == D_NU || nRelationship == D_LI) )
|
||||
{
|
||||
// hit something we don't want to hurt, so turn around.
|
||||
Vector vecVel = GetAbsVelocity();
|
||||
|
||||
VectorNormalize( vecVel );
|
||||
|
||||
vecVel.x *= -1;
|
||||
vecVel.y *= -1;
|
||||
|
||||
SetAbsOrigin( GetAbsOrigin() + vecVel * 4 ); // bounce the hornet off a bit.
|
||||
SetAbsVelocity( vecVel * m_flFlySpeed );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
DieTouch( pOther );
|
||||
}
|
||||
|
||||
void CNPC_Hornet::DartTouch( CBaseEntity *pOther )
|
||||
{
|
||||
DieTouch( pOther );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Hornet is flying, gently tracking target
|
||||
//=========================================================
|
||||
void CNPC_Hornet::TrackTarget ( void )
|
||||
{
|
||||
Vector vecFlightDir;
|
||||
Vector vecDirToEnemy;
|
||||
float flDelta;
|
||||
|
||||
StudioFrameAdvance( );
|
||||
|
||||
if (gpGlobals->curtime > m_flStopAttack)
|
||||
{
|
||||
SetTouch( NULL );
|
||||
SetThink( &CBaseEntity::SUB_Remove );
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
return;
|
||||
}
|
||||
|
||||
// UNDONE: The player pointer should come back after returning from another level
|
||||
if ( GetEnemy() == NULL )
|
||||
{// enemy is dead.
|
||||
GetSenses()->Look( 1024 );
|
||||
SetEnemy( BestEnemy() );
|
||||
}
|
||||
|
||||
if ( GetEnemy() != NULL && FVisible( GetEnemy() ))
|
||||
{
|
||||
m_vecEnemyLKP = GetEnemy()->BodyTarget( GetAbsOrigin() );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_vecEnemyLKP = m_vecEnemyLKP + GetAbsVelocity() * m_flFlySpeed * 0.1;
|
||||
}
|
||||
|
||||
vecDirToEnemy = m_vecEnemyLKP - GetAbsOrigin();
|
||||
VectorNormalize( vecDirToEnemy );
|
||||
|
||||
if ( GetAbsVelocity().Length() < 0.1 )
|
||||
vecFlightDir = vecDirToEnemy;
|
||||
else
|
||||
{
|
||||
vecFlightDir = GetAbsVelocity();
|
||||
VectorNormalize( vecFlightDir );
|
||||
}
|
||||
|
||||
SetAbsVelocity( vecFlightDir + vecDirToEnemy );
|
||||
|
||||
// measure how far the turn is, the wider the turn, the slow we'll go this time.
|
||||
flDelta = DotProduct ( vecFlightDir, vecDirToEnemy );
|
||||
|
||||
if ( flDelta < 0.5 )
|
||||
{// hafta turn wide again. play sound
|
||||
CPASAttenuationFilter filter( this );
|
||||
EmitSound( filter, entindex(), "Hornet.Buzz" );
|
||||
}
|
||||
|
||||
if ( flDelta <= 0 && m_iHornetType == HORNET_TYPE_RED )
|
||||
{// no flying backwards, but we don't want to invert this, cause we'd go fast when we have to turn REAL far.
|
||||
flDelta = 0.25;
|
||||
}
|
||||
|
||||
Vector vecVel = vecFlightDir + vecDirToEnemy;
|
||||
VectorNormalize( vecVel );
|
||||
|
||||
if ( GetOwnerEntity() && (GetOwnerEntity()->GetFlags() & FL_NPC) )
|
||||
{
|
||||
// random pattern only applies to hornets fired by monsters, not players.
|
||||
|
||||
vecVel.x += random->RandomFloat ( -0.10, 0.10 );// scramble the flight dir a bit.
|
||||
vecVel.y += random->RandomFloat ( -0.10, 0.10 );
|
||||
vecVel.z += random->RandomFloat ( -0.10, 0.10 );
|
||||
}
|
||||
|
||||
switch ( m_iHornetType )
|
||||
{
|
||||
case HORNET_TYPE_RED:
|
||||
SetAbsVelocity( vecVel * ( m_flFlySpeed * flDelta ) );// scale the dir by the ( speed * width of turn )
|
||||
SetNextThink( gpGlobals->curtime + random->RandomFloat( 0.1, 0.3 ) );
|
||||
break;
|
||||
default:
|
||||
Assert( false ); //fall through if release
|
||||
case HORNET_TYPE_ORANGE:
|
||||
SetAbsVelocity( vecVel * m_flFlySpeed );// do not have to slow down to turn.
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );// fixed think time
|
||||
break;
|
||||
}
|
||||
|
||||
QAngle angNewAngles;
|
||||
VectorAngles( GetAbsVelocity(), angNewAngles );
|
||||
SetAbsAngles( angNewAngles );
|
||||
|
||||
SetSolid( SOLID_BBOX );
|
||||
|
||||
// if hornet is close to the enemy, jet in a straight line for a half second.
|
||||
// (only in the single player game)
|
||||
if ( GetEnemy() != NULL && !g_pGameRules->IsMultiplayer() )
|
||||
{
|
||||
if ( flDelta >= 0.4 && ( GetAbsOrigin() - m_vecEnemyLKP ).Length() <= 300 )
|
||||
{
|
||||
CPVSFilter filter( GetAbsOrigin() );
|
||||
te->Sprite( filter, 0.0,
|
||||
&GetAbsOrigin(), // pos
|
||||
iHornetPuff, // model
|
||||
0.2, //size
|
||||
128 // brightness
|
||||
);
|
||||
|
||||
CPASAttenuationFilter filter2( this );
|
||||
EmitSound( filter2, entindex(), "Hornet.Buzz" );
|
||||
SetAbsVelocity( GetAbsVelocity() * 2 );
|
||||
SetNextThink( gpGlobals->curtime + 1.0f );
|
||||
// don't attack again
|
||||
m_flStopAttack = gpGlobals->curtime;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
#ifndef NPC_HORNET_H
|
||||
#define NPC_HORNET_H
|
||||
|
||||
#include "hl1_ai_basenpc.h"
|
||||
|
||||
//=========================================================
|
||||
// Hornets
|
||||
//=========================================================
|
||||
|
||||
//=========================================================
|
||||
// Hornet Defines
|
||||
//=========================================================
|
||||
#define HORNET_TYPE_RED 0
|
||||
#define HORNET_TYPE_ORANGE 1
|
||||
#define HORNET_RED_SPEED (float)600
|
||||
#define HORNET_ORANGE_SPEED (float)800
|
||||
|
||||
extern int iHornetPuff;
|
||||
|
||||
//=========================================================
|
||||
// Hornet - this is the projectile that the Alien Grunt fires.
|
||||
//=========================================================
|
||||
class CNPC_Hornet : public CHL1BaseNPC
|
||||
{
|
||||
DECLARE_CLASS( CNPC_Hornet, CHL1BaseNPC );
|
||||
public:
|
||||
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
Class_T Classify ( void );
|
||||
Disposition_t IRelationType(CBaseEntity *pTarget);
|
||||
|
||||
void DieTouch ( CBaseEntity *pOther );
|
||||
void DartTouch( CBaseEntity *pOther );
|
||||
void TrackTouch ( CBaseEntity *pOther );
|
||||
void TrackTarget ( void );
|
||||
void StartDart ( void );
|
||||
void IgniteTrail( void );
|
||||
void StartTrack(void);
|
||||
|
||||
|
||||
virtual unsigned int PhysicsSolidMaskForEntity( void ) const;
|
||||
virtual bool ShouldGib( const CTakeDamageInfo &info ) { return false; }
|
||||
|
||||
/* virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
|
||||
void EXPORT StartTrack ( void );
|
||||
|
||||
void EXPORT TrackTarget ( void );
|
||||
void EXPORT TrackTouch ( CBaseEntity *pOther );
|
||||
void EXPORT DartTouch( CBaseEntity *pOther );
|
||||
|
||||
|
||||
int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType );*/
|
||||
|
||||
float m_flStopAttack;
|
||||
int m_iHornetType;
|
||||
float m_flFlySpeed;
|
||||
int m_flDamage;
|
||||
|
||||
DECLARE_DATADESC();
|
||||
|
||||
Vector m_vecEnemyLKP;
|
||||
};
|
||||
|
||||
#endif //NPC_HORNET_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,73 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
#ifndef NPC_HOUNDEYE_H
|
||||
#define NPC_HOUNDEYE_H
|
||||
#pragma once
|
||||
|
||||
#include "hl1_ai_basenpc.h"
|
||||
#define HOUNDEYE_MAX_ATTACK_RADIUS 384
|
||||
|
||||
class CNPC_Houndeye : public CHL1BaseNPC
|
||||
{
|
||||
DECLARE_CLASS( CNPC_Houndeye, CHL1BaseNPC );
|
||||
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
|
||||
void Event_Killed( const CTakeDamageInfo &info );
|
||||
|
||||
void WarmUpSound ( void );
|
||||
void AlertSound( void );
|
||||
void DeathSound( const CTakeDamageInfo &info );
|
||||
void WarnSound( void );
|
||||
void PainSound( const CTakeDamageInfo &info );
|
||||
void IdleSound( void );
|
||||
|
||||
float MaxYawSpeed ( void );
|
||||
|
||||
Class_T Classify ( void );
|
||||
|
||||
void HandleAnimEvent( animevent_t *pEvent );
|
||||
|
||||
void SonicAttack ( void );
|
||||
|
||||
Vector WriteBeamColor( void );
|
||||
bool ShouldGoToIdleState( void );
|
||||
bool FValidateHintType ( CAI_Hint *pHint );
|
||||
|
||||
void SetActivity ( Activity NewActivity );
|
||||
|
||||
void StartTask( const Task_t *pTask );
|
||||
void RunTask ( const Task_t *pTask );
|
||||
void PrescheduleThink ( void );
|
||||
|
||||
int TranslateSchedule( int scheduleType );
|
||||
int SelectSchedule( void );
|
||||
|
||||
float FLSoundVolume( CSound *pSound );
|
||||
int RangeAttack1Conditions ( float flDot, float flDist );
|
||||
|
||||
void StartNPC ( void );
|
||||
|
||||
virtual float InnateRange1MinRange( void ) { return 0.0f; }
|
||||
virtual float InnateRange1MaxRange( void ) { return HOUNDEYE_MAX_ATTACK_RADIUS; }
|
||||
|
||||
DEFINE_CUSTOM_AI;
|
||||
DECLARE_DATADESC();
|
||||
|
||||
private:
|
||||
int SquadRecruit( int searchRadius, int maxMembers );
|
||||
|
||||
int m_iSpriteTexture;
|
||||
bool m_fAsleep;// some houndeyes sleep in idle mode if this is set, the houndeye is lying down
|
||||
bool m_fDontBlink;// don't try to open/close eye if this bit is set!
|
||||
Vector m_vecPackCenter; // the center of the pack. The leader maintains this by averaging the origins of all pack members.
|
||||
};
|
||||
|
||||
#endif // NPC_HOUNDEYE_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,97 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
#ifndef NPC_ICHTHYOSAUR_H
|
||||
#define NPC_ICHTHYOSAUR_H
|
||||
|
||||
|
||||
#include "hl1_ai_basenpc.h"
|
||||
|
||||
#define SEARCH_RETRY 16
|
||||
|
||||
#define ICHTHYOSAUR_SPEED 150
|
||||
|
||||
#define EYE_MAD 0
|
||||
#define EYE_BASE 1
|
||||
#define EYE_CLOSED 2
|
||||
#define EYE_BACK 3
|
||||
#define EYE_LOOK 4
|
||||
|
||||
|
||||
//
|
||||
// CNPC_Ichthyosaur
|
||||
//
|
||||
|
||||
class CNPC_Ichthyosaur : public CHL1BaseNPC
|
||||
{
|
||||
DECLARE_CLASS( CNPC_Ichthyosaur, CHL1BaseNPC );
|
||||
public:
|
||||
|
||||
void Precache( void );
|
||||
void Spawn( void );
|
||||
Class_T Classify ( void );
|
||||
void NPCThink ( void );
|
||||
void Swim ( void );
|
||||
void StartTask(const Task_t *pTask);
|
||||
void RunTask( const Task_t *pTask );
|
||||
int RangeAttack1Conditions( float flDot, float flDist );
|
||||
int MeleeAttack1Conditions ( float flDot, float flDist );
|
||||
void BiteTouch( CBaseEntity *pOther );
|
||||
void HandleAnimEvent( animevent_t *pEvent );
|
||||
int TranslateSchedule( int scheduleType );
|
||||
int SelectSchedule();
|
||||
virtual bool FVisible ( CBaseEntity *pEntity, int traceMask = MASK_BLOCKLOS, CBaseEntity **ppBlocker = NULL );
|
||||
|
||||
Vector DoProbe( const Vector &Probe );
|
||||
bool ProbeZ( const Vector &position, const Vector &probe, float *pFraction);
|
||||
|
||||
float GetGroundSpeed ( void );
|
||||
|
||||
bool OverrideMove( float flInterval );
|
||||
void MoveExecute_Alive(float flInterval);
|
||||
|
||||
void InputStartCombat( inputdata_t &input );
|
||||
void InputEndCombat( inputdata_t &input );
|
||||
|
||||
virtual void IdleSound( void );
|
||||
virtual void AlertSound( void );
|
||||
virtual void DeathSound( const CTakeDamageInfo &info );
|
||||
virtual void PainSound( const CTakeDamageInfo &info );
|
||||
|
||||
void AttackSound( void );
|
||||
void BiteSound( void );
|
||||
|
||||
virtual void GatherEnemyConditions( CBaseEntity *pEnemy );
|
||||
|
||||
DEFINE_CUSTOM_AI;
|
||||
DECLARE_DATADESC();
|
||||
|
||||
private:
|
||||
Vector m_SaveVelocity;
|
||||
float m_idealDist;
|
||||
|
||||
float m_flBlink;
|
||||
|
||||
float m_flEnemyTouched;
|
||||
bool m_bOnAttack;
|
||||
|
||||
float m_flMaxSpeed;
|
||||
float m_flMinSpeed;
|
||||
float m_flMaxDist;
|
||||
|
||||
float m_flNextAlert;
|
||||
float m_flLastAttackSound;
|
||||
|
||||
//Save the info from that run
|
||||
Vector m_vecLastMoveTarget;
|
||||
bool m_bHasMoveTarget;
|
||||
|
||||
float m_flFlyingSpeed;
|
||||
};
|
||||
|
||||
|
||||
#endif //NPC_ICHTHYOSAUR_H
|
||||
@@ -0,0 +1,724 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
#include "cbase.h"
|
||||
#include "ai_default.h"
|
||||
#include "ai_task.h"
|
||||
#include "ai_schedule.h"
|
||||
#include "ai_node.h"
|
||||
#include "ai_hull.h"
|
||||
#include "ai_hint.h"
|
||||
#include "ai_memory.h"
|
||||
#include "ai_route.h"
|
||||
#include "ai_motor.h"
|
||||
#include "soundent.h"
|
||||
#include "game.h"
|
||||
#include "npcevent.h"
|
||||
#include "entitylist.h"
|
||||
#include "activitylist.h"
|
||||
#include "animation.h"
|
||||
#include "basecombatweapon.h"
|
||||
#include "IEffects.h"
|
||||
#include "vstdlib/random.h"
|
||||
#include "engine/IEngineSound.h"
|
||||
#include "ammodef.h"
|
||||
#include "hl1_ai_basenpc.h"
|
||||
#include "ai_senses.h"
|
||||
|
||||
// Animation events
|
||||
#define LEECH_AE_ATTACK 1
|
||||
#define LEECH_AE_FLOP 2
|
||||
|
||||
//#define DEBUG_BEAMS 0
|
||||
|
||||
ConVar sk_leech_health( "sk_leech_health", "2" );
|
||||
ConVar sk_leech_dmg_bite( "sk_leech_dmg_bite", "2" );
|
||||
|
||||
// Movement constants
|
||||
|
||||
#define LEECH_ACCELERATE 10
|
||||
#define LEECH_CHECK_DIST 45
|
||||
#define LEECH_SWIM_SPEED 50
|
||||
#define LEECH_SWIM_ACCEL 80
|
||||
#define LEECH_SWIM_DECEL 10
|
||||
#define LEECH_TURN_RATE 70
|
||||
#define LEECH_SIZEX 10
|
||||
#define LEECH_FRAMETIME 0.1
|
||||
|
||||
class CNPC_Leech : public CHL1BaseNPC
|
||||
{
|
||||
DECLARE_CLASS( CNPC_Leech, CHL1BaseNPC );
|
||||
public:
|
||||
|
||||
DECLARE_DATADESC();
|
||||
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
|
||||
static const char *pAlertSounds[];
|
||||
|
||||
void SwimThink( void );
|
||||
void DeadThink( void );
|
||||
|
||||
void SwitchLeechState( void );
|
||||
float ObstacleDistance( CBaseEntity *pTarget );
|
||||
void UpdateMotion( void );
|
||||
|
||||
void RecalculateWaterlevel( void );
|
||||
void Touch( CBaseEntity *pOther );
|
||||
|
||||
Disposition_t IRelationType(CBaseEntity *pTarget);
|
||||
|
||||
void HandleAnimEvent( animevent_t *pEvent );
|
||||
|
||||
void AttackSound( void );
|
||||
void AlertSound( void );
|
||||
|
||||
void Activate( void );
|
||||
|
||||
Class_T Classify( void ) { return CLASS_INSECT; };
|
||||
|
||||
void Event_Killed( const CTakeDamageInfo &info );
|
||||
|
||||
|
||||
bool ShouldGib( const CTakeDamageInfo &info );
|
||||
|
||||
|
||||
/* // Base entity functions
|
||||
void Killed( entvars_t *pevAttacker, int iGib );
|
||||
int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType );
|
||||
*/
|
||||
|
||||
private:
|
||||
// UNDONE: Remove unused boid vars, do group behavior
|
||||
float m_flTurning;// is this boid turning?
|
||||
bool m_fPathBlocked;// TRUE if there is an obstacle ahead
|
||||
float m_flAccelerate;
|
||||
float m_obstacle;
|
||||
float m_top;
|
||||
float m_bottom;
|
||||
float m_height;
|
||||
float m_waterTime;
|
||||
float m_sideTime; // Timer to randomly check clearance on sides
|
||||
float m_zTime;
|
||||
float m_stateTime;
|
||||
float m_attackSoundTime;
|
||||
Vector m_oldOrigin;
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( monster_leech, CNPC_Leech );
|
||||
|
||||
BEGIN_DATADESC( CNPC_Leech )
|
||||
DEFINE_FIELD( m_flTurning, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( m_fPathBlocked, FIELD_BOOLEAN ),
|
||||
DEFINE_FIELD( m_flAccelerate, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( m_obstacle, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( m_top, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( m_bottom, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( m_height, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( m_waterTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( m_sideTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( m_zTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( m_stateTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( m_attackSoundTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( m_oldOrigin, FIELD_VECTOR ),
|
||||
|
||||
DEFINE_THINKFUNC( SwimThink ),
|
||||
DEFINE_THINKFUNC( DeadThink ),
|
||||
END_DATADESC()
|
||||
|
||||
|
||||
bool CNPC_Leech::ShouldGib( const CTakeDamageInfo &info )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void CNPC_Leech::Spawn( void )
|
||||
{
|
||||
Precache();
|
||||
SetModel( "models/leech.mdl" );
|
||||
|
||||
SetHullType(HULL_TINY_CENTERED);
|
||||
SetHullSizeNormal();
|
||||
|
||||
UTIL_SetSize( this, Vector(-1,-1,0), Vector(1,1,2));
|
||||
|
||||
Vector vecSurroundingMins(-8,-8,0);
|
||||
Vector vecSurroundingMaxs(8,8,2);
|
||||
CollisionProp()->SetSurroundingBoundsType( USE_SPECIFIED_BOUNDS, &vecSurroundingMins, &vecSurroundingMaxs );
|
||||
|
||||
// Don't push the minz down too much or the water check will fail because this entity is really point-sized
|
||||
SetSolid( SOLID_BBOX );
|
||||
AddSolidFlags( FSOLID_NOT_STANDABLE );
|
||||
SetMoveType( MOVETYPE_FLY );
|
||||
AddFlag( FL_SWIM );
|
||||
m_iHealth = sk_leech_health.GetInt();
|
||||
|
||||
m_flFieldOfView = -0.5; // 180 degree FOV
|
||||
SetDistLook( 750 );
|
||||
NPCInit();
|
||||
SetThink( &CNPC_Leech::SwimThink );
|
||||
SetUse( NULL );
|
||||
SetTouch( NULL );
|
||||
SetViewOffset( vec3_origin );
|
||||
|
||||
m_flTurning = 0;
|
||||
m_fPathBlocked = FALSE;
|
||||
SetActivity( ACT_SWIM );
|
||||
SetState( NPC_STATE_IDLE );
|
||||
m_stateTime = gpGlobals->curtime + random->RandomFloat( 1, 5 );
|
||||
|
||||
SetRenderColor( 255, 255, 255, 255 );
|
||||
|
||||
m_bloodColor = DONT_BLEED;
|
||||
SetCollisionGroup( COLLISION_GROUP_DEBRIS );
|
||||
}
|
||||
|
||||
void CNPC_Leech::Activate( void )
|
||||
{
|
||||
RecalculateWaterlevel();
|
||||
|
||||
BaseClass::Activate();
|
||||
}
|
||||
|
||||
void CNPC_Leech::DeadThink( void )
|
||||
{
|
||||
if ( IsSequenceFinished() )
|
||||
{
|
||||
if ( GetActivity() == ACT_DIEFORWARD )
|
||||
{
|
||||
SetThink( NULL );
|
||||
StopAnimation();
|
||||
return;
|
||||
}
|
||||
else if ( GetFlags() & FL_ONGROUND )
|
||||
{
|
||||
AddSolidFlags( FSOLID_NOT_SOLID );
|
||||
SetActivity( ACT_DIEFORWARD );
|
||||
}
|
||||
}
|
||||
StudioFrameAdvance();
|
||||
SetNextThink( gpGlobals->curtime + 0.1 );
|
||||
|
||||
// Apply damage velocity, but keep out of the walls
|
||||
if ( GetAbsVelocity().x != 0 || GetAbsVelocity().y != 0 )
|
||||
{
|
||||
trace_t tr;
|
||||
|
||||
// Look 0.5 seconds ahead
|
||||
UTIL_TraceLine( GetLocalOrigin(), GetLocalOrigin() + GetAbsVelocity() * 0.5, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr);
|
||||
if (tr.fraction != 1.0)
|
||||
{
|
||||
Vector vVelocity = GetAbsVelocity();
|
||||
|
||||
vVelocity.x = 0;
|
||||
vVelocity.y = 0;
|
||||
|
||||
SetAbsVelocity( vVelocity );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Disposition_t CNPC_Leech::IRelationType( CBaseEntity *pTarget )
|
||||
{
|
||||
if ( pTarget->IsPlayer() )
|
||||
return D_HT;
|
||||
|
||||
return BaseClass::IRelationType( pTarget );
|
||||
}
|
||||
|
||||
void CNPC_Leech::Touch( CBaseEntity *pOther )
|
||||
{
|
||||
if ( !pOther->IsPlayer() )
|
||||
return;
|
||||
|
||||
if ( pOther == GetTouchTrace().m_pEnt )
|
||||
{
|
||||
if ( pOther->GetAbsVelocity() == vec3_origin )
|
||||
return;
|
||||
|
||||
SetBaseVelocity( pOther->GetAbsVelocity() );
|
||||
AddFlag( FL_BASEVELOCITY );
|
||||
}
|
||||
}
|
||||
|
||||
void CNPC_Leech::HandleAnimEvent( animevent_t *pEvent )
|
||||
{
|
||||
CBaseEntity *pEnemy = GetEnemy();
|
||||
|
||||
switch( pEvent->event )
|
||||
{
|
||||
case LEECH_AE_FLOP:
|
||||
// Play flop sound
|
||||
break;
|
||||
|
||||
case LEECH_AE_ATTACK:
|
||||
AttackSound();
|
||||
|
||||
if ( pEnemy != NULL )
|
||||
{
|
||||
Vector dir, face;
|
||||
|
||||
AngleVectors( GetAbsAngles(), &face );
|
||||
|
||||
face.z = 0;
|
||||
dir = (pEnemy->GetLocalOrigin() - GetLocalOrigin() );
|
||||
dir.z = 0;
|
||||
|
||||
VectorNormalize( dir );
|
||||
VectorNormalize( face );
|
||||
|
||||
if ( DotProduct(dir, face) > 0.9 ) // Only take damage if the leech is facing the prey
|
||||
{
|
||||
CTakeDamageInfo info( this, this, sk_leech_dmg_bite.GetInt(), DMG_SLASH );
|
||||
CalculateMeleeDamageForce( &info, dir, pEnemy->GetAbsOrigin() );
|
||||
pEnemy->TakeDamage( info );
|
||||
}
|
||||
}
|
||||
m_stateTime -= 2;
|
||||
break;
|
||||
|
||||
|
||||
|
||||
default:
|
||||
BaseClass::HandleAnimEvent( pEvent );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CNPC_Leech::Precache( void )
|
||||
{
|
||||
PrecacheModel("models/leech.mdl");
|
||||
|
||||
PrecacheScriptSound( "Leech.Attack" );
|
||||
PrecacheScriptSound( "Leech.Alert" );
|
||||
}
|
||||
|
||||
|
||||
void CNPC_Leech::AttackSound( void )
|
||||
{
|
||||
if ( gpGlobals->curtime > m_attackSoundTime )
|
||||
{
|
||||
CPASAttenuationFilter filter( this );
|
||||
|
||||
EmitSound(filter, entindex(), "Leech.Attack" );
|
||||
m_attackSoundTime = gpGlobals->curtime + 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CNPC_Leech::AlertSound( void )
|
||||
{
|
||||
CPASAttenuationFilter filter( this );
|
||||
EmitSound(filter, entindex(), "Leech.Alert" );
|
||||
}
|
||||
|
||||
void CNPC_Leech::SwitchLeechState( void )
|
||||
{
|
||||
m_stateTime = gpGlobals->curtime + random->RandomFloat( 3, 6 );
|
||||
if ( m_NPCState == NPC_STATE_COMBAT )
|
||||
{
|
||||
SetEnemy ( NULL );
|
||||
SetState( NPC_STATE_IDLE );
|
||||
// We may be up against the player, so redo the side checks
|
||||
m_sideTime = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
GetSenses()->Look( GetSenses()->GetDistLook() );
|
||||
CBaseEntity *pEnemy = BestEnemy();
|
||||
if ( pEnemy && pEnemy->GetWaterLevel() != 0 )
|
||||
{
|
||||
SetEnemy ( pEnemy );
|
||||
SetState( NPC_STATE_COMBAT );
|
||||
m_stateTime = gpGlobals->curtime + random->RandomFloat( 18, 25 );
|
||||
AlertSound();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CNPC_Leech::RecalculateWaterlevel( void )
|
||||
{
|
||||
// Calculate boundaries
|
||||
Vector vecTest = GetLocalOrigin() - Vector(0,0,400);
|
||||
|
||||
trace_t tr;
|
||||
|
||||
UTIL_TraceLine( GetLocalOrigin(), vecTest, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr);
|
||||
|
||||
if ( tr.fraction != 1.0 )
|
||||
m_bottom = tr.endpos.z + 1;
|
||||
else
|
||||
m_bottom = vecTest.z;
|
||||
|
||||
m_top = UTIL_WaterLevel( GetLocalOrigin(), GetLocalOrigin().z, GetLocalOrigin().z + 400 ) - 1;
|
||||
|
||||
#if DEBUG_BEAMS
|
||||
NDebugOverlay::Line( GetLocalOrigin(), GetLocalOrigin() + Vector( 0, 0, m_bottom ), 0, 255, 0, false, 0.1f );
|
||||
NDebugOverlay::Line( GetLocalOrigin(), GetLocalOrigin() + Vector( 0, 0, m_top ), 0, 255, 255, false, 0.1f );
|
||||
#endif
|
||||
|
||||
// Chop off 20% of the outside range
|
||||
float newBottom = m_bottom * 0.8 + m_top * 0.2;
|
||||
m_top = m_bottom * 0.2 + m_top * 0.8;
|
||||
m_bottom = newBottom;
|
||||
m_height = random->RandomFloat( m_bottom, m_top );
|
||||
m_waterTime = gpGlobals->curtime + random->RandomFloat( 5, 7 );
|
||||
}
|
||||
|
||||
void CNPC_Leech::SwimThink( void )
|
||||
{
|
||||
trace_t tr;
|
||||
float flLeftSide;
|
||||
float flRightSide;
|
||||
float targetSpeed;
|
||||
float targetYaw = 0;
|
||||
CBaseEntity *pTarget;
|
||||
|
||||
/*if ( !UTIL_FindClientInPVS( edict() ) )
|
||||
{
|
||||
m_flNextThink = gpGlobals->curtime + random->RandomFloat( 1.0f, 1.5f );
|
||||
SetAbsVelocity( vec3_origin );
|
||||
return;
|
||||
}
|
||||
else*/
|
||||
SetNextThink( gpGlobals->curtime + 0.1 );
|
||||
|
||||
targetSpeed = LEECH_SWIM_SPEED;
|
||||
|
||||
if ( m_waterTime < gpGlobals->curtime )
|
||||
RecalculateWaterlevel();
|
||||
|
||||
if ( m_stateTime < gpGlobals->curtime )
|
||||
SwitchLeechState();
|
||||
|
||||
ClearCondition( COND_CAN_MELEE_ATTACK1 );
|
||||
|
||||
switch( m_NPCState )
|
||||
{
|
||||
case NPC_STATE_COMBAT:
|
||||
pTarget = GetEnemy();
|
||||
if ( !pTarget )
|
||||
SwitchLeechState();
|
||||
else
|
||||
{
|
||||
// Chase the enemy's eyes
|
||||
m_height = pTarget->GetLocalOrigin().z + pTarget->GetViewOffset().z - 5;
|
||||
// Clip to viable water area
|
||||
if ( m_height < m_bottom )
|
||||
m_height = m_bottom;
|
||||
else if ( m_height > m_top )
|
||||
m_height = m_top;
|
||||
Vector location = pTarget->GetLocalOrigin() - GetLocalOrigin();
|
||||
location.z += (pTarget->GetViewOffset().z);
|
||||
if ( location.Length() < 80 )
|
||||
SetCondition( COND_CAN_MELEE_ATTACK1 );
|
||||
// Turn towards target ent
|
||||
targetYaw = UTIL_VecToYaw( location );
|
||||
|
||||
QAngle vTestAngle = GetAbsAngles();
|
||||
|
||||
targetYaw = UTIL_AngleDiff( targetYaw, UTIL_AngleMod( GetAbsAngles().y ) );
|
||||
|
||||
if ( targetYaw < (-LEECH_TURN_RATE) )
|
||||
targetYaw = (-LEECH_TURN_RATE);
|
||||
else if ( targetYaw > (LEECH_TURN_RATE) )
|
||||
targetYaw = (LEECH_TURN_RATE);
|
||||
else
|
||||
targetSpeed *= 2;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
if ( m_zTime < gpGlobals->curtime )
|
||||
{
|
||||
float newHeight = random->RandomFloat( m_bottom, m_top );
|
||||
m_height = 0.5 * m_height + 0.5 * newHeight;
|
||||
m_zTime = gpGlobals->curtime + random->RandomFloat( 1, 4 );
|
||||
}
|
||||
if ( random->RandomInt( 0, 100 ) < 10 )
|
||||
targetYaw = random->RandomInt( -30, 30 );
|
||||
pTarget = NULL;
|
||||
// oldorigin test
|
||||
if ( ( GetLocalOrigin() - m_oldOrigin ).Length() < 1 )
|
||||
{
|
||||
// If leech didn't move, there must be something blocking it, so try to turn
|
||||
m_sideTime = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
m_obstacle = ObstacleDistance( pTarget );
|
||||
m_oldOrigin = GetLocalOrigin();
|
||||
if ( m_obstacle < 0.1 )
|
||||
m_obstacle = 0.1;
|
||||
|
||||
Vector vForward, vRight;
|
||||
|
||||
AngleVectors( GetAbsAngles(), &vForward, &vRight, NULL );
|
||||
|
||||
// is the way ahead clear?
|
||||
if ( m_obstacle == 1.0 )
|
||||
{
|
||||
// if the leech is turning, stop the trend.
|
||||
if ( m_flTurning != 0 )
|
||||
{
|
||||
m_flTurning = 0;
|
||||
}
|
||||
|
||||
m_fPathBlocked = FALSE;
|
||||
m_flSpeed = UTIL_Approach( targetSpeed, m_flSpeed, LEECH_SWIM_ACCEL * LEECH_FRAMETIME );
|
||||
SetAbsVelocity( vForward * m_flSpeed );
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
m_obstacle = 1.0 / m_obstacle;
|
||||
// IF we get this far in the function, the leader's path is blocked!
|
||||
m_fPathBlocked = TRUE;
|
||||
|
||||
if ( m_flTurning == 0 )// something in the way and leech is not already turning to avoid
|
||||
{
|
||||
Vector vecTest;
|
||||
// measure clearance on left and right to pick the best dir to turn
|
||||
vecTest = GetLocalOrigin() + ( vRight * LEECH_SIZEX) + ( vForward * LEECH_CHECK_DIST);
|
||||
UTIL_TraceLine( GetLocalOrigin(), vecTest, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr);
|
||||
flRightSide = tr.fraction;
|
||||
|
||||
vecTest = GetLocalOrigin() + ( vRight * -LEECH_SIZEX) + ( vForward * LEECH_CHECK_DIST);
|
||||
UTIL_TraceLine( GetLocalOrigin(), vecTest, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr);
|
||||
|
||||
flLeftSide = tr.fraction;
|
||||
|
||||
// turn left, right or random depending on clearance ratio
|
||||
float delta = (flRightSide - flLeftSide);
|
||||
if ( delta > 0.1 || (delta > -0.1 && random->RandomInt( 0,100 ) < 50 ) )
|
||||
m_flTurning = -LEECH_TURN_RATE;
|
||||
else
|
||||
m_flTurning = LEECH_TURN_RATE;
|
||||
}
|
||||
|
||||
m_flSpeed = UTIL_Approach( -(LEECH_SWIM_SPEED*0.5), m_flSpeed, LEECH_SWIM_DECEL * LEECH_FRAMETIME * m_obstacle );
|
||||
SetAbsVelocity( vForward * m_flSpeed );
|
||||
}
|
||||
|
||||
GetMotor()->SetIdealYaw( m_flTurning + targetYaw );
|
||||
UpdateMotion();
|
||||
}
|
||||
|
||||
//
|
||||
// ObstacleDistance - returns normalized distance to obstacle
|
||||
//
|
||||
float CNPC_Leech::ObstacleDistance( CBaseEntity *pTarget )
|
||||
{
|
||||
trace_t tr;
|
||||
Vector vecTest;
|
||||
Vector vForward, vRight;
|
||||
|
||||
// use VELOCITY, not angles, not all boids point the direction they are flying
|
||||
//Vector vecDir = UTIL_VecToAngles( pev->velocity );
|
||||
QAngle tmp = GetAbsAngles();
|
||||
tmp.x = -tmp.x;
|
||||
AngleVectors ( tmp, &vForward, &vRight, NULL );
|
||||
|
||||
// check for obstacle ahead
|
||||
vecTest = GetLocalOrigin() + vForward * LEECH_CHECK_DIST;
|
||||
UTIL_TraceLine( GetLocalOrigin(), vecTest, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr);
|
||||
|
||||
if ( tr.startsolid )
|
||||
{
|
||||
m_flSpeed = -LEECH_SWIM_SPEED * 0.5;
|
||||
}
|
||||
|
||||
if ( tr.fraction != 1.0 )
|
||||
{
|
||||
if ( (pTarget == NULL || tr.m_pEnt != pTarget ) )
|
||||
{
|
||||
return tr.fraction;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( fabs( m_height - GetLocalOrigin().z ) > 10 )
|
||||
return tr.fraction;
|
||||
}
|
||||
}
|
||||
|
||||
if ( m_sideTime < gpGlobals->curtime )
|
||||
{
|
||||
// extra wide checks
|
||||
vecTest = GetLocalOrigin() + vRight * LEECH_SIZEX * 2 + vForward * LEECH_CHECK_DIST;
|
||||
UTIL_TraceLine( GetLocalOrigin(), vecTest, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr);
|
||||
|
||||
if (tr.fraction != 1.0)
|
||||
return tr.fraction;
|
||||
|
||||
vecTest = GetLocalOrigin() - vRight * LEECH_SIZEX * 2 + vForward * LEECH_CHECK_DIST;
|
||||
UTIL_TraceLine( GetLocalOrigin(), vecTest, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr);
|
||||
if (tr.fraction != 1.0)
|
||||
return tr.fraction;
|
||||
|
||||
// Didn't hit either side, so stop testing for another 0.5 - 1 seconds
|
||||
m_sideTime = gpGlobals->curtime + random->RandomFloat(0.5,1);
|
||||
}
|
||||
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
void CNPC_Leech::UpdateMotion( void )
|
||||
{
|
||||
float flapspeed = ( m_flSpeed - m_flAccelerate) / LEECH_ACCELERATE;
|
||||
m_flAccelerate = m_flAccelerate * 0.8 + m_flSpeed * 0.2;
|
||||
|
||||
if (flapspeed < 0)
|
||||
flapspeed = -flapspeed;
|
||||
flapspeed += 1.0;
|
||||
if (flapspeed < 0.5)
|
||||
flapspeed = 0.5;
|
||||
if (flapspeed > 1.9)
|
||||
flapspeed = 1.9;
|
||||
|
||||
m_flPlaybackRate = flapspeed;
|
||||
|
||||
QAngle vAngularVelocity = GetLocalAngularVelocity();
|
||||
QAngle vAngles = GetLocalAngles();
|
||||
|
||||
if ( !m_fPathBlocked )
|
||||
vAngularVelocity.y = GetMotor()->GetIdealYaw();
|
||||
else
|
||||
vAngularVelocity.y = GetMotor()->GetIdealYaw() * m_obstacle;
|
||||
|
||||
if ( vAngularVelocity.y > 150 )
|
||||
SetIdealActivity( ACT_TURN_LEFT );
|
||||
else if ( vAngularVelocity.y < -150 )
|
||||
SetIdealActivity( ACT_TURN_RIGHT );
|
||||
else
|
||||
SetIdealActivity( ACT_SWIM );
|
||||
|
||||
// lean
|
||||
float targetPitch, delta;
|
||||
delta = m_height - GetLocalOrigin().z;
|
||||
|
||||
/* if ( delta < -10 )
|
||||
targetPitch = -30;
|
||||
else if ( delta > 10 )
|
||||
targetPitch = 30;
|
||||
else*/
|
||||
targetPitch = 0;
|
||||
|
||||
vAngles.x = UTIL_Approach( targetPitch, vAngles.x, 60 * LEECH_FRAMETIME );
|
||||
|
||||
// bank
|
||||
vAngularVelocity.z = - ( vAngles.z + (vAngularVelocity.y * 0.25));
|
||||
|
||||
if ( m_NPCState == NPC_STATE_COMBAT && HasCondition( COND_CAN_MELEE_ATTACK1 ) )
|
||||
SetIdealActivity( ACT_MELEE_ATTACK1 );
|
||||
|
||||
// Out of water check
|
||||
if ( !GetWaterLevel() )
|
||||
{
|
||||
SetMoveType( MOVETYPE_FLYGRAVITY );
|
||||
SetIdealActivity( ACT_HOP );
|
||||
SetAbsVelocity( vec3_origin );
|
||||
|
||||
// Animation will intersect the floor if either of these is non-zero
|
||||
vAngles.z = 0;
|
||||
vAngles.x = 0;
|
||||
|
||||
m_flPlaybackRate = random->RandomFloat( 0.8, 1.2 );
|
||||
}
|
||||
else if ( GetMoveType() == MOVETYPE_FLYGRAVITY )
|
||||
{
|
||||
SetMoveType( MOVETYPE_FLY );
|
||||
SetGroundEntity( NULL );
|
||||
|
||||
// TODO
|
||||
RecalculateWaterlevel();
|
||||
m_waterTime = gpGlobals->curtime + 2; // Recalc again soon, water may be rising
|
||||
}
|
||||
|
||||
if ( GetActivity() != GetIdealActivity() )
|
||||
{
|
||||
SetActivity ( GetIdealActivity() );
|
||||
}
|
||||
StudioFrameAdvance();
|
||||
|
||||
DispatchAnimEvents ( this );
|
||||
|
||||
SetLocalAngles( vAngles );
|
||||
SetLocalAngularVelocity( vAngularVelocity );
|
||||
|
||||
Vector vForward, vRight;
|
||||
|
||||
AngleVectors( vAngles, &vForward, &vRight, NULL );
|
||||
|
||||
#if DEBUG_BEAMS
|
||||
if ( m_fPathBlocked )
|
||||
{
|
||||
float color = m_obstacle * 30;
|
||||
if ( m_obstacle == 1.0 )
|
||||
color = 0;
|
||||
if ( color > 255 )
|
||||
color = 255;
|
||||
NDebugOverlay::Line( GetLocalOrigin(), GetLocalOrigin() + vForward * LEECH_CHECK_DIST, 255, color, color, false, 0.1f );
|
||||
}
|
||||
else
|
||||
NDebugOverlay::Line( GetLocalOrigin(), GetLocalOrigin() + vForward * LEECH_CHECK_DIST, 255, 255, 0, false, 0.1f );
|
||||
|
||||
NDebugOverlay::Line( GetLocalOrigin(), GetLocalOrigin() + vRight * (vAngularVelocity.y*0.25), 0, 0, 255, false, 0.1f );
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void CNPC_Leech::Event_Killed( const CTakeDamageInfo &info )
|
||||
{
|
||||
Vector vecSplatDir;
|
||||
trace_t tr;
|
||||
|
||||
//ALERT(at_aiconsole, "Leech: killed\n");
|
||||
// tell owner ( if any ) that we're dead.This is mostly for MonsterMaker functionality.
|
||||
CBaseEntity *pOwner = GetOwnerEntity();
|
||||
if (pOwner)
|
||||
pOwner->DeathNotice( this );
|
||||
|
||||
// When we hit the ground, play the "death_end" activity
|
||||
if ( GetWaterLevel() )
|
||||
{
|
||||
QAngle qAngles = GetAbsAngles();
|
||||
QAngle qAngularVel = GetLocalAngularVelocity();
|
||||
Vector vOrigin = GetLocalOrigin();
|
||||
|
||||
qAngles.z = 0;
|
||||
qAngles.x = 0;
|
||||
|
||||
vOrigin.z += 1;
|
||||
|
||||
SetAbsVelocity( vec3_origin );
|
||||
|
||||
if ( random->RandomInt( 0, 99 ) < 70 )
|
||||
qAngularVel.y = random->RandomInt( -720, 720 );
|
||||
|
||||
SetAbsAngles( qAngles );
|
||||
SetLocalAngularVelocity( qAngularVel );
|
||||
SetAbsOrigin( vOrigin );
|
||||
|
||||
|
||||
SetGravity ( 0.02 );
|
||||
SetGroundEntity( NULL );
|
||||
SetActivity( ACT_DIESIMPLE );
|
||||
}
|
||||
else
|
||||
SetActivity( ACT_DIEFORWARD );
|
||||
|
||||
SetMoveType( MOVETYPE_FLYGRAVITY );
|
||||
m_takedamage = DAMAGE_NO;
|
||||
|
||||
SetThink( &CNPC_Leech::DeadThink );
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,471 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
#include "cbase.h"
|
||||
#include "hl1_ai_basenpc.h"
|
||||
#include "ai_default.h"
|
||||
#include "ai_task.h"
|
||||
#include "ai_schedule.h"
|
||||
#include "ai_node.h"
|
||||
#include "ai_hull.h"
|
||||
#include "ai_hint.h"
|
||||
#include "ai_memory.h"
|
||||
#include "ai_route.h"
|
||||
#include "ai_motor.h"
|
||||
#include "ai_senses.h"
|
||||
#include "soundent.h"
|
||||
#include "game.h"
|
||||
#include "npcevent.h"
|
||||
#include "entitylist.h"
|
||||
#include "activitylist.h"
|
||||
#include "animation.h"
|
||||
#include "basecombatweapon.h"
|
||||
#include "IEffects.h"
|
||||
#include "vstdlib/random.h"
|
||||
#include "engine/IEngineSound.h"
|
||||
#include "ammodef.h"
|
||||
#include "ai_behavior_follow.h"
|
||||
#include "ai_navigator.h"
|
||||
#include "decals.h"
|
||||
|
||||
|
||||
#define ROACH_IDLE 0
|
||||
#define ROACH_BORED 1
|
||||
#define ROACH_SCARED_BY_ENT 2
|
||||
#define ROACH_SCARED_BY_LIGHT 3
|
||||
#define ROACH_SMELL_FOOD 4
|
||||
#define ROACH_EAT 5
|
||||
|
||||
//=========================================================
|
||||
// Monster's Anim Events Go Here
|
||||
//=========================================================
|
||||
class CNPC_Roach : public CHL1BaseNPC
|
||||
{
|
||||
DECLARE_CLASS( CNPC_Roach, CHL1BaseNPC );
|
||||
|
||||
public:
|
||||
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
float MaxYawSpeed( void );
|
||||
|
||||
// DECLARE_DATADESC();
|
||||
|
||||
void NPCThink ( void );
|
||||
void PickNewDest ( int iCondition );
|
||||
void Look ( int iDistance );
|
||||
void Move ( float flInterval );
|
||||
|
||||
Class_T Classify( void ) { return CLASS_INSECT; }
|
||||
|
||||
void Touch ( CBaseEntity *pOther );
|
||||
|
||||
void Event_Killed( const CTakeDamageInfo &info );
|
||||
int GetSoundInterests ( void );
|
||||
|
||||
void Eat( float flFullDuration );
|
||||
bool ShouldEat( void );
|
||||
|
||||
bool ShouldGib( const CTakeDamageInfo &info ) { return false; }
|
||||
|
||||
float m_flLastLightLevel;
|
||||
float m_flNextSmellTime;
|
||||
|
||||
// UNDONE: These don't necessarily need to be save/restored, but if we add more data, it may
|
||||
bool m_fLightHacked;
|
||||
int m_iMode;
|
||||
|
||||
float m_flHungryTime;
|
||||
// -----------------------------
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( monster_cockroach, CNPC_Roach );
|
||||
|
||||
//BEGIN_DATADESC( CNPC_Roach )
|
||||
|
||||
// DEFINE_FUNCTION( RoachTouch ),
|
||||
|
||||
//END_DATADESC()
|
||||
|
||||
|
||||
//=========================================================
|
||||
// Spawn
|
||||
//=========================================================
|
||||
void CNPC_Roach::Spawn()
|
||||
{
|
||||
Precache( );
|
||||
|
||||
SetModel( "models/roach.mdl" );
|
||||
UTIL_SetSize( this, Vector( -1, -1, 0 ), Vector( 1, 1, 2 ) );
|
||||
|
||||
SetSolid( SOLID_BBOX );
|
||||
AddSolidFlags( FSOLID_NOT_STANDABLE );
|
||||
SetMoveType( MOVETYPE_STEP );
|
||||
m_bloodColor = BLOOD_COLOR_YELLOW;
|
||||
ClearEffects();
|
||||
m_iHealth = 1;
|
||||
m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result )
|
||||
m_NPCState = NPC_STATE_NONE;
|
||||
|
||||
SetRenderColor( 255, 255, 255, 255 );
|
||||
|
||||
NPCInit();
|
||||
SetActivity ( ACT_IDLE );
|
||||
|
||||
SetViewOffset ( Vector ( 0, 0, 1 ) );// position of the eyes relative to monster's origin.
|
||||
m_takedamage = DAMAGE_YES;
|
||||
m_fLightHacked = FALSE;
|
||||
m_flLastLightLevel = -1;
|
||||
m_iMode = ROACH_IDLE;
|
||||
m_flNextSmellTime = gpGlobals->curtime;
|
||||
|
||||
AddEffects( EF_NOSHADOW );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Precache - precaches all resources this monster needs
|
||||
//=========================================================
|
||||
void CNPC_Roach::Precache()
|
||||
{
|
||||
PrecacheModel("models/roach.mdl");
|
||||
|
||||
PrecacheScriptSound( "Roach.Walk" );
|
||||
PrecacheScriptSound( "Roach.Die" );
|
||||
PrecacheScriptSound( "Roach.Smash" );
|
||||
}
|
||||
|
||||
float CNPC_Roach::MaxYawSpeed( void )
|
||||
{
|
||||
return 120.0f;
|
||||
}
|
||||
|
||||
void CNPC_Roach::Eat( float flFullDuration )
|
||||
{
|
||||
m_flHungryTime = gpGlobals->curtime + flFullDuration;
|
||||
}
|
||||
|
||||
bool CNPC_Roach::ShouldEat( void )
|
||||
{
|
||||
if ( m_flHungryTime > gpGlobals->curtime )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// MonsterThink, overridden for roaches.
|
||||
//=========================================================
|
||||
void CNPC_Roach::NPCThink( void )
|
||||
{
|
||||
if ( FNullEnt( UTIL_FindClientInPVS( edict() ) ) )
|
||||
SetNextThink( gpGlobals->curtime + random->RandomFloat( 1.0f , 1.5f ) );
|
||||
else
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );// keep monster thinking
|
||||
|
||||
float flInterval = gpGlobals->curtime - GetLastThink();
|
||||
|
||||
StudioFrameAdvance( ); // animate
|
||||
|
||||
if ( !m_fLightHacked )
|
||||
{
|
||||
// if light value hasn't been collection for the first time yet,
|
||||
// suspend the creature for a second so the world finishes spawning, then we'll collect the light level.
|
||||
SetNextThink( gpGlobals->curtime + 1 );
|
||||
m_fLightHacked = TRUE;
|
||||
return;
|
||||
}
|
||||
else if ( m_flLastLightLevel < 0 )
|
||||
{
|
||||
// collect light level for the first time, now that all of the lightmaps in the roach's area have been calculated.
|
||||
m_flLastLightLevel = 0;
|
||||
}
|
||||
|
||||
switch ( m_iMode )
|
||||
{
|
||||
case ROACH_IDLE:
|
||||
case ROACH_EAT:
|
||||
{
|
||||
// if not moving, sample environment to see if anything scary is around. Do a radius search 'look' at random.
|
||||
if ( random->RandomInt( 0, 3 ) == 1 )
|
||||
{
|
||||
Look( 150 );
|
||||
|
||||
if ( HasCondition( COND_SEE_FEAR ) )
|
||||
{
|
||||
// if see something scary
|
||||
//ALERT ( at_aiconsole, "Scared\n" );
|
||||
Eat( 30 + ( random->RandomInt( 0, 14 ) ) );// roach will ignore food for 30 to 45 seconds
|
||||
PickNewDest( ROACH_SCARED_BY_ENT );
|
||||
SetActivity ( ACT_WALK );
|
||||
}
|
||||
else if ( random->RandomInt( 0,149 ) == 1 )
|
||||
{
|
||||
// if roach doesn't see anything, there's still a chance that it will move. (boredom)
|
||||
//ALERT ( at_aiconsole, "Bored\n" );
|
||||
PickNewDest( ROACH_BORED );
|
||||
SetActivity ( ACT_WALK );
|
||||
|
||||
if ( m_iMode == ROACH_EAT )
|
||||
{
|
||||
// roach will ignore food for 30 to 45 seconds if it got bored while eating.
|
||||
Eat( 30 + ( random->RandomInt(0,14) ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// don't do this stuff if eating!
|
||||
if ( m_iMode == ROACH_IDLE )
|
||||
{
|
||||
if ( ShouldEat() )
|
||||
{
|
||||
GetSenses()->Listen();
|
||||
}
|
||||
|
||||
if ( 0 > m_flLastLightLevel )
|
||||
{
|
||||
// someone turned on lights!
|
||||
//ALERT ( at_console, "Lights!\n" );
|
||||
PickNewDest( ROACH_SCARED_BY_LIGHT );
|
||||
SetActivity ( ACT_WALK );
|
||||
}
|
||||
else if ( HasCondition( COND_SMELL ) )
|
||||
{
|
||||
CSound *pSound = GetLoudestSoundOfType( ALL_SOUNDS );
|
||||
|
||||
// roach smells food and is just standing around. Go to food unless food isn't on same z-plane.
|
||||
if ( pSound && abs( pSound->GetSoundOrigin().z - GetAbsOrigin().z ) <= 3 )
|
||||
{
|
||||
PickNewDest( ROACH_SMELL_FOOD );
|
||||
SetActivity ( ACT_WALK );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case ROACH_SCARED_BY_LIGHT:
|
||||
{
|
||||
// if roach was scared by light, then stop if we're over a spot at least as dark as where we started!
|
||||
if ( 0 <= m_flLastLightLevel )
|
||||
{
|
||||
SetActivity ( ACT_IDLE );
|
||||
m_flLastLightLevel = 0;// make this our new light level.
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( GetActivity() != ACT_IDLE )
|
||||
{
|
||||
Move( flInterval );
|
||||
}
|
||||
}
|
||||
|
||||
void CNPC_Roach::PickNewDest ( int iCondition )
|
||||
{
|
||||
Vector vecNewDir;
|
||||
Vector vecDest;
|
||||
float flDist;
|
||||
|
||||
m_iMode = iCondition;
|
||||
|
||||
GetNavigator()->ClearGoal();
|
||||
|
||||
if ( m_iMode == ROACH_SMELL_FOOD )
|
||||
{
|
||||
// find the food and go there.
|
||||
CSound *pSound = GetLoudestSoundOfType( ALL_SOUNDS );
|
||||
|
||||
if ( pSound )
|
||||
{
|
||||
GetNavigator()->SetRandomGoal( 3 - random->RandomInt( 0,5 ) );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
// picks a random spot, requiring that it be at least 128 units away
|
||||
// else, the roach will pick a spot too close to itself and run in
|
||||
// circles. this is a hack but buys me time to work on the real monsters.
|
||||
vecNewDir.x = random->RandomInt( -1, 1 );
|
||||
vecNewDir.y = random->RandomInt( -1, 1 );
|
||||
flDist = 256 + ( random->RandomInt(0,255) );
|
||||
vecDest = GetAbsOrigin() + vecNewDir * flDist;
|
||||
|
||||
} while ( ( vecDest - GetAbsOrigin() ).Length2D() < 128 );
|
||||
|
||||
Vector vecLocation;
|
||||
|
||||
vecLocation.x = vecDest.x;
|
||||
vecLocation.y = vecDest.y;
|
||||
vecLocation.z = GetAbsOrigin().z;
|
||||
|
||||
AI_NavGoal_t goal( GOALTYPE_LOCATION, vecLocation, ACT_WALK );
|
||||
|
||||
GetNavigator()->SetGoal( goal );
|
||||
|
||||
if ( random->RandomInt( 0, 9 ) == 1 )
|
||||
{
|
||||
// every once in a while, a roach will play a skitter sound when they decide to run
|
||||
CPASAttenuationFilter filter( this );
|
||||
EmitSound( filter, entindex(), "Roach.Walk" );
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Look - overriden for the roach, which can virtually see
|
||||
// 360 degrees.
|
||||
//=========================================================
|
||||
void CNPC_Roach::Look ( int iDistance )
|
||||
{
|
||||
CBaseEntity *pSightEnt = NULL;// the current visible entity that we're dealing with
|
||||
|
||||
// DON'T let visibility information from last frame sit around!
|
||||
ClearCondition( COND_SEE_HATE | COND_SEE_DISLIKE | COND_SEE_ENEMY | COND_SEE_FEAR );
|
||||
|
||||
// don't let monsters outside of the player's PVS act up, or most of the interesting
|
||||
// things will happen before the player gets there!
|
||||
if ( FNullEnt( UTIL_FindClientInPVS( edict() ) ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Does sphere also limit itself to PVS?
|
||||
// Examine all entities within a reasonable radius
|
||||
// !!!PERFORMANCE - let's trivially reject the ent list before radius searching!
|
||||
|
||||
for ( CEntitySphereQuery sphere( GetAbsOrigin(), iDistance ); ( pSightEnt = sphere.GetCurrentEntity() ) != NULL; sphere.NextEntity() )
|
||||
{
|
||||
// only consider ents that can be damaged. !!!temporarily only considering other monsters and clients
|
||||
if ( pSightEnt->IsPlayer() || FBitSet ( pSightEnt->GetFlags(), FL_NPC ) )
|
||||
{
|
||||
if ( /*FVisible( pSightEnt ) &&*/ !FBitSet( pSightEnt->GetFlags(), FL_NOTARGET ) && pSightEnt->m_iHealth > 0 )
|
||||
{
|
||||
// don't add the Enemy's relationship to the conditions. We only want to worry about conditions when
|
||||
// we see monsters other than the Enemy.
|
||||
switch ( IRelationType ( pSightEnt ) )
|
||||
{
|
||||
case D_FR:
|
||||
SetCondition( COND_SEE_FEAR );
|
||||
break;
|
||||
case D_NU:
|
||||
break;
|
||||
default:
|
||||
Msg ( "%s can't asses %s\n", GetClassname(), pSightEnt->GetClassname() );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// roach's move function
|
||||
//=========================================================
|
||||
void CNPC_Roach::Move ( float flInterval )
|
||||
{
|
||||
float flWaypointDist;
|
||||
Vector vecApex;
|
||||
|
||||
// local move to waypoint.
|
||||
flWaypointDist = ( GetNavigator()->GetGoalPos() - GetAbsOrigin() ).Length2D();
|
||||
|
||||
GetMotor()->SetIdealYawToTargetAndUpdate( GetNavigator()->GetGoalPos() );
|
||||
|
||||
float speed = 150 * flInterval;
|
||||
|
||||
Vector vToTarget = GetNavigator()->GetGoalPos() - GetAbsOrigin();
|
||||
vToTarget.NormalizeInPlace();
|
||||
Vector vMovePos = vToTarget * speed;
|
||||
|
||||
if ( random->RandomInt( 0,7 ) == 1 )
|
||||
{
|
||||
// randomly change direction
|
||||
PickNewDest( m_iMode );
|
||||
}
|
||||
|
||||
if( !WalkMove( vMovePos, MASK_NPCSOLID ) )
|
||||
{
|
||||
PickNewDest( m_iMode );
|
||||
}
|
||||
|
||||
// if the waypoint is closer than step size, then stop after next step (ok for roach to overshoot)
|
||||
if ( flWaypointDist <= m_flGroundSpeed * flInterval )
|
||||
{
|
||||
// take truncated step and stop
|
||||
|
||||
SetActivity ( ACT_IDLE );
|
||||
m_flLastLightLevel = 0;// this is roach's new comfortable light level
|
||||
|
||||
if ( m_iMode == ROACH_SMELL_FOOD )
|
||||
{
|
||||
m_iMode = ROACH_EAT;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_iMode = ROACH_IDLE;
|
||||
}
|
||||
}
|
||||
|
||||
if ( random->RandomInt( 0,149 ) == 1 && m_iMode != ROACH_SCARED_BY_LIGHT && m_iMode != ROACH_SMELL_FOOD )
|
||||
{
|
||||
// random skitter while moving as long as not on a b-line to get out of light or going to food
|
||||
PickNewDest( FALSE );
|
||||
}
|
||||
}
|
||||
|
||||
void CNPC_Roach::Touch ( CBaseEntity *pOther )
|
||||
{
|
||||
Vector vecSpot;
|
||||
trace_t tr;
|
||||
|
||||
if ( pOther->GetAbsVelocity() == vec3_origin || !pOther->IsPlayer() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
vecSpot = GetAbsOrigin() + Vector ( 0 , 0 , 8 );//move up a bit, and trace down.
|
||||
//UTIL_TraceLine ( vecSpot, vecSpot + Vector ( 0, 0, -24 ), ignore_monsters, ENT(pev), & tr);
|
||||
|
||||
UTIL_TraceLine ( vecSpot, vecSpot + Vector ( 0, 0, -24 ), MASK_ALL, this, COLLISION_GROUP_NONE, &tr);
|
||||
|
||||
// This isn't really blood. So you don't have to screen it out based on violence levels (UTIL_ShouldShowBlood())
|
||||
UTIL_DecalTrace( &tr, "YellowBlood" );
|
||||
|
||||
// DMG_GENERIC because we don't want any physics force generated
|
||||
TakeDamage( CTakeDamageInfo( pOther, pOther, m_iHealth, DMG_GENERIC ) );
|
||||
}
|
||||
|
||||
void CNPC_Roach::Event_Killed( const CTakeDamageInfo &info )
|
||||
{
|
||||
RemoveSolidFlags( FSOLID_NOT_SOLID );
|
||||
|
||||
CPASAttenuationFilter filter( this );
|
||||
|
||||
//random sound
|
||||
if ( random->RandomInt( 0,4 ) == 1 )
|
||||
{
|
||||
EmitSound( filter, entindex(), "Roach.Die" );
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitSound( filter, entindex(), "Roach.Smash" );
|
||||
}
|
||||
|
||||
CSoundEnt::InsertSound ( SOUND_WORLD, GetAbsOrigin(), 128, 1 );
|
||||
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
|
||||
int CNPC_Roach::GetSoundInterests ( void)
|
||||
{
|
||||
return SOUND_CARCASS |
|
||||
SOUND_MEAT;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,139 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
#ifndef NPC_SCIENTIST_H
|
||||
#define NPC_SCIENTIST_H
|
||||
|
||||
#include "hl1_npc_talker.h"
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
class CNPC_Scientist : public CHL1NPCTalker
|
||||
{
|
||||
DECLARE_CLASS( CNPC_Scientist, CHL1NPCTalker );
|
||||
public:
|
||||
|
||||
// DECLARE_SERVERCLASS();
|
||||
DECLARE_DATADESC();
|
||||
|
||||
|
||||
void Precache( void );
|
||||
void Spawn( void );
|
||||
void Activate();
|
||||
Class_T Classify( void );
|
||||
int GetSoundInterests ( void );
|
||||
|
||||
virtual void ModifyOrAppendCriteria( AI_CriteriaSet& set );
|
||||
|
||||
virtual int ObjectCaps( void ) { return UsableNPCObjectCaps(BaseClass::ObjectCaps()); }
|
||||
float MaxYawSpeed( void );
|
||||
|
||||
float TargetDistance( void );
|
||||
bool IsValidEnemy( CBaseEntity *pEnemy );
|
||||
|
||||
|
||||
int OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo );
|
||||
void Event_Killed( const CTakeDamageInfo &info );
|
||||
|
||||
void Heal( void );
|
||||
bool CanHeal( void );
|
||||
|
||||
int TranslateSchedule( int scheduleType );
|
||||
void HandleAnimEvent( animevent_t *pEvent );
|
||||
int SelectSchedule( void );
|
||||
void StartTask( const Task_t *pTask );
|
||||
void RunTask( const Task_t *pTask );
|
||||
|
||||
NPC_STATE SelectIdealState ( void );
|
||||
|
||||
int FriendNumber( int arrayNumber );
|
||||
|
||||
bool DisregardEnemy( CBaseEntity *pEnemy ) { return !pEnemy->IsAlive() || (gpGlobals->curtime - m_flFearTime) > 15; }
|
||||
|
||||
void TalkInit( void );
|
||||
|
||||
void DeclineFollowing( void );
|
||||
|
||||
bool CanBecomeRagdoll( void );
|
||||
bool ShouldGib( const CTakeDamageInfo &info );
|
||||
|
||||
void SUB_StartLVFadeOut( float delay = 10.0f, bool bNotSolid = true );
|
||||
void SUB_LVFadeOut( void );
|
||||
|
||||
void Scream( void );
|
||||
|
||||
Activity GetStoppedActivity( void );
|
||||
Activity NPC_TranslateActivity( Activity newActivity );
|
||||
|
||||
void PainSound( const CTakeDamageInfo &info );
|
||||
void DeathSound( const CTakeDamageInfo &info );
|
||||
|
||||
enum
|
||||
{
|
||||
SCHED_SCI_HEAL = BaseClass::NEXT_SCHEDULE,
|
||||
SCHED_SCI_FOLLOWTARGET,
|
||||
SCHED_SCI_STOPFOLLOWING,
|
||||
SCHED_SCI_FACETARGET,
|
||||
SCHED_SCI_COVER,
|
||||
SCHED_SCI_HIDE,
|
||||
SCHED_SCI_IDLESTAND,
|
||||
SCHED_SCI_PANIC,
|
||||
SCHED_SCI_FOLLOWSCARED,
|
||||
SCHED_SCI_FACETARGETSCARED,
|
||||
SCHED_SCI_FEAR,
|
||||
SCHED_SCI_STARTLE,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
TASK_SAY_HEAL = BaseClass::NEXT_TASK,
|
||||
TASK_HEAL,
|
||||
TASK_SAY_FEAR,
|
||||
TASK_RUN_PATH_SCARED,
|
||||
TASK_SCREAM,
|
||||
TASK_RANDOM_SCREAM,
|
||||
TASK_MOVE_TO_TARGET_RANGE_SCARED,
|
||||
};
|
||||
|
||||
DEFINE_CUSTOM_AI;
|
||||
|
||||
private:
|
||||
|
||||
float m_flFearTime;
|
||||
float m_flHealTime;
|
||||
float m_flPainTime;
|
||||
//float m_flResponseDelay;
|
||||
};
|
||||
|
||||
//=========================================================
|
||||
// Sitting Scientist PROP
|
||||
//=========================================================
|
||||
|
||||
class CNPC_SittingScientist : public CNPC_Scientist // kdb: changed from public CBaseMonster so he can speak
|
||||
{
|
||||
DECLARE_CLASS( CNPC_SittingScientist, CNPC_Scientist );
|
||||
public:
|
||||
|
||||
// DECLARE_SERVERCLASS();
|
||||
DECLARE_DATADESC();
|
||||
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
|
||||
int FriendNumber( int arrayNumber );
|
||||
|
||||
void SittingThink( void );
|
||||
|
||||
virtual void SetAnswerQuestion( CNPCSimpleTalker *pSpeaker );
|
||||
int m_baseSequence;
|
||||
int m_iHeadTurn;
|
||||
float m_flResponseDelay;
|
||||
|
||||
//DEFINE_CUSTOM_AI;
|
||||
};
|
||||
|
||||
#endif // NPC_SCIENTIST_H
|
||||
@@ -0,0 +1,527 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Projectile shot from the MP5
|
||||
//
|
||||
// $Workfile: $
|
||||
// $Date: $
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// $Log: $
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
|
||||
#include "cbase.h"
|
||||
#include "soundent.h"
|
||||
#include "engine/IEngineSound.h"
|
||||
#include "ai_senses.h"
|
||||
#include "hl1_npc_snark.h"
|
||||
#include "SoundEmitterSystem/isoundemittersystembase.h"
|
||||
|
||||
|
||||
ConVar sk_snark_health ( "sk_snark_health", "0" );
|
||||
ConVar sk_snark_dmg_bite ( "sk_snark_dmg_bite", "0" );
|
||||
ConVar sk_snark_dmg_pop ( "sk_snark_dmg_pop", "0" );
|
||||
|
||||
|
||||
LINK_ENTITY_TO_CLASS( monster_snark, CSnark);
|
||||
|
||||
|
||||
//---------------------------------------------------------
|
||||
// Save/Restore
|
||||
//---------------------------------------------------------
|
||||
BEGIN_DATADESC( CSnark )
|
||||
DEFINE_FIELD( m_flDie, FIELD_TIME ),
|
||||
DEFINE_FIELD( m_vecTarget, FIELD_VECTOR ),
|
||||
DEFINE_FIELD( m_flNextHunt, FIELD_TIME ),
|
||||
DEFINE_FIELD( m_flNextHit, FIELD_TIME ),
|
||||
DEFINE_FIELD( m_posPrev, FIELD_POSITION_VECTOR ),
|
||||
DEFINE_FIELD( m_hOwner, FIELD_EHANDLE ),
|
||||
DEFINE_FIELD( m_iMyClass, FIELD_INTEGER ),
|
||||
|
||||
DEFINE_ENTITYFUNC( SuperBounceTouch ),
|
||||
DEFINE_THINKFUNC( HuntThink ),
|
||||
END_DATADESC()
|
||||
|
||||
|
||||
#define SQUEEK_DETONATE_DELAY 15.0
|
||||
#define SNARK_EXPLOSION_VOLUME 512
|
||||
|
||||
|
||||
enum w_squeak_e {
|
||||
WSQUEAK_IDLE1 = 0,
|
||||
WSQUEAK_FIDGET,
|
||||
WSQUEAK_JUMP,
|
||||
WSQUEAK_RUN,
|
||||
};
|
||||
|
||||
float CSnark::m_flNextBounceSoundTime = 0;
|
||||
|
||||
void CSnark::Precache( void )
|
||||
{
|
||||
BaseClass::Precache();
|
||||
|
||||
PrecacheModel( "models/w_squeak2.mdl" );
|
||||
|
||||
PrecacheScriptSound( "Snark.Die" );
|
||||
PrecacheScriptSound( "Snark.Gibbed" );
|
||||
PrecacheScriptSound( "Snark.Squeak" );
|
||||
PrecacheScriptSound( "Snark.Deploy" );
|
||||
PrecacheScriptSound( "Snark.Bounce" );
|
||||
|
||||
}
|
||||
|
||||
|
||||
void CSnark::Spawn( void )
|
||||
{
|
||||
Precache();
|
||||
|
||||
SetSolid( SOLID_BBOX );
|
||||
AddSolidFlags( FSOLID_NOT_STANDABLE );
|
||||
SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_CUSTOM );
|
||||
SetFriction(1.0);
|
||||
|
||||
SetModel( "models/w_squeak2.mdl" );
|
||||
UTIL_SetSize( this, Vector( -4, -4, 0 ), Vector( 4, 4, 8 ) );
|
||||
|
||||
SetBloodColor( BLOOD_COLOR_YELLOW );
|
||||
|
||||
SetTouch( &CSnark::SuperBounceTouch );
|
||||
SetThink( &CSnark::HuntThink );
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
|
||||
m_flNextHit = gpGlobals->curtime;
|
||||
m_flNextHunt = gpGlobals->curtime + 1E6;
|
||||
m_flNextBounceSoundTime = gpGlobals->curtime;
|
||||
|
||||
AddFlag( FL_AIMTARGET | FL_NPC );
|
||||
m_takedamage = DAMAGE_YES;
|
||||
|
||||
m_iHealth = sk_snark_health.GetFloat();
|
||||
m_iMaxHealth = m_iHealth;
|
||||
|
||||
SetGravity( UTIL_ScaleForGravity( 400 ) ); // use a lower gravity for snarks
|
||||
SetFriction( 0.5 );
|
||||
|
||||
SetDamage( sk_snark_dmg_pop.GetFloat() );
|
||||
|
||||
m_flDie = gpGlobals->curtime + SQUEEK_DETONATE_DELAY;
|
||||
|
||||
m_flFieldOfView = 0; // 180 degrees
|
||||
|
||||
if ( GetOwnerEntity() )
|
||||
m_hOwner = GetOwnerEntity();
|
||||
|
||||
m_flNextBounceSoundTime = gpGlobals->curtime;// reset each time a snark is spawned.
|
||||
|
||||
SetSequence( WSQUEAK_RUN );
|
||||
ResetSequenceInfo( );
|
||||
|
||||
m_iMyClass = CLASS_NONE;
|
||||
|
||||
m_posPrev = Vector( 0, 0, 0 );
|
||||
}
|
||||
|
||||
|
||||
Class_T CSnark::Classify( void )
|
||||
{
|
||||
if ( m_iMyClass != CLASS_NONE )
|
||||
return m_iMyClass; // protect against recursion
|
||||
|
||||
if ( GetEnemy() != NULL )
|
||||
{
|
||||
m_iMyClass = CLASS_INSECT; // no one cares about it
|
||||
switch( GetEnemy()->Classify( ) )
|
||||
{
|
||||
case CLASS_PLAYER:
|
||||
case CLASS_HUMAN_PASSIVE:
|
||||
case CLASS_HUMAN_MILITARY:
|
||||
m_iMyClass = CLASS_NONE;
|
||||
return CLASS_ALIEN_MILITARY; // barney's get mad, grunts get mad at it
|
||||
}
|
||||
m_iMyClass = CLASS_NONE;
|
||||
}
|
||||
|
||||
return CLASS_ALIEN_BIOWEAPON;
|
||||
}
|
||||
|
||||
|
||||
void CSnark::Event_Killed( const CTakeDamageInfo &inputInfo )
|
||||
{
|
||||
// pev->model = iStringNull;// make invisible
|
||||
SetThink( &CSnark::SUB_Remove );
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
SetTouch( NULL );
|
||||
|
||||
// since squeak grenades never leave a body behind, clear out their takedamage now.
|
||||
// Squeaks do a bit of radius damage when they pop, and that radius damage will
|
||||
// continue to call this function unless we acknowledge the Squeak's death now. (sjb)
|
||||
m_takedamage = DAMAGE_NO;
|
||||
|
||||
// play squeek blast
|
||||
CPASAttenuationFilter filter( this, 0.5 );
|
||||
EmitSound( filter, entindex(), "Snark.Die" );
|
||||
|
||||
CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), SNARK_EXPLOSION_VOLUME, 3.0 );
|
||||
|
||||
UTIL_BloodDrips( WorldSpaceCenter(), Vector( 0, 0, 0 ), BLOOD_COLOR_YELLOW, 80 );
|
||||
|
||||
if ( m_hOwner != NULL )
|
||||
{
|
||||
RadiusDamage( CTakeDamageInfo( this, m_hOwner, GetDamage(), DMG_BLAST ), GetAbsOrigin(), GetDamage() * 2.5, CLASS_NONE, NULL );
|
||||
}
|
||||
else
|
||||
{
|
||||
RadiusDamage( CTakeDamageInfo( this, this, GetDamage(), DMG_BLAST ), GetAbsOrigin(), GetDamage() * 2.5, CLASS_NONE, NULL );
|
||||
}
|
||||
|
||||
// reset owner so death message happens
|
||||
if ( m_hOwner != NULL )
|
||||
SetOwnerEntity( m_hOwner );
|
||||
|
||||
CTakeDamageInfo info = inputInfo;
|
||||
int iGibDamage = g_pGameRules->Damage_GetShouldGibCorpse();
|
||||
info.SetDamageType( iGibDamage );
|
||||
|
||||
BaseClass::Event_Killed( info );
|
||||
}
|
||||
|
||||
|
||||
bool CSnark::Event_Gibbed( const CTakeDamageInfo &info )
|
||||
{
|
||||
CPASAttenuationFilter filter( this );
|
||||
EmitSound( filter, entindex(), "Snark.Gibbed" );
|
||||
|
||||
return BaseClass::Event_Gibbed( info );
|
||||
}
|
||||
|
||||
|
||||
void CSnark::HuntThink( void )
|
||||
{
|
||||
if (!IsInWorld())
|
||||
{
|
||||
SetTouch( NULL );
|
||||
UTIL_Remove( this );
|
||||
return;
|
||||
}
|
||||
|
||||
StudioFrameAdvance( );
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
|
||||
//FIXME: There's a problem in this movetype that causes it to set a ground entity but never recheck to clear it
|
||||
// For now, we stomp it clear and force it to revalidate -- jdw
|
||||
|
||||
SetGroundEntity( NULL );
|
||||
PhysicsStepRecheckGround();
|
||||
|
||||
// explode when ready
|
||||
if ( gpGlobals->curtime >= m_flDie )
|
||||
{
|
||||
g_vecAttackDir = GetAbsVelocity();
|
||||
VectorNormalize( g_vecAttackDir );
|
||||
m_iHealth = -1;
|
||||
CTakeDamageInfo info( this, this, 1, DMG_GENERIC );
|
||||
Event_Killed( info );
|
||||
return;
|
||||
}
|
||||
|
||||
// float
|
||||
if ( GetWaterLevel() != 0)
|
||||
{
|
||||
if ( GetMoveType() == MOVETYPE_FLYGRAVITY )
|
||||
{
|
||||
SetMoveType( MOVETYPE_FLY, MOVECOLLIDE_FLY_CUSTOM );
|
||||
}
|
||||
|
||||
Vector vecVel = GetAbsVelocity();
|
||||
vecVel *= 0.9;
|
||||
vecVel.z += 8.0;
|
||||
SetAbsVelocity( vecVel );
|
||||
}
|
||||
else if ( GetMoveType() == MOVETYPE_FLY )
|
||||
{
|
||||
SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_CUSTOM );
|
||||
}
|
||||
|
||||
// return if not time to hunt
|
||||
if ( m_flNextHunt > gpGlobals->curtime )
|
||||
return;
|
||||
|
||||
m_flNextHunt = gpGlobals->curtime + 2.0;
|
||||
|
||||
Vector vecFlat = GetAbsVelocity();
|
||||
vecFlat.z = 0;
|
||||
VectorNormalize( vecFlat );
|
||||
|
||||
if ( GetEnemy() == NULL || !GetEnemy()->IsAlive() )
|
||||
{
|
||||
// find target, bounce a bit towards it.
|
||||
GetSenses()->Look( 1024 );
|
||||
SetEnemy( BestEnemy() );
|
||||
}
|
||||
|
||||
// squeek if it's about time blow up
|
||||
if ( (m_flDie - gpGlobals->curtime <= 0.5) && (m_flDie - gpGlobals->curtime >= 0.3) )
|
||||
{
|
||||
CPASAttenuationFilter filter( this );
|
||||
EmitSound( filter, entindex(), "Snark.Squeak" );
|
||||
CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), 256, 0.25 );
|
||||
}
|
||||
|
||||
// higher pitch as squeeker gets closer to detonation time
|
||||
float flpitch = 155.0 - 60.0 * ( (m_flDie - gpGlobals->curtime) / SQUEEK_DETONATE_DELAY );
|
||||
if ( flpitch < 80 )
|
||||
flpitch = 80;
|
||||
|
||||
if ( GetEnemy() != NULL )
|
||||
{
|
||||
if ( FVisible( GetEnemy() ) )
|
||||
{
|
||||
m_vecTarget = GetEnemy()->EyePosition() - GetAbsOrigin();
|
||||
VectorNormalize( m_vecTarget );
|
||||
}
|
||||
|
||||
float flVel = GetAbsVelocity().Length();
|
||||
float flAdj = 50.0 / ( flVel + 10.0 );
|
||||
|
||||
if ( flAdj > 1.2 )
|
||||
flAdj = 1.2;
|
||||
|
||||
// ALERT( at_console, "think : enemy\n");
|
||||
|
||||
// ALERT( at_console, "%.0f %.2f %.2f %.2f\n", flVel, m_vecTarget.x, m_vecTarget.y, m_vecTarget.z );
|
||||
|
||||
SetAbsVelocity( GetAbsVelocity() * flAdj + (m_vecTarget * 300) );
|
||||
}
|
||||
|
||||
if ( GetFlags() & FL_ONGROUND )
|
||||
{
|
||||
SetLocalAngularVelocity( QAngle( 0, 0, 0 ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
QAngle angVel = GetLocalAngularVelocity();
|
||||
if ( angVel == QAngle( 0, 0, 0 ) )
|
||||
{
|
||||
angVel.x = random->RandomFloat( -100, 100 );
|
||||
angVel.z = random->RandomFloat( -100, 100 );
|
||||
SetLocalAngularVelocity( angVel );
|
||||
}
|
||||
}
|
||||
|
||||
if ( ( GetAbsOrigin() - m_posPrev ).Length() < 1.0 )
|
||||
{
|
||||
Vector vecVel = GetAbsVelocity();
|
||||
vecVel.x = random->RandomFloat( -100, 100 );
|
||||
vecVel.y = random->RandomFloat( -100, 100 );
|
||||
SetAbsVelocity( vecVel );
|
||||
}
|
||||
|
||||
m_posPrev = GetAbsOrigin();
|
||||
|
||||
QAngle angles;
|
||||
VectorAngles( GetAbsVelocity(), angles );
|
||||
angles.z = 0;
|
||||
angles.x = 0;
|
||||
SetAbsAngles( angles );
|
||||
}
|
||||
|
||||
unsigned int CSnark::PhysicsSolidMaskForEntity( void ) const
|
||||
{
|
||||
unsigned int iMask = BaseClass::PhysicsSolidMaskForEntity();
|
||||
|
||||
iMask &= ~CONTENTS_MONSTERCLIP;
|
||||
|
||||
return iMask;
|
||||
}
|
||||
|
||||
|
||||
// Custom collision that provides a good bounce when we hit walls
|
||||
// and also gives gravity velocity so the snarks fall off of edges.
|
||||
void CSnark::ResolveFlyCollisionCustom( trace_t &trace, Vector &vecVelocity )
|
||||
{
|
||||
// Get the impact surface's friction.
|
||||
float flSurfaceFriction;
|
||||
physprops->GetPhysicsProperties( trace.surface.surfaceProps, NULL, NULL, &flSurfaceFriction, NULL );
|
||||
|
||||
Vector vecAbsVelocity = GetAbsVelocity();
|
||||
|
||||
// If we hit a wall
|
||||
if ( trace.plane.normal.z <= 0.7 ) // Floor
|
||||
{
|
||||
Vector vecDir = vecAbsVelocity;
|
||||
|
||||
float speed = vecDir.Length();
|
||||
|
||||
VectorNormalize( vecDir );
|
||||
|
||||
float hitDot = DotProduct( trace.plane.normal, -vecDir );
|
||||
|
||||
Vector vReflection = 2.0f * trace.plane.normal * hitDot + vecDir;
|
||||
|
||||
SetAbsVelocity( vReflection * speed * 0.6f );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Stop if on ground.
|
||||
// Get the total velocity (player + conveyors, etc.)
|
||||
VectorAdd( vecAbsVelocity, GetBaseVelocity(), vecVelocity );
|
||||
float flSpeedSqr = DotProduct( vecVelocity, vecVelocity );
|
||||
|
||||
// Verify that we have an entity.
|
||||
CBaseEntity *pEntity = trace.m_pEnt;
|
||||
Assert( pEntity );
|
||||
|
||||
if ( vecVelocity.z < ( 800 * gpGlobals->frametime ) )
|
||||
{
|
||||
vecAbsVelocity.z = 0.0f;
|
||||
|
||||
// Recompute speedsqr based on the new absvel
|
||||
VectorAdd( vecAbsVelocity, GetBaseVelocity(), vecVelocity );
|
||||
flSpeedSqr = DotProduct( vecVelocity, vecVelocity );
|
||||
}
|
||||
SetAbsVelocity( vecAbsVelocity );
|
||||
|
||||
if ( flSpeedSqr < ( 30 * 30 ) )
|
||||
{
|
||||
if ( pEntity->IsStandable() )
|
||||
{
|
||||
SetGroundEntity( pEntity );
|
||||
}
|
||||
|
||||
// Reset velocities.
|
||||
SetAbsVelocity( vec3_origin );
|
||||
SetLocalAngularVelocity( vec3_angle );
|
||||
}
|
||||
else
|
||||
{
|
||||
vecAbsVelocity += GetBaseVelocity();
|
||||
vecAbsVelocity *= ( 1.0f - trace.fraction ) * gpGlobals->frametime * flSurfaceFriction;
|
||||
PhysicsPushEntity( vecAbsVelocity, &trace );
|
||||
}
|
||||
}
|
||||
|
||||
void CSnark::SuperBounceTouch( CBaseEntity *pOther )
|
||||
{
|
||||
float flpitch;
|
||||
trace_t tr;
|
||||
tr = CBaseEntity::GetTouchTrace( );
|
||||
|
||||
// don't hit the guy that launched this grenade
|
||||
if ( GetOwnerEntity() && ( pOther == GetOwnerEntity() ) )
|
||||
return;
|
||||
|
||||
// at least until we've bounced once
|
||||
SetOwnerEntity( NULL );
|
||||
|
||||
QAngle angles = GetAbsAngles();
|
||||
angles.x = 0;
|
||||
angles.z = 0;
|
||||
SetAbsAngles( angles );
|
||||
|
||||
// avoid bouncing too much
|
||||
if ( m_flNextHit > gpGlobals->curtime)
|
||||
return;
|
||||
|
||||
// higher pitch as squeeker gets closer to detonation time
|
||||
flpitch = 155.0 - 60.0 * ( ( m_flDie - gpGlobals->curtime ) / SQUEEK_DETONATE_DELAY );
|
||||
|
||||
if ( pOther->m_takedamage && m_flNextAttack < gpGlobals->curtime )
|
||||
{
|
||||
// attack!
|
||||
|
||||
// make sure it's me who has touched them
|
||||
if ( tr.m_pEnt == pOther )
|
||||
{
|
||||
// and it's not another squeakgrenade
|
||||
if ( tr.m_pEnt->GetModelIndex() != GetModelIndex() )
|
||||
{
|
||||
// ALERT( at_console, "hit enemy\n");
|
||||
ClearMultiDamage();
|
||||
|
||||
Vector vecForward;
|
||||
AngleVectors( GetAbsAngles(), &vecForward );
|
||||
|
||||
if ( m_hOwner != NULL )
|
||||
{
|
||||
CTakeDamageInfo info( this, m_hOwner, sk_snark_dmg_bite.GetFloat(), DMG_SLASH );
|
||||
CalculateMeleeDamageForce( &info, vecForward, tr.endpos );
|
||||
pOther->DispatchTraceAttack( info, vecForward, &tr );
|
||||
}
|
||||
else
|
||||
{
|
||||
CTakeDamageInfo info( this, this, sk_snark_dmg_bite.GetFloat(), DMG_SLASH );
|
||||
CalculateMeleeDamageForce( &info, vecForward, tr.endpos );
|
||||
pOther->DispatchTraceAttack( info, vecForward, &tr );
|
||||
}
|
||||
|
||||
ApplyMultiDamage();
|
||||
|
||||
SetDamage( GetDamage() + sk_snark_dmg_pop.GetFloat() ); // add more explosion damage
|
||||
// m_flDie += 2.0; // add more life
|
||||
|
||||
// make bite sound
|
||||
CPASAttenuationFilter filter( this );
|
||||
CSoundParameters params;
|
||||
if ( GetParametersForSound( "Snark.Deploy", params, NULL ) )
|
||||
{
|
||||
EmitSound_t ep( params );
|
||||
ep.m_nPitch = (int)flpitch;
|
||||
|
||||
EmitSound( filter, entindex(), ep );
|
||||
}
|
||||
m_flNextAttack = gpGlobals->curtime + 0.5;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// ALERT( at_console, "been hit\n");
|
||||
}
|
||||
}
|
||||
|
||||
m_flNextHit = gpGlobals->curtime + 0.1;
|
||||
m_flNextHunt = gpGlobals->curtime;
|
||||
|
||||
if ( g_pGameRules->IsMultiplayer() )
|
||||
{
|
||||
// in multiplayer, we limit how often snarks can make their bounce sounds to prevent overflows.
|
||||
if ( gpGlobals->curtime < m_flNextBounceSoundTime )
|
||||
{
|
||||
// too soon!
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !( GetFlags() & FL_ONGROUND ) )
|
||||
{
|
||||
// play bounce sound
|
||||
CPASAttenuationFilter filter2( this );
|
||||
|
||||
CSoundParameters params;
|
||||
if ( GetParametersForSound( "Snark.Bounce", params, NULL ) )
|
||||
{
|
||||
EmitSound_t ep( params );
|
||||
ep.m_nPitch = (int)flpitch;
|
||||
|
||||
EmitSound( filter2, entindex(), ep );
|
||||
}
|
||||
|
||||
CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), 256, 0.25 );
|
||||
}
|
||||
else
|
||||
{
|
||||
// skittering sound
|
||||
CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), 100, 0.1 );
|
||||
}
|
||||
|
||||
m_flNextBounceSoundTime = gpGlobals->curtime + 0.5;// half second.
|
||||
}
|
||||
|
||||
|
||||
bool CSnark::IsValidEnemy( CBaseEntity *pEnemy )
|
||||
{
|
||||
return CHL1BaseNPC::IsValidEnemy( pEnemy );
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Projectile shot from the MP5
|
||||
//
|
||||
// $Workfile: $
|
||||
// $Date: $
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// $Log: $
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
|
||||
#ifndef NPC_SNARK_H
|
||||
#define NPC_SNARK_H
|
||||
|
||||
|
||||
#include "hl1_ai_basenpc.h"
|
||||
|
||||
|
||||
class CSnark : public CHL1BaseNPC
|
||||
{
|
||||
DECLARE_CLASS( CSnark, CHL1BaseNPC );
|
||||
public:
|
||||
|
||||
DECLARE_DATADESC();
|
||||
|
||||
void Precache( void );
|
||||
void Spawn( void );
|
||||
Class_T Classify( void );
|
||||
void Event_Killed( const CTakeDamageInfo &info );
|
||||
bool Event_Gibbed( const CTakeDamageInfo &info );
|
||||
void HuntThink( void );
|
||||
void SuperBounceTouch( CBaseEntity *pOther );
|
||||
|
||||
virtual void ResolveFlyCollisionCustom( trace_t &trace, Vector &vecVelocity );
|
||||
|
||||
virtual unsigned int PhysicsSolidMaskForEntity( void ) const;
|
||||
|
||||
virtual bool ShouldGib( const CTakeDamageInfo &info ) { return false; }
|
||||
static float m_flNextBounceSoundTime;
|
||||
|
||||
virtual bool IsValidEnemy( CBaseEntity *pEnemy );
|
||||
|
||||
private:
|
||||
Class_T m_iMyClass;
|
||||
float m_flDie;
|
||||
Vector m_vecTarget;
|
||||
float m_flNextHunt;
|
||||
float m_flNextHit;
|
||||
Vector m_posPrev;
|
||||
EHANDLE m_hOwner;
|
||||
};
|
||||
|
||||
|
||||
#endif // NPC_SNARK_H
|
||||
@@ -0,0 +1,728 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
#include "cbase.h"
|
||||
#include "hl1_npc_talker.h"
|
||||
#include "scripted.h"
|
||||
#include "soundent.h"
|
||||
#include "animation.h"
|
||||
#include "entitylist.h"
|
||||
#include "ai_navigator.h"
|
||||
#include "ai_motor.h"
|
||||
#include "player.h"
|
||||
#include "vstdlib/random.h"
|
||||
#include "engine/IEngineSound.h"
|
||||
#include "npcevent.h"
|
||||
#include "ai_interactions.h"
|
||||
#include "doors.h"
|
||||
|
||||
#include "effect_dispatch_data.h"
|
||||
#include "te_effect_dispatch.h"
|
||||
#include "hl1_ai_basenpc.h"
|
||||
#include "SoundEmitterSystem/isoundemittersystembase.h"
|
||||
|
||||
ConVar hl1_debug_sentence_volume( "hl1_debug_sentence_volume", "0" );
|
||||
ConVar hl1_fixup_sentence_sndlevel( "hl1_fixup_sentence_sndlevel", "1" );
|
||||
|
||||
//#define TALKER_LOOK 0
|
||||
|
||||
BEGIN_DATADESC( CHL1NPCTalker )
|
||||
|
||||
DEFINE_ENTITYFUNC( Touch ),
|
||||
DEFINE_FIELD( m_bInBarnacleMouth, FIELD_BOOLEAN ),
|
||||
DEFINE_USEFUNC( FollowerUse ),
|
||||
|
||||
END_DATADESC()
|
||||
|
||||
void CHL1NPCTalker::RunTask( const Task_t *pTask )
|
||||
{
|
||||
switch ( pTask->iTask )
|
||||
{
|
||||
case TASK_HL1TALKER_FOLLOW_WALK_PATH_FOR_UNITS:
|
||||
{
|
||||
float distance;
|
||||
|
||||
distance = (m_vecLastPosition - GetLocalOrigin()).Length2D();
|
||||
|
||||
// Walk path until far enough away
|
||||
if ( distance > pTask->flTaskData ||
|
||||
GetNavigator()->GetGoalType() == GOALTYPE_NONE )
|
||||
{
|
||||
TaskComplete();
|
||||
GetNavigator()->ClearGoal(); // Stop moving
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
|
||||
case TASK_TALKER_CLIENT_STARE:
|
||||
case TASK_TALKER_LOOK_AT_CLIENT:
|
||||
{
|
||||
CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
|
||||
|
||||
// track head to the client for a while.
|
||||
if ( m_NPCState == NPC_STATE_IDLE &&
|
||||
!IsMoving() &&
|
||||
!GetExpresser()->IsSpeaking() )
|
||||
{
|
||||
|
||||
if ( pPlayer )
|
||||
{
|
||||
IdleHeadTurn( pPlayer );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// started moving or talking
|
||||
TaskFail( "moved away" );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( pTask->iTask == TASK_TALKER_CLIENT_STARE )
|
||||
{
|
||||
// fail out if the player looks away or moves away.
|
||||
if ( ( pPlayer->GetAbsOrigin() - GetAbsOrigin() ).Length2D() > TALKER_STARE_DIST )
|
||||
{
|
||||
// player moved away.
|
||||
TaskFail( NO_TASK_FAILURE );
|
||||
}
|
||||
|
||||
Vector vForward;
|
||||
AngleVectors( GetAbsAngles(), &vForward );
|
||||
if ( UTIL_DotPoints( pPlayer->GetAbsOrigin(), GetAbsOrigin(), vForward ) < m_flFieldOfView )
|
||||
{
|
||||
// player looked away
|
||||
TaskFail( "looked away" );
|
||||
}
|
||||
}
|
||||
|
||||
if ( gpGlobals->curtime > m_flWaitFinished )
|
||||
{
|
||||
TaskComplete( NO_TASK_FAILURE );
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case TASK_WAIT_FOR_MOVEMENT:
|
||||
{
|
||||
if ( GetExpresser()->IsSpeaking() && GetSpeechTarget() != NULL)
|
||||
{
|
||||
// ALERT(at_console, "walking, talking\n");
|
||||
IdleHeadTurn( GetSpeechTarget(), GetExpresser()->GetTimeSpeechComplete() - gpGlobals->curtime );
|
||||
}
|
||||
else if ( GetEnemy() )
|
||||
{
|
||||
IdleHeadTurn( GetEnemy() );
|
||||
}
|
||||
|
||||
BaseClass::RunTask( pTask );
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case TASK_FACE_PLAYER:
|
||||
{
|
||||
CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
|
||||
|
||||
if ( pPlayer )
|
||||
{
|
||||
//GetMotor()->SetIdealYaw( pPlayer->GetAbsOrigin() );
|
||||
IdleHeadTurn( pPlayer );
|
||||
if ( gpGlobals->curtime > m_flWaitFinished && GetMotor()->DeltaIdealYaw() < 10 )
|
||||
{
|
||||
TaskComplete();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TaskFail( FAIL_NO_PLAYER );
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case TASK_TALKER_EYECONTACT:
|
||||
{
|
||||
if (!IsMoving() && GetExpresser()->IsSpeaking() && GetSpeechTarget() != NULL)
|
||||
{
|
||||
// ALERT( at_console, "waiting %f\n", m_flStopTalkTime - gpGlobals->time );
|
||||
IdleHeadTurn( GetSpeechTarget(), GetExpresser()->GetTimeSpeechComplete() - gpGlobals->curtime );
|
||||
}
|
||||
|
||||
BaseClass::RunTask( pTask );
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
|
||||
default:
|
||||
{
|
||||
if ( GetExpresser()->IsSpeaking() && GetSpeechTarget() != NULL)
|
||||
{
|
||||
IdleHeadTurn( GetSpeechTarget(), GetExpresser()->GetTimeSpeechComplete() - gpGlobals->curtime );
|
||||
}
|
||||
else if ( GetEnemy() && m_NPCState == NPC_STATE_COMBAT )
|
||||
{
|
||||
IdleHeadTurn( GetEnemy() );
|
||||
}
|
||||
else if ( GetFollowTarget() )
|
||||
{
|
||||
IdleHeadTurn( GetFollowTarget() );
|
||||
}
|
||||
|
||||
BaseClass::RunTask( pTask );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CHL1NPCTalker::ShouldGib( const CTakeDamageInfo &info )
|
||||
{
|
||||
if ( info.GetDamageType() & DMG_NEVERGIB )
|
||||
return false;
|
||||
|
||||
if ( ( g_pGameRules->Damage_ShouldGibCorpse( info.GetDamageType() ) && m_iHealth < GIB_HEALTH_VALUE ) || ( info.GetDamageType() & DMG_ALWAYSGIB ) )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
void CHL1NPCTalker::StartTask( const Task_t *pTask )
|
||||
{
|
||||
switch( pTask->iTask )
|
||||
{
|
||||
case TASK_HL1TALKER_FOLLOW_WALK_PATH_FOR_UNITS:
|
||||
{
|
||||
GetNavigator()->SetMovementActivity( ACT_WALK );
|
||||
break;
|
||||
}
|
||||
case TASK_TALKER_SPEAK:
|
||||
// ask question or make statement
|
||||
FIdleSpeak();
|
||||
TaskComplete();
|
||||
break;
|
||||
default:
|
||||
BaseClass::StartTask( pTask );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// FIdleSpeak
|
||||
// ask question of nearby friend, or make statement
|
||||
//=========================================================
|
||||
int CHL1NPCTalker::FIdleSpeak ( void )
|
||||
{
|
||||
if (!IsOkToSpeak())
|
||||
return FALSE;
|
||||
|
||||
// if there is a friend nearby to speak to, play sentence, set friend's response time, return
|
||||
// try to talk to any standing or sitting scientists nearby
|
||||
CBaseEntity *pentFriend = FindNearestFriend( false );
|
||||
CHL1NPCTalker *pentTalker = dynamic_cast<CHL1NPCTalker *>( pentFriend );
|
||||
if (pentTalker && random->RandomInt(0,1) )
|
||||
{
|
||||
Speak( TLK_QUESTION );
|
||||
SetSpeechTarget( pentFriend );
|
||||
|
||||
pentTalker->SetSpeechTarget( this );
|
||||
pentTalker->SetCondition( COND_TALKER_RESPOND_TO_QUESTION );
|
||||
pentTalker->SetSchedule( SCHED_TALKER_IDLE_RESPONSE );
|
||||
pentTalker->GetExpresser()->BlockSpeechUntil( GetExpresser()->GetTimeSpeechComplete() );
|
||||
|
||||
GetExpresser()->BlockSpeechUntil( gpGlobals->curtime + random->RandomFloat(4.8, 5.2) );
|
||||
|
||||
//DevMsg( "Asking some question!\n" );
|
||||
return TRUE;
|
||||
}
|
||||
else if ( random->RandomInt(0,1)) // otherwise, play an idle statement
|
||||
{
|
||||
//DevMsg( "Making idle statement!\n" );
|
||||
|
||||
Speak( TLK_IDLE );
|
||||
// set global min delay for next conversation
|
||||
GetExpresser()->BlockSpeechUntil( gpGlobals->curtime + random->RandomFloat(4.8, 5.2) );
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// never spoke
|
||||
GetExpresser()->BlockSpeechUntil( 0 );
|
||||
m_flNextIdleSpeechTime = gpGlobals->curtime + 3;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool CHL1NPCTalker::IsValidSpeechTarget( int flags, CBaseEntity *pEntity )
|
||||
{
|
||||
if ( pEntity == this )
|
||||
return false;
|
||||
|
||||
CHL1NPCTalker *pentTarget = dynamic_cast<CHL1NPCTalker *>( pEntity );
|
||||
if ( pentTarget )
|
||||
{
|
||||
if ( !(flags & AIST_IGNORE_RELATIONSHIP) )
|
||||
{
|
||||
if ( pEntity->IsPlayer() )
|
||||
{
|
||||
if ( !IsPlayerAlly( (CBasePlayer *)pEntity ) )
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( IRelationType( pEntity ) != D_LI )
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !pEntity->IsAlive() )
|
||||
// don't dead people
|
||||
return false;
|
||||
|
||||
// Ignore no-target entities
|
||||
if ( pEntity->GetFlags() & FL_NOTARGET )
|
||||
return false;
|
||||
|
||||
CAI_BaseNPC *pNPC = pEntity->MyNPCPointer();
|
||||
if ( pNPC )
|
||||
{
|
||||
// If not a NPC for some reason, or in a script.
|
||||
//if ( (pNPC->m_NPCState == NPC_STATE_SCRIPT || pNPC->m_NPCState == NPC_STATE_PRONE))
|
||||
// return false;
|
||||
|
||||
if ( pNPC->IsInAScript() )
|
||||
return false;
|
||||
|
||||
// Don't bother people who don't want to be bothered
|
||||
if ( !pNPC->CanBeUsedAsAFriend() )
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( flags & AIST_FACING_TARGET )
|
||||
{
|
||||
if ( pEntity->IsPlayer() )
|
||||
return HasCondition( COND_SEE_PLAYER );
|
||||
else if ( !FInViewCone( pEntity ) )
|
||||
return false;
|
||||
}
|
||||
|
||||
return FVisible( pEntity );
|
||||
}
|
||||
else
|
||||
return BaseClass::IsValidSpeechTarget( flags, pEntity );
|
||||
}
|
||||
|
||||
|
||||
int CHL1NPCTalker::SelectSchedule ( void )
|
||||
{
|
||||
switch( m_NPCState )
|
||||
{
|
||||
case NPC_STATE_PRONE:
|
||||
{
|
||||
if (m_bInBarnacleMouth)
|
||||
{
|
||||
return SCHED_HL1TALKER_BARNACLE_CHOMP;
|
||||
}
|
||||
else
|
||||
{
|
||||
return SCHED_HL1TALKER_BARNACLE_HIT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return BaseClass::SelectSchedule();
|
||||
}
|
||||
|
||||
void CHL1NPCTalker::Precache()
|
||||
{
|
||||
BaseClass::Precache();
|
||||
|
||||
PrecacheScriptSound( "Barney.Close" );
|
||||
}
|
||||
|
||||
bool CHL1NPCTalker::HandleInteraction(int interactionType, void *data, CBaseCombatCharacter* sourceEnt)
|
||||
{
|
||||
if (interactionType == g_interactionBarnacleVictimDangle)
|
||||
{
|
||||
// Force choosing of a new schedule
|
||||
ClearSchedule( "NPC talker being eaten by a barnacle" );
|
||||
m_bInBarnacleMouth = true;
|
||||
return true;
|
||||
}
|
||||
else if ( interactionType == g_interactionBarnacleVictimReleased )
|
||||
{
|
||||
SetState ( NPC_STATE_IDLE );
|
||||
|
||||
CPASAttenuationFilter filter( this );
|
||||
|
||||
CSoundParameters params;
|
||||
|
||||
if ( GetParametersForSound( "Barney.Close", params, NULL ) )
|
||||
{
|
||||
EmitSound_t ep( params );
|
||||
ep.m_nPitch = GetExpresser()->GetVoicePitch();
|
||||
|
||||
EmitSound( filter, entindex(), ep );
|
||||
}
|
||||
|
||||
m_bInBarnacleMouth = false;
|
||||
SetAbsVelocity( vec3_origin );
|
||||
SetMoveType( MOVETYPE_STEP );
|
||||
return true;
|
||||
}
|
||||
else if ( interactionType == g_interactionBarnacleVictimGrab )
|
||||
{
|
||||
if ( GetFlags() & FL_ONGROUND )
|
||||
{
|
||||
SetGroundEntity( NULL );
|
||||
}
|
||||
|
||||
if ( GetState() == NPC_STATE_SCRIPT )
|
||||
{
|
||||
if ( m_hCine )
|
||||
{
|
||||
m_hCine->CancelScript();
|
||||
}
|
||||
}
|
||||
|
||||
SetState( NPC_STATE_PRONE );
|
||||
ClearSchedule( "NPC talker grabbed by a barnacle" );
|
||||
|
||||
CTakeDamageInfo info;
|
||||
PainSound( info );
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CHL1NPCTalker::StartFollowing( CBaseEntity *pLeader )
|
||||
{
|
||||
if ( !HasSpawnFlags( SF_NPC_GAG ) )
|
||||
{
|
||||
if ( m_iszUse != NULL_STRING )
|
||||
{
|
||||
PlaySentence( STRING( m_iszUse ), 0.0f );
|
||||
}
|
||||
else
|
||||
{
|
||||
Speak( TLK_STARTFOLLOW );
|
||||
}
|
||||
|
||||
SetSpeechTarget( pLeader );
|
||||
}
|
||||
|
||||
BaseClass::StartFollowing( pLeader );
|
||||
}
|
||||
|
||||
int CHL1NPCTalker::PlayScriptedSentence( const char *pszSentence, float delay, float volume, soundlevel_t soundlevel, bool bConcurrent, CBaseEntity *pListener )
|
||||
{
|
||||
if( hl1_debug_sentence_volume.GetBool() )
|
||||
{
|
||||
Msg( "SENTENCE: %s Vol:%f SndLevel:%d\n", GetDebugName(), volume, soundlevel );
|
||||
}
|
||||
|
||||
if( hl1_fixup_sentence_sndlevel.GetBool() )
|
||||
{
|
||||
if( soundlevel < SNDLVL_TALKING )
|
||||
{
|
||||
soundlevel = SNDLVL_TALKING;
|
||||
}
|
||||
}
|
||||
|
||||
return BaseClass::PlayScriptedSentence( pszSentence, delay, volume, soundlevel, bConcurrent, pListener );
|
||||
}
|
||||
|
||||
Disposition_t CHL1NPCTalker::IRelationType( CBaseEntity *pTarget )
|
||||
{
|
||||
if ( pTarget->IsPlayer() )
|
||||
{
|
||||
if ( HasMemory( bits_MEMORY_PROVOKED ) )
|
||||
{
|
||||
return D_HT;
|
||||
}
|
||||
}
|
||||
|
||||
return BaseClass::IRelationType( pTarget );
|
||||
}
|
||||
|
||||
void CHL1NPCTalker::Touch( CBaseEntity *pOther )
|
||||
{
|
||||
if ( m_NPCState == NPC_STATE_SCRIPT )
|
||||
return;
|
||||
|
||||
BaseClass::Touch(pOther);
|
||||
}
|
||||
|
||||
void CHL1NPCTalker::StopFollowing( void )
|
||||
{
|
||||
if ( !(m_afMemory & bits_MEMORY_PROVOKED) )
|
||||
{
|
||||
if ( !HasSpawnFlags( SF_NPC_GAG ) )
|
||||
{
|
||||
if ( m_iszUnUse != NULL_STRING )
|
||||
{
|
||||
PlaySentence( STRING( m_iszUnUse ), 0.0f );
|
||||
}
|
||||
else
|
||||
{
|
||||
Speak( TLK_STOPFOLLOW );
|
||||
}
|
||||
|
||||
SetSpeechTarget( GetFollowTarget() );
|
||||
}
|
||||
}
|
||||
|
||||
BaseClass::StopFollowing();
|
||||
}
|
||||
|
||||
void CHL1NPCTalker::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
|
||||
{
|
||||
if ( info.GetDamage() >= 1.0 && !(info.GetDamageType() & DMG_SHOCK ) )
|
||||
{
|
||||
UTIL_BloodImpact( ptr->endpos, vecDir, BloodColor(), 4 );
|
||||
}
|
||||
|
||||
BaseClass::TraceAttack( info, vecDir, ptr, pAccumulator );
|
||||
}
|
||||
|
||||
void CHL1NPCTalker::FollowerUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
// Don't allow use during a scripted_sentence
|
||||
if ( GetUseTime() > gpGlobals->curtime )
|
||||
return;
|
||||
|
||||
if ( m_hCine && !m_hCine->CanInterrupt() )
|
||||
return;
|
||||
|
||||
if ( pCaller != NULL && pCaller->IsPlayer() )
|
||||
{
|
||||
// Pre-disaster followers can't be used
|
||||
if ( m_spawnflags & SF_NPC_PREDISASTER )
|
||||
{
|
||||
SetSpeechTarget( pCaller );
|
||||
DeclineFollowing();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
BaseClass::FollowerUse( pActivator, pCaller, useType, value );
|
||||
}
|
||||
|
||||
int CHL1NPCTalker::TranslateSchedule( int scheduleType )
|
||||
{
|
||||
return BaseClass::TranslateSchedule( scheduleType );
|
||||
}
|
||||
|
||||
float CHL1NPCTalker::PickLookTarget( bool bExcludePlayers, float minTime, float maxTime )
|
||||
{
|
||||
return random->RandomFloat( 5.0f, 10.0f );
|
||||
}
|
||||
|
||||
void CHL1NPCTalker::IdleHeadTurn( CBaseEntity *pTarget, float flDuration, float flImportance )
|
||||
{
|
||||
// Must be able to turn our head
|
||||
if (!(CapabilitiesGet() & bits_CAP_TURN_HEAD))
|
||||
return;
|
||||
|
||||
// If the target is invalid, or we're in a script, do nothing
|
||||
if ( ( !pTarget ) || ( m_NPCState == NPC_STATE_SCRIPT ) )
|
||||
return;
|
||||
|
||||
// Fill in a duration if we haven't specified one
|
||||
if ( flDuration == 0.0f )
|
||||
{
|
||||
flDuration = random->RandomFloat( 2.0, 4.0 );
|
||||
}
|
||||
|
||||
// Add a look target
|
||||
AddLookTarget( pTarget, 1.0, flDuration );
|
||||
}
|
||||
|
||||
void CHL1NPCTalker::SetHeadDirection( const Vector &vTargetPos, float flInterval)
|
||||
{
|
||||
#ifdef TALKER_LOOK
|
||||
// Draw line in body, head, and eye directions
|
||||
Vector vEyePos = EyePosition();
|
||||
Vector vHeadDir = HeadDirection3D();
|
||||
Vector vBodyDir = BodyDirection2D();
|
||||
|
||||
//UNDONE <<TODO>>
|
||||
// currently eye dir just returns head dir, so use vTargetPos for now
|
||||
//Vector vEyeDir; w
|
||||
//EyeDirection3D(&vEyeDir);
|
||||
NDebugOverlay::Line( vEyePos, vEyePos+(50*vHeadDir), 255, 0, 0, false, 0.1 );
|
||||
NDebugOverlay::Line( vEyePos, vEyePos+(50*vBodyDir), 0, 255, 0, false, 0.1 );
|
||||
NDebugOverlay::Line( vEyePos, vTargetPos, 0, 0, 255, false, 0.1 );
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Output : Returns true on success, false on failure.
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CHL1NPCTalker::CorpseGib( const CTakeDamageInfo &info )
|
||||
{
|
||||
CEffectData data;
|
||||
|
||||
data.m_vOrigin = WorldSpaceCenter();
|
||||
data.m_vNormal = data.m_vOrigin - info.GetDamagePosition();
|
||||
VectorNormalize( data.m_vNormal );
|
||||
|
||||
data.m_flScale = RemapVal( m_iHealth, 0, -500, 1, 3 );
|
||||
data.m_flScale = clamp( data.m_flScale, 1, 3 );
|
||||
|
||||
data.m_nMaterial = 1;
|
||||
data.m_nHitBox = -m_iHealth;
|
||||
|
||||
data.m_nColor = BloodColor();
|
||||
|
||||
DispatchEffect( "HL1Gib", data );
|
||||
|
||||
CSoundEnt::InsertSound( SOUND_MEAT, GetAbsOrigin(), 256, 0.5f, this );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CHL1NPCTalker::OnObstructingDoor( AILocalMoveGoal_t *pMoveGoal, CBaseDoor *pDoor, float distClear, AIMoveResult_t *pResult )
|
||||
{
|
||||
// If we can't get through the door, try and open it
|
||||
if ( BaseClass::OnObstructingDoor( pMoveGoal, pDoor, distClear, pResult ) )
|
||||
{
|
||||
if ( IsMoveBlocked( *pResult ) && pMoveGoal->directTrace.vHitNormal != vec3_origin )
|
||||
{
|
||||
// Can't do anything if the door's locked
|
||||
if ( !pDoor->m_bLocked && !pDoor->HasSpawnFlags(SF_DOOR_NONPCS) )
|
||||
{
|
||||
// Tell the door to open
|
||||
variant_t emptyVariant;
|
||||
pDoor->AcceptInput( "Open", this, this, emptyVariant, USE_TOGGLE );
|
||||
*pResult = AIMR_OK;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// HL1 version - never return Ragdoll as the automatic schedule at the end of a
|
||||
// scripted sequence
|
||||
int CHL1NPCTalker::SelectDeadSchedule()
|
||||
{
|
||||
// Alread dead (by animation event maybe?)
|
||||
// Is it safe to set it to SCHED_NONE?
|
||||
if ( m_lifeState == LIFE_DEAD )
|
||||
return SCHED_NONE;
|
||||
|
||||
CleanupOnDeath();
|
||||
return SCHED_DIE;
|
||||
}
|
||||
|
||||
|
||||
AI_BEGIN_CUSTOM_NPC( monster_hl1talker, CHL1NPCTalker )
|
||||
|
||||
DECLARE_TASK( TASK_HL1TALKER_FOLLOW_WALK_PATH_FOR_UNITS )
|
||||
|
||||
//=========================================================
|
||||
// > SCHED_HL1TALKER_MOVE_AWAY_FOLLOW
|
||||
//=========================================================
|
||||
DEFINE_SCHEDULE
|
||||
(
|
||||
SCHED_HL1TALKER_FOLLOW_MOVE_AWAY,
|
||||
|
||||
" Tasks"
|
||||
" TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_TARGET_FACE"
|
||||
" TASK_STORE_LASTPOSITION 0"
|
||||
" TASK_MOVE_AWAY_PATH 100"
|
||||
" TASK_HL1TALKER_FOLLOW_WALK_PATH_FOR_UNITS 100"
|
||||
" TASK_STOP_MOVING 0"
|
||||
" TASK_FACE_PLAYER 0"
|
||||
" TASK_SET_ACTIVITY ACT_IDLE"
|
||||
""
|
||||
" Interrupts"
|
||||
)
|
||||
|
||||
//=========================================================
|
||||
// > SCHED_HL1TALKER_IDLE_SPEAK_WAIT
|
||||
//=========================================================
|
||||
DEFINE_SCHEDULE
|
||||
(
|
||||
SCHED_HL1TALKER_IDLE_SPEAK_WAIT,
|
||||
|
||||
" Tasks"
|
||||
" TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE" // Stop and talk
|
||||
" TASK_FACE_PLAYER 0"
|
||||
""
|
||||
" Interrupts"
|
||||
" COND_NEW_ENEMY"
|
||||
" COND_LIGHT_DAMAGE"
|
||||
" COND_HEAVY_DAMAGE"
|
||||
" COND_HEAR_DANGER"
|
||||
)
|
||||
|
||||
//=========================================================
|
||||
// > SCHED_HL1TALKER_BARNACLE_HIT
|
||||
//=========================================================
|
||||
DEFINE_SCHEDULE
|
||||
(
|
||||
SCHED_HL1TALKER_BARNACLE_HIT,
|
||||
|
||||
" Tasks"
|
||||
" TASK_STOP_MOVING 0"
|
||||
" TASK_PLAY_SEQUENCE ACTIVITY:ACT_BARNACLE_HIT"
|
||||
" TASK_SET_SCHEDULE SCHEDULE:SCHED_HL1TALKER_BARNACLE_PULL"
|
||||
""
|
||||
" Interrupts"
|
||||
)
|
||||
|
||||
//=========================================================
|
||||
// > SCHED_HL1TALKER_BARNACLE_PULL
|
||||
//=========================================================
|
||||
DEFINE_SCHEDULE
|
||||
(
|
||||
SCHED_HL1TALKER_BARNACLE_PULL,
|
||||
|
||||
" Tasks"
|
||||
" TASK_PLAY_SEQUENCE ACTIVITY:ACT_BARNACLE_PULL"
|
||||
""
|
||||
" Interrupts"
|
||||
)
|
||||
|
||||
//=========================================================
|
||||
// > SCHED_HL1TALKER_BARNACLE_CHOMP
|
||||
//=========================================================
|
||||
DEFINE_SCHEDULE
|
||||
(
|
||||
SCHED_HL1TALKER_BARNACLE_CHOMP,
|
||||
|
||||
" Tasks"
|
||||
" TASK_PLAY_SEQUENCE ACTIVITY:ACT_BARNACLE_CHOMP"
|
||||
" TASK_SET_SCHEDULE SCHEDULE:SCHED_HL1TALKER_BARNACLE_CHEW"
|
||||
""
|
||||
" Interrupts"
|
||||
)
|
||||
|
||||
//=========================================================
|
||||
// > SCHED_HL1TALKER_BARNACLE_CHEW
|
||||
//=========================================================
|
||||
DEFINE_SCHEDULE
|
||||
(
|
||||
SCHED_HL1TALKER_BARNACLE_CHEW,
|
||||
|
||||
" Tasks"
|
||||
" TASK_PLAY_SEQUENCE ACTIVITY:ACT_BARNACLE_CHEW"
|
||||
)
|
||||
|
||||
AI_END_CUSTOM_NPC()
|
||||
@@ -0,0 +1,125 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Base combat character with no AI
|
||||
//
|
||||
// $Workfile: $
|
||||
// $Date: $
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef HL1TALKNPC_H
|
||||
#define HL1TALKNPC_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "soundflags.h"
|
||||
|
||||
#include "ai_task.h"
|
||||
#include "ai_schedule.h"
|
||||
#include "ai_default.h"
|
||||
#include "ai_speech.h"
|
||||
#include "ai_basenpc.h"
|
||||
#include "ai_behavior.h"
|
||||
#include "ai_behavior_follow.h"
|
||||
#include "npc_talker.h"
|
||||
|
||||
|
||||
#define SF_NPC_PREDISASTER ( 1 << 16 ) // This is a predisaster scientist or barney. Influences how they speak.
|
||||
|
||||
|
||||
|
||||
//=========================================================
|
||||
// Talking NPC base class
|
||||
// Used for scientists and barneys
|
||||
//=========================================================
|
||||
|
||||
//=============================================================================
|
||||
// >> CHL1NPCTalker
|
||||
//=============================================================================
|
||||
|
||||
class CHL1NPCTalker : public CNPCSimpleTalker
|
||||
{
|
||||
DECLARE_CLASS( CHL1NPCTalker, CNPCSimpleTalker );
|
||||
|
||||
public:
|
||||
CHL1NPCTalker( void )
|
||||
{
|
||||
}
|
||||
|
||||
virtual void Precache();
|
||||
|
||||
void StartTask( const Task_t *pTask );
|
||||
void RunTask( const Task_t *pTask );
|
||||
int SelectSchedule ( void );
|
||||
bool HandleInteraction(int interactionType, void *data, CBaseCombatCharacter* sourceEnt);
|
||||
bool ShouldGib( const CTakeDamageInfo &info );
|
||||
|
||||
int TranslateSchedule( int scheduleType );
|
||||
void IdleHeadTurn( CBaseEntity *pTarget, float flDuration = 0.0, float flImportance = 1.0f );
|
||||
void SetHeadDirection( const Vector &vTargetPos, float flInterval);
|
||||
bool CorpseGib( const CTakeDamageInfo &info );
|
||||
|
||||
Disposition_t IRelationType( CBaseEntity *pTarget );
|
||||
|
||||
void TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator );
|
||||
|
||||
void StartFollowing( CBaseEntity *pLeader );
|
||||
void StopFollowing( void );
|
||||
int PlayScriptedSentence( const char *pszSentence, float delay, float volume, soundlevel_t soundlevel, bool bConcurrent, CBaseEntity *pListener );
|
||||
|
||||
|
||||
void Touch( CBaseEntity *pOther );
|
||||
|
||||
float PickLookTarget( bool bExcludePlayers = false, float minTime = 1.5, float maxTime = 2.5 );
|
||||
|
||||
bool OnObstructingDoor( AILocalMoveGoal_t *pMoveGoal, CBaseDoor *pDoor, float distClear, AIMoveResult_t *pResult );
|
||||
|
||||
// Hacks! HL2 has a system for avoiding the player, we don't
|
||||
// This ensures that we fall back to the real player avoidance
|
||||
// Essentially does the opposite of what it says
|
||||
virtual bool ShouldPlayerAvoid( void ) { return false; }
|
||||
|
||||
bool IsValidSpeechTarget( int flags, CBaseEntity *pEntity );
|
||||
|
||||
protected:
|
||||
virtual void FollowerUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
int FIdleSpeak ( void );
|
||||
|
||||
private:
|
||||
virtual void DeclineFollowing( void ) {}
|
||||
|
||||
virtual int SelectDeadSchedule( void );
|
||||
|
||||
public:
|
||||
|
||||
bool m_bInBarnacleMouth;
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
SCHED_HL1TALKER_FOLLOW_MOVE_AWAY = BaseClass::NEXT_SCHEDULE,
|
||||
SCHED_HL1TALKER_IDLE_SPEAK_WAIT,
|
||||
SCHED_HL1TALKER_BARNACLE_HIT,
|
||||
SCHED_HL1TALKER_BARNACLE_PULL,
|
||||
SCHED_HL1TALKER_BARNACLE_CHOMP,
|
||||
SCHED_HL1TALKER_BARNACLE_CHEW,
|
||||
|
||||
NEXT_SCHEDULE,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
TASK_HL1TALKER_FOLLOW_WALK_PATH_FOR_UNITS = BaseClass::NEXT_TASK,
|
||||
|
||||
NEXT_TASK,
|
||||
};
|
||||
|
||||
DECLARE_DATADESC();
|
||||
DEFINE_CUSTOM_AI;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif //HL1TALKNPC_H
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,743 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Alien slave monster
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "beam_shared.h"
|
||||
#include "game.h"
|
||||
#include "ai_default.h"
|
||||
#include "ai_schedule.h"
|
||||
#include "ai_hull.h"
|
||||
#include "ai_route.h"
|
||||
#include "ai_squad.h"
|
||||
#include "npcevent.h"
|
||||
#include "gib.h"
|
||||
//#include "AI_Interactions.h"
|
||||
#include "ndebugoverlay.h"
|
||||
#include "vstdlib/random.h"
|
||||
#include "engine/IEngineSound.h"
|
||||
#include "hl1_npc_vortigaunt.h"
|
||||
#include "soundent.h"
|
||||
#include "player.h"
|
||||
#include "IEffects.h"
|
||||
#include "basecombatweapon.h"
|
||||
#include "SoundEmitterSystem/isoundemittersystembase.h"
|
||||
|
||||
//=========================================================
|
||||
// Monster's Anim Events Go Here
|
||||
//=========================================================
|
||||
#define ISLAVE_AE_CLAW ( 1 )
|
||||
#define ISLAVE_AE_CLAWRAKE ( 2 )
|
||||
#define ISLAVE_AE_ZAP_POWERUP ( 3 )
|
||||
#define ISLAVE_AE_ZAP_SHOOT ( 4 )
|
||||
#define ISLAVE_AE_ZAP_DONE ( 5 )
|
||||
|
||||
|
||||
ConVar sk_islave_health( "sk_islave_health","50");
|
||||
ConVar sk_islave_dmg_claw( "sk_islave_dmg_claw","8");
|
||||
ConVar sk_islave_dmg_clawrake( "sk_islave_dmg_clawrake","25");
|
||||
ConVar sk_islave_dmg_zap( "sk_islave_dmg_zap","15");
|
||||
|
||||
LINK_ENTITY_TO_CLASS( monster_alien_slave, CNPC_Vortigaunt );
|
||||
|
||||
BEGIN_DATADESC( CNPC_Vortigaunt )
|
||||
DEFINE_FIELD( m_iBravery, FIELD_INTEGER ),
|
||||
DEFINE_ARRAY( m_pBeam, FIELD_CLASSPTR, VORTIGAUNT_MAX_BEAMS ),
|
||||
DEFINE_FIELD( m_iBeams, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( m_flNextAttack, FIELD_TIME ),
|
||||
DEFINE_FIELD( m_iVoicePitch, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( m_hDead, FIELD_EHANDLE ),
|
||||
END_DATADESC()
|
||||
|
||||
enum
|
||||
{
|
||||
SCHED_VORTIGAUNT_ATTACK = LAST_SHARED_SCHEDULE,
|
||||
};
|
||||
|
||||
#define VORTIGAUNT_IGNORE_PLAYER 64
|
||||
|
||||
//=========================================================
|
||||
// Spawn
|
||||
//=========================================================
|
||||
void CNPC_Vortigaunt::Spawn()
|
||||
{
|
||||
Precache( );
|
||||
|
||||
SetModel( "models/islave.mdl" );
|
||||
|
||||
SetRenderColor( 255, 255, 255, 255 );
|
||||
|
||||
SetHullType(HULL_HUMAN);
|
||||
SetHullSizeNormal();
|
||||
|
||||
SetSolid( SOLID_BBOX );
|
||||
AddSolidFlags( FSOLID_NOT_STANDABLE );
|
||||
SetMoveType( MOVETYPE_STEP );
|
||||
m_bloodColor = BLOOD_COLOR_GREEN;
|
||||
ClearEffects();
|
||||
m_iHealth = sk_islave_health.GetFloat();
|
||||
//pev->view_ofs = VEC_VIEW;// position of the eyes relative to monster's origin.
|
||||
m_flFieldOfView = VIEW_FIELD_WIDE;
|
||||
m_NPCState = NPC_STATE_NONE;
|
||||
|
||||
m_iVoicePitch = random->RandomInt( 85, 110 );
|
||||
|
||||
CapabilitiesClear();
|
||||
CapabilitiesAdd( bits_CAP_MOVE_GROUND );
|
||||
CapabilitiesAdd( bits_CAP_SQUAD );
|
||||
|
||||
CapabilitiesAdd( bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP );
|
||||
|
||||
CapabilitiesAdd ( bits_CAP_INNATE_RANGE_ATTACK1 );
|
||||
CapabilitiesAdd ( bits_CAP_INNATE_MELEE_ATTACK1 );
|
||||
|
||||
m_iBravery = 0;
|
||||
|
||||
NPCInit();
|
||||
|
||||
BaseClass::Spawn();
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Precache - precaches all resources this monster needs
|
||||
//=========================================================
|
||||
void CNPC_Vortigaunt::Precache()
|
||||
{
|
||||
BaseClass::Precache();
|
||||
|
||||
PrecacheModel("models/islave.mdl");
|
||||
PrecacheModel("sprites/lgtning.vmt");
|
||||
|
||||
PrecacheScriptSound( "Vortigaunt.Pain" );
|
||||
PrecacheScriptSound( "Vortigaunt.Die" );
|
||||
PrecacheScriptSound( "Vortigaunt.AttackHit" );
|
||||
PrecacheScriptSound( "Vortigaunt.AttackMiss" );
|
||||
PrecacheScriptSound( "Vortigaunt.ZapPowerup" );
|
||||
PrecacheScriptSound( "Vortigaunt.ZapShoot" );
|
||||
}
|
||||
|
||||
Disposition_t CNPC_Vortigaunt::IRelationType ( CBaseEntity *pTarget )
|
||||
{
|
||||
if ( (pTarget->IsPlayer()) )
|
||||
{
|
||||
if ( (GetSpawnFlags() & VORTIGAUNT_IGNORE_PLAYER ) && !HasMemory( bits_MEMORY_PROVOKED ) )
|
||||
return D_NU;
|
||||
}
|
||||
|
||||
return BaseClass::IRelationType( pTarget );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input :
|
||||
// Output :
|
||||
//-----------------------------------------------------------------------------
|
||||
Class_T CNPC_Vortigaunt::Classify ( void )
|
||||
{
|
||||
return CLASS_ALIEN_MILITARY;
|
||||
}
|
||||
|
||||
void CNPC_Vortigaunt::CallForHelp( char *szClassname, float flDist, CBaseEntity * pEnemy, Vector &vecLocation )
|
||||
{
|
||||
// ALERT( at_aiconsole, "help " );
|
||||
|
||||
// skip ones not on my netname
|
||||
if ( !m_pSquad )
|
||||
return;
|
||||
|
||||
AISquadIter_t iter;
|
||||
for (CAI_BaseNPC *pSquadMember = m_pSquad->GetFirstMember( &iter ); pSquadMember; pSquadMember = m_pSquad->GetNextMember( &iter ) )
|
||||
{
|
||||
float d = ( GetAbsOrigin() - pSquadMember->GetAbsOrigin() ).Length();
|
||||
|
||||
if ( d < flDist )
|
||||
{
|
||||
pSquadMember->Remember( bits_MEMORY_PROVOKED );
|
||||
pSquadMember->UpdateEnemyMemory( pEnemy, vecLocation );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// ALertSound - scream
|
||||
//=========================================================
|
||||
void CNPC_Vortigaunt::AlertSound( void )
|
||||
{
|
||||
if ( GetEnemy() != NULL )
|
||||
{
|
||||
SENTENCEG_PlayRndSz( edict(), "SLV_ALERT", 0.85, SNDLVL_NORM, 0, m_iVoicePitch );
|
||||
|
||||
Vector vecTmp = GetEnemy()->GetAbsOrigin();
|
||||
CallForHelp( "monster_alien_slave", 512, GetEnemy(), vecTmp );
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// IdleSound
|
||||
//=========================================================
|
||||
void CNPC_Vortigaunt::IdleSound( void )
|
||||
{
|
||||
if ( random->RandomInt( 0, 2 ) == 0)
|
||||
SENTENCEG_PlayRndSz( edict(), "SLV_IDLE", 0.85, SNDLVL_NORM, 0, m_iVoicePitch);
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// PainSound
|
||||
//=========================================================
|
||||
void CNPC_Vortigaunt::PainSound( const CTakeDamageInfo &info )
|
||||
{
|
||||
if ( random->RandomInt( 0, 2 ) == 0)
|
||||
{
|
||||
CPASAttenuationFilter filter( this );
|
||||
|
||||
CSoundParameters params;
|
||||
if ( GetParametersForSound( "Vortigaunt.Pain", params, NULL ) )
|
||||
{
|
||||
EmitSound_t ep( params );
|
||||
params.pitch = m_iVoicePitch;
|
||||
|
||||
EmitSound( filter, entindex(), ep );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// DieSound
|
||||
//=========================================================
|
||||
|
||||
void CNPC_Vortigaunt::DeathSound( const CTakeDamageInfo &info )
|
||||
{
|
||||
CPASAttenuationFilter filter( this );
|
||||
CSoundParameters params;
|
||||
if ( GetParametersForSound( "Vortigaunt.Die", params, NULL ) )
|
||||
{
|
||||
EmitSound_t ep( params );
|
||||
params.pitch = m_iVoicePitch;
|
||||
|
||||
EmitSound( filter, entindex(), ep );
|
||||
}
|
||||
}
|
||||
|
||||
int CNPC_Vortigaunt::GetSoundInterests ( void )
|
||||
{
|
||||
return SOUND_WORLD |
|
||||
SOUND_COMBAT |
|
||||
SOUND_DANGER |
|
||||
SOUND_PLAYER;
|
||||
}
|
||||
|
||||
void CNPC_Vortigaunt::Event_Killed( const CTakeDamageInfo &info )
|
||||
{
|
||||
ClearBeams( );
|
||||
BaseClass::Event_Killed( info );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// SetYawSpeed - allows each sequence to have a different
|
||||
// turn rate associated with it.
|
||||
//=========================================================
|
||||
float CNPC_Vortigaunt::MaxYawSpeed ( void )
|
||||
{
|
||||
float flYS;
|
||||
|
||||
switch ( GetActivity() )
|
||||
{
|
||||
case ACT_WALK:
|
||||
flYS = 50;
|
||||
break;
|
||||
case ACT_RUN:
|
||||
flYS = 70;
|
||||
break;
|
||||
case ACT_IDLE:
|
||||
flYS = 50;
|
||||
break;
|
||||
default:
|
||||
flYS = 90;
|
||||
break;
|
||||
}
|
||||
|
||||
return flYS;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// HandleAnimEvent - catches the monster-specific messages
|
||||
// that occur when tagged animation frames are played.
|
||||
//
|
||||
// Returns number of events handled, 0 if none.
|
||||
//=========================================================
|
||||
void CNPC_Vortigaunt::HandleAnimEvent( animevent_t *pEvent )
|
||||
{
|
||||
// ALERT( at_console, "event %d : %f\n", pEvent->event, pev->frame );
|
||||
switch( pEvent->event )
|
||||
{
|
||||
case ISLAVE_AE_CLAW:
|
||||
{
|
||||
// SOUND HERE!
|
||||
CBaseEntity *pHurt = CheckTraceHullAttack( 40, Vector(-10,-10,-10), Vector(10,10,10), sk_islave_dmg_claw.GetFloat(), DMG_SLASH );
|
||||
CPASAttenuationFilter filter( this );
|
||||
if ( pHurt )
|
||||
{
|
||||
if ( pHurt->GetFlags() & ( FL_NPC | FL_CLIENT ) )
|
||||
pHurt->ViewPunch( QAngle( 5, 0, -18 ) );
|
||||
|
||||
// Play a random attack hit sound
|
||||
CSoundParameters params;
|
||||
if ( GetParametersForSound( "Vortigaunt.AttackHit", params, NULL ) )
|
||||
{
|
||||
EmitSound_t ep( params );
|
||||
params.pitch = m_iVoicePitch;
|
||||
|
||||
EmitSound( filter, entindex(), ep );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Play a random attack miss sound
|
||||
CSoundParameters params;
|
||||
if ( GetParametersForSound( "Vortigaunt.AttackMiss", params, NULL ) )
|
||||
{
|
||||
EmitSound_t ep( params );
|
||||
params.pitch = m_iVoicePitch;
|
||||
|
||||
EmitSound( filter, entindex(), ep );
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ISLAVE_AE_CLAWRAKE:
|
||||
{
|
||||
CBaseEntity *pHurt = CheckTraceHullAttack( 40, Vector(-10,-10,-10), Vector(10,10,10), sk_islave_dmg_clawrake.GetFloat(), DMG_SLASH );
|
||||
CPASAttenuationFilter filter2( this );
|
||||
if ( pHurt )
|
||||
{
|
||||
if ( pHurt->GetFlags() & ( FL_NPC | FL_CLIENT ) )
|
||||
pHurt->ViewPunch( QAngle( 5, 0, 18 ) );
|
||||
|
||||
CSoundParameters params;
|
||||
if ( GetParametersForSound( "Vortigaunt.AttackHit", params, NULL ) )
|
||||
{
|
||||
EmitSound_t ep( params );
|
||||
params.pitch = m_iVoicePitch;
|
||||
|
||||
EmitSound( filter2, entindex(), ep );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CSoundParameters params;
|
||||
if ( GetParametersForSound( "Vortigaunt.AttackMiss", params, NULL ) )
|
||||
{
|
||||
EmitSound_t ep( params );
|
||||
params.pitch = m_iVoicePitch;
|
||||
|
||||
EmitSound( filter2, entindex(), ep );
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ISLAVE_AE_ZAP_POWERUP:
|
||||
{
|
||||
// speed up attack when on hard
|
||||
if ( g_iSkillLevel == SKILL_HARD )
|
||||
m_flPlaybackRate = 1.5;
|
||||
|
||||
Vector v_forward;
|
||||
GetVectors( &v_forward, NULL, NULL );
|
||||
|
||||
CBroadcastRecipientFilter filter;
|
||||
te->DynamicLight( filter, 0.0, &GetAbsOrigin(), 125, 200, 100, 2, 120, 0.2 / m_flPlaybackRate, 0 );
|
||||
|
||||
if ( m_hDead != NULL )
|
||||
{
|
||||
WackBeam( -1, m_hDead );
|
||||
WackBeam( 1, m_hDead );
|
||||
}
|
||||
else
|
||||
{
|
||||
ArmBeam( -1 );
|
||||
ArmBeam( 1 );
|
||||
BeamGlow( );
|
||||
}
|
||||
|
||||
CPASAttenuationFilter filter3( this );
|
||||
CSoundParameters params;
|
||||
if ( GetParametersForSound( "Vortigaunt.ZapPowerup", params, NULL ) )
|
||||
{
|
||||
EmitSound_t ep( params );
|
||||
ep.m_nPitch = 100 + m_iBeams * 10;
|
||||
EmitSound( filter3, entindex(), ep );
|
||||
}
|
||||
|
||||
// Huh? Model doesn't have multiple texturegroups, commented this out. -LH
|
||||
// m_nSkin = m_iBeams / 2;
|
||||
}
|
||||
break;
|
||||
|
||||
case ISLAVE_AE_ZAP_SHOOT:
|
||||
{
|
||||
ClearBeams( );
|
||||
|
||||
if ( m_hDead != NULL )
|
||||
{
|
||||
Vector vecDest = m_hDead->GetAbsOrigin() + Vector( 0, 0, 38 );
|
||||
trace_t trace;
|
||||
UTIL_TraceHull( vecDest, vecDest, GetHullMins(), GetHullMaxs(),MASK_SOLID, m_hDead, COLLISION_GROUP_NONE, &trace );
|
||||
|
||||
if ( !trace.startsolid )
|
||||
{
|
||||
CBaseEntity *pNew = Create( "monster_alien_slave", m_hDead->GetAbsOrigin(), m_hDead->GetAbsAngles() );
|
||||
|
||||
pNew->AddSpawnFlags( 1 );
|
||||
WackBeam( -1, pNew );
|
||||
WackBeam( 1, pNew );
|
||||
UTIL_Remove( m_hDead );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ClearMultiDamage();
|
||||
|
||||
ZapBeam( -1 );
|
||||
ZapBeam( 1 );
|
||||
|
||||
CPASAttenuationFilter filter4( this );
|
||||
EmitSound( filter4, entindex(), "Vortigaunt.ZapShoot" );
|
||||
ApplyMultiDamage();
|
||||
|
||||
m_flNextAttack = gpGlobals->curtime + random->RandomFloat( 0.5, 4.0 );
|
||||
}
|
||||
break;
|
||||
|
||||
case ISLAVE_AE_ZAP_DONE:
|
||||
{
|
||||
ClearBeams();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
BaseClass::HandleAnimEvent( pEvent );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose : For innate range attack
|
||||
// Input :
|
||||
// Output :
|
||||
//------------------------------------------------------------------------------
|
||||
int CNPC_Vortigaunt::RangeAttack1Conditions( float flDot, float flDist )
|
||||
{
|
||||
if ( GetEnemy() == NULL )
|
||||
return( COND_LOST_ENEMY );
|
||||
|
||||
if ( gpGlobals->curtime < m_flNextAttack )
|
||||
return COND_NONE;
|
||||
|
||||
if ( HasCondition( COND_CAN_MELEE_ATTACK1 ) )
|
||||
return COND_NONE;
|
||||
|
||||
return COND_CAN_RANGE_ATTACK1;
|
||||
}
|
||||
|
||||
|
||||
void CNPC_Vortigaunt::StartTask( const Task_t *pTask )
|
||||
{
|
||||
ClearBeams();
|
||||
BaseClass::StartTask( pTask );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// TakeDamage - get provoked when injured
|
||||
//=========================================================
|
||||
|
||||
int CNPC_Vortigaunt::OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo )
|
||||
{
|
||||
// don't slash one of your own
|
||||
if ( ( inputInfo.GetDamageType() & DMG_SLASH ) && inputInfo.GetAttacker() && IRelationType( inputInfo.GetAttacker() ) == D_NU )
|
||||
return 0;
|
||||
|
||||
Remember( bits_MEMORY_PROVOKED );
|
||||
|
||||
return BaseClass::OnTakeDamage_Alive( inputInfo );
|
||||
}
|
||||
|
||||
|
||||
void CNPC_Vortigaunt::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
|
||||
{
|
||||
if ( info.GetDamageType() & DMG_SHOCK )
|
||||
return;
|
||||
|
||||
BaseClass::TraceAttack( info, vecDir, ptr, pAccumulator );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
int CNPC_Vortigaunt::SelectSchedule( void )
|
||||
{
|
||||
ClearBeams();
|
||||
|
||||
switch ( m_NPCState )
|
||||
{
|
||||
case NPC_STATE_COMBAT:
|
||||
// dead enemy
|
||||
if ( HasCondition( COND_ENEMY_DEAD ) )
|
||||
{
|
||||
// call base class, all code to handle dead enemies is centralized there.
|
||||
return BaseClass::SelectSchedule();
|
||||
}
|
||||
|
||||
if ( HasCondition( COND_CAN_RANGE_ATTACK1 ) )
|
||||
return SCHED_RANGE_ATTACK1;
|
||||
|
||||
if ( m_iHealth < 20 || m_iBravery < 0)
|
||||
{
|
||||
if ( !HasCondition( COND_CAN_MELEE_ATTACK1 ) )
|
||||
{
|
||||
SetDefaultFailSchedule( SCHED_CHASE_ENEMY );
|
||||
if ( HasCondition( COND_LIGHT_DAMAGE ) || HasCondition( COND_HEAVY_DAMAGE ) )
|
||||
return SCHED_TAKE_COVER_FROM_ENEMY;
|
||||
if ( HasCondition ( COND_SEE_ENEMY ) && HasCondition ( COND_ENEMY_FACING_ME ) )
|
||||
return SCHED_TAKE_COVER_FROM_ENEMY;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return BaseClass::SelectSchedule( );
|
||||
}
|
||||
|
||||
int CNPC_Vortigaunt::TranslateSchedule( int scheduleType )
|
||||
{
|
||||
//Oops can't get to my enemy.
|
||||
if ( scheduleType == SCHED_CHASE_ENEMY_FAILED )
|
||||
{
|
||||
return SCHED_ESTABLISH_LINE_OF_FIRE;
|
||||
}
|
||||
|
||||
switch ( scheduleType )
|
||||
{
|
||||
case SCHED_FAIL:
|
||||
|
||||
if ( HasCondition( COND_CAN_MELEE_ATTACK1 ) )
|
||||
{
|
||||
return ( SCHED_MELEE_ATTACK1 );
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case SCHED_RANGE_ATTACK1:
|
||||
{
|
||||
//Adrian - HACK HACK! This should've been done up there ^^^^
|
||||
if ( HasCondition( COND_CAN_MELEE_ATTACK1 ) )
|
||||
{
|
||||
return ( SCHED_MELEE_ATTACK1 );
|
||||
}
|
||||
|
||||
return SCHED_VORTIGAUNT_ATTACK;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return BaseClass::TranslateSchedule( scheduleType );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// ArmBeam - small beam from arm to nearby geometry
|
||||
//=========================================================
|
||||
void CNPC_Vortigaunt::ArmBeam( int side )
|
||||
{
|
||||
trace_t tr;
|
||||
float flDist = 1.0;
|
||||
|
||||
if ( m_iBeams >= VORTIGAUNT_MAX_BEAMS )
|
||||
return;
|
||||
|
||||
Vector forward, right, up;
|
||||
Vector vecAim;
|
||||
AngleVectors( GetAbsAngles(), &forward, &right, &up );
|
||||
Vector vecSrc = GetAbsOrigin() + up * 36 + right * side * 16 + forward * 32;
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
vecAim = right * side * random->RandomFloat( 0, 1 ) + up * random->RandomFloat( -1, 1 );
|
||||
trace_t tr1;
|
||||
UTIL_TraceLine ( vecSrc, vecSrc + vecAim * 512, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr1);
|
||||
if (flDist > tr1.fraction)
|
||||
{
|
||||
tr = tr1;
|
||||
flDist = tr.fraction;
|
||||
}
|
||||
}
|
||||
|
||||
// Couldn't find anything close enough
|
||||
if ( flDist == 1.0 )
|
||||
return;
|
||||
|
||||
if( tr.m_pEnt && tr.m_pEnt->m_takedamage && !tr.m_pEnt->IsNPC() )
|
||||
{
|
||||
CTakeDamageInfo info( this, this, 10, DMG_SHOCK );
|
||||
CalculateMeleeDamageForce( &info, vecAim, tr.endpos );
|
||||
|
||||
tr.m_pEnt->TakeDamage( info );
|
||||
}
|
||||
|
||||
UTIL_DecalTrace( &tr, "FadingScorch" );
|
||||
|
||||
m_pBeam[m_iBeams] = CBeam::BeamCreate( "sprites/lgtning.vmt", 3.0f );
|
||||
|
||||
if ( m_pBeam[m_iBeams] == NULL )
|
||||
return;
|
||||
|
||||
m_pBeam[m_iBeams]->PointEntInit( tr.endpos, this );
|
||||
m_pBeam[m_iBeams]->SetEndAttachment( side < 0 ? 2 : 1 );
|
||||
|
||||
m_pBeam[m_iBeams]->SetColor( 96, 128, 16 );
|
||||
|
||||
m_pBeam[m_iBeams]->SetBrightness( 64 );
|
||||
m_pBeam[m_iBeams]->SetNoise( 12.8 );
|
||||
m_pBeam[m_iBeams]->AddSpawnFlags( SF_BEAM_TEMPORARY );
|
||||
|
||||
m_iBeams++;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// BeamGlow - brighten all beams
|
||||
//=========================================================
|
||||
void CNPC_Vortigaunt::BeamGlow( )
|
||||
{
|
||||
int b = m_iBeams * 32;
|
||||
|
||||
if ( b > 255 )
|
||||
b = 255;
|
||||
|
||||
for ( int i = 0; i < m_iBeams; i++ )
|
||||
{
|
||||
if ( m_pBeam[i] != NULL )
|
||||
{
|
||||
if ( m_pBeam[i]->GetBrightness() != 255 )
|
||||
m_pBeam[i]->SetBrightness( b );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// WackBeam - regenerate dead colleagues
|
||||
//=========================================================
|
||||
void CNPC_Vortigaunt::WackBeam( int side, CBaseEntity *pEntity )
|
||||
{
|
||||
Vector vecDest;
|
||||
|
||||
if ( m_iBeams >= VORTIGAUNT_MAX_BEAMS )
|
||||
return;
|
||||
|
||||
if ( pEntity == NULL )
|
||||
return;
|
||||
|
||||
m_pBeam[m_iBeams] = CBeam::BeamCreate( "sprites/lgtning.vmt", 3.0f );
|
||||
if ( m_pBeam[m_iBeams] == NULL )
|
||||
return;
|
||||
|
||||
m_pBeam[m_iBeams]->PointEntInit( pEntity->WorldSpaceCenter(), this );
|
||||
m_pBeam[m_iBeams]->SetEndAttachment( side < 0 ? 2 : 1 );
|
||||
m_pBeam[m_iBeams]->SetColor( 180, 255, 96 );
|
||||
m_pBeam[m_iBeams]->SetBrightness( 255 );
|
||||
m_pBeam[m_iBeams]->SetNoise( 12.8 );
|
||||
m_pBeam[m_iBeams]->AddSpawnFlags( SF_BEAM_TEMPORARY );
|
||||
m_iBeams++;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// ZapBeam - heavy damage directly forward
|
||||
//=========================================================
|
||||
void CNPC_Vortigaunt::ZapBeam( int side )
|
||||
{
|
||||
Vector vecSrc, vecAim;
|
||||
trace_t tr;
|
||||
CBaseEntity *pEntity;
|
||||
|
||||
if ( m_iBeams >= VORTIGAUNT_MAX_BEAMS )
|
||||
return;
|
||||
|
||||
Vector forward, right, up;
|
||||
AngleVectors( GetAbsAngles(), &forward, &right, &up );
|
||||
|
||||
vecSrc = GetAbsOrigin() + up * 36;
|
||||
vecAim = GetShootEnemyDir( vecSrc );
|
||||
float deflection = 0.01;
|
||||
vecAim = vecAim + side * right * random->RandomFloat( 0, deflection ) + up * random->RandomFloat( -deflection, deflection );
|
||||
UTIL_TraceLine ( vecSrc, vecSrc + vecAim * 1024, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr);
|
||||
|
||||
m_pBeam[m_iBeams] = CBeam::BeamCreate( "sprites/lgtning.vmt", 5.0f );
|
||||
if ( m_pBeam[m_iBeams] == NULL )
|
||||
return;
|
||||
|
||||
m_pBeam[m_iBeams]->PointEntInit( tr.endpos, this );
|
||||
m_pBeam[m_iBeams]->SetEndAttachment( side < 0 ? 2 : 1 );
|
||||
m_pBeam[m_iBeams]->SetColor( 180, 255, 96 );
|
||||
m_pBeam[m_iBeams]->SetBrightness( 255 );
|
||||
m_pBeam[m_iBeams]->SetNoise( 3.2f );
|
||||
m_pBeam[m_iBeams]->AddSpawnFlags( SF_BEAM_TEMPORARY );
|
||||
m_iBeams++;
|
||||
|
||||
pEntity = tr.m_pEnt;
|
||||
|
||||
if ( pEntity != NULL && m_takedamage )
|
||||
{
|
||||
CTakeDamageInfo info( this, this, sk_islave_dmg_zap.GetFloat(), DMG_SHOCK );
|
||||
CalculateMeleeDamageForce( &info, vecAim, tr.endpos );
|
||||
pEntity->DispatchTraceAttack( info, vecAim, &tr );
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// ClearBeams - remove all beams
|
||||
//=========================================================
|
||||
void CNPC_Vortigaunt::ClearBeams( )
|
||||
{
|
||||
for (int i = 0; i < VORTIGAUNT_MAX_BEAMS; i++)
|
||||
{
|
||||
if (m_pBeam[i])
|
||||
{
|
||||
UTIL_Remove( m_pBeam[i] );
|
||||
m_pBeam[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
m_iBeams = 0;
|
||||
m_nSkin = 0;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Schedules
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
AI_BEGIN_CUSTOM_NPC( monster_alien_slave, CNPC_Vortigaunt )
|
||||
|
||||
//=========================================================
|
||||
// > SCHED_VORTIGAUNT_ATTACK
|
||||
//=========================================================
|
||||
DEFINE_SCHEDULE
|
||||
(
|
||||
SCHED_VORTIGAUNT_ATTACK,
|
||||
|
||||
" Tasks"
|
||||
" TASK_STOP_MOVING 0"
|
||||
" TASK_FACE_IDEAL 0"
|
||||
" TASK_RANGE_ATTACK1 0"
|
||||
" "
|
||||
" Interrupts"
|
||||
" COND_CAN_MELEE_ATTACK1"
|
||||
" COND_HEAVY_DAMAGE"
|
||||
" COND_HEAR_DANGER"
|
||||
)
|
||||
|
||||
AI_END_CUSTOM_NPC()
|
||||
@@ -0,0 +1,74 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
#ifndef NPC_VORTIGAUNT_H
|
||||
#define NPC_VORTIGAUNT_H
|
||||
|
||||
#define VORTIGAUNT_MAX_BEAMS 8
|
||||
|
||||
#include "hl1_ai_basenpc.h"
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
class CNPC_Vortigaunt : public CHL1BaseNPC
|
||||
{
|
||||
DECLARE_CLASS( CNPC_Vortigaunt, CHL1BaseNPC );
|
||||
public:
|
||||
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
Class_T Classify ( void );
|
||||
|
||||
void AlertSound( void );
|
||||
void IdleSound( void );
|
||||
void PainSound( const CTakeDamageInfo &info );
|
||||
void DeathSound( const CTakeDamageInfo &info );
|
||||
|
||||
int GetSoundInterests ( void );
|
||||
|
||||
float MaxYawSpeed ( void );
|
||||
|
||||
void Event_Killed( const CTakeDamageInfo &info );
|
||||
void CallForHelp( char *szClassname, float flDist, CBaseEntity * pEnemy, Vector &vecLocation );
|
||||
|
||||
int RangeAttack1Conditions( float flDot, float flDist );
|
||||
|
||||
int OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo );
|
||||
void TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator );
|
||||
|
||||
void StartTask( const Task_t *pTask );
|
||||
|
||||
int SelectSchedule( void );
|
||||
int TranslateSchedule( int scheduleType );
|
||||
|
||||
void ArmBeam( int side );
|
||||
void BeamGlow( void );
|
||||
void WackBeam( int side, CBaseEntity *pEntity );
|
||||
void ZapBeam( int side );
|
||||
void ClearBeams( void );
|
||||
|
||||
void HandleAnimEvent( animevent_t *pEvent );
|
||||
|
||||
virtual Disposition_t IRelationType ( CBaseEntity *pTarget );
|
||||
|
||||
DEFINE_CUSTOM_AI;
|
||||
DECLARE_DATADESC();
|
||||
|
||||
private:
|
||||
int m_iVoicePitch;
|
||||
int m_iBeams;
|
||||
|
||||
int m_iBravery;
|
||||
|
||||
CBeam *m_pBeam[VORTIGAUNT_MAX_BEAMS];
|
||||
|
||||
float m_flNextAttack;
|
||||
|
||||
EHANDLE m_hDead;
|
||||
};
|
||||
|
||||
|
||||
#endif //NPC_VORTIGAUNT_
|
||||
@@ -0,0 +1,307 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: A slow-moving, once-human headcrab victim with only melee attacks.
|
||||
//
|
||||
// UNDONE: Make head take 100% damage, body take 30% damage.
|
||||
// UNDONE: Don't flinch every time you get hit.
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "game.h"
|
||||
#include "ai_default.h"
|
||||
#include "ai_schedule.h"
|
||||
#include "ai_hull.h"
|
||||
#include "ai_route.h"
|
||||
#include "npcevent.h"
|
||||
#include "hl1_npc_zombie.h"
|
||||
#include "gib.h"
|
||||
//#include "AI_Interactions.h"
|
||||
#include "ndebugoverlay.h"
|
||||
#include "vstdlib/random.h"
|
||||
#include "engine/IEngineSound.h"
|
||||
|
||||
ConVar sk_zombie_health( "sk_zombie_health","50");
|
||||
ConVar sk_zombie_dmg_one_slash( "sk_zombie_dmg_one_slash", "20" );
|
||||
ConVar sk_zombie_dmg_both_slash( "sk_zombie_dmg_both_slash", "40" );
|
||||
|
||||
|
||||
|
||||
LINK_ENTITY_TO_CLASS( monster_zombie, CNPC_Zombie );
|
||||
|
||||
|
||||
//=========================================================
|
||||
// Spawn
|
||||
//=========================================================
|
||||
void CNPC_Zombie::Spawn()
|
||||
{
|
||||
Precache( );
|
||||
|
||||
SetModel( "models/zombie.mdl" );
|
||||
|
||||
SetRenderColor( 255, 255, 255, 255 );
|
||||
|
||||
SetHullType(HULL_HUMAN);
|
||||
SetHullSizeNormal();
|
||||
|
||||
SetSolid( SOLID_BBOX );
|
||||
AddSolidFlags( FSOLID_NOT_STANDABLE );
|
||||
SetMoveType( MOVETYPE_STEP );
|
||||
m_bloodColor = BLOOD_COLOR_GREEN;
|
||||
m_iHealth = sk_zombie_health.GetFloat();
|
||||
//pev->view_ofs = VEC_VIEW;// position of the eyes relative to monster's origin.
|
||||
m_flFieldOfView = 0.5;
|
||||
m_NPCState = NPC_STATE_NONE;
|
||||
CapabilitiesClear();
|
||||
CapabilitiesAdd( bits_CAP_MOVE_GROUND | bits_CAP_INNATE_MELEE_ATTACK1 | bits_CAP_DOORS_GROUP );
|
||||
|
||||
NPCInit();
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Precache - precaches all resources this monster needs
|
||||
//=========================================================
|
||||
void CNPC_Zombie::Precache()
|
||||
{
|
||||
PrecacheModel( "models/zombie.mdl" );
|
||||
|
||||
PrecacheScriptSound( "Zombie.AttackHit" );
|
||||
PrecacheScriptSound( "Zombie.AttackMiss" );
|
||||
PrecacheScriptSound( "Zombie.Pain" );
|
||||
PrecacheScriptSound( "Zombie.Idle" );
|
||||
PrecacheScriptSound( "Zombie.Alert" );
|
||||
PrecacheScriptSound( "Zombie.Attack" );
|
||||
|
||||
BaseClass::Precache();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Returns this monster's place in the relationship table.
|
||||
//-----------------------------------------------------------------------------
|
||||
Class_T CNPC_Zombie::Classify( void )
|
||||
{
|
||||
return CLASS_ALIEN_MONSTER;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// HandleAnimEvent - catches the monster-specific messages
|
||||
// that occur when tagged animation frames are played.
|
||||
//=========================================================
|
||||
void CNPC_Zombie::HandleAnimEvent( animevent_t *pEvent )
|
||||
{
|
||||
Vector v_forward, v_right;
|
||||
switch( pEvent->event )
|
||||
{
|
||||
case ZOMBIE_AE_ATTACK_RIGHT:
|
||||
{
|
||||
// do stuff for this event.
|
||||
// ALERT( at_console, "Slash right!\n" );
|
||||
|
||||
Vector vecMins = GetHullMins();
|
||||
Vector vecMaxs = GetHullMaxs();
|
||||
vecMins.z = vecMins.x;
|
||||
vecMaxs.z = vecMaxs.x;
|
||||
|
||||
CBaseEntity *pHurt = CheckTraceHullAttack( 70, vecMins, vecMaxs, sk_zombie_dmg_one_slash.GetFloat(), DMG_SLASH );
|
||||
CPASAttenuationFilter filter( this );
|
||||
if ( pHurt )
|
||||
{
|
||||
if ( pHurt->GetFlags() & ( FL_NPC | FL_CLIENT ) )
|
||||
{
|
||||
pHurt->ViewPunch( QAngle( 5, 0, 18 ) );
|
||||
|
||||
GetVectors( &v_forward, &v_right, NULL );
|
||||
|
||||
pHurt->SetAbsVelocity( pHurt->GetAbsVelocity() - v_right * 100 );
|
||||
}
|
||||
// Play a random attack hit sound
|
||||
EmitSound( filter, entindex(), "Zombie.AttackHit" );
|
||||
}
|
||||
else // Play a random attack miss sound
|
||||
EmitSound( filter, entindex(), "Zombie.AttackMiss" );
|
||||
|
||||
if ( random->RandomInt( 0, 1 ) )
|
||||
AttackSound();
|
||||
}
|
||||
break;
|
||||
|
||||
case ZOMBIE_AE_ATTACK_LEFT:
|
||||
{
|
||||
// do stuff for this event.
|
||||
// ALERT( at_console, "Slash left!\n" );
|
||||
Vector vecMins = GetHullMins();
|
||||
Vector vecMaxs = GetHullMaxs();
|
||||
vecMins.z = vecMins.x;
|
||||
vecMaxs.z = vecMaxs.x;
|
||||
|
||||
CBaseEntity *pHurt = CheckTraceHullAttack( 70, vecMins, vecMaxs, sk_zombie_dmg_one_slash.GetFloat(), DMG_SLASH );
|
||||
|
||||
CPASAttenuationFilter filter2( this );
|
||||
if ( pHurt )
|
||||
{
|
||||
if ( pHurt->GetFlags() & ( FL_NPC | FL_CLIENT ) )
|
||||
{
|
||||
pHurt->ViewPunch( QAngle ( 5, 0, -18 ) );
|
||||
|
||||
GetVectors( &v_forward, &v_right, NULL );
|
||||
|
||||
pHurt->SetAbsVelocity( pHurt->GetAbsVelocity() - v_right * 100 );
|
||||
}
|
||||
EmitSound( filter2, entindex(), "Zombie.AttackHit" );
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitSound( filter2, entindex(), "Zombie.AttackMiss" );
|
||||
}
|
||||
|
||||
if ( random->RandomInt( 0,1 ) )
|
||||
AttackSound();
|
||||
}
|
||||
break;
|
||||
|
||||
case ZOMBIE_AE_ATTACK_BOTH:
|
||||
{
|
||||
// do stuff for this event.
|
||||
Vector vecMins = GetHullMins();
|
||||
Vector vecMaxs = GetHullMaxs();
|
||||
vecMins.z = vecMins.x;
|
||||
vecMaxs.z = vecMaxs.x;
|
||||
|
||||
CBaseEntity *pHurt = CheckTraceHullAttack( 70, vecMins, vecMaxs, sk_zombie_dmg_both_slash.GetFloat(), DMG_SLASH );
|
||||
|
||||
|
||||
CPASAttenuationFilter filter3( this );
|
||||
if ( pHurt )
|
||||
{
|
||||
if ( pHurt->GetFlags() & ( FL_NPC | FL_CLIENT ) )
|
||||
{
|
||||
pHurt->ViewPunch( QAngle ( 5, 0, 0 ) );
|
||||
|
||||
GetVectors( &v_forward, &v_right, NULL );
|
||||
pHurt->SetAbsVelocity( pHurt->GetAbsVelocity() - v_right * 100 );
|
||||
}
|
||||
EmitSound( filter3, entindex(), "Zombie.AttackHit" );
|
||||
}
|
||||
else
|
||||
EmitSound( filter3, entindex(),"Zombie.AttackMiss" );
|
||||
|
||||
if ( random->RandomInt( 0,1 ) )
|
||||
AttackSound();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
BaseClass::HandleAnimEvent( pEvent );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static float DamageForce( const Vector &size, float damage )
|
||||
{
|
||||
float force = damage * ((32 * 32 * 72.0) / (size.x * size.y * size.z)) * 5;
|
||||
|
||||
if ( force > 1000.0)
|
||||
{
|
||||
force = 1000.0;
|
||||
}
|
||||
|
||||
return force;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : pInflictor -
|
||||
// pAttacker -
|
||||
// flDamage -
|
||||
// bitsDamageType -
|
||||
// Output : int
|
||||
//-----------------------------------------------------------------------------
|
||||
int CNPC_Zombie::OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo )
|
||||
{
|
||||
CTakeDamageInfo info = inputInfo;
|
||||
|
||||
// Take 30% damage from bullets
|
||||
if ( info.GetDamageType() == DMG_BULLET )
|
||||
{
|
||||
Vector vecDir = GetAbsOrigin() - info.GetInflictor()->WorldSpaceCenter();
|
||||
VectorNormalize( vecDir );
|
||||
float flForce = DamageForce( WorldAlignSize(), info.GetDamage() );
|
||||
SetAbsVelocity( GetAbsVelocity() + vecDir * flForce );
|
||||
info.ScaleDamage( 0.3f );
|
||||
}
|
||||
|
||||
// HACK HACK -- until we fix this.
|
||||
if ( IsAlive() )
|
||||
PainSound( info );
|
||||
|
||||
return BaseClass::OnTakeDamage_Alive( info );
|
||||
}
|
||||
|
||||
void CNPC_Zombie::PainSound( const CTakeDamageInfo &info )
|
||||
{
|
||||
if ( random->RandomInt(0,5) < 2)
|
||||
{
|
||||
CPASAttenuationFilter filter( this );
|
||||
EmitSound( filter, entindex(), "Zombie.Pain" );
|
||||
}
|
||||
}
|
||||
|
||||
void CNPC_Zombie::AlertSound( void )
|
||||
{
|
||||
CPASAttenuationFilter filter( this );
|
||||
EmitSound( filter, entindex(), "Zombie.Alert" );
|
||||
}
|
||||
|
||||
void CNPC_Zombie::IdleSound( void )
|
||||
{
|
||||
// Play a random idle sound
|
||||
CPASAttenuationFilter filter( this );
|
||||
EmitSound( filter, entindex(), "Zombie.Idle" );
|
||||
}
|
||||
|
||||
void CNPC_Zombie::AttackSound( void )
|
||||
{
|
||||
// Play a random attack sound
|
||||
CPASAttenuationFilter filter( this );
|
||||
EmitSound( filter, entindex(), "Zombie.Attack" );
|
||||
}
|
||||
|
||||
int CNPC_Zombie::MeleeAttack1Conditions ( float flDot, float flDist )
|
||||
{
|
||||
if ( flDist > 64)
|
||||
{
|
||||
return COND_TOO_FAR_TO_ATTACK;
|
||||
}
|
||||
else if (flDot < 0.7)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else if (GetEnemy() == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return COND_CAN_MELEE_ATTACK1;
|
||||
}
|
||||
|
||||
void CNPC_Zombie::RemoveIgnoredConditions ( void )
|
||||
{
|
||||
if ( GetActivity() == ACT_MELEE_ATTACK1 )
|
||||
{
|
||||
// Nothing stops an attacking zombie.
|
||||
ClearCondition( COND_LIGHT_DAMAGE );
|
||||
ClearCondition( COND_HEAVY_DAMAGE );
|
||||
}
|
||||
|
||||
if (( GetActivity() == ACT_SMALL_FLINCH ) || ( GetActivity() == ACT_BIG_FLINCH ))
|
||||
{
|
||||
if (m_flNextFlinch < gpGlobals->curtime)
|
||||
m_flNextFlinch = gpGlobals->curtime + ZOMBIE_FLINCH_DELAY;
|
||||
}
|
||||
|
||||
BaseClass::RemoveIgnoredConditions();
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
#ifndef NPC_ZOMBIE_H
|
||||
#define NPC_ZOMBIE_H
|
||||
|
||||
|
||||
#include "hl1_ai_basenpc.h"
|
||||
//=========================================================
|
||||
// Monster's Anim Events Go Here
|
||||
//=========================================================
|
||||
#define ZOMBIE_AE_ATTACK_RIGHT 0x01
|
||||
#define ZOMBIE_AE_ATTACK_LEFT 0x02
|
||||
#define ZOMBIE_AE_ATTACK_BOTH 0x03
|
||||
|
||||
#define ZOMBIE_FLINCH_DELAY 2 // at most one flinch every n secs
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
class CNPC_Zombie : public CHL1BaseNPC
|
||||
{
|
||||
DECLARE_CLASS( CNPC_Zombie, CHL1BaseNPC );
|
||||
public:
|
||||
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
float MaxYawSpeed( void ) { return 120.0f; };
|
||||
Class_T Classify( void );
|
||||
void HandleAnimEvent( animevent_t *pEvent );
|
||||
// int IgnoreConditions ( void );
|
||||
|
||||
float m_flNextFlinch;
|
||||
|
||||
void PainSound( const CTakeDamageInfo &info );
|
||||
void AlertSound( void );
|
||||
void IdleSound( void );
|
||||
void AttackSound( void );
|
||||
|
||||
// No range attacks
|
||||
BOOL CheckRangeAttack1 ( float flDot, float flDist ) { return FALSE; }
|
||||
BOOL CheckRangeAttack2 ( float flDot, float flDist ) { return FALSE; }
|
||||
int OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo );
|
||||
|
||||
void RemoveIgnoredConditions ( void );
|
||||
int MeleeAttack1Conditions ( float flDot, float flDist );
|
||||
};
|
||||
|
||||
#endif //NPC_ZOMBIE_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,167 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Player for HL1.
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef HL1_PLAYER_H
|
||||
#define HL1_PLAYER_H
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "player.h"
|
||||
|
||||
extern int TrainSpeed(int iSpeed, int iMax);
|
||||
extern void CopyToBodyQue( CBaseAnimating *pCorpse );
|
||||
|
||||
enum HL1PlayerPhysFlag_e
|
||||
{
|
||||
// 1 -- 5 are used by enum PlayerPhysFlag_e in player.h
|
||||
|
||||
PFLAG_ONBARNACLE = ( 1<<6 ) // player is hangning from the barnalce
|
||||
};
|
||||
|
||||
class IPhysicsPlayerController;
|
||||
|
||||
|
||||
//=============================================================================
|
||||
//=============================================================================
|
||||
class CSuitPowerDevice
|
||||
{
|
||||
public:
|
||||
CSuitPowerDevice( int bitsID, float flDrainRate ) { m_bitsDeviceID = bitsID; m_flDrainRate = flDrainRate; }
|
||||
private:
|
||||
int m_bitsDeviceID; // tells what the device is. DEVICE_SPRINT, DEVICE_FLASHLIGHT, etc. BITMASK!!!!!
|
||||
float m_flDrainRate; // how quickly does this device deplete suit power? ( percent per second )
|
||||
|
||||
public:
|
||||
int GetDeviceID( void ) const { return m_bitsDeviceID; }
|
||||
float GetDeviceDrainRate( void ) const { return m_flDrainRate; }
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// >> HL1_PLAYER
|
||||
//=============================================================================
|
||||
class CHL1_Player : public CBasePlayer
|
||||
{
|
||||
DECLARE_CLASS( CHL1_Player, CBasePlayer );
|
||||
DECLARE_SERVERCLASS();
|
||||
public:
|
||||
|
||||
DECLARE_DATADESC();
|
||||
|
||||
CHL1_Player();
|
||||
~CHL1_Player( void );
|
||||
|
||||
static CHL1_Player *CreatePlayer( const char *className, edict_t *ed )
|
||||
{
|
||||
CHL1_Player::s_PlayerEdict = ed;
|
||||
return (CHL1_Player*)CreateEntityByName( className );
|
||||
}
|
||||
|
||||
void CreateCorpse( void ) { CopyToBodyQue( this ); };
|
||||
|
||||
void Precache( void );
|
||||
void Spawn(void);
|
||||
void Event_Killed( const CTakeDamageInfo &info );
|
||||
void CheatImpulseCommands( int iImpulse );
|
||||
void PlayerRunCommand( CUserCmd *ucmd, IMoveHelper *moveHelper );
|
||||
void UpdateClientData( void );
|
||||
void OnSave( IEntitySaveUtils *pUtils );
|
||||
|
||||
void CheckTimeBasedDamage( void );
|
||||
|
||||
// from cbasecombatcharacter
|
||||
void InitVCollision( const Vector &vecAbsOrigin, const Vector &vecAbsVelocity );
|
||||
|
||||
Class_T Classify ( void );
|
||||
Class_T m_nControlClass; // Class when player is controlling another entity
|
||||
|
||||
// from CBasePlayer
|
||||
void SetupVisibility( CBaseEntity *pViewEntity, unsigned char *pvs, int pvssize );
|
||||
|
||||
// Aiming heuristics accessors
|
||||
float GetIdleTime( void ) const { return ( m_flIdleTime - m_flMoveTime ); }
|
||||
float GetMoveTime( void ) const { return ( m_flMoveTime - m_flIdleTime ); }
|
||||
float GetLastDamageTime( void ) const { return m_flLastDamageTime; }
|
||||
bool IsDucking( void ) const { return !!( GetFlags() & FL_DUCKING ); }
|
||||
|
||||
int OnTakeDamage( const CTakeDamageInfo &info );
|
||||
int OnTakeDamage_Alive( const CTakeDamageInfo &info );
|
||||
void FindMissTargets( void );
|
||||
bool GetMissPosition( Vector *position );
|
||||
|
||||
void OnDamagedByExplosion( const CTakeDamageInfo &info ) { };
|
||||
void PlayerPickupObject( CBasePlayer *pPlayer, CBaseEntity *pObject );
|
||||
|
||||
virtual void CreateViewModel( int index /*=0*/ );
|
||||
|
||||
virtual CBaseEntity *GiveNamedItem( const char *pszName, int iSubType = 0 );
|
||||
|
||||
virtual void OnRestore( void );
|
||||
|
||||
bool IsPullingObject() { return m_bIsPullingObject; }
|
||||
void StartPullingObject( CBaseEntity *pObject );
|
||||
void StopPullingObject();
|
||||
void UpdatePullingObject();
|
||||
|
||||
|
||||
protected:
|
||||
void PreThink( void );
|
||||
bool HandleInteraction(int interactionType, void *data, CBaseCombatCharacter* sourceEnt);
|
||||
|
||||
private:
|
||||
Vector m_vecMissPositions[16];
|
||||
int m_nNumMissPositions;
|
||||
|
||||
// Aiming heuristics code
|
||||
float m_flIdleTime; //Amount of time we've been motionless
|
||||
float m_flMoveTime; //Amount of time we've been in motion
|
||||
float m_flLastDamageTime; //Last time we took damage
|
||||
float m_flTargetFindTime;
|
||||
|
||||
EHANDLE m_hPullObject;
|
||||
IPhysicsConstraint *m_pPullConstraint;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
// Flashlight Device
|
||||
int FlashlightIsOn( void );
|
||||
void FlashlightTurnOn( void );
|
||||
void FlashlightTurnOff( void );
|
||||
float m_flFlashLightTime; // Time until next battery draw/Recharge
|
||||
CNetworkVar( int, m_nFlashBattery ); // Flashlight Battery Draw
|
||||
|
||||
// For gauss weapon
|
||||
// float m_flStartCharge;
|
||||
// float m_flAmmoStartCharge;
|
||||
// float m_flPlayAftershock;
|
||||
// float m_flNextAmmoBurn; // while charging, when to absorb another unit of player's ammo?
|
||||
|
||||
CNetworkVar( float, m_flStartCharge );
|
||||
CNetworkVar( float, m_flAmmoStartCharge );
|
||||
CNetworkVar( float, m_flPlayAftershock );
|
||||
CNetworkVar( float, m_flNextAmmoBurn ); // while charging, when to absorb another unit of player's ammo?
|
||||
|
||||
CNetworkVar( bool, m_bHasLongJump );
|
||||
CNetworkVar( bool, m_bIsPullingObject );
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Converts an entity to a HL1 player
|
||||
//-----------------------------------------------------------------------------
|
||||
inline CHL1_Player *ToHL1Player( CBaseEntity *pEntity )
|
||||
{
|
||||
if ( !pEntity || !pEntity->IsPlayer() )
|
||||
return NULL;
|
||||
#if _DEBUG
|
||||
return dynamic_cast<CHL1_Player *>( pEntity );
|
||||
#else
|
||||
return static_cast<CHL1_Player *>( pEntity );
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif //HL1_PLAYER_H
|
||||
@@ -0,0 +1,76 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "player_command.h"
|
||||
#include "igamemovement.h"
|
||||
#include "in_buttons.h"
|
||||
#include "ipredictionsystem.h"
|
||||
#include "hl1_player.h"
|
||||
|
||||
|
||||
static CMoveData g_MoveData;
|
||||
CMoveData *g_pMoveData = &g_MoveData;
|
||||
|
||||
IPredictionSystem *IPredictionSystem::g_pPredictionSystems = NULL;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Sets up the move data for Halflife 1
|
||||
//-----------------------------------------------------------------------------
|
||||
class CHL1PlayerMove : public CPlayerMove
|
||||
{
|
||||
DECLARE_CLASS( CHL1PlayerMove, CPlayerMove );
|
||||
|
||||
public:
|
||||
virtual void StartCommand( CBasePlayer *player, CUserCmd *cmd );
|
||||
virtual void SetupMove( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper *pHelper, CMoveData *move );
|
||||
virtual void FinishMove( CBasePlayer *player, CUserCmd *ucmd, CMoveData *move );
|
||||
};
|
||||
|
||||
// PlayerMove Interface
|
||||
static CHL1PlayerMove g_PlayerMove;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Singleton accessor
|
||||
//-----------------------------------------------------------------------------
|
||||
CPlayerMove *PlayerMove()
|
||||
{
|
||||
return &g_PlayerMove;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Main setup, finish
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void CHL1PlayerMove::StartCommand( CBasePlayer *player, CUserCmd *cmd )
|
||||
{
|
||||
BaseClass::StartCommand( player, cmd );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: This is called pre player movement and copies all the data necessary
|
||||
// from the player for movement. (Server-side, the client-side version
|
||||
// of this code can be found in prediction.cpp.)
|
||||
//-----------------------------------------------------------------------------
|
||||
void CHL1PlayerMove::SetupMove( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper *pHelper, CMoveData *move )
|
||||
{
|
||||
BaseClass::SetupMove( player, ucmd, pHelper, move );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: This is called post player movement to copy back all data that
|
||||
// movement could have modified and that is necessary for future
|
||||
// movement. (Server-side, the client-side version of this code can
|
||||
// be found in prediction.cpp.)
|
||||
//-----------------------------------------------------------------------------
|
||||
void CHL1PlayerMove::FinishMove( CBasePlayer *player, CUserCmd *ucmd, CMoveData *move )
|
||||
{
|
||||
// Call the default FinishMove code.
|
||||
BaseClass::FinishMove( player, ucmd, move );
|
||||
}
|
||||
@@ -0,0 +1,379 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Crowbar - an old favorite
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "hl1mp_basecombatweapon_shared.h"
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
#include "c_baseplayer.h"
|
||||
#include "fx_impact.h"
|
||||
#include "fx.h"
|
||||
#else
|
||||
#include "player.h"
|
||||
#include "soundent.h"
|
||||
#endif
|
||||
|
||||
#include "gamerules.h"
|
||||
#include "ammodef.h"
|
||||
#include "mathlib/mathlib.h"
|
||||
#include "in_buttons.h"
|
||||
|
||||
#include "vstdlib/random.h"
|
||||
|
||||
extern ConVar sk_plr_dmg_crowbar;
|
||||
|
||||
#define CROWBAR_RANGE 64.0f
|
||||
#define CROWBAR_REFIRE_MISS 0.5f
|
||||
#define CROWBAR_REFIRE_HIT 0.25f
|
||||
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
#define CWeaponCrowbar C_WeaponCrowbar
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CWeaponCrowbar
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class CWeaponCrowbar : public CBaseHL1MPCombatWeapon
|
||||
{
|
||||
DECLARE_CLASS( CWeaponCrowbar, CBaseHL1MPCombatWeapon );
|
||||
public:
|
||||
DECLARE_NETWORKCLASS();
|
||||
DECLARE_PREDICTABLE();
|
||||
#ifndef CLIENT_DLL
|
||||
DECLARE_DATADESC();
|
||||
#endif
|
||||
|
||||
CWeaponCrowbar();
|
||||
|
||||
void Precache( void );
|
||||
virtual void ItemPostFrame( void );
|
||||
void PrimaryAttack( void );
|
||||
|
||||
public:
|
||||
trace_t m_traceHit;
|
||||
Activity m_nHitActivity;
|
||||
|
||||
private:
|
||||
virtual void Swing( void );
|
||||
virtual void Hit( void );
|
||||
virtual void ImpactEffect( void );
|
||||
void ImpactSound( CBaseEntity *pHitEntity );
|
||||
virtual Activity ChooseIntersectionPointAndActivity( trace_t &hitTrace, const Vector &mins, const Vector &maxs, CBasePlayer *pOwner );
|
||||
|
||||
public:
|
||||
|
||||
};
|
||||
|
||||
IMPLEMENT_NETWORKCLASS_ALIASED( WeaponCrowbar, DT_WeaponCrowbar );
|
||||
|
||||
BEGIN_NETWORK_TABLE( CWeaponCrowbar, DT_WeaponCrowbar )
|
||||
/// what
|
||||
END_NETWORK_TABLE()
|
||||
|
||||
BEGIN_PREDICTION_DATA( CWeaponCrowbar )
|
||||
END_PREDICTION_DATA()
|
||||
|
||||
LINK_ENTITY_TO_CLASS( weapon_crowbar, CWeaponCrowbar );
|
||||
PRECACHE_WEAPON_REGISTER( weapon_crowbar );
|
||||
|
||||
#ifndef CLIENT_DLL
|
||||
BEGIN_DATADESC( CWeaponCrowbar )
|
||||
|
||||
// DEFINE_FIELD( m_trLineHit, trace_t ),
|
||||
// DEFINE_FIELD( m_trHullHit, trace_t ),
|
||||
// DEFINE_FIELD( m_nHitActivity, FIELD_INTEGER ),
|
||||
// DEFINE_FIELD( m_traceHit, trace_t ),
|
||||
|
||||
// Class CWeaponCrowbar:
|
||||
// DEFINE_FIELD( m_nHitActivity, FIELD_INTEGER ),
|
||||
|
||||
// Function Pointers
|
||||
DEFINE_FUNCTION( Hit ),
|
||||
|
||||
END_DATADESC()
|
||||
#endif
|
||||
|
||||
#define BLUDGEON_HULL_DIM 16
|
||||
|
||||
static const Vector g_bludgeonMins(-BLUDGEON_HULL_DIM,-BLUDGEON_HULL_DIM,-BLUDGEON_HULL_DIM);
|
||||
static const Vector g_bludgeonMaxs(BLUDGEON_HULL_DIM,BLUDGEON_HULL_DIM,BLUDGEON_HULL_DIM);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Constructor
|
||||
//-----------------------------------------------------------------------------
|
||||
CWeaponCrowbar::CWeaponCrowbar()
|
||||
{
|
||||
m_bFiresUnderwater = true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Precache the weapon
|
||||
//-----------------------------------------------------------------------------
|
||||
void CWeaponCrowbar::Precache( void )
|
||||
{
|
||||
//Call base class first
|
||||
BaseClass::Precache();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose : Update weapon
|
||||
//------------------------------------------------------------------------------
|
||||
void CWeaponCrowbar::ItemPostFrame( void )
|
||||
{
|
||||
CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
|
||||
|
||||
if ( pOwner == NULL )
|
||||
return;
|
||||
|
||||
if ( (pOwner->m_nButtons & IN_ATTACK) && (m_flNextPrimaryAttack <= gpGlobals->curtime) )
|
||||
{
|
||||
PrimaryAttack();
|
||||
}
|
||||
else
|
||||
{
|
||||
WeaponIdle();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose :
|
||||
// Input :
|
||||
// Output :
|
||||
//------------------------------------------------------------------------------
|
||||
void CWeaponCrowbar::PrimaryAttack()
|
||||
{
|
||||
Swing();
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose: Implement impact function
|
||||
//------------------------------------------------------------------------------
|
||||
void CWeaponCrowbar::Hit( void )
|
||||
{
|
||||
//Make sound for the AI
|
||||
#ifndef CLIENT_DLL
|
||||
|
||||
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
|
||||
|
||||
CSoundEnt::InsertSound( SOUND_BULLET_IMPACT, m_traceHit.endpos, 400, 0.2f, pPlayer );
|
||||
|
||||
CBaseEntity *pHitEntity = m_traceHit.m_pEnt;
|
||||
|
||||
//Apply damage to a hit target
|
||||
if ( pHitEntity != NULL )
|
||||
{
|
||||
Vector hitDirection;
|
||||
pPlayer->EyeVectors( &hitDirection, NULL, NULL );
|
||||
VectorNormalize( hitDirection );
|
||||
|
||||
ClearMultiDamage();
|
||||
CTakeDamageInfo info( GetOwner(), GetOwner(), sk_plr_dmg_crowbar.GetFloat(), DMG_CLUB );
|
||||
CalculateMeleeDamageForce( &info, hitDirection, m_traceHit.endpos );
|
||||
pHitEntity->DispatchTraceAttack( info, hitDirection, &m_traceHit );
|
||||
ApplyMultiDamage();
|
||||
|
||||
// Now hit all triggers along the ray that...
|
||||
TraceAttackToTriggers( CTakeDamageInfo( GetOwner(), GetOwner(), sk_plr_dmg_crowbar.GetFloat(), DMG_CLUB ), m_traceHit.startpos, m_traceHit.endpos, hitDirection );
|
||||
|
||||
//Play an impact sound
|
||||
ImpactSound( pHitEntity );
|
||||
}
|
||||
#endif
|
||||
|
||||
//Apply an impact effect
|
||||
ImpactEffect();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Play the impact sound
|
||||
// Input : pHitEntity - entity that we hit
|
||||
// assumes pHitEntity is not null
|
||||
//-----------------------------------------------------------------------------
|
||||
void CWeaponCrowbar::ImpactSound( CBaseEntity *pHitEntity )
|
||||
{
|
||||
bool bIsWorld = ( pHitEntity->entindex() == 0 );
|
||||
#ifndef CLIENT_DLL
|
||||
if ( !bIsWorld )
|
||||
{
|
||||
bIsWorld |= pHitEntity->Classify() == CLASS_NONE || pHitEntity->Classify() == CLASS_MACHINE;
|
||||
}
|
||||
#endif
|
||||
|
||||
if( bIsWorld )
|
||||
{
|
||||
WeaponSound( MELEE_HIT_WORLD );
|
||||
}
|
||||
else
|
||||
{
|
||||
WeaponSound( MELEE_HIT );
|
||||
}
|
||||
}
|
||||
|
||||
Activity CWeaponCrowbar::ChooseIntersectionPointAndActivity( trace_t &hitTrace, const Vector &mins, const Vector &maxs, CBasePlayer *pOwner )
|
||||
{
|
||||
int i, j, k;
|
||||
float distance;
|
||||
const float *minmaxs[2] = {mins.Base(), maxs.Base()};
|
||||
trace_t tmpTrace;
|
||||
Vector vecHullEnd = hitTrace.endpos;
|
||||
Vector vecEnd;
|
||||
|
||||
distance = 1e6f;
|
||||
Vector vecSrc = hitTrace.startpos;
|
||||
|
||||
vecHullEnd = vecSrc + ((vecHullEnd - vecSrc)*2);
|
||||
UTIL_TraceLine( vecSrc, vecHullEnd, MASK_SHOT_HULL, pOwner, COLLISION_GROUP_NONE, &tmpTrace );
|
||||
if ( tmpTrace.fraction == 1.0 )
|
||||
{
|
||||
for ( i = 0; i < 2; i++ )
|
||||
{
|
||||
for ( j = 0; j < 2; j++ )
|
||||
{
|
||||
for ( k = 0; k < 2; k++ )
|
||||
{
|
||||
vecEnd.x = vecHullEnd.x + minmaxs[i][0];
|
||||
vecEnd.y = vecHullEnd.y + minmaxs[j][1];
|
||||
vecEnd.z = vecHullEnd.z + minmaxs[k][2];
|
||||
|
||||
UTIL_TraceLine( vecSrc, vecEnd, MASK_SHOT_HULL, pOwner, COLLISION_GROUP_NONE, &tmpTrace );
|
||||
if ( tmpTrace.fraction < 1.0 )
|
||||
{
|
||||
float thisDistance = (tmpTrace.endpos - vecSrc).Length();
|
||||
if ( thisDistance < distance )
|
||||
{
|
||||
hitTrace = tmpTrace;
|
||||
distance = thisDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
hitTrace = tmpTrace;
|
||||
}
|
||||
|
||||
|
||||
return ACT_VM_HITCENTER;
|
||||
}
|
||||
|
||||
#ifdef HL1MP_CLIENT_DLL
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Handle jeep impacts
|
||||
//-----------------------------------------------------------------------------
|
||||
void ImpactCrowbarCallback( const CEffectData &data )
|
||||
{
|
||||
trace_t tr;
|
||||
Vector vecOrigin, vecStart, vecShotDir;
|
||||
int iMaterial, iDamageType, iHitbox;
|
||||
short nSurfaceProp;
|
||||
C_BaseEntity *pEntity = ParseImpactData( data, &vecOrigin, &vecStart, &vecShotDir, nSurfaceProp, iMaterial, iDamageType, iHitbox );
|
||||
|
||||
bool bIsWorld = ( pEntity->entindex() == 0 );
|
||||
|
||||
if ( !pEntity )
|
||||
{
|
||||
// This happens for impacts that occur on an object that's then destroyed.
|
||||
// Clear out the fraction so it uses the server's data
|
||||
tr.fraction = 1.0;
|
||||
GetActiveWeapon()->WeaponSound( bIsWorld ? MELEE_HIT_WORLD : MELEE_HIT );
|
||||
return;
|
||||
}
|
||||
|
||||
// If we hit, perform our custom effects and play the sound
|
||||
if ( Impact( vecOrigin, vecStart, iMaterial, iDamageType, iHitbox, pEntity, tr ) )
|
||||
{
|
||||
// Check for custom effects based on the Decal index
|
||||
PerformCustomEffects( vecOrigin, tr, vecShotDir, iMaterial, 2 );
|
||||
}
|
||||
|
||||
GetActiveWeapon()->WeaponSound( bIsWorld ? MELEE_HIT_WORLD : MELEE_HIT );
|
||||
}
|
||||
|
||||
DECLARE_CLIENT_EFFECT( "ImpactCrowbar", ImpactCrowbarCallback );
|
||||
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CWeaponCrowbar::ImpactEffect( void )
|
||||
{
|
||||
//FIXME: need new decals
|
||||
#ifdef HL1MP_CLIENT_DLL
|
||||
// in hl1mp force the basic crowbar sound
|
||||
UTIL_ImpactTrace( &m_traceHit, DMG_CLUB, "ImpactCrowbar" );
|
||||
#else
|
||||
UTIL_ImpactTrace( &m_traceHit, DMG_CLUB );
|
||||
#endif
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Purpose : Starts the swing of the weapon and determines the animation
|
||||
//------------------------------------------------------------------------------
|
||||
void CWeaponCrowbar::Swing( void )
|
||||
{
|
||||
// Try a ray
|
||||
CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
|
||||
if ( !pOwner )
|
||||
return;
|
||||
|
||||
Vector swingStart = pOwner->Weapon_ShootPosition( );
|
||||
Vector forward;
|
||||
|
||||
pOwner->EyeVectors( &forward, NULL, NULL );
|
||||
|
||||
Vector swingEnd = swingStart + forward * CROWBAR_RANGE;
|
||||
|
||||
UTIL_TraceLine( swingStart, swingEnd, MASK_SHOT_HULL, pOwner, COLLISION_GROUP_NONE, &m_traceHit );
|
||||
m_nHitActivity = ACT_VM_HITCENTER;
|
||||
|
||||
if ( m_traceHit.fraction == 1.0 )
|
||||
{
|
||||
float bludgeonHullRadius = 1.732f * BLUDGEON_HULL_DIM; // hull is +/- 16, so use cuberoot of 2 to determine how big the hull is from center to the corner point
|
||||
|
||||
// Back off by hull "radius"
|
||||
swingEnd -= forward * bludgeonHullRadius;
|
||||
|
||||
UTIL_TraceHull( swingStart, swingEnd, g_bludgeonMins, g_bludgeonMaxs, MASK_SHOT_HULL, pOwner, COLLISION_GROUP_NONE, &m_traceHit );
|
||||
if ( m_traceHit.fraction < 1.0 )
|
||||
{
|
||||
m_nHitActivity = ChooseIntersectionPointAndActivity( m_traceHit, g_bludgeonMins, g_bludgeonMaxs, pOwner );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -------------------------
|
||||
// Miss
|
||||
// -------------------------
|
||||
if ( m_traceHit.fraction == 1.0f )
|
||||
{
|
||||
m_nHitActivity = ACT_VM_MISSCENTER;
|
||||
|
||||
//Play swing sound
|
||||
WeaponSound( SINGLE );
|
||||
|
||||
//Setup our next attack times
|
||||
m_flNextPrimaryAttack = gpGlobals->curtime + CROWBAR_REFIRE_MISS;
|
||||
}
|
||||
else
|
||||
{
|
||||
Hit();
|
||||
|
||||
//Setup our next attack times
|
||||
m_flNextPrimaryAttack = gpGlobals->curtime + CROWBAR_REFIRE_HIT;
|
||||
}
|
||||
|
||||
//Send the anim
|
||||
SendWeaponAnim( m_nHitActivity );
|
||||
pOwner->SetAnimation( PLAYER_ATTACK1 );
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Snark
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "npcevent.h"
|
||||
#include "hl1_basecombatweapon_shared.h"
|
||||
#include "basecombatcharacter.h"
|
||||
#include "ai_basenpc.h"
|
||||
#include "player.h"
|
||||
#include "gamerules.h"
|
||||
#include "in_buttons.h"
|
||||
#include "soundent.h"
|
||||
#include "game.h"
|
||||
#include "vstdlib/random.h"
|
||||
#include "engine/IEngineSound.h"
|
||||
#include "hl1_npc_snark.h"
|
||||
#include "beam_shared.h"
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CWeaponSnark
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
#define SNARK_NEST_MODEL "models/w_sqknest.mdl"
|
||||
|
||||
|
||||
class CWeaponSnark : public CBaseHL1CombatWeapon
|
||||
{
|
||||
DECLARE_CLASS( CWeaponSnark, CBaseHL1CombatWeapon );
|
||||
public:
|
||||
|
||||
CWeaponSnark( void );
|
||||
|
||||
void Precache( void );
|
||||
void PrimaryAttack( void );
|
||||
void WeaponIdle( void );
|
||||
bool Deploy( void );
|
||||
bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL );
|
||||
|
||||
DECLARE_SERVERCLASS();
|
||||
DECLARE_DATADESC();
|
||||
|
||||
private:
|
||||
bool m_bJustThrown;
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( weapon_snark, CWeaponSnark );
|
||||
|
||||
PRECACHE_WEAPON_REGISTER( weapon_snark );
|
||||
|
||||
IMPLEMENT_SERVERCLASS_ST( CWeaponSnark, DT_WeaponSnark )
|
||||
END_SEND_TABLE()
|
||||
|
||||
BEGIN_DATADESC( CWeaponSnark )
|
||||
DEFINE_FIELD( m_bJustThrown, FIELD_BOOLEAN ),
|
||||
END_DATADESC()
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Constructor
|
||||
//-----------------------------------------------------------------------------
|
||||
CWeaponSnark::CWeaponSnark( void )
|
||||
{
|
||||
m_bReloadsSingly = false;
|
||||
m_bFiresUnderwater = true;
|
||||
m_bJustThrown = false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CWeaponSnark::Precache( void )
|
||||
{
|
||||
BaseClass::Precache();
|
||||
|
||||
PrecacheScriptSound( "WpnSnark.PrimaryAttack" );
|
||||
PrecacheScriptSound( "WpnSnark.Deploy" );
|
||||
|
||||
UTIL_PrecacheOther("monster_snark");
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CWeaponSnark::PrimaryAttack( void )
|
||||
{
|
||||
// Only the player fires this way so we can cast
|
||||
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
|
||||
|
||||
if ( !pPlayer )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) <= 0 )
|
||||
return;
|
||||
|
||||
Vector vecForward;
|
||||
pPlayer->EyeVectors( &vecForward );
|
||||
|
||||
// find place to toss monster
|
||||
// Does this need to consider a crouched player?
|
||||
Vector vecStart = pPlayer->WorldSpaceCenter() + (vecForward * 20);
|
||||
Vector vecEnd = vecStart + (vecForward * 44);
|
||||
trace_t tr;
|
||||
UTIL_TraceLine( vecStart, vecEnd, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
|
||||
if ( tr.allsolid || tr.startsolid || tr.fraction <= 0.25 )
|
||||
return;
|
||||
|
||||
// player "shoot" animation
|
||||
SendWeaponAnim( ACT_VM_PRIMARYATTACK );
|
||||
pPlayer->SetAnimation( PLAYER_ATTACK1 );
|
||||
|
||||
CSnark *pSnark = (CSnark*)Create( "monster_snark", tr.endpos, pPlayer->EyeAngles(), GetOwner() );
|
||||
if ( pSnark )
|
||||
{
|
||||
pSnark->SetAbsVelocity( vecForward * 200 + pPlayer->GetAbsVelocity() );
|
||||
}
|
||||
|
||||
// play hunt sound
|
||||
CPASAttenuationFilter filter( this );
|
||||
EmitSound( filter, entindex(), "WpnSnark.PrimaryAttack" );
|
||||
|
||||
CSoundEnt::InsertSound( SOUND_DANGER, GetAbsOrigin(), 200, 0.2 );
|
||||
|
||||
pPlayer->RemoveAmmo( 1, m_iPrimaryAmmoType );
|
||||
|
||||
m_bJustThrown = true;
|
||||
|
||||
m_flNextPrimaryAttack = gpGlobals->curtime + 0.3;
|
||||
SetWeaponIdleTime( gpGlobals->curtime + 1.0 );
|
||||
}
|
||||
|
||||
void CWeaponSnark::WeaponIdle( void )
|
||||
{
|
||||
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
|
||||
|
||||
if ( !pPlayer )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !HasWeaponIdleTimeElapsed() )
|
||||
return;
|
||||
|
||||
if ( m_bJustThrown )
|
||||
{
|
||||
m_bJustThrown = false;
|
||||
|
||||
if ( pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) <= 0 )
|
||||
{
|
||||
if ( !pPlayer->SwitchToNextBestWeapon( pPlayer->GetActiveWeapon() ) )
|
||||
Holster();
|
||||
}
|
||||
else
|
||||
{
|
||||
SendWeaponAnim( ACT_VM_DRAW );
|
||||
SetWeaponIdleTime( gpGlobals->curtime + random->RandomFloat( 10, 15 ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( random->RandomFloat( 0, 1 ) <= 0.75 )
|
||||
{
|
||||
SendWeaponAnim( ACT_VM_IDLE );
|
||||
}
|
||||
else
|
||||
{
|
||||
SendWeaponAnim( ACT_VM_FIDGET );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CWeaponSnark::Deploy( void )
|
||||
{
|
||||
CPASAttenuationFilter filter( this );
|
||||
EmitSound( filter, entindex(), "WpnSnark.Deploy" );
|
||||
|
||||
return BaseClass::Deploy();
|
||||
}
|
||||
|
||||
bool CWeaponSnark::Holster( CBaseCombatWeapon *pSwitchingTo )
|
||||
{
|
||||
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
|
||||
if ( !pPlayer )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( !BaseClass::Holster( pSwitchingTo ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) <= 0 )
|
||||
{
|
||||
SetThink( &CWeaponSnark::DestroyItem );
|
||||
SetNextThink( gpGlobals->curtime + 0.1 );
|
||||
}
|
||||
|
||||
pPlayer->SetNextAttack( gpGlobals->curtime + 0.5 );
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,573 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Tripmine
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "npcevent.h"
|
||||
#include "hl1_basecombatweapon_shared.h"
|
||||
#include "basecombatcharacter.h"
|
||||
#include "ai_basenpc.h"
|
||||
#include "player.h"
|
||||
#include "gamerules.h"
|
||||
#include "in_buttons.h"
|
||||
#include "soundent.h"
|
||||
#include "game.h"
|
||||
#include "vstdlib/random.h"
|
||||
#include "engine/IEngineSound.h"
|
||||
#include "hl1_player.h"
|
||||
#include "hl1_basegrenade.h"
|
||||
#include "beam_shared.h"
|
||||
|
||||
extern ConVar sk_plr_dmg_tripmine;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CWeaponTripMine
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
#define TRIPMINE_MODEL "models/w_tripmine.mdl"
|
||||
|
||||
|
||||
class CWeaponTripMine : public CBaseHL1CombatWeapon
|
||||
{
|
||||
DECLARE_CLASS( CWeaponTripMine, CBaseHL1CombatWeapon );
|
||||
public:
|
||||
|
||||
CWeaponTripMine( void );
|
||||
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void Equip( CBaseCombatCharacter *pOwner );
|
||||
void PrimaryAttack( void );
|
||||
void WeaponIdle( void );
|
||||
bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL );
|
||||
|
||||
DECLARE_SERVERCLASS();
|
||||
DECLARE_DATADESC();
|
||||
|
||||
private:
|
||||
int m_iGroundIndex;
|
||||
int m_iPickedUpIndex;
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( weapon_tripmine, CWeaponTripMine );
|
||||
|
||||
PRECACHE_WEAPON_REGISTER( weapon_tripmine );
|
||||
|
||||
IMPLEMENT_SERVERCLASS_ST( CWeaponTripMine, DT_WeaponTripMine )
|
||||
END_SEND_TABLE()
|
||||
|
||||
BEGIN_DATADESC( CWeaponTripMine )
|
||||
END_DATADESC()
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Constructor
|
||||
//-----------------------------------------------------------------------------
|
||||
CWeaponTripMine::CWeaponTripMine( void )
|
||||
{
|
||||
m_bReloadsSingly = false;
|
||||
m_bFiresUnderwater = true;
|
||||
}
|
||||
|
||||
void CWeaponTripMine::Spawn( void )
|
||||
{
|
||||
BaseClass::Spawn();
|
||||
|
||||
m_iWorldModelIndex = m_iGroundIndex;
|
||||
SetModel( TRIPMINE_MODEL );
|
||||
|
||||
SetActivity( ACT_TRIPMINE_GROUND );
|
||||
ResetSequenceInfo( );
|
||||
m_flPlaybackRate = 0;
|
||||
|
||||
if ( !g_pGameRules->IsDeathmatch() )
|
||||
{
|
||||
UTIL_SetSize( this, Vector(-16, -16, 0), Vector(16, 16, 28) );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CWeaponTripMine::Precache( void )
|
||||
{
|
||||
BaseClass::Precache();
|
||||
|
||||
m_iGroundIndex = PrecacheModel( TRIPMINE_MODEL );
|
||||
m_iPickedUpIndex = PrecacheModel( GetWorldModel() );
|
||||
|
||||
UTIL_PrecacheOther( "monster_tripmine" );
|
||||
}
|
||||
|
||||
void CWeaponTripMine::Equip( CBaseCombatCharacter *pOwner )
|
||||
{
|
||||
m_iWorldModelIndex = m_iPickedUpIndex;
|
||||
|
||||
BaseClass::Equip( pOwner );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CWeaponTripMine::PrimaryAttack( void )
|
||||
{
|
||||
CHL1_Player *pPlayer = ToHL1Player( GetOwner() );
|
||||
if ( !pPlayer )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) <= 0 )
|
||||
return;
|
||||
|
||||
Vector vecAiming = pPlayer->GetAutoaimVector( 0 );
|
||||
Vector vecSrc = pPlayer->Weapon_ShootPosition( );
|
||||
|
||||
trace_t tr;
|
||||
|
||||
UTIL_TraceLine( vecSrc, vecSrc + vecAiming * 64, MASK_SHOT, pPlayer, COLLISION_GROUP_NONE, &tr );
|
||||
|
||||
if ( tr.fraction < 1.0 )
|
||||
{
|
||||
CBaseEntity *pEntity = tr.m_pEnt;
|
||||
if ( pEntity && !( pEntity->GetFlags() & FL_CONVEYOR ) )
|
||||
{
|
||||
QAngle angles;
|
||||
VectorAngles( tr.plane.normal, angles );
|
||||
|
||||
CBaseEntity::Create( "monster_tripmine", tr.endpos + tr.plane.normal * 2, angles, pPlayer );
|
||||
|
||||
pPlayer->RemoveAmmo( 1, m_iPrimaryAmmoType );
|
||||
|
||||
pPlayer->SetAnimation( PLAYER_ATTACK1 );
|
||||
|
||||
if ( pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) <= 0 )
|
||||
{
|
||||
if ( !pPlayer->SwitchToNextBestWeapon( pPlayer->GetActiveWeapon() ) )
|
||||
Holster();
|
||||
}
|
||||
else
|
||||
{
|
||||
SendWeaponAnim( ACT_VM_DRAW );
|
||||
SetWeaponIdleTime( gpGlobals->curtime + random->RandomFloat( 10, 15 ) );
|
||||
}
|
||||
|
||||
m_flNextPrimaryAttack = gpGlobals->curtime + 0.5;
|
||||
|
||||
SetWeaponIdleTime( gpGlobals->curtime ); // MO curtime correct ?
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SetWeaponIdleTime( m_flTimeWeaponIdle = gpGlobals->curtime + random->RandomFloat( 10, 15 ) );
|
||||
}
|
||||
}
|
||||
|
||||
void CWeaponTripMine::WeaponIdle( void )
|
||||
{
|
||||
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
|
||||
|
||||
if ( !pPlayer )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !HasWeaponIdleTimeElapsed() )
|
||||
return;
|
||||
|
||||
int iAnim;
|
||||
|
||||
if ( random->RandomFloat( 0, 1 ) <= 0.75 )
|
||||
{
|
||||
iAnim = ACT_VM_IDLE;
|
||||
}
|
||||
else
|
||||
{
|
||||
iAnim = ACT_VM_FIDGET;
|
||||
}
|
||||
|
||||
SendWeaponAnim( iAnim );
|
||||
}
|
||||
|
||||
bool CWeaponTripMine::Holster( CBaseCombatWeapon *pSwitchingTo )
|
||||
{
|
||||
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
|
||||
if ( !pPlayer )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( !BaseClass::Holster( pSwitchingTo ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) <= 0 )
|
||||
{
|
||||
SetThink( &CWeaponTripMine::DestroyItem );
|
||||
SetNextThink( gpGlobals->curtime + 0.1 );
|
||||
}
|
||||
|
||||
pPlayer->SetNextAttack( gpGlobals->curtime + 0.5 );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CTripmineGrenade
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#define TRIPMINE_BEAM_SPRITE "sprites/laserbeam.vmt"
|
||||
|
||||
class CTripmineGrenade : public CHL1BaseGrenade
|
||||
{
|
||||
DECLARE_CLASS( CTripmineGrenade, CHL1BaseGrenade );
|
||||
public:
|
||||
CTripmineGrenade();
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
|
||||
int OnTakeDamage_Alive( const CTakeDamageInfo &info );
|
||||
|
||||
void WarningThink( void );
|
||||
void PowerupThink( void );
|
||||
void BeamBreakThink( void );
|
||||
void DelayDeathThink( void );
|
||||
void Event_Killed( const CTakeDamageInfo &info );
|
||||
|
||||
DECLARE_DATADESC();
|
||||
|
||||
private:
|
||||
void MakeBeam( void );
|
||||
void KillBeam( void );
|
||||
|
||||
private:
|
||||
float m_flPowerUp;
|
||||
Vector m_vecDir;
|
||||
Vector m_vecEnd;
|
||||
float m_flBeamLength;
|
||||
|
||||
CHandle<CBaseEntity> m_hRealOwner;
|
||||
CHandle<CBeam> m_hBeam;
|
||||
|
||||
CHandle<CBaseEntity> m_hStuckOn;
|
||||
Vector m_posStuckOn;
|
||||
QAngle m_angStuckOn;
|
||||
|
||||
int m_iLaserModel;
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( monster_tripmine, CTripmineGrenade );
|
||||
|
||||
BEGIN_DATADESC( CTripmineGrenade )
|
||||
DEFINE_FIELD( m_flPowerUp, FIELD_TIME ),
|
||||
DEFINE_FIELD( m_vecDir, FIELD_VECTOR ),
|
||||
DEFINE_FIELD( m_vecEnd, FIELD_POSITION_VECTOR ),
|
||||
DEFINE_FIELD( m_flBeamLength, FIELD_FLOAT ),
|
||||
// DEFINE_FIELD( m_hBeam, FIELD_EHANDLE ),
|
||||
DEFINE_FIELD( m_hRealOwner, FIELD_EHANDLE ),
|
||||
DEFINE_FIELD( m_hStuckOn, FIELD_EHANDLE ),
|
||||
DEFINE_FIELD( m_posStuckOn, FIELD_POSITION_VECTOR ),
|
||||
DEFINE_FIELD( m_angStuckOn, FIELD_VECTOR ),
|
||||
//DEFINE_FIELD( m_iLaserModel, FIELD_INTEGER ),
|
||||
|
||||
// Function Pointers
|
||||
DEFINE_THINKFUNC( WarningThink ),
|
||||
DEFINE_THINKFUNC( PowerupThink ),
|
||||
DEFINE_THINKFUNC( BeamBreakThink ),
|
||||
DEFINE_THINKFUNC( DelayDeathThink ),
|
||||
END_DATADESC()
|
||||
|
||||
|
||||
CTripmineGrenade::CTripmineGrenade()
|
||||
{
|
||||
m_vecDir.Init();
|
||||
m_vecEnd.Init();
|
||||
}
|
||||
|
||||
void CTripmineGrenade::Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
// motor
|
||||
SetMoveType( MOVETYPE_FLY );
|
||||
SetSolid( SOLID_BBOX );
|
||||
AddSolidFlags( FSOLID_NOT_SOLID );
|
||||
SetModel( TRIPMINE_MODEL );
|
||||
|
||||
// Don't collide with the player (the beam will still be tripped by one, however)
|
||||
SetCollisionGroup( COLLISION_GROUP_WEAPON );
|
||||
|
||||
SetCycle( 0 );
|
||||
SetSequence( SelectWeightedSequence( ACT_TRIPMINE_WORLD ) );
|
||||
ResetSequenceInfo();
|
||||
m_flPlaybackRate = 0;
|
||||
|
||||
UTIL_SetSize( this, Vector( -8, -8, -8), Vector(8, 8, 8) );
|
||||
|
||||
m_flDamage = sk_plr_dmg_tripmine.GetFloat();
|
||||
m_DmgRadius = m_flDamage * 2.5;
|
||||
|
||||
if ( m_spawnflags & 1 )
|
||||
{
|
||||
// power up quickly
|
||||
m_flPowerUp = gpGlobals->curtime + 1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// power up in 2.5 seconds
|
||||
m_flPowerUp = gpGlobals->curtime + 2.5;
|
||||
}
|
||||
|
||||
SetThink( &CTripmineGrenade::PowerupThink );
|
||||
SetNextThink( gpGlobals->curtime + 0.2 );
|
||||
|
||||
m_takedamage = DAMAGE_YES;
|
||||
|
||||
m_iHealth = 1;
|
||||
|
||||
if ( GetOwnerEntity() != NULL )
|
||||
{
|
||||
// play deploy sound
|
||||
EmitSound( "TripmineGrenade.Deploy" );
|
||||
EmitSound( "TripmineGrenade.Charge" );
|
||||
|
||||
m_hRealOwner = GetOwnerEntity();
|
||||
}
|
||||
AngleVectors( GetAbsAngles(), &m_vecDir );
|
||||
m_vecEnd = GetAbsOrigin() + m_vecDir * MAX_TRACE_LENGTH;
|
||||
}
|
||||
|
||||
|
||||
void CTripmineGrenade::Precache( void )
|
||||
{
|
||||
PrecacheModel( TRIPMINE_MODEL );
|
||||
m_iLaserModel = PrecacheModel( TRIPMINE_BEAM_SPRITE );
|
||||
|
||||
PrecacheScriptSound( "TripmineGrenade.Deploy" );
|
||||
PrecacheScriptSound( "TripmineGrenade.Charge" );
|
||||
PrecacheScriptSound( "TripmineGrenade.Activate" );
|
||||
|
||||
}
|
||||
|
||||
|
||||
void CTripmineGrenade::WarningThink( void )
|
||||
{
|
||||
// set to power up
|
||||
SetThink( &CTripmineGrenade::PowerupThink );
|
||||
SetNextThink( gpGlobals->curtime + 1.0f );
|
||||
}
|
||||
|
||||
|
||||
void CTripmineGrenade::PowerupThink( void )
|
||||
{
|
||||
if ( m_hStuckOn == NULL )
|
||||
{
|
||||
trace_t tr;
|
||||
CBaseEntity *pOldOwner = GetOwnerEntity();
|
||||
|
||||
// don't explode if the player is standing in front of the laser
|
||||
UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + m_vecDir * 32, MASK_SHOT, NULL, COLLISION_GROUP_NONE, &tr );
|
||||
|
||||
if( tr.m_pEnt && pOldOwner &&
|
||||
( tr.m_pEnt == pOldOwner ) && pOldOwner->IsPlayer() )
|
||||
{
|
||||
m_flPowerUp += 0.1; //delay the arming
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
return;
|
||||
}
|
||||
|
||||
// find out what we've been stuck on
|
||||
SetOwnerEntity( NULL );
|
||||
|
||||
UTIL_TraceLine( GetAbsOrigin() + m_vecDir * 8, GetAbsOrigin() - m_vecDir * 32, MASK_SHOT, pOldOwner, COLLISION_GROUP_NONE, &tr );
|
||||
|
||||
if ( tr.startsolid )
|
||||
{
|
||||
SetOwnerEntity( pOldOwner );
|
||||
m_flPowerUp += 0.1;
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
return;
|
||||
}
|
||||
if ( tr.fraction < 1.0 )
|
||||
{
|
||||
SetOwnerEntity( tr.m_pEnt );
|
||||
m_hStuckOn = tr.m_pEnt;
|
||||
m_posStuckOn = m_hStuckOn->GetAbsOrigin();
|
||||
m_angStuckOn = m_hStuckOn->GetAbsAngles();
|
||||
}
|
||||
else
|
||||
{
|
||||
// somehow we've been deployed on nothing, or something that was there, but now isn't.
|
||||
// remove ourselves
|
||||
|
||||
StopSound( "TripmineGrenade.Deploy" );
|
||||
StopSound( "TripmineGrenade.Charge" );
|
||||
SetThink( &CBaseEntity::SUB_Remove );
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
// ALERT( at_console, "WARNING:Tripmine at %.0f, %.0f, %.0f removed\n", pev->origin.x, pev->origin.y, pev->origin.z );
|
||||
KillBeam();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if ( (m_posStuckOn != m_hStuckOn->GetAbsOrigin()) || (m_angStuckOn != m_hStuckOn->GetAbsAngles()) )
|
||||
{
|
||||
// what we were stuck on has moved, or rotated. Create a tripmine weapon and drop to ground
|
||||
|
||||
StopSound( "TripmineGrenade.Deploy" );
|
||||
StopSound( "TripmineGrenade.Charge" );
|
||||
CBaseEntity *pMine = Create( "weapon_tripmine", GetAbsOrigin() + m_vecDir * 24, GetAbsAngles() );
|
||||
pMine->AddSpawnFlags( SF_NORESPAWN );
|
||||
|
||||
SetThink( &CBaseEntity::SUB_Remove );
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
KillBeam();
|
||||
return;
|
||||
}
|
||||
|
||||
if ( gpGlobals->curtime > m_flPowerUp )
|
||||
{
|
||||
MakeBeam( );
|
||||
RemoveSolidFlags( FSOLID_NOT_SOLID );
|
||||
|
||||
m_bIsLive = true;
|
||||
|
||||
// play enabled sound
|
||||
EmitSound( "TripmineGrenade.Activate" );
|
||||
}
|
||||
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
}
|
||||
|
||||
|
||||
void CTripmineGrenade::KillBeam( void )
|
||||
{
|
||||
if ( m_hBeam )
|
||||
{
|
||||
UTIL_Remove( m_hBeam );
|
||||
m_hBeam = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CTripmineGrenade::MakeBeam( void )
|
||||
{
|
||||
trace_t tr;
|
||||
|
||||
UTIL_TraceLine( GetAbsOrigin(), m_vecEnd, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
|
||||
|
||||
m_flBeamLength = tr.fraction;
|
||||
|
||||
// set to follow laser spot
|
||||
SetThink( &CTripmineGrenade::BeamBreakThink );
|
||||
|
||||
SetNextThink( gpGlobals->curtime + 1.0f );
|
||||
|
||||
Vector vecTmpEnd = GetAbsOrigin() + m_vecDir * MAX_TRACE_LENGTH * m_flBeamLength;
|
||||
|
||||
m_hBeam = CBeam::BeamCreate( TRIPMINE_BEAM_SPRITE, 1.0 );
|
||||
m_hBeam->PointEntInit( vecTmpEnd, this );
|
||||
m_hBeam->SetColor( 0, 214, 198 );
|
||||
m_hBeam->SetScrollRate( 25.5 );
|
||||
m_hBeam->SetBrightness( 64 );
|
||||
m_hBeam->AddSpawnFlags( SF_BEAM_TEMPORARY ); // so it won't save and come back to haunt us later..
|
||||
}
|
||||
|
||||
|
||||
void CTripmineGrenade::BeamBreakThink( void )
|
||||
{
|
||||
bool bBlowup = false;
|
||||
trace_t tr;
|
||||
|
||||
// NOT MASK_SHOT because we want only simple hit boxes
|
||||
UTIL_TraceLine( GetAbsOrigin(), m_vecEnd, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
|
||||
|
||||
// ALERT( at_console, "%f : %f\n", tr.flFraction, m_flBeamLength );
|
||||
|
||||
// respawn detect.
|
||||
if ( !m_hBeam )
|
||||
{
|
||||
MakeBeam();
|
||||
|
||||
trace_t stuckOnTrace;
|
||||
Vector forward;
|
||||
GetVectors( &forward, NULL, NULL );
|
||||
|
||||
UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() - forward * 12.0f, MASK_SOLID, this, COLLISION_GROUP_NONE, &stuckOnTrace );
|
||||
|
||||
if ( stuckOnTrace.m_pEnt )
|
||||
{
|
||||
m_hStuckOn = stuckOnTrace.m_pEnt; // reset stuck on ent too
|
||||
}
|
||||
}
|
||||
|
||||
CBaseEntity *pEntity = tr.m_pEnt;
|
||||
CBaseCombatCharacter *pBCC = ToBaseCombatCharacter( pEntity );
|
||||
|
||||
if ( pBCC || fabs( m_flBeamLength - tr.fraction ) > 0.001 )
|
||||
{
|
||||
bBlowup = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( m_hStuckOn == NULL )
|
||||
bBlowup = true;
|
||||
else if ( m_posStuckOn != m_hStuckOn->GetAbsOrigin() )
|
||||
bBlowup = true;
|
||||
else if ( m_angStuckOn != m_hStuckOn->GetAbsAngles() )
|
||||
bBlowup = true;
|
||||
}
|
||||
|
||||
if ( bBlowup )
|
||||
{
|
||||
SetOwnerEntity( m_hRealOwner );
|
||||
m_iHealth = 0;
|
||||
Event_Killed( CTakeDamageInfo( this, m_hRealOwner, 100, GIB_NORMAL ) );
|
||||
return;
|
||||
}
|
||||
|
||||
SetNextThink( gpGlobals->curtime + 0.1 );
|
||||
}
|
||||
/*
|
||||
int CTripmineGrenade::OnTakeDamage_Alive( const CTakeDamageInfo &info )
|
||||
{
|
||||
if (gpGlobals->curtime < m_flPowerUp && info.GetDamage() < m_iHealth)
|
||||
{
|
||||
// disable
|
||||
// Create( "weapon_tripmine", GetLocalOrigin() + m_vecDir * 24, GetAngles() );
|
||||
SetThink( &CBaseEntity::SUB_Remove );
|
||||
SetNextThink( gpGlobals->curtime + 0.1f );
|
||||
KillBeam();
|
||||
return 0;
|
||||
}
|
||||
return BaseClass::OnTakeDamage_Alive( info );
|
||||
}*/
|
||||
|
||||
void CTripmineGrenade::Event_Killed( const CTakeDamageInfo &info )
|
||||
{
|
||||
m_takedamage = DAMAGE_NO;
|
||||
|
||||
if ( info.GetAttacker() && ( info.GetAttacker()->GetFlags() & FL_CLIENT ) )
|
||||
{
|
||||
// some client has destroyed this mine, he'll get credit for any kills
|
||||
SetOwnerEntity( info.GetAttacker() );
|
||||
}
|
||||
|
||||
SetThink( &CTripmineGrenade::DelayDeathThink );
|
||||
SetNextThink( gpGlobals->curtime + random->RandomFloat( 0.1, 0.3 ) );
|
||||
|
||||
StopSound( "TripmineGrenade.Charge" );
|
||||
}
|
||||
|
||||
void CTripmineGrenade::DelayDeathThink( void )
|
||||
{
|
||||
KillBeam();
|
||||
trace_t tr;
|
||||
UTIL_TraceLine ( GetAbsOrigin() + m_vecDir * 8, GetAbsOrigin() - m_vecDir * 64, MASK_SOLID, this, COLLISION_GROUP_NONE, & tr);
|
||||
|
||||
Explode( &tr, DMG_BLAST );
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
|
||||
#include "cbase.h"
|
||||
#include "hl1_items.h"
|
||||
#include "ammodef.h"
|
||||
|
||||
|
||||
#define WEAPONBOX_MODEL "models/w_weaponbox.mdl"
|
||||
|
||||
|
||||
class CWeaponBox : public CHL1Item
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CWeaponBox, CHL1Item );
|
||||
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
bool KeyValue( const char *szKeyName, const char *szValue );
|
||||
void BoxTouch( CBaseEntity *pPlayer );
|
||||
|
||||
DECLARE_DATADESC();
|
||||
|
||||
private:
|
||||
bool PackAmmo( char *szName, int iCount );
|
||||
int GiveAmmo( int iCount, char *szName, int iMax, int *pIndex = NULL );
|
||||
|
||||
int m_cAmmoTypes; // how many ammo types packed into this box (if packed by a level designer)
|
||||
string_t m_rgiszAmmo[MAX_AMMO_SLOTS]; // ammo names
|
||||
int m_rgAmmo[MAX_AMMO_SLOTS]; // ammo quantities
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS(weaponbox, CWeaponBox);
|
||||
PRECACHE_REGISTER(weaponbox);
|
||||
|
||||
BEGIN_DATADESC( CWeaponBox )
|
||||
DEFINE_ARRAY( m_rgiszAmmo, FIELD_STRING, MAX_AMMO_SLOTS ),
|
||||
DEFINE_ARRAY( m_rgAmmo, FIELD_INTEGER, MAX_AMMO_SLOTS ),
|
||||
DEFINE_FIELD( m_cAmmoTypes, FIELD_INTEGER ),
|
||||
|
||||
DEFINE_ENTITYFUNC( BoxTouch ),
|
||||
END_DATADESC()
|
||||
|
||||
|
||||
bool CWeaponBox::KeyValue( const char *szKeyName, const char *szValue )
|
||||
{
|
||||
if ( m_cAmmoTypes < MAX_AMMO_SLOTS )
|
||||
{
|
||||
if ( PackAmmo( (char *)szKeyName, atoi( szValue ) ) )
|
||||
{
|
||||
m_cAmmoTypes++;// count this new ammo type.
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning( "WeaponBox too full! only %d ammotypes allowed\n", MAX_AMMO_SLOTS );
|
||||
}
|
||||
|
||||
return BaseClass::KeyValue( szKeyName, szValue );
|
||||
}
|
||||
|
||||
void CWeaponBox::Spawn( void )
|
||||
{
|
||||
Precache();
|
||||
SetModel( WEAPONBOX_MODEL );
|
||||
BaseClass::Spawn();
|
||||
|
||||
PrecacheScriptSound( "Item.Pickup" );
|
||||
|
||||
SetTouch( &CWeaponBox::BoxTouch );
|
||||
}
|
||||
|
||||
|
||||
void CWeaponBox::Precache( void )
|
||||
{
|
||||
PrecacheModel( WEAPONBOX_MODEL );
|
||||
}
|
||||
|
||||
|
||||
void CWeaponBox::BoxTouch( CBaseEntity *pOther )
|
||||
{
|
||||
if ( !( GetFlags() & FL_ONGROUND ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !pOther->IsPlayer() )
|
||||
{
|
||||
// only players may touch a weaponbox.
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !pOther->IsAlive() )
|
||||
{
|
||||
// no dead guys.
|
||||
return;
|
||||
}
|
||||
|
||||
CBasePlayer *pPlayer = (CBasePlayer *)pOther;
|
||||
int i;
|
||||
|
||||
// dole out ammo
|
||||
for ( i = 0 ; i < MAX_AMMO_SLOTS ; i++ )
|
||||
{
|
||||
if ( m_rgiszAmmo[ i ] != NULL_STRING )
|
||||
{
|
||||
// there's some ammo of this type.
|
||||
pPlayer->GiveAmmo( m_rgAmmo[ i ], (char *)STRING( m_rgiszAmmo[ i ] ) );
|
||||
|
||||
// now empty the ammo from the weaponbox since we just gave it to the player
|
||||
m_rgiszAmmo[ i ] = NULL_STRING;
|
||||
m_rgAmmo[ i ] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
CPASAttenuationFilter filter( pOther, "Item.Pickup" );
|
||||
EmitSound( filter, pOther->entindex(), "Item.Pickup" );
|
||||
|
||||
SetTouch(NULL);
|
||||
if ( g_pGameRules->ItemShouldRespawn( this ) == GR_ITEM_RESPAWN_NO )
|
||||
{
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool CWeaponBox::PackAmmo( char *szName, int iCount )
|
||||
{
|
||||
char szConvertedName[ 32 ];
|
||||
|
||||
if ( FStrEq( szName, "" ) )
|
||||
{
|
||||
// error here
|
||||
Warning( "NULL String in PackAmmo!\n" );
|
||||
return false;
|
||||
}
|
||||
|
||||
Q_snprintf( szConvertedName, sizeof( szConvertedName ), "%s", szName );
|
||||
if ( !stricmp( szName, "bolts" ) )
|
||||
{
|
||||
Q_snprintf( szConvertedName, sizeof( szConvertedName ), "XBowBolt" );
|
||||
}
|
||||
if ( !stricmp( szName, "uranium" ) )
|
||||
{
|
||||
Q_snprintf( szConvertedName, sizeof( szConvertedName ), "Uranium" );
|
||||
}
|
||||
if ( !stricmp( szName, "9mm" ) )
|
||||
{
|
||||
Q_snprintf( szConvertedName, sizeof( szConvertedName ), "9mmRound" );
|
||||
}
|
||||
if ( !stricmp( szName, "Hand Grenade" ) )
|
||||
{
|
||||
Q_snprintf( szConvertedName, sizeof( szConvertedName ), "Grenade" );
|
||||
}
|
||||
if ( !stricmp( szName, "Hornets" ) )
|
||||
{
|
||||
Q_snprintf( szConvertedName, sizeof( szConvertedName ), "Hornet" );
|
||||
}
|
||||
if ( !stricmp( szName, "ARgrenades" ) )
|
||||
{
|
||||
Q_snprintf( szConvertedName, sizeof( szConvertedName ), "MP5_Grenade" );
|
||||
}
|
||||
if ( !stricmp( szName, "357" ) )
|
||||
{
|
||||
Q_snprintf( szConvertedName, sizeof( szConvertedName ), "357Round" );
|
||||
}
|
||||
if ( !stricmp( szName, "rockets" ) )
|
||||
{
|
||||
Q_snprintf( szConvertedName, sizeof( szConvertedName ), "RPG_Rocket" );
|
||||
}
|
||||
if ( !stricmp( szName, "Satchel Charge" ) )
|
||||
{
|
||||
Q_snprintf( szConvertedName, sizeof( szConvertedName ), "Satchel" );
|
||||
}
|
||||
if ( !stricmp( szName, "buckshot" ) )
|
||||
{
|
||||
Q_snprintf( szConvertedName, sizeof( szConvertedName ), "Buckshot" );
|
||||
}
|
||||
if ( !stricmp( szName, "Snarks" ) )
|
||||
{
|
||||
Q_snprintf( szConvertedName, sizeof( szConvertedName ), "Snark" );
|
||||
}
|
||||
if ( !stricmp( szName, "Trip Mine" ) )
|
||||
{
|
||||
Q_snprintf( szConvertedName, sizeof( szConvertedName ), "TripMine" );
|
||||
}
|
||||
|
||||
int iMaxCarry = GetAmmoDef()->MaxCarry( GetAmmoDef()->Index( szConvertedName ) );
|
||||
|
||||
if ( iMaxCarry > 0 && iCount > 0 )
|
||||
{
|
||||
//ALERT ( at_console, "Packed %d rounds of %s\n", iCount, STRING(iszName) );
|
||||
GiveAmmo( iCount, szConvertedName, iMaxCarry );
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// CWeaponBox - GiveAmmo
|
||||
//=========================================================
|
||||
int CWeaponBox::GiveAmmo( int iCount, char *szName, int iMax, int *pIndex )
|
||||
{
|
||||
int i;
|
||||
|
||||
for ( i = 1; ( i < MAX_AMMO_SLOTS ) && ( m_rgiszAmmo[i] != NULL_STRING ); i++ )
|
||||
{
|
||||
if ( stricmp( szName, STRING( m_rgiszAmmo[i] ) ) == 0 )
|
||||
{
|
||||
if (pIndex)
|
||||
*pIndex = i;
|
||||
|
||||
int iAdd = MIN( iCount, iMax - m_rgAmmo[i]);
|
||||
if (iCount == 0 || iAdd > 0)
|
||||
{
|
||||
m_rgAmmo[i] += iAdd;
|
||||
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (i < MAX_AMMO_SLOTS)
|
||||
{
|
||||
if (pIndex)
|
||||
*pIndex = i;
|
||||
|
||||
m_rgiszAmmo[i] = AllocPooledString( szName );
|
||||
m_rgAmmo[i] = iCount;
|
||||
|
||||
return i;
|
||||
}
|
||||
Warning( "out of named ammo slots\n");
|
||||
return i;
|
||||
}
|
||||
@@ -0,0 +1,432 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Basic BOT handling.
|
||||
//
|
||||
// $Workfile: $
|
||||
// $Date: $
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// $Log: $
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
|
||||
#include "cbase.h"
|
||||
#include "player.h"
|
||||
#include "hl1mp_player.h"
|
||||
#include "in_buttons.h"
|
||||
#include "movehelper_server.h"
|
||||
|
||||
void ClientPutInServer( edict_t *pEdict, const char *playername );
|
||||
void Bot_Think( CHL1MP_Player *pBot );
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
ConVar bot_forcefireweapon( "bot_forcefireweapon", "", 0, "Force bots with the specified weapon to fire." );
|
||||
ConVar bot_forceattack2( "bot_forceattack2", "0", 0, "When firing, use attack2." );
|
||||
ConVar bot_forceattackon( "bot_forceattackon", "0", 0, "When firing, don't tap fire, hold it down." );
|
||||
ConVar bot_flipout( "bot_flipout", "0", 0, "When on, all bots fire their guns." );
|
||||
ConVar bot_defend( "bot_defend", "0", 0, "Set to a team number, and that team will all keep their combat shields raised." );
|
||||
ConVar bot_changeclass( "bot_changeclass", "0", 0, "Force all bots to change to the specified class." );
|
||||
ConVar bot_zombie( "bot_zombie", "0", 0, "Brraaaaaiiiins." );
|
||||
static ConVar bot_mimic( "bot_mimic", "0", 0, "Bot uses usercmd of player by index." );
|
||||
static ConVar bot_mimic_yaw_offset( "bot_mimic_yaw_offset", "0", 0, "Offsets the bot yaw." );
|
||||
ConVar bot_attack( "bot_attack", "1", 0, "Shoot!" );
|
||||
|
||||
ConVar bot_sendcmd( "bot_sendcmd", "", 0, "Forces bots to send the specified command." );
|
||||
|
||||
ConVar bot_crouch( "bot_crouch", "0", 0, "Bot crouches" );
|
||||
|
||||
static int BotNumber = 1;
|
||||
static int g_iNextBotTeam = -1;
|
||||
static int g_iNextBotClass = -1;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
bool backwards;
|
||||
|
||||
float nextturntime;
|
||||
bool lastturntoright;
|
||||
|
||||
float nextstrafetime;
|
||||
float sidemove;
|
||||
|
||||
QAngle forwardAngle;
|
||||
QAngle lastAngles;
|
||||
|
||||
float m_flJoinTeamTime;
|
||||
int m_WantedTeam;
|
||||
int m_WantedClass;
|
||||
} botdata_t;
|
||||
|
||||
static botdata_t g_BotData[ MAX_PLAYERS ];
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Create a new Bot and put it in the game.
|
||||
// Output : Pointer to the new Bot, or NULL if there's no free clients.
|
||||
//-----------------------------------------------------------------------------
|
||||
CBasePlayer *BotPutInServer( bool bFrozen, int iTeam )
|
||||
{
|
||||
g_iNextBotTeam = iTeam;
|
||||
|
||||
char botname[ 64 ];
|
||||
Q_snprintf( botname, sizeof( botname ), "Bot%02i", BotNumber );
|
||||
|
||||
// This is an evil hack, but we use it to prevent sv_autojointeam from kicking in.
|
||||
|
||||
edict_t *pEdict = engine->CreateFakeClient( botname );
|
||||
|
||||
if (!pEdict)
|
||||
{
|
||||
Msg( "Failed to create Bot.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Allocate a CBasePlayer for the bot, and call spawn
|
||||
//ClientPutInServer( pEdict, botname );
|
||||
CHL1MP_Player *pPlayer = ((CHL1MP_Player *)CBaseEntity::Instance( pEdict ));
|
||||
pPlayer->ClearFlags();
|
||||
pPlayer->AddFlag( FL_CLIENT | FL_FAKECLIENT );
|
||||
|
||||
if ( bFrozen )
|
||||
pPlayer->AddEFlags( EFL_BOT_FROZEN );
|
||||
|
||||
char szReturnString[512];
|
||||
|
||||
Q_snprintf( szReturnString, sizeof (szReturnString ), "cl_playermodel %s\n", "gman" );
|
||||
engine->ClientCommand ( pPlayer->edict(), szReturnString );
|
||||
|
||||
BotNumber++;
|
||||
|
||||
g_BotData[pPlayer->entindex()-1].m_WantedTeam = iTeam;
|
||||
g_BotData[pPlayer->entindex()-1].m_flJoinTeamTime = gpGlobals->curtime + 0.3;
|
||||
|
||||
return pPlayer;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Run through all the Bots in the game and let them think.
|
||||
//-----------------------------------------------------------------------------
|
||||
void Bot_RunAll( void )
|
||||
{
|
||||
for ( int i = 1; i <= gpGlobals->maxClients; i++ )
|
||||
{
|
||||
CHL1MP_Player *pPlayer = ToHL1MPPlayer( UTIL_PlayerByIndex( i ) );
|
||||
|
||||
if ( pPlayer && (pPlayer->GetFlags() & FL_FAKECLIENT) )
|
||||
{
|
||||
Bot_Think( pPlayer );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool RunMimicCommand( CUserCmd& cmd )
|
||||
{
|
||||
if ( bot_mimic.GetInt() <= 0 )
|
||||
return false;
|
||||
|
||||
if ( bot_mimic.GetInt() > gpGlobals->maxClients )
|
||||
return false;
|
||||
|
||||
|
||||
CBasePlayer *pPlayer = UTIL_PlayerByIndex( bot_mimic.GetInt() );
|
||||
if ( !pPlayer )
|
||||
return false;
|
||||
|
||||
if ( !pPlayer->GetLastUserCommand() )
|
||||
return false;
|
||||
|
||||
cmd = *pPlayer->GetLastUserCommand();
|
||||
cmd.viewangles[YAW] += bot_mimic_yaw_offset.GetFloat();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Simulates a single frame of movement for a player
|
||||
// Input : *fakeclient -
|
||||
// *viewangles -
|
||||
// forwardmove -
|
||||
// sidemove -
|
||||
// upmove -
|
||||
// buttons -
|
||||
// impulse -
|
||||
// msec -
|
||||
// Output : virtual void
|
||||
//-----------------------------------------------------------------------------
|
||||
static void RunPlayerMove( CHL1MP_Player *fakeclient, const QAngle& viewangles, float forwardmove, float sidemove, float upmove, unsigned short buttons, byte impulse, float frametime )
|
||||
{
|
||||
if ( !fakeclient )
|
||||
return;
|
||||
|
||||
CUserCmd cmd;
|
||||
|
||||
// Store off the globals.. they're gonna get whacked
|
||||
float flOldFrametime = gpGlobals->frametime;
|
||||
float flOldCurtime = gpGlobals->curtime;
|
||||
|
||||
float flTimeBase = gpGlobals->curtime + gpGlobals->frametime - frametime;
|
||||
fakeclient->SetTimeBase( flTimeBase );
|
||||
|
||||
Q_memset( &cmd, 0, sizeof( cmd ) );
|
||||
|
||||
if ( !RunMimicCommand( cmd ) && !bot_zombie.GetBool() )
|
||||
{
|
||||
VectorCopy( viewangles, cmd.viewangles );
|
||||
cmd.forwardmove = forwardmove;
|
||||
cmd.sidemove = sidemove;
|
||||
cmd.upmove = upmove;
|
||||
cmd.buttons = buttons;
|
||||
cmd.impulse = impulse;
|
||||
cmd.random_seed = random->RandomInt( 0, 0x7fffffff );
|
||||
}
|
||||
|
||||
if( bot_crouch.GetInt() )
|
||||
cmd.buttons |= IN_DUCK;
|
||||
|
||||
if ( bot_attack.GetBool() )
|
||||
cmd.buttons |= IN_ATTACK;
|
||||
|
||||
MoveHelperServer()->SetHost( fakeclient );
|
||||
fakeclient->PlayerRunCommand( &cmd, MoveHelperServer() );
|
||||
|
||||
// save off the last good usercmd
|
||||
fakeclient->SetLastUserCommand( cmd );
|
||||
|
||||
// Clear out any fixangle that has been set
|
||||
fakeclient->pl.fixangle = FIXANGLE_NONE;
|
||||
|
||||
// Restore the globals..
|
||||
gpGlobals->frametime = flOldFrametime;
|
||||
gpGlobals->curtime = flOldCurtime;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Run this Bot's AI for one frame.
|
||||
//-----------------------------------------------------------------------------
|
||||
void Bot_Think( CHL1MP_Player *pBot )
|
||||
{
|
||||
// Make sure we stay being a bot
|
||||
pBot->AddFlag( FL_FAKECLIENT );
|
||||
|
||||
botdata_t *botdata = &g_BotData[ ENTINDEX( pBot->edict() ) - 1 ];
|
||||
|
||||
QAngle vecViewAngles;
|
||||
float forwardmove = 0.0;
|
||||
float sidemove = botdata->sidemove;
|
||||
float upmove = 0.0;
|
||||
unsigned short buttons = 0;
|
||||
byte impulse = 0;
|
||||
float frametime = gpGlobals->frametime;
|
||||
|
||||
vecViewAngles = pBot->GetLocalAngles();
|
||||
|
||||
|
||||
// Create some random values
|
||||
if ( pBot->IsAlive() && (pBot->GetSolid() == SOLID_BBOX) )
|
||||
{
|
||||
trace_t trace;
|
||||
|
||||
// Stop when shot
|
||||
if ( !pBot->IsEFlagSet(EFL_BOT_FROZEN) )
|
||||
{
|
||||
if ( pBot->m_iHealth == 100 )
|
||||
{
|
||||
forwardmove = 600 * ( botdata->backwards ? -1 : 1 );
|
||||
if ( botdata->sidemove != 0.0f )
|
||||
{
|
||||
forwardmove *= random->RandomFloat( 0.1, 1.0f );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
forwardmove = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Only turn if I haven't been hurt
|
||||
if ( !pBot->IsEFlagSet(EFL_BOT_FROZEN) && pBot->m_iHealth == 100 )
|
||||
{
|
||||
Vector vecEnd;
|
||||
Vector forward;
|
||||
|
||||
QAngle angle;
|
||||
float angledelta = 15.0;
|
||||
|
||||
int maxtries = (int)360.0/angledelta;
|
||||
|
||||
if ( botdata->lastturntoright )
|
||||
{
|
||||
angledelta = -angledelta;
|
||||
}
|
||||
|
||||
angle = pBot->GetLocalAngles();
|
||||
|
||||
Vector vecSrc;
|
||||
while ( --maxtries >= 0 )
|
||||
{
|
||||
AngleVectors( angle, &forward );
|
||||
|
||||
vecSrc = pBot->GetLocalOrigin() + Vector( 0, 0, 36 );
|
||||
|
||||
vecEnd = vecSrc + forward * 10;
|
||||
|
||||
UTIL_TraceHull( vecSrc, vecEnd, VEC_HULL_MIN_SCALED( pBot ), VEC_HULL_MAX_SCALED( pBot ),
|
||||
MASK_PLAYERSOLID, pBot, COLLISION_GROUP_NONE, &trace );
|
||||
|
||||
if ( trace.fraction == 1.0 )
|
||||
{
|
||||
if ( gpGlobals->curtime < botdata->nextturntime )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
angle.y += angledelta;
|
||||
|
||||
if ( angle.y > 180 )
|
||||
angle.y -= 360;
|
||||
else if ( angle.y < -180 )
|
||||
angle.y += 360;
|
||||
|
||||
botdata->nextturntime = gpGlobals->curtime + 2.0;
|
||||
botdata->lastturntoright = random->RandomInt( 0, 1 ) == 0 ? true : false;
|
||||
|
||||
botdata->forwardAngle = angle;
|
||||
botdata->lastAngles = angle;
|
||||
|
||||
}
|
||||
|
||||
|
||||
if ( gpGlobals->curtime >= botdata->nextstrafetime )
|
||||
{
|
||||
botdata->nextstrafetime = gpGlobals->curtime + 1.0f;
|
||||
|
||||
if ( random->RandomInt( 0, 5 ) == 0 )
|
||||
{
|
||||
botdata->sidemove = -600.0f + 1200.0f * random->RandomFloat( 0, 2 );
|
||||
}
|
||||
else
|
||||
{
|
||||
botdata->sidemove = 0;
|
||||
}
|
||||
sidemove = botdata->sidemove;
|
||||
|
||||
if ( random->RandomInt( 0, 20 ) == 0 )
|
||||
{
|
||||
botdata->backwards = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
botdata->backwards = false;
|
||||
}
|
||||
}
|
||||
|
||||
pBot->SetLocalAngles( angle );
|
||||
vecViewAngles = angle;
|
||||
}
|
||||
|
||||
// Is my team being forced to defend?
|
||||
if ( bot_defend.GetInt() == pBot->GetTeamNumber() )
|
||||
{
|
||||
buttons |= IN_ATTACK2;
|
||||
}
|
||||
// If bots are being forced to fire a weapon, see if I have it
|
||||
else if ( bot_forcefireweapon.GetString() )
|
||||
{
|
||||
CBaseCombatWeapon *pWeapon = pBot->Weapon_OwnsThisType( bot_forcefireweapon.GetString() );
|
||||
if ( pWeapon )
|
||||
{
|
||||
// Switch to it if we don't have it out
|
||||
CBaseCombatWeapon *pActiveWeapon = pBot->GetActiveWeapon();
|
||||
|
||||
// Switch?
|
||||
if ( pActiveWeapon != pWeapon )
|
||||
{
|
||||
pBot->Weapon_Switch( pWeapon );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Start firing
|
||||
// Some weapons require releases, so randomise firing
|
||||
if ( bot_forceattackon.GetBool() || (RandomFloat(0.0,1.0) > 0.5) )
|
||||
{
|
||||
buttons |= bot_forceattack2.GetBool() ? IN_ATTACK2 : IN_ATTACK;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( bot_flipout.GetInt() )
|
||||
{
|
||||
if ( bot_forceattackon.GetBool() || (RandomFloat(0.0,1.0) > 0.5) )
|
||||
{
|
||||
buttons |= bot_forceattack2.GetBool() ? IN_ATTACK2 : IN_ATTACK;
|
||||
}
|
||||
}
|
||||
|
||||
if ( strlen( bot_sendcmd.GetString() ) > 0 )
|
||||
{
|
||||
//send the cmd from this bot
|
||||
CCommand args;
|
||||
args.Tokenize( bot_sendcmd.GetString() );
|
||||
pBot->ClientCommand( args );
|
||||
|
||||
bot_sendcmd.SetValue("");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Wait for Reinforcement wave
|
||||
if ( !pBot->IsAlive() )
|
||||
{
|
||||
// Try hitting my buttons occasionally
|
||||
if ( random->RandomInt( 0, 100 ) > 80 )
|
||||
{
|
||||
// Respawn the bot
|
||||
if ( random->RandomInt( 0, 1 ) == 0 )
|
||||
{
|
||||
buttons |= IN_JUMP;
|
||||
}
|
||||
else
|
||||
{
|
||||
buttons = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( bot_flipout.GetInt() >= 2 )
|
||||
{
|
||||
|
||||
QAngle angOffset = RandomAngle( -1, 1 );
|
||||
|
||||
botdata->lastAngles += angOffset;
|
||||
|
||||
for ( int i = 0 ; i < 2; i++ )
|
||||
{
|
||||
if ( fabs( botdata->lastAngles[ i ] - botdata->forwardAngle[ i ] ) > 15.0f )
|
||||
{
|
||||
if ( botdata->lastAngles[ i ] > botdata->forwardAngle[ i ] )
|
||||
{
|
||||
botdata->lastAngles[ i ] = botdata->forwardAngle[ i ] + 15;
|
||||
}
|
||||
else
|
||||
{
|
||||
botdata->lastAngles[ i ] = botdata->forwardAngle[ i ] - 15;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
botdata->lastAngles[ 2 ] = 0;
|
||||
|
||||
pBot->SetLocalAngles( botdata->lastAngles );
|
||||
}
|
||||
|
||||
RunPlayerMove( pBot, pBot->GetLocalAngles(), forwardmove, sidemove, upmove, buttons, impulse, frametime );
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
#ifndef BOT_BASE_H
|
||||
#define BOT_BASE_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
// If iTeam or iClass is -1, then a team or class is randomly chosen.
|
||||
CBasePlayer *BotPutInServer( bool bFrozen, int iTeam );
|
||||
|
||||
|
||||
#endif // BOT_BASE_H
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "gameinterface.h"
|
||||
#include "mapentities.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
void CServerGameClients::GetPlayerLimits( int& minplayers, int& maxplayers, int &defaultMaxPlayers ) const
|
||||
{
|
||||
minplayers = defaultMaxPlayers = 8;
|
||||
maxplayers = MAX_PLAYERS - 1;
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------------------------- //
|
||||
// Mod-specific CServerGameDLL implementation.
|
||||
// -------------------------------------------------------------------------------------------- //
|
||||
|
||||
void CServerGameDLL::LevelInit_ParseAllEntities( const char *pMapEntities )
|
||||
{
|
||||
MapEntity_ParseAllEntities( pMapEntities, NULL );
|
||||
}
|
||||
@@ -0,0 +1,636 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Multiplayer Player for HL1.
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "hl1mp_player.h"
|
||||
#include "client.h"
|
||||
#include "team.h"
|
||||
|
||||
class CTEPlayerAnimEvent : public CBaseTempEntity
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CTEPlayerAnimEvent, CBaseTempEntity );
|
||||
DECLARE_SERVERCLASS();
|
||||
|
||||
CTEPlayerAnimEvent( const char *name ) : CBaseTempEntity( name )
|
||||
{
|
||||
}
|
||||
|
||||
CNetworkHandle( CBasePlayer, m_hPlayer );
|
||||
CNetworkVar( int, m_iEvent );
|
||||
CNetworkVar( int, m_nData );
|
||||
};
|
||||
|
||||
IMPLEMENT_SERVERCLASS_ST_NOBASE( CTEPlayerAnimEvent, DT_TEPlayerAnimEvent )
|
||||
SendPropEHandle( SENDINFO( m_hPlayer ) ),
|
||||
SendPropInt( SENDINFO( m_iEvent ), Q_log2( PLAYERANIMEVENT_COUNT ) + 1, SPROP_UNSIGNED ),
|
||||
SendPropInt( SENDINFO( m_nData ), 32 )
|
||||
END_SEND_TABLE()
|
||||
|
||||
static CTEPlayerAnimEvent g_TEPlayerAnimEvent( "PlayerAnimEvent" );
|
||||
|
||||
void TE_PlayerAnimEvent( CBasePlayer *pPlayer, PlayerAnimEvent_t event, int nData )
|
||||
{
|
||||
CPVSFilter filter( pPlayer->EyePosition() );
|
||||
|
||||
// The player himself doesn't need to be sent his animation events
|
||||
// unless cs_showanimstate wants to show them.
|
||||
// if ( !ToolsEnabled() && ( cl_showanimstate.GetInt() == pPlayer->entindex() ) )
|
||||
{
|
||||
// filter.RemoveRecipient( pPlayer );
|
||||
}
|
||||
|
||||
g_TEPlayerAnimEvent.m_hPlayer = pPlayer;
|
||||
g_TEPlayerAnimEvent.m_iEvent = event;
|
||||
g_TEPlayerAnimEvent.m_nData = nData;
|
||||
g_TEPlayerAnimEvent.Create( filter, 0 );
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
extern int gEvilImpulse101;
|
||||
|
||||
LINK_ENTITY_TO_CLASS( player_mp, CHL1MP_Player );
|
||||
PRECACHE_REGISTER( player_mp );
|
||||
|
||||
IMPLEMENT_SERVERCLASS_ST( CHL1MP_Player, DT_HL1MP_PLAYER )
|
||||
SendPropExclude( "DT_BaseAnimating", "m_flPoseParameter" ),
|
||||
SendPropExclude( "DT_BaseAnimating", "m_flPlaybackRate" ),
|
||||
SendPropExclude( "DT_BaseAnimating", "m_nSequence" ),
|
||||
SendPropExclude( "DT_BaseEntity", "m_angRotation" ),
|
||||
SendPropExclude( "DT_BaseAnimatingOverlay", "overlay_vars" ),
|
||||
|
||||
// cs_playeranimstate and clientside animation takes care of these on the client
|
||||
// SendPropExclude( "DT_ServerAnimationData" , "m_flCycle" ),
|
||||
SendPropExclude( "DT_AnimTimeMustBeFirst" , "m_flAnimTime" ),
|
||||
|
||||
SendPropAngle( SENDINFO_VECTORELEM(m_angEyeAngles, 0), 11 ),
|
||||
SendPropAngle( SENDINFO_VECTORELEM(m_angEyeAngles, 1), 11 ),
|
||||
|
||||
SendPropEHandle( SENDINFO( m_hRagdoll ) ),
|
||||
SendPropInt( SENDINFO( m_iSpawnInterpCounter), 4 ),
|
||||
SendPropInt( SENDINFO( m_iRealSequence ), 9 ),
|
||||
|
||||
|
||||
// SendPropDataTable( SENDINFO_DT( m_Shared ), &REFERENCE_SEND_TABLE( DT_TFCPlayerShared ) )
|
||||
END_SEND_TABLE()
|
||||
|
||||
void cc_CreatePredictionError_f()
|
||||
{
|
||||
CBaseEntity *pEnt = CBaseEntity::Instance( 1 );
|
||||
pEnt->SetAbsOrigin( pEnt->GetAbsOrigin() + Vector( 63, 0, 0 ) );
|
||||
}
|
||||
|
||||
ConCommand cc_CreatePredictionError( "CreatePredictionError", cc_CreatePredictionError_f, "Create a prediction error", FCVAR_CHEAT );
|
||||
|
||||
static const char * s_szModelPath = "models/player/mp/";
|
||||
|
||||
CHL1MP_Player::CHL1MP_Player()
|
||||
{
|
||||
m_PlayerAnimState = CreatePlayerAnimState( this );
|
||||
// item_list = 0;
|
||||
|
||||
UseClientSideAnimation();
|
||||
m_angEyeAngles.Init();
|
||||
// m_pCurStateInfo = NULL;
|
||||
m_lifeState = LIFE_DEAD; // Start "dead".
|
||||
|
||||
m_iSpawnInterpCounter = 0;
|
||||
m_flNextModelChangeTime = 0;
|
||||
m_flNextTeamChangeTime = 0;
|
||||
|
||||
// SetViewOffset( TFC_PLAYER_VIEW_OFFSET );
|
||||
|
||||
// SetContextThink( &CTFCPlayer::TFCPlayerThink, gpGlobals->curtime, "TFCPlayerThink" );
|
||||
}
|
||||
|
||||
CHL1MP_Player::~CHL1MP_Player()
|
||||
{
|
||||
m_PlayerAnimState->Release();
|
||||
}
|
||||
|
||||
void CHL1MP_Player::PostThink( void )
|
||||
{
|
||||
BaseClass::PostThink();
|
||||
|
||||
QAngle angles = GetLocalAngles();
|
||||
angles[PITCH] = 0;
|
||||
SetLocalAngles( angles );
|
||||
|
||||
// Store the eye angles pitch so the client can compute its animation state correctly.
|
||||
m_angEyeAngles = EyeAngles();
|
||||
|
||||
m_PlayerAnimState->Update( m_angEyeAngles[YAW], m_angEyeAngles[PITCH] );
|
||||
}
|
||||
|
||||
void CHL1MP_Player::Spawn( void )
|
||||
{
|
||||
if ( !IsObserver() )
|
||||
{
|
||||
RemoveEffects( EF_NODRAW );
|
||||
SetMoveType( MOVETYPE_WALK );
|
||||
RemoveSolidFlags( FSOLID_NOT_SOLID );
|
||||
|
||||
// if no model, force one
|
||||
if ( !GetModelPtr() )
|
||||
SetModel( "models/player/mp/gordon/gordon.mdl" );
|
||||
}
|
||||
|
||||
m_flNextModelChangeTime = 0;
|
||||
m_flNextTeamChangeTime = 0;
|
||||
|
||||
BaseClass::Spawn();
|
||||
|
||||
if ( !IsObserver() )
|
||||
{
|
||||
GiveDefaultItems();
|
||||
SetPlayerModel();
|
||||
}
|
||||
|
||||
m_bHasLongJump = false;
|
||||
|
||||
m_iSpawnInterpCounter = (m_iSpawnInterpCounter + 1) % 8;
|
||||
}
|
||||
|
||||
void CHL1MP_Player::DoAnimationEvent( PlayerAnimEvent_t event, int nData )
|
||||
{
|
||||
m_PlayerAnimState->DoAnimationEvent( event, nData );
|
||||
TE_PlayerAnimEvent( this, event, nData ); // Send to any clients who can see this guy.
|
||||
}
|
||||
|
||||
void CHL1MP_Player::GiveDefaultItems( void )
|
||||
{
|
||||
GiveNamedItem( "weapon_crowbar" );
|
||||
GiveNamedItem( "weapon_glock" );
|
||||
|
||||
CBasePlayer::GiveAmmo( 68, "9mmRound" );
|
||||
}
|
||||
|
||||
void CHL1MP_Player::UpdateOnRemove( void )
|
||||
{
|
||||
if ( m_hRagdoll )
|
||||
{
|
||||
UTIL_RemoveImmediate( m_hRagdoll );
|
||||
m_hRagdoll = NULL;
|
||||
}
|
||||
|
||||
BaseClass::UpdateOnRemove();
|
||||
}
|
||||
|
||||
|
||||
void CHL1MP_Player::DetonateSatchelCharges( void )
|
||||
{
|
||||
CBaseEntity *pSatchel = NULL;
|
||||
|
||||
while ( (pSatchel = gEntList.FindEntityByClassname( pSatchel, "monster_satchel" ) ) != NULL)
|
||||
{
|
||||
if ( pSatchel->GetOwnerEntity() == this )
|
||||
{
|
||||
pSatchel->Use( this, this, USE_ON, 0 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CHL1MP_Player::Event_Killed( const CTakeDamageInfo &info )
|
||||
{
|
||||
DoAnimationEvent( PLAYERANIMEVENT_DIE );
|
||||
// SetNumAnimOverlays( 0 );
|
||||
|
||||
|
||||
// Note: since we're dead, it won't draw us on the client, but we don't set EF_NODRAW
|
||||
// because we still want to transmit to the clients in our PVS.
|
||||
if ( !IsHLTV() )
|
||||
CreateRagdollEntity();
|
||||
|
||||
DetonateSatchelCharges();
|
||||
|
||||
BaseClass::Event_Killed( info );
|
||||
|
||||
m_lifeState = LIFE_DEAD;
|
||||
RemoveEffects( EF_NODRAW ); // still draw player body
|
||||
}
|
||||
|
||||
|
||||
void CHL1MP_Player::SetAnimation( PLAYER_ANIM playerAnim )
|
||||
{
|
||||
// BaseClass::SetAnimation( playerAnim );
|
||||
if ( playerAnim == PLAYER_ATTACK1 )
|
||||
{
|
||||
DoAnimationEvent( PLAYERANIMEVENT_FIRE_GUN );
|
||||
}
|
||||
|
||||
int animDesired;
|
||||
char szAnim[64];
|
||||
|
||||
float speed;
|
||||
|
||||
speed = GetAbsVelocity().Length2D();
|
||||
|
||||
if (GetFlags() & (FL_FROZEN|FL_ATCONTROLS))
|
||||
{
|
||||
speed = 0;
|
||||
playerAnim = PLAYER_IDLE;
|
||||
}
|
||||
|
||||
if ( playerAnim == PLAYER_ATTACK1 )
|
||||
{
|
||||
if ( speed > 0 )
|
||||
{
|
||||
playerAnim = PLAYER_WALK;
|
||||
}
|
||||
else
|
||||
{
|
||||
playerAnim = PLAYER_IDLE;
|
||||
}
|
||||
}
|
||||
|
||||
Activity idealActivity = ACT_WALK;// TEMP!!!!!
|
||||
|
||||
// This could stand to be redone. Why is playerAnim abstracted from activity? (sjb)
|
||||
if (playerAnim == PLAYER_JUMP)
|
||||
{
|
||||
idealActivity = ACT_HOP;
|
||||
}
|
||||
else if (playerAnim == PLAYER_SUPERJUMP)
|
||||
{
|
||||
idealActivity = ACT_LEAP;
|
||||
}
|
||||
else if (playerAnim == PLAYER_DIE)
|
||||
{
|
||||
if ( m_lifeState == LIFE_ALIVE )
|
||||
{
|
||||
idealActivity = ACT_DIERAGDOLL;
|
||||
}
|
||||
}
|
||||
else if (playerAnim == PLAYER_ATTACK1)
|
||||
{
|
||||
if ( GetActivity() == ACT_HOVER ||
|
||||
GetActivity() == ACT_SWIM ||
|
||||
GetActivity() == ACT_HOP ||
|
||||
GetActivity() == ACT_LEAP ||
|
||||
GetActivity() == ACT_DIESIMPLE )
|
||||
{
|
||||
idealActivity = GetActivity();
|
||||
}
|
||||
else
|
||||
{
|
||||
idealActivity = ACT_RANGE_ATTACK1;
|
||||
}
|
||||
}
|
||||
else if (playerAnim == PLAYER_IDLE || playerAnim == PLAYER_WALK)
|
||||
{
|
||||
if ( !( GetFlags() & FL_ONGROUND ) && (GetActivity() == ACT_HOP || GetActivity() == ACT_LEAP) ) // Still jumping
|
||||
{
|
||||
idealActivity = GetActivity();
|
||||
}
|
||||
else if ( GetWaterLevel() > 1 )
|
||||
{
|
||||
if ( speed == 0 )
|
||||
idealActivity = ACT_HOVER;
|
||||
else
|
||||
idealActivity = ACT_SWIM;
|
||||
}
|
||||
else if ( speed > 0 )
|
||||
{
|
||||
idealActivity = ACT_WALK;
|
||||
}
|
||||
else
|
||||
{
|
||||
idealActivity = ACT_IDLE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (idealActivity == ACT_RANGE_ATTACK1)
|
||||
{
|
||||
if ( GetFlags() & FL_DUCKING ) // crouching
|
||||
{
|
||||
Q_strncpy( szAnim, "crouch_shoot_" ,sizeof(szAnim));
|
||||
}
|
||||
else
|
||||
{
|
||||
Q_strncpy( szAnim, "ref_shoot_" ,sizeof(szAnim));
|
||||
}
|
||||
Q_strncat( szAnim, m_szAnimExtension ,sizeof(szAnim), COPY_ALL_CHARACTERS );
|
||||
animDesired = LookupSequence( szAnim );
|
||||
if (animDesired == -1)
|
||||
animDesired = 0;
|
||||
|
||||
if ( GetSequence() != animDesired || !SequenceLoops() )
|
||||
{
|
||||
SetCycle( 0 );
|
||||
}
|
||||
|
||||
// Tracker 24588: In single player when firing own weapon this causes eye and punchangle to jitter
|
||||
//if (!SequenceLoops())
|
||||
//{
|
||||
// IncrementInterpolationFrame();
|
||||
//}
|
||||
|
||||
SetActivity( idealActivity );
|
||||
ResetSequence( animDesired );
|
||||
}
|
||||
else if (idealActivity == ACT_IDLE)
|
||||
{
|
||||
if ( GetFlags() & FL_DUCKING )
|
||||
{
|
||||
animDesired = LookupSequence( "crouch_idle" );
|
||||
}
|
||||
else
|
||||
{
|
||||
animDesired = LookupSequence( "look_idle" );
|
||||
}
|
||||
if (animDesired == -1)
|
||||
animDesired = 0;
|
||||
|
||||
SetActivity( ACT_IDLE );
|
||||
}
|
||||
else if ( idealActivity == ACT_WALK )
|
||||
{
|
||||
if ( GetFlags() & FL_DUCKING )
|
||||
{
|
||||
animDesired = SelectWeightedSequence( ACT_CROUCH );
|
||||
SetActivity( ACT_CROUCH );
|
||||
}
|
||||
else
|
||||
{
|
||||
animDesired = SelectWeightedSequence( ACT_RUN );
|
||||
SetActivity( ACT_RUN );
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( GetActivity() == idealActivity)
|
||||
return;
|
||||
|
||||
SetActivity( idealActivity );
|
||||
|
||||
animDesired = SelectWeightedSequence( GetActivity() );
|
||||
|
||||
// Already using the desired animation?
|
||||
if (GetSequence() == animDesired)
|
||||
return;
|
||||
|
||||
m_iRealSequence = animDesired;
|
||||
ResetSequence( animDesired );
|
||||
SetCycle( 0 );
|
||||
return;
|
||||
}
|
||||
|
||||
// Already using the desired animation?
|
||||
if (GetSequence() == animDesired)
|
||||
return;
|
||||
|
||||
m_iRealSequence = animDesired;
|
||||
|
||||
//Msg( "Set animation to %d\n", animDesired );
|
||||
// Reset to first frame of desired animation
|
||||
ResetSequence( animDesired );
|
||||
SetCycle( 0 );
|
||||
}
|
||||
|
||||
static ConVar sv_debugweaponpickup( "sv_debugweaponpickup", "0", FCVAR_CHEAT, "Prints descriptive reasons as to why pickup did not work." );
|
||||
|
||||
// correct respawning of weapons
|
||||
bool CHL1MP_Player::BumpWeapon( CBaseCombatWeapon *pWeapon )
|
||||
{ CBaseCombatCharacter *pOwner = pWeapon->GetOwner();
|
||||
|
||||
// Can I have this weapon type?
|
||||
if ( !IsAllowedToPickupWeapons() )
|
||||
{
|
||||
if ( sv_debugweaponpickup.GetBool() )
|
||||
Msg("sv_debugweaponpickup: IsAllowedToPickupWeapons() returned false\n");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( pOwner || !Weapon_CanUse( pWeapon ) || !g_pGameRules->CanHavePlayerItem( this, pWeapon ) )
|
||||
{
|
||||
if ( sv_debugweaponpickup.GetBool() && pOwner )
|
||||
Msg("sv_debugweaponpickup: pOwner\n");
|
||||
|
||||
if ( sv_debugweaponpickup.GetBool() && !Weapon_CanUse( pWeapon ) )
|
||||
Msg("sv_debugweaponpickup: Can't use weapon\n");
|
||||
|
||||
if ( sv_debugweaponpickup.GetBool() && !g_pGameRules->CanHavePlayerItem( this, pWeapon ) )
|
||||
Msg("sv_debugweaponpickup: Gamerules says player can't have item\n");
|
||||
|
||||
if ( gEvilImpulse101 )
|
||||
{
|
||||
UTIL_Remove( pWeapon );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't let the player fetch weapons through walls (use MASK_SOLID so that you can't pickup through windows)
|
||||
if( !pWeapon->FVisible( this, MASK_SOLID ) && !(GetFlags() & FL_NOTARGET) )
|
||||
{
|
||||
if ( sv_debugweaponpickup.GetBool() && !FVisible( this, MASK_SOLID ) )
|
||||
Msg("sv_debugweaponpickup: Can't fetch weapon through a wall\n");
|
||||
|
||||
if ( sv_debugweaponpickup.GetBool() && !(GetFlags() & FL_NOTARGET) )
|
||||
Msg("sv_debugweaponpickup: NoTarget\n");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bOwnsWeaponAlready = !!Weapon_OwnsThisType( pWeapon->GetClassname(), pWeapon->GetSubType());
|
||||
|
||||
if ( bOwnsWeaponAlready == true )
|
||||
{
|
||||
//If we have room for the ammo, then "take" the weapon too.
|
||||
if ( Weapon_EquipAmmoOnly( pWeapon ) )
|
||||
{
|
||||
pWeapon->CheckRespawn();
|
||||
|
||||
UTIL_Remove( pWeapon );
|
||||
|
||||
if ( sv_debugweaponpickup.GetBool() )
|
||||
Msg("sv_debugweaponpickup: Picking up weapon\n");
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( sv_debugweaponpickup.GetBool() )
|
||||
Msg("sv_debugweaponpickup: Owns weapon already\n");
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
pWeapon->CheckRespawn();
|
||||
Weapon_Equip( pWeapon );
|
||||
|
||||
if ( sv_debugweaponpickup.GetBool() )
|
||||
Msg("sv_debugweaponpickup: Picking up weapon\n");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void CHL1MP_Player::ChangeTeam( int iTeamNum )
|
||||
{
|
||||
bool bKill = false;
|
||||
|
||||
if ( g_pGameRules->IsTeamplay() == true )
|
||||
{
|
||||
if ( iTeamNum != GetTeamNumber() && GetTeamNumber() != TEAM_UNASSIGNED )
|
||||
{
|
||||
bKill = true;
|
||||
}
|
||||
}
|
||||
|
||||
BaseClass::ChangeTeam( iTeamNum );
|
||||
|
||||
m_flNextTeamChangeTime = gpGlobals->curtime + 5;
|
||||
|
||||
if ( g_pGameRules->IsTeamplay() == true )
|
||||
{
|
||||
SetPlayerTeamModel();
|
||||
}
|
||||
else
|
||||
{
|
||||
SetPlayerModel();
|
||||
}
|
||||
|
||||
if ( bKill == true )
|
||||
{
|
||||
CommitSuicide();
|
||||
}
|
||||
}
|
||||
|
||||
void CHL1MP_Player::SetPlayerTeamModel( void )
|
||||
{
|
||||
int iTeamNum = GetTeamNumber();
|
||||
|
||||
if ( iTeamNum <= TEAM_SPECTATOR )
|
||||
return;
|
||||
|
||||
CTeam * pTeam = GetGlobalTeam( iTeamNum );
|
||||
|
||||
char szModelName[256];
|
||||
Q_snprintf( szModelName, 256, "%s%s/%s.mdl", s_szModelPath, pTeam->GetName(), pTeam->GetName() );
|
||||
|
||||
// Check to see if the model was properly precached, do not error out if not.
|
||||
int i = modelinfo->GetModelIndex( szModelName );
|
||||
if ( i == -1 )
|
||||
{
|
||||
Warning("Model %s does not exist.\n", szModelName );
|
||||
return;
|
||||
}
|
||||
|
||||
SetModel( szModelName );
|
||||
m_flNextModelChangeTime = gpGlobals->curtime + 5;
|
||||
}
|
||||
|
||||
|
||||
void CHL1MP_Player::SetPlayerModel( void )
|
||||
{
|
||||
char szBaseName[128];
|
||||
Q_FileBase( engine->GetClientConVarValue( engine->IndexOfEdict( edict() ), "cl_playermodel" ), szBaseName, 128 );
|
||||
|
||||
// Don't let it be 'none'; default to Barney
|
||||
if ( Q_stricmp( "none", szBaseName ) == 0 )
|
||||
{
|
||||
Q_strcpy( szBaseName, "gordon" );
|
||||
}
|
||||
|
||||
char szModelName[256];
|
||||
Q_snprintf( szModelName, 256, "%s%s/%s.mdl", s_szModelPath, szBaseName, szBaseName );
|
||||
|
||||
// Check to see if the model was properly precached, do not error out if not.
|
||||
int i = modelinfo->GetModelIndex( szModelName );
|
||||
if ( i == -1 )
|
||||
{
|
||||
SetModel( "models/player/mp/gordon/gordon.mdl" );
|
||||
engine->ClientCommand ( edict(), "cl_playermodel models/gordon.mdl\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
SetModel( szModelName );
|
||||
|
||||
m_flNextModelChangeTime = gpGlobals->curtime + 5;
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------------- //
|
||||
// Ragdoll entities.
|
||||
// -------------------------------------------------------------------------------- //
|
||||
|
||||
class CHL1MPRagdoll : public CBaseAnimatingOverlay
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CHL1MPRagdoll, CBaseAnimatingOverlay );
|
||||
DECLARE_SERVERCLASS();
|
||||
|
||||
// Transmit ragdolls to everyone.
|
||||
virtual int UpdateTransmitState()
|
||||
{
|
||||
return SetTransmitState( FL_EDICT_ALWAYS );
|
||||
}
|
||||
|
||||
public:
|
||||
// In case the client has the player entity, we transmit the player index.
|
||||
// In case the client doesn't have it, we transmit the player's model index, origin, and angles
|
||||
// so they can create a ragdoll in the right place.
|
||||
CNetworkHandle( CBaseEntity, m_hPlayer ); // networked entity handle
|
||||
CNetworkVector( m_vecRagdollVelocity );
|
||||
CNetworkVector( m_vecRagdollOrigin );
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( hl1mp_ragdoll, CHL1MPRagdoll );
|
||||
|
||||
IMPLEMENT_SERVERCLASS_ST_NOBASE( CHL1MPRagdoll, DT_HL1MPRagdoll )
|
||||
SendPropVector ( SENDINFO( m_vecRagdollOrigin), -1, SPROP_COORD ),
|
||||
SendPropEHandle ( SENDINFO( m_hPlayer ) ),
|
||||
SendPropModelIndex( SENDINFO( m_nModelIndex ) ),
|
||||
SendPropInt ( SENDINFO( m_nForceBone), 8, 0 ),
|
||||
SendPropVector ( SENDINFO( m_vecForce), -1, SPROP_NOSCALE ),
|
||||
SendPropVector ( SENDINFO( m_vecRagdollVelocity ) )
|
||||
END_SEND_TABLE()
|
||||
|
||||
|
||||
void CHL1MP_Player::CreateRagdollEntity( void )
|
||||
{
|
||||
if ( m_hRagdoll )
|
||||
{
|
||||
UTIL_RemoveImmediate( m_hRagdoll );
|
||||
m_hRagdoll = NULL;
|
||||
}
|
||||
|
||||
// If we already have a ragdoll, don't make another one.
|
||||
CHL1MPRagdoll *pRagdoll = dynamic_cast< CHL1MPRagdoll* >(m_hRagdoll.Get() );
|
||||
|
||||
if ( !pRagdoll )
|
||||
{
|
||||
// Create a new one
|
||||
pRagdoll = dynamic_cast< CHL1MPRagdoll* >( CreateEntityByName( "hl1mp_ragdoll" ) );
|
||||
}
|
||||
|
||||
if ( pRagdoll )
|
||||
{
|
||||
pRagdoll->m_hPlayer = this;
|
||||
pRagdoll->m_vecRagdollOrigin = GetAbsOrigin();
|
||||
pRagdoll->m_vecRagdollVelocity = GetAbsVelocity();
|
||||
pRagdoll->m_nModelIndex = m_nModelIndex;
|
||||
pRagdoll->m_nForceBone = m_nForceBone;
|
||||
//pRagdoll->m_vecForce = m_vecTotalBulletForce;
|
||||
pRagdoll->SetAbsOrigin( GetAbsOrigin() );
|
||||
|
||||
}
|
||||
|
||||
m_hRagdoll = pRagdoll;
|
||||
}
|
||||
|
||||
void CHL1MP_Player::CreateCorpse( void )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef HL1MP_PLAYER_H
|
||||
#define HL1MP_PLAYER_H
|
||||
#pragma once
|
||||
|
||||
#include "cbase.h"
|
||||
#include "hl1_player_shared.h"
|
||||
#include "hl1_player.h"
|
||||
#include "takedamageinfo.h"
|
||||
|
||||
|
||||
class CHL1MP_Player;
|
||||
|
||||
|
||||
//=============================================================================
|
||||
// >> HL1MP_Player
|
||||
//=============================================================================
|
||||
class CHL1MP_Player : public CHL1_Player
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CHL1MP_Player, CHL1_Player );
|
||||
DECLARE_SERVERCLASS();
|
||||
|
||||
CHL1MP_Player();
|
||||
~CHL1MP_Player( void );
|
||||
|
||||
virtual void Event_Killed( const CTakeDamageInfo &info );
|
||||
virtual void Spawn( void );
|
||||
virtual void PostThink( void );
|
||||
virtual void SetAnimation( PLAYER_ANIM playerAnim );
|
||||
void GiveDefaultItems( void );
|
||||
void CreateRagdollEntity( void );
|
||||
void UpdateOnRemove( void );
|
||||
virtual bool BecomeRagdollOnClient( const Vector &force ) { return true; };
|
||||
virtual void CreateCorpse( void );
|
||||
|
||||
virtual bool BumpWeapon( CBaseCombatWeapon *pWeapon );
|
||||
|
||||
virtual void ChangeTeam( int iTeamNum ) OVERRIDE;
|
||||
|
||||
void SetPlayerTeamModel( void );
|
||||
|
||||
float GetNextModelChangeTime( void ) { return m_flNextModelChangeTime; }
|
||||
float GetNextTeamChangeTime( void ) { return m_flNextTeamChangeTime; }
|
||||
|
||||
void SetPlayerModel( void );
|
||||
|
||||
void DoAnimationEvent( PlayerAnimEvent_t event, int nData = 0 );
|
||||
|
||||
virtual bool StartObserverMode (int mode)
|
||||
{
|
||||
if ( !IsHLTV() )
|
||||
return false;
|
||||
return BaseClass::StartObserverMode( mode );
|
||||
}
|
||||
|
||||
void DetonateSatchelCharges( void );
|
||||
|
||||
CNetworkVar( int, m_iRealSequence );
|
||||
|
||||
private:
|
||||
CNetworkHandle( CBaseEntity, m_hRagdoll );
|
||||
CNetworkVar( int, m_iSpawnInterpCounter );
|
||||
CNetworkQAngle( m_angEyeAngles );
|
||||
|
||||
IHL1MPPlayerAnimState* m_PlayerAnimState;
|
||||
float m_flNextModelChangeTime;
|
||||
float m_flNextTeamChangeTime;
|
||||
};
|
||||
|
||||
inline CHL1MP_Player *ToHL1MPPlayer( CBaseEntity *pEntity )
|
||||
{
|
||||
if ( !pEntity || !pEntity->IsPlayer() )
|
||||
return NULL;
|
||||
|
||||
return dynamic_cast<CHL1MP_Player*>( pEntity );
|
||||
}
|
||||
|
||||
|
||||
#endif //HL1MP_PLAYER_H
|
||||
Reference in New Issue
Block a user