Понедельник, 30.12.2024, 19:23
Приветствую Вас Guest | RSS
Главная страница | Статьи | Регистрация | Вход
Меню сайта

Случайный рисунок

Категории каталога
Туториалы по маппингу (HL2) [68]
Туториалы по маппингу для Half-Life
Туториалы по текстурированию (HL2) [10]
Туторы по текстурированию для Half-Life
Туториалы по моделированию (HL2) [9]
Туторы по моделированию для Half-Life
Туториалы по программированию (HL2) [53]
Туторы по прагроммированию для Half-life
Другие туториалы (HL1 и HL2) [4]
Туторы которые не вошли в другие категории
Half-Life 2 Beta [1]
Статьи о npc и weapon в бете Half-Life 2, а так же мануалы и FAQ.
Туториалы по маппингу (HL1) [14]
Туторы по маппингу для Half-Life 1
Туториалы по текстурированию (HL1) [1]
Туторы по текстурированию для Half-Life 1
Туториалы по моделированию (HL1) [1]
Туторы по моделированию для Half-Life 1
Туториалы по программированию (HL1) [30]
Туторы по программированию для Half-Life 1

Наш опрос
Очистить ли форум от всех сообщений и начать всё с чистого листа?
Всего ответов: 563

Начало » Статьи » Туториалы по программированию (HL2)

Npc_houndeye. Часть 1
В этом туториале вы узнаете как закодить одного монстра из хл1, а именно Houndeye (собака). Для начала создам в сервер части файл под названием npc_houndeye.cpp и в него впишем следующие:

//========= Copyright © 1996-2002, Valve LLC, All rights reserved. ============
//
// Purpose: Houndeye - a spooky sonic dog.
//
// $NoKeywords: $
//=============================================================================

#include "cbase.h"
#include "npc_houndeye.h"
#include "ai_default.h"
#include "ai_node.h"
#include "ai_route.h"
#include "AI_Navigator.h"
#include "AI_Motor.h"
#include "AI_Squad.h"
#include "AI_TacticalServices.h"
#include "soundent.h"
#include "EntityList.h"
#include "game.h"
#include "activitylist.h"
#include "hl2_shareddefs.h"
#include "grenade_energy.h"
#include "energy_wave.h"
#include "ai_interactions.h"
#include "ndebugoverlay.h"
#include "NPCEvent.h"
#include "player.h"
#include "vstdlib/random.h"
#include "engine/IEngineSound.h"
#include "movevars_shared.h"

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"

#define HOUNDEYE_MAX_ATTACK_RADIUS 500
#define HOUNDEYE_MIN_ATTACK_RADIUS 100

#define HOUNDEYE_EYE_FRAMES 4 // how many different switchable maps for the eye

ConVar sk_Houndeye_health( "sk_Houndeye_health","0");
ConVar sk_Houndeye_dmg_blast( "sk_Houndeye_dmg_blast","0");

//=========================================================
// Interactions
//=========================================================
int g_interactionHoundeyeGroupAttack = 0;
int g_interactionHoundeyeGroupRetreat = 0;
int g_interactionHoundeyeGroupRalley = 0;

//=========================================================
// Specialized Tasks
//=========================================================
enum
{
TASK_HOUND_CLOSE_EYE = LAST_SHARED_TASK,
TASK_HOUND_OPEN_EYE,
TASK_HOUND_THREAT_DISPLAY,
TASK_HOUND_FALL_ASLEEP,
TASK_HOUND_WAKE_UP,
TASK_HOUND_HOP_BACK,

TASK_HOUND_GET_PATH_TO_CIRCLE,
TASK_HOUND_REVERSE_STRAFE_DIR,
};

//-----------------------------------------------------------------------------
// Custom Conditions
//-----------------------------------------------------------------------------
enum Houndeye_Conds
{
COND_HOUND_GROUP_ATTACK = LAST_SHARED_CONDITION,
COND_HOUND_GROUP_RETREAT,
COND_HOUND_GROUP_RALLEY,
};

//=========================================================
// Specialized Shedules
//=========================================================
enum
{
SCHED_HOUND_AGITATED = LAST_SHARED_SCHEDULE,
SCHED_HOUND_HOP_RETREAT,
SCHED_HOUND_RANGE_ATTACK1,

SCHED_HOUND_ATTACK_STRAFE,
SCHED_HOUND_ATTACK_STRAFE_REVERSE,
SCHED_HOUND_GROUP_ATTACK,
SCHED_HOUND_GROUP_RETREAT,
SCHED_HOUND_CHASE_ENEMY,
SCHED_HOUND_COVER_WAIT,
SCHED_HOUND_GROUP_RALLEY,
};

//=========================================================
// Specialized activities
//=========================================================
int ACT_HOUND_GUARD;

//=========================================================
// Monster's Anim Events Go Here
//=========================================================
#define HOUND_AE_WARN 1
#define HOUND_AE_STARTATTACK 2
#define HOUND_AE_THUMP 3
#define HOUND_AE_ANGERSOUND1 4
#define HOUND_AE_ANGERSOUND2 5
#define HOUND_AE_HOPBACK 6
#define HOUND_AE_CLOSE_EYE 7
#define HOUND_AE_LEAP_HIT 8

