Modified source engine (2017) developed by valve and leaked in 2020. Not for commercial purporses
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.

340 lines
9.3 KiB

#include "cbase.h"
#include "asw_boomer.h"
#include "npcevent.h"
#include "asw_gamerules.h"
#include "asw_shareddefs.h"
#include "asw_fx_shared.h"
#include "asw_grenade_cluster.h"
#include "world.h"
#include "particle_parse.h"
#include "asw_util_shared.h"
#include "ai_squad.h"
#include "asw_marine.h"
#include "gib.h"
#include "te_effect_dispatch.h"
#include "asw_ai_behavior.h"
#include "props_shared.h"
#include "asw_player.h"
#include "asw_achievements.h"
#include "asw_marine_resource.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
LINK_ENTITY_TO_CLASS( asw_boomer, CASW_Boomer );
IMPLEMENT_SERVERCLASS_ST( CASW_Boomer, DT_ASW_Boomer )
SendPropBool( SENDINFO( m_bInflated ) ),
END_SEND_TABLE()
BEGIN_DATADESC( CASW_Boomer )
DEFINE_EMBEDDEDBYREF( m_pExpresser ),
DEFINE_FIELD( m_bInflating, FIELD_BOOLEAN ),
END_DATADESC()
ConVar asw_boomer_health( "asw_boomer_health", "800", FCVAR_CHEAT );
ConVar asw_boomer_inflate_speed( "asw_boomer_inflate_speed", "6.0f", FCVAR_CHEAT );
ConVar asw_boomer_inflate_debug( "asw_boomer_inflate_debug", "1.0f", FCVAR_CHEAT );
extern ConVar asw_alien_debug_death_style;
extern ConVar asw_debug_alien_damage;
int ACT_RUN_INFLATED;
int ACT_IDLE_INFLATED;
int ACT_MELEE_ATTACK1_INFLATED;
int ACT_DEATH_FIRE_INFLATED;
int AE_BOOMER_INFLATED;
CASW_Boomer::CASW_Boomer()
{
m_pszAlienModelName = "models/aliens/boomer/boomer.mdl";
m_bInflated = false;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input:
// Output:
//-----------------------------------------------------------------------------
void CASW_Boomer::Spawn( void )
{
SetHullType( HULL_LARGE );
BaseClass::Spawn();
SetHullType( HULL_LARGE );
SetCollisionGroup( ASW_COLLISION_GROUP_ALIEN );
SetHealthByDifficultyLevel();
CapabilitiesAdd( bits_CAP_MOVE_GROUND | bits_CAP_INNATE_MELEE_ATTACK1 | bits_CAP_INNATE_MELEE_ATTACK2 );
// "Health" "435"
// "WalkSpeed" "45"
// "RunSpeed" "254"
SetIdealState( NPC_STATE_ALERT );
m_bNeverRagdoll = true;
}
void CASW_Boomer::SetHealthByDifficultyLevel()
{
int iHealth = MAX( 25, ASWGameRules()->ModifyAlienHealthBySkillLevel( asw_boomer_health.GetInt() ) );
if ( asw_debug_alien_damage.GetBool() )
Msg( "Setting boomer's initial health to %d\n", iHealth );
SetHealth( iHealth );
SetMaxHealth( iHealth );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input:
// Output:
//-----------------------------------------------------------------------------
void CASW_Boomer::Precache( void )
{
PrecacheParticleSystem ( "boomer_explode" );
PrecacheParticleSystem ( "joint_goo" );
PrecacheModel( "models/aliens/boomer/boomerLegA.mdl");
PrecacheModel( "models/aliens/boomer/boomerLegB.mdl");
PrecacheModel( "models/aliens/boomer/boomerLegC.mdl");
PrecacheScriptSound( "ASW_Boomer.Death_Explode" );
PrecacheScriptSound( "ASW_Boomer.Death_Gib" );
BaseClass::Precache();
}
//-----------------------------------------------------------------------------
// Purpose:
// Input:
// Output:
//-----------------------------------------------------------------------------
float CASW_Boomer::MaxYawSpeed( void )
{
if ( GetActivity() == ACT_STRAFE_LEFT || GetActivity() == ACT_STRAFE_RIGHT )
{
return 0.0f;
}
return 32.0f;// * GetMovementSpeedModifier();
}
//-----------------------------------------------------------------------------
// Purpose:
// Input:
// Output:
//-----------------------------------------------------------------------------
void CASW_Boomer::HandleAnimEvent( animevent_t *pEvent )
{
int nEvent = pEvent->Event();
if ( nEvent == AE_BOOMER_INFLATED )
{
m_bInflated = true;
return;
}
BaseClass::HandleAnimEvent( pEvent );
}
void CASW_Boomer::DeathSound( const CTakeDamageInfo &info )
{
// if we are playing a fancy death animation, don't play death sounds from code
// all death sounds are played from anim events inside the fancy death animation
if ( m_nDeathStyle == kDIE_FANCY )
return;
if ( m_nDeathStyle == kDIE_INSTAGIB )
EmitSound( "ASW_Boomer.Death_Explode" );
else
EmitSound( "ASW_Boomer.Death_Gib" );
}
int CASW_Boomer::OnTakeDamage_Alive( const CTakeDamageInfo &info )
{
CTakeDamageInfo infoNew( info );
if ( infoNew.GetAttacker() && info.GetAttacker()->Classify() == CLASS_ASW_MARINE )
{
EHANDLE hAttacker = infoNew.GetAttacker();
if ( m_hMarineAttackers.Find( hAttacker ) == m_hMarineAttackers.InvalidIndex() )
{
m_hMarineAttackers.AddToTail( hAttacker );
}
if ( infoNew.GetAttacker()->WorldSpaceCenter().DistTo( WorldSpaceCenter() ) < 40 )
{
// Stuck inside! Kill it good!
infoNew.ScaleDamage( 8 );
}
}
return BaseClass::OnTakeDamage_Alive( infoNew );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input:
// Output:
//-----------------------------------------------------------------------------
void CASW_Boomer::Event_Killed( const CTakeDamageInfo &info )
{
SendBehaviorEvent( info.GetAttacker(), BEHAVIOR_EVENT_EXPLODE, 1, false );
BaseClass::Event_Killed( info );
if ( m_bInflated )
{
m_nDeathStyle = kDIE_INSTAGIB;
}
else
{
if ( m_bOnFire )
{
m_nDeathStyle = kDIE_FANCY;
}
else
{
m_nDeathStyle = kDIE_BREAKABLE;
}
for ( int i = 0; i < m_hMarineAttackers.Count(); i++ )
{
CASW_Marine *pMarine = dynamic_cast<CASW_Marine*>( m_hMarineAttackers[i].Get() );
if ( pMarine && pMarine->IsInhabited() && pMarine->GetCommander() )
{
pMarine->GetCommander()->AwardAchievement( ACHIEVEMENT_ASW_BOOMER_KILL_EARLY );
if ( pMarine->GetMarineResource() )
{
pMarine->GetMarineResource()->m_bKilledBoomerEarly = true;
}
}
}
}
if ( asw_alien_debug_death_style.GetBool() )
Msg( "CASW_Boomer::Event_Killed: m_nDeathStyle = %d\n", m_nDeathStyle );
}
bool CASW_Boomer::CanDoFancyDeath()
{
if ( m_bInflated )
return false;
else
return BaseClass::CanDoFancyDeath();
}
//-----------------------------------------------------------------------------
// Purpose:
// Input:
// Output:
//-----------------------------------------------------------------------------
bool CASW_Boomer::CorpseGib( const CTakeDamageInfo &info )
{
CEffectData data;
m_LagCompensation.UndoLaggedPosition();
data.m_vOrigin = WorldSpaceCenter();
data.m_vNormal = data.m_vOrigin - info.GetDamagePosition();
VectorNormalize( data.m_vNormal );
data.m_flScale = RemapVal( m_iHealth, 0, 3, 0.5f, 2 );
data.m_nColor = m_nSkin;
data.m_fFlags = IsOnFire() ? ASW_GIBFLAG_ON_FIRE : 0;
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input:
// Output:
//-----------------------------------------------------------------------------
int CASW_Boomer::SelectDeadSchedule()
{
if ( m_lifeState == LIFE_DEAD )
{
return SCHED_NONE;
}
CleanupOnDeath();
return SCHED_DIE;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input:
// Output:
//-----------------------------------------------------------------------------
void CASW_Boomer::BuildScheduleTestBits()
{
// Ignore damage if we were recently damaged or we're attacking.
if ( GetActivity() == ACT_MELEE_ATTACK1 )
{
ClearCustomInterruptCondition( COND_LIGHT_DAMAGE );
ClearCustomInterruptCondition( COND_HEAVY_DAMAGE );
}
BaseClass::BuildScheduleTestBits();
}
//-----------------------------------------------------------------------------
//
// Schedules
//
//-----------------------------------------------------------------------------
AI_BEGIN_CUSTOM_NPC( asw_boomer, CASW_Boomer )
DECLARE_ACTIVITY( ACT_RUN_INFLATED )
DECLARE_ACTIVITY( ACT_IDLE_INFLATED );
DECLARE_ACTIVITY( ACT_MELEE_ATTACK1_INFLATED );
DECLARE_ACTIVITY( ACT_DEATH_FIRE_INFLATED );
DECLARE_ANIMEVENT( AE_BOOMER_INFLATED )
AI_END_CUSTOM_NPC()
bool CASW_Boomer::CreateBehaviors()
{
m_ExplodeBehavior.KeyValue( "schedule_chance", "40" );
m_ExplodeBehavior.KeyValue( "schedule_chance_rate", "1" );
m_ExplodeBehavior.KeyValue( "range", "200" );
m_ExplodeBehavior.KeyValue( "max_projectiles", "8" );
m_ExplodeBehavior.KeyValue( "max_buildup_time", "3" );
m_ExplodeBehavior.KeyValue( "min_velocity", "150" );
m_ExplodeBehavior.KeyValue( "max_velocity", "450" );
m_ExplodeBehavior.KeyValue( "attach_name", "sack_" );
m_ExplodeBehavior.KeyValue( "attach_count", "12" );
m_ExplodeBehavior.KeyValue( "damage", "55" );
m_ExplodeBehavior.KeyValue( "radius", "240" );
AddBehavior( &m_ExplodeBehavior );
m_ExplodeBehavior.Init();
m_MeleeBehavior.KeyValue( "range", "140" );
m_MeleeBehavior.KeyValue( "min_damage", "4" );
m_MeleeBehavior.KeyValue( "max_damage", "6" );
m_MeleeBehavior.KeyValue( "force", "4" );
AddBehavior( &m_MeleeBehavior );
m_MeleeBehavior.Init();
m_ChaseEnemyBehavior.KeyValue( "chase_distance", "600" );
AddBehavior( &m_ChaseEnemyBehavior );
m_ChaseEnemyBehavior.Init();
return BaseClass::CreateBehaviors();
}
Activity CASW_Boomer::NPC_TranslateActivity( Activity baseAct )
{
Activity translated = BaseClass::NPC_TranslateActivity( baseAct );
if ( m_bInflating )
{
if ( translated == ACT_RUN ) return (Activity) ACT_RUN_INFLATED;
if ( translated == ACT_IDLE ) return (Activity) ACT_IDLE_INFLATED;
if ( translated == ACT_MELEE_ATTACK1 ) return (Activity) ACT_MELEE_ATTACK1_INFLATED;
if ( translated == ACT_DEATH_FIRE ) return (Activity) ACT_DEATH_FIRE_INFLATED;
}
return translated;
}