add hl1,portal,dod source code

This commit is contained in:
nillerusr
2022-04-16 12:05:19 +03:00
parent bc6873014e
commit 23a370d9bb
524 changed files with 174173 additions and 0 deletions
+215
View File
@@ -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;
}
+51
View File
@@ -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
+86
View File
@@ -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 ) );
}
}
+116
View File
@@ -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 );
}
}
+42
View File
@@ -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
+199
View File
@@ -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
+73
View File
@@ -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
+219
View File
@@ -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 );
}
+55
View File
@@ -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;
}
+239
View File
@@ -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
+160
View File
@@ -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" );
}
+42
View File
@@ -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
+171
View File
@@ -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)
{
}
+49
View File
@@ -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
+410
View File
@@ -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);
+78
View File
@@ -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);
+384
View File
@@ -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 );
}
+61
View File
@@ -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);
+61
View File
@@ -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);
+45
View File
@@ -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;
}
}
+26
View File
@@ -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
+294
View File
@@ -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--;
}
+71
View File
@@ -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
+858
View File
@@ -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
+766
View File
@@ -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 );
}
}
}
+516
View File
@@ -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;
}
+63
View File
@@ -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
+938
View File
@@ -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();
}
+81
View File
@@ -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
+86
View File
@@ -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
+68
View File
@@ -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
+170
View File
@@ -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
+86
View File
@@ -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
+232
View File
@@ -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;
}
+951
View File
@@ -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()
+698
View File
@@ -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;
}
+63
View File
@@ -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
+117
View File
@@ -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
+400
View File
@@ -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;
}
}
}
+76
View File
@@ -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
+73
View File
@@ -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
+97
View File
@@ -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
+724
View File
@@ -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
+471
View File
@@ -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
+139
View File
@@ -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
+527
View File
@@ -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 );
}
+57
View File
@@ -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
+728
View File
@@ -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()
+125
View File
@@ -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
+743
View File
@@ -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()
+74
View File
@@ -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_
+307
View File
@@ -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();
}
+52
View File
@@ -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
+167
View File
@@ -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
+76
View File
@@ -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 );
}
+379
View File
@@ -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 );
}
+208
View File
@@ -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;
}
+573
View File
@@ -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 );
}
+242
View File
@@ -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;
}
+432
View File
@@ -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
+19
View File
@@ -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
+28
View File
@@ -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 );
}
+636
View File
@@ -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 )
{
}
+87
View File
@@ -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