//-----------------------------------------------------------------------------
// Purpose: Initialize the custom schedules
// Input :
// Output :
//-----------------------------------------------------------------------------
void CNPC_Houndeye::InitCustomSchedules(void)
{
INIT_CUSTOM_AI(CNPC_Houndeye);

ADD_CUSTOM_TASK(CNPC_Houndeye, TASK_HOUND_CLOSE_EYE);
ADD_CUSTOM_TASK(CNPC_Houndeye, TASK_HOUND_OPEN_EYE);
ADD_CUSTOM_TASK(CNPC_Houndeye, TASK_HOUND_THREAT_DISPLAY);
ADD_CUSTOM_TASK(CNPC_Houndeye, TASK_HOUND_FALL_ASLEEP);
ADD_CUSTOM_TASK(CNPC_Houndeye, TASK_HOUND_WAKE_UP);
ADD_CUSTOM_TASK(CNPC_Houndeye, TASK_HOUND_HOP_BACK);

ADD_CUSTOM_TASK(CNPC_Houndeye, TASK_HOUND_GET_PATH_TO_CIRCLE);
ADD_CUSTOM_TASK(CNPC_Houndeye, TASK_HOUND_REVERSE_STRAFE_DIR);

ADD_CUSTOM_CONDITION(CNPC_Houndeye, COND_HOUND_GROUP_ATTACK);
ADD_CUSTOM_CONDITION(CNPC_Houndeye, COND_HOUND_GROUP_RETREAT);

ADD_CUSTOM_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_HOP_RETREAT);
ADD_CUSTOM_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_RANGE_ATTACK1);
ADD_CUSTOM_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_ATTACK_STRAFE);
ADD_CUSTOM_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_ATTACK_STRAFE_REVERSE);
ADD_CUSTOM_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_GROUP_ATTACK);
ADD_CUSTOM_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_GROUP_RETREAT);
ADD_CUSTOM_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_CHASE_ENEMY);
ADD_CUSTOM_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_COVER_WAIT);
ADD_CUSTOM_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_GROUP_RALLEY);

ADD_CUSTOM_ACTIVITY(CNPC_Houndeye, ACT_HOUND_GUARD);

g_interactionHoundeyeGroupAttack = CBaseCombatCharacter::GetInteractionID();
g_interactionHoundeyeGroupRetreat = CBaseCombatCharacter::GetInteractionID();
g_interactionHoundeyeGroupRalley = CBaseCombatCharacter::GetInteractionID();

AI_LOAD_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_HOP_RETREAT);
AI_LOAD_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_RANGE_ATTACK1);
AI_LOAD_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_ATTACK_STRAFE);
AI_LOAD_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_ATTACK_STRAFE_REVERSE);
AI_LOAD_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_GROUP_ATTACK);
AI_LOAD_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_GROUP_RETREAT);
AI_LOAD_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_CHASE_ENEMY);
AI_LOAD_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_COVER_WAIT);
AI_LOAD_SCHEDULE(CNPC_Houndeye, SCHED_HOUND_GROUP_RALLEY);
}

LINK_ENTITY_TO_CLASS( npc_houndeye, CNPC_Houndeye );
IMPLEMENT_CUSTOM_AI( npc_houndeye, CNPC_Houndeye );

BEGIN_DATADESC( CNPC_Houndeye )

DEFINE_FIELD( m_fAsleep, FIELD_BOOLEAN ),
DEFINE_FIELD( m_fDontBlink, FIELD_BOOLEAN ),
DEFINE_FIELD( m_flNextSecondaryAttack, FIELD_TIME ),
DEFINE_FIELD( m_bLoopClockwise, FIELD_BOOLEAN ),
DEFINE_FIELD( m_pEnergyWave, FIELD_CLASSPTR ),
DEFINE_FIELD( m_flEndEnergyWaveTime, FIELD_TIME ),

END_DATADESC()

//=========================================================
// Classify - indicates this monster's place in the
// relationship table.
//=========================================================
Class_T CNPC_Houndeye::Classify ( void )
{
return CLASS_HOUNDEYE;
}

//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output :
//-----------------------------------------------------------------------------
int CNPC_Houndeye::RangeAttack1Conditions ( float flDot, float flDist )
{
// I'm not allowed to attack if standing in another hound eye
// (note houndeyes allowed to interpenetrate)
trace_t tr;
AI_TraceHull( GetAbsOrigin(), GetAbsOrigin() + Vector(0,0,0.1),
GetHullMins(), GetHullMaxs(),
MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr );
if (tr.startsolid)
{
CBaseEntity *pEntity = tr.m_pEnt;
if (pEntity->Classify() == CLASS_HOUNDEYE)
{
return( COND_NONE );
}
}

// If I'm really close to my enemy allow me to attack if
// I'm facing regardless of next attack time
if (flDist < 100 && flDot >= 0.3)
{
return COND_CAN_RANGE_ATTACK1;
}
if ( gpGlobals->curtime < m_flNextAttack )
{
return( COND_NONE );
}
if (flDist > ( HOUNDEYE_MAX_ATTACK_RADIUS * 0.5 ))
{
return COND_TOO_FAR_TO_ATTACK;
}
if (flDot < 0.3)
{
return COND_NOT_FACING_ATTACK;
}
return COND_CAN_RANGE_ATTACK1;
}

//-----------------------------------------------------------------------------
// Purpose: Overidden for human grunts because they hear the DANGER sound
// Input :
// Output :
//-----------------------------------------------------------------------------
int CNPC_Houndeye::GetSoundInterests( void )
{
return SOUND_WORLD |
SOUND_COMBAT |
SOUND_PLAYER |
SOUND_DANGER;
}

