|
|
|
/***
|
|
|
|
*
|
|
|
|
* Copyright (c) 1996-2001, Valve LLC. All rights reserved.
|
|
|
|
*
|
|
|
|
* This product contains software technology licensed from Id
|
|
|
|
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
|
|
|
* All Rights Reserved.
|
|
|
|
*
|
|
|
|
* This source code contains proprietary and confidential information of
|
|
|
|
* Valve LLC and its suppliers. Access to this code is restricted to
|
|
|
|
* persons who have executed a written SDK license with Valve. Any access,
|
|
|
|
* use or distribution of this code by or to any unlicensed person is illegal.
|
|
|
|
*
|
|
|
|
****/
|
|
|
|
|
|
|
|
#include "extdll.h"
|
|
|
|
#include "util.h"
|
|
|
|
#include "cbase.h"
|
|
|
|
#include "nodes.h"
|
|
|
|
#include "monsters.h"
|
|
|
|
#include "schedule.h"
|
|
|
|
#include "soundent.h"
|
|
|
|
#include "weapons.h"
|
|
|
|
#include "player.h"
|
|
|
|
|
|
|
|
#define NUM_PITWORM_LEVELS 4
|
|
|
|
|
|
|
|
class CPitWorm : public CBaseMonster
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
void Spawn(void);
|
|
|
|
void Precache(void);
|
|
|
|
int Classify(void);
|
|
|
|
virtual int ObjectCaps(void) { return CBaseMonster::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
|
|
|
|
|
|
|
|
void IdleSound(void);
|
|
|
|
void AlertSound(void);
|
|
|
|
void DeathSound(void);
|
|
|
|
|
|
|
|
int Save(CSave &save);
|
|
|
|
int Restore(CRestore &restore);
|
|
|
|
static TYPEDESCRIPTION m_SaveData[];
|
|
|
|
|
|
|
|
void AngrySound(void);
|
|
|
|
void FlinchSound(void);
|
|
|
|
void SwipeSound(void);
|
|
|
|
void BeamSound(void);
|
|
|
|
void pPainSound(void);
|
|
|
|
|
|
|
|
void SetObjectCollisionBox(void);
|
|
|
|
void HandleAnimEvent(MonsterEvent_t *pEvent);
|
|
|
|
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 CanPlaySequence(BOOL fDisregardState);
|
|
|
|
void Activate();
|
|
|
|
|
|
|
|
void EXPORT StartupThink(void);
|
|
|
|
void EXPORT DyingThink(void);
|
|
|
|
void EXPORT StartupUse(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value);
|
|
|
|
void EXPORT NullThink(void);
|
|
|
|
void EXPORT CommandUse(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value);
|
|
|
|
void EXPORT HuntThink(void);
|
|
|
|
|
|
|
|
void EXPORT WormTouch(CBaseEntity* pOther);
|
|
|
|
|
|
|
|
void FloatSequence(void);
|
|
|
|
void NextActivity(void);
|
|
|
|
|
|
|
|
Vector m_avelocity;
|
|
|
|
|
|
|
|
Vector m_vecTarget;
|
|
|
|
Vector m_posTarget;
|
|
|
|
|
|
|
|
Vector m_vecDesired;
|
|
|
|
Vector m_posDesired;
|
|
|
|
|
|
|
|
Vector m_vecEmemyPos;
|
|
|
|
|
|
|
|
float m_flMinZ;
|
|
|
|
float m_flMaxZ;
|
|
|
|
|
|
|
|
float m_flLastSeen;
|
|
|
|
float m_flPrevSeen;
|
|
|
|
|
|
|
|
int m_iLevel;
|
|
|
|
int m_iClass;
|
|
|
|
float m_flAdj;
|
|
|
|
float m_distDesired;
|
|
|
|
float m_flNextBlink;
|
|
|
|
|
|
|
|
int Level(float dz);
|
|
|
|
|
|
|
|
const Vector& IdealPosition(const int level) const;
|
|
|
|
const Vector& IdealPosition(const float dz) const;
|
|
|
|
const Vector& IdealPosition(CBaseEntity* pEnemy) const;
|
|
|
|
|
|
|
|
void UpdateBodyControllers(void);
|
|
|
|
|
|
|
|
void CreateBeam(const Vector& src, const Vector& target, int width);
|
|
|
|
void DestroyBeam(void);
|
|
|
|
void UpdateBeam(const Vector& src, const Vector& target);
|
|
|
|
|
|
|
|
void SetupBeamPoints(CBaseEntity* pEnemy, Vector* vecleft, Vector* vecRight);
|
|
|
|
void UpdateBeamPoints(CBaseEntity* pEnemy, Vector* vecTarget);
|
|
|
|
|
|
|
|
void CreateGlow(void);
|
|
|
|
void DestroyGlow(void);
|
|
|
|
void EyeOff(void);
|
|
|
|
void EyeOn(int level);
|
|
|
|
void EyeUpdate(void);
|
|
|
|
void CreateBeam();
|
|
|
|
|
|
|
|
void SetYawSpeed();
|
|
|
|
BOOL CheckMeleeAttack1(float flDot, float flDist);
|
|
|
|
BOOL CheckRangeAttack1(float flDot, float flDist);
|
|
|
|
|
|
|
|
|
|
|
|
float m_flInitialYaw;
|
|
|
|
Vector m_spawnAngles;
|
|
|
|
Vector m_vecLevels[NUM_PITWORM_LEVELS];
|
|
|
|
|
|
|
|
CBeam* m_pBeam;
|
|
|
|
float m_flBeamYaw;
|
|
|
|
float m_flBeamTime;
|
|
|
|
BOOL m_fBeamOn;
|
|
|
|
BOOL m_fActivated;
|
|
|
|
CSprite* m_pEyeGlow;
|
|
|
|
int m_eyeBrightness;
|
|
|
|
float m_painSoundTime;
|
|
|
|
float m_slowMode;
|
|
|
|
float m_slowTime;
|
|
|
|
Vector m_vecGoalAngles;
|
|
|
|
Vector m_vecCurAngles;
|
|
|
|
Vector m_eyes;
|
|
|
|
float m_flDamageTime;
|
|
|
|
float m_flDamage;
|
|
|
|
float m_flPlayerDamage;
|
|
|
|
BOOL m_fBlink;
|
|
|
|
int m_iLaserHitTimes;
|
|
|
|
|
|
|
|
float m_flNextAttackTime;
|
|
|
|
float m_flNextIdleSoundTime;
|
|
|
|
|
|
|
|
static const char* pHitSilo[];
|
|
|
|
static const char* pClangSounds[];
|
|
|
|
static const char* pAngrySounds[];
|
|
|
|
static const char* pSwipeSounds[];
|
|
|
|
static const char* pPainSounds[];
|
|
|
|
static const char* pFlinchSounds[];
|
|
|
|
|
|
|
|
static const char* pAlertSounds[];
|
|
|
|
static const char* pIdleSounds[];
|
|
|
|
static const char* pDeathSounds[];
|
|
|
|
};
|
|
|
|
|
|
|
|
LINK_ENTITY_TO_CLASS(monster_pitworm, CPitWorm)
|
|
|
|
LINK_ENTITY_TO_CLASS(monster_pitworm_up, CPitWorm)
|
|
|
|
|
|
|
|
#define PITWORM_HEIGHT 584
|
|
|
|
#define PITWORM_EYE_OFFSET Vector(0, 0, 445)
|
|
|
|
#define PITWORM_EYEBLAST_ATTACK_DIST 460 // 464
|
|
|
|
#define PITWORM_EYEBLAST_DURATION 3.03f // 0.49 approximatly. (49 miliseconds)
|
|
|
|
|
|
|
|
#define PITWORM_CONTROLLER_EYE_YAW 0
|
|
|
|
#define PITWORM_CONTROLLER_EYE_PITCH 1
|
|
|
|
#define PITWORM_CONTROLLER_BODY_YAW 2
|
|
|
|
|
|
|
|
#define PITWORM_EYE_PITCH_MIN -45
|
|
|
|
#define PITWORM_EYE_PITCH_MAX 45
|
|
|
|
#define PITWORM_EYE_YAW_MIN -45
|
|
|
|
#define PITWORM_EYE_YAW_MAX 45
|
|
|
|
#define PITWORM_BODY_YAW_MIN -50
|
|
|
|
#define PITWORM_BODY_YAW_MAX 50
|
|
|
|
|
|
|
|
#define PITWORM_LEVEL0 0 // Pitworm's 'foot' level | when player is at First level 1.
|
|
|
|
#define PITWORM_LEVEL1 1 // First level | player goes to waste station door 1 or 2.
|
|
|
|
#define PITWORM_LEVEL2 2 // Second level | player goes to narrow corridor that leads to stairs.
|
|
|
|
#define PITWORM_LEVEL3 3 // Third level | entry room, steam vent and valve pressure controls.
|
|
|
|
|
|
|
|
#define PITWORM_LEVEL0_HEIGHT -632 // Pitworm's 'foot' level | when player is at First level 1. | (Hammer units)
|
|
|
|
#define PITWORM_LEVEL1_HEIGHT -48 // First level | player goes to waste station door 1 or 2. | (Hammer units)
|
|
|
|
#define PITWORM_LEVEL2_HEIGHT 208 // Second level | player goes to narrow corridor that leads to stairs. | (Hammer units)
|
|
|
|
#define PITWORM_LEVEL3_HEIGHT 304 // Third level | entry room, steam vent and valve pressure controls. | (Hammer units)
|
|
|
|
|
|
|
|
|
|
|
|
#define PITWORM_LEVEL0_VIEWOFFSET Vector(0, 0, PITWORM_HEIGHT)
|
|
|
|
#define PITWORM_LEVEL1_VIEWOFFSET Vector(0, 0, 256)
|
|
|
|
#define PITWORM_LEVEL2_VIEWOFFSET Vector(0, 0, 96)
|
|
|
|
#define PITWORM_LEVEL3_VIEWOFFSET PITWORM_LEVEL2_VIEWOFFSET
|
|
|
|
|
|
|
|
#define bits_MEMORY_CHILDPAIR ( bits_MEMORY_CUSTOM1 )
|
|
|
|
#define bits_MEMORY_ADVANCE_NODE ( bits_MEMORY_CUSTOM2 )
|
|
|
|
#define bits_MEMORY_COMPLETED_NODE ( bits_MEMORY_CUSTOM3 )
|
|
|
|
#define bits_MEMORY_FIRED_NODE ( bits_MEMORY_CUSTOM4 )
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// Monster's Anim Events Go Here
|
|
|
|
//=========================================================
|
|
|
|
#define PITWORM_AE_SWIPE ( 1 )
|
|
|
|
#define PITWORM_AE_EYEBLAST_START ( 2 )
|
|
|
|
#define PITWORM_AE_EYEBLAST_END ( 4 )
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// Save & Restore
|
|
|
|
//=========================================================
|
|
|
|
TYPEDESCRIPTION CPitWorm::m_SaveData[] =
|
|
|
|
{
|
|
|
|
DEFINE_FIELD(CPitWorm, m_avelocity, FIELD_VECTOR),
|
|
|
|
|
|
|
|
DEFINE_FIELD(CPitWorm, m_vecTarget, FIELD_VECTOR),
|
|
|
|
DEFINE_FIELD(CPitWorm, m_posTarget, FIELD_POSITION_VECTOR),
|
|
|
|
DEFINE_FIELD(CPitWorm, m_vecDesired, FIELD_VECTOR),
|
|
|
|
DEFINE_FIELD(CPitWorm, m_posDesired, FIELD_POSITION_VECTOR),
|
|
|
|
DEFINE_FIELD(CPitWorm, m_flMinZ, FIELD_FLOAT),
|
|
|
|
DEFINE_FIELD(CPitWorm, m_flMaxZ, FIELD_FLOAT),
|
|
|
|
|
|
|
|
DEFINE_FIELD(CPitWorm, m_flLastSeen, FIELD_TIME),
|
|
|
|
DEFINE_FIELD(CPitWorm, m_flPrevSeen, FIELD_TIME),
|
|
|
|
DEFINE_FIELD(CPitWorm, m_iLevel, FIELD_INTEGER),
|
|
|
|
DEFINE_FIELD(CPitWorm, m_flAdj, FIELD_FLOAT),
|
|
|
|
DEFINE_FIELD(CPitWorm, m_distDesired, FIELD_FLOAT),
|
|
|
|
|
|
|
|
DEFINE_FIELD(CPitWorm, m_flInitialYaw, FIELD_FLOAT),
|
|
|
|
DEFINE_FIELD(CPitWorm, m_spawnAngles, FIELD_VECTOR),
|
|
|
|
DEFINE_ARRAY(CPitWorm, m_vecLevels, FIELD_POSITION_VECTOR, NUM_PITWORM_LEVELS),
|
|
|
|
DEFINE_FIELD(CPitWorm, m_pBeam, FIELD_CLASSPTR),
|
|
|
|
DEFINE_FIELD(CPitWorm, m_flBeamTime, FIELD_TIME),
|
|
|
|
DEFINE_FIELD(CPitWorm, m_flBeamYaw, FIELD_FLOAT),
|
|
|
|
DEFINE_FIELD(CPitWorm, m_fBeamOn, FIELD_BOOLEAN),
|
|
|
|
DEFINE_FIELD(CPitWorm, m_pEyeGlow, FIELD_CLASSPTR),
|
|
|
|
DEFINE_FIELD(CPitWorm, m_eyeBrightness, FIELD_INTEGER),
|
|
|
|
|
|
|
|
DEFINE_FIELD(CPitWorm, m_vecGoalAngles, FIELD_VECTOR),
|
|
|
|
DEFINE_FIELD(CPitWorm, m_vecCurAngles, FIELD_VECTOR),
|
|
|
|
DEFINE_FIELD(CPitWorm, m_flNextAttackTime, FIELD_TIME),
|
|
|
|
DEFINE_FIELD(CPitWorm, m_flNextIdleSoundTime, FIELD_TIME),
|
|
|
|
DEFINE_FIELD(CPitWorm, m_fActivated, FIELD_BOOLEAN),
|
|
|
|
DEFINE_FIELD(CPitWorm, m_iLaserHitTimes, FIELD_INTEGER),
|
|
|
|
DEFINE_FIELD(CPitWorm, m_flDamage, FIELD_FLOAT),
|
|
|
|
DEFINE_FIELD(CPitWorm, m_fBlink, FIELD_BOOLEAN),
|
|
|
|
DEFINE_FIELD(CPitWorm, m_flPlayerDamage, FIELD_FLOAT),
|
|
|
|
DEFINE_FIELD(CPitWorm, m_vecEmemyPos, FIELD_VECTOR),
|
|
|
|
DEFINE_FIELD(CPitWorm, m_flNextBlink, FIELD_FLOAT),
|
|
|
|
};
|
|
|
|
|
|
|
|
IMPLEMENT_SAVERESTORE(CPitWorm, CBaseMonster)
|
|
|
|
|
|
|
|
|
|
|
|
const char *CPitWorm::pHitSilo[] =
|
|
|
|
{
|
|
|
|
"tentacle/te_strike1.wav",
|
|
|
|
"tentacle/te_strike2.wav",
|
|
|
|
};
|
|
|
|
|
|
|
|
const char* CPitWorm::pClangSounds[] =
|
|
|
|
{
|
|
|
|
"pitworm/clang1.wav",
|
|
|
|
"pitworm/clang2.wav",
|
|
|
|
"pitworm/clang3.wav",
|
|
|
|
};
|
|
|
|
|
|
|
|
const char* CPitWorm::pAngrySounds[] =
|
|
|
|
{
|
|
|
|
"pitworm/pit_worm_angry1.wav",
|
|
|
|
"pitworm/pit_worm_angry2.wav",
|
|
|
|
"pitworm/pit_worm_angry3.wav",
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const char* CPitWorm::pSwipeSounds[] =
|
|
|
|
{
|
|
|
|
"pitworm/pit_worm_attack_swipe1.wav",
|
|
|
|
"pitworm/pit_worm_attack_swipe2.wav",
|
|
|
|
"pitworm/pit_worm_attack_swipe3.wav",
|
|
|
|
};
|
|
|
|
|
|
|
|
const char* CPitWorm::pPainSounds[] =
|
|
|
|
{
|
|
|
|
"pitworm/pit_worm_flinch2.wav",
|
|
|
|
"pitworm/pit_worm_flinch2.wav",
|
|
|
|
"pitworm/pit_worm_flinch2.wav",
|
|
|
|
};
|
|
|
|
|
|
|
|
const char* CPitWorm::pFlinchSounds[] =
|
|
|
|
{
|
|
|
|
"pitworm/pit_worm_flinch1.wav",
|
|
|
|
"pitworm/pit_worm_flinch2.wav",
|
|
|
|
};
|
|
|
|
|
|
|
|
const char* CPitWorm::pAlertSounds[] =
|
|
|
|
{
|
|
|
|
"pitworm/pit_worm_alert.wav",
|
|
|
|
};
|
|
|
|
|
|
|
|
const char* CPitWorm::pIdleSounds[] =
|
|
|
|
{
|
|
|
|
"pitworm/pit_worm_idle1.wav",
|
|
|
|
"pitworm/pit_worm_idle2.wav",
|
|
|
|
"pitworm/pit_worm_idle3.wav",
|
|
|
|
};
|
|
|
|
|
|
|
|
const char* CPitWorm::pDeathSounds[] =
|
|
|
|
{
|
|
|
|
"pitworm/pit_worm_death.wav",
|
|
|
|
};
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// Spawn
|
|
|
|
//=========================================================
|
|
|
|
void CPitWorm::Spawn()
|
|
|
|
{
|
|
|
|
Precache();
|
|
|
|
|
|
|
|
pev->movetype = MOVETYPE_STEP;
|
|
|
|
pev->solid = SOLID_SLIDEBOX;
|
|
|
|
|
|
|
|
SET_MODEL(ENT(pev), "models/pit_worm_up.mdl");
|
|
|
|
UTIL_SetSize(pev, Vector(-64, -128, 0), Vector(64, 128, 64));
|
|
|
|
UTIL_SetOrigin(pev, pev->origin);
|
|
|
|
|
|
|
|
pev->health = 150 * gSkillData.pwormHealth;
|
|
|
|
pev->view_ofs = PITWORM_EYE_OFFSET;
|
|
|
|
|
|
|
|
m_flFieldOfView = -1;
|
|
|
|
|
|
|
|
pev->sequence = 0;
|
|
|
|
ResetSequenceInfo();
|
|
|
|
|
|
|
|
InitBoneControllers();
|
|
|
|
|
|
|
|
SetThink(&CPitWorm::StartupThink);
|
|
|
|
pev->nextthink = gpGlobals->time + 0.1;
|
|
|
|
|
|
|
|
m_spawnAngles = pev->angles;
|
|
|
|
m_flInitialYaw = m_spawnAngles.y;
|
|
|
|
pev->ideal_yaw = m_flInitialYaw;
|
|
|
|
|
|
|
|
m_IdealMonsterState = MONSTERSTATE_IDLE;// Assume monster will be idle, until proven otherwise
|
|
|
|
|
|
|
|
m_IdealActivity = ACT_IDLE;
|
|
|
|
|
|
|
|
m_flDistTooFar = 1024.0;
|
|
|
|
m_flDistLook = 2048.0;
|
|
|
|
|
|
|
|
// set eye position
|
|
|
|
SetEyePosition();
|
|
|
|
|
|
|
|
m_flLastSeen = 0.0f;
|
|
|
|
m_flPrevSeen = 0.0f;
|
|
|
|
m_iLevel = PITWORM_LEVEL1;
|
|
|
|
|
|
|
|
m_pBeam = NULL;
|
|
|
|
m_flBeamYaw = 0.0f;
|
|
|
|
m_fBeamOn = FALSE;
|
|
|
|
m_eyeBrightness = 0;
|
|
|
|
m_slowMode = 4;
|
|
|
|
m_afCapability = bits_CAP_MELEE_ATTACK1;
|
|
|
|
m_slowTime = gpGlobals->time;
|
|
|
|
m_vecCurAngles = Vector(0, pev->angles.y, 0);
|
|
|
|
m_vecGoalAngles = Vector(0, pev->angles.y, 0);
|
|
|
|
m_flNextAttackTime = gpGlobals->time;
|
|
|
|
m_flNextIdleSoundTime = gpGlobals->time;
|
|
|
|
m_bloodColor = BLOOD_COLOR_GREEN;
|
|
|
|
m_fActivated = FALSE;
|
|
|
|
m_vecEmemyPos = g_vecZero;
|
|
|
|
m_flNextBlink = gpGlobals->time;
|
|
|
|
m_fBlink = FALSE;
|
|
|
|
m_iLaserHitTimes = 0;
|
|
|
|
|
|
|
|
// Create the eye glow.
|
|
|
|
CreateGlow();
|
|
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// Precache - precaches all resources this monster needs
|
|
|
|
//=========================================================
|
|
|
|
void CPitWorm::Precache()
|
|
|
|
{
|
|
|
|
PRECACHE_MODEL("models/pit_worm_up.mdl");
|
|
|
|
|
|
|
|
PRECACHE_SOUND_ARRAY(pHitSilo);
|
|
|
|
|
|
|
|
PRECACHE_SOUND("pitworm/pit_worm_alert.wav");
|
|
|
|
PRECACHE_SOUND("pitworm/pit_worm_attack_eyeblast.wav");
|
|
|
|
PRECACHE_SOUND("pitworm/pit_worm_attack_eyeblast_impact.wav");
|
|
|
|
PRECACHE_SOUND("pitworm/pit_worm_death.wav");
|
|
|
|
PRECACHE_SOUND("pitworm/pit_worm_alert(scream).wav");
|
|
|
|
|
|
|
|
PRECACHE_SOUND_ARRAY(pClangSounds);
|
|
|
|
PRECACHE_SOUND_ARRAY(pAngrySounds);
|
|
|
|
PRECACHE_SOUND_ARRAY(pSwipeSounds);
|
|
|
|
PRECACHE_SOUND_ARRAY(pFlinchSounds);
|
|
|
|
PRECACHE_SOUND_ARRAY(pIdleSounds);
|
|
|
|
PRECACHE_SOUND_ARRAY(pPainSounds);
|
|
|
|
|
|
|
|
PRECACHE_MODEL("sprites/gworm_beam_02.spr");
|
|
|
|
PRECACHE_MODEL("sprites/glow_grn.spr");
|
|
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// Classify
|
|
|
|
//=========================================================
|
|
|
|
int CPitWorm::Classify(void)
|
|
|
|
{
|
|
|
|
return CLASS_ALIEN_MONSTER;
|
|
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// IdleSound
|
|
|
|
//=========================================================
|
|
|
|
void CPitWorm::IdleSound(void)
|
|
|
|
{
|
|
|
|
EMIT_SOUND(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pIdleSounds), VOL_NORM, ATTN_NORM);
|
|
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// AlertSound
|
|
|
|
//=========================================================
|
|
|
|
void CPitWorm::AlertSound(void)
|
|
|
|
{
|
|
|
|
EMIT_SOUND(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pAlertSounds), VOL_NORM, ATTN_NORM);
|
|
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// DeathSound
|
|
|
|
//=========================================================
|
|
|
|
void CPitWorm::DeathSound(void)
|
|
|
|
{
|
|
|
|
EMIT_SOUND(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pDeathSounds), VOL_NORM, ATTN_NORM);
|
|
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// AngrySound
|
|
|
|
//=========================================================
|
|
|
|
void CPitWorm::AngrySound(void)
|
|
|
|
{
|
|
|
|
EMIT_SOUND(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pAngrySounds), VOL_NORM, ATTN_NORM);
|
|
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// FlinchSound
|
|
|
|
//=========================================================
|
|
|
|
void CPitWorm::FlinchSound(void)
|
|
|
|
{
|
|
|
|
EMIT_SOUND(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pFlinchSounds), VOL_NORM, ATTN_NORM);
|
|
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// SwipeSound
|
|
|
|
//=========================================================
|
|
|
|
void CPitWorm::SwipeSound(void)
|
|
|
|
{
|
|
|
|
EMIT_SOUND(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pSwipeSounds), VOL_NORM, ATTN_NORM);
|
|
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// BeamSound
|
|
|
|
//=========================================================
|
|
|
|
void CPitWorm::BeamSound(void)
|
|
|
|
{
|
|
|
|
EMIT_SOUND(ENT(pev), CHAN_VOICE, "pitworm/pit_worm_attack_eyeblast.wav", VOL_NORM, ATTN_NORM);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPitWorm::pPainSound(void)
|
|
|
|
{
|
|
|
|
EMIT_SOUND(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pPainSounds), VOL_NORM, ATTN_NORM);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// SetObjectCollisionBox
|
|
|
|
//=========================================================
|
|
|
|
void CPitWorm::SetObjectCollisionBox(void)
|
|
|
|
{
|
|
|
|
pev->absmin = pev->origin + Vector(-96, -310, 0);
|
|
|
|
pev->absmax = pev->origin + Vector(96, 32, 512);
|
|
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// HandleAnimEvent
|
|
|
|
//=========================================================
|
|
|
|
void CPitWorm::HandleAnimEvent(MonsterEvent_t *pEvent)
|
|
|
|
{
|
|
|
|
if(gpGlobals->time - m_slowTime <= 3.5)
|
|
|
|
return;
|
|
|
|
|
|
|
|
switch (pEvent->event)
|
|
|
|
{
|
|
|
|
case PITWORM_AE_SWIPE: // bang
|
|
|
|
{
|
|
|
|
Vector vecSrc, vecAngles;
|
|
|
|
GetAttachment(1, vecSrc, vecAngles);
|
|
|
|
Vector mins = pev->origin - Vector( 360, 430, 0 );
|
|
|
|
Vector maxs = pev->origin + Vector( 410, 100, 800 );
|
|
|
|
|
|
|
|
CBaseEntity *pList[10];
|
|
|
|
int count = UTIL_EntitiesInBox( pList, 10, mins, maxs, ( FL_CLIENT | FL_MONSTER ) );
|
|
|
|
|
|
|
|
if( count )
|
|
|
|
{
|
|
|
|
for( int i = 0; i < count; i++ )
|
|
|
|
{
|
|
|
|
// only clients and monsters
|
|
|
|
if( pList[i] != this && IRelationship( pList[i] ) > R_NO && pList[ i ]->pev->deadflag == DEAD_NO ) // this ent is one of our enemies. Barnacle tries to eat it.
|
|
|
|
{
|
|
|
|
pList[i]->TakeDamage(VARS(pev), VARS(pev), gSkillData.pwormDmgSwipe, DMG_CLUB | DMG_SLASH);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Shake the screen.
|
|
|
|
UTIL_EmitAmbientSound(ENT(pev), vecSrc, RANDOM_SOUND_ARRAY(pHitSilo), 1.0, ATTN_NORM, 0, 100);
|
|
|
|
UTIL_ScreenShake(vecSrc, 6.0, 3.0, 1.0, 768);
|
|
|
|
gpGlobals->force_retouch++;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case PITWORM_AE_EYEBLAST_START: // start killing swing
|
|
|
|
{
|
|
|
|
// ALERT(at_console, "PITWORM_AE_EYEBLAST_START\n");
|
|
|
|
|
|
|
|
// Remove the beam if not NULL.
|
|
|
|
DestroyBeam();
|
|
|
|
|
|
|
|
Vector src, angles;
|
|
|
|
GetAttachment(0, src, angles);
|
|
|
|
|
|
|
|
// Create a new beam.
|
|
|
|
CreateBeam(src, angles, 32);
|
|
|
|
|
|
|
|
// Turn eye glow on.
|
|
|
|
EyeOn(255);
|
|
|
|
|
|
|
|
// Reset beam yaw.
|
|
|
|
m_flBeamYaw = 0.0f;
|
|
|
|
m_flBeamTime = gpGlobals->time + PITWORM_EYEBLAST_DURATION;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case PITWORM_AE_EYEBLAST_END: // end killing swing
|
|
|
|
{}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
CBaseMonster::HandleAnimEvent(pEvent);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void CPitWorm::SetYawSpeed( void )
|
|
|
|
{
|
|
|
|
int ys;
|
|
|
|
|
|
|
|
switch ( m_Activity )
|
|
|
|
{
|
|
|
|
case ACT_IDLE:
|
|
|
|
ys = 100;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ys = 90;
|
|
|
|
}
|
|
|
|
pev->yaw_speed = ys;
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOL CPitWorm::CheckRangeAttack1(float flDot, float flDist)
|
|
|
|
{
|
|
|
|
if(flDist >= 1024 && m_flNextAttackTime > gpGlobals->time)
|
|
|
|
{
|
|
|
|
if(m_hEnemy != 0 && m_hEnemy->IsAlive())
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOL CPitWorm::CheckMeleeAttack1(float flDot, float flDist)
|
|
|
|
{
|
|
|
|
if(flDist <= 192 && flDot > 0.7)
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPitWorm::TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType )
|
|
|
|
{
|
|
|
|
if ( ptr->iHitgroup == HITGROUP_HEAD )
|
|
|
|
{
|
|
|
|
if (gpGlobals->time - m_slowTime > 3.5 )
|
|
|
|
{
|
|
|
|
if ( m_flDamage >= 65)
|
|
|
|
{
|
|
|
|
m_slowTime = gpGlobals->time;
|
|
|
|
pev->sequence = LookupSequence("flinch1");
|
|
|
|
|
|
|
|
if(m_fBeamOn)
|
|
|
|
{
|
|
|
|
DestroyBeam();
|
|
|
|
EyeOff();
|
|
|
|
}
|
|
|
|
|
|
|
|
EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "pitworm/pit_worm_flinch2.wav", 1, 0.8, 0, 100);
|
|
|
|
|
|
|
|
m_flDamage = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(gpGlobals->time - m_flDamageTime > 0.5)
|
|
|
|
m_flDamage = 0;
|
|
|
|
|
|
|
|
if((pev->sequence != LookupSequence("scream") || pev->sequence != LookupSequence("flinch1")) && m_fActivated)
|
|
|
|
{
|
|
|
|
m_flDamage += flDamage;
|
|
|
|
|
|
|
|
if(gpGlobals->time - m_flNextBlink >= 0.25)
|
|
|
|
m_fBlink = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
UTIL_BloodDrips(ptr->vecEndPos, ptr->vecEndPos, m_bloodColor, flDamage * 10);
|
|
|
|
UTIL_BloodDecalTrace(ptr, m_bloodColor);
|
|
|
|
|
|
|
|
m_flDamageTime = gpGlobals->time;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (pev->dmgtime != gpGlobals->time || RANDOM_LONG(0, 10) >= 0 )
|
|
|
|
{
|
|
|
|
UTIL_Ricochet(ptr->vecEndPos, RANDOM_LONG(0.5, 1.5));
|
|
|
|
pev->dmgtime = gpGlobals->time;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPitWorm::Activate()
|
|
|
|
{
|
|
|
|
if(m_hTargetEnt == 0)
|
|
|
|
Remember(bits_MEMORY_ADVANCE_NODE);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void CPitWorm::CommandUse(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value)
|
|
|
|
{
|
|
|
|
switch (useType)
|
|
|
|
{
|
|
|
|
case USE_OFF:
|
|
|
|
//ALERT(at_console, "USE_OFF\n");
|
|
|
|
break;
|
|
|
|
case USE_ON:
|
|
|
|
//ALERT(at_console, "USE_ON\n");
|
|
|
|
break;
|
|
|
|
case USE_SET:
|
|
|
|
//ALERT(at_console, "USE_SET\n");
|
|
|
|
break;
|
|
|
|
case USE_TOGGLE:
|
|
|
|
{
|
|
|
|
m_fSequenceFinished = TRUE;
|
|
|
|
pev->health = 0;
|
|
|
|
|
|
|
|
SetThink(&CPitWorm::DyingThink);
|
|
|
|
pev->nextthink = gpGlobals->time;
|
|
|
|
}
|
|
|
|
// ALERT(at_console, "USE_TOGGLE\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
//
|
|
|
|
//=========================================================
|
|
|
|
int CPitWorm::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
|
|
|
|
{
|
|
|
|
// Don't take any acid damage -- BigMomma's mortar is acid
|
|
|
|
if( bitsDamageType & DMG_ACID )
|
|
|
|
flDamage = 0;
|
|
|
|
|
|
|
|
if( !HasMemory( bits_MEMORY_PATH_FINISHED ) )
|
|
|
|
{
|
|
|
|
if( pev->health <= flDamage )
|
|
|
|
{
|
|
|
|
pev->health = flDamage + 1;
|
|
|
|
Remember( bits_MEMORY_ADVANCE_NODE | bits_MEMORY_COMPLETED_NODE );
|
|
|
|
ALERT( at_aiconsole, "BM: Finished node health!!!\n" );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return CBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// StartupThink
|
|
|
|
//=========================================================
|
|
|
|
void CPitWorm::StartupThink(void)
|
|
|
|
{
|
|
|
|
m_flAdj = 350;
|
|
|
|
Vector src = pev->origin;
|
|
|
|
|
|
|
|
CBaseEntity *pEntity = NULL;
|
|
|
|
|
|
|
|
pEntity = UTIL_FindEntityByTargetname(NULL, "pw_tleveldead");
|
|
|
|
if (pEntity)
|
|
|
|
m_vecLevels[PITWORM_LEVEL0] = pEntity->pev->origin;
|
|
|
|
else
|
|
|
|
m_vecLevels[PITWORM_LEVEL0] = Vector(src.x, src.y, PITWORM_LEVEL0_HEIGHT);
|
|
|
|
|
|
|
|
pEntity = UTIL_FindEntityByTargetname(NULL, "pw_tlevel1");
|
|
|
|
if (pEntity)
|
|
|
|
m_vecLevels[PITWORM_LEVEL1] = pEntity->pev->origin;
|
|
|
|
else
|
|
|
|
m_vecLevels[PITWORM_LEVEL1] = Vector(src.x, src.y, PITWORM_LEVEL2_HEIGHT);
|
|
|
|
|
|
|
|
pEntity = UTIL_FindEntityByTargetname(NULL, "pw_tlevel2");
|
|
|
|
if (pEntity)
|
|
|
|
m_vecLevels[PITWORM_LEVEL2] = pEntity->pev->origin;
|
|
|
|
else
|
|
|
|
m_vecLevels[PITWORM_LEVEL2] = Vector(src.x, src.y, PITWORM_LEVEL2_HEIGHT);
|
|
|
|
|
|
|
|
pEntity = UTIL_FindEntityByTargetname(NULL, "pw_tlevel3");
|
|
|
|
if (pEntity)
|
|
|
|
m_vecLevels[PITWORM_LEVEL3] = pEntity->pev->origin;
|
|
|
|
else
|
|
|
|
m_vecLevels[PITWORM_LEVEL3] = Vector(src.x, src.y, PITWORM_LEVEL3_HEIGHT);
|
|
|
|
|
|
|
|
// Set altitude limits.
|
|
|
|
m_flMinZ = m_vecLevels[PITWORM_LEVEL0].z;
|
|
|
|
m_flMaxZ = m_vecLevels[PITWORM_LEVEL3].z;
|
|
|
|
|
|
|
|
// Set default level.
|
|
|
|
UTIL_SetOrigin(pev, IdealPosition(m_iLevel));
|
|
|
|
|
|
|
|
SetThink(&CPitWorm::HuntThink);
|
|
|
|
SetTouch(&CPitWorm::WormTouch);
|
|
|
|
SetUse(&CPitWorm::CommandUse);
|
|
|
|
pev->nextthink = gpGlobals->time + 0.1;
|
|
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
//
|
|
|
|
//=========================================================
|
|
|
|
void CPitWorm::StartupUse(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value)
|
|
|
|
{
|
|
|
|
SetThink(&CPitWorm::HuntThink);
|
|
|
|
pev->nextthink = gpGlobals->time + 0.1;
|
|
|
|
SetUse(&CPitWorm::CommandUse);
|
|
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// NullThink
|
|
|
|
//=========================================================
|
|
|
|
void CPitWorm::NullThink(void)
|
|
|
|
{
|
|
|
|
StudioFrameAdvance();
|
|
|
|
pev->nextthink = gpGlobals->time + 0.5;
|
|
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// DyingThink
|
|
|
|
//=========================================================
|
|
|
|
void CPitWorm::DyingThink(void)
|
|
|
|
{
|
|
|
|
pev->nextthink = gpGlobals->time + 0.1;
|
|
|
|
GlowShellUpdate();
|
|
|
|
DispatchAnimEvents();
|
|
|
|
StudioFrameAdvance();
|
|
|
|
|
|
|
|
if (pev->deadflag == DEAD_NO)
|
|
|
|
{
|
|
|
|
DestroyGlow();
|
|
|
|
DeathSound();
|
|
|
|
pev->deadflag = DEAD_DYING;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pev->deadflag == DEAD_DYING)
|
|
|
|
{
|
|
|
|
if (fabs(pev->origin.z - m_flMaxZ) < 16)
|
|
|
|
{
|
|
|
|
pev->velocity = Vector(0, 0, 0);
|
|
|
|
pev->deadflag = DEAD_DEAD;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(m_fBeamOn)
|
|
|
|
DestroyBeam();
|
|
|
|
|
|
|
|
if (m_fSequenceFinished)
|
|
|
|
{
|
|
|
|
if (pev->sequence != LookupSequence("death"))
|
|
|
|
{
|
|
|
|
pev->sequence = LookupSequence("death");
|
|
|
|
pev->frame = 0;
|
|
|
|
pev->framerate = 0.2f; // Set the framerate manually until we find a better fix.
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
UTIL_Remove(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// HuntThink
|
|
|
|
//=========================================================
|
|
|
|
void CPitWorm::HuntThink(void)
|
|
|
|
{
|
|
|
|
pev->nextthink = gpGlobals->time + 0.1;
|
|
|
|
GlowShellUpdate();
|
|
|
|
DispatchAnimEvents();
|
|
|
|
StudioFrameAdvance();
|
|
|
|
|
|
|
|
// Update the eye glow.
|
|
|
|
EyeUpdate();
|
|
|
|
|
|
|
|
if(!m_fActivated)
|
|
|
|
{
|
|
|
|
if(m_hEnemy == 0)
|
|
|
|
{
|
|
|
|
Look(4096);
|
|
|
|
m_hEnemy = BestVisibleEnemy();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if((pev->origin - m_hEnemy->pev->origin).Length() <= 936)
|
|
|
|
{
|
|
|
|
EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "pitworm/pit_worm_alert(scream).wav", 1, 0.8, 0, 100);
|
|
|
|
pev->frame = 0;
|
|
|
|
pev->sequence = LookupSequence("scream");
|
|
|
|
ResetSequenceInfo();
|
|
|
|
m_fActivated = TRUE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pev->nextthink = gpGlobals->time + 0.01f;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_hEnemy)
|
|
|
|
{
|
|
|
|
// Update body controllers.
|
|
|
|
UpdateBodyControllers();
|
|
|
|
|
|
|
|
//float flDist = (m_hEnemy->pev->origin - m_vecLevels[m_iLevel]).Length();
|
|
|
|
//ALERT(at_console, "pitworm: Distance from eye %f\n", flDist);
|
|
|
|
|
|
|
|
// Update beam if we need to.
|
|
|
|
if (m_fBeamOn)
|
|
|
|
{
|
|
|
|
if (m_flBeamTime > gpGlobals->time && !m_fSequenceFinished)
|
|
|
|
{
|
|
|
|
Vector src, target, angles;
|
|
|
|
GetAttachment(0, src, angles);
|
|
|
|
UpdateBeamPoints(m_hEnemy, &target);
|
|
|
|
|
|
|
|
TraceResult tr;
|
|
|
|
UTIL_TraceLine(src, target + ((target - src).Normalize() * 1000), dont_ignore_monsters, ENT(pev), &tr);
|
|
|
|
|
|
|
|
UpdateBeam(src, tr.vecEndPos);
|
|
|
|
|
|
|
|
// Test to see if we hit
|
|
|
|
CBaseEntity *pHurt = CBaseEntity::Instance(tr.pHit);
|
|
|
|
|
|
|
|
if (pHurt->pev->takedamage)
|
|
|
|
{
|
|
|
|
if(m_flPlayerDamage <= gSkillData.pwormDmgBeam)
|
|
|
|
{
|
|
|
|
pHurt->TakeDamage(VARS(pev), VARS(pev), 1, DMG_GENERIC);
|
|
|
|
UTIL_ScreenShake(pHurt->pev->origin, 1, 10, 0.1, 1);
|
|
|
|
|
|
|
|
if(m_iLaserHitTimes >= 15)
|
|
|
|
{
|
|
|
|
UTIL_BloodDrips(tr.vecEndPos, tr.vecEndPos, BLOOD_COLOR_RED, 128);
|
|
|
|
m_iLaserHitTimes = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
m_flPlayerDamage += gSkillData.pwormDmgBeam/100;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
UTIL_DecalTrace(&tr, RANDOM_LONG(1,2));
|
|
|
|
|
|
|
|
if(m_iLaserHitTimes >= 8)
|
|
|
|
{
|
|
|
|
UTIL_Sparks(tr.vecEndPos);
|
|
|
|
m_iLaserHitTimes = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m_iLaserHitTimes++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Remove the beam.
|
|
|
|
DestroyBeam();
|
|
|
|
|
|
|
|
// Turn eye glow off.
|
|
|
|
EyeOff();
|
|
|
|
|
|
|
|
// Reset beam yaw.
|
|
|
|
m_flBeamYaw = 0.0f;
|
|
|
|
|
|
|
|
// Reset beam time.
|
|
|
|
m_flBeamTime = 0.0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
pev->nextthink = gpGlobals->time + 0.01f;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (m_iLevel)
|
|
|
|
{
|
|
|
|
case PITWORM_LEVEL0: pev->view_ofs = PITWORM_EYE_OFFSET; break;
|
|
|
|
case PITWORM_LEVEL1: pev->view_ofs = PITWORM_EYE_OFFSET; break;
|
|
|
|
case PITWORM_LEVEL2: pev->view_ofs = PITWORM_EYE_OFFSET; break;
|
|
|
|
case PITWORM_LEVEL3: pev->view_ofs = PITWORM_EYE_OFFSET; break;
|
|
|
|
}
|
|
|
|
{
|
|
|
|
float currentZ = pev->origin.z;
|
|
|
|
float desiredZ = m_posDesired.z - m_flAdj;
|
|
|
|
|
|
|
|
if (currentZ > desiredZ)
|
|
|
|
{
|
|
|
|
pev->origin.z -= 8;
|
|
|
|
|
|
|
|
if (pev->origin.z < desiredZ)
|
|
|
|
pev->origin.z = desiredZ;
|
|
|
|
}
|
|
|
|
else if (currentZ < desiredZ)
|
|
|
|
{
|
|
|
|
pev->origin.z += 8;
|
|
|
|
|
|
|
|
if (pev->origin.z > desiredZ)
|
|
|
|
pev->origin.z = desiredZ;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// if dead, force cancelation of current animation
|
|
|
|
if (pev->health <= 0)
|
|
|
|
{
|
|
|
|
SetThink(&CPitWorm::DyingThink);
|
|
|
|
m_fSequenceFinished = TRUE;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// get new sequence
|
|
|
|
if (m_fSequenceFinished)
|
|
|
|
{
|
|
|
|
pev->frame = 0;
|
|
|
|
NextActivity();
|
|
|
|
ResetSequenceInfo();
|
|
|
|
}
|
|
|
|
|
|
|
|
// look for current enemy
|
|
|
|
if (m_hEnemy != 0)
|
|
|
|
{
|
|
|
|
// Update level based on enemy's altitude (z).
|
|
|
|
m_iLevel = Level(m_hEnemy->pev->origin.z);
|
|
|
|
|
|
|
|
if (FVisible(m_hEnemy))
|
|
|
|
{
|
|
|
|
if (m_flLastSeen < gpGlobals->time - 5)
|
|
|
|
m_flPrevSeen = gpGlobals->time;
|
|
|
|
|
|
|
|
m_flLastSeen = gpGlobals->time;
|
|
|
|
m_posTarget = m_hEnemy->pev->origin;
|
|
|
|
m_vecTarget = (m_posTarget - pev->origin).Normalize();
|
|
|
|
m_vecDesired = m_vecTarget;
|
|
|
|
m_posDesired = IdealPosition(m_iLevel);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_flNextIdleSoundTime < gpGlobals->time)
|
|
|
|
{
|
|
|
|
//IdleSound();
|
|
|
|
// Use flinch sounds.
|
|
|
|
FlinchSound();
|
|
|
|
m_flNextIdleSoundTime = gpGlobals->time + RANDOM_LONG(4.0f, 4.1f);
|
|
|
|
}
|
|
|
|
|
|
|
|
// don't go too high
|
|
|
|
if (m_posDesired.z > m_flMaxZ)
|
|
|
|
m_posDesired.z = m_flMaxZ;
|
|
|
|
|
|
|
|
// don't go too low
|
|
|
|
if (m_posDesired.z < m_flMinZ)
|
|
|
|
m_posDesired.z = m_flMinZ;
|
|
|
|
|
|
|
|
if(RANDOM_LONG(0, 20) == 5 && gpGlobals->time - m_flNextBlink >= RANDOM_FLOAT(3,5))
|
|
|
|
{
|
|
|
|
m_fBlink = TRUE;
|
|
|
|
m_flNextBlink = gpGlobals->time;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(gpGlobals->time - m_flNextBlink >= 0.05 && m_fBlink && !m_fBeamOn)
|
|
|
|
{
|
|
|
|
if(pev->skin < 3)
|
|
|
|
{
|
|
|
|
pev->skin++;
|
|
|
|
m_flNextBlink = gpGlobals->time;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pev->skin = 0;
|
|
|
|
m_fBlink = FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
//
|
|
|
|
//=========================================================
|
|
|
|
void CPitWorm::WormTouch(CBaseEntity* pOther)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
//
|
|
|
|
//=========================================================
|
|
|
|
int CPitWorm::CanPlaySequence(BOOL fDisregardState)
|
|
|
|
{
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// NextActivity
|
|
|
|
//=========================================================
|
|
|
|
void CPitWorm::NextActivity()
|
|
|
|
{
|
|
|
|
if(!m_fActivated)
|
|
|
|
return;
|
|
|
|
|
|
|
|
UTIL_MakeAimVectors(pev->angles);
|
|
|
|
|
|
|
|
Vector forward = gpGlobals->v_forward;
|
|
|
|
forward.z = 0;
|
|
|
|
|
|
|
|
float flDist = (m_vecDesired - m_vecLevels[m_iLevel]).Length();
|
|
|
|
float flDot = DotProduct(m_vecDesired, forward);
|
|
|
|
float flDotSpawnAngles = DotProduct(m_vecDesired, m_spawnAngles);
|
|
|
|
|
|
|
|
if (m_hEnemy == 0)
|
|
|
|
{
|
|
|
|
m_flLastSeen = gpGlobals->time;
|
|
|
|
m_hEnemy = UTIL_PlayerByIndex(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_hEnemy == 0)
|
|
|
|
{
|
|
|
|
Look(4096);
|
|
|
|
m_hEnemy = BestVisibleEnemy();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_hEnemy != 0)
|
|
|
|
{
|
|
|
|
|
|
|
|
if(gpGlobals->time - m_slowTime <= 3.5)
|
|
|
|
return;
|
|
|
|
|
|
|
|
m_vecDesired = m_hEnemy->pev->origin;
|
|
|
|
flDist = (m_vecDesired - m_vecLevels[m_iLevel]).Length();
|
|
|
|
flDot = DotProduct(m_vecDesired, forward);
|
|
|
|
flDotSpawnAngles = DotProduct(m_vecDesired, Vector(0, -1, 0));
|
|
|
|
|
|
|
|
if (m_flLastSeen + 5 > gpGlobals->time && (m_flNextAttackTime < gpGlobals->time))
|
|
|
|
{
|
|
|
|
Vector enemyDir = m_hEnemy->pev->origin - pev->origin;
|
|
|
|
Vector spawnAngles = m_spawnAngles;
|
|
|
|
Vector up = CrossProduct(enemyDir, spawnAngles);
|
|
|
|
float dist = UTIL_AngleDistance(m_hEnemy->pev->origin.y, pev->origin.y);
|
|
|
|
|
|
|
|
if (m_iLevel < PITWORM_LEVEL3)
|
|
|
|
{
|
|
|
|
if (FVisible(m_hEnemy) && flDot > 0.95f || dist <= -140 && dist >= -326)
|
|
|
|
{
|
|
|
|
if (flDist > PITWORM_EYEBLAST_ATTACK_DIST)
|
|
|
|
{
|
|
|
|
pev->sequence = LookupSequence("eyeblast");
|
|
|
|
BeamSound();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pev->sequence = LookupSequence("attacklow");
|
|
|
|
SwipeSound();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (up.z < 0)
|
|
|
|
{
|
|
|
|
pev->sequence = LookupSequence("doorclaw2");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (m_iLevel < PITWORM_LEVEL2)
|
|
|
|
{
|
|
|
|
pev->sequence = LookupSequence("doorclaw3");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pev->sequence = LookupSequence("doorclaw1");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (flDist > PITWORM_EYEBLAST_ATTACK_DIST)
|
|
|
|
{
|
|
|
|
pev->sequence = LookupSequence("eyeblast");
|
|
|
|
BeamSound();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (up.z < 0)
|
|
|
|
{
|
|
|
|
pev->sequence = LookupSequence("platclaw1");
|
|
|
|
|
|
|
|
if (RANDOM_LONG(0, 1) == 1) SwipeSound();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pev->sequence = LookupSequence("platclaw2");
|
|
|
|
if (RANDOM_LONG(0, 1) == 1) SwipeSound();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m_flNextAttackTime = gpGlobals->time + RANDOM_FLOAT(3, 3.1f);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_hEnemy)
|
|
|
|
{
|
|
|
|
pev->sequence = LookupSequence("idle2");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
FloatSequence();
|
|
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// FloatSequence
|
|
|
|
//=========================================================
|
|
|
|
void CPitWorm::FloatSequence(void)
|
|
|
|
{
|
|
|
|
switch (RANDOM_LONG(0, 1))
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
pev->sequence = LookupSequence("idle");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 1:
|
|
|
|
pev->sequence = LookupSequence("idle2");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// IdealPosition
|
|
|
|
//=========================================================
|
|
|
|
int CPitWorm::Level(float dz)
|
|
|
|
{
|
|
|
|
if (dz < PITWORM_LEVEL1_HEIGHT)
|
|
|
|
return PITWORM_LEVEL0;
|
|
|
|
|
|
|
|
if (dz < PITWORM_LEVEL2_HEIGHT)
|
|
|
|
return PITWORM_LEVEL1;
|
|
|
|
|
|
|
|
if (dz < PITWORM_LEVEL3_HEIGHT)
|
|
|
|
return PITWORM_LEVEL2;
|
|
|
|
|
|
|
|
return PITWORM_LEVEL3;
|
|
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// IdealPosition
|
|
|
|
//=========================================================
|
|
|
|
const Vector& CPitWorm::IdealPosition(const int level) const
|
|
|
|
{
|
|
|
|
switch (level)
|
|
|
|
{
|
|
|
|
case PITWORM_LEVEL0:
|
|
|
|
return m_vecLevels[PITWORM_LEVEL0];
|
|
|
|
case PITWORM_LEVEL1:
|
|
|
|
return m_vecLevels[PITWORM_LEVEL1];
|
|
|
|
case PITWORM_LEVEL2:
|
|
|
|
return m_vecLevels[PITWORM_LEVEL2];
|
|
|
|
case PITWORM_LEVEL3:
|
|
|
|
return m_vecLevels[PITWORM_LEVEL3];
|
|
|
|
}
|
|
|
|
|
|
|
|
return m_vecLevels[PITWORM_LEVEL3];
|
|
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// IdealPosition
|
|
|
|
//=========================================================
|
|
|
|
const Vector& CPitWorm::IdealPosition(const float dz) const
|
|
|
|
{
|
|
|
|
if (dz < PITWORM_LEVEL1_HEIGHT)
|
|
|
|
return m_vecLevels[PITWORM_LEVEL0];
|
|
|
|
|
|
|
|
if (dz < PITWORM_LEVEL2_HEIGHT)
|
|
|
|
return m_vecLevels[PITWORM_LEVEL1];
|
|
|
|
|
|
|
|
if (dz < PITWORM_LEVEL3_HEIGHT)
|
|
|
|
return m_vecLevels[PITWORM_LEVEL2];
|
|
|
|
|
|
|
|
return m_vecLevels[PITWORM_LEVEL3];
|
|
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// IdealPosition
|
|
|
|
//=========================================================
|
|
|
|
const Vector& CPitWorm::IdealPosition(CBaseEntity* pEnemy) const
|
|
|
|
{
|
|
|
|
return IdealPosition(pEnemy->pev->origin.z);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define clamp( val, min, max ) ( ((val) > (max)) ? (max) : ( ((val) < (min)) ? (min) : (val) ) )
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// UpdateBodyControllers
|
|
|
|
//=========================================================
|
|
|
|
void CPitWorm::UpdateBodyControllers(void)
|
|
|
|
{
|
|
|
|
if (m_hEnemy == 0 || m_fBeamOn)
|
|
|
|
return;
|
|
|
|
|
|
|
|
ASSERT(m_hEnemy);
|
|
|
|
|
|
|
|
Vector vecGoal = m_hEnemy->EyePosition();
|
|
|
|
Vector vecEye = EyePosition();
|
|
|
|
|
|
|
|
Vector target = (vecGoal - vecEye).Normalize();
|
|
|
|
Vector angles = UTIL_VecToAngles(target);
|
|
|
|
float yaw = UTIL_VecToYaw(target);
|
|
|
|
float pitch = angles.x;
|
|
|
|
float targetYaw = yaw - m_flInitialYaw;
|
|
|
|
|
|
|
|
|
|
|
|
if (pitch < -180)
|
|
|
|
pitch += 360;
|
|
|
|
|
|
|
|
if (pitch > 180)
|
|
|
|
pitch -= 360;
|
|
|
|
|
|
|
|
float bodyYaw = clamp(targetYaw, PITWORM_BODY_YAW_MIN, PITWORM_BODY_YAW_MAX);
|
|
|
|
|
|
|
|
|
|
|
|
float targetBody = (fabs(targetYaw) * (bodyYaw - 5)) / 90;
|
|
|
|
|
|
|
|
// Convert value to clamp it between min / max value of bone controller.
|
|
|
|
float eyePitch = pitch;
|
|
|
|
float eyeYaw = (targetBody * PITWORM_EYE_YAW_MAX) / PITWORM_BODY_YAW_MAX;
|
|
|
|
|
|
|
|
|
|
|
|
// Clamp those values (ensure they stay between min / max).
|
|
|
|
eyePitch = clamp(eyePitch, PITWORM_EYE_PITCH_MIN, PITWORM_EYE_PITCH_MAX);
|
|
|
|
eyeYaw = clamp(eyeYaw, PITWORM_EYE_YAW_MIN, PITWORM_EYE_YAW_MAX);
|
|
|
|
|
|
|
|
// Update body yaw controller value.
|
|
|
|
SetBoneController(PITWORM_CONTROLLER_BODY_YAW, bodyYaw);
|
|
|
|
|
|
|
|
// Update eye pitch controller value.
|
|
|
|
SetBoneController(PITWORM_CONTROLLER_EYE_PITCH, -eyePitch);
|
|
|
|
|
|
|
|
// Update eye yaw contoller value.
|
|
|
|
SetBoneController(PITWORM_CONTROLLER_EYE_YAW, eyeYaw);
|
|
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// CreateBeam
|
|
|
|
//=========================================================
|
|
|
|
void CPitWorm::CreateBeam(const Vector& src, const Vector& target, int width)
|
|
|
|
{
|
|
|
|
pev->skin = 0;
|
|
|
|
|
|
|
|
m_pBeam = CBeam::BeamCreate("sprites/gworm_beam_02.spr", width);
|
|
|
|
if (m_pBeam)
|
|
|
|
{
|
|
|
|
m_pBeam->PointsInit(src, target);
|
|
|
|
m_pBeam->SetBrightness(200);
|
|
|
|
m_pBeam->SetColor(0, 255, 0);
|
|
|
|
m_pBeam->SetNoise(0);
|
|
|
|
m_pBeam->SetFrame(0);
|
|
|
|
m_pBeam->SetScrollRate(10);
|
|
|
|
m_pBeam->RelinkBeam();
|
|
|
|
|
|
|
|
m_fBeamOn = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// DestroyBeam
|
|
|
|
//=========================================================
|
|
|
|
void CPitWorm::DestroyBeam(void)
|
|
|
|
{
|
|
|
|
if (m_pBeam)
|
|
|
|
{
|
|
|
|
UTIL_Remove(m_pBeam);
|
|
|
|
m_pBeam = NULL;
|
|
|
|
|
|
|
|
m_fBeamOn = FALSE;
|
|
|
|
m_vecEmemyPos = g_vecZero;
|
|
|
|
m_flPlayerDamage = 0;
|
|
|
|
m_iLaserHitTimes = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// UpdateBeam
|
|
|
|
//=========================================================
|
|
|
|
void CPitWorm::UpdateBeam(const Vector& src, const Vector& target)
|
|
|
|
{
|
|
|
|
if (m_pBeam)
|
|
|
|
{
|
|
|
|
m_pBeam->SetStartPos(src);
|
|
|
|
m_pBeam->SetEndPos(target);
|
|
|
|
m_pBeam->SetColor(0, 255, 0);
|
|
|
|
m_pBeam->SetBrightness(200);
|
|
|
|
m_pBeam->RelinkBeam();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// SetupBeamPoints
|
|
|
|
//=========================================================
|
|
|
|
void CPitWorm::SetupBeamPoints(CBaseEntity* pEnemy, Vector* vecleft, Vector* vecRight)
|
|
|
|
{
|
|
|
|
Vector forward, right, up;
|
|
|
|
|
|
|
|
if(m_vecEmemyPos == g_vecZero)
|
|
|
|
m_vecEmemyPos = pEnemy->Center() + Vector(-10,0,25);
|
|
|
|
|
|
|
|
if (pEnemy->IsPlayer())
|
|
|
|
UTIL_MakeAimVectors(pEnemy->pev->v_angle);
|
|
|
|
else
|
|
|
|
UTIL_MakeAimVectors(pEnemy->pev->angles);
|
|
|
|
|
|
|
|
forward = gpGlobals->v_forward;
|
|
|
|
right = gpGlobals->v_right;
|
|
|
|
up = gpGlobals->v_up;
|
|
|
|
|
|
|
|
*vecleft = m_vecEmemyPos + (right * -16);
|
|
|
|
*vecRight = m_vecEmemyPos + (right * 32);
|
|
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// UpdateBeamPoints
|
|
|
|
//=========================================================
|
|
|
|
void CPitWorm::UpdateBeamPoints(CBaseEntity* pEnemy, Vector* target)
|
|
|
|
{
|
|
|
|
Vector left, right;
|
|
|
|
Vector dir;
|
|
|
|
float delta;
|
|
|
|
|
|
|
|
delta = 1.0f / (PITWORM_EYEBLAST_DURATION * 2); // 0.24 approximatly. (248 miliseconds)
|
|
|
|
|
|
|
|
// Update beam yaw.
|
|
|
|
m_flBeamYaw += delta;
|
|
|
|
|
|
|
|
// Convert to distance to make the correct interpolation.
|
|
|
|
float flDist = (6.25 * m_flBeamYaw) / PITWORM_EYEBLAST_DURATION;
|
|
|
|
|
|
|
|
SetupBeamPoints(pEnemy, &left, &right);
|
|
|
|
|
|
|
|
// Direction from right to left.
|
|
|
|
dir = (left - right).Normalize();
|
|
|
|
|
|
|
|
// This is the result representing the next beam target
|
|
|
|
// position along the player's left and right computed beam
|
|
|
|
// positions.
|
|
|
|
*target = right + (dir * (flDist*1.2));
|
|
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// CreateGlow
|
|
|
|
//=========================================================
|
|
|
|
void CPitWorm::CreateGlow(void)
|
|
|
|
{
|
|
|
|
m_pEyeGlow = CSprite::SpriteCreate("sprites/glow_grn.spr", pev->origin, TRUE);
|
|
|
|
m_pEyeGlow->SetTransparency(kRenderGlow, 255, 255, 255, 0, kRenderFxNoDissipation);
|
|
|
|
m_pEyeGlow->SetAttachment(edict(), 1);
|
|
|
|
m_pEyeGlow->SetScale(2.0f);
|
|
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// DestroyGlow
|
|
|
|
//=========================================================
|
|
|
|
void CPitWorm::DestroyGlow(void)
|
|
|
|
{
|
|
|
|
if (m_pEyeGlow)
|
|
|
|
{
|
|
|
|
UTIL_Remove(m_pEyeGlow);
|
|
|
|
m_pEyeGlow = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// EyeOn
|
|
|
|
//=========================================================
|
|
|
|
void CPitWorm::EyeOn(int level)
|
|
|
|
{
|
|
|
|
m_eyeBrightness = level;
|
|
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// EyeOff
|
|
|
|
//=========================================================
|
|
|
|
void CPitWorm::EyeOff(void)
|
|
|
|
{
|
|
|
|
m_eyeBrightness = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// EyeUpdate
|
|
|
|
//=========================================================
|
|
|
|
void CPitWorm::EyeUpdate(void)
|
|
|
|
{
|
|
|
|
if (m_pEyeGlow)
|
|
|
|
{
|
|
|
|
m_pEyeGlow->pev->renderamt = UTIL_Approach(m_eyeBrightness, m_pEyeGlow->pev->renderamt, 100);
|
|
|
|
if (m_pEyeGlow->pev->renderamt == 0)
|
|
|
|
m_pEyeGlow->pev->effects |= EF_NODRAW;
|
|
|
|
else
|
|
|
|
m_pEyeGlow->pev->effects &= ~EF_NODRAW;
|
|
|
|
|
|
|
|
UTIL_SetOrigin(m_pEyeGlow->pev, pev->origin);
|
|
|
|
}
|
|
|
|
}
|