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.
1368 lines
36 KiB
1368 lines
36 KiB
/*** |
|
* |
|
* 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. |
|
* 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. |
|
* |
|
****/ |
|
#ifndef OEM_BUILD |
|
|
|
//========================================================= |
|
// Gargantua |
|
//========================================================= |
|
#include "extdll.h" |
|
#include "util.h" |
|
#include "cbase.h" |
|
#include "nodes.h" |
|
#include "monsters.h" |
|
#include "schedule.h" |
|
#include "customentity.h" |
|
#include "weapons.h" |
|
#include "effects.h" |
|
#include "soundent.h" |
|
#include "decals.h" |
|
#include "explode.h" |
|
#include "func_break.h" |
|
|
|
//========================================================= |
|
// Gargantua Monster |
|
//========================================================= |
|
const float GARG_ATTACKDIST = 80.0; |
|
|
|
// Garg animation events |
|
#define GARG_AE_SLASH_LEFT 1 |
|
//#define GARG_AE_BEAM_ATTACK_RIGHT 2 // No longer used |
|
#define GARG_AE_LEFT_FOOT 3 |
|
#define GARG_AE_RIGHT_FOOT 4 |
|
#define GARG_AE_STOMP 5 |
|
#define GARG_AE_BREATHE 6 |
|
#define STOMP_FRAMETIME 0.015 // gpGlobals->frametime |
|
|
|
// Gargantua is immune to any damage but this |
|
#define GARG_DAMAGE (DMG_ENERGYBEAM|DMG_CRUSH|DMG_MORTAR|DMG_BLAST) |
|
#define GARG_EYE_SPRITE_NAME "sprites/gargeye1.spr" |
|
#define GARG_BEAM_SPRITE_NAME "sprites/xbeam3.spr" |
|
#define GARG_BEAM_SPRITE2 "sprites/xbeam3.spr" |
|
#define GARG_STOMP_SPRITE_NAME "sprites/gargeye1.spr" |
|
#define GARG_STOMP_BUZZ_SOUND "weapons/mine_charge.wav" |
|
#define GARG_FLAME_LENGTH 330 |
|
#define GARG_GIB_MODEL "models/metalplategibs.mdl" |
|
|
|
#define ATTN_GARG (ATTN_NORM) |
|
|
|
#define STOMP_SPRITE_COUNT 10 |
|
|
|
int gStompSprite = 0, gGargGibModel = 0; |
|
void SpawnExplosion( Vector center, float randomRange, float time, int magnitude ); |
|
|
|
class CSmoker; |
|
|
|
// Spiral Effect |
|
class CSpiral : public CBaseEntity |
|
{ |
|
public: |
|
void Spawn( void ); |
|
void Think( void ); |
|
int ObjectCaps( void ) { return FCAP_DONT_SAVE; } |
|
static CSpiral *Create( const Vector &origin, float height, float radius, float duration ); |
|
}; |
|
LINK_ENTITY_TO_CLASS( streak_spiral, CSpiral ); |
|
|
|
|
|
class CStomp : public CBaseEntity |
|
{ |
|
public: |
|
void Spawn( void ); |
|
void Think( void ); |
|
static CStomp *StompCreate( const Vector &origin, const Vector &end, float speed ); |
|
|
|
private: |
|
// UNDONE: re-use this sprite list instead of creating new ones all the time |
|
// CSprite *m_pSprites[ STOMP_SPRITE_COUNT ]; |
|
}; |
|
|
|
LINK_ENTITY_TO_CLASS( garg_stomp, CStomp ); |
|
CStomp *CStomp::StompCreate( const Vector &origin, const Vector &end, float speed ) |
|
{ |
|
CStomp *pStomp = GetClassPtr( (CStomp *)NULL ); |
|
|
|
pStomp->pev->origin = origin; |
|
Vector dir = (end - origin); |
|
pStomp->pev->scale = dir.Length(); |
|
pStomp->pev->movedir = dir.Normalize(); |
|
pStomp->pev->speed = speed; |
|
pStomp->Spawn(); |
|
|
|
return pStomp; |
|
} |
|
|
|
void CStomp::Spawn( void ) |
|
{ |
|
pev->nextthink = gpGlobals->time; |
|
pev->classname = MAKE_STRING("garg_stomp"); |
|
pev->dmgtime = gpGlobals->time; |
|
|
|
pev->framerate = 30; |
|
pev->model = MAKE_STRING(GARG_STOMP_SPRITE_NAME); |
|
pev->rendermode = kRenderTransTexture; |
|
pev->renderamt = 0; |
|
EMIT_SOUND_DYN( edict(), CHAN_BODY, GARG_STOMP_BUZZ_SOUND, 1, ATTN_NORM, 0, PITCH_NORM * 0.55); |
|
} |
|
|
|
|
|
#define STOMP_INTERVAL 0.025 |
|
|
|
void CStomp::Think( void ) |
|
{ |
|
TraceResult tr; |
|
|
|
pev->nextthink = gpGlobals->time + 0.1; |
|
|
|
// Do damage for this frame |
|
Vector vecStart = pev->origin; |
|
vecStart.z += 30; |
|
Vector vecEnd = vecStart + (pev->movedir * pev->speed * STOMP_FRAMETIME); |
|
|
|
UTIL_TraceHull( vecStart, vecEnd, dont_ignore_monsters, head_hull, ENT(pev), &tr ); |
|
|
|
if ( tr.pHit && tr.pHit != pev->owner ) |
|
{ |
|
CBaseEntity *pEntity = CBaseEntity::Instance( tr.pHit ); |
|
entvars_t *pevOwner = pev; |
|
if ( pev->owner ) |
|
pevOwner = VARS(pev->owner); |
|
|
|
if ( pEntity ) |
|
pEntity->TakeDamage( pev, pevOwner, gSkillData.gargantuaDmgStomp, DMG_SONIC ); |
|
} |
|
|
|
// Accelerate the effect |
|
pev->speed = pev->speed + (STOMP_FRAMETIME) * pev->framerate; |
|
pev->framerate = pev->framerate + (STOMP_FRAMETIME) * 1500; |
|
|
|
// Move and spawn trails |
|
while ( gpGlobals->time - pev->dmgtime > STOMP_INTERVAL ) |
|
{ |
|
pev->origin = pev->origin + pev->movedir * pev->speed * STOMP_INTERVAL; |
|
for ( int i = 0; i < 2; i++ ) |
|
{ |
|
CSprite *pSprite = CSprite::SpriteCreate( GARG_STOMP_SPRITE_NAME, pev->origin, TRUE ); |
|
if ( pSprite ) |
|
{ |
|
UTIL_TraceLine( pev->origin, pev->origin - Vector(0,0,500), ignore_monsters, edict(), &tr ); |
|
pSprite->pev->origin = tr.vecEndPos; |
|
pSprite->pev->velocity = Vector(RANDOM_FLOAT(-200,200),RANDOM_FLOAT(-200,200),175); |
|
// pSprite->AnimateAndDie( RANDOM_FLOAT( 8.0, 12.0 ) ); |
|
pSprite->pev->nextthink = gpGlobals->time + 0.3; |
|
pSprite->SetThink( &CBaseEntity::SUB_Remove ); |
|
pSprite->SetTransparency( kRenderTransAdd, 255, 255, 255, 255, kRenderFxFadeFast ); |
|
} |
|
} |
|
pev->dmgtime += STOMP_INTERVAL; |
|
// Scale has the "life" of this effect |
|
pev->scale -= STOMP_INTERVAL * pev->speed; |
|
if ( pev->scale <= 0 ) |
|
{ |
|
// Life has run out |
|
UTIL_Remove(this); |
|
STOP_SOUND( edict(), CHAN_BODY, GARG_STOMP_BUZZ_SOUND ); |
|
} |
|
|
|
} |
|
} |
|
|
|
|
|
void StreakSplash( const Vector &origin, const Vector &direction, int color, int count, int speed, int velocityRange ) |
|
{ |
|
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, origin ); |
|
WRITE_BYTE( TE_STREAK_SPLASH ); |
|
WRITE_COORD( origin.x ); // origin |
|
WRITE_COORD( origin.y ); |
|
WRITE_COORD( origin.z ); |
|
WRITE_COORD( direction.x ); // direction |
|
WRITE_COORD( direction.y ); |
|
WRITE_COORD( direction.z ); |
|
WRITE_BYTE( color ); // Streak color 6 |
|
WRITE_SHORT( count ); // count |
|
WRITE_SHORT( speed ); |
|
WRITE_SHORT( velocityRange ); // Random velocity modifier |
|
MESSAGE_END(); |
|
} |
|
|
|
|
|
class CGargantua : public CBaseMonster |
|
{ |
|
public: |
|
void Spawn( void ); |
|
void Precache( void ); |
|
void SetYawSpeed( void ); |
|
int Classify ( void ); |
|
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 HandleAnimEvent( MonsterEvent_t *pEvent ); |
|
|
|
BOOL CheckMeleeAttack1( float flDot, float flDist ); // Swipe |
|
BOOL CheckMeleeAttack2( float flDot, float flDist ); // Flames |
|
BOOL CheckRangeAttack1( float flDot, float flDist ); // Stomp attack |
|
void SetObjectCollisionBox( void ) |
|
{ |
|
pev->absmin = pev->origin + Vector( -80, -80, 0 ); |
|
pev->absmax = pev->origin + Vector( 80, 80, 214 ); |
|
} |
|
|
|
Schedule_t *GetScheduleOfType( int Type ); |
|
void StartTask( Task_t *pTask ); |
|
void RunTask( Task_t *pTask ); |
|
|
|
void PrescheduleThink( void ); |
|
|
|
void Killed( entvars_t *pevAttacker, int iGib ); |
|
void DeathEffect( void ); |
|
|
|
void EyeOff( void ); |
|
void EyeOn( int level ); |
|
void EyeUpdate( void ); |
|
void Leap( void ); |
|
void StompAttack( void ); |
|
void FlameCreate( void ); |
|
void FlameUpdate( void ); |
|
void FlameControls( float angleX, float angleY ); |
|
void FlameDestroy( void ); |
|
inline BOOL FlameIsOn( void ) { return m_pFlame[0] != NULL; } |
|
|
|
void FlameDamage( Vector vecStart, Vector vecEnd, entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int iClassIgnore, int bitsDamageType ); |
|
|
|
virtual int Save( CSave &save ); |
|
virtual int Restore( CRestore &restore ); |
|
static TYPEDESCRIPTION m_SaveData[]; |
|
|
|
CUSTOM_SCHEDULES; |
|
|
|
private: |
|
static const char *pAttackHitSounds[]; |
|
static const char *pBeamAttackSounds[]; |
|
static const char *pAttackMissSounds[]; |
|
static const char *pRicSounds[]; |
|
static const char *pFootSounds[]; |
|
static const char *pIdleSounds[]; |
|
static const char *pAlertSounds[]; |
|
static const char *pPainSounds[]; |
|
static const char *pAttackSounds[]; |
|
static const char *pStompSounds[]; |
|
static const char *pBreatheSounds[]; |
|
|
|
CBaseEntity* GargantuaCheckTraceHullAttack(float flDist, int iDamage, int iDmgType); |
|
|
|
CSprite *m_pEyeGlow; // Glow around the eyes |
|
CBeam *m_pFlame[4]; // Flame beams |
|
|
|
int m_eyeBrightness; // Brightness target |
|
float m_seeTime; // Time to attack (when I see the enemy, I set this) |
|
float m_flameTime; // Time of next flame attack |
|
float m_painSoundTime; // Time of next pain sound |
|
float m_streakTime; // streak timer (don't send too many) |
|
float m_flameX; // Flame thrower aim |
|
float m_flameY; |
|
}; |
|
|
|
LINK_ENTITY_TO_CLASS( monster_gargantua, CGargantua ); |
|
|
|
TYPEDESCRIPTION CGargantua::m_SaveData[] = |
|
{ |
|
DEFINE_FIELD( CGargantua, m_pEyeGlow, FIELD_CLASSPTR ), |
|
DEFINE_FIELD( CGargantua, m_eyeBrightness, FIELD_INTEGER ), |
|
DEFINE_FIELD( CGargantua, m_seeTime, FIELD_TIME ), |
|
DEFINE_FIELD( CGargantua, m_flameTime, FIELD_TIME ), |
|
DEFINE_FIELD( CGargantua, m_streakTime, FIELD_TIME ), |
|
DEFINE_FIELD( CGargantua, m_painSoundTime, FIELD_TIME ), |
|
DEFINE_ARRAY( CGargantua, m_pFlame, FIELD_CLASSPTR, 4 ), |
|
DEFINE_FIELD( CGargantua, m_flameX, FIELD_FLOAT ), |
|
DEFINE_FIELD( CGargantua, m_flameY, FIELD_FLOAT ), |
|
}; |
|
|
|
IMPLEMENT_SAVERESTORE( CGargantua, CBaseMonster ); |
|
|
|
const char *CGargantua::pAttackHitSounds[] = |
|
{ |
|
"zombie/claw_strike1.wav", |
|
"zombie/claw_strike2.wav", |
|
"zombie/claw_strike3.wav", |
|
}; |
|
|
|
const char *CGargantua::pBeamAttackSounds[] = |
|
{ |
|
"garg/gar_flameoff1.wav", |
|
"garg/gar_flameon1.wav", |
|
"garg/gar_flamerun1.wav", |
|
}; |
|
|
|
|
|
const char *CGargantua::pAttackMissSounds[] = |
|
{ |
|
"zombie/claw_miss1.wav", |
|
"zombie/claw_miss2.wav", |
|
}; |
|
|
|
const char *CGargantua::pRicSounds[] = |
|
{ |
|
#if 0 |
|
"weapons/ric1.wav", |
|
"weapons/ric2.wav", |
|
"weapons/ric3.wav", |
|
"weapons/ric4.wav", |
|
"weapons/ric5.wav", |
|
#else |
|
"debris/metal4.wav", |
|
"debris/metal6.wav", |
|
"weapons/ric4.wav", |
|
"weapons/ric5.wav", |
|
#endif |
|
}; |
|
|
|
const char *CGargantua::pFootSounds[] = |
|
{ |
|
"garg/gar_step1.wav", |
|
"garg/gar_step2.wav", |
|
}; |
|
|
|
|
|
const char *CGargantua::pIdleSounds[] = |
|
{ |
|
"garg/gar_idle1.wav", |
|
"garg/gar_idle2.wav", |
|
"garg/gar_idle3.wav", |
|
"garg/gar_idle4.wav", |
|
"garg/gar_idle5.wav", |
|
}; |
|
|
|
|
|
const char *CGargantua::pAttackSounds[] = |
|
{ |
|
"garg/gar_attack1.wav", |
|
"garg/gar_attack2.wav", |
|
"garg/gar_attack3.wav", |
|
}; |
|
|
|
const char *CGargantua::pAlertSounds[] = |
|
{ |
|
"garg/gar_alert1.wav", |
|
"garg/gar_alert2.wav", |
|
"garg/gar_alert3.wav", |
|
}; |
|
|
|
const char *CGargantua::pPainSounds[] = |
|
{ |
|
"garg/gar_pain1.wav", |
|
"garg/gar_pain2.wav", |
|
"garg/gar_pain3.wav", |
|
}; |
|
|
|
const char *CGargantua::pStompSounds[] = |
|
{ |
|
"garg/gar_stomp1.wav", |
|
}; |
|
|
|
const char *CGargantua::pBreatheSounds[] = |
|
{ |
|
"garg/gar_breathe1.wav", |
|
"garg/gar_breathe2.wav", |
|
"garg/gar_breathe3.wav", |
|
}; |
|
//========================================================= |
|
// AI Schedules Specific to this monster |
|
//========================================================= |
|
#if 0 |
|
enum |
|
{ |
|
SCHED_ = LAST_COMMON_SCHEDULE + 1, |
|
}; |
|
#endif |
|
|
|
enum |
|
{ |
|
TASK_SOUND_ATTACK = LAST_COMMON_TASK + 1, |
|
TASK_FLAME_SWEEP, |
|
}; |
|
|
|
Task_t tlGargFlame[] = |
|
{ |
|
{ TASK_STOP_MOVING, (float)0 }, |
|
{ TASK_FACE_ENEMY, (float)0 }, |
|
{ TASK_SOUND_ATTACK, (float)0 }, |
|
// { TASK_PLAY_SEQUENCE, (float)ACT_SIGNAL1 }, |
|
{ TASK_SET_ACTIVITY, (float)ACT_MELEE_ATTACK2 }, |
|
{ TASK_FLAME_SWEEP, (float)4.5 }, |
|
{ TASK_SET_ACTIVITY, (float)ACT_IDLE }, |
|
}; |
|
|
|
Schedule_t slGargFlame[] = |
|
{ |
|
{ |
|
tlGargFlame, |
|
ARRAYSIZE ( tlGargFlame ), |
|
0, |
|
0, |
|
"GargFlame" |
|
}, |
|
}; |
|
|
|
|
|
// primary melee attack |
|
Task_t tlGargSwipe[] = |
|
{ |
|
{ TASK_STOP_MOVING, 0 }, |
|
{ TASK_FACE_ENEMY, (float)0 }, |
|
{ TASK_MELEE_ATTACK1, (float)0 }, |
|
}; |
|
|
|
Schedule_t slGargSwipe[] = |
|
{ |
|
{ |
|
tlGargSwipe, |
|
ARRAYSIZE ( tlGargSwipe ), |
|
bits_COND_CAN_MELEE_ATTACK2, |
|
0, |
|
"GargSwipe" |
|
}, |
|
}; |
|
|
|
|
|
DEFINE_CUSTOM_SCHEDULES( CGargantua ) |
|
{ |
|
slGargFlame, |
|
slGargSwipe, |
|
}; |
|
|
|
IMPLEMENT_CUSTOM_SCHEDULES( CGargantua, CBaseMonster ); |
|
|
|
|
|
void CGargantua::EyeOn( int level ) |
|
{ |
|
m_eyeBrightness = level; |
|
} |
|
|
|
|
|
void CGargantua::EyeOff( void ) |
|
{ |
|
m_eyeBrightness = 0; |
|
} |
|
|
|
|
|
void CGargantua::EyeUpdate( void ) |
|
{ |
|
if ( m_pEyeGlow ) |
|
{ |
|
m_pEyeGlow->pev->renderamt = UTIL_Approach( m_eyeBrightness, m_pEyeGlow->pev->renderamt, 26 ); |
|
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 ); |
|
} |
|
} |
|
|
|
|
|
void CGargantua::StompAttack( void ) |
|
{ |
|
TraceResult trace; |
|
|
|
UTIL_MakeVectors( pev->angles ); |
|
Vector vecStart = pev->origin + Vector(0,0,60) + 35 * gpGlobals->v_forward; |
|
Vector vecAim = ShootAtEnemy( vecStart ); |
|
Vector vecEnd = (vecAim * 1024) + vecStart; |
|
|
|
UTIL_TraceLine( vecStart, vecEnd, ignore_monsters, edict(), &trace ); |
|
CStomp::StompCreate( vecStart, trace.vecEndPos, 0 ); |
|
UTIL_ScreenShake( pev->origin, 12.0, 100.0, 2.0, 1000 ); |
|
EMIT_SOUND_DYN ( edict(), CHAN_WEAPON, pStompSounds[ RANDOM_LONG(0,ARRAYSIZE(pStompSounds)-1) ], 1.0, ATTN_GARG, 0, PITCH_NORM + RANDOM_LONG(-10,10) ); |
|
|
|
UTIL_TraceLine( pev->origin, pev->origin - Vector(0,0,20), ignore_monsters, edict(), &trace ); |
|
if ( trace.flFraction < 1.0 ) |
|
UTIL_DecalTrace( &trace, DECAL_GARGSTOMP1 ); |
|
} |
|
|
|
|
|
void CGargantua :: FlameCreate( void ) |
|
{ |
|
int i; |
|
Vector posGun, angleGun; |
|
TraceResult trace; |
|
|
|
UTIL_MakeVectors( pev->angles ); |
|
|
|
for ( i = 0; i < 4; i++ ) |
|
{ |
|
if ( i < 2 ) |
|
m_pFlame[i] = CBeam::BeamCreate( GARG_BEAM_SPRITE_NAME, 240 ); |
|
else |
|
m_pFlame[i] = CBeam::BeamCreate( GARG_BEAM_SPRITE2, 140 ); |
|
if ( m_pFlame[i] ) |
|
{ |
|
int attach = i%2; |
|
// attachment is 0 based in GetAttachment |
|
GetAttachment( attach+1, posGun, angleGun ); |
|
|
|
Vector vecEnd = (gpGlobals->v_forward * GARG_FLAME_LENGTH) + posGun; |
|
UTIL_TraceLine( posGun, vecEnd, dont_ignore_monsters, edict(), &trace ); |
|
|
|
m_pFlame[i]->PointEntInit( trace.vecEndPos, entindex() ); |
|
if ( i < 2 ) |
|
m_pFlame[i]->SetColor( 255, 130, 90 ); |
|
else |
|
m_pFlame[i]->SetColor( 0, 120, 255 ); |
|
m_pFlame[i]->SetBrightness( 190 ); |
|
m_pFlame[i]->SetFlags( BEAM_FSHADEIN ); |
|
m_pFlame[i]->SetScrollRate( 20 ); |
|
// attachment is 1 based in SetEndAttachment |
|
m_pFlame[i]->SetEndAttachment( attach + 2 ); |
|
CSoundEnt::InsertSound( bits_SOUND_COMBAT, posGun, 384, 0.3 ); |
|
} |
|
} |
|
EMIT_SOUND_DYN ( edict(), CHAN_BODY, pBeamAttackSounds[ 1 ], 1.0, ATTN_NORM, 0, PITCH_NORM ); |
|
EMIT_SOUND_DYN ( edict(), CHAN_WEAPON, pBeamAttackSounds[ 2 ], 1.0, ATTN_NORM, 0, PITCH_NORM ); |
|
} |
|
|
|
|
|
void CGargantua :: FlameControls( float angleX, float angleY ) |
|
{ |
|
if ( angleY < -180 ) |
|
angleY += 360; |
|
else if ( angleY > 180 ) |
|
angleY -= 360; |
|
|
|
if ( angleY < -45 ) |
|
angleY = -45; |
|
else if ( angleY > 45 ) |
|
angleY = 45; |
|
|
|
m_flameX = UTIL_ApproachAngle( angleX, m_flameX, 4 ); |
|
m_flameY = UTIL_ApproachAngle( angleY, m_flameY, 8 ); |
|
SetBoneController( 0, m_flameY ); |
|
SetBoneController( 1, m_flameX ); |
|
} |
|
|
|
|
|
void CGargantua :: FlameUpdate( void ) |
|
{ |
|
int i; |
|
static float offset[2] = { 60, -60 }; |
|
TraceResult trace; |
|
Vector vecStart, angleGun; |
|
BOOL streaks = FALSE; |
|
|
|
for ( i = 0; i < 2; i++ ) |
|
{ |
|
if ( m_pFlame[i] ) |
|
{ |
|
Vector vecAim = pev->angles; |
|
vecAim.x += m_flameX; |
|
vecAim.y += m_flameY; |
|
|
|
UTIL_MakeVectors( vecAim ); |
|
|
|
GetAttachment( i+1, vecStart, angleGun ); |
|
Vector vecEnd = vecStart + (gpGlobals->v_forward * GARG_FLAME_LENGTH); // - offset[i] * gpGlobals->v_right; |
|
|
|
UTIL_TraceLine( vecStart, vecEnd, dont_ignore_monsters, edict(), &trace ); |
|
|
|
m_pFlame[i]->SetStartPos( trace.vecEndPos ); |
|
m_pFlame[i+2]->SetStartPos( (vecStart * 0.6) + (trace.vecEndPos * 0.4) ); |
|
|
|
if ( trace.flFraction != 1.0 && gpGlobals->time > m_streakTime ) |
|
{ |
|
StreakSplash( trace.vecEndPos, trace.vecPlaneNormal, 6, 20, 50, 400 ); |
|
streaks = TRUE; |
|
UTIL_DecalTrace( &trace, DECAL_SMALLSCORCH1 + RANDOM_LONG(0,2) ); |
|
} |
|
// RadiusDamage( trace.vecEndPos, pev, pev, gSkillData.gargantuaDmgFire, CLASS_ALIEN_MONSTER, DMG_BURN ); |
|
FlameDamage( vecStart, trace.vecEndPos, pev, pev, gSkillData.gargantuaDmgFire, CLASS_ALIEN_MONSTER, DMG_BURN ); |
|
|
|
MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); |
|
WRITE_BYTE( TE_ELIGHT ); |
|
WRITE_SHORT( entindex( ) + 0x1000 * (i + 2) ); // entity, attachment |
|
WRITE_COORD( vecStart.x ); // origin |
|
WRITE_COORD( vecStart.y ); |
|
WRITE_COORD( vecStart.z ); |
|
WRITE_COORD( RANDOM_FLOAT( 32, 48 ) ); // radius |
|
WRITE_BYTE( 255 ); // R |
|
WRITE_BYTE( 255 ); // G |
|
WRITE_BYTE( 255 ); // B |
|
WRITE_BYTE( 2 ); // life * 10 |
|
WRITE_COORD( 0 ); // decay |
|
MESSAGE_END(); |
|
} |
|
} |
|
if ( streaks ) |
|
m_streakTime = gpGlobals->time; |
|
} |
|
|
|
|
|
|
|
void CGargantua :: FlameDamage( Vector vecStart, Vector vecEnd, entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int iClassIgnore, int bitsDamageType ) |
|
{ |
|
CBaseEntity *pEntity = NULL; |
|
TraceResult tr; |
|
float flAdjustedDamage; |
|
Vector vecSpot; |
|
|
|
Vector vecMid = (vecStart + vecEnd) * 0.5; |
|
|
|
float searchRadius = (vecStart - vecMid).Length(); |
|
|
|
Vector vecAim = (vecEnd - vecStart).Normalize( ); |
|
|
|
// iterate on all entities in the vicinity. |
|
while ((pEntity = UTIL_FindEntityInSphere( pEntity, vecMid, searchRadius )) != NULL) |
|
{ |
|
if ( pEntity->pev->takedamage != DAMAGE_NO ) |
|
{ |
|
// UNDONE: this should check a damage mask, not an ignore |
|
if ( iClassIgnore != CLASS_NONE && pEntity->Classify() == iClassIgnore ) |
|
{// houndeyes don't hurt other houndeyes with their attack |
|
continue; |
|
} |
|
|
|
vecSpot = pEntity->BodyTarget( vecMid ); |
|
|
|
float dist = DotProduct( vecAim, vecSpot - vecMid ); |
|
if (dist > searchRadius) |
|
dist = searchRadius; |
|
else if (dist < -searchRadius) |
|
dist = searchRadius; |
|
|
|
Vector vecSrc = vecMid + dist * vecAim; |
|
|
|
UTIL_TraceLine ( vecSrc, vecSpot, dont_ignore_monsters, ENT(pev), &tr ); |
|
|
|
if ( tr.flFraction == 1.0 || tr.pHit == pEntity->edict() ) |
|
{// the explosion can 'see' this entity, so hurt them! |
|
// decrease damage for an ent that's farther from the flame. |
|
dist = ( vecSrc - tr.vecEndPos ).Length(); |
|
|
|
if (dist > 64) |
|
{ |
|
flAdjustedDamage = flDamage - (dist - 64) * 0.4; |
|
if (flAdjustedDamage <= 0) |
|
continue; |
|
} |
|
else |
|
{ |
|
flAdjustedDamage = flDamage; |
|
} |
|
|
|
// ALERT( at_console, "hit %s\n", STRING( pEntity->pev->classname ) ); |
|
if (tr.flFraction != 1.0) |
|
{ |
|
ClearMultiDamage( ); |
|
pEntity->TraceAttack( pevInflictor, flAdjustedDamage, (tr.vecEndPos - vecSrc).Normalize( ), &tr, bitsDamageType ); |
|
ApplyMultiDamage( pevInflictor, pevAttacker ); |
|
} |
|
else |
|
{ |
|
pEntity->TakeDamage ( pevInflictor, pevAttacker, flAdjustedDamage, bitsDamageType ); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
void CGargantua :: FlameDestroy( void ) |
|
{ |
|
int i; |
|
|
|
EMIT_SOUND_DYN ( edict(), CHAN_WEAPON, pBeamAttackSounds[ 0 ], 1.0, ATTN_NORM, 0, PITCH_NORM ); |
|
for ( i = 0; i < 4; i++ ) |
|
{ |
|
if ( m_pFlame[i] ) |
|
{ |
|
UTIL_Remove( m_pFlame[i] ); |
|
m_pFlame[i] = NULL; |
|
} |
|
} |
|
} |
|
|
|
|
|
void CGargantua :: PrescheduleThink( void ) |
|
{ |
|
if ( !HasConditions( bits_COND_SEE_ENEMY ) ) |
|
{ |
|
m_seeTime = gpGlobals->time + 5; |
|
EyeOff(); |
|
} |
|
else |
|
EyeOn( 200 ); |
|
|
|
EyeUpdate(); |
|
} |
|
|
|
|
|
//========================================================= |
|
// Classify - indicates this monster's place in the |
|
// relationship table. |
|
//========================================================= |
|
int CGargantua :: Classify ( void ) |
|
{ |
|
return CLASS_ALIEN_MONSTER; |
|
} |
|
|
|
//========================================================= |
|
// SetYawSpeed - allows each sequence to have a different |
|
// turn rate associated with it. |
|
//========================================================= |
|
void CGargantua :: SetYawSpeed ( void ) |
|
{ |
|
int ys; |
|
|
|
switch ( m_Activity ) |
|
{ |
|
case ACT_IDLE: |
|
ys = 60; |
|
break; |
|
case ACT_TURN_LEFT: |
|
case ACT_TURN_RIGHT: |
|
ys = 180; |
|
break; |
|
case ACT_WALK: |
|
case ACT_RUN: |
|
ys = 60; |
|
break; |
|
|
|
default: |
|
ys = 60; |
|
break; |
|
} |
|
|
|
pev->yaw_speed = ys; |
|
} |
|
|
|
|
|
//========================================================= |
|
// Spawn |
|
//========================================================= |
|
void CGargantua :: Spawn() |
|
{ |
|
Precache( ); |
|
|
|
SET_MODEL(ENT(pev), "models/garg.mdl"); |
|
UTIL_SetSize( pev, Vector( -32, -32, 0 ), Vector( 32, 32, 64 ) ); |
|
|
|
pev->solid = SOLID_SLIDEBOX; |
|
pev->movetype = MOVETYPE_STEP; |
|
m_bloodColor = BLOOD_COLOR_GREEN; |
|
pev->health = gSkillData.gargantuaHealth; |
|
//pev->view_ofs = Vector ( 0, 0, 96 );// taken from mdl file |
|
m_flFieldOfView = -0.2;// width of forward view cone ( as a dotproduct result ) |
|
m_MonsterState = MONSTERSTATE_NONE; |
|
|
|
MonsterInit(); |
|
|
|
m_pEyeGlow = CSprite::SpriteCreate( GARG_EYE_SPRITE_NAME, pev->origin, FALSE ); |
|
m_pEyeGlow->SetTransparency( kRenderGlow, 255, 255, 255, 0, kRenderFxNoDissipation ); |
|
m_pEyeGlow->SetAttachment( edict(), 1 ); |
|
EyeOff(); |
|
m_seeTime = gpGlobals->time + 5; |
|
m_flameTime = gpGlobals->time + 2; |
|
} |
|
|
|
|
|
//========================================================= |
|
// Precache - precaches all resources this monster needs |
|
//========================================================= |
|
void CGargantua :: Precache() |
|
{ |
|
int i; |
|
|
|
PRECACHE_MODEL("models/garg.mdl"); |
|
PRECACHE_MODEL( GARG_EYE_SPRITE_NAME ); |
|
PRECACHE_MODEL( GARG_BEAM_SPRITE_NAME ); |
|
PRECACHE_MODEL( GARG_BEAM_SPRITE2 ); |
|
gStompSprite = PRECACHE_MODEL( GARG_STOMP_SPRITE_NAME ); |
|
gGargGibModel = PRECACHE_MODEL( GARG_GIB_MODEL ); |
|
PRECACHE_SOUND( GARG_STOMP_BUZZ_SOUND ); |
|
|
|
for ( i = 0; i < ARRAYSIZE( pAttackHitSounds ); i++ ) |
|
PRECACHE_SOUND((char *)pAttackHitSounds[i]); |
|
|
|
for ( i = 0; i < ARRAYSIZE( pBeamAttackSounds ); i++ ) |
|
PRECACHE_SOUND((char *)pBeamAttackSounds[i]); |
|
|
|
for ( i = 0; i < ARRAYSIZE( pAttackMissSounds ); i++ ) |
|
PRECACHE_SOUND((char *)pAttackMissSounds[i]); |
|
|
|
for ( i = 0; i < ARRAYSIZE( pRicSounds ); i++ ) |
|
PRECACHE_SOUND((char *)pRicSounds[i]); |
|
|
|
for ( i = 0; i < ARRAYSIZE( pFootSounds ); i++ ) |
|
PRECACHE_SOUND((char *)pFootSounds[i]); |
|
|
|
for ( i = 0; i < ARRAYSIZE( pIdleSounds ); i++ ) |
|
PRECACHE_SOUND((char *)pIdleSounds[i]); |
|
|
|
for ( i = 0; i < ARRAYSIZE( pAlertSounds ); i++ ) |
|
PRECACHE_SOUND((char *)pAlertSounds[i]); |
|
|
|
for ( i = 0; i < ARRAYSIZE( pPainSounds ); i++ ) |
|
PRECACHE_SOUND((char *)pPainSounds[i]); |
|
|
|
for ( i = 0; i < ARRAYSIZE( pAttackSounds ); i++ ) |
|
PRECACHE_SOUND((char *)pAttackSounds[i]); |
|
|
|
for ( i = 0; i < ARRAYSIZE( pStompSounds ); i++ ) |
|
PRECACHE_SOUND((char *)pStompSounds[i]); |
|
|
|
for ( i = 0; i < ARRAYSIZE( pBreatheSounds ); i++ ) |
|
PRECACHE_SOUND((char *)pBreatheSounds[i]); |
|
} |
|
|
|
|
|
void CGargantua::TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType ) |
|
{ |
|
ALERT( at_aiconsole, "CGargantua::TraceAttack\n"); |
|
|
|
if ( !IsAlive() ) |
|
{ |
|
CBaseMonster::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType ); |
|
return; |
|
} |
|
|
|
// UNDONE: Hit group specific damage? |
|
if ( bitsDamageType & (GARG_DAMAGE|DMG_BLAST) ) |
|
{ |
|
if ( m_painSoundTime < gpGlobals->time ) |
|
{ |
|
EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, pPainSounds[ RANDOM_LONG(0,ARRAYSIZE(pPainSounds)-1) ], 1.0, ATTN_GARG, 0, PITCH_NORM ); |
|
m_painSoundTime = gpGlobals->time + RANDOM_FLOAT( 2.5, 4 ); |
|
} |
|
} |
|
|
|
bitsDamageType &= GARG_DAMAGE; |
|
|
|
if ( bitsDamageType == 0) |
|
{ |
|
if ( pev->dmgtime != gpGlobals->time || (RANDOM_LONG(0,100) < 20) ) |
|
{ |
|
UTIL_Ricochet( ptr->vecEndPos, RANDOM_FLOAT(0.5,1.5) ); |
|
pev->dmgtime = gpGlobals->time; |
|
// if ( RANDOM_LONG(0,100) < 25 ) |
|
// EMIT_SOUND_DYN( ENT(pev), CHAN_BODY, pRicSounds[ RANDOM_LONG(0,ARRAYSIZE(pRicSounds)-1) ], 1.0, ATTN_NORM, 0, PITCH_NORM ); |
|
} |
|
flDamage = 0; |
|
} |
|
|
|
CBaseMonster::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType ); |
|
|
|
} |
|
|
|
|
|
|
|
int CGargantua::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) |
|
{ |
|
ALERT( at_aiconsole, "CGargantua::TakeDamage\n"); |
|
|
|
if ( IsAlive() ) |
|
{ |
|
if ( !(bitsDamageType & GARG_DAMAGE) ) |
|
flDamage *= 0.01; |
|
if ( bitsDamageType & DMG_BLAST ) |
|
SetConditions( bits_COND_LIGHT_DAMAGE ); |
|
} |
|
|
|
return CBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType ); |
|
} |
|
|
|
|
|
void CGargantua::DeathEffect( void ) |
|
{ |
|
int i; |
|
UTIL_MakeVectors(pev->angles); |
|
Vector deathPos = pev->origin + gpGlobals->v_forward * 100; |
|
|
|
// Create a spiral of streaks |
|
CSpiral::Create( deathPos, (pev->absmax.z - pev->absmin.z) * 0.6, 125, 1.5 ); |
|
|
|
Vector position = pev->origin; |
|
position.z += 32; |
|
for ( i = 0; i < 7; i+=2 ) |
|
{ |
|
SpawnExplosion( position, 70, (i * 0.3), 60 + (i*20) ); |
|
position.z += 15; |
|
} |
|
|
|
CBaseEntity *pSmoker = CBaseEntity::Create( "env_smoker", pev->origin, g_vecZero, NULL ); |
|
pSmoker->pev->health = 1; // 1 smoke balls |
|
pSmoker->pev->scale = 46; // 4.6X normal size |
|
pSmoker->pev->dmg = 0; // 0 radial distribution |
|
pSmoker->pev->nextthink = gpGlobals->time + 2.5; // Start in 2.5 seconds |
|
} |
|
|
|
|
|
void CGargantua::Killed( entvars_t *pevAttacker, int iGib ) |
|
{ |
|
EyeOff(); |
|
UTIL_Remove( m_pEyeGlow ); |
|
m_pEyeGlow = NULL; |
|
CBaseMonster::Killed( pevAttacker, GIB_NEVER ); |
|
} |
|
|
|
//========================================================= |
|
// CheckMeleeAttack1 |
|
// Garg swipe attack |
|
// |
|
//========================================================= |
|
BOOL CGargantua::CheckMeleeAttack1( float flDot, float flDist ) |
|
{ |
|
// ALERT(at_aiconsole, "CheckMelee(%f, %f)\n", flDot, flDist); |
|
|
|
if (flDot >= 0.7) |
|
{ |
|
if (flDist <= GARG_ATTACKDIST) |
|
return TRUE; |
|
} |
|
return FALSE; |
|
} |
|
|
|
|
|
// Flame thrower madness! |
|
BOOL CGargantua::CheckMeleeAttack2( float flDot, float flDist ) |
|
{ |
|
// ALERT(at_aiconsole, "CheckMelee(%f, %f)\n", flDot, flDist); |
|
|
|
if ( gpGlobals->time > m_flameTime ) |
|
{ |
|
if (flDot >= 0.8 && flDist > GARG_ATTACKDIST) |
|
{ |
|
if ( flDist <= GARG_FLAME_LENGTH ) |
|
return TRUE; |
|
} |
|
} |
|
return FALSE; |
|
} |
|
|
|
|
|
//========================================================= |
|
// CheckRangeAttack1 |
|
// flDot is the cos of the angle of the cone within which |
|
// the attack can occur. |
|
//========================================================= |
|
// |
|
// Stomp attack |
|
// |
|
//========================================================= |
|
BOOL CGargantua::CheckRangeAttack1( float flDot, float flDist ) |
|
{ |
|
if ( gpGlobals->time > m_seeTime ) |
|
{ |
|
if (flDot >= 0.7 && flDist > GARG_ATTACKDIST) |
|
{ |
|
return TRUE; |
|
} |
|
} |
|
return FALSE; |
|
} |
|
|
|
|
|
|
|
|
|
//========================================================= |
|
// HandleAnimEvent - catches the monster-specific messages |
|
// that occur when tagged animation frames are played. |
|
//========================================================= |
|
void CGargantua::HandleAnimEvent(MonsterEvent_t *pEvent) |
|
{ |
|
switch( pEvent->event ) |
|
{ |
|
case GARG_AE_SLASH_LEFT: |
|
{ |
|
// HACKHACK!!! |
|
CBaseEntity *pHurt = GargantuaCheckTraceHullAttack( GARG_ATTACKDIST + 10.0, gSkillData.gargantuaDmgSlash, DMG_SLASH ); |
|
if (pHurt) |
|
{ |
|
if ( pHurt->pev->flags & (FL_MONSTER|FL_CLIENT) ) |
|
{ |
|
pHurt->pev->punchangle.x = -30; // pitch |
|
pHurt->pev->punchangle.y = -30; // yaw |
|
pHurt->pev->punchangle.z = 30; // roll |
|
//UTIL_MakeVectors(pev->angles); // called by CheckTraceHullAttack |
|
pHurt->pev->velocity = pHurt->pev->velocity - gpGlobals->v_right * 100; |
|
} |
|
EMIT_SOUND_DYN ( edict(), CHAN_WEAPON, pAttackHitSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackHitSounds)-1) ], 1.0, ATTN_NORM, 0, 50 + RANDOM_LONG(0,15) ); |
|
} |
|
else // Play a random attack miss sound |
|
EMIT_SOUND_DYN ( edict(), CHAN_WEAPON, pAttackMissSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackMissSounds)-1) ], 1.0, ATTN_NORM, 0, 50 + RANDOM_LONG(0,15) ); |
|
|
|
Vector forward; |
|
UTIL_MakeVectorsPrivate( pev->angles, forward, NULL, NULL ); |
|
} |
|
break; |
|
|
|
case GARG_AE_RIGHT_FOOT: |
|
case GARG_AE_LEFT_FOOT: |
|
UTIL_ScreenShake( pev->origin, 4.0, 3.0, 1.0, 750 ); |
|
EMIT_SOUND_DYN ( edict(), CHAN_BODY, pFootSounds[ RANDOM_LONG(0,ARRAYSIZE(pFootSounds)-1) ], 1.0, ATTN_GARG, 0, PITCH_NORM + RANDOM_LONG(-10,10) ); |
|
break; |
|
|
|
case GARG_AE_STOMP: |
|
StompAttack(); |
|
m_seeTime = gpGlobals->time + 12; |
|
break; |
|
|
|
case GARG_AE_BREATHE: |
|
EMIT_SOUND_DYN ( edict(), CHAN_VOICE, pBreatheSounds[ RANDOM_LONG(0,ARRAYSIZE(pBreatheSounds)-1) ], 1.0, ATTN_GARG, 0, PITCH_NORM + RANDOM_LONG(-10,10) ); |
|
break; |
|
|
|
default: |
|
CBaseMonster::HandleAnimEvent(pEvent); |
|
break; |
|
} |
|
} |
|
|
|
|
|
//========================================================= |
|
// CheckTraceHullAttack - expects a length to trace, amount |
|
// of damage to do, and damage type. Returns a pointer to |
|
// the damaged entity in case the monster wishes to do |
|
// other stuff to the victim (punchangle, etc) |
|
// Used for many contact-range melee attacks. Bites, claws, etc. |
|
|
|
// Overridden for Gargantua because his swing starts lower as |
|
// a percentage of his height (otherwise he swings over the |
|
// players head) |
|
//========================================================= |
|
CBaseEntity* CGargantua::GargantuaCheckTraceHullAttack(float flDist, int iDamage, int iDmgType) |
|
{ |
|
TraceResult tr; |
|
|
|
UTIL_MakeVectors( pev->angles ); |
|
Vector vecStart = pev->origin; |
|
vecStart.z += 64; |
|
Vector vecEnd = vecStart + (gpGlobals->v_forward * flDist) - (gpGlobals->v_up * flDist * 0.3); |
|
|
|
UTIL_TraceHull( vecStart, vecEnd, dont_ignore_monsters, head_hull, ENT(pev), &tr ); |
|
|
|
if ( tr.pHit ) |
|
{ |
|
CBaseEntity *pEntity = CBaseEntity::Instance( tr.pHit ); |
|
|
|
if ( iDamage > 0 ) |
|
{ |
|
pEntity->TakeDamage( pev, pev, iDamage, iDmgType ); |
|
} |
|
|
|
return pEntity; |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
|
|
Schedule_t *CGargantua::GetScheduleOfType( int Type ) |
|
{ |
|
// HACKHACK - turn off the flames if they are on and garg goes scripted / dead |
|
if ( FlameIsOn() ) |
|
FlameDestroy(); |
|
|
|
switch( Type ) |
|
{ |
|
case SCHED_MELEE_ATTACK2: |
|
return slGargFlame; |
|
case SCHED_MELEE_ATTACK1: |
|
return slGargSwipe; |
|
break; |
|
} |
|
|
|
return CBaseMonster::GetScheduleOfType( Type ); |
|
} |
|
|
|
|
|
void CGargantua::StartTask( Task_t *pTask ) |
|
{ |
|
switch ( pTask->iTask ) |
|
{ |
|
case TASK_FLAME_SWEEP: |
|
FlameCreate(); |
|
m_flWaitFinished = gpGlobals->time + pTask->flData; |
|
m_flameTime = gpGlobals->time + 6; |
|
m_flameX = 0; |
|
m_flameY = 0; |
|
break; |
|
|
|
case TASK_SOUND_ATTACK: |
|
if ( RANDOM_LONG(0,100) < 30 ) |
|
EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, pAttackSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackSounds)-1) ], 1.0, ATTN_GARG, 0, PITCH_NORM ); |
|
TaskComplete(); |
|
break; |
|
|
|
case TASK_DIE: |
|
m_flWaitFinished = gpGlobals->time + 1.6; |
|
DeathEffect(); |
|
// FALL THROUGH |
|
default: |
|
CBaseMonster::StartTask( pTask ); |
|
break; |
|
} |
|
} |
|
|
|
//========================================================= |
|
// RunTask |
|
//========================================================= |
|
void CGargantua::RunTask( Task_t *pTask ) |
|
{ |
|
switch ( pTask->iTask ) |
|
{ |
|
case TASK_DIE: |
|
if ( gpGlobals->time > m_flWaitFinished ) |
|
{ |
|
pev->renderfx = kRenderFxExplode; |
|
pev->rendercolor.x = 255; |
|
pev->rendercolor.y = 0; |
|
pev->rendercolor.z = 0; |
|
StopAnimation(); |
|
pev->nextthink = gpGlobals->time + 0.15; |
|
SetThink( &CBaseEntity::SUB_Remove ); |
|
int i; |
|
int parts = MODEL_FRAMES( gGargGibModel ); |
|
for ( i = 0; i < 10; i++ ) |
|
{ |
|
CGib *pGib = GetClassPtr( (CGib *)NULL ); |
|
|
|
pGib->Spawn( GARG_GIB_MODEL ); |
|
|
|
int bodyPart = 0; |
|
if ( parts > 1 ) |
|
bodyPart = RANDOM_LONG( 0, pev->body-1 ); |
|
|
|
pGib->pev->body = bodyPart; |
|
pGib->m_bloodColor = BLOOD_COLOR_YELLOW; |
|
pGib->m_material = matNone; |
|
pGib->pev->origin = pev->origin; |
|
pGib->pev->velocity = UTIL_RandomBloodVector() * RANDOM_FLOAT( 300, 500 ); |
|
pGib->pev->nextthink = gpGlobals->time + 1.25; |
|
pGib->SetThink( &CBaseEntity::SUB_FadeOut ); |
|
} |
|
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin ); |
|
WRITE_BYTE( TE_BREAKMODEL); |
|
|
|
// position |
|
WRITE_COORD( pev->origin.x ); |
|
WRITE_COORD( pev->origin.y ); |
|
WRITE_COORD( pev->origin.z ); |
|
|
|
// size |
|
WRITE_COORD( 200 ); |
|
WRITE_COORD( 200 ); |
|
WRITE_COORD( 128 ); |
|
|
|
// velocity |
|
WRITE_COORD( 0 ); |
|
WRITE_COORD( 0 ); |
|
WRITE_COORD( 0 ); |
|
|
|
// randomization |
|
WRITE_BYTE( 200 ); |
|
|
|
// Model |
|
WRITE_SHORT( gGargGibModel ); //model id# |
|
|
|
// # of shards |
|
WRITE_BYTE( 50 ); |
|
|
|
// duration |
|
WRITE_BYTE( 20 );// 3.0 seconds |
|
|
|
// flags |
|
|
|
WRITE_BYTE( BREAK_FLESH ); |
|
MESSAGE_END(); |
|
|
|
return; |
|
} |
|
else |
|
CBaseMonster::RunTask(pTask); |
|
break; |
|
|
|
case TASK_FLAME_SWEEP: |
|
if ( gpGlobals->time > m_flWaitFinished ) |
|
{ |
|
FlameDestroy(); |
|
TaskComplete(); |
|
FlameControls( 0, 0 ); |
|
SetBoneController( 0, 0 ); |
|
SetBoneController( 1, 0 ); |
|
} |
|
else |
|
{ |
|
BOOL cancel = FALSE; |
|
|
|
Vector angles = g_vecZero; |
|
|
|
FlameUpdate(); |
|
CBaseEntity *pEnemy = m_hEnemy; |
|
if ( pEnemy ) |
|
{ |
|
Vector org = pev->origin; |
|
org.z += 64; |
|
Vector dir = pEnemy->BodyTarget(org) - org; |
|
angles = UTIL_VecToAngles( dir ); |
|
angles.x = -angles.x; |
|
angles.y -= pev->angles.y; |
|
if ( dir.Length() > 400 ) |
|
cancel = TRUE; |
|
} |
|
if ( fabs(angles.y) > 60 ) |
|
cancel = TRUE; |
|
|
|
if ( cancel ) |
|
{ |
|
m_flWaitFinished -= 0.5; |
|
m_flameTime -= 0.5; |
|
} |
|
// FlameControls( angles.x + 2 * sin(gpGlobals->time*8), angles.y + 28 * sin(gpGlobals->time*8.5) ); |
|
FlameControls( angles.x, angles.y ); |
|
} |
|
break; |
|
|
|
default: |
|
CBaseMonster::RunTask( pTask ); |
|
break; |
|
} |
|
} |
|
|
|
|
|
class CSmoker : public CBaseEntity |
|
{ |
|
public: |
|
void Spawn( void ); |
|
void Think( void ); |
|
}; |
|
|
|
LINK_ENTITY_TO_CLASS( env_smoker, CSmoker ); |
|
|
|
void CSmoker::Spawn( void ) |
|
{ |
|
pev->movetype = MOVETYPE_NONE; |
|
pev->nextthink = gpGlobals->time; |
|
pev->solid = SOLID_NOT; |
|
UTIL_SetSize(pev, g_vecZero, g_vecZero ); |
|
pev->effects |= EF_NODRAW; |
|
pev->angles = g_vecZero; |
|
} |
|
|
|
|
|
void CSmoker::Think( void ) |
|
{ |
|
// lots of smoke |
|
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin ); |
|
WRITE_BYTE( TE_SMOKE ); |
|
WRITE_COORD( pev->origin.x + RANDOM_FLOAT( -pev->dmg, pev->dmg )); |
|
WRITE_COORD( pev->origin.y + RANDOM_FLOAT( -pev->dmg, pev->dmg )); |
|
WRITE_COORD( pev->origin.z); |
|
WRITE_SHORT( g_sModelIndexSmoke ); |
|
WRITE_BYTE( RANDOM_LONG(pev->scale, pev->scale * 1.1) ); |
|
WRITE_BYTE( RANDOM_LONG(8,14) ); // framerate |
|
MESSAGE_END(); |
|
|
|
pev->health--; |
|
if ( pev->health > 0 ) |
|
pev->nextthink = gpGlobals->time + RANDOM_FLOAT(0.1, 0.2); |
|
else |
|
UTIL_Remove( this ); |
|
} |
|
|
|
|
|
void CSpiral::Spawn( void ) |
|
{ |
|
pev->movetype = MOVETYPE_NONE; |
|
pev->nextthink = gpGlobals->time; |
|
pev->solid = SOLID_NOT; |
|
UTIL_SetSize(pev, g_vecZero, g_vecZero ); |
|
pev->effects |= EF_NODRAW; |
|
pev->angles = g_vecZero; |
|
} |
|
|
|
|
|
CSpiral *CSpiral::Create( const Vector &origin, float height, float radius, float duration ) |
|
{ |
|
if ( duration <= 0 ) |
|
return NULL; |
|
|
|
CSpiral *pSpiral = GetClassPtr( (CSpiral *)NULL ); |
|
pSpiral->Spawn(); |
|
pSpiral->pev->dmgtime = pSpiral->pev->nextthink; |
|
pSpiral->pev->origin = origin; |
|
pSpiral->pev->scale = radius; |
|
pSpiral->pev->dmg = height; |
|
pSpiral->pev->speed = duration; |
|
pSpiral->pev->health = 0; |
|
pSpiral->pev->angles = g_vecZero; |
|
|
|
return pSpiral; |
|
} |
|
|
|
#define SPIRAL_INTERVAL 0.1 //025 |
|
|
|
void CSpiral::Think( void ) |
|
{ |
|
float time = gpGlobals->time - pev->dmgtime; |
|
|
|
while ( time > SPIRAL_INTERVAL ) |
|
{ |
|
Vector position = pev->origin; |
|
Vector direction = Vector(0,0,1); |
|
|
|
float fraction = 1.0 / pev->speed; |
|
|
|
float radius = (pev->scale * pev->health) * fraction; |
|
|
|
position.z += (pev->health * pev->dmg) * fraction; |
|
pev->angles.y = (pev->health * 360 * 8) * fraction; |
|
UTIL_MakeVectors( pev->angles ); |
|
position = position + gpGlobals->v_forward * radius; |
|
direction = (direction + gpGlobals->v_forward).Normalize(); |
|
|
|
StreakSplash( position, Vector(0,0,1), RANDOM_LONG(8,11), 20, RANDOM_LONG(50,150), 400 ); |
|
|
|
// Jeez, how many counters should this take ? :) |
|
pev->dmgtime += SPIRAL_INTERVAL; |
|
pev->health += SPIRAL_INTERVAL; |
|
time -= SPIRAL_INTERVAL; |
|
} |
|
|
|
pev->nextthink = gpGlobals->time; |
|
|
|
if ( pev->health >= pev->speed ) |
|
UTIL_Remove( this ); |
|
} |
|
|
|
|
|
// HACKHACK Cut and pasted from explode.cpp |
|
void SpawnExplosion( Vector center, float randomRange, float time, int magnitude ) |
|
{ |
|
KeyValueData kvd; |
|
char buf[128]; |
|
|
|
center.x += RANDOM_FLOAT( -randomRange, randomRange ); |
|
center.y += RANDOM_FLOAT( -randomRange, randomRange ); |
|
|
|
CBaseEntity *pExplosion = CBaseEntity::Create( "env_explosion", center, g_vecZero, NULL ); |
|
sprintf( buf, "%3d", magnitude ); |
|
kvd.szKeyName = "iMagnitude"; |
|
kvd.szValue = buf; |
|
pExplosion->KeyValue( &kvd ); |
|
pExplosion->pev->spawnflags |= SF_ENVEXPLOSION_NODAMAGE; |
|
|
|
pExplosion->Spawn(); |
|
pExplosion->SetThink( &CBaseEntity::SUB_CallUseToggle ); |
|
pExplosion->pev->nextthink = gpGlobals->time + time; |
|
} |
|
|
|
|
|
|
|
#endif
|
|
|