//=========================================================
// MaxYawSpeed - allows each sequence to have a different
// turn rate associated with it.
//=========================================================
float CNPC_Houndeye::MaxYawSpeed ( void )
{
int ys = 90;

switch ( GetActivity() )
{
case ACT_CROUCHIDLE://sleeping!
ys = 0;
break;
case ACT_IDLE:
ys = 60;
break;
case ACT_WALK:
ys = 90;
break;
case ACT_RUN:
ys = 90;
break;
case ACT_TURN_LEFT:
case ACT_TURN_RIGHT:
ys = 90;
break;
}
return ys;
}

//=========================================================
// HandleAnimEvent - catches the monster-specific messages
// that occur when tagged animation frames are played.
//=========================================================
void CNPC_Houndeye::HandleAnimEvent( animevent_t *pEvent )
{
switch ( pEvent->event )
{
case HOUND_AE_WARN:
// do stuff for this event.
WarnSound();
break;

case HOUND_AE_STARTATTACK:
WarmUpSound();
break;

case HOUND_AE_HOPBACK:
{
float flGravity = sv_gravity.GetFloat();

RemoveFlag( FL_ONGROUND );

Vector forward;
AngleVectors( GetLocalAngles(), &forward );
Vector vecNewVelocity = forward * -200;
vecNewVelocity.z += (0.6 * flGravity) * 0.5;
SetAbsVelocity( vecNewVelocity );
break;
}

case HOUND_AE_THUMP:
// emit the shockwaves
SonicAttack();
m_flNextAttack = gpGlobals->curtime + random->RandomFloat( 5.0, 8.0 );
break;

case HOUND_AE_ANGERSOUND1:
{
EmitSound( "NPC_Houndeye.Anger1" );
}
break;

case HOUND_AE_ANGERSOUND2:
{
EmitSound( "NPC_Houndeye.Anger2" );
}
break;

case HOUND_AE_CLOSE_EYE:
if ( !m_fDontBlink )
{
//<> pev->skin = HOUNDEYE_EYE_FRAMES - 1;
}
break;

case HOUND_AE_LEAP_HIT:
{
//<>return;//<>
RemoveFlag( FL_ONGROUND );

//
// Take him off ground so engine doesn't instantly reset FL_ONGROUND.
//
UTIL_SetOrigin( this, GetLocalOrigin() + Vector( 0 , 0 , 1 ));
Vector vecJumpDir;
if ( GetEnemy() != NULL )
{
Vector vecEnemyEyePos = GetEnemy()->EyePosition();

float gravity = sv_gravity.GetFloat();
if ( gravity <= 1 )
{
gravity = 1;
}

//
// How fast does the houndeye 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
{
Vector forward, up;
AngleVectors( GetLocalAngles(), &forward, NULL, &up );
//
// Jump hop, don't care where.
//
vecJumpDir = Vector( forward.x, forward.y, up.z ) * 350;
}

int iSound = random->RandomInt( 0 , 2 );
if ( iSound != 0 )
{
//<> UTIL_EmitSoundDyn( edict(), CHAN_VOICE, pAttackSounds[iSound], GetSoundVolue(), ATTN_IDLE, 0, GetVoicePitch() );
}

SetAbsVelocity( vecJumpDir );
m_flNextAttack = gpGlobals->curtime + 2;
break;
}
default:
BaseClass::HandleAnimEvent( pEvent );
break;
}
}

//=========================================================
// Spawn
//=========================================================
void CNPC_Houndeye::Spawn()
{
Precache( );

SetModel("models/houndeye.mdl");
SetHullType(HULL_WIDE_SHORT);
SetHullSizeNormal();

SetSolid( SOLID_BBOX );
AddSolidFlags( FSOLID_NOT_STANDABLE );
SetMoveType( MOVETYPE_STEP );
SetBloodColor( BLOOD_COLOR_YELLOW );
m_iHealth = sk_Houndeye_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_fAsleep = false; // everyone spawns awake
m_fDontBlink = false;
CapabilitiesAdd( bits_CAP_SQUAD );
CapabilitiesAdd( bits_CAP_MOVE_GROUND );
CapabilitiesAdd( bits_CAP_INNATE_RANGE_ATTACK1 );
CapabilitiesAdd( bits_CAP_TURN_HEAD );

m_flNextSecondaryAttack = 0;
m_bLoopClockwise = random->RandomInt(0,1) ? true : false;

m_pEnergyWave = NULL;
m_flEndEnergyWaveTime = 0;

SetCollisionGroup( HL2COLLISION_GROUP_HOUNDEYE );

NPCInit();
}

//=========================================================
// Precache - precaches all resources this monster needs
//=========================================================
void CNPC_Houndeye::Precache()
{
PrecacheModel("models/houndeye.mdl");

PrecacheScriptSound( "NPC_Houndeye.Anger1");
PrecacheScriptSound( "NPC_Houndeye.Anger2" );
PrecacheScriptSound( "NPC_Houndeye.SpeakSentence" );
PrecacheScriptSound( "NPC_Houndeye.Idle" );
PrecacheScriptSound( "NPC_Houndeye.WarmUp" );
PrecacheScriptSound( "NPC_Houndeye.Warn" );
PrecacheScriptSound( "NPC_Houndeye.Alert" );
PrecacheScriptSound( "NPC_Houndeye.Die" );
PrecacheScriptSound( "NPC_Houndeye.Pain" );
PrecacheScriptSound( "NPC_Houndeye.Retreat" );
PrecacheScriptSound( "NPC_Houndeye.SonicAttack" );
PrecacheScriptSound( "NPC_Houndeye.GroupAttack" );
PrecacheScriptSound( "NPC_Houndeye.GroupFollow" );

UTIL_PrecacheOther("grenade_energy");
BaseClass::Precache();
}

//------------------------------------------------------------------------------
// Purpose :
// Input :
// Output :
//------------------------------------------------------------------------------
void CNPC_Houndeye::SpeakSentence( int sentenceType )
{
if (gpGlobals->curtime > m_flSoundWaitTime)
{
EmitSound( "NPC_Houndeye.SpeakSentence" );
m_flSoundWaitTime = gpGlobals->curtime + 1.0;
}
}

//=========================================================
// IdleSound
//=========================================================
void CNPC_Houndeye::IdleSound ( void )
{
if (!FOkToMakeSound())
{
return;
}
CPASAttenuationFilter filter( this,"NPC_Houndeye.Idle" );
EmitSound( filter, entindex(),"NPC_Houndeye.Idle" );
}

//=========================================================
// IdleSound
//=========================================================
void CNPC_Houndeye::WarmUpSound ( void )
{
EmitSound( "NPC_Houndeye.WarmUp" );
}

//=========================================================
// WarnSound
//=========================================================
void CNPC_Houndeye::WarnSound ( void )
{
EmitSound( "NPC_Houndeye.Warn" );
}

//=========================================================
// AlertSound
//=========================================================
void CNPC_Houndeye::AlertSound ( void )
{
// only first squad member makes ALERT sound.
if ( m_pSquad && !m_pSquad->IsLeader( this ) )
{
return;
}

EmitSound( "NPC_Houndeye.Alert" );
}

//=========================================================
// DeathSound
//=========================================================
void CNPC_Houndeye::DeathSound ( const CTakeDamageInfo &info )
{
EmitSound( "NPC_Houndeye.Die" );
}

//=========================================================
// PainSound
//=========================================================
void CNPC_Houndeye::PainSound ( const CTakeDamageInfo &info )
{
EmitSound( "NPC_Houndeye.Pain" );
}

//=========================================================
// WriteBeamColor - writes a color vector to the network
// based on the size of the group.
//=========================================================
void CNPC_Houndeye::WriteBeamColor ( void )
{
BYTE bRed, bGreen, bBlue;

if ( m_pSquad )
{
switch ( m_pSquad->NumMembers() )
{
case 2:
// no case for 0 or 1, cause those are impossible for monsters in Squads.
bRed = 101;
bGreen = 133;
bBlue = 221;
break;
case 3:
bRed = 67;
bGreen = 85;
bBlue = 255;
break;
case 4:
bRed = 62;
bGreen = 33;
bBlue = 211;
break;
default:
DevWarning( 2, "Unsupported Houndeye SquadSize!\n" );
bRed = 188;
bGreen = 220;
bBlue = 255;
break;
}
}
else
{
// solo houndeye - weakest beam
bRed = 188;
bGreen = 220;
bBlue = 255;
}

WRITE_BYTE( bRed );
WRITE_BYTE( bGreen );
WRITE_BYTE( bBlue );
}

//-----------------------------------------------------------------------------
// Purpose: Plays the engine sound.
//-----------------------------------------------------------------------------
void CNPC_Houndeye::NPCThink(void)
{
if (m_pEnergyWave)
{
if (gpGlobals->curtime > m_flEndEnergyWaveTime)
{
UTIL_Remove(m_pEnergyWave);
m_pEnergyWave = NULL;
}
}

// -----------------------------------------------------
// Update collision group
// While I'm running I'm allowed to penetrate
// other houndeyes
// -----------------------------------------------------
Vector vVelocity;
GetVelocity( &vVelocity, NULL );
if (vVelocity.Length() > 10)
{
SetCollisionGroup( HL2COLLISION_GROUP_HOUNDEYE );
}
else
{
// Don't go solid if resting in another houndeye
trace_t tr;
AI_TraceHull( GetAbsOrigin(), GetAbsOrigin() + Vector(0,0,1),
GetHullMins(), GetHullMaxs(),
MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr );
if (!tr.startsolid)
{
SetCollisionGroup( COLLISION_GROUP_NONE );
}
else
{
SetCollisionGroup( HL2COLLISION_GROUP_HOUNDEYE );
}
}
/*
if (GetCollisionGroup() == HL2COLLISION_GROUP_HOUNDEYE)
{
NDebugOverlay::Box(GetAbsOrigin(), GetHullMins(), GetHullMaxs(), 0, 255, 0, 0, 0);
}
else
{
NDebugOverlay::Box(GetAbsOrigin(), GetHullMins(), GetHullMaxs(), 255, 0, 0, 0, 0);
}
*/
BaseClass::NPCThink();
}

//------------------------------------------------------------------------------
// Purpose : Broadcast retreat occasionally when hurt
// Input :
// Output :
//------------------------------------------------------------------------------
int CNPC_Houndeye::OnTakeDamage_Alive( const CTakeDamageInfo &info )
{
if (m_pSquad && random->RandomInt(0,10) == 10)
{
EmitSound( "NPC_Houndeye.Retreat" );
m_flSoundWaitTime = gpGlobals->curtime + 1.0;

m_pSquad->BroadcastInteraction( g_interactionHoundeyeGroupRetreat, NULL, this );
}

return BaseClass::OnTakeDamage_Alive( info );
}

//------------------------------------------------------------------------------
// Purpose : Broadcast retreat when member of squad killed
// Input :
// Output :
//------------------------------------------------------------------------------
void CNPC_Houndeye::Event_Killed( const CTakeDamageInfo &info )
{
EmitSound( "NPC_Houndeye.Retreat" );
m_flSoundWaitTime = gpGlobals->curtime + 1.0;

if (m_pSquad)
{
m_pSquad->BroadcastInteraction( g_interactionHoundeyeGroupRetreat, NULL, this );
}

BaseClass::Event_Killed( info );
}

//=========================================================
// SonicAttack
//=========================================================
void CNPC_Houndeye::SonicAttack ( void )
{
EmitSound( "NPC_Houndeye.SonicAttack" );

if (m_pEnergyWave)
{
UTIL_Remove(m_pEnergyWave);
}
Vector vFacingDir = EyeDirection3D( );
m_pEnergyWave = (CEnergyWave*)Create( "energy_wave", EyePosition(), GetLocalAngles() );
m_flEndEnergyWaveTime = gpGlobals->curtime + 1; //<> magic
m_pEnergyWave->SetAbsVelocity( 100*vFacingDir );

CBaseEntity *pEntity = NULL;
// iterate on all entities in the vicinity.
for ( CEntitySphereQuery sphere( GetAbsOrigin(), HOUNDEYE_MAX_ATTACK_RADIUS ); pEntity = sphere.GetCurrentEntity(); sphere.NextEntity() )
{
if (pEntity->Classify() == CLASS_HOUNDEYE)
{
continue;
}

if (pEntity->GetFlags() & FL_NOTARGET)
{
continue;
}

IPhysicsObject *pPhysicsObject = pEntity->VPhysicsGetObject();

if ( pEntity->m_takedamage != DAMAGE_NO || pPhysicsObject)
{
// --------------------------
// Adjust damage by distance
// --------------------------
float flDist = (pEntity->WorldSpaceCenter() - GetAbsOrigin()).Length();
float flDamageAdjuster = 1-( flDist / HOUNDEYE_MAX_ATTACK_RADIUS );

// --------------------------
// Adjust damage by direction
// --------------------------
Vector forward;
AngleVectors( GetAbsAngles(), &forward );
Vector vEntDir = (pEntity->GetAbsOrigin() - GetAbsOrigin());
VectorNormalize(vEntDir);
float flDotPr = DotProduct(forward,vEntDir);
flDamageAdjuster *= flDotPr;

if (flDamageAdjuster < 0)
{
continue;
}

// --------------------------
// Adjust damage by visibility
// --------------------------
if ( !FVisible( pEntity ) )
{
if ( pEntity->IsPlayer() )
{
// if this entity is a client, and is not in full view, inflict half damage. We do this so that players still
// take the residual damage if they don't totally leave the houndeye's effective radius. We restrict it to clients
// so that monsters in other parts of the level don't take the damage and get pissed.
flDamageAdjuster *= 0.5;
}
else if ( !FClassnameIs( pEntity, "func_breakable" ) && !FClassnameIs( pEntity, "func_pushable" ) )
{
// do not hurt nonclients through walls, but allow damage to be done to breakables
continue;
}
}

// ------------------------------
// Apply the damage
// ------------------------------
if (pEntity->m_takedamage != DAMAGE_NO)
{
CTakeDamageInfo info( this, this, flDamageAdjuster * sk_Houndeye_dmg_blast.GetFloat(), DMG_SONIC | DMG_ALWAYSGIB );
CalculateExplosiveDamageForce( &info, (pEntity->GetAbsOrigin() - GetAbsOrigin()), pEntity->GetAbsOrigin() );

pEntity->TakeDamage( info );

// Throw the player
if ( pEntity->IsPlayer() )
{
Vector forward;
AngleVectors( GetLocalAngles(), &forward );

Vector vecVelocity = pEntity->GetAbsVelocity();
vecVelocity += forward * 250 * flDamageAdjuster;
vecVelocity.z = 300 * flDamageAdjuster;
pEntity->SetAbsVelocity( vecVelocity );
pEntity->ViewPunch( QAngle(random->RandomInt(-20,20), 0, random->RandomInt(-20,20)) );
}
}
// ------------------------------
// Apply physics foces
// ------------------------------
IPhysicsObject *pPhysicsObject = pEntity->VPhysicsGetObject();
if (pPhysicsObject)
{
float flForce = flDamageAdjuster * 8000;
pPhysicsObject->ApplyForceCenter( (vEntDir+Vector(0,0,0.2)) * flForce );
pPhysicsObject->ApplyTorqueCenter( vEntDir * flForce );
}
}
}
}

//=========================================================
// start task
//=========================================================
void CNPC_Houndeye::StartTask( const Task_t *pTask )
{
switch ( pTask->iTask )
{
case TASK_HOUND_GET_PATH_TO_CIRCLE:
{
if (GetEnemy() == NULL)
{
TaskFail(FAIL_NO_ENEMY);
}
else
{
Vector vTargetPos = GetEnemyLKP();
vTargetPos.z = GetFloorZ(vTargetPos);

if (GetNavigator()->IsGoalActive())
{
TaskComplete();
return;
}
TaskFail(FAIL_NO_ROUTE);
}
break;
}
case TASK_HOUND_REVERSE_STRAFE_DIR:
{
// Try the other direction
m_bLoopClockwise = (m_bLoopClockwise) ? false : true;
TaskComplete();
break;
}

// Override to set appropriate distances
case TASK_GET_PATH_TO_ENEMY_LOS:
{
float flMaxRange = HOUNDEYE_MAX_ATTACK_RADIUS * 0.9;
float flMinRange = HOUNDEYE_MIN_ATTACK_RADIUS;
Vector posLos;
bool foundLos = false;

if (GetEnemy() != NULL)
{
foundLos = GetTacticalServices()->FindLos(GetEnemyLKP(),GetEnemy()->EyePosition(), flMinRange, flMaxRange, 0.0, &posLos);
}
else
{
TaskFail(FAIL_NO_TARGET);
return;
}

if (foundLos)
{
GetNavigator()->SetGoal( AI_NavGoal_t( posLos, ACT_RUN, AIN_HULL_TOLERANCE ) );
}
else
{
TaskFail(FAIL_NO_SHOOT);
}
break;
}

case TASK_HOUND_FALL_ASLEEP:
{
m_fAsleep = true; // signal that hound is lying down (must stand again before doing anything else!)
TaskComplete( true );
break;
}
case TASK_HOUND_WAKE_UP:
{
m_fAsleep = false; // signal that hound is standing again
TaskComplete( true );
break;
}
case TASK_HOUND_OPEN_EYE:
{
m_fDontBlink = false; // turn blinking back on and that code will automatically open the eye
TaskComplete( true );
break;
}
case TASK_HOUND_CLOSE_EYE:
{
//<> pev->skin = 0;
m_fDontBlink = true; // tell blink code to leave the eye alone.
break;
}
case TASK_HOUND_THREAT_DISPLAY:
{
SetIdealActivity( ACT_IDLE_ANGRY );
break;
}
case TASK_HOUND_HOP_BACK:
{
SetIdealActivity( ACT_LEAP );
break;
}
case TASK_RANGE_ATTACK1:
{
SetIdealActivity( ACT_RANGE_ATTACK1 );
break;
}
default:
{
BaseClass::StartTask(pTask);
break;
}
}
}

//=========================================================
// RunTask
//=========================================================
void CNPC_Houndeye::RunTask( const Task_t *pTask )
{
switch ( pTask->iTask )
{
case TASK_HOUND_THREAT_DISPLAY:
{
GetMotor()->SetIdealYawToTargetAndUpdate( GetEnemyLKP(), AI_KEEP_YAW_SPEED );

if ( IsActivityFinished() )
{
TaskComplete();
}

break;
}
case TASK_HOUND_CLOSE_EYE:
{
/*//<>
if ( pev->skin < HOUNDEYE_EYE_FRAMES - 1 )
{
pev->skin++;
}
*/
break;
}
case TASK_HOUND_HOP_BACK:
{
if ( IsActivityFinished() )
{
TaskComplete();
}
break;
}
default:
{
BaseClass::RunTask(pTask);
break;
}
}
}

//------------------------------------------------------------------------------
// Purpose :
// Input :
// Output :
//------------------------------------------------------------------------------
void CNPC_Houndeye::PrescheduleThink ( void )
{
BaseClass::PrescheduleThink();

// if the hound is mad and is running, make hunt noises.
if ( m_NPCState == NPC_STATE_COMBAT && ( GetActivity() == ACT_RUN ) && random->RandomFloat( 0, 1 ) < 0.2 )
{
WarnSound();
}

// at random, initiate a blink if not already blinking or sleeping
if ( !m_fDontBlink )
{
/*//<>//<>
if ( ( pev->skin == 0 ) && random->RandomInt(0,0x7F) == 0 )
{// start blinking!
pev->skin = HOUNDEYE_EYE_FRAMES - 1;
}
else if ( pev->skin != 0 )
{// already blinking
pev->skin--;
}
*/
}
}

//-----------------------------------------------------------------------------
// Purpose: Override base class activiites
// Input :
// Output :
//-----------------------------------------------------------------------------
Activity CNPC_Houndeye::NPC_TranslateActivity( Activity eNewActivity )
{
if ( eNewActivity == ACT_IDLE && m_NPCState == NPC_STATE_COMBAT )
{
return ACT_IDLE_ANGRY;
}
return eNewActivity;
}

//------------------------------------------------------------------------------
// Purpose :
// Input :
// Output :
//------------------------------------------------------------------------------
int CNPC_Houndeye::TranslateSchedule( int scheduleType )
{
switch( scheduleType )
{
case SCHED_IDLE_STAND:
{
return BaseClass::TranslateSchedule( scheduleType );
}
case SCHED_RANGE_ATTACK1:
{
return SCHED_HOUND_RANGE_ATTACK1;
}
case SCHED_CHASE_ENEMY_FAILED:
{
return SCHED_COMBAT_FACE;
}
default:
{
return BaseClass::TranslateSchedule ( scheduleType );
}
}
}

//------------------------------------------------------------------------------
// Purpose : Is anyone in the squad currently attacking
// Input :
// Output :
//------------------------------------------------------------------------------
bool CNPC_Houndeye::IsAnyoneInSquadAttacking( void )
{
if (!m_pSquad)
{
return false;
}

AISquadIter_t iter;
for (CAI_BaseNPC *pSquadMember = m_pSquad->GetFirstMember( &iter ); pSquadMember; pSquadMember = m_pSquad->GetNextMember( &iter ) )
{
if (pSquadMember->IsCurSchedule(SCHED_HOUND_RANGE_ATTACK1))
{
return true;
}
}
return false;
}

//=========================================================
// SelectSchedule
//=========================================================
int CNPC_Houndeye::SelectSchedule( void )
{
switch ( m_NPCState )
{
case NPC_STATE_IDLE:
case NPC_STATE_ALERT:
{
if ( HasCondition(COND_LIGHT_DAMAGE) ||
HasCondition(COND_HEAVY_DAMAGE) )
{
return SCHED_TAKE_COVER_FROM_ORIGIN;
}
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();
}

// If a group attack was requested attack even if attack conditions not met
if ( HasCondition( COND_HOUND_GROUP_ATTACK ))
{
// Check that I'm not standing in another hound eye
// before attacking
trace_t tr;
AI_TraceHull( GetAbsOrigin(), GetAbsOrigin() + Vector(0,0,1),
GetHullMins(), GetHullMaxs(),
MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr );
if (!tr.startsolid)
{
return SCHED_HOUND_GROUP_ATTACK;
}

// Otherwise attack as soon as I can
else
{
m_flNextAttack = gpGlobals->curtime;
SCHED_HOUND_ATTACK_STRAFE;
}
}

// If a group retread was requested
if ( HasCondition( COND_HOUND_GROUP_RETREAT ))
{
return SCHED_HOUND_GROUP_RETREAT;
}

if ( HasCondition( COND_LIGHT_DAMAGE ) |
HasCondition( COND_HEAVY_DAMAGE ) )
{
if ( random->RandomFloat( 0 , 1 ) <= 0.4 )
{
trace_t tr;
Vector forward;
AngleVectors( GetAbsAngles(), &forward );
AI_TraceHull( GetAbsOrigin(), GetAbsOrigin() + forward * -128,
GetHullMins(), GetHullMaxs(),
MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr );

if ( tr.fraction == 1.0 )
{
// it's clear behind, so the hound will jump
return SCHED_HOUND_HOP_RETREAT;
}
}

return SCHED_TAKE_COVER_FROM_ENEMY;
}

// If a group rally was requested
if ( HasCondition( COND_HOUND_GROUP_RALLEY ))
{
return SCHED_HOUND_GROUP_RALLEY;
}

if ( HasCondition( COND_CAN_RANGE_ATTACK1 ) )
{
if (m_pSquad && random->RandomInt(0,4) == 0)
{
if (!IsAnyoneInSquadAttacking())
{
EmitSound( "NPC_Houndeye.GroupAttack" );

m_flSoundWaitTime = gpGlobals->curtime + 1.0;

m_pSquad->BroadcastInteraction( g_interactionHoundeyeGroupAttack, NULL, this );
return SCHED_HOUND_GROUP_ATTACK;
}
}
//<>comment
SetCollisionGroup( COLLISION_GROUP_NONE );
return SCHED_RANGE_ATTACK1;
}
else
{
if (m_pSquad && random->RandomInt(0,5) == 0)
{
if (!IsAnyoneInSquadAttacking())
{
EmitSound( "NPC_Houndeye.GroupFollow" );

m_flSoundWaitTime = gpGlobals->curtime + 1.0;

m_pSquad->BroadcastInteraction( g_interactionHoundeyeGroupRalley, NULL, this );
return SCHED_HOUND_ATTACK_STRAFE;
}
}
return SCHED_HOUND_ATTACK_STRAFE;
}
break;
}
}

return BaseClass::SelectSchedule();
}

