Portable Half-Life SDK. GoldSource and Xash3D. Crossplatform.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1402 lines
33 KiB

/***
*
* 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();
}
}