summaryrefslogtreecommitdiff
path: root/game/server/hl1/hl1_npc_gargantua.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/server/hl1/hl1_npc_gargantua.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/server/hl1/hl1_npc_gargantua.cpp')
-rw-r--r--game/server/hl1/hl1_npc_gargantua.cpp1174
1 files changed, 1174 insertions, 0 deletions
diff --git a/game/server/hl1/hl1_npc_gargantua.cpp b/game/server/hl1/hl1_npc_gargantua.cpp
new file mode 100644
index 0000000..c5d8b2c
--- /dev/null
+++ b/game/server/hl1/hl1_npc_gargantua.cpp
@@ -0,0 +1,1174 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Bullseyes act as targets for other NPC's to attack and to trigger
+// events
+//
+// $Workfile: $
+// $Date: $
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "beam_shared.h"
+#include "Sprite.h"
+#include "ai_default.h"
+#include "ai_task.h"
+#include "ai_schedule.h"
+#include "ai_node.h"
+#include "ai_hull.h"
+#include "ai_hint.h"
+#include "ai_memory.h"
+#include "ai_route.h"
+#include "ai_motor.h"
+#include "hl1_npc_gargantua.h"
+#include "soundent.h"
+#include "game.h"
+#include "npcevent.h"
+#include "entitylist.h"
+#include "activitylist.h"
+#include "animation.h"
+#include "basecombatweapon.h"
+#include "IEffects.h"
+#include "vstdlib/random.h"
+#include "engine/IEngineSound.h"
+#include "ammodef.h"
+#include "shake.h"
+#include "decals.h"
+#include "particle_smokegrenade.h"
+#include "gib.h"
+#include "func_break.h"
+#include "hl1_shareddefs.h"
+
+
+extern short g_sModelIndexFireball;
+int gGargGibModel;
+
+//=========================================================
+// Gargantua Monster
+//=========================================================
+#define GARG_ATTACKDIST 120.0f
+
+// 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
+
+
+// Gargantua is immune to any damage but this
+#define GARG_DAMAGE ( DMG_ENERGYBEAM | DMG_CRUSH | DMG_MISSILEDEFENSE | DMG_BLAST )
+#define GARG_EYE_SPRITE_NAME "sprites/gargeye1.vmt"
+#define GARG_BEAM_SPRITE_NAME "sprites/xbeam3.vmt"
+#define GARG_BEAM_SPRITE2 "sprites/xbeam3.vmt"
+#define GARG_STOMP_SPRITE_NAME "sprites/gargeye1.vmt"
+#define GARG_FLAME_LENGTH 330
+#define GARG_GIB_MODEL "models/metalplategibs.mdl"
+
+#define STOMP_SPRITE_COUNT 10
+
+
+#define ATTACH_EYE 1
+
+ConVar sk_gargantua_health ( "sk_gargantua_health", "800" );
+ConVar sk_gargantua_dmg_slash( "sk_gargantua_dmg_slash", "10" );
+ConVar sk_gargantua_dmg_fire ( "sk_gargantua_dmg_fire", "3" );
+ConVar sk_gargantua_dmg_stomp( "sk_gargantua_dmg_stomp", "50" );
+
+enum
+{
+ TASK_SOUND_ATTACK = LAST_SHARED_TASK,
+ TASK_FLAME_SWEEP,
+};
+
+enum
+{
+ SCHED_GARG_FLAME = LAST_SHARED_SCHEDULE,
+ SCHED_GARG_SWIPE,
+ SCHED_GARG_CHASE_ENEMY,
+ SCHED_GARG_CHASE_ENEMY_FAILED,
+};
+
+LINK_ENTITY_TO_CLASS( monster_gargantua, CNPC_Gargantua );
+
+BEGIN_DATADESC( CNPC_Gargantua )
+ DEFINE_FIELD( m_pEyeGlow, FIELD_CLASSPTR ),
+ DEFINE_FIELD( m_eyeBrightness, FIELD_INTEGER ),
+ DEFINE_FIELD( m_seeTime, FIELD_TIME ),
+ DEFINE_FIELD( m_flameTime, FIELD_TIME ),
+ DEFINE_FIELD( m_streakTime, FIELD_TIME ),
+ DEFINE_ARRAY( m_pFlame, FIELD_CLASSPTR, 4 ),
+ DEFINE_FIELD( m_flameX, FIELD_FLOAT ),
+ DEFINE_FIELD( m_flameY, FIELD_FLOAT ),
+ DEFINE_FIELD( m_flDmgTime, FIELD_TIME ),
+ DEFINE_FIELD( m_painSoundTime, FIELD_TIME ),
+END_DATADESC()
+
+static void MoveToGround( Vector *position, CBaseEntity *ignore, const Vector &mins, const Vector &maxs )
+{
+ trace_t tr;
+ // Find point on floor where enemy would stand at chasePosition
+ Vector floor = *position;
+ floor.z -= 1024;
+ UTIL_TraceHull( *position, floor, mins, maxs, MASK_NPCSOLID, ignore, COLLISION_GROUP_NONE, &tr );
+ if ( tr.fraction < 1 )
+ {
+ position->z = tr.endpos.z;
+ }
+}
+
+class CStomp : public CBaseEntity
+{
+ DECLARE_CLASS( CStomp, CBaseEntity );
+
+public:
+ DECLARE_DATADESC();
+
+ virtual void Precache();
+
+ void Spawn( void );
+ void Think( void );
+ static CStomp *StompCreate( Vector &origin, Vector &end, float speed, CBaseEntity* pOwner );
+
+private:
+ Vector m_vecMoveDir;
+ float m_flScale;
+ float m_flSpeed;
+ unsigned int m_uiFramerate;
+ float m_flDmgTime;
+ CBaseEntity* m_pOwner;
+
+// UNDONE: re-use this sprite list instead of creating new ones all the time
+// CSprite *m_pSprites[ STOMP_SPRITE_COUNT ];
+};
+
+BEGIN_DATADESC(CStomp)
+ DEFINE_FIELD( m_vecMoveDir, FIELD_VECTOR ),
+ DEFINE_FIELD( m_flScale, FIELD_FLOAT ),
+ DEFINE_FIELD( m_flSpeed, FIELD_FLOAT ),
+ DEFINE_FIELD( m_uiFramerate, FIELD_INTEGER ),
+ DEFINE_FIELD( m_flDmgTime, FIELD_TIME ),
+ DEFINE_FIELD( m_pOwner, FIELD_CLASSPTR ),
+END_DATADESC()
+
+LINK_ENTITY_TO_CLASS( garg_stomp, CStomp );
+CStomp *CStomp::StompCreate( Vector &origin, Vector &end, float speed, CBaseEntity* pOwner )
+{
+ CStomp *pStomp = (CStomp*)CreateEntityByName( "garg_stomp" );
+
+ pStomp->SetAbsOrigin( origin );
+ Vector dir = (end - origin);
+// pStomp->m_flScale = dir.Length();
+ pStomp->m_flScale = 2048;
+ pStomp->m_vecMoveDir = dir;
+ VectorNormalize( pStomp->m_vecMoveDir );
+ pStomp->m_flSpeed = speed;
+ pStomp->m_pOwner = pOwner;
+ pStomp->Spawn();
+
+ return pStomp;
+
+}
+
+void CStomp::Precache()
+{
+ BaseClass::Precache();
+
+ PrecacheScriptSound( "Garg.Stomp" );
+ PrecacheModel( GARG_STOMP_SPRITE_NAME );
+}
+
+
+void CStomp::Spawn( void )
+{
+ Precache();
+
+ SetNextThink( gpGlobals->curtime );
+ SetClassname( "garg_stomp" );
+ m_flDmgTime = gpGlobals->curtime;
+
+ m_uiFramerate = 30;
+// pev->rendermode = kRenderTransTexture;
+// SetBrightness( 0 );
+ CPASAttenuationFilter filter( this );
+ EmitSound( filter, entindex(), "Garg.Stomp" );
+
+}
+
+
+#define STOMP_INTERVAL 0.025
+
+void CStomp::Think( void )
+{
+ trace_t tr;
+
+ SetNextThink( gpGlobals->curtime + 0.1 );
+
+ // Do damage for this frame
+ Vector vecStart = GetAbsOrigin();
+ vecStart.z += 30;
+ Vector vecEnd = vecStart + (m_vecMoveDir * m_flSpeed * gpGlobals->frametime);
+
+ UTIL_TraceHull( vecStart, vecEnd, Vector(-32, -32, -32), Vector(32, 32, 32), MASK_SOLID, m_pOwner, COLLISION_GROUP_NONE, &tr );
+// NDebugOverlay::Line( vecStart, vecEnd, 0, 255, 0, false, 10.0f );
+
+ if ( tr.m_pEnt )
+ {
+ CBaseEntity *pEntity = tr.m_pEnt;
+ CTakeDamageInfo info( this, this, 50, DMG_SONIC );
+ CalculateMeleeDamageForce( &info, m_vecMoveDir, tr.endpos );
+ pEntity->TakeDamage( info );
+ }
+
+ // Accelerate the effect
+ m_flSpeed += (gpGlobals->frametime) * m_uiFramerate;
+ m_uiFramerate += (gpGlobals->frametime) * 2000;
+
+ // Move and spawn trails
+ if ( gpGlobals->curtime - m_flDmgTime > 0.2f )
+ {
+ m_flDmgTime = gpGlobals->curtime - 0.2f;
+ }
+
+ while ( gpGlobals->curtime - m_flDmgTime > STOMP_INTERVAL )
+ {
+ SetAbsOrigin( GetAbsOrigin() + m_vecMoveDir * m_flSpeed * STOMP_INTERVAL );
+ for ( int i = 0; i < 2; i++ )
+ {
+ CSprite *pSprite = CSprite::SpriteCreate( GARG_STOMP_SPRITE_NAME, GetAbsOrigin(), TRUE );
+ if ( pSprite )
+ {
+ UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() - Vector(0,0,500), MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
+ pSprite->SetAbsOrigin( tr.endpos );
+// pSprite->pev->velocity = Vector(RandomFloat(-200,200),RandomFloat(-200,200),175);
+ pSprite->SetNextThink( gpGlobals->curtime + 0.3 );
+ pSprite->SetThink( &CSprite::SUB_Remove );
+ pSprite->SetTransparency( kRenderTransAdd, 255, 255, 255, 255, kRenderFxFadeFast );
+ }
+ g_pEffects->EnergySplash( tr.endpos, tr.plane.normal );
+ }
+ m_flDmgTime += STOMP_INTERVAL;
+ // Scale has the "life" of this effect
+ m_flScale -= STOMP_INTERVAL * m_flSpeed;
+ if ( m_flScale <= 0 )
+ {
+ // Life has run out
+ UTIL_Remove(this);
+ CPASAttenuationFilter filter( this );
+ StopSound( entindex(), CHAN_STATIC, "Garg.Stomp" );
+ }
+ }
+}
+
+
+//=========================================================================
+// Gargantua
+//=========================================================================
+
+//=========================================================
+// Spawn
+//=========================================================
+void CNPC_Gargantua::Spawn()
+{
+ Precache( );
+
+ SetModel( "models/garg.mdl" );
+
+ SetNavType(NAV_GROUND);
+ SetSolid( SOLID_BBOX );
+ AddSolidFlags( FSOLID_NOT_STANDABLE );
+ SetMoveType( MOVETYPE_STEP );
+
+ Vector vecSurroundingMins( -80, -80, 0 );
+ Vector vecSurroundingMaxs( 80, 80, 214 );
+ CollisionProp()->SetSurroundingBoundsType( USE_SPECIFIED_BOUNDS, &vecSurroundingMins, &vecSurroundingMaxs );
+
+ m_bloodColor = BLOOD_COLOR_GREEN;
+ m_iHealth = sk_gargantua_health.GetFloat();
+ SetViewOffset( Vector ( 0, 0, 96 ) );// taken from mdl file
+ m_flFieldOfView = -0.2;// width of forward view cone ( as a dotproduct result )
+ m_NPCState = NPC_STATE_NONE;
+
+ CapabilitiesAdd( bits_CAP_MOVE_GROUND );
+ CapabilitiesAdd( bits_CAP_INNATE_RANGE_ATTACK1 | bits_CAP_INNATE_MELEE_ATTACK1 | bits_CAP_INNATE_MELEE_ATTACK2 );
+
+ SetHullType( HULL_LARGE );
+ SetHullSizeNormal();
+
+ m_pEyeGlow = CSprite::SpriteCreate( GARG_EYE_SPRITE_NAME, GetAbsOrigin(), FALSE );
+ m_pEyeGlow->SetTransparency( kRenderGlow, 255, 255, 255, 0, kRenderFxNoDissipation );
+ m_pEyeGlow->SetAttachment( this, 1 );
+ EyeOff();
+
+ m_seeTime = gpGlobals->curtime + 5;
+ m_flameTime = gpGlobals->curtime + 2;
+
+ NPCInit();
+
+ BaseClass::Spawn();
+
+ // Give garg a healthy free knowledge.
+ GetEnemies()->SetFreeKnowledgeDuration( 59.0f );
+}
+
+//=========================================================
+// Precache - precaches all resources this monster needs
+//=========================================================
+void CNPC_Gargantua::Precache()
+{
+ PrecacheModel("models/garg.mdl");
+ PrecacheModel( GARG_EYE_SPRITE_NAME );
+ PrecacheModel( GARG_BEAM_SPRITE_NAME );
+ PrecacheModel( GARG_BEAM_SPRITE2 );
+ //gStompSprite = PRECACHE_MODEL( GARG_STOMP_SPRITE_NAME );
+ gGargGibModel = PrecacheModel( GARG_GIB_MODEL );
+
+ PrecacheScriptSound( "Garg.AttackHit" );
+ PrecacheScriptSound( "Garg.AttackMiss" );
+ PrecacheScriptSound( "Garg.Footstep" );
+ PrecacheScriptSound( "Garg.Breath" );
+ PrecacheScriptSound( "Garg.Attack" );
+ PrecacheScriptSound( "Garg.Pain" );
+ PrecacheScriptSound( "Garg.BeamAttackOn" );
+ PrecacheScriptSound( "Garg.BeamAttackRun" );
+ PrecacheScriptSound( "Garg.BeamAttackOff" );
+ PrecacheScriptSound( "Garg.StompSound" );
+}
+
+
+Class_T CNPC_Gargantua::Classify ( void )
+{
+ return CLASS_ALIEN_MONSTER;
+}
+
+void CNPC_Gargantua::PrescheduleThink( void )
+{
+ if ( !HasCondition( COND_SEE_ENEMY ) )
+ {
+ m_seeTime = gpGlobals->curtime + 5;
+ EyeOff();
+ }
+ else
+ {
+ EyeOn( 200 );
+ }
+
+ EyeUpdate();
+}
+
+float CNPC_Gargantua::MaxYawSpeed ( void )
+{
+ float ys = 60;
+
+ switch ( GetActivity() )
+ {
+ 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;
+ }
+
+ return ys;
+}
+
+int CNPC_Gargantua::MeleeAttack1Conditions( float flDot, float flDist )
+{
+ if (flDot >= 0.7)
+ {
+ if ( flDist <= GARG_ATTACKDIST )
+ {
+ return COND_CAN_MELEE_ATTACK1;
+ }
+ }
+
+ return COND_NONE;
+}
+
+
+// Flame thrower madness!
+int CNPC_Gargantua::MeleeAttack2Conditions( float flDot, float flDist )
+{
+ if ( gpGlobals->curtime > m_flameTime )
+ {
+ if ( flDot >= 0.8 )
+ {
+ if ( flDist > GARG_ATTACKDIST )
+ {
+ if ( flDist <= GARG_FLAME_LENGTH )
+ return COND_CAN_MELEE_ATTACK2;
+ }
+ }
+ }
+
+ return COND_NONE;
+}
+
+//=========================================================
+// CheckRangeAttack1
+// flDot is the cos of the angle of the cone within which
+// the attack can occur.
+//=========================================================
+//
+// Stomp attack
+//
+//=========================================================
+int CNPC_Gargantua::RangeAttack1Conditions( float flDot, float flDist )
+{
+ if ( gpGlobals->curtime > m_seeTime )
+ {
+ if ( flDot >= 0.7 )
+ {
+ if ( flDist > GARG_ATTACKDIST )
+ {
+ return COND_CAN_RANGE_ATTACK1;
+ }
+ }
+ }
+
+ return COND_NONE;
+}
+
+//=========================================================
+// 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* CNPC_Gargantua::GargantuaCheckTraceHullAttack(float flDist, int iDamage, int iDmgType)
+{
+ trace_t tr;
+
+ Vector vForward, vUp;
+ AngleVectors( GetAbsAngles(), &vForward, NULL, &vUp );
+
+ Vector vecStart = GetAbsOrigin();
+ vecStart.z += 64;
+ Vector vecEnd = vecStart + ( vForward * flDist) - ( vUp * flDist * 0.3);
+
+ //UTIL_TraceHull( vecStart, vecEnd, dont_ignore_monsters, head_hull, ENT(pev), &tr );
+
+ UTIL_TraceEntity( this, GetAbsOrigin(), vecEnd, MASK_SOLID, &tr );
+
+ if ( tr.m_pEnt )
+ {
+ CBaseEntity *pEntity = tr.m_pEnt;
+
+ if ( iDamage > 0 )
+ {
+ CTakeDamageInfo info( this, this, iDamage, iDmgType );
+ CalculateMeleeDamageForce( &info, vForward, tr.endpos );
+ pEntity->TakeDamage( info );
+ }
+
+ return pEntity;
+ }
+
+ return NULL;
+}
+
+void CNPC_Gargantua::HandleAnimEvent( animevent_t *pEvent )
+{
+ CPASAttenuationFilter filter( this );
+
+ switch( pEvent->event )
+ {
+ case GARG_AE_SLASH_LEFT:
+ {
+ // HACKHACK!!!
+ CBaseEntity *pHurt = GargantuaCheckTraceHullAttack( GARG_ATTACKDIST + 10.0, sk_gargantua_dmg_slash.GetFloat(), DMG_SLASH );
+
+ if (pHurt)
+ {
+ if ( pHurt->GetFlags() & ( FL_NPC | FL_CLIENT ) )
+ {
+ pHurt->ViewPunch( QAngle( -30, -30, 30 ) );
+
+ Vector vRight;
+ AngleVectors( GetAbsAngles(), NULL, &vRight, NULL );
+ pHurt->SetAbsVelocity( pHurt->GetAbsVelocity() - vRight * 100 );
+ }
+
+ EmitSound( filter, entindex(), "Garg.AttackHit" );
+ }
+ else // Play a random attack miss sound
+ {
+ EmitSound( filter, entindex(),"Garg.AttackMiss" );
+ }
+ }
+ break;
+
+ case GARG_AE_RIGHT_FOOT:
+ case GARG_AE_LEFT_FOOT:
+
+ UTIL_ScreenShake( GetAbsOrigin(), 4.0, 3.0, 1.0, 1500, SHAKE_START );
+ EmitSound( filter, entindex(), "Garg.Footstep" );
+ break;
+
+ case GARG_AE_STOMP:
+ StompAttack();
+ m_seeTime = gpGlobals->curtime + 12;
+ break;
+
+ case GARG_AE_BREATHE:
+ EmitSound( filter, entindex(), "Garg.Breath" );
+ break;
+
+ default:
+ BaseClass::HandleAnimEvent(pEvent);
+ break;
+ }
+}
+
+int CNPC_Gargantua::TranslateSchedule( int scheduleType )
+{
+ //TEMP TEMP
+ if ( FlameIsOn() )
+ FlameDestroy();
+
+ switch( scheduleType )
+ {
+ case SCHED_MELEE_ATTACK2:
+ return SCHED_GARG_FLAME;
+ case SCHED_MELEE_ATTACK1:
+ return SCHED_GARG_SWIPE;
+
+ case SCHED_CHASE_ENEMY:
+ return SCHED_GARG_CHASE_ENEMY;
+
+ case SCHED_CHASE_ENEMY_FAILED:
+ return SCHED_GARG_CHASE_ENEMY_FAILED;
+
+ case SCHED_ALERT_STAND:
+ return SCHED_CHASE_ENEMY;
+
+ break;
+ }
+
+ return BaseClass::TranslateSchedule( scheduleType );
+}
+
+void CNPC_Gargantua::StartTask( const Task_t *pTask )
+{
+ switch ( pTask->iTask )
+ {
+ case TASK_FLAME_SWEEP:
+
+ //TEMP TEMP
+ FlameCreate();
+ m_flWaitFinished = gpGlobals->curtime + pTask->flTaskData;
+ m_flameTime = gpGlobals->curtime + 6;
+ m_flameX = 0;
+ m_flameY = 0;
+ break;
+
+ case TASK_SOUND_ATTACK:
+
+ if ( random->RandomInt(0,100) < 30 )
+ {
+ CPASAttenuationFilter filter( this );
+ EmitSound( filter, entindex(), "Garg.Attack" );
+ }
+
+ TaskComplete();
+ break;
+
+ case TASK_DIE:
+ m_flWaitFinished = gpGlobals->curtime + 1.6;
+ DeathEffect();
+ // FALL THROUGH
+ default:
+ BaseClass::StartTask( pTask );
+ break;
+ }
+}
+
+bool CNPC_Gargantua::ShouldGib( const CTakeDamageInfo &info )
+{
+ return false;
+}
+
+//=========================================================
+// RunTask
+//=========================================================
+void CNPC_Gargantua::RunTask( const Task_t *pTask )
+{
+ switch ( pTask->iTask )
+ {
+ case TASK_DIE:
+
+ if ( gpGlobals->curtime > m_flWaitFinished )
+ {
+ //TEMP TEMP
+ m_nRenderFX = kRenderFxExplode;
+ SetRenderColor( 255, 0, 0 , 255 );
+ StopAnimation();
+ SetNextThink( gpGlobals->curtime + 0.15 );
+ SetThink( &CBaseEntity::SUB_Remove );
+
+ int i;
+
+ int parts = modelinfo->GetModelFrameCount( modelinfo->GetModel( gGargGibModel ) );
+
+ for ( i = 0; i < 10; i++ )
+ {
+ CGib *pGib = CREATE_ENTITY( CGib, "gib" );
+
+ pGib->Spawn( GARG_GIB_MODEL);
+
+ int bodyPart = 0;
+
+ if ( parts > 1 )
+ bodyPart = random->RandomInt( 0, parts-1 );
+
+ pGib->SetBodygroup( 0, bodyPart );
+ pGib->SetBloodColor( BLOOD_COLOR_YELLOW );
+ pGib->m_material = matNone;
+ pGib->SetAbsOrigin( GetAbsOrigin() );
+ pGib->SetAbsVelocity( UTIL_RandomBloodVector() * random->RandomFloat( 300, 500 ) );
+
+ pGib->SetNextThink( gpGlobals->curtime + 1.25 );
+ pGib->SetThink( &CBaseEntity::SUB_FadeOut );
+ }
+
+ Vector vecSize = Vector( 200, 200, 128 );
+ CPVSFilter filter( GetAbsOrigin() );
+ te->BreakModel( filter, 0.0, GetAbsOrigin(), vec3_angle, vecSize, vec3_origin,
+ gGargGibModel, 200, 50, 3.0, BREAK_FLESH );
+
+ return;
+ }
+ else
+ BaseClass::RunTask( pTask );
+ break;
+
+ case TASK_FLAME_SWEEP:
+ if ( gpGlobals->curtime > m_flWaitFinished )
+ {
+ //TEMP TEMP
+ FlameDestroy();
+ TaskComplete();
+ FlameControls( 0, 0 );
+ SetBoneController( 0, 0 );
+ SetBoneController( 1, 0 );
+ }
+ else
+ {
+ bool cancel = false;
+
+ QAngle angles = QAngle( 0, 0, 0 );
+
+ //TEMP TEMP
+ FlameUpdate();
+ CBaseEntity *pEnemy = GetEnemy();
+
+ if ( pEnemy )
+ {
+ Vector org = GetAbsOrigin();
+ org.z += 64;
+ Vector dir = pEnemy->BodyTarget(org) - org;
+
+ VectorAngles( dir, angles );
+ angles.x = -angles.x;
+ angles.y -= GetAbsAngles().y;
+
+ if ( dir.Length() > 400 )
+ cancel = true;
+ }
+ if ( fabs(angles.y) > 60 )
+ cancel = true;
+
+ if ( cancel )
+ {
+ m_flWaitFinished -= 0.5;
+ m_flameTime -= 0.5;
+ }
+
+ //TEMP TEMP
+ //FlameControls( angles.x + 2 * sin(gpGlobals->curtime*8), angles.y + 28 * sin(gpGlobals->curtime*8.5) );
+ FlameControls( angles.x, angles.y );
+ }
+ break;
+
+ default:
+ BaseClass::RunTask( pTask );
+ break;
+ }
+}
+
+void CNPC_Gargantua::FlameCreate( void )
+{
+ int i;
+ Vector posGun;
+ QAngle angleGun;
+
+ trace_t trace;
+
+ Vector vForward;
+
+ AngleVectors( GetAbsAngles(), &vForward );
+
+ for ( i = 0; i < 4; i++ )
+ {
+ if ( i < 2 )
+ m_pFlame[i] = CBeam::BeamCreate( GARG_BEAM_SPRITE_NAME, 24.0 );
+ else
+ m_pFlame[i] = CBeam::BeamCreate( GARG_BEAM_SPRITE2, 14.0 );
+ if ( m_pFlame[i] )
+ {
+ int attach = i%2;
+ // attachment is 0 based in GetAttachment
+ GetAttachment( attach+1, posGun, angleGun );
+
+ Vector vecEnd = ( vForward * GARG_FLAME_LENGTH) + posGun;
+ //UTIL_TraceLine( posGun, vecEnd, dont_ignore_monsters, edict(), &trace );
+
+ UTIL_TraceLine ( posGun, vecEnd, MASK_SOLID, this, COLLISION_GROUP_NONE, &trace);
+// NDebugOverlay::Line( posGun, vecEnd, 255, 255, 255, false, 10.0f );
+
+ m_pFlame[i]->PointEntInit( trace.endpos, this );
+ 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]->SetBeamFlags( FBEAM_SHADEIN );
+ m_pFlame[i]->SetScrollRate( 20 );
+ // attachment is 1 based in SetEndAttachment
+ m_pFlame[i]->SetEndAttachment( attach + 2 );
+ CSoundEnt::InsertSound( SOUND_COMBAT, posGun, 384, 0.3 );
+ }
+ }
+
+ CPASAttenuationFilter filter4( this );
+ EmitSound( filter4, entindex(), "Garg.BeamAttackOn" );
+ EmitSound( filter4, entindex(), "Garg.BeamAttackRun" );
+}
+
+
+void CNPC_Gargantua::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 CNPC_Gargantua::FlameUpdate( void )
+{
+ int i;
+ static float offset[2] = { 60, -60 };
+ trace_t trace;
+ Vector vecStart;
+ QAngle angleGun;
+ BOOL streaks = FALSE;
+
+ Vector vForward;
+
+ for ( i = 0; i < 2; i++ )
+ {
+ if ( m_pFlame[i] )
+ {
+ QAngle vecAim = GetAbsAngles();
+ vecAim.x += -m_flameX;
+ vecAim.y += m_flameY;
+
+ AngleVectors( vecAim, &vForward );
+
+ GetAttachment( i + 2, vecStart, angleGun );
+ Vector vecEnd = vecStart + ( vForward * GARG_FLAME_LENGTH); // - offset[i] * gpGlobals->v_right;
+
+ UTIL_TraceLine ( vecStart, vecEnd, MASK_SOLID, this, COLLISION_GROUP_NONE, &trace);
+
+ m_pFlame[i]->SetStartPos( trace.endpos );
+ m_pFlame[i+2]->SetStartPos( (vecStart * 0.6) + (trace.endpos * 0.4) );
+
+ if ( trace.fraction != 1.0 && gpGlobals->curtime > m_streakTime )
+ {
+ g_pEffects->Sparks( trace.endpos, 1, 1, &trace.plane.normal );
+ streaks = TRUE;
+ UTIL_DecalTrace( &trace, "SmallScorch" );
+ }
+ // RadiusDamage( trace.vecEndPos, pev, pev, gSkillData.gargantuaDmgFire, CLASS_ALIEN_MONSTER, DMG_BURN );
+ FlameDamage( vecStart, trace.endpos, this, this, sk_gargantua_dmg_fire.GetFloat(), CLASS_ALIEN_MONSTER, DMG_BURN );
+
+ CBroadcastRecipientFilter filter;
+ GetAttachment(i + 2, vecStart, angleGun);
+ te->DynamicLight( filter, 0.0, &vecStart, 255, 0, 0, 0, 48, 0.2, 150 );
+ }
+ }
+ if ( streaks )
+ m_streakTime = gpGlobals->curtime;
+}
+
+
+
+void CNPC_Gargantua::FlameDamage( Vector vecStart, Vector vecEnd, CBaseEntity *pevInflictor, CBaseEntity *pevAttacker, float flDamage, int iClassIgnore, int bitsDamageType )
+{
+ CBaseEntity *pEntity = NULL;
+ trace_t tr;
+ float flAdjustedDamage;
+ Vector vecSpot;
+
+ Vector vecMid = (vecStart + vecEnd) * 0.5;
+
+// float searchRadius = (vecStart - vecMid).Length();
+ float searchRadius = GARG_FLAME_LENGTH;
+ float maxDamageRadius = searchRadius / 2.0;
+
+ Vector vecAim = (vecEnd - vecStart);
+
+ VectorNormalize( vecAim );
+
+ // iterate on all entities in the vicinity.
+ while ((pEntity = gEntList.FindEntityInSphere( pEntity, GetAbsOrigin(), searchRadius )) != NULL)
+ {
+
+ if ( pEntity->m_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 ( vecStart, vecSpot, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr);
+// NDebugOverlay::Line( vecStart, vecSpot, 0, 255, 0, false, 10.0f );
+
+ if ( tr.fraction == 1.0 || tr.m_pEnt == pEntity )
+ {// the explosion can 'see' this entity, so hurt them!
+ // decrease damage for an ent that's farther from the flame.
+ dist = ( vecSrc - tr.endpos ).Length();
+
+ if (dist > maxDamageRadius)
+ {
+ flAdjustedDamage = flDamage - (dist - maxDamageRadius) * 0.4;
+ if (flAdjustedDamage <= 0)
+ continue;
+ }
+ else
+ {
+ flAdjustedDamage = flDamage;
+ }
+
+ // ALERT( at_console, "hit %s\n", STRING( pEntity->pev->classname ) );
+ if (tr.fraction != 1.0)
+ {
+ ClearMultiDamage( );
+
+ Vector vDir = (tr.endpos - vecSrc);
+ VectorNormalize( vDir );
+
+ CTakeDamageInfo info( pevInflictor, pevAttacker, flAdjustedDamage, bitsDamageType );
+ CalculateMeleeDamageForce( &info, vDir, tr.endpos );
+ pEntity->DispatchTraceAttack( info, vDir, &tr );
+ ApplyMultiDamage();
+ }
+ else
+ {
+ pEntity->TakeDamage( CTakeDamageInfo( pevInflictor, pevAttacker, flAdjustedDamage, bitsDamageType ) );
+ }
+ }
+ }
+ }
+}
+
+
+void CNPC_Gargantua::FlameDestroy( void )
+{
+ int i;
+
+ CPASAttenuationFilter filter4( this );
+ EmitSound( filter4, entindex(), "Garg.BeamAttackOff" );
+
+ for ( i = 0; i < 4; i++ )
+ {
+ if ( m_pFlame[i] )
+ {
+ UTIL_Remove( m_pFlame[i] );
+ m_pFlame[i] = NULL;
+ }
+ }
+}
+
+
+void CNPC_Gargantua::EyeOn( int level )
+{
+ m_eyeBrightness = level;
+}
+
+void CNPC_Gargantua::EyeOff( void )
+{
+ m_eyeBrightness = 0;
+}
+
+void CNPC_Gargantua::EyeUpdate( void )
+{
+ if ( m_pEyeGlow )
+ {
+ m_pEyeGlow->SetBrightness( UTIL_Approach( m_eyeBrightness, m_pEyeGlow->GetBrightness(), 26 ), 0.5f );
+ if ( m_pEyeGlow->GetBrightness() == 0 )
+ {
+ m_pEyeGlow->AddEffects( EF_NODRAW );
+ }
+ else
+ {
+ m_pEyeGlow->RemoveEffects( EF_NODRAW );
+ }
+ }
+}
+
+
+void CNPC_Gargantua::StompAttack( void )
+{
+ trace_t trace;
+
+ Vector vecForward;
+ AngleVectors(GetAbsAngles(), &vecForward );
+ Vector vecStart = GetAbsOrigin() + Vector(0,0,60) + 35 * vecForward;
+
+ CBaseEntity* pPlayer = GetEnemy();
+ if ( !pPlayer )
+ return;
+
+ Vector vecAim = pPlayer->GetAbsOrigin() - GetAbsOrigin();
+ VectorNormalize( vecAim );
+ Vector vecEnd = (vecAim * 1024) + vecStart;
+
+ UTIL_TraceLine( vecStart, vecEnd, MASK_SOLID, this, COLLISION_GROUP_NONE, &trace );
+// NDebugOverlay::Line( vecStart, vecEnd, 255, 0, 0, false, 10.0f );
+
+ CStomp::StompCreate( vecStart, trace.endpos, 0, this );
+ UTIL_ScreenShake( GetAbsOrigin(), 12.0, 100.0, 2.0, 1000, SHAKE_START );
+ CPASAttenuationFilter filter( this );
+ EmitSound( filter, entindex(), "Garg.StompSound" );
+
+ UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() - Vector(0,0,20), MASK_SOLID, this, COLLISION_GROUP_NONE, &trace );
+ if ( trace.fraction < 1.0 )
+ {
+ UTIL_DecalTrace( &trace, "SmallScorch" );
+ }
+}
+
+void CNPC_Gargantua::DeathEffect( void )
+{
+ int i;
+
+ Vector vForward;
+ AngleVectors( GetAbsAngles(), &vForward );
+ Vector deathPos = GetAbsOrigin() + vForward * 100;
+
+ Vector position = GetAbsOrigin();
+ position.z += 32;
+
+ CPASFilter filter( GetAbsOrigin() );
+ for ( i = 0; i < 7; i++)
+ {
+ te->Explosion( filter, i * 0.2, &GetAbsOrigin(), g_sModelIndexFireball, 10, 15, TE_EXPLFLAG_NONE, 100, 0 );
+ position.z += 15;
+ }
+
+ UTIL_Smoke(GetAbsOrigin(),random->RandomInt(10, 15), 10);
+ UTIL_ScreenShake( GetAbsOrigin(), 25.0, 100.0, 5.0, 1000, SHAKE_START );
+
+}
+
+void CNPC_Gargantua::Event_Killed( const CTakeDamageInfo &info )
+{
+ EyeOff();
+ UTIL_Remove( m_pEyeGlow );
+ m_pEyeGlow = NULL;
+ BaseClass::Event_Killed( info );
+ m_takedamage = DAMAGE_NO;
+}
+
+void CNPC_Gargantua::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
+{
+ CTakeDamageInfo subInfo = info;
+
+ if ( !IsAlive() )
+ {
+ BaseClass::TraceAttack( subInfo, vecDir, ptr, pAccumulator );
+ return;
+ }
+
+ // UNDONE: Hit group specific damage?
+ if ( subInfo.GetDamageType() & ( GARG_DAMAGE | DMG_BLAST ) )
+ {
+ if ( m_painSoundTime < gpGlobals->curtime )
+ {
+ CPASAttenuationFilter filter( this );
+ EmitSound( filter, entindex(), "Garg.Pain" );
+
+ m_painSoundTime = gpGlobals->curtime + random->RandomFloat( 2.5, 4 );
+ }
+ }
+
+ int bitsDamageType = subInfo.GetDamageType();
+
+ bitsDamageType &= GARG_DAMAGE;
+
+ subInfo.SetDamageType( bitsDamageType );
+
+ if ( subInfo.GetDamageType() == 0 )
+ {
+ if ( m_flDmgTime != gpGlobals->curtime || (random->RandomInt( 0, 100 ) < 20) )
+ {
+ g_pEffects->Ricochet(ptr->endpos, -vecDir );
+ m_flDmgTime = gpGlobals->curtime;
+ }
+
+ subInfo.SetDamage( 0 );
+ }
+
+ BaseClass::TraceAttack( subInfo, vecDir, ptr, pAccumulator );
+}
+
+int CNPC_Gargantua::OnTakeDamage_Alive( const CTakeDamageInfo &info )
+{
+ if( GetState() == NPC_STATE_SCRIPT )
+ {
+ // Invulnerable while scripted. This fixes the problem where garg wouldn't
+ // explode in C2A1 because for some reason he wouldn't die while scripted, he'd
+ // only freeze in place. Now he's just immune until he gets to the script and stops.
+ return 0;
+ }
+
+ CTakeDamageInfo subInfo = info;
+
+ float flDamage = subInfo.GetDamage();
+
+ if ( IsAlive() )
+ {
+ if ( !(subInfo.GetDamageType() & GARG_DAMAGE) )
+ {
+ flDamage *= 0.01;
+ subInfo.SetDamage( flDamage );
+ }
+ if ( subInfo.GetDamageType() & DMG_BLAST )
+ {
+ SetCondition( COND_LIGHT_DAMAGE );
+ }
+ }
+
+ return BaseClass::OnTakeDamage_Alive( subInfo );
+}
+
+AI_BEGIN_CUSTOM_NPC( monster_gargantua, CNPC_Gargantua )
+
+DECLARE_TASK ( TASK_SOUND_ATTACK )
+DECLARE_TASK ( TASK_FLAME_SWEEP )
+
+ //=========================================================
+ // > SCHED_GARG_FLAME
+ //=========================================================
+ DEFINE_SCHEDULE
+ (
+ SCHED_GARG_FLAME,
+
+ " Tasks"
+ " TASK_STOP_MOVING 0"
+ " TASK_FACE_ENEMY 0"
+ " TASK_SOUND_ATTACK 0"
+ " TASK_SET_ACTIVITY ACTIVITY:ACT_MELEE_ATTACK2"
+ " TASK_FLAME_SWEEP 4.5"
+ " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
+ )
+
+ //=========================================================
+ // > SCHED_GARG_SWIPE
+ //=========================================================
+ DEFINE_SCHEDULE
+ (
+ SCHED_GARG_SWIPE,
+
+ " Tasks"
+ " TASK_STOP_MOVING 0"
+ " TASK_FACE_ENEMY 0"
+ " TASK_MELEE_ATTACK1 0"
+ " "
+ " Interrupts"
+ " COND_CAN_MELEE_ATTACK2"
+ )
+
+ DEFINE_SCHEDULE
+ (
+ SCHED_GARG_CHASE_ENEMY,
+
+ " Tasks"
+ " TASK_STOP_MOVING 0"
+ " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_GARG_CHASE_ENEMY_FAILED"
+ " TASK_GET_CHASE_PATH_TO_ENEMY 300"
+ " TASK_RUN_PATH 0"
+ " TASK_WAIT_FOR_MOVEMENT 0"
+ " TASK_FACE_ENEMY 0"
+ ""
+ " Interrupts"
+ " COND_NEW_ENEMY"
+ " COND_ENEMY_DEAD"
+ " COND_ENEMY_UNREACHABLE"
+ " COND_CAN_RANGE_ATTACK1"
+ " COND_CAN_MELEE_ATTACK1"
+ " COND_CAN_RANGE_ATTACK2"
+ " COND_CAN_MELEE_ATTACK2"
+ " COND_TOO_CLOSE_TO_ATTACK"
+ " COND_LOST_ENEMY"
+ );
+
+ DEFINE_SCHEDULE
+ (
+ SCHED_GARG_CHASE_ENEMY_FAILED,
+
+ " Tasks"
+ " TASK_SET_ROUTE_SEARCH_TIME 2" // Spend 2 seconds trying to build a path if stuck
+ " TASK_GET_PATH_TO_RANDOM_NODE 180"
+ " TASK_WALK_PATH 0"
+ " TASK_WAIT_FOR_MOVEMENT 0"
+ ""
+ " Interrupts"
+ " COND_NEW_ENEMY"
+ " COND_ENEMY_DEAD"
+ " COND_CAN_RANGE_ATTACK1"
+ " COND_CAN_MELEE_ATTACK1"
+ " COND_CAN_RANGE_ATTACK2"
+ " COND_CAN_MELEE_ATTACK2"
+ );
+
+AI_END_CUSTOM_NPC()