//-----------------------------------------------------------------------------
// Purpose: This is a generic function (to be implemented by sub-classes) to
// handle specific interactions between different types of characters
// (For example the barnacle grabbing an NPC)
// Input : Constant for the type of interaction
// Output : true - if sub-class has a response for the interaction
// false - if sub-class has no response
//-----------------------------------------------------------------------------
bool CNPC_Houndeye::HandleInteraction(int interactionType, void *data, CBaseCombatCharacter* sourceEnt)
{
if (interactionType == g_interactionHoundeyeGroupAttack)
{
SetCondition(COND_HOUND_GROUP_ATTACK);
return true;
}
else if (interactionType == g_interactionHoundeyeGroupRetreat)
{
SetCondition(COND_HOUND_GROUP_RETREAT);
return true;
}
else if (interactionType == g_interactionHoundeyeGroupRalley)
{
SetCondition(COND_HOUND_GROUP_RALLEY);
SetTarget(sourceEnt);
m_bLoopClockwise = false;
return true;
}

return false;
}

//-----------------------------------------------------------------------------
//
// Schedules
//
//-----------------------------------------------------------------------------

//=========================================================
// SCHED_HOUND_ATTACK_STRAFE
//
// Run a cirle around my enemy
//=========================================================
AI_DEFINE_SCHEDULE
(
SCHED_HOUND_ATTACK_STRAFE ,

" Tasks "
" TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_HOUND_ATTACK_STRAFE_REVERSE"
" TASK_HOUND_GET_PATH_TO_CIRCLE 0"
" TASK_RUN_PATH 0"
" TASK_WAIT_FOR_MOVEMENT 0"
""
" Interrupts "
" COND_WEAPON_SIGHT_OCCLUDED"
" COND_NEW_ENEMY"
" COND_ENEMY_DEAD"
" COND_HEAVY_DAMAGE"
" COND_CAN_RANGE_ATTACK1"
" COND_HOUND_GROUP_ATTACK"
" COND_HOUND_GROUP_RETREAT"
);

