1771 lines
49 KiB
C++
1771 lines
49 KiB
C++
/***
|
|
*
|
|
* Copyright (c) 1996-2001, Valve LLC. All rights reserved.
|
|
*
|
|
* This product contains software technology licensed from Id
|
|
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
|
* All Rights Reserved.
|
|
*
|
|
* This source code contains proprietary and confidential information of
|
|
* Valve LLC and its suppliers. Access to this code is restricted to
|
|
* persons who have executed a written SDK license with Valve. Any access,
|
|
* use or distribution of this code by or to any unlicensed person is illegal.
|
|
*
|
|
****/
|
|
|
|
//=========================================================
|
|
// Gargantua
|
|
//=========================================================
|
|
#include "extdll.h"
|
|
#include "util.h"
|
|
#include "cmbase.h"
|
|
#include "cmbasemonster.h"
|
|
#include "skill.h"
|
|
#include "nodes.h"
|
|
#include "monsters.h"
|
|
#include "schedule.h"
|
|
#include "customentity.h"
|
|
#include "weapons.h"
|
|
#include "effects.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
|
|
|
|
|
|
// 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, edict_t *owner );
|
|
|
|
// Spiral Effect
|
|
class CSpiral : public CMBaseEntity
|
|
{
|
|
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 );
|
|
};
|
|
|
|
class CStomp : public CMBaseEntity
|
|
{
|
|
public:
|
|
void Spawn( void );
|
|
void Think( void );
|
|
static CStomp *StompCreate( const Vector &origin, const Vector &end, float speed, float damage );
|
|
|
|
private:
|
|
// UNDONE: re-use this sprite list instead of creating new ones all the time
|
|
// CSprite *m_pSprites[ STOMP_SPRITE_COUNT ];
|
|
};
|
|
|
|
CStomp *CStomp::StompCreate( const Vector &origin, const Vector &end, float speed, float damage )
|
|
{
|
|
CStomp *pStomp = CreateClassPtr( (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->pev->dmg = damage;
|
|
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 * gpGlobals->frametime);
|
|
|
|
UTIL_TraceHull( vecStart, vecEnd, dont_ignore_monsters, head_hull, ENT(pev), &tr );
|
|
|
|
if ( tr.pHit && tr.pHit != pev->owner )
|
|
{
|
|
edict_t *pEntity = tr.pHit;
|
|
entvars_t *pevOwner = pev;
|
|
if ( pev->owner )
|
|
pevOwner = VARS(pev->owner);
|
|
|
|
if (pEntity->v.takedamage)
|
|
{
|
|
if (UTIL_IsPlayer(pEntity))
|
|
UTIL_TakeDamage(pEntity, pev, pevOwner, pev->dmg, DMG_SONIC);
|
|
else if (pEntity->v.euser4 != NULL)
|
|
{
|
|
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pEntity));
|
|
pMonster->TakeDamage(pev, pevOwner, pev->dmg, DMG_SONIC);
|
|
}
|
|
else
|
|
UTIL_TakeDamageExternal(pEntity, pev, pevOwner, pev->dmg, DMG_SONIC);
|
|
}
|
|
}
|
|
|
|
// Accelerate the effect
|
|
pev->speed = pev->speed + (gpGlobals->frametime) * pev->framerate;
|
|
pev->framerate = pev->framerate + (gpGlobals->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++ )
|
|
{
|
|
CMSprite *pSprite = CMSprite::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( &CMSprite::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->edict());
|
|
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();
|
|
}
|
|
|
|
const char *CMGargantua::pAttackHitSounds[] =
|
|
{
|
|
"zombie/claw_strike1.wav",
|
|
"zombie/claw_strike2.wav",
|
|
"zombie/claw_strike3.wav",
|
|
};
|
|
|
|
const char *CMGargantua::pBeamAttackSounds[] =
|
|
{
|
|
"garg/gar_flameoff1.wav",
|
|
"garg/gar_flameon1.wav",
|
|
"garg/gar_flamerun1.wav",
|
|
};
|
|
|
|
|
|
const char *CMGargantua::pAttackMissSounds[] =
|
|
{
|
|
"zombie/claw_miss1.wav",
|
|
"zombie/claw_miss2.wav",
|
|
};
|
|
|
|
const char *CMGargantua::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 *CMGargantua::pFootSounds[] =
|
|
{
|
|
"garg/gar_step1.wav",
|
|
"garg/gar_step2.wav",
|
|
};
|
|
|
|
|
|
const char *CMGargantua::pIdleSounds[] =
|
|
{
|
|
"garg/gar_idle1.wav",
|
|
"garg/gar_idle2.wav",
|
|
"garg/gar_idle3.wav",
|
|
"garg/gar_idle4.wav",
|
|
"garg/gar_idle5.wav",
|
|
};
|
|
|
|
|
|
const char *CMGargantua::pAttackSounds[] =
|
|
{
|
|
"garg/gar_attack1.wav",
|
|
"garg/gar_attack2.wav",
|
|
"garg/gar_attack3.wav",
|
|
};
|
|
|
|
const char *CMGargantua::pAlertSounds[] =
|
|
{
|
|
"garg/gar_alert1.wav",
|
|
"garg/gar_alert2.wav",
|
|
"garg/gar_alert3.wav",
|
|
};
|
|
|
|
const char *CMGargantua::pPainSounds[] =
|
|
{
|
|
"garg/gar_pain1.wav",
|
|
"garg/gar_pain2.wav",
|
|
"garg/gar_pain3.wav",
|
|
};
|
|
|
|
const char *CMGargantua::pStompSounds[] =
|
|
{
|
|
"garg/gar_stomp1.wav",
|
|
};
|
|
|
|
const char *CMGargantua::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( CMGargantua )
|
|
{
|
|
slGargFlame,
|
|
slGargSwipe,
|
|
};
|
|
|
|
IMPLEMENT_CUSTOM_SCHEDULES( CMGargantua, CMBaseMonster );
|
|
|
|
|
|
void CMGargantua::EyeOn( int level )
|
|
{
|
|
m_eyeBrightness = level;
|
|
}
|
|
|
|
|
|
void CMGargantua::EyeOff( void )
|
|
{
|
|
m_eyeBrightness = 0;
|
|
}
|
|
|
|
|
|
void CMGargantua::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 CMGargantua::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, gSkillData.gargantuaDmgStomp );
|
|
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 CMGargantua :: 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] = CMBeam::BeamCreate( GARG_BEAM_SPRITE_NAME, 240 );
|
|
else
|
|
m_pFlame[i] = CMBeam::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 );
|
|
}
|
|
}
|
|
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 CMGargantua :: 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 CMGargantua :: 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, Classify(), DMG_BURN );
|
|
FlameDamage( vecStart, trace.vecEndPos, pev, pev, gSkillData.gargantuaDmgFire, Classify(), 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 CMGargantua :: FlameDamage( Vector vecStart, Vector vecEnd, entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int iClassIgnore, int bitsDamageType )
|
|
{
|
|
edict_t *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->v.takedamage != DAMAGE_NO )
|
|
{
|
|
vecSpot = UTIL_BodyTarget( pEntity, g_vecZero ); //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 )
|
|
{// 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->v.classname ) );
|
|
if (tr.flFraction != 1.0)
|
|
{
|
|
ClearMultiDamage();
|
|
if ( UTIL_IsPlayer( pEntity ) )
|
|
UTIL_TraceAttack( pEntity, pevInflictor, flAdjustedDamage, (tr.vecEndPos - vecSrc).Normalize(), &tr, bitsDamageType );
|
|
else if ( pEntity->v.euser4 != NULL )
|
|
{
|
|
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pEntity));
|
|
pMonster->TraceAttack( pevInflictor, flAdjustedDamage, (tr.vecEndPos - vecSrc).Normalize(), &tr, bitsDamageType );
|
|
}
|
|
else
|
|
UTIL_TraceAttack( pEntity, pevInflictor, flAdjustedDamage, (tr.vecEndPos - vecSrc).Normalize(), &tr, bitsDamageType );
|
|
ApplyMultiDamage( pevInflictor, pevAttacker );
|
|
}
|
|
else
|
|
{
|
|
if ( UTIL_IsPlayer( pEntity ) )
|
|
UTIL_TakeDamage( pEntity, pevInflictor, pevAttacker, flAdjustedDamage, bitsDamageType );
|
|
else if ( pEntity->v.euser4 != NULL )
|
|
{
|
|
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pEntity));
|
|
pMonster->TakeDamage( pevInflictor, pevAttacker, flAdjustedDamage, bitsDamageType );
|
|
}
|
|
else
|
|
UTIL_TakeDamageExternal( pEntity, pevInflictor, pevAttacker, flAdjustedDamage, bitsDamageType );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void CMGargantua :: 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]->edict() );
|
|
m_pFlame[i] = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void CMGargantua :: 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 CMGargantua :: Classify ( void )
|
|
{
|
|
if ( m_iClassifyOverride == -1 ) // helper
|
|
return CLASS_NONE;
|
|
else if ( m_iClassifyOverride > 0 )
|
|
return m_iClassifyOverride; // override
|
|
|
|
return CLASS_ALIEN_MONSTER;
|
|
}
|
|
|
|
//=========================================================
|
|
// SetYawSpeed - allows each sequence to have a different
|
|
// turn rate associated with it.
|
|
//=========================================================
|
|
void CMGargantua :: 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 CMGargantua :: Spawn()
|
|
{
|
|
Precache( );
|
|
|
|
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/garg.mdl"));
|
|
UTIL_SetSize( pev, Vector( -32, -32, 0 ), Vector( 32, 32, 64 ) );
|
|
|
|
pev->solid = SOLID_SLIDEBOX;
|
|
pev->movetype = MOVETYPE_STEP;
|
|
m_bloodColor = !m_bloodColor ? BLOOD_COLOR_YELLOW : m_bloodColor;
|
|
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 = CMSprite::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;
|
|
|
|
pev->classname = MAKE_STRING( "monster_gargantua" );
|
|
if ( strlen( STRING( m_szMonsterName ) ) == 0 )
|
|
{
|
|
// default name
|
|
m_szMonsterName = MAKE_STRING( "Gargantua" );
|
|
}
|
|
}
|
|
|
|
|
|
//=========================================================
|
|
// Precache - precaches all resources this monster needs
|
|
//=========================================================
|
|
void CMGargantua :: Precache()
|
|
{
|
|
PRECACHE_MODEL("models/garg.mdl");
|
|
PRECACHE_MODEL( GARG_EYE_SPRITE_NAME );
|
|
PRECACHE_MODEL( GARG_BEAM_SPRITE_NAME );
|
|
PRECACHE_MODEL( GARG_BEAM_SPRITE2 );
|
|
gStompSprite = PRECACHE_MODELINDEX( GARG_STOMP_SPRITE_NAME );
|
|
gGargGibModel = PRECACHE_MODELINDEX( GARG_GIB_MODEL );
|
|
PRECACHE_SOUND( GARG_STOMP_BUZZ_SOUND );
|
|
|
|
PRECACHE_SOUND_ARRAY(pAttackHitSounds);
|
|
PRECACHE_SOUND_ARRAY(pBeamAttackSounds);
|
|
PRECACHE_SOUND_ARRAY(pAttackMissSounds);
|
|
PRECACHE_SOUND_ARRAY(pRicSounds);
|
|
PRECACHE_SOUND_ARRAY(pFootSounds);
|
|
PRECACHE_SOUND_ARRAY(pIdleSounds);
|
|
PRECACHE_SOUND_ARRAY(pAlertSounds);
|
|
PRECACHE_SOUND_ARRAY(pPainSounds);
|
|
PRECACHE_SOUND_ARRAY(pAttackSounds);
|
|
PRECACHE_SOUND_ARRAY(pStompSounds);
|
|
PRECACHE_SOUND_ARRAY(pBreatheSounds);
|
|
}
|
|
|
|
|
|
void CMGargantua::TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType )
|
|
{
|
|
ALERT( at_aiconsole, "CMGargantua::TraceAttack\n");
|
|
|
|
if ( !IsAlive() )
|
|
{
|
|
CMBaseMonster::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 *= (1.00f - gSkillData.gargantuaArmor); // in here...?
|
|
}
|
|
|
|
CMBaseMonster::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType );
|
|
|
|
}
|
|
|
|
|
|
int CMGargantua::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
|
|
{
|
|
if ( IsAlive() )
|
|
{
|
|
if ( !(bitsDamageType & GARG_DAMAGE) )
|
|
flDamage *= (1.00f - gSkillData.gargantuaArmor); // This is for mods that don't use explosives of any kind or do not work with the gargantua.
|
|
|
|
// Always set
|
|
SetConditions( bits_COND_LIGHT_DAMAGE );
|
|
}
|
|
|
|
return CMBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType );
|
|
}
|
|
|
|
|
|
void CMGargantua::DeathEffect( void )
|
|
{
|
|
int i;
|
|
UTIL_MakeVectors(pev->angles);
|
|
Vector deathPos = pev->origin + gpGlobals->v_up * 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), pev->owner );
|
|
position.z += 15;
|
|
}
|
|
|
|
// 1 smoke balls
|
|
// 4.6X normal size
|
|
// 0 radial distribution
|
|
// start in 2.5 seconds
|
|
SmokeCreate( pev->origin, 1, 46, 0, 2.5 );
|
|
}
|
|
|
|
|
|
void CMGargantua::Killed( entvars_t *pevAttacker, int iGib )
|
|
{
|
|
EyeOff();
|
|
UTIL_Remove( m_pEyeGlow->edict() );
|
|
m_pEyeGlow = NULL;
|
|
CMBaseMonster::Killed( pevAttacker, GIB_NEVER );
|
|
}
|
|
|
|
//=========================================================
|
|
// CheckMeleeAttack1
|
|
// Garg swipe attack
|
|
//
|
|
//=========================================================
|
|
BOOL CMGargantua::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 CMGargantua::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 CMGargantua::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 CMGargantua::HandleAnimEvent(MonsterEvent_t *pEvent)
|
|
{
|
|
switch( pEvent->event )
|
|
{
|
|
case GARG_AE_SLASH_LEFT:
|
|
{
|
|
// HACKHACK!!!
|
|
edict_t *pHurt = GargantuaCheckTraceHullAttack( GARG_ATTACKDIST + 10.0, gSkillData.gargantuaDmgSlash, DMG_SLASH );
|
|
if (pHurt)
|
|
{
|
|
if ( pHurt->v.flags & (FL_MONSTER|FL_CLIENT) )
|
|
{
|
|
pHurt->v.punchangle.x = -30; // pitch
|
|
pHurt->v.punchangle.y = -30; // yaw
|
|
pHurt->v.punchangle.z = 30; // roll
|
|
//UTIL_MakeVectors(pev->angles); // called by CheckTraceHullAttack
|
|
pHurt->v.velocity = pHurt->v.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:
|
|
CMBaseMonster::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)
|
|
//=========================================================
|
|
edict_t *CMGargantua::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 )
|
|
{
|
|
if ( iDamage > 0 )
|
|
{
|
|
if ( UTIL_IsPlayer( tr.pHit ) )
|
|
UTIL_TakeDamage( tr.pHit, pev, pev, iDamage, iDmgType );
|
|
else if ( tr.pHit->v.euser4 != NULL )
|
|
{
|
|
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(tr.pHit));
|
|
pMonster->TakeDamage( pev, pev, iDamage, iDmgType );
|
|
}
|
|
else
|
|
UTIL_TakeDamageExternal( tr.pHit, pev, pev, iDamage, iDmgType );
|
|
}
|
|
|
|
return tr.pHit;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
Schedule_t *CMGargantua::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 CMBaseMonster::GetScheduleOfType( Type );
|
|
}
|
|
|
|
|
|
void CMGargantua::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:
|
|
CMBaseMonster::StartTask( pTask );
|
|
break;
|
|
}
|
|
}
|
|
|
|
//=========================================================
|
|
// RunTask
|
|
//=========================================================
|
|
void CMGargantua::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( &CMGargantua::SUB_Remove );
|
|
int i;
|
|
int parts = MODEL_FRAMES( gGargGibModel );
|
|
for ( i = 0; i < 10; i++ )
|
|
{
|
|
CMGib *pGib = CreateClassPtr( (CMGib *)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( &CMGib::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
|
|
CMBaseMonster::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();
|
|
CMBaseEntity *pEnemy = CMBaseEntity::Instance( m_hEnemy.Get() );
|
|
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:
|
|
CMBaseMonster::RunTask( pTask );
|
|
break;
|
|
}
|
|
}
|
|
|
|
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 = CreateClassPtr( (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->edict() );
|
|
}
|
|
|
|
|
|
void SpawnExplosion( Vector center, float randomRange, float time, int magnitude, edict_t *owner )
|
|
{
|
|
/* no need for this
|
|
KeyValueData kvd;
|
|
char buf[128];
|
|
|
|
CMBaseEntity *pExplosion = CreateClassPtr((CEnvExplosion *)NULL); // CMBaseEntity::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( &CMBaseEntity::SUB_CallUseToggle );
|
|
pExplosion->pev->nextthink = gpGlobals->time + time;
|
|
*/
|
|
|
|
center.x += RANDOM_FLOAT( -randomRange, randomRange );
|
|
center.y += RANDOM_FLOAT( -randomRange, randomRange );
|
|
|
|
// explode.h
|
|
ExplosionCreate( center, g_vecZero, owner, magnitude, SF_ENVEXPLOSION_NODAMAGE, time );
|
|
}
|
|
|
|
/*
|
|
* The Sven Co-op's monster code was recreated from scratch.
|
|
* They do not contain their unique new attacks... YET. -Giegue
|
|
* */
|
|
|
|
//=========================================================
|
|
// Baby Gargantua
|
|
//=========================================================
|
|
const float BABYGARG_ATTACKDIST = 65.0;
|
|
#define BABYGARG_FLAME_LENGTH 180
|
|
|
|
|
|
const char *CMBabyGargantua::pBeamAttackSounds[] =
|
|
{
|
|
"babygarg/gar_flameoff1.wav",
|
|
"babygarg/gar_flameon1.wav",
|
|
"babygarg/gar_flamerun1.wav",
|
|
};
|
|
|
|
const char *CMBabyGargantua::pFootSounds[] =
|
|
{
|
|
"babygarg/gar_step1.wav",
|
|
"babygarg/gar_step2.wav",
|
|
};
|
|
|
|
const char *CMBabyGargantua::pIdleSounds[] =
|
|
{
|
|
"babygarg/gar_idle1.wav",
|
|
"babygarg/gar_idle2.wav",
|
|
"babygarg/gar_idle3.wav",
|
|
"babygarg/gar_idle4.wav",
|
|
"babygarg/gar_idle5.wav",
|
|
};
|
|
|
|
const char *CMBabyGargantua::pAttackSounds[] =
|
|
{
|
|
"babygarg/gar_attack1.wav",
|
|
"babygarg/gar_attack2.wav",
|
|
"babygarg/gar_attack3.wav",
|
|
};
|
|
|
|
const char *CMBabyGargantua::pAlertSounds[] =
|
|
{
|
|
"babygarg/gar_alert1.wav",
|
|
"babygarg/gar_alert2.wav",
|
|
"babygarg/gar_alert3.wav",
|
|
};
|
|
|
|
const char *CMBabyGargantua::pPainSounds[] =
|
|
{
|
|
"babygarg/gar_pain1.wav",
|
|
"babygarg/gar_pain2.wav",
|
|
"babygarg/gar_pain3.wav",
|
|
};
|
|
|
|
const char *CMBabyGargantua::pStompSounds[] =
|
|
{
|
|
"babygarg/gar_stomp1.wav",
|
|
};
|
|
|
|
const char *CMBabyGargantua::pBreatheSounds[] =
|
|
{
|
|
"babygarg/gar_breathe1.wav",
|
|
"babygarg/gar_breathe2.wav",
|
|
"babygarg/gar_breathe3.wav",
|
|
};
|
|
|
|
const char *CMBabyGargantua::pDieSounds[] =
|
|
{
|
|
"babygarg/gar_die1.wav",
|
|
"babygarg/gar_die2.wav",
|
|
};
|
|
|
|
//=========================================================
|
|
// Spawn
|
|
//=========================================================
|
|
void CMBabyGargantua::Spawn()
|
|
{
|
|
Precache( );
|
|
|
|
SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/babygarg.mdl"));
|
|
UTIL_SetSize( pev, Vector( -32, -32, 0 ), Vector( 32, 32, 64 ) );
|
|
|
|
pev->solid = SOLID_SLIDEBOX;
|
|
pev->movetype = MOVETYPE_STEP;
|
|
m_bloodColor = !m_bloodColor ? BLOOD_COLOR_YELLOW : m_bloodColor;
|
|
pev->health = gSkillData.babygargHealth;
|
|
//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();
|
|
|
|
// Sven Co-op uses a modified gargeye1.spr for the eye and stomp effects.
|
|
// To economize precache count, we are going to recycle the normal garg's sprites.
|
|
|
|
m_pEyeGlow = CMSprite::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;
|
|
|
|
pev->classname = MAKE_STRING( "monster_babygarg" );
|
|
if ( strlen( STRING( m_szMonsterName ) ) == 0 )
|
|
{
|
|
// default name
|
|
m_szMonsterName = MAKE_STRING( "Baby Gargantua" );
|
|
}
|
|
}
|
|
|
|
//=========================================================
|
|
// Precache - precaches all resources this monster needs
|
|
//=========================================================
|
|
void CMBabyGargantua::Precache()
|
|
{
|
|
PRECACHE_MODEL("models/babygarg.mdl");
|
|
PRECACHE_MODEL( GARG_EYE_SPRITE_NAME );
|
|
PRECACHE_MODEL( GARG_BEAM_SPRITE_NAME );
|
|
PRECACHE_MODEL( GARG_BEAM_SPRITE2 );
|
|
gStompSprite = PRECACHE_MODELINDEX( GARG_STOMP_SPRITE_NAME );
|
|
gGargGibModel = PRECACHE_MODELINDEX( GARG_GIB_MODEL );
|
|
PRECACHE_SOUND( GARG_STOMP_BUZZ_SOUND );
|
|
|
|
PRECACHE_SOUND_ARRAY(pAttackHitSounds);
|
|
PRECACHE_SOUND_ARRAY(pBeamAttackSounds);
|
|
PRECACHE_SOUND_ARRAY(pAttackMissSounds);
|
|
PRECACHE_SOUND_ARRAY(pRicSounds);
|
|
PRECACHE_SOUND_ARRAY(pFootSounds);
|
|
PRECACHE_SOUND_ARRAY(pIdleSounds);
|
|
PRECACHE_SOUND_ARRAY(pAlertSounds);
|
|
PRECACHE_SOUND_ARRAY(pPainSounds);
|
|
PRECACHE_SOUND_ARRAY(pAttackSounds);
|
|
PRECACHE_SOUND_ARRAY(pStompSounds);
|
|
PRECACHE_SOUND_ARRAY(pBreatheSounds);
|
|
PRECACHE_SOUND_ARRAY(pDieSounds);
|
|
}
|
|
|
|
void CMBabyGargantua::TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType )
|
|
{
|
|
ALERT( at_aiconsole, "CMBabyGargantua::TraceAttack\n");
|
|
|
|
if ( !IsAlive() )
|
|
{
|
|
CMBaseMonster::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType );
|
|
return;
|
|
}
|
|
|
|
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 );
|
|
}
|
|
|
|
// Override Gargantua's specific damage. Baby Garg has no protection from those.
|
|
CMBaseMonster::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType );
|
|
}
|
|
|
|
int CMBabyGargantua::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
|
|
{
|
|
if ( IsAlive() )
|
|
{
|
|
// Always set
|
|
SetConditions( bits_COND_LIGHT_DAMAGE );
|
|
}
|
|
|
|
return CMBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType );
|
|
}
|
|
|
|
//=========================================================
|
|
// HandleAnimEvent - catches the monster-specific messages
|
|
// that occur when tagged animation frames are played.
|
|
//=========================================================
|
|
void CMBabyGargantua::HandleAnimEvent(MonsterEvent_t *pEvent)
|
|
{
|
|
switch( pEvent->event )
|
|
{
|
|
case GARG_AE_SLASH_LEFT:
|
|
{
|
|
// HACKHACK!!!
|
|
edict_t *pHurt = BabyGargCheckTraceHullAttack( BABYGARG_ATTACKDIST + 10.0, gSkillData.babygargDmgSlash, DMG_SLASH );
|
|
if (pHurt)
|
|
{
|
|
if ( pHurt->v.flags & (FL_MONSTER|FL_CLIENT) )
|
|
{
|
|
// Slightly lower numbers for babygarg (-20%)
|
|
pHurt->v.punchangle.x = -24; // pitch
|
|
pHurt->v.punchangle.y = -24; // yaw
|
|
pHurt->v.punchangle.z = 24; // roll
|
|
//UTIL_MakeVectors(pev->angles); // called by CheckTraceHullAttack
|
|
pHurt->v.velocity = pHurt->v.velocity - gpGlobals->v_right * 80;
|
|
}
|
|
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:
|
|
// babygarg does not shake the screen
|
|
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:
|
|
CMBaseMonster::HandleAnimEvent(pEvent);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//=========================================================
|
|
// CheckMeleeAttack1
|
|
// Garg swipe attack
|
|
//=========================================================
|
|
BOOL CMBabyGargantua::CheckMeleeAttack1( float flDot, float flDist )
|
|
{
|
|
// ALERT(at_aiconsole, "CheckMelee(%f, %f)\n", flDot, flDist);
|
|
|
|
if (flDot >= 0.7)
|
|
{
|
|
if (flDist <= BABYGARG_ATTACKDIST)
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
//=========================================================
|
|
// CheckMeleeAttack2
|
|
// Flame thrower madness!
|
|
//=========================================================
|
|
BOOL CMBabyGargantua::CheckMeleeAttack2( float flDot, float flDist )
|
|
{
|
|
// ALERT(at_aiconsole, "CheckMelee(%f, %f)\n", flDot, flDist);
|
|
|
|
if ( gpGlobals->time > m_flameTime )
|
|
{
|
|
if (flDot >= 0.8 && flDist > BABYGARG_ATTACKDIST)
|
|
{
|
|
if ( flDist <= BABYGARG_FLAME_LENGTH )
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
//=========================================================
|
|
// CheckRangeAttack1
|
|
// Stomp attack
|
|
//=========================================================
|
|
BOOL CMBabyGargantua::CheckRangeAttack1( float flDot, float flDist )
|
|
{
|
|
if ( gpGlobals->time > m_seeTime )
|
|
{
|
|
if (flDot >= 0.7 && flDist > BABYGARG_ATTACKDIST)
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
void CMBabyGargantua::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:
|
|
// no death effect for babygarg, but give it a sound
|
|
EMIT_SOUND_DYN ( edict(), CHAN_WEAPON, pDieSounds[ RANDOM_LONG(0,ARRAYSIZE(pDieSounds)-1) ], 1.0, ATTN_GARG, 0, PITCH_NORM );
|
|
default:
|
|
CMBaseMonster::StartTask( pTask );
|
|
break;
|
|
}
|
|
}
|
|
|
|
//=========================================================
|
|
// RunTask
|
|
//=========================================================
|
|
void CMBabyGargantua::RunTask( Task_t *pTask )
|
|
{
|
|
switch ( pTask->iTask )
|
|
{
|
|
// babygarg does not explode upon death
|
|
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();
|
|
CMBaseEntity *pEnemy = CMBaseEntity::Instance( m_hEnemy.Get() );
|
|
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:
|
|
CMBaseMonster::RunTask( pTask );
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CMBabyGargantua::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, gSkillData.babygargDmgStomp );
|
|
UTIL_ScreenShake( pev->origin, 9.6, 80.0, 1.8, 800 ); // -20% "power" to the babygarg's stomp
|
|
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 CMBabyGargantua::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] = CMBeam::BeamCreate( GARG_BEAM_SPRITE_NAME, 120 );
|
|
else
|
|
m_pFlame[i] = CMBeam::BeamCreate( GARG_BEAM_SPRITE2, 70 );
|
|
if ( m_pFlame[i] )
|
|
{
|
|
int attach = i%2;
|
|
// attachment is 0 based in GetAttachment
|
|
GetAttachment( attach+1, posGun, angleGun );
|
|
|
|
Vector vecEnd = (gpGlobals->v_forward * BABYGARG_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 );
|
|
}
|
|
}
|
|
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 CMBabyGargantua::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 * BABYGARG_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.babygargDmgFire, Classify(), DMG_BURN );
|
|
FlameDamage( vecStart, trace.vecEndPos, pev, pev, gSkillData.babygargDmgFire, Classify(), 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 CMBabyGargantua::FlameDestroy( void )
|
|
{
|
|
int i;
|
|
|
|
EMIT_SOUND_DYN ( edict(), CHAN_WEAPON, pBeamAttackSounds[ 0 ], 1.0, ATTN_NORM, 0, PITCH_NORM ); // sound must stop.
|
|
for ( i = 0; i < 4; i++ )
|
|
{
|
|
if ( m_pFlame[i] )
|
|
{
|
|
UTIL_Remove( m_pFlame[i]->edict() );
|
|
m_pFlame[i] = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
//=========================================================
|
|
// 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)
|
|
|
|
// Also overriden for Baby Gargantua to prevent players from
|
|
// dodging the swing attacks by crouching.
|
|
//=========================================================
|
|
edict_t *CMBabyGargantua::BabyGargCheckTraceHullAttack(float flDist, int iDamage, int iDmgType)
|
|
{
|
|
TraceResult tr;
|
|
|
|
UTIL_MakeVectors( pev->angles );
|
|
Vector vecStart = pev->origin;
|
|
vecStart.z += 32;
|
|
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 )
|
|
{
|
|
if ( iDamage > 0 )
|
|
{
|
|
if ( UTIL_IsPlayer( tr.pHit ) )
|
|
UTIL_TakeDamage( tr.pHit, pev, pev, iDamage, iDmgType );
|
|
else if ( tr.pHit->v.euser4 != NULL )
|
|
{
|
|
CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(tr.pHit));
|
|
pMonster->TakeDamage( pev, pev, iDamage, iDmgType );
|
|
}
|
|
else
|
|
UTIL_TakeDamageExternal( tr.pHit, pev, pev, iDamage, iDmgType );
|
|
}
|
|
|
|
return tr.pHit;
|
|
}
|
|
|
|
return NULL;
|
|
}
|