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.
640 lines
17 KiB
640 lines
17 KiB
5 years ago
|
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||
|
#include "cbase.h"
|
||
|
|
||
|
#include "tf_gamerules.h"
|
||
|
#include "tf_base_boss.h"
|
||
|
#include "entity_currencypack.h"
|
||
|
#include "tf_gamestats.h"
|
||
|
#include "tf_player.h"
|
||
|
|
||
|
LINK_ENTITY_TO_CLASS( base_boss, CTFBaseBoss );
|
||
|
|
||
|
PRECACHE_REGISTER( base_boss );
|
||
|
|
||
|
IMPLEMENT_SERVERCLASS_ST( CTFBaseBoss, DT_TFBaseBoss)
|
||
|
SendPropFloat( SENDINFO(m_lastHealthPercentage), 11, SPROP_NOSCALE, 0.0, 1.0 ),
|
||
|
END_SEND_TABLE()
|
||
|
|
||
|
BEGIN_DATADESC( CTFBaseBoss )
|
||
|
|
||
|
DEFINE_KEYFIELD( m_initialHealth, FIELD_INTEGER, "health" ),
|
||
|
DEFINE_KEYFIELD( m_modelString, FIELD_STRING, "model" ),
|
||
|
DEFINE_KEYFIELD( m_speed, FIELD_FLOAT, "speed" ),
|
||
|
DEFINE_KEYFIELD( m_startDisabled, FIELD_INTEGER, "start_disabled" ),
|
||
|
|
||
|
DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
|
||
|
DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
|
||
|
DEFINE_INPUTFUNC( FIELD_FLOAT, "SetSpeed", InputSetSpeed ),
|
||
|
|
||
|
DEFINE_INPUTFUNC( FIELD_INTEGER, "SetHealth", InputSetHealth ),
|
||
|
DEFINE_INPUTFUNC( FIELD_INTEGER, "SetMaxHealth", InputSetMaxHealth ),
|
||
|
DEFINE_INPUTFUNC( FIELD_INTEGER, "AddHealth", InputAddHealth ),
|
||
|
DEFINE_INPUTFUNC( FIELD_INTEGER, "RemoveHealth", InputRemoveHealth ),
|
||
|
|
||
|
DEFINE_OUTPUT( m_outputOnHealthBelow90Percent, "OnHealthBelow90Percent" ),
|
||
|
DEFINE_OUTPUT( m_outputOnHealthBelow80Percent, "OnHealthBelow80Percent" ),
|
||
|
DEFINE_OUTPUT( m_outputOnHealthBelow70Percent, "OnHealthBelow70Percent" ),
|
||
|
DEFINE_OUTPUT( m_outputOnHealthBelow60Percent, "OnHealthBelow60Percent" ),
|
||
|
DEFINE_OUTPUT( m_outputOnHealthBelow50Percent, "OnHealthBelow50Percent" ),
|
||
|
DEFINE_OUTPUT( m_outputOnHealthBelow40Percent, "OnHealthBelow40Percent" ),
|
||
|
DEFINE_OUTPUT( m_outputOnHealthBelow30Percent, "OnHealthBelow30Percent" ),
|
||
|
DEFINE_OUTPUT( m_outputOnHealthBelow20Percent, "OnHealthBelow20Percent" ),
|
||
|
DEFINE_OUTPUT( m_outputOnHealthBelow10Percent, "OnHealthBelow10Percent" ),
|
||
|
|
||
|
DEFINE_OUTPUT( m_outputOnKilled, "OnKilled" ),
|
||
|
|
||
|
DEFINE_THINKFUNC( BossThink ),
|
||
|
|
||
|
END_DATADESC()
|
||
|
|
||
|
ConVar tf_base_boss_speed( "tf_base_boss_speed", "75", FCVAR_CHEAT );
|
||
|
ConVar tf_base_boss_max_turn_rate( "tf_base_boss_max_turn_rate", "25", FCVAR_CHEAT );
|
||
|
|
||
|
extern void HandleRageGain( CTFPlayer *pPlayer, unsigned int iRequiredBuffFlags, float flDamage, float fInverseRageGainScale );
|
||
|
|
||
|
//--------------------------------------------------------------------------------------
|
||
|
//--------------------------------------------------------------------------------------
|
||
|
float CTFBaseBossLocomotion::GetRunSpeed( void ) const
|
||
|
{
|
||
|
CTFBaseBoss *boss = (CTFBaseBoss *)GetBot()->GetEntity();
|
||
|
return boss->GetMaxSpeed();
|
||
|
}
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------------------------
|
||
|
void CTFBaseBossLocomotion::FaceTowards( const Vector &target )
|
||
|
{
|
||
|
CTFBaseBoss *pTank = static_cast< CTFBaseBoss* >( GetBot()->GetEntity() );
|
||
|
|
||
|
const float deltaT = GetUpdateInterval();
|
||
|
|
||
|
QAngle angles = pTank->GetLocalAngles();
|
||
|
|
||
|
float desiredYaw = UTIL_VecToYaw( target - GetFeet() );
|
||
|
|
||
|
float angleDiff = UTIL_AngleDiff( desiredYaw, angles.y );
|
||
|
|
||
|
float deltaYaw = tf_base_boss_max_turn_rate.GetFloat() * deltaT;
|
||
|
|
||
|
if ( angleDiff < -deltaYaw )
|
||
|
{
|
||
|
angles.y -= deltaYaw;
|
||
|
}
|
||
|
else if ( angleDiff > deltaYaw )
|
||
|
{
|
||
|
angles.y += deltaYaw;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
angles.y += angleDiff;
|
||
|
}
|
||
|
|
||
|
Vector forward, right;
|
||
|
pTank->GetVectors( NULL, &right, NULL );
|
||
|
|
||
|
forward = CrossProduct( GetGroundNormal(), right );
|
||
|
|
||
|
float desiredPitch = UTIL_VecToPitch( forward );
|
||
|
|
||
|
angleDiff = UTIL_AngleDiff( desiredPitch, angles.x );
|
||
|
|
||
|
float deltaPitch = tf_base_boss_max_turn_rate.GetFloat() * deltaT;
|
||
|
|
||
|
if ( angleDiff < -deltaPitch )
|
||
|
{
|
||
|
angles.x -= deltaPitch;
|
||
|
}
|
||
|
else if ( angleDiff > deltaPitch )
|
||
|
{
|
||
|
angles.x += deltaPitch;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
angles.x += angleDiff;
|
||
|
}
|
||
|
|
||
|
pTank->SetLocalAngles( angles );
|
||
|
pTank->UpdateCollisionBounds();
|
||
|
}
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------------------------
|
||
|
//--------------------------------------------------------------------------------------
|
||
|
CTFBaseBoss::CTFBaseBoss()
|
||
|
{
|
||
|
m_modelString = NULL_STRING;
|
||
|
m_lastHealthPercentage = 1.0f;
|
||
|
m_speed = tf_base_boss_speed.GetFloat();
|
||
|
m_locomotor = new CTFBaseBossLocomotion( this );
|
||
|
m_currencyValue = TF_BASE_BOSS_CURRENCY;
|
||
|
m_initialHealth = 0;
|
||
|
|
||
|
m_bResolvePlayerCollisions = true;
|
||
|
}
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------------------------
|
||
|
CTFBaseBoss::~CTFBaseBoss()
|
||
|
{
|
||
|
delete m_locomotor;
|
||
|
}
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------------------------
|
||
|
void CTFBaseBoss::Precache( void )
|
||
|
{
|
||
|
if ( m_modelString != NULL_STRING )
|
||
|
{
|
||
|
PrecacheModel( STRING( m_modelString ) );
|
||
|
}
|
||
|
|
||
|
BaseClass::Precache();
|
||
|
}
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------------------------
|
||
|
void CTFBaseBoss::Spawn( void )
|
||
|
{
|
||
|
Precache();
|
||
|
|
||
|
BaseClass::Spawn();
|
||
|
|
||
|
if ( m_modelString != NULL_STRING )
|
||
|
{
|
||
|
SetModel( STRING( m_modelString ) );
|
||
|
}
|
||
|
|
||
|
m_isEnabled = m_startDisabled ? false : true;
|
||
|
|
||
|
SetHealth( m_initialHealth );
|
||
|
SetMaxHealth( m_initialHealth );
|
||
|
|
||
|
if ( TFGameRules() )
|
||
|
{
|
||
|
TFGameRules()->AddActiveBoss( this );
|
||
|
}
|
||
|
|
||
|
m_lastHealthPercentage = 1.0f;
|
||
|
m_damagePoseParameter = -1;
|
||
|
|
||
|
SetThink( &CTFBaseBoss::BossThink );
|
||
|
SetNextThink( gpGlobals->curtime );
|
||
|
}
|
||
|
|
||
|
int CTFBaseBoss::UpdateTransmitState()
|
||
|
{
|
||
|
// ALWAYS transmit to all clients.
|
||
|
return SetTransmitState( FL_EDICT_ALWAYS );
|
||
|
}
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------------------------
|
||
|
void CTFBaseBoss::ResolvePlayerCollision( CTFPlayer *player )
|
||
|
{
|
||
|
Vector bossGlobalMins = WorldAlignMins() + GetAbsOrigin();
|
||
|
Vector bossGlobalMaxs = WorldAlignMaxs() + GetAbsOrigin();
|
||
|
|
||
|
Vector playerGlobalMins = player->WorldAlignMins() + player->GetAbsOrigin();
|
||
|
Vector playerGlobalMaxs = player->WorldAlignMaxs() + player->GetAbsOrigin();
|
||
|
|
||
|
Vector newPlayerPos = player->GetAbsOrigin();
|
||
|
|
||
|
|
||
|
if ( playerGlobalMins.x > bossGlobalMaxs.x ||
|
||
|
playerGlobalMaxs.x < bossGlobalMins.x ||
|
||
|
playerGlobalMins.y > bossGlobalMaxs.y ||
|
||
|
playerGlobalMaxs.y < bossGlobalMins.y ||
|
||
|
playerGlobalMins.z > bossGlobalMaxs.z ||
|
||
|
playerGlobalMaxs.z < bossGlobalMins.z )
|
||
|
{
|
||
|
// no overlap
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
Vector toPlayer = player->WorldSpaceCenter() - WorldSpaceCenter();
|
||
|
|
||
|
Vector overlap;
|
||
|
float signX, signY, signZ;
|
||
|
|
||
|
if ( toPlayer.x >= 0 )
|
||
|
{
|
||
|
overlap.x = bossGlobalMaxs.x - playerGlobalMins.x;
|
||
|
signX = 1.0f;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
overlap.x = playerGlobalMaxs.x - bossGlobalMins.x;
|
||
|
signX = -1.0f;
|
||
|
}
|
||
|
|
||
|
if ( toPlayer.y >= 0 )
|
||
|
{
|
||
|
overlap.y = bossGlobalMaxs.y - playerGlobalMins.y;
|
||
|
signY = 1.0f;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
overlap.y = playerGlobalMaxs.y - bossGlobalMins.y;
|
||
|
signY = -1.0f;
|
||
|
}
|
||
|
|
||
|
if ( toPlayer.z >= 0 )
|
||
|
{
|
||
|
overlap.z = bossGlobalMaxs.z - playerGlobalMins.z;
|
||
|
signZ = 1.0f;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// don't push player underground
|
||
|
overlap.z = 99999.9f; // playerGlobalMaxs.z - bossGlobalMins.z;
|
||
|
signZ = -1.0f;
|
||
|
}
|
||
|
|
||
|
float bloat = 5.0f;
|
||
|
|
||
|
if ( overlap.x < overlap.y )
|
||
|
{
|
||
|
if ( overlap.x < overlap.z )
|
||
|
{
|
||
|
// X is least overlap
|
||
|
newPlayerPos.x += signX * ( overlap.x + bloat );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Z is least overlap
|
||
|
newPlayerPos.z += signZ * ( overlap.z + bloat );
|
||
|
}
|
||
|
}
|
||
|
else if ( overlap.z < overlap.y )
|
||
|
{
|
||
|
// Z is least overlap
|
||
|
newPlayerPos.z += signZ * ( overlap.z + bloat );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Y is least overlap
|
||
|
newPlayerPos.y += signY * ( overlap.y + bloat );
|
||
|
}
|
||
|
|
||
|
// check if new location is valid
|
||
|
trace_t result;
|
||
|
Ray_t ray;
|
||
|
ray.Init( newPlayerPos, newPlayerPos, player->WorldAlignMins(), player->WorldAlignMaxs() );
|
||
|
UTIL_TraceRay( ray, MASK_PLAYERSOLID, player, COLLISION_GROUP_PLAYER_MOVEMENT, &result );
|
||
|
|
||
|
if ( result.DidHit() )
|
||
|
{
|
||
|
// Trace down from above to find safe ground
|
||
|
ray.Init( newPlayerPos + Vector( 0.0f, 0.0f, 32.0f ), newPlayerPos, player->WorldAlignMins(), player->WorldAlignMaxs() );
|
||
|
UTIL_TraceRay( ray, MASK_PLAYERSOLID, player, COLLISION_GROUP_PLAYER_MOVEMENT, &result );
|
||
|
|
||
|
if ( result.startsolid )
|
||
|
{
|
||
|
// player was crushed against something
|
||
|
player->TakeDamage( CTakeDamageInfo( this, this, 99999.9f, DMG_CRUSH ) );
|
||
|
return;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Use the trace end position
|
||
|
newPlayerPos = result.endpos;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
player->SetAbsOrigin( newPlayerPos );
|
||
|
}
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------------------------
|
||
|
void CTFBaseBoss::Touch( CBaseEntity *pOther )
|
||
|
{
|
||
|
BaseClass::Touch( pOther );
|
||
|
|
||
|
if ( pOther && pOther->IsBaseObject() )
|
||
|
{
|
||
|
// ran over an engineer building - destroy it
|
||
|
pOther->TakeDamage( CTakeDamageInfo( this, this, 99999.9f, DMG_CRUSH ) );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------------------------
|
||
|
void CTFBaseBoss::BossThink( void )
|
||
|
{
|
||
|
SetNextThink( gpGlobals->curtime );
|
||
|
|
||
|
if ( m_damagePoseParameter < 0 )
|
||
|
{
|
||
|
m_damagePoseParameter = LookupPoseParameter( "damage" );
|
||
|
}
|
||
|
|
||
|
if ( m_damagePoseParameter >= 0 )
|
||
|
{
|
||
|
// Avoid dividing by zero
|
||
|
if ( GetMaxHealth() )
|
||
|
{
|
||
|
SetPoseParameter( m_damagePoseParameter, 1.0f - ( (float)GetHealth() / (float)GetMaxHealth() ) );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
SetPoseParameter( m_damagePoseParameter, 1.0f );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( !m_isEnabled )
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
Update();
|
||
|
|
||
|
if ( m_bResolvePlayerCollisions )
|
||
|
{
|
||
|
CUtlVector< CTFPlayer * > playerVector;
|
||
|
CollectPlayers( &playerVector, TEAM_ANY, COLLECT_ONLY_LIVING_PLAYERS );
|
||
|
for( int i=0; i<playerVector.Count(); ++i )
|
||
|
{
|
||
|
ResolvePlayerCollision( playerVector[i] );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//--------------------------------------------------------------------------------------
|
||
|
void CTFBaseBoss::Event_Killed( const CTakeDamageInfo &info )
|
||
|
{
|
||
|
m_outputOnKilled.FireOutput( this, this );
|
||
|
|
||
|
// drop some loot!
|
||
|
m_currencyValue = GetCurrencyValue();
|
||
|
|
||
|
int nRemainingMoney = m_currencyValue;
|
||
|
|
||
|
QAngle angRand = vec3_angle;
|
||
|
|
||
|
while( nRemainingMoney > 0 )
|
||
|
{
|
||
|
int nAmount = 0;
|
||
|
|
||
|
if ( nRemainingMoney >= 100 )
|
||
|
{
|
||
|
nAmount = 25;
|
||
|
}
|
||
|
else if ( nRemainingMoney >= 40 )
|
||
|
{
|
||
|
nAmount = 10;
|
||
|
}
|
||
|
else if ( nRemainingMoney >= 5 )
|
||
|
{
|
||
|
nAmount = 5;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
nAmount = nRemainingMoney;
|
||
|
}
|
||
|
|
||
|
nRemainingMoney -= nAmount;
|
||
|
|
||
|
angRand.y = RandomFloat( -180.0f, 180.0f );
|
||
|
|
||
|
CCurrencyPackCustom *pCurrencyPack = assert_cast< CCurrencyPackCustom* >( CBaseEntity::CreateNoSpawn( "item_currencypack_custom", WorldSpaceCenter(), angRand, this ) );
|
||
|
|
||
|
if ( pCurrencyPack )
|
||
|
{
|
||
|
pCurrencyPack->SetAmount( nAmount );
|
||
|
|
||
|
Vector vecImpulse = RandomVector( -1,1 );
|
||
|
vecImpulse.z = RandomFloat( 5.0f, 20.0f );
|
||
|
VectorNormalize( vecImpulse );
|
||
|
Vector vecVelocity = vecImpulse * 250.0 * RandomFloat( 1.0f, 4.0f );
|
||
|
|
||
|
DispatchSpawn( pCurrencyPack );
|
||
|
pCurrencyPack->DropSingleInstance( vecVelocity, this, 0, 0 );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
BaseClass::Event_Killed( info );
|
||
|
|
||
|
UTIL_Remove( this );
|
||
|
}
|
||
|
|
||
|
void CTFBaseBoss::UpdateOnRemove()
|
||
|
{
|
||
|
if ( TFGameRules() )
|
||
|
{
|
||
|
TFGameRules()->RemoveActiveBoss( this );
|
||
|
}
|
||
|
|
||
|
BaseClass::UpdateOnRemove();
|
||
|
}
|
||
|
|
||
|
int CTFBaseBoss::OnTakeDamage( const CTakeDamageInfo &rawInfo )
|
||
|
{
|
||
|
CTakeDamageInfo info = rawInfo;
|
||
|
|
||
|
if ( TFGameRules() )
|
||
|
{
|
||
|
TFGameRules()->ApplyOnDamageModifyRules( info, this, true );
|
||
|
}
|
||
|
|
||
|
// On damage Rage
|
||
|
// Give the soldier/pyro some rage points for dealing/taking damage.
|
||
|
if ( info.GetDamage() && info.GetAttacker() != this )
|
||
|
{
|
||
|
CTFPlayer *pAttacker = ToTFPlayer( info.GetAttacker() );
|
||
|
|
||
|
// Buff flag 1: we get rage when we deal damage. Here, that means the soldier that attacked
|
||
|
// gets rage when we take damage.
|
||
|
HandleRageGain( pAttacker, kRageBuffFlag_OnDamageDealt, info.GetDamage(), 6.0f );
|
||
|
|
||
|
// Buff 5: our pyro attacker get rage when we're damaged by fire
|
||
|
if ( ( info.GetDamageType() & DMG_BURN ) != 0 || ( info.GetDamageType() & DMG_PLASMA ) != 0 )
|
||
|
{
|
||
|
HandleRageGain( pAttacker, kRageBuffFlag_OnBurnDamageDealt, info.GetDamage(), 30.f );
|
||
|
}
|
||
|
|
||
|
if ( pAttacker && info.GetWeapon() )
|
||
|
{
|
||
|
CTFWeaponBase *pWeapon = dynamic_cast<CTFWeaponBase *>( info.GetWeapon() );
|
||
|
if ( pWeapon )
|
||
|
{
|
||
|
pWeapon->ApplyOnHitAttributes( this, pAttacker, info );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return BaseClass::OnTakeDamage( info );
|
||
|
}
|
||
|
|
||
|
//--------------------------------------------------------------------------------------
|
||
|
int CTFBaseBoss::OnTakeDamage_Alive( const CTakeDamageInfo &rawInfo )
|
||
|
{
|
||
|
if ( !rawInfo.GetAttacker() || rawInfo.GetAttacker()->GetTeamNumber() == GetTeamNumber() )
|
||
|
{
|
||
|
// no friendly fire damage
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
CTakeDamageInfo info = rawInfo;
|
||
|
|
||
|
// weapon-specific damage modification
|
||
|
ModifyDamage( &info );
|
||
|
|
||
|
if ( TFGameRules() )
|
||
|
{
|
||
|
CTFGameRules::DamageModifyExtras_t outParams;
|
||
|
info.SetDamage( TFGameRules()->ApplyOnDamageAliveModifyRules( info, this, outParams ) );
|
||
|
}
|
||
|
|
||
|
// fire event for client combat text, beep, etc.
|
||
|
IGameEvent *event = gameeventmanager->CreateEvent( "npc_hurt" );
|
||
|
if ( event )
|
||
|
{
|
||
|
|
||
|
event->SetInt( "entindex", entindex() );
|
||
|
event->SetInt( "health", MAX( 0, GetHealth() ) );
|
||
|
event->SetInt( "damageamount", info.GetDamage() );
|
||
|
event->SetBool( "crit", ( info.GetDamageType() & DMG_CRITICAL ) ? true : false );
|
||
|
|
||
|
CTFPlayer *attackerPlayer = ToTFPlayer( info.GetAttacker() );
|
||
|
if ( attackerPlayer )
|
||
|
{
|
||
|
event->SetInt( "attacker_player", attackerPlayer->GetUserID() );
|
||
|
|
||
|
if ( attackerPlayer->GetActiveTFWeapon() )
|
||
|
{
|
||
|
event->SetInt( "weaponid", attackerPlayer->GetActiveTFWeapon()->GetWeaponID() );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
event->SetInt( "weaponid", 0 );
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// hurt by world
|
||
|
event->SetInt( "attacker_player", 0 );
|
||
|
event->SetInt( "weaponid", 0 );
|
||
|
}
|
||
|
|
||
|
gameeventmanager->FireEvent( event );
|
||
|
}
|
||
|
|
||
|
int result = BaseClass::OnTakeDamage_Alive( info );
|
||
|
|
||
|
// emit injury outputs
|
||
|
float healthPercentage = (float)GetHealth() / (float)GetMaxHealth();
|
||
|
|
||
|
if ( m_lastHealthPercentage > 0.9f && healthPercentage < 0.9f )
|
||
|
{
|
||
|
m_outputOnHealthBelow90Percent.FireOutput( this, this );
|
||
|
}
|
||
|
else if ( m_lastHealthPercentage > 0.8f && healthPercentage < 0.8f )
|
||
|
{
|
||
|
m_outputOnHealthBelow80Percent.FireOutput( this, this );
|
||
|
}
|
||
|
else if ( m_lastHealthPercentage > 0.7f && healthPercentage < 0.7f )
|
||
|
{
|
||
|
m_outputOnHealthBelow70Percent.FireOutput( this, this );
|
||
|
}
|
||
|
else if ( m_lastHealthPercentage > 0.6f && healthPercentage < 0.6f )
|
||
|
{
|
||
|
m_outputOnHealthBelow60Percent.FireOutput( this, this );
|
||
|
}
|
||
|
else if ( m_lastHealthPercentage > 0.5f && healthPercentage < 0.5f )
|
||
|
{
|
||
|
m_outputOnHealthBelow50Percent.FireOutput( this, this );
|
||
|
}
|
||
|
else if ( m_lastHealthPercentage > 0.4f && healthPercentage < 0.4f )
|
||
|
{
|
||
|
m_outputOnHealthBelow40Percent.FireOutput( this, this );
|
||
|
}
|
||
|
else if ( m_lastHealthPercentage > 0.3f && healthPercentage < 0.3f )
|
||
|
{
|
||
|
m_outputOnHealthBelow30Percent.FireOutput( this, this );
|
||
|
}
|
||
|
else if ( m_lastHealthPercentage > 0.2f && healthPercentage < 0.2f )
|
||
|
{
|
||
|
m_outputOnHealthBelow20Percent.FireOutput( this, this );
|
||
|
}
|
||
|
else if ( m_lastHealthPercentage > 0.1f && healthPercentage < 0.1f )
|
||
|
{
|
||
|
m_outputOnHealthBelow10Percent.FireOutput( this, this );
|
||
|
}
|
||
|
|
||
|
m_lastHealthPercentage = healthPercentage;
|
||
|
|
||
|
// Let attacker react to the damage they dealt
|
||
|
CTFPlayer *pAttacker = ToTFPlayer( rawInfo.GetAttacker() );
|
||
|
if ( pAttacker )
|
||
|
{
|
||
|
pAttacker->OnDealtDamage( this, info );
|
||
|
|
||
|
CTF_GameStats.Event_BossDamage( pAttacker, info.GetDamage() );
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------------------------
|
||
|
void CTFBaseBoss::InputEnable( inputdata_t &inputdata )
|
||
|
{
|
||
|
m_isEnabled = true;
|
||
|
}
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------------------------
|
||
|
void CTFBaseBoss::InputDisable( inputdata_t &inputdata )
|
||
|
{
|
||
|
m_isEnabled = false;
|
||
|
}
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
void CTFBaseBoss::InputSetSpeed( inputdata_t &inputdata )
|
||
|
{
|
||
|
m_speed = inputdata.value.Float();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Set the health of the boss
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CTFBaseBoss::InputSetHealth( inputdata_t &inputdata )
|
||
|
{
|
||
|
m_iHealth = inputdata.value.Int();
|
||
|
SetHealth( m_iHealth );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Set the max health of the boss
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CTFBaseBoss::InputSetMaxHealth( inputdata_t &inputdata )
|
||
|
{
|
||
|
m_iMaxHealth = inputdata.value.Int();
|
||
|
SetMaxHealth( m_iMaxHealth );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Add health to the boss
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CTFBaseBoss::InputAddHealth( inputdata_t &inputdata )
|
||
|
{
|
||
|
int iHealth = inputdata.value.Int();
|
||
|
SetHealth( MIN( GetMaxHealth(), GetHealth() + iHealth ) );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Remove health from the boss
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CTFBaseBoss::InputRemoveHealth( inputdata_t &inputdata )
|
||
|
{
|
||
|
int iDamage = inputdata.value.Int();
|
||
|
|
||
|
SetHealth( GetHealth() - iDamage );
|
||
|
if ( GetHealth() <= 0 )
|
||
|
{
|
||
|
CTakeDamageInfo info( inputdata.pCaller, inputdata.pActivator, vec3_origin, GetAbsOrigin(), iDamage, DMG_GENERIC, TF_DMG_CUSTOM_NONE );
|
||
|
Event_Killed( info );
|
||
|
}
|
||
|
}
|