//=========================================================
// SCHED_HOUND_ATTACK_STRAFE_REVERSE
//
// Run a cirle around my enemy
//=========================================================
AI_DEFINE_SCHEDULE
(
SCHED_HOUND_ATTACK_STRAFE_REVERSE ,

" Tasks "
" TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_HOUND_CHASE_ENEMY"
" TASK_HOUND_REVERSE_STRAFE_DIR 0"
" TASK_HOUND_GET_PATH_TO_CIRCLE 0"
" TASK_RUN_PATH 0"
" TASK_WAIT_FOR_MOVEMENT 0"
""
" Interrupts "
" COND_WEAPON_SIGHT_OCCLUDED"
" COND_NEW_ENEMY"
" COND_ENEMY_DEAD"
" COND_HEAVY_DAMAGE"
" COND_CAN_RANGE_ATTACK1"
" COND_HOUND_GROUP_ATTACK"
" COND_HOUND_GROUP_RETREAT"
);

//========================================================
// SCHED_HOUND_CHASE_ENEMY
//=========================================================
AI_DEFINE_SCHEDULE
(
SCHED_HOUND_CHASE_ENEMY,

" Tasks"
" TASK_SET_TOLERANCE_DISTANCE 30 "
" TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_CHASE_ENEMY_FAILED"
" TASK_GET_PATH_TO_ENEMY 0"
" TASK_RUN_PATH 0"
" TASK_WAIT_FOR_MOVEMENT 0"
""
" Interrupts"
" COND_NEW_ENEMY"
" COND_ENEMY_DEAD"
" COND_CAN_RANGE_ATTACK1"
" COND_HOUND_GROUP_ATTACK"
" COND_HOUND_GROUP_RETREAT"
);

