Skip to content

Instantly share code, notes, and snippets.

@Kolesias123
Created January 30, 2013 18:56
Show Gist options
  • Save Kolesias123/4675717 to your computer and use it in GitHub Desktop.
Save Kolesias123/4675717 to your computer and use it in GitHub Desktop.
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Zombi clasico: Lento y con solo ataques de cuerpo a cuerpo.
//
//=============================================================================//
#include "cbase.h"
#include "doors.h"
#include "simtimer.h"
#include "npc_BaseZombie.h"
#include "ai_hull.h"
#include "ai_navigator.h"
#include "ai_memory.h"
#include "gib.h"
#include "soundenvelope.h"
#include "engine/IEngineSound.h"
#include "ammodef.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//----------------------------------------------------
// Definición de variables de configuración.
//----------------------------------------------------
ConVar sk_zombie_health("sk_zombie_health", "0", 0, "Salud del zombi clasico.");
//----------------------------------------------------
// InSource - Modelos
//----------------------------------------------------
#define MODEL_HEADCRAB "models/headcrabclassic.mdl"
#define MODEL_LEGS "models/zombie/classic_legs.mdl"
#define MODEL_TORSO "models/zombie/classic_torso.mdl"
#define MODEL_ZOMBIE "models/zombie/classic.mdl"
//----------------------------------------------------
// Información
//----------------------------------------------------
#define ZOMBIE_SQUASH_MASS 300.0f
//----------------------------------------------------
// Sonidos y su volumen.
//----------------------------------------------------
envelopePoint_t envZombieMoanVolumeFast[] =
{
{ 7.0f, 7.0f,
0.1f, 0.1f,
},
{ 0.0f, 0.0f,
0.2f, 0.3f,
},
};
envelopePoint_t envZombieMoanVolume[] =
{
{ 1.0f, 1.0f,
0.1f, 0.1f,
},
{ 1.0f, 1.0f,
0.2f, 0.2f,
},
{ 0.0f, 0.0f,
0.3f, 0.4f,
},
};
envelopePoint_t envZombieMoanVolumeLong[] =
{
{ 1.0f, 1.0f,
0.3f, 0.5f,
},
{ 1.0f, 1.0f,
0.6f, 1.0f,
},
{ 0.0f, 0.0f,
0.3f, 0.4f,
},
};
envelopePoint_t envZombieMoanIgnited[] =
{
{ 1.0f, 1.0f,
0.5f, 1.0f,
},
{ 1.0f, 1.0f,
30.0f, 30.0f,
},
{ 0.0f, 0.0f,
0.5f, 1.0f,
},
};
/*---------------------------------------------------
CZombie
Clase del zombi
---------------------------------------------------*/
class CZombie : public CAI_BlendingHost<CNPC_BaseZombie>
{
DECLARE_DATADESC();
DECLARE_CLASS(CZombie, CAI_BlendingHost<CNPC_BaseZombie>);
public:
CZombie() : m_DurationDoorBash( 2, 6), m_NextTimeToStartDoorBash( 3.0 )
{
}
void Spawn();
void Precache();
void SetZombieModel();
void MoanSound(envelopePoint_t *pEnvelope, int iEnvelopeSize);
bool ShouldBecomeTorso(const CTakeDamageInfo &info, float flDamageThreshold);
bool CanBecomeLiveTorso()
{
return !m_fIsHeadless;
}
void GatherConditions();
int SelectFailSchedule(int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode);
int TranslateSchedule(int scheduleType);
#ifndef HL2_EPISODIC
void CheckFlinches() {}
#endif // HL2_EPISODIC
Activity NPC_TranslateActivity(Activity newActivity);
Activity SelectDoorBash();
void OnStateChange(NPC_STATE OldState, NPC_STATE NewState);
void StartTask(const Task_t *pTask);
void RunTask(const Task_t *pTask);
virtual const char *GetLegsModel();
virtual const char *GetTorsoModel();
virtual const char *GetHeadcrabClassname();
virtual const char *GetHeadcrabModel();
virtual bool OnObstructingDoor(AILocalMoveGoal_t *pMoveGoal, CBaseDoor *pDoor, float distClear, AIMoveResult_t *pResult);
void Ignite(float flFlameLifetime, bool bNPCOnly = true, float flSize = 0.0f, bool bCalledByLevelDesigner = false);
void Extinguish();
int OnTakeDamage_Alive(const CTakeDamageInfo &inputInfo);
bool IsHeavyDamage(const CTakeDamageInfo &info);
bool IsSquashed(const CTakeDamageInfo &info);
void BuildScheduleTestBits();
void PrescheduleThink();
int SelectSchedule();
void PainSound(const CTakeDamageInfo &info);
void DeathSound(const CTakeDamageInfo &info);
void AlertSound();
void IdleSound();
void AttackSound();
void AttackHitSound();
void AttackMissSound();
void FootstepSound(bool fRightFoot);
void FootscuffSound(bool fRightFoot);
const char *GetMoanSound(int nSound);
//float GetIdealAccel() const;
//float GetIdealSpeed() const;
public:
DEFINE_CUSTOM_AI;
protected:
static const char *pMoanSounds[];
private:
CHandle< CBaseDoor > m_hBlockingDoor;
float m_flDoorBashYaw;
CRandSimTimer m_DurationDoorBash;
CSimTimer m_NextTimeToStartDoorBash;
Vector m_vPositionCharged;
};
LINK_ENTITY_TO_CLASS(npc_zombie, CZombie);
LINK_ENTITY_TO_CLASS(npc_zombie_torso, CZombie);
//----------------------------------------------------
// Sonidos de los gemidos del zombie
//----------------------------------------------------
const char *CZombie::pMoanSounds[] =
{
"NPC_BaseZombie.Moan1",
"NPC_BaseZombie.Moan2",
"NPC_BaseZombie.Moan3",
"NPC_BaseZombie.Moan4",
};
//----------------------------------------------------
// Condiciones
//----------------------------------------------------
enum
{
COND_BLOCKED_BY_DOOR = LAST_BASE_ZOMBIE_CONDITION,
COND_DOOR_OPENED,
COND_ZOMBIE_CHARGE_TARGET_MOVED,
};
//----------------------------------------------------
// Eventos
//----------------------------------------------------
enum
{
SCHED_ZOMBIE_BASH_DOOR = LAST_BASE_ZOMBIE_SCHEDULE,
SCHED_ZOMBIE_WANDER_ANGRILY,
SCHED_ZOMBIE_CHARGE_ENEMY,
SCHED_ZOMBIE_FAIL,
};
//----------------------------------------------------
// Tareas
//----------------------------------------------------
enum
{
TASK_ZOMBIE_EXPRESS_ANGER = LAST_BASE_ZOMBIE_TASK,
TASK_ZOMBIE_YAW_TO_DOOR,
TASK_ZOMBIE_ATTACK_DOOR,
TASK_ZOMBIE_CHARGE_ENEMY,
};
//----------------------------------------------------
// Actividades
//----------------------------------------------------
int ACT_ZOMBIE_TANTRUM;
int ACT_ZOMBIE_WALLPOUND;
//----------------------------------------------------
// Definición de datos.
//----------------------------------------------------
BEGIN_DATADESC(CZombie)
DEFINE_FIELD(m_hBlockingDoor, FIELD_EHANDLE),
DEFINE_FIELD(m_flDoorBashYaw, FIELD_FLOAT),
DEFINE_EMBEDDED(m_DurationDoorBash),
DEFINE_EMBEDDED(m_NextTimeToStartDoorBash),
DEFINE_FIELD(m_vPositionCharged, FIELD_POSITION_VECTOR),
END_DATADESC()
//----------------------------------------------------
// Precache()
// Precachear objetos y sonidos para su uso.
//----------------------------------------------------
void CZombie::Precache()
{
BaseClass::Precache();
PrecacheModel("models/zombie/classic.mdl");
PrecacheModel("models/zombie/classic_torso.mdl");
PrecacheModel("models/zombie/classic_legs.mdl");
PrecacheScriptSound("Zombie.FootstepRight");
PrecacheScriptSound("Zombie.FootstepLeft");
PrecacheScriptSound("Zombie.FootstepLeft");
PrecacheScriptSound("Zombie.ScuffRight");
PrecacheScriptSound("Zombie.ScuffLeft");
PrecacheScriptSound("Zombie.AttackHit");
PrecacheScriptSound("Zombie.AttackMiss");
PrecacheScriptSound("Zombie.Pain");
PrecacheScriptSound("Zombie.Die");
PrecacheScriptSound("Zombie.Alert");
PrecacheScriptSound("Zombie.Idle");
PrecacheScriptSound("Zombie.Attack");
PrecacheScriptSound("NPC_BaseZombie.Moan1");
PrecacheScriptSound("NPC_BaseZombie.Moan2");
PrecacheScriptSound("NPC_BaseZombie.Moan3");
PrecacheScriptSound("NPC_BaseZombie.Moan4");
}
//----------------------------------------------------
// Spawn()
// "Spawnear" al zombi.
//----------------------------------------------------
void CZombie::Spawn()
{
Precache();
if(FClassnameIs(this, "npc_zombie"))
m_fIsTorso = false;
else
m_fIsTorso = true;
m_fIsHeadless = false;
// Color de la sangre.
SetBloodColor(BLOOD_COLOR_RED);
m_iHealth = sk_zombie_health.GetFloat();
m_flFieldOfView = 0.2;
m_flGroundSpeed = 500.0f;
CapabilitiesClear();
BaseClass::Spawn();
// La próxima vez que "gemire"
m_flNextMoanSound = gpGlobals->curtime + random->RandomFloat(1.0, 4.0);
// Deben ser más rápidos que en HL2
SetAddSpeed(70);
SetAddAccel(30);
}
//----------------------------------------------------
// PrescheduleThink()
// Tareas futuras a ejecutar.
//----------------------------------------------------
void CZombie::PrescheduleThink()
{
// Me toca "gemir"
if(gpGlobals->curtime > m_flNextMoanSound)
{
// ¿Puedo "gemir"?
if(CanPlayMoanSound())
{
// Gemir y decidir cuando volvere a gemir.
IdleSound();
m_flNextMoanSound = gpGlobals->curtime + random->RandomFloat(2.0, 5.0);
}
else
m_flNextMoanSound = gpGlobals->curtime + random->RandomFloat( 1.0, 2.0 );
}
BaseClass::PrescheduleThink();
}
//----------------------------------------------------
// SelectSchedule()
// Seleccionar evento
//----------------------------------------------------
int CZombie::SelectSchedule()
{
// Daño por físicas (o también conocido como "daño por mundo")
if(HasCondition(COND_PHYSICS_DAMAGE) && !m_ActBusyBehavior.IsActive())
return SCHED_FLINCH_PHYSICS;
return BaseClass::SelectSchedule();
}
//----------------------------------------------------
// FootstepSound()
// Reproducir sonido de pasos.
//----------------------------------------------------
void CZombie::FootstepSound(bool fRightFoot)
{
if(fRightFoot)
EmitSound("Zombie.FootstepRight");
else
EmitSound("Zombie.FootstepLeft");
}
//----------------------------------------------------
// FootscuffSound()
// Reproducir sonido de pasos deslizantes.
//----------------------------------------------------
void CZombie::FootscuffSound(bool fRightFoot)
{
if(fRightFoot )
EmitSound("Zombie.ScuffRight");
else
EmitSound("Zombie.ScuffLeft");
}
//----------------------------------------------------
// AttackHitSound()
// Reproducir sonido al azar de ataque.
//----------------------------------------------------
void CZombie::AttackHitSound()
{
EmitSound("Zombie.AttackHit");
}
//----------------------------------------------------
// AttackMissSound()
// Reproducir sonido al azar de ataque fallido.
//----------------------------------------------------
void CZombie::AttackMissSound()
{
EmitSound( "Zombie.AttackMiss" );
}
//----------------------------------------------------
// PainSound()
// Reproducir sonido de dolor.
//----------------------------------------------------
void CZombie::PainSound(const CTakeDamageInfo &info)
{
// Cuando el zombi esta en llamas sufre daños constantemente.
// Para evitar un bucle de sonidos, no reproducir cuando esta en llamas.
if (IsOnFire())
return;
EmitSound("Zombie.Pain");
}
//----------------------------------------------------
// DeathSound()
// Reproducir sonido de muerte.
//----------------------------------------------------
void CZombie::DeathSound(const CTakeDamageInfo &info)
{
EmitSound("Zombie.Die");
}
//----------------------------------------------------
// AlertSound()
// Reproducir sonido de alerta.
//----------------------------------------------------
void CZombie::AlertSound()
{
// Huelo algo... ¿estas ahí? quiero tu carne...
EmitSound("Zombie.Alert");
// Retrasar el sonido de "gemido".
m_flNextMoanSound += random->RandomFloat( 2.0, 4.0 );
}
//----------------------------------------------------
// GetMoanSound()
// Obtener los sonidos de "gemido" para esta clase de zombi.
//----------------------------------------------------
const char *CZombie::GetMoanSound(int nSound)
{
return pMoanSounds[nSound % ARRAYSIZE(pMoanSounds)];
}
//----------------------------------------------------
// IdleSound()
// Reproducir sonido al azar de descanso.
//----------------------------------------------------
void CZombie::IdleSound()
{
// Evitamos sonidos constantes.
if(GetState() == NPC_STATE_IDLE && random->RandomFloat(0, 1) == 0)
return;
// ¿Estamos "durmiendo"?
if(IsSlumped())
return;
EmitSound("Zombie.Idle");
MakeAISpookySound(360.0f);
}
//----------------------------------------------------
// AttackSound()
// Reproducir sonido al azar de ataque.
//----------------------------------------------------
void CZombie::AttackSound()
{
EmitSound("Zombie.Attack");
}
//----------------------------------------------------
// GetHeadcrabClassname()
// Nombre de la clase de NPC del headcrab de este zombi.
// Es el mismo que se "desprenderá" del zombi al morir.
//----------------------------------------------------
const char *CZombie::GetHeadcrabClassname()
{
return "npc_headcrab";
}
//----------------------------------------------------
// GetHeadcrabModel()
// Ubicación del modelo del headcrab de este zombi.
// Es el mismo que se "desprenderá" del zombi al morir.
//----------------------------------------------------
const char *CZombie::GetHeadcrabModel()
{
return MODEL_HEADCRAB;
}
//----------------------------------------------------
// GetLegsModel()
// Ubicación del modelo de las piernas de este zombi.
// Usado en caso de que el zombi se parta a la mitad.
//----------------------------------------------------
const char *CZombie::GetLegsModel( void )
{
return MODEL_LEGS;
}
//----------------------------------------------------
// GetTorsoModel()
// Ubicación del modelo del torso de este zombi.
// Usado en caso de que el zombi se parta a la mitad.
//----------------------------------------------------
const char *CZombie::GetTorsoModel( void )
{
return MODEL_TORSO;
}
//----------------------------------------------------
// SetZombieModel()
// Establecer el modelo de este zombi.
//----------------------------------------------------
void CZombie::SetZombieModel( void )
{
Hull_t lastHull = GetHullType();
// ¡Me han partido a la mitad!
if (m_fIsTorso)
{
SetModel(MODEL_TORSO);
SetHullType(HULL_TINY);
}
else
{
SetModel(MODEL_ZOMBIE);
SetHullType(HULL_HUMAN);
}
SetBodygroup(ZOMBIE_BODYGROUP_HEADCRAB, !m_fIsHeadless);
SetHullSizeNormal(true);
SetDefaultEyeOffset();
SetActivity(ACT_IDLE);
if (lastHull != GetHullType())
{
if (VPhysicsGetObject())
SetupVPhysicsHull();
}
}
//----------------------------------------------------
// MoanSound()
// Reproducir sonido de gemido.
//----------------------------------------------------
void CZombie::MoanSound(envelopePoint_t *pEnvelope, int iEnvelopeSize)
{
if(IsOnFire())
BaseClass::MoanSound(pEnvelope, iEnvelopeSize);
}
//----------------------------------------------------
// ShouldBecomeTorso()
// ¿Partir el zombi a la mitad?
//----------------------------------------------------
bool CZombie::ShouldBecomeTorso(const CTakeDamageInfo &info, float flDamageThreshold)
{
// No partir a la mitad cuando esta "dormido".
// Aparte de ser poco realista, si el jugador mata al zombi con un explosivo (granada, barril)
// la fuerza de la explosión causara que las piernas/torso salgan disparadas a velocidades ridículas debido a su peso.
if(IsSlumped())
return false;
// Dejar la desición a la clase madre. (basezombie)
return BaseClass::ShouldBecomeTorso(info, flDamageThreshold);
}
//----------------------------------------------------
// GatherConditions()
// Reunir condiciones
//----------------------------------------------------
void CZombie::GatherConditions( void )
{
BaseClass::GatherConditions();
static int conditionsToClear[] =
{
COND_BLOCKED_BY_DOOR,
COND_DOOR_OPENED,
COND_ZOMBIE_CHARGE_TARGET_MOVED,
};
ClearConditions(conditionsToClear, ARRAYSIZE(conditionsToClear));
// ¿Objetivo bloqueado por una puerta?
if (m_hBlockingDoor == NULL ||
(m_hBlockingDoor->m_toggle_state == TS_AT_TOP ||
m_hBlockingDoor->m_toggle_state == TS_GOING_UP) )
{
// No hay puerta.
ClearCondition(COND_BLOCKED_BY_DOOR);
// Al parecer porque se abrio.
if (m_hBlockingDoor != NULL)
{
SetCondition(COND_DOOR_OPENED);
m_hBlockingDoor = NULL;
}
}
else
SetCondition(COND_BLOCKED_BY_DOOR);
if (ConditionInterruptsCurSchedule(COND_ZOMBIE_CHARGE_TARGET_MOVED))
{
if (GetNavigator()->IsGoalActive())
{
const float CHARGE_RESET_TOLERANCE = 60.0;
if (!GetEnemy() || (m_vPositionCharged - GetEnemyLKP()).Length() > CHARGE_RESET_TOLERANCE)
SetCondition(COND_ZOMBIE_CHARGE_TARGET_MOVED);
}
}
}
//----------------------------------------------------
// SelectFailSchedule()
//----------------------------------------------------
int CZombie::SelectFailSchedule(int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode)
{
if (HasCondition(COND_BLOCKED_BY_DOOR) && m_hBlockingDoor != NULL)
{
ClearCondition(COND_BLOCKED_BY_DOOR);
if (m_NextTimeToStartDoorBash.Expired() && failedSchedule != SCHED_ZOMBIE_BASH_DOOR)
return SCHED_ZOMBIE_BASH_DOOR;
m_hBlockingDoor = NULL;
}
if (failedSchedule != SCHED_ZOMBIE_CHARGE_ENEMY && IsPathTaskFailure(taskFailCode) && random->RandomInt(1, 100) < 50)
return SCHED_ZOMBIE_CHARGE_ENEMY;
if (failedSchedule != SCHED_ZOMBIE_WANDER_ANGRILY && (failedSchedule == SCHED_TAKE_COVER_FROM_ENEMY || failedSchedule == SCHED_CHASE_ENEMY_FAILED))
return SCHED_ZOMBIE_WANDER_ANGRILY;
return BaseClass::SelectFailSchedule(failedSchedule, failedTask, taskFailCode);
}
//----------------------------------------------------
// TranslateSchedule()
//----------------------------------------------------
int CZombie::TranslateSchedule(int scheduleType)
{
if (scheduleType == SCHED_COMBAT_FACE && IsUnreachable(GetEnemy()))
return SCHED_TAKE_COVER_FROM_ENEMY;
if (!m_fIsTorso && scheduleType == SCHED_FAIL)
return SCHED_ZOMBIE_FAIL;
return BaseClass::TranslateSchedule(scheduleType);
}
//----------------------------------------------------
// NPC_TranslateActivity()
// Convertir actividades que este zombi no posee a
// actividades válidas.
//----------------------------------------------------
Activity CZombie::NPC_TranslateActivity(Activity newActivity)
{
newActivity = BaseClass::NPC_TranslateActivity(newActivity);
// Los zombis clasicos no pueden corren (¡carajo!)
if (newActivity == ACT_RUN)
return ACT_WALK;
if (m_fIsTorso && (newActivity == ACT_ZOMBIE_TANTRUM))
return ACT_IDLE;
return newActivity;
}
//----------------------------------------------------
// OnStateChange()
// Cuando el estado cambia.
//----------------------------------------------------
void CZombie::OnStateChange(NPC_STATE OldState, NPC_STATE NewState)
{
BaseClass::OnStateChange(OldState, NewState);
}
//----------------------------------------------------
// StartTask()
// Empezar tarea
//----------------------------------------------------
void CZombie::StartTask(const Task_t *pTask)
{
switch( pTask->iTask )
{
case TASK_ZOMBIE_EXPRESS_ANGER:
{
if (random->RandomInt( 1, 4 ) == 2)
SetIdealActivity((Activity)ACT_ZOMBIE_TANTRUM);
else
TaskComplete();
break;
}
case TASK_ZOMBIE_YAW_TO_DOOR:
{
AssertMsg(m_hBlockingDoor != NULL, "Expected condition handling to break schedule before landing here");
if (m_hBlockingDoor != NULL)
GetMotor()->SetIdealYaw(m_flDoorBashYaw);
TaskComplete();
break;
}
// Atacar puerta.
case TASK_ZOMBIE_ATTACK_DOOR:
{
m_DurationDoorBash.Reset();
SetIdealActivity(SelectDoorBash());
break;
}
// ¿Encontrar enemigo?
case TASK_ZOMBIE_CHARGE_ENEMY:
{
if (!GetEnemy())
TaskFail(FAIL_NO_ENEMY);
else if (GetNavigator()->SetVectorGoalFromTarget(GetEnemy()->GetLocalOrigin()))
{
m_vPositionCharged = GetEnemy()->GetLocalOrigin();
TaskComplete();
}
else
TaskFail( FAIL_NO_ROUTE );
break;
}
default:
BaseClass::StartTask(pTask);
break;
}
}
//----------------------------------------------------
// RunTask()
// Ejecutar tarea.
//----------------------------------------------------
void CZombie::RunTask(const Task_t *pTask)
{
switch( pTask->iTask )
{
// Atacar puerta
case TASK_ZOMBIE_ATTACK_DOOR:
{
if (IsActivityFinished())
{
if (m_DurationDoorBash.Expired())
{
TaskComplete();
m_NextTimeToStartDoorBash.Reset();
}
else
ResetIdealActivity(SelectDoorBash());
}
break;
}
// ¿Buscar enemigo?
case TASK_ZOMBIE_CHARGE_ENEMY:
{
break;
}
case TASK_ZOMBIE_EXPRESS_ANGER:
{
if (IsActivityFinished())
TaskComplete();
break;
}
default:
BaseClass::RunTask(pTask);
break;
}
}
//----------------------------------------------------
// OnObstructingDoor()
// Cuando una puerta obstruye el objetivo.
//----------------------------------------------------
bool CZombie::OnObstructingDoor(AILocalMoveGoal_t *pMoveGoal, CBaseDoor *pDoor, float distClear, AIMoveResult_t *pResult)
{
if (BaseClass::OnObstructingDoor(pMoveGoal, pDoor, distClear, pResult))
{
if (IsMoveBlocked(*pResult) && pMoveGoal->directTrace.vHitNormal != vec3_origin)
{
m_hBlockingDoor = pDoor;
m_flDoorBashYaw = UTIL_VecToYaw( pMoveGoal->directTrace.vHitNormal * -1 );
}
return true;
}
return false;
}
//----------------------------------------------------
// SelectDoorBash()
// Seleccionar tarea adecuada cuando una puerta esta
// obstruyendo el objetivo.
//----------------------------------------------------
Activity CZombie::SelectDoorBash()
{
// Animación de ataque.
if (random->RandomInt( 1, 3 ) == 1)
return ACT_MELEE_ATTACK1;
// Animación de golpear puerta.
return (Activity)ACT_ZOMBIE_WALLPOUND;
}
//----------------------------------------------------
// Ignite()
// Tareas al quemarse.
// Nota: Esta función solo sirve para decidir si el
// zombi debe "gritar espantosamente" al quemarse.
// Nota 2: Alemania (y otros países) no permiten
// "gritar espantosamente", encerio...
//----------------------------------------------------
void CZombie::Ignite(float flFlameLifetime, bool bNPCOnly, float flSize, bool bCalledByLevelDesigner)
{
// Zombi vivo y no se esta quemando.
if(!IsOnFire() && IsAlive())
{
// ¡Prenderle fuego!
BaseClass::Ignite(flFlameLifetime, bNPCOnly, flSize, bCalledByLevelDesigner);
// El filtro de "violencia baja" esta desactivado.
// (Gracias países estrictos...)
if (!UTIL_IsLowViolence())
{
RemoveSpawnFlags(SF_NPC_GAG);
MoanSound(envZombieMoanIgnited, ARRAYSIZE(envZombieMoanIgnited));
if (m_pMoanSound)
{
ENVELOPE_CONTROLLER.SoundChangePitch(m_pMoanSound, 120, 1.0);
ENVELOPE_CONTROLLER.SoundChangeVolume(m_pMoanSound, 1, 1.0);
}
}
}
}
//----------------------------------------------------
// Extinguish()
// Zombi quemandose, extinguir fuego.
//----------------------------------------------------
void CZombie::Extinguish()
{
// ¡Calma! Deja de gritar.
if(m_pMoanSound)
{
ENVELOPE_CONTROLLER.SoundChangeVolume(m_pMoanSound, 0, 2.0);
ENVELOPE_CONTROLLER.SoundChangePitch(m_pMoanSound, 100, 2.0);
m_flNextMoanSound = gpGlobals->curtime + random->RandomFloat(2.0, 4.0);
}
BaseClass::Extinguish();
}
//----------------------------------------------------
// OnTakeDamage_Alive()
// Al recibir daño y estar aún vivo.
//----------------------------------------------------
int CZombie::OnTakeDamage_Alive(const CTakeDamageInfo &inputInfo)
{
return BaseClass::OnTakeDamage_Alive(inputInfo);
}
//----------------------------------------------------
// IsHeavyDamage()
// Daños graves
//----------------------------------------------------
bool CZombie::IsHeavyDamage(const CTakeDamageInfo &info)
{
return BaseClass::IsHeavyDamage(info);
}
//----------------------------------------------------
// IsSquashed()
// ¿Producir el efecto de aplastado?
//----------------------------------------------------
bool CZombie::IsSquashed(const CTakeDamageInfo &info)
{
if(GetHealth() > 0)
return false;
if(info.GetDamageType() & DMG_CRUSH)
{
IPhysicsObject *pCrusher = info.GetInflictor()->VPhysicsGetObject();
// Calcular si el objeto es pesado como para producir el efecto.
// ZOMBIE_SQUASH_MASS = 300 kg
if(pCrusher && pCrusher->GetMass() >= ZOMBIE_SQUASH_MASS && info.GetInflictor()->WorldSpaceCenter().z > EyePosition().z)
return true;
}
return false;
}
//----------------------------------------------------
// BuildScheduleTestBits()
//
//----------------------------------------------------
void CZombie::BuildScheduleTestBits()
{
BaseClass::BuildScheduleTestBits();
if(!m_fIsTorso && !IsCurSchedule(SCHED_FLINCH_PHYSICS) && !m_ActBusyBehavior.IsActive())
SetCustomInterruptCondition( COND_PHYSICS_DAMAGE );
}
//----------------------------------------------------
// Condiciones e información de la IA
//----------------------------------------------------
AI_BEGIN_CUSTOM_NPC(npc_zombie, CZombie)
DECLARE_CONDITION(COND_BLOCKED_BY_DOOR)
DECLARE_CONDITION(COND_DOOR_OPENED)
DECLARE_CONDITION(COND_ZOMBIE_CHARGE_TARGET_MOVED)
DECLARE_TASK(TASK_ZOMBIE_EXPRESS_ANGER)
DECLARE_TASK(TASK_ZOMBIE_YAW_TO_DOOR)
DECLARE_TASK(TASK_ZOMBIE_ATTACK_DOOR)
DECLARE_TASK(TASK_ZOMBIE_CHARGE_ENEMY)
DECLARE_ACTIVITY(ACT_ZOMBIE_TANTRUM);
DECLARE_ACTIVITY(ACT_ZOMBIE_WALLPOUND);
DEFINE_SCHEDULE
(
SCHED_ZOMBIE_BASH_DOOR,
" Tasks"
" TASK_SET_ACTIVITY ACTIVITY:ACT_ZOMBIE_TANTRUM"
" TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_TAKE_COVER_FROM_ENEMY"
" TASK_ZOMBIE_YAW_TO_DOOR 0"
" TASK_FACE_IDEAL 0"
" TASK_ZOMBIE_ATTACK_DOOR 0"
""
" Interrupts"
" COND_ZOMBIE_RELEASECRAB"
" COND_ENEMY_DEAD"
" COND_NEW_ENEMY"
" COND_DOOR_OPENED"
)
DEFINE_SCHEDULE
(
SCHED_ZOMBIE_WANDER_ANGRILY,
" Tasks"
" TASK_WANDER 480240" // 48 units to 240 units.
" TASK_WALK_PATH 0"
" TASK_WAIT_FOR_MOVEMENT 4"
""
" Interrupts"
" COND_ZOMBIE_RELEASECRAB"
" COND_ENEMY_DEAD"
" COND_NEW_ENEMY"
" COND_DOOR_OPENED"
)
DEFINE_SCHEDULE
(
SCHED_ZOMBIE_CHARGE_ENEMY,
" Tasks"
" TASK_ZOMBIE_CHARGE_ENEMY 0"
" TASK_WALK_PATH 0"
" TASK_WAIT_FOR_MOVEMENT 0"
" TASK_PLAY_SEQUENCE ACTIVITY:ACT_ZOMBIE_TANTRUM" /* placeholder until frustration/rage/fence shake animation available */
""
" Interrupts"
" COND_ZOMBIE_RELEASECRAB"
" COND_ENEMY_DEAD"
" COND_NEW_ENEMY"
" COND_DOOR_OPENED"
" COND_ZOMBIE_CHARGE_TARGET_MOVED"
)
DEFINE_SCHEDULE
(
SCHED_ZOMBIE_FAIL,
" Tasks"
" TASK_STOP_MOVING 0"
" TASK_SET_ACTIVITY ACTIVITY:ACT_ZOMBIE_TANTRUM"
" TASK_WAIT 1"
" TASK_WAIT_PVS 0"
""
" Interrupts"
" COND_CAN_RANGE_ATTACK1 "
" COND_CAN_RANGE_ATTACK2 "
" COND_CAN_MELEE_ATTACK1 "
" COND_CAN_MELEE_ATTACK2"
" COND_GIVE_WAY"
" COND_DOOR_OPENED"
)
AI_END_CUSTOM_NPC()
//=============================================================================
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment