|
|
|
/***
|
|
|
|
*
|
|
|
|
* SPIRIT OF HALF-LIFE 1.9: OPPOSING-FORCE EDITION
|
|
|
|
*
|
|
|
|
* Spirit of Half-Life and their logos are the property of their respective owners.
|
|
|
|
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
|
|
|
|
*
|
|
|
|
* This product contains software technology licensed from Id
|
|
|
|
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
|
|
|
*
|
|
|
|
* Use, distribution, and modification of this source code and/or resulting
|
|
|
|
* object code is restricted to non-commercial enhancements to products from
|
|
|
|
* Valve LLC. All other use, distribution, or modification is prohibited
|
|
|
|
* without written permission from Valve LLC.
|
|
|
|
*
|
|
|
|
* All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Base Source-Code written by Marc-Antoine Lortie (https://github.com/malortie).
|
|
|
|
* Modifications by Hammermaps.de DEV Team (support@hammermaps.de).
|
|
|
|
*
|
|
|
|
***/
|
|
|
|
|
|
|
|
#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"
|
|
|
|
#include "decals.h"
|
|
|
|
|
|
|
|
#define PITWORM_ATTN 0.1
|
|
|
|
#define NUM_PITWORM_LEVELS 4
|
|
|
|
|
|
|
|
class CPitWorm : public CBaseMonster
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
void Spawn(void);
|
|
|
|
void Precache(void);
|
|
|
|
int Classify(void) { return CLASS_ALIEN_MONSTER; }
|
|
|
|
virtual int ObjectCaps(void) { return CBaseMonster::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
|
|
|
|
void SetObjectCollisionBox()
|
|
|
|
{
|
|
|
|
pev->absmin = pev->origin + Vector( -400, -400, 0 );
|
|
|
|
pev->absmax = pev->origin + Vector( 400, 400, 850 );
|
|
|
|
}
|
|
|
|
BOOL FVisible(CBaseEntity* pEntity);
|
|
|
|
BOOL FVisible(const Vector& vecOrigin);
|
|
|
|
|
|
|
|
void IdleSound(void);
|
|
|
|
void AlertSound(void);
|
|
|
|
void DeathSound(void);
|
|
|
|
void PainSound(void);
|
|
|
|
|
|
|
|
int Save(CSave &save);
|
|
|
|
int Restore(CRestore &restore);
|
|
|
|
static TYPEDESCRIPTION m_SaveData[];
|
|
|
|
|
|
|
|
void AngrySound(void);
|
|
|
|
void SwipeSound(void);
|
|
|
|
void BeamSound(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);
|
|
|
|
|
|
|
|
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 HitTouch(CBaseEntity* pOther);
|
|
|
|
|
|
|
|
void LockTopLevel();
|
|
|
|
BOOL ClawAttack();
|
|
|
|
void ShootBeam();
|
|
|
|
void StrafeBeam();
|
|
|
|
void ChangeLevel();
|
|
|
|
void TrackEnemy();
|
|
|
|
|
|
|
|
void NextActivity(void);
|
|
|
|
|
|
|
|
void EyeLight(const Vector& vecEyePos);
|
|
|
|
void BeamEffect(TraceResult& tr);
|
|
|
|
|
|
|
|
int SizeForGrapple() { return GRAPPLE_LARGE; }
|
|
|
|
|
|
|
|
//
|
|
|
|
float m_flNextPainSound;
|
|
|
|
|
|
|
|
Vector m_vecTarget;
|
|
|
|
Vector m_posTarget;
|
|
|
|
Vector m_vecDesired;
|
|
|
|
Vector m_posDesired;
|
|
|
|
|
|
|
|
float m_offsetBeam;
|
|
|
|
Vector m_posBeam;
|
|
|
|
Vector m_vecBeam;
|
|
|
|
Vector m_angleBeam;
|
|
|
|
float m_flBeamExpireTime;
|
|
|
|
float m_flBeamDir;
|
|
|
|
|
|
|
|
float m_flTorsoYaw;
|
|
|
|
float m_flHeadYaw;
|
|
|
|
float m_flHeadPitch;
|
|
|
|
float m_flIdealTorsoYaw;
|
|
|
|
float m_flIdealHeadYaw;
|
|
|
|
float m_flIdealHeadPitch;
|
|
|
|
|
|
|
|
float m_flLevels[NUM_PITWORM_LEVELS];
|
|
|
|
float m_flTargetLevels[NUM_PITWORM_LEVELS];
|
|
|
|
|
|
|
|
float m_flLastSeen;
|
|
|
|
|
|
|
|
int m_iLevel;
|
|
|
|
float m_flLevelSpeed;
|
|
|
|
|
|
|
|
CBeam* m_pBeam;
|
|
|
|
CSprite* m_pSprite;
|
|
|
|
|
|
|
|
BOOL m_fAttacking;
|
|
|
|
BOOL m_fLockHeight;
|
|
|
|
BOOL m_fLockYaw;
|
|
|
|
|
|
|
|
int m_iWasHit;
|
|
|
|
float m_flTakeHitTime;
|
|
|
|
|
|
|
|
float m_flHitTime;
|
|
|
|
float m_flNextMeleeTime;
|
|
|
|
float m_flNextRangeTime;
|
|
|
|
float m_flDeathStartTime;
|
|
|
|
|
|
|
|
BOOL m_fFirstSighting;
|
|
|
|
BOOL m_fTopLevelLocked;
|
|
|
|
|
|
|
|
float m_flLastBlinkTime;
|
|
|
|
float m_flLastBlinkInterval;
|
|
|
|
float m_flLastEventTime;
|
|
|
|
|
|
|
|
static const char* pHitGroundSounds[];
|
|
|
|
static const char* pAngrySounds[];
|
|
|
|
static const char* pSwipeSounds[];
|
|
|
|
static const char* pShootSounds[];
|
|
|
|
|
|
|
|
static const char* pPainSounds[];
|
|
|
|
static const char* pAlertSounds[];
|
|
|
|
static const char* pIdleSounds[];
|
|
|
|
static const char* pDeathSounds[];
|
|
|
|
static const char* pAttackSounds[];
|
|
|
|
};
|
|
|
|
|
|
|
|
LINK_ENTITY_TO_CLASS(monster_pitworm, CPitWorm)
|
|
|
|
LINK_ENTITY_TO_CLASS(monster_pitworm_up, CPitWorm)
|
|
|
|
|
|
|
|
#define PITWORM_EYE_OFFSET Vector(0, 0, 300)
|
|
|
|
|
|
|
|
#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
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// 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_flNextPainSound, FIELD_TIME),
|
|
|
|
|
|
|
|
DEFINE_FIELD(CPitWorm, m_vecTarget, FIELD_POSITION_VECTOR),
|
|
|
|
DEFINE_FIELD(CPitWorm, m_posTarget, FIELD_POSITION_VECTOR),
|
|
|
|
DEFINE_FIELD(CPitWorm, m_vecDesired, FIELD_POSITION_VECTOR),
|
|
|
|
DEFINE_FIELD(CPitWorm, m_posDesired, FIELD_POSITION_VECTOR),
|
|
|
|
|
|
|
|
DEFINE_FIELD(CPitWorm, m_offsetBeam, FIELD_FLOAT),
|
|
|
|
DEFINE_FIELD(CPitWorm, m_posBeam, FIELD_POSITION_VECTOR),
|
|
|
|
DEFINE_FIELD(CPitWorm, m_vecBeam, FIELD_POSITION_VECTOR),
|
|
|
|
DEFINE_FIELD(CPitWorm, m_angleBeam, FIELD_POSITION_VECTOR),
|
|
|
|
DEFINE_FIELD(CPitWorm, m_flBeamExpireTime, FIELD_TIME),
|
|
|
|
DEFINE_FIELD(CPitWorm, m_flBeamDir, FIELD_FLOAT),
|
|
|
|
|
|
|
|
DEFINE_ARRAY(CPitWorm, m_flLevels, FIELD_FLOAT, NUM_PITWORM_LEVELS),
|
|
|
|
DEFINE_ARRAY(CPitWorm, m_flTargetLevels, FIELD_FLOAT, NUM_PITWORM_LEVELS),
|
|
|
|
|
|
|
|
DEFINE_FIELD(CPitWorm, m_flLastSeen, FIELD_TIME),
|
|
|
|
|
|
|
|
DEFINE_FIELD(CPitWorm, m_iLevel, FIELD_INTEGER),
|
|
|
|
DEFINE_FIELD(CPitWorm, m_flLevelSpeed, FIELD_FLOAT),
|
|
|
|
|
|
|
|
DEFINE_FIELD(CPitWorm, m_pBeam, FIELD_CLASSPTR),
|
|
|
|
DEFINE_FIELD(CPitWorm, m_pSprite, FIELD_CLASSPTR),
|
|
|
|
|
|
|
|
DEFINE_FIELD(CPitWorm, m_fAttacking, FIELD_BOOLEAN),
|
|
|
|
DEFINE_FIELD(CPitWorm, m_fLockHeight, FIELD_BOOLEAN),
|
|
|
|
DEFINE_FIELD(CPitWorm, m_fLockYaw, FIELD_BOOLEAN),
|
|
|
|
|
|
|
|
DEFINE_FIELD(CPitWorm, m_iWasHit, FIELD_INTEGER),
|
|
|
|
DEFINE_FIELD(CPitWorm, m_flTakeHitTime, FIELD_TIME),
|
|
|
|
|
|
|
|
DEFINE_FIELD(CPitWorm, m_flHitTime, FIELD_TIME),
|
|
|
|
DEFINE_FIELD(CPitWorm, m_flNextMeleeTime, FIELD_TIME),
|
|
|
|
DEFINE_FIELD(CPitWorm, m_flNextRangeTime, FIELD_TIME),
|
|
|
|
DEFINE_FIELD(CPitWorm, m_flDeathStartTime, FIELD_TIME),
|
|
|
|
|
|
|
|
DEFINE_FIELD(CPitWorm, m_fFirstSighting, FIELD_BOOLEAN),
|
|
|
|
DEFINE_FIELD(CPitWorm, m_fTopLevelLocked, FIELD_BOOLEAN),
|
|
|
|
|
|
|
|
DEFINE_FIELD(CPitWorm, m_flLastBlinkTime, FIELD_TIME),
|
|
|
|
DEFINE_FIELD(CPitWorm, m_flLastBlinkInterval, FIELD_FLOAT),
|
|
|
|
DEFINE_FIELD(CPitWorm, m_flLastEventTime, FIELD_FLOAT),
|
|
|
|
};
|
|
|
|
|
|
|
|
IMPLEMENT_SAVERESTORE(CPitWorm, CBaseMonster)
|
|
|
|
|
|
|
|
const char* CPitWorm::pHitGroundSounds[] =
|
|
|
|
{
|
|
|
|
"tentacle/te_strike1.wav",
|
|
|
|
"tentacle/te_strike2.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::pShootSounds[] =
|
|
|
|
{
|
|
|
|
"debris/beamstart3.wav",
|
|
|
|
"debris/beamstart8.wav",
|
|
|
|
};
|
|
|
|
|
|
|
|
const char* CPitWorm::pPainSounds[] =
|
|
|
|
{
|
|
|
|
"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",
|
|
|
|
};
|
|
|
|
|
|
|
|
const char *CPitWorm::pAttackSounds[] =
|
|
|
|
{
|
|
|
|
"zombie/claw_strike1.wav",
|
|
|
|
"zombie/claw_strike2.wav",
|
|
|
|
"zombie/claw_strike3.wav"
|
|
|
|
};
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// Spawn
|
|
|
|
//=========================================================
|
|
|
|
void CPitWorm::Spawn()
|
|
|
|
{
|
|
|
|
Precache();
|
|
|
|
SET_MODEL(ENT(pev), "models/pit_worm_up.mdl");
|
|
|
|
|
|
|
|
pev->movetype = MOVETYPE_FLY;
|
|
|
|
pev->solid = SOLID_BBOX;
|
|
|
|
|
|
|
|
UTIL_SetSize(pev, Vector(-32, -32, 0), Vector(32, 32, 64));
|
|
|
|
UTIL_SetOrigin(pev, pev->origin);
|
|
|
|
|
|
|
|
pev->flags |= FL_MONSTER|FL_FLY;
|
|
|
|
pev->takedamage = DAMAGE_AIM;
|
|
|
|
|
|
|
|
pev->health = gSkillData.pwormHealth;
|
|
|
|
pev->max_health = pev->health;
|
|
|
|
|
|
|
|
pev->view_ofs = PITWORM_EYE_OFFSET;
|
|
|
|
m_bloodColor = BLOOD_COLOR_GREEN;
|
|
|
|
m_flFieldOfView = 0.5;
|
|
|
|
|
|
|
|
pev->sequence = 0;
|
|
|
|
ResetSequenceInfo();
|
|
|
|
|
|
|
|
m_flTorsoYaw = 0;
|
|
|
|
m_flHeadYaw = 0;
|
|
|
|
m_flHeadPitch = 0;
|
|
|
|
m_flIdealTorsoYaw = 0;
|
|
|
|
m_flIdealHeadYaw = 0;
|
|
|
|
m_flIdealHeadPitch = 0;
|
|
|
|
|
|
|
|
InitBoneControllers();
|
|
|
|
|
|
|
|
SetThink(&CPitWorm::StartupThink);
|
|
|
|
SetTouch(&CPitWorm::HitTouch);
|
|
|
|
pev->nextthink = gpGlobals->time + 0.1;
|
|
|
|
|
|
|
|
m_vecDesired = Vector(1,0,0);
|
|
|
|
m_posDesired = pev->origin;
|
|
|
|
|
|
|
|
m_fAttacking = FALSE;
|
|
|
|
m_fLockHeight = FALSE;
|
|
|
|
m_fFirstSighting = FALSE;
|
|
|
|
m_flBeamExpireTime = gpGlobals->time;
|
|
|
|
m_iLevel = 0;
|
|
|
|
m_fLockYaw = FALSE;
|
|
|
|
|
|
|
|
m_iWasHit = 0;
|
|
|
|
m_flTakeHitTime = 0;
|
|
|
|
m_flHitTime = 0;
|
|
|
|
m_flLevelSpeed = 10;
|
|
|
|
|
|
|
|
m_fTopLevelLocked = FALSE;
|
|
|
|
m_flLastBlinkTime = gpGlobals->time;
|
|
|
|
m_flLastBlinkInterval = gpGlobals->time;
|
|
|
|
m_flLastEventTime = gpGlobals->time;
|
|
|
|
m_flTargetLevels[3] = pev->origin.z;
|
|
|
|
m_flLevels[3] = pev->origin.z - 350.0;
|
|
|
|
m_flTargetLevels[2] = pev->origin.z;
|
|
|
|
m_flLevels[2] = pev->origin.z - 300.0;
|
|
|
|
m_flTargetLevels[1] = pev->origin.z;
|
|
|
|
m_flLevels[1] = pev->origin.z - 300.0;
|
|
|
|
m_flTargetLevels[0] = pev->origin.z;
|
|
|
|
m_flLevels[0] = pev->origin.z - 300.0;;
|
|
|
|
m_pBeam = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// Precache - precaches all resources this monster needs
|
|
|
|
//=========================================================
|
|
|
|
void CPitWorm::Precache()
|
|
|
|
{
|
|
|
|
PRECACHE_MODEL("models/pit_worm_up.mdl");
|
|
|
|
|
|
|
|
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_ARRAY(pHitGroundSounds);
|
|
|
|
PRECACHE_SOUND_ARRAY(pAngrySounds);
|
|
|
|
PRECACHE_SOUND_ARRAY(pSwipeSounds);
|
|
|
|
PRECACHE_SOUND_ARRAY(pIdleSounds);
|
|
|
|
PRECACHE_SOUND_ARRAY(pPainSounds);
|
|
|
|
PRECACHE_SOUND_ARRAY(pAttackSounds);
|
|
|
|
|
|
|
|
PRECACHE_MODEL("sprites/laserbeam.spr");
|
|
|
|
PRECACHE_MODEL("sprites/tele1.spr");
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOL CPitWorm::FVisible(CBaseEntity *pEntity)
|
|
|
|
{
|
|
|
|
if( FBitSet( pEntity->pev->flags, FL_NOTARGET ) )
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if( ( pev->waterlevel != 3 && pEntity->pev->waterlevel == 3 )
|
|
|
|
|| ( pev->waterlevel == 3 && pEntity->pev->waterlevel == 0 ) )
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
TraceResult tr;
|
|
|
|
Vector vecLookerOrigin;
|
|
|
|
Vector vecLookerAngle;
|
|
|
|
|
|
|
|
GetAttachment(0, vecLookerOrigin, vecLookerAngle);
|
|
|
|
UTIL_TraceLine( vecLookerOrigin, pEntity->EyePosition(), ignore_monsters, ignore_glass, ENT( pev ), &tr );
|
|
|
|
|
|
|
|
return tr.flFraction == 1.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOL CPitWorm::FVisible(const Vector& vecOrigin)
|
|
|
|
{
|
|
|
|
TraceResult tr;
|
|
|
|
Vector vecLookerOrigin;
|
|
|
|
Vector vecLookerAngle;
|
|
|
|
|
|
|
|
GetAttachment(0, vecLookerOrigin, vecLookerAngle);
|
|
|
|
UTIL_TraceLine( vecLookerOrigin, vecOrigin, ignore_monsters, ignore_glass, ENT( pev ), &tr );
|
|
|
|
return tr.flFraction == 1.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// IdleSound
|
|
|
|
//=========================================================
|
|
|
|
void CPitWorm::IdleSound(void)
|
|
|
|
{
|
|
|
|
EMIT_SOUND(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pPainSounds), VOL_NORM, PITWORM_ATTN);
|
|
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// AlertSound
|
|
|
|
//=========================================================
|
|
|
|
void CPitWorm::AlertSound(void)
|
|
|
|
{
|
|
|
|
EMIT_SOUND(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pAlertSounds), VOL_NORM, PITWORM_ATTN);
|
|
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// DeathSound
|
|
|
|
//=========================================================
|
|
|
|
void CPitWorm::DeathSound(void)
|
|
|
|
{
|
|
|
|
EMIT_SOUND(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pDeathSounds), VOL_NORM, PITWORM_ATTN);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPitWorm::PainSound(void)
|
|
|
|
{
|
|
|
|
if (m_flNextPainSound <= gpGlobals->time)
|
|
|
|
{
|
|
|
|
m_flNextPainSound = gpGlobals->time + RANDOM_LONG(2, 5);
|
|
|
|
EMIT_SOUND(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pPainSounds), VOL_NORM, PITWORM_ATTN);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// AngrySound
|
|
|
|
//=========================================================
|
|
|
|
void CPitWorm::AngrySound(void)
|
|
|
|
{
|
|
|
|
EMIT_SOUND(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pAngrySounds), VOL_NORM, PITWORM_ATTN);
|
|
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// SwipeSound
|
|
|
|
//=========================================================
|
|
|
|
void CPitWorm::SwipeSound(void)
|
|
|
|
{
|
|
|
|
EMIT_SOUND(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pSwipeSounds), VOL_NORM, PITWORM_ATTN);
|
|
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// BeamSound
|
|
|
|
//=========================================================
|
|
|
|
void CPitWorm::BeamSound(void)
|
|
|
|
{
|
|
|
|
EMIT_SOUND(ENT(pev), CHAN_VOICE, "pitworm/pit_worm_attack_eyeblast.wav", VOL_NORM, PITWORM_ATTN);
|
|
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// HandleAnimEvent
|
|
|
|
//=========================================================
|
|
|
|
void CPitWorm::HandleAnimEvent(MonsterEvent_t *pEvent)
|
|
|
|
{
|
|
|
|
switch (pEvent->event)
|
|
|
|
{
|
|
|
|
case PITWORM_AE_SWIPE: // bang
|
|
|
|
{
|
|
|
|
EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pHitGroundSounds), VOL_NORM, ATTN_NORM, 0, 100 + RANDOM_FLOAT(-5,5));
|
|
|
|
|
|
|
|
if (pev->sequence == 2)
|
|
|
|
UTIL_ScreenShake(pev->origin, 12.0, 100.0, 2.0, 100);
|
|
|
|
else
|
|
|
|
UTIL_ScreenShake(pev->origin, 4.0, 3.0, 1.0, 750.0);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case PITWORM_AE_EYEBLAST_START: // start killing swing
|
|
|
|
{
|
|
|
|
if ( gpGlobals->time - m_flLastEventTime >= 1.1 )
|
|
|
|
{
|
|
|
|
if (m_hEnemy)
|
|
|
|
{
|
|
|
|
m_posBeam = m_hEnemy->pev->origin;
|
|
|
|
m_posBeam.z += 24;
|
|
|
|
|
|
|
|
Vector vecEyePos, vecEyeAng;
|
|
|
|
GetAttachment(0, vecEyePos, vecEyeAng);
|
|
|
|
|
|
|
|
m_vecBeam = (m_posBeam - vecEyePos).Normalize();
|
|
|
|
m_angleBeam = UTIL_VecToAngles(m_vecBeam);
|
|
|
|
UTIL_MakeVectors(m_angleBeam);
|
|
|
|
ShootBeam();
|
|
|
|
m_fLockYaw = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case PITWORM_AE_EYEBLAST_END: // end killing swing
|
|
|
|
{
|
|
|
|
m_fLockYaw = TRUE;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
CBaseMonster::HandleAnimEvent(pEvent);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPitWorm::TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType )
|
|
|
|
{
|
|
|
|
if ( ptr->iHitgroup == HITGROUP_HEAD )
|
|
|
|
{
|
|
|
|
if (gpGlobals->time > m_flTakeHitTime )
|
|
|
|
{
|
|
|
|
pev->health -= flDamage;
|
|
|
|
if (pev->health <= 0)
|
|
|
|
{
|
|
|
|
pev->health = pev->max_health;
|
|
|
|
m_iWasHit = TRUE;
|
|
|
|
m_flTakeHitTime = m_flTakeHitTime + RANDOM_LONG(2,4);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
UTIL_BloodDrips(ptr->vecEndPos, vecDir, m_bloodColor, flDamage * 10);
|
|
|
|
UTIL_BloodDecalTrace(ptr, m_bloodColor);
|
|
|
|
|
|
|
|
if (m_hEnemy == 0)
|
|
|
|
{
|
|
|
|
m_hEnemy = Instance(pevAttacker);
|
|
|
|
}
|
|
|
|
if (pev->skin == 0)
|
|
|
|
{
|
|
|
|
pev->skin = 1;
|
|
|
|
m_flLastBlinkInterval = gpGlobals->time;
|
|
|
|
m_flLastBlinkTime = 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::CommandUse(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value)
|
|
|
|
{
|
|
|
|
switch (useType)
|
|
|
|
{
|
|
|
|
case USE_ON:
|
|
|
|
CSoundEnt::InsertSound(bits_SOUND_WORLD, pActivator->pev->origin, 1024, 1.0);
|
|
|
|
//ALERT(at_console, "USE_ON\n");
|
|
|
|
break;
|
|
|
|
case USE_OFF:
|
|
|
|
case USE_TOGGLE:
|
|
|
|
{
|
|
|
|
pev->takedamage = DAMAGE_NO;
|
|
|
|
pev->health = 0;
|
|
|
|
|
|
|
|
SetThink(&CPitWorm::DyingThink);
|
|
|
|
pev->nextthink = gpGlobals->time;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
//
|
|
|
|
//=========================================================
|
|
|
|
int CPitWorm::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
|
|
|
|
{
|
|
|
|
PainSound();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// StartupThink
|
|
|
|
//=========================================================
|
|
|
|
void CPitWorm::StartupThink(void)
|
|
|
|
{
|
|
|
|
CBaseEntity *pEntity = NULL;
|
|
|
|
|
|
|
|
pEntity = UTIL_FindEntityByTargetname(NULL, "pw_tleveldead");
|
|
|
|
if (pEntity)
|
|
|
|
{
|
|
|
|
ALERT(at_console, "level dead node set\n");
|
|
|
|
m_flTargetLevels[0] = pEntity->pev->origin.z;
|
|
|
|
m_flLevels[0] = m_flTargetLevels[0] - 300;
|
|
|
|
}
|
|
|
|
|
|
|
|
pEntity = UTIL_FindEntityByTargetname(NULL, "pw_tlevel1");
|
|
|
|
if (pEntity)
|
|
|
|
{
|
|
|
|
ALERT(at_console, "level 1 node set\n");
|
|
|
|
m_flTargetLevels[1] = pEntity->pev->origin.z;
|
|
|
|
m_flLevels[1] = m_flTargetLevels[1] - 300;
|
|
|
|
}
|
|
|
|
|
|
|
|
pEntity = UTIL_FindEntityByTargetname(NULL, "pw_tlevel2");
|
|
|
|
if (pEntity)
|
|
|
|
{
|
|
|
|
ALERT(at_console, "level 2 node set\n");
|
|
|
|
m_flTargetLevels[2] = pEntity->pev->origin.z;
|
|
|
|
m_flLevels[2] = m_flTargetLevels[2] - 300;
|
|
|
|
}
|
|
|
|
|
|
|
|
pEntity = UTIL_FindEntityByTargetname(NULL, "pw_tlevel3");
|
|
|
|
if (pEntity)
|
|
|
|
{
|
|
|
|
ALERT(at_console, "level 3 node set\n");
|
|
|
|
m_flTargetLevels[3] = pEntity->pev->origin.z;
|
|
|
|
m_flLevels[3] = m_flTargetLevels[3] - 350;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_iLevel = 2;
|
|
|
|
if (!FStringNull(pev->target))
|
|
|
|
{
|
|
|
|
if (FStrEq(STRING(pev->target), "pw_level1"))
|
|
|
|
m_iLevel = 1;
|
|
|
|
else if (FStrEq(STRING(pev->target), "pw_level2"))
|
|
|
|
m_iLevel = 2;
|
|
|
|
else if (FStrEq(STRING(pev->target), "pw_level3"))
|
|
|
|
m_iLevel = 3;
|
|
|
|
else if (FStrEq(STRING(pev->target), "pw_leveldead"))
|
|
|
|
m_iLevel = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_posDesired.z = m_flLevels[m_iLevel];
|
|
|
|
|
|
|
|
Vector vecEyePos, vecEyeAng;
|
|
|
|
GetAttachment(0, vecEyePos, vecEyeAng);
|
|
|
|
pev->view_ofs = vecEyePos - pev->origin;
|
|
|
|
|
|
|
|
SetThink(&CPitWorm::HuntThink);
|
|
|
|
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_DYING)
|
|
|
|
{
|
|
|
|
if ( gpGlobals->time - m_flDeathStartTime > 3.0 )
|
|
|
|
{
|
|
|
|
ChangeLevel();
|
|
|
|
}
|
|
|
|
if ( fabs(pev->origin.z - this->m_flLevels[0]) < 16.0 )
|
|
|
|
{
|
|
|
|
pev->velocity = Vector(0, 0, 0);
|
|
|
|
pev->deadflag = DEAD_DEAD;
|
|
|
|
SetThink(&CBaseEntity::SUB_Remove);
|
|
|
|
pev->nextthink = gpGlobals->time + 0.1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pev->deadflag = DEAD_DYING;
|
|
|
|
int deathAnim = LookupSequence("death");
|
|
|
|
int iDir = 1;
|
|
|
|
m_posDesired.z = m_flLevels[0];
|
|
|
|
pev->sequence = FindTransition(pev->sequence, deathAnim, &iDir);
|
|
|
|
if (iDir <= 0)
|
|
|
|
{
|
|
|
|
pev->frame = 255;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pev->frame = 0;
|
|
|
|
}
|
|
|
|
m_flLevelSpeed = 5;
|
|
|
|
ResetSequenceInfo();
|
|
|
|
DeathSound();
|
|
|
|
|
|
|
|
if (m_pBeam)
|
|
|
|
{
|
|
|
|
UTIL_Remove(m_pBeam);
|
|
|
|
m_pBeam = NULL;
|
|
|
|
}
|
|
|
|
if (m_pSprite)
|
|
|
|
{
|
|
|
|
UTIL_Remove(m_pSprite);
|
|
|
|
m_pSprite = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
SetUse(NULL);
|
|
|
|
SetTouch(NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// HuntThink
|
|
|
|
//=========================================================
|
|
|
|
void CPitWorm::HuntThink(void)
|
|
|
|
{
|
|
|
|
pev->nextthink = gpGlobals->time + 0.1;
|
|
|
|
GlowShellUpdate();
|
|
|
|
DispatchAnimEvents();
|
|
|
|
StudioFrameAdvance();
|
|
|
|
|
|
|
|
if (m_pBeam)
|
|
|
|
{
|
|
|
|
if (m_hEnemy != 0 && m_flBeamExpireTime > gpGlobals->time)
|
|
|
|
{
|
|
|
|
StrafeBeam();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
UTIL_Remove(m_pBeam);
|
|
|
|
m_pBeam = NULL;
|
|
|
|
UTIL_Remove(m_pSprite);
|
|
|
|
m_pSprite = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pev->health <= 0)
|
|
|
|
{
|
|
|
|
SetThink(&CPitWorm::DyingThink);
|
|
|
|
m_fSequenceFinished = TRUE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ( (gpGlobals->time - m_flLastBlinkTime) >= 6.0 && !m_pBeam )
|
|
|
|
{
|
|
|
|
pev->skin = 1;
|
|
|
|
m_flLastBlinkInterval = gpGlobals->time;
|
|
|
|
m_flLastBlinkTime = gpGlobals->time;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pev->skin)
|
|
|
|
{
|
|
|
|
if ( gpGlobals->time - m_flLastBlinkInterval >= 0.0 )
|
|
|
|
{
|
|
|
|
if ( pev->skin == 5 )
|
|
|
|
pev->skin = 0;
|
|
|
|
else
|
|
|
|
pev->skin++;
|
|
|
|
m_flLastBlinkInterval = gpGlobals->time;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_iWasHit)
|
|
|
|
{
|
|
|
|
int iDir = 1;
|
|
|
|
const char* flinchAnim = RANDOM_LONG(0,1) ? "flinch1" : "flinch2";
|
|
|
|
pev->sequence = FindTransition(pev->sequence, LookupSequence(flinchAnim), &iDir);
|
|
|
|
if (iDir > 0)
|
|
|
|
{
|
|
|
|
pev->frame = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pev->frame = 255;
|
|
|
|
}
|
|
|
|
ResetSequenceInfo();
|
|
|
|
m_iWasHit = FALSE;
|
|
|
|
PainSound();
|
|
|
|
}
|
|
|
|
else if (m_fSequenceFinished)
|
|
|
|
{
|
|
|
|
int oldSeq = pev->sequence;
|
|
|
|
if ( m_fAttacking )
|
|
|
|
{
|
|
|
|
m_fLockHeight = FALSE;
|
|
|
|
m_fLockYaw = FALSE;
|
|
|
|
m_fAttacking = FALSE;
|
|
|
|
m_flNextMeleeTime = gpGlobals->time + 0.25;
|
|
|
|
}
|
|
|
|
NextActivity();
|
|
|
|
if (pev->sequence != oldSeq || !m_fSequenceLoops)
|
|
|
|
{
|
|
|
|
pev->frame = 0;
|
|
|
|
ResetSequenceInfo();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_hEnemy != 0)
|
|
|
|
{
|
|
|
|
if (FVisible(m_hEnemy))
|
|
|
|
{
|
|
|
|
m_flLastSeen = gpGlobals->time;
|
|
|
|
m_posTarget = m_hEnemy->pev->origin;
|
|
|
|
m_posTarget.z += 24;
|
|
|
|
Vector vecEyePos, vecEyeAng;
|
|
|
|
GetAttachment(0, vecEyePos, vecEyeAng);
|
|
|
|
m_vecTarget = (m_posTarget - vecEyePos).Normalize();
|
|
|
|
m_vecDesired = m_vecTarget;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_posDesired.z > m_flLevels[3])
|
|
|
|
{
|
|
|
|
m_posDesired.z = m_flLevels[3];
|
|
|
|
}
|
|
|
|
else if (m_posDesired.z < m_flLevels[0])
|
|
|
|
{
|
|
|
|
m_posDesired.z = m_flLevels[0];
|
|
|
|
}
|
|
|
|
ChangeLevel();
|
|
|
|
if (m_hEnemy != 0 && !m_pBeam)
|
|
|
|
{
|
|
|
|
TrackEnemy();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
//
|
|
|
|
//=========================================================
|
|
|
|
void CPitWorm::HitTouch(CBaseEntity* pOther)
|
|
|
|
{
|
|
|
|
TraceResult tr = UTIL_GetGlobalTrace();
|
|
|
|
|
|
|
|
if (pOther->pev->modelindex == pev->modelindex)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (m_flHitTime > gpGlobals->time)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if( tr.pHit == NULL || tr.pHit->v.modelindex != pev->modelindex )
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (pOther->pev->takedamage)
|
|
|
|
{
|
|
|
|
pOther->TakeDamage(pev, pev, gSkillData.pwormDmgSwipe, DMG_CRUSH|DMG_SLASH);
|
|
|
|
pOther->pev->punchangle.z = 15;
|
|
|
|
pOther->pev->velocity.z += 200;
|
|
|
|
EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pAttackSounds), VOL_NORM, ATTN_NORM, 0, 100 + RANDOM_FLOAT(-5,5));
|
|
|
|
m_flHitTime = gpGlobals->time + 1.0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// NextActivity
|
|
|
|
//=========================================================
|
|
|
|
void CPitWorm::NextActivity()
|
|
|
|
{
|
|
|
|
UTIL_MakeAimVectors(pev->angles);
|
|
|
|
|
|
|
|
if (m_hEnemy)
|
|
|
|
{
|
|
|
|
if (!m_hEnemy->IsAlive())
|
|
|
|
{
|
|
|
|
m_hEnemy = 0;
|
|
|
|
m_flIdealHeadYaw = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (gpGlobals->time > m_flLastSeen + 15)
|
|
|
|
{
|
|
|
|
if (m_hEnemy != 0)
|
|
|
|
{
|
|
|
|
if ((pev->origin - m_hEnemy->pev->origin).Length() > 700)
|
|
|
|
m_hEnemy = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_hEnemy == 0)
|
|
|
|
{
|
|
|
|
Look(4096);
|
|
|
|
m_hEnemy = BestVisibleEnemy();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_hEnemy != 0 || m_fFirstSighting)
|
|
|
|
{
|
|
|
|
if (m_iWasHit)
|
|
|
|
{
|
|
|
|
const char* flinchAnim = RANDOM_LONG(0,1) ? "flinch1" : "flinch2";
|
|
|
|
pev->sequence = LookupSequence(flinchAnim);
|
|
|
|
m_iWasHit = FALSE;
|
|
|
|
PainSound();
|
|
|
|
m_fLockHeight = 0;
|
|
|
|
m_fLockYaw = 0;
|
|
|
|
m_fAttacking = 0;
|
|
|
|
}
|
|
|
|
else if (pev->origin.z == m_posDesired.z)
|
|
|
|
{
|
|
|
|
if ( abs((int)floor(m_flIdealTorsoYaw - m_flTorsoYaw)) > 10 || !ClawAttack() )
|
|
|
|
{
|
|
|
|
if ( RANDOM_LONG(0, 2) == 0 )
|
|
|
|
{
|
|
|
|
IdleSound();
|
|
|
|
}
|
|
|
|
|
|
|
|
pev->sequence = LookupSequence("idle2");
|
|
|
|
m_fLockHeight = FALSE;
|
|
|
|
m_fLockYaw = FALSE;
|
|
|
|
m_fAttacking = FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ( RANDOM_LONG(0, 2) == 0 )
|
|
|
|
{
|
|
|
|
IdleSound();
|
|
|
|
}
|
|
|
|
pev->sequence = LookupSequence("idle");
|
|
|
|
m_fLockHeight = FALSE;
|
|
|
|
m_fLockYaw = FALSE;
|
|
|
|
m_fAttacking = FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (m_hEnemy != 0 && !m_fFirstSighting)
|
|
|
|
{
|
|
|
|
EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "pitworm/pit_worm_alert.wav", VOL_NORM, 0.1, 0, 100);
|
|
|
|
m_fFirstSighting = TRUE;
|
|
|
|
pev->sequence = LookupSequence("scream");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPitWorm::EyeLight(const Vector &vecEyePos)
|
|
|
|
{
|
|
|
|
MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY );
|
|
|
|
WRITE_BYTE( TE_ELIGHT );
|
|
|
|
WRITE_SHORT( entindex() ); // entity, attachment
|
|
|
|
WRITE_COORD( vecEyePos.x ); // origin
|
|
|
|
WRITE_COORD( vecEyePos.y );
|
|
|
|
WRITE_COORD( vecEyePos.z );
|
|
|
|
WRITE_COORD( 128 ); // radius
|
|
|
|
WRITE_BYTE( 128 ); // R
|
|
|
|
WRITE_BYTE( 255 ); // G
|
|
|
|
WRITE_BYTE( 128 ); // B
|
|
|
|
WRITE_BYTE( 1 ); // life * 10
|
|
|
|
WRITE_COORD( 2 ); // decay
|
|
|
|
MESSAGE_END();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPitWorm::BeamEffect(TraceResult &tr)
|
|
|
|
{
|
|
|
|
CBaseEntity *pEntity = CBaseEntity::Instance( tr.pHit );
|
|
|
|
if( pEntity != NULL && pEntity->pev->takedamage )
|
|
|
|
{
|
|
|
|
ClearMultiDamage();
|
|
|
|
pEntity->TraceAttack(pev, gSkillData.pwormDmgBeam, m_vecBeam, &tr, DMG_ENERGYBEAM);
|
|
|
|
ApplyMultiDamage(pev, pev);
|
|
|
|
}
|
|
|
|
else if ( tr.flFraction != 1.0f )
|
|
|
|
{
|
|
|
|
UTIL_DecalTrace(&tr, DECAL_GUNSHOT1 + RANDOM_LONG(0,4));
|
|
|
|
m_pBeam->DoSparks(tr.vecEndPos, tr.vecEndPos);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPitWorm::LockTopLevel()
|
|
|
|
{
|
|
|
|
if (m_iLevel == 3)
|
|
|
|
{
|
|
|
|
pev->health = pev->max_health;
|
|
|
|
m_iWasHit = 1;
|
|
|
|
m_iLevel = 2;
|
|
|
|
m_fTopLevelLocked = TRUE;
|
|
|
|
m_flTakeHitTime = gpGlobals->time + RANDOM_LONG(0,2);
|
|
|
|
m_posDesired.z = m_flLevels[2];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_fTopLevelLocked = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOL CPitWorm::ClawAttack()
|
|
|
|
{
|
|
|
|
if (m_hEnemy == 0)
|
|
|
|
return FALSE;
|
|
|
|
if (pev->origin.z != m_posDesired.z)
|
|
|
|
return FALSE;
|
|
|
|
if ( m_flNextMeleeTime > gpGlobals->time )
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
float flDist = (pev->origin - m_hEnemy->pev->origin).Length2D();
|
|
|
|
Vector targetAngle = UTIL_VecToAngles((m_posTarget - pev->origin).Normalize());
|
|
|
|
float angleDiff = UTIL_AngleDiff(targetAngle.y, pev->angles.y);
|
|
|
|
|
|
|
|
if (!FVisible(m_posTarget))
|
|
|
|
{
|
|
|
|
if (flDist < 600)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOL shouldClaw = FALSE;
|
|
|
|
if (m_iLevel == 2)
|
|
|
|
{
|
|
|
|
if (angleDiff >= 30)
|
|
|
|
{
|
|
|
|
pev->sequence = LookupSequence("doorclaw1");
|
|
|
|
m_flIdealHeadYaw = 0;
|
|
|
|
shouldClaw = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (m_iLevel == 1)
|
|
|
|
{
|
|
|
|
if ( angleDiff <= -30.0 )
|
|
|
|
{
|
|
|
|
pev->sequence = LookupSequence("doorclaw2");
|
|
|
|
m_flIdealHeadYaw = 0;
|
|
|
|
shouldClaw = TRUE;
|
|
|
|
}
|
|
|
|
if ( angleDiff >= 30.0 )
|
|
|
|
{
|
|
|
|
pev->sequence = LookupSequence("doorclaw3");
|
|
|
|
m_flIdealHeadYaw = 0;
|
|
|
|
shouldClaw = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (shouldClaw)
|
|
|
|
{
|
|
|
|
SwipeSound();
|
|
|
|
m_fLockHeight = TRUE;
|
|
|
|
m_fLockYaw = TRUE;
|
|
|
|
m_fAttacking = TRUE;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_fLockYaw = FALSE;
|
|
|
|
if (m_iLevel == 2)
|
|
|
|
{
|
|
|
|
if (angleDiff < 30)
|
|
|
|
{
|
|
|
|
if (flDist > 425 || angleDiff >= -50)
|
|
|
|
{
|
|
|
|
pev->sequence = LookupSequence("eyeblast");
|
|
|
|
m_posBeam = m_posTarget;
|
|
|
|
m_vecBeam = m_vecTarget;
|
|
|
|
m_angleBeam = UTIL_VecToAngles(m_vecBeam);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pev->sequence = LookupSequence("attack");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pev->sequence = LookupSequence("doorclaw1");
|
|
|
|
m_flIdealHeadYaw = 0;
|
|
|
|
m_fLockYaw = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (m_iLevel == 3)
|
|
|
|
{
|
|
|
|
if (flDist <= 425)
|
|
|
|
{
|
|
|
|
pev->sequence = RANDOM_LONG(0,1) ? LookupSequence("platclaw1") : LookupSequence("platclaw2");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pev->sequence = LookupSequence("eyeblast");
|
|
|
|
m_posBeam = m_posTarget;
|
|
|
|
m_vecBeam = m_vecTarget;
|
|
|
|
m_angleBeam = UTIL_VecToAngles(m_vecBeam);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (m_iLevel == 1)
|
|
|
|
{
|
|
|
|
if (angleDiff < 50)
|
|
|
|
{
|
|
|
|
if (angleDiff > -30)
|
|
|
|
{
|
|
|
|
if (flDist > 425)
|
|
|
|
{
|
|
|
|
pev->sequence = LookupSequence("eyeblast");
|
|
|
|
m_posBeam = m_posTarget;
|
|
|
|
m_vecBeam = m_vecTarget;
|
|
|
|
m_angleBeam = UTIL_VecToAngles(m_vecBeam);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pev->sequence = LookupSequence("attacklow");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pev->sequence = LookupSequence("doorclaw2");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pev->sequence = LookupSequence("doorclaw3");
|
|
|
|
m_flIdealHeadYaw = 0;
|
|
|
|
m_fLockYaw = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (pev->sequence == LookupSequence("eyeblast"))
|
|
|
|
{
|
|
|
|
BeamSound();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
SwipeSound();
|
|
|
|
}
|
|
|
|
m_fAttacking = TRUE;
|
|
|
|
m_fLockHeight = TRUE;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPitWorm::ShootBeam()
|
|
|
|
{
|
|
|
|
if ( m_hEnemy != 0 )
|
|
|
|
{
|
|
|
|
if ( m_flHeadYaw > 0.0 )
|
|
|
|
{
|
|
|
|
m_offsetBeam = 80;
|
|
|
|
m_flBeamDir = -1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_offsetBeam = -80;
|
|
|
|
m_flBeamDir = 1;
|
|
|
|
}
|
|
|
|
Vector vecEyePos, vecEyeAng;
|
|
|
|
GetAttachment(0, vecEyePos, vecEyeAng);
|
|
|
|
|
|
|
|
m_vecBeam = (m_posBeam - vecEyePos).Normalize();
|
|
|
|
m_angleBeam = UTIL_VecToAngles(m_vecBeam);
|
|
|
|
UTIL_MakeVectors(m_angleBeam);
|
|
|
|
m_vecBeam = gpGlobals->v_forward;
|
|
|
|
m_vecBeam.z = -m_vecBeam.z;
|
|
|
|
Vector vecEnd = m_vecBeam * m_offsetBeam + m_vecBeam * 1280 + vecEyePos;
|
|
|
|
|
|
|
|
TraceResult tr;
|
|
|
|
UTIL_TraceLine(vecEyePos, vecEnd, dont_ignore_monsters, ENT(pev), &tr);
|
|
|
|
|
|
|
|
m_pBeam = CBeam::BeamCreate("sprites/laserbeam.spr", 80);
|
|
|
|
if ( m_pBeam )
|
|
|
|
{
|
|
|
|
m_pBeam->PointEntInit(tr.vecEndPos, entindex());
|
|
|
|
m_pBeam->SetEndAttachment(1);
|
|
|
|
m_pBeam->SetColor(0,255,32);
|
|
|
|
m_pBeam->SetBrightness(128);
|
|
|
|
m_pBeam->SetWidth(32);
|
|
|
|
m_pBeam->pev->spawnflags |= SF_BEAM_SPARKSTART;
|
|
|
|
|
|
|
|
BeamEffect(tr);
|
|
|
|
|
|
|
|
m_pBeam->DoSparks(vecEyePos, vecEyePos);
|
|
|
|
m_flBeamExpireTime = gpGlobals->time + 0.9;
|
|
|
|
float beamYaw = m_flHeadYaw - m_flBeamDir * 25.0;
|
|
|
|
if ( beamYaw < -45.0 || beamYaw > 45.0 )
|
|
|
|
{
|
|
|
|
m_flIdealHeadYaw += m_flBeamDir * 50;
|
|
|
|
}
|
|
|
|
|
|
|
|
EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pShootSounds), VOL_NORM, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5));
|
|
|
|
|
|
|
|
EyeLight(vecEyePos);
|
|
|
|
|
|
|
|
m_pSprite = CSprite::SpriteCreate("sprites/tele1.spr", vecEyePos, TRUE);
|
|
|
|
if ( m_pSprite )
|
|
|
|
{
|
|
|
|
m_pSprite->SetTransparency(kRenderGlow, 0, 255, 0, 0, kRenderFxNoDissipation);
|
|
|
|
m_pSprite->SetAttachment(edict(), 1);
|
|
|
|
m_pSprite->SetScale(0.75);
|
|
|
|
m_pSprite->pev->framerate = 10;
|
|
|
|
m_pSprite->TurnOn();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPitWorm::StrafeBeam()
|
|
|
|
{
|
|
|
|
m_offsetBeam += m_flBeamDir * 20;
|
|
|
|
|
|
|
|
Vector vecEyePos, vecEyeAng;
|
|
|
|
GetAttachment(0, vecEyePos, vecEyeAng);
|
|
|
|
|
|
|
|
m_vecBeam = (m_posBeam - vecEyePos).Normalize();
|
|
|
|
m_angleBeam = UTIL_VecToAngles(m_vecBeam);
|
|
|
|
UTIL_MakeVectors(m_angleBeam);
|
|
|
|
m_vecBeam = gpGlobals->v_forward;
|
|
|
|
m_vecBeam.z = -m_vecBeam.z;
|
|
|
|
|
|
|
|
Vector vecEnd = m_vecBeam * m_offsetBeam + m_vecBeam * 1280 + vecEyePos;
|
|
|
|
|
|
|
|
TraceResult tr;
|
|
|
|
UTIL_TraceLine(vecEyePos, vecEnd, dont_ignore_monsters, ENT(pev), &tr);
|
|
|
|
|
|
|
|
m_pBeam->SetStartPos(tr.vecEndPos);
|
|
|
|
BeamEffect(tr);
|
|
|
|
|
|
|
|
EyeLight(vecEyePos);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPitWorm::ChangeLevel()
|
|
|
|
{
|
|
|
|
float currentZ = pev->origin.z;
|
|
|
|
float desiredZ = m_posDesired.z;
|
|
|
|
if (currentZ > desiredZ)
|
|
|
|
{
|
|
|
|
pev->origin.z -= m_flLevelSpeed;
|
|
|
|
|
|
|
|
if (pev->origin.z < desiredZ)
|
|
|
|
pev->origin.z = desiredZ;
|
|
|
|
}
|
|
|
|
else if (currentZ < desiredZ)
|
|
|
|
{
|
|
|
|
pev->origin.z += m_flLevelSpeed;
|
|
|
|
|
|
|
|
if (pev->origin.z > desiredZ)
|
|
|
|
pev->origin.z = desiredZ;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_flIdealTorsoYaw != m_flTorsoYaw)
|
|
|
|
{
|
|
|
|
if (m_flIdealTorsoYaw < m_flTorsoYaw)
|
|
|
|
{
|
|
|
|
m_flTorsoYaw -= 5.0;
|
|
|
|
if (m_flTorsoYaw < m_flIdealTorsoYaw)
|
|
|
|
m_flTorsoYaw = m_flIdealTorsoYaw;
|
|
|
|
}
|
|
|
|
else if (m_flIdealTorsoYaw > m_flTorsoYaw)
|
|
|
|
{
|
|
|
|
m_flTorsoYaw += 5;
|
|
|
|
if (m_flTorsoYaw > m_flIdealTorsoYaw)
|
|
|
|
m_flTorsoYaw = m_flIdealTorsoYaw;
|
|
|
|
}
|
|
|
|
SetBoneController(PITWORM_CONTROLLER_BODY_YAW, m_flTorsoYaw);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_flIdealHeadYaw != m_flHeadYaw)
|
|
|
|
{
|
|
|
|
if (m_flIdealHeadYaw < m_flHeadYaw)
|
|
|
|
{
|
|
|
|
m_flHeadYaw -= 5.0;
|
|
|
|
if (m_flHeadYaw < m_flIdealHeadYaw)
|
|
|
|
m_flHeadYaw = m_flIdealHeadYaw;
|
|
|
|
}
|
|
|
|
else if (m_flIdealHeadYaw > m_flHeadYaw)
|
|
|
|
{
|
|
|
|
m_flHeadYaw += 5;
|
|
|
|
if (m_flHeadYaw > m_flIdealHeadYaw)
|
|
|
|
m_flHeadYaw = m_flIdealHeadYaw;
|
|
|
|
}
|
|
|
|
SetBoneController(PITWORM_CONTROLLER_EYE_YAW, m_flHeadYaw);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_flIdealHeadPitch != m_flHeadPitch)
|
|
|
|
{
|
|
|
|
if (m_flIdealHeadPitch < m_flHeadPitch)
|
|
|
|
{
|
|
|
|
m_flHeadPitch -= 5.0;
|
|
|
|
if (m_flHeadPitch < m_flIdealHeadPitch)
|
|
|
|
m_flHeadPitch = m_flIdealHeadPitch;
|
|
|
|
}
|
|
|
|
else if (m_flIdealHeadPitch > m_flHeadPitch)
|
|
|
|
{
|
|
|
|
m_flHeadPitch += 5;
|
|
|
|
if (m_flHeadPitch > m_flIdealHeadPitch)
|
|
|
|
m_flHeadPitch = m_flIdealHeadPitch;
|
|
|
|
}
|
|
|
|
SetBoneController(PITWORM_CONTROLLER_EYE_PITCH, m_flHeadPitch);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPitWorm::TrackEnemy()
|
|
|
|
{
|
|
|
|
Vector vecEyePos, vecEyeAng;
|
|
|
|
GetAttachment(0, vecEyePos, vecEyeAng);
|
|
|
|
|
|
|
|
Vector vec = (m_hEnemy->pev->origin + m_hEnemy->pev->view_ofs) - Vector(pev->origin.x, pev->origin.y, vecEyePos.z);
|
|
|
|
Vector vecDir = UTIL_VecToAngles(vec);
|
|
|
|
float angleDiff = UTIL_AngleDiff(vecDir.x, pev->angles.x);
|
|
|
|
if (angleDiff < PITWORM_EYE_PITCH_MIN)
|
|
|
|
{
|
|
|
|
angleDiff = PITWORM_EYE_PITCH_MIN;
|
|
|
|
}
|
|
|
|
else if (angleDiff > PITWORM_EYE_PITCH_MAX)
|
|
|
|
{
|
|
|
|
angleDiff = PITWORM_EYE_PITCH_MAX;
|
|
|
|
}
|
|
|
|
m_flIdealHeadPitch = angleDiff;
|
|
|
|
|
|
|
|
float targetYaw = UTIL_VecToYaw(m_hEnemy->pev->origin + m_hEnemy->pev->view_ofs - vecEyePos) - pev->angles.y;
|
|
|
|
if (targetYaw > 180)
|
|
|
|
{
|
|
|
|
targetYaw -= 360;
|
|
|
|
}
|
|
|
|
else if (targetYaw < -180)
|
|
|
|
{
|
|
|
|
targetYaw += 360;
|
|
|
|
}
|
|
|
|
if (!m_fLockYaw)
|
|
|
|
{
|
|
|
|
if (targetYaw < 0)
|
|
|
|
{
|
|
|
|
const float minYaw = (m_iLevel == 1) ? -30 : -50;
|
|
|
|
if (targetYaw <= minYaw)
|
|
|
|
targetYaw = minYaw;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
const float maxYaw = (m_iLevel == 2) ? 30 :50;
|
|
|
|
if (targetYaw > maxYaw)
|
|
|
|
targetYaw = maxYaw;
|
|
|
|
}
|
|
|
|
m_flIdealTorsoYaw = targetYaw;
|
|
|
|
}
|
|
|
|
float torsoYawDiff = m_flTorsoYaw - targetYaw;
|
|
|
|
if (torsoYawDiff > 0)
|
|
|
|
{
|
|
|
|
if (torsoYawDiff > PITWORM_EYE_YAW_MAX)
|
|
|
|
{
|
|
|
|
torsoYawDiff = PITWORM_EYE_YAW_MAX;
|
|
|
|
}
|
|
|
|
else if (torsoYawDiff < PITWORM_EYE_YAW_MIN)
|
|
|
|
{
|
|
|
|
torsoYawDiff = PITWORM_EYE_YAW_MIN;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!m_fAttacking || m_pBeam != 0)
|
|
|
|
{
|
|
|
|
m_flIdealHeadYaw = torsoYawDiff;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!m_fLockHeight)
|
|
|
|
{
|
|
|
|
if (m_hEnemy->pev->origin.z <= m_flTargetLevels[1])
|
|
|
|
{
|
|
|
|
m_iLevel = 0;
|
|
|
|
}
|
|
|
|
else if (m_hEnemy->pev->origin.z <= m_flTargetLevels[2])
|
|
|
|
{
|
|
|
|
m_iLevel = 1;
|
|
|
|
}
|
|
|
|
else if (m_fTopLevelLocked || m_hEnemy->pev->origin.z <= m_flTargetLevels[3])
|
|
|
|
{
|
|
|
|
m_iLevel = 2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_iLevel = 3;
|
|
|
|
}
|
|
|
|
m_posDesired.z = m_flLevels[m_iLevel];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class CPitWormSteamTrigger : public CBaseEntity
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
void Spawn();
|
|
|
|
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
|
|
|
};
|
|
|
|
|
|
|
|
LINK_ENTITY_TO_CLASS(info_pitworm_steam_lock, CPitWormSteamTrigger)
|
|
|
|
|
|
|
|
void CPitWormSteamTrigger::Spawn()
|
|
|
|
{
|
|
|
|
pev->solid = SOLID_NOT;
|
|
|
|
pev->movetype = MOVETYPE_NONE;
|
|
|
|
pev->effects = EF_NODRAW;
|
|
|
|
UTIL_SetOrigin(pev, pev->origin);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPitWormSteamTrigger::Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value)
|
|
|
|
{
|
|
|
|
CPitWorm* pWorm = (CPitWorm*)UTIL_FindEntityByClassname(0, "monster_pitworm_up");
|
|
|
|
if (pWorm)
|
|
|
|
{
|
|
|
|
pWorm->LockTopLevel();
|
|
|
|
}
|
|
|
|
}
|