//=========================================================
// SCHED_HOUND_GROUP_ATTACK
//
// Face enemy, pause, then attack
//=========================================================
AI_DEFINE_SCHEDULE
(
SCHED_HOUND_GROUP_ATTACK ,

" Tasks "
" TASK_STOP_MOVING 0"
" TASK_FACE_ENEMY 0"
" TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE_ANGRY"
" TASK_SPEAK_SENTENCE 0"
" TASK_WAIT 1"
" TASK_SET_SCHEDULE SCHEDULE:SCHED_HOUND_RANGE_ATTACK1"
""
" Interrupts "
" COND_NEW_ENEMY"
" COND_ENEMY_DEAD"
" COND_HEAVY_DAMAGE"
);

//=========================================================
// > SCHED_HOUND_GROUP_RETREAT
//
// Take cover from enemy!
//=========================================================
AI_DEFINE_SCHEDULE
(
SCHED_HOUND_GROUP_RETREAT,

" Tasks"
" TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_HOUND_ATTACK_STRAFE"
" TASK_STOP_MOVING 0"
" TASK_WAIT 0.2"
" TASK_SET_TOLERANCE_DISTANCE 24"
" TASK_FIND_COVER_FROM_ENEMY 0"
" TASK_RUN_PATH 0"
" TASK_WAIT_FOR_MOVEMENT 0"
" TASK_REMEMBER MEMORY:INCOVER"
" TASK_FACE_ENEMY 0"
" TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE" // Translated to cover
" TASK_SET_SCHEDULE SCHEDULE:SCHED_HOUND_COVER_WAIT"
""
" Interrupts"
" COND_NEW_ENEMY"
);

//=========================================================
// > SCHED_HOUND_GROUP_RALLEY
//
// Run to rally hound!
//=========================================================
AI_DEFINE_SCHEDULE
(
SCHED_HOUND_GROUP_RALLEY,

" Tasks"
" TASK_SET_TOLERANCE_DISTANCE 30"
" TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_HOUND_ATTACK_STRAFE"
" TASK_GET_PATH_TO_TARGET 0"
" TASK_RUN_PATH 0"
" TASK_WAIT_FOR_MOVEMENT 0"
""
" Interrupts"
" COND_NEW_ENEMY"
" COND_ENEMY_DEAD"
" COND_HEAVY_DAMAGE"
" COND_HOUND_GROUP_ATTACK"
" COND_HOUND_GROUP_RETREAT"
);

//=========================================================
// > SCHED_HOUND_COVER_WAIT
//
// Wait in cover till enemy see's me or I take damage
//=========================================================
AI_DEFINE_SCHEDULE
(
SCHED_HOUND_COVER_WAIT,

" Tasks"
" TASK_WAIT 2"
""
" Interrupts"
" COND_SEE_ENEMY"
" COND_LIGHT_DAMAGE"
);

//=========================================================
// > SCHED_HOUND_RANGE_ATTACK1
//=========================================================
AI_DEFINE_SCHEDULE
(
SCHED_HOUND_RANGE_ATTACK1,

" Tasks"
" TASK_STOP_MOVING 0"
" TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE_ANGRY"
" TASK_FACE_IDEAL 0"
" TASK_RANGE_ATTACK1 0"
""
" Interrupts"
" COND_LIGHT_DAMAGE" // don't interupt on small damage
" COND_HEAVY_DAMAGE"
);

//=========================================================
// > SCHED_HOUND_HOP_RETREAT
//=========================================================
AI_DEFINE_SCHEDULE
(
SCHED_HOUND_HOP_RETREAT,

" Tasks"
" TASK_STOP_MOVING 0"
" TASK_HOUND_HOP_BACK 0"
" TASK_SET_SCHEDULE SCHEDULE:SCHED_TAKE_COVER_FROM_ENEMY"
""
" Interrupts"
);

Ок, сделали. Продолжение туториала...

Категория: Туториалы по программированию (HL2) | Добавил: DetLeR (26.11.2007) | Автор: Роман
Просмотров: 2653 | Рейтинг: 5.0 |

Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Форма входа

Кто в Онлайн

Друзья сайта

Баннеры

  Сайт CrazyArts   Black   Сообщество сайтов о Half-Life   Самый   Только   Все   hl2 top 100     Rambler's Top100  

игры
игры

  Каталог сайтов Планета Топ 100 - Planet Top 100       ТОП ЛУЧШИХ ИГРОВЫХ САЙТОВ           Detroit Team Site :: Моды от Detroit Team, видео, новости.   Naruto-kun[Звезда Наруто]  


The idea of dising: Homie7