diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/shared/tf/tf_robot_destruction_robot.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'game/shared/tf/tf_robot_destruction_robot.cpp')
| -rw-r--r-- | game/shared/tf/tf_robot_destruction_robot.cpp | 1313 |
1 files changed, 1313 insertions, 0 deletions
diff --git a/game/shared/tf/tf_robot_destruction_robot.cpp b/game/shared/tf/tf_robot_destruction_robot.cpp new file mode 100644 index 0000000..5fe2a66 --- /dev/null +++ b/game/shared/tf/tf_robot_destruction_robot.cpp @@ -0,0 +1,1313 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: The robots for use in the Robot Destruction TF2 game mode. +// +//=========================================================================// + +#include "cbase.h" +#include "tf_logic_robot_destruction.h" +#include "tf_shareddefs.h" +#include "particle_parse.h" +#include "tf_gamerules.h" +#include "debugoverlay_shared.h" +#ifdef GAME_DLL + #include "tf_ammo_pack.h" + #include "entity_bonuspack.h" + #include "entity_capture_flag.h" + #include "effect_dispatch_data.h" + #include "te_effect_dispatch.h" + #include "tf_gamestats.h" + #include "eventlist.h" +#else + #include "eventlist.h" +#endif + +#ifdef GAME_DLL + extern ConVar tf_obj_gib_velocity_min; + extern ConVar tf_obj_gib_velocity_max; + extern ConVar tf_obj_gib_maxspeed; +#endif + +#define ADD_POINTS_CONTEXT "add_points_context" +#define SPEW_BARS_CONTEXT "spew_bars_context" +#define SELF_DESTRUCT_THINK "self_destruct_think" +#define ANIMS_THINK "anims_think" + +ConVar tf_rd_robot_repair_rate( "tf_rd_robot_repair_rate", "60", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY ); + +RobotData_t* g_RobotData[ NUM_ROBOT_TYPES ] = +{ + // Model // Busted model // Pain // Death // Collide // Idle // Bar offset + new RobotData_t( "models/bots/bot_worker/bot_worker_A.mdl", "models/bots/bot_worker/bot_worker_A.mdl", "Robot.Pain", "Robot.Death", "Robot.Collide", "Robot.Greeting", -35.f ), +#ifdef STAGING_ONLY + new RobotData_t( "models/bots/bot_worker/bot_worker_b.mdl", "models/bots/bot_worker/bot_worker_b.mdl", "Robot.Pain", "Robot.Death", "Robot.Collide", "Robot.Greeting", -30.f ), +#else + new RobotData_t( "models/bots/bot_worker/bot_worker2.mdl", "models/bots/bot_worker/bot_worker2.mdl", "Robot.Pain", "Robot.Death", "Robot.Collide", "Robot.Greeting", -30.f ), +#endif + new RobotData_t( "models/bots/bot_worker/bot_worker3.mdl", "models/bots/bot_worker/bot_worker3_nohead.mdl", "Robot.Pain", "Robot.Death", "Robot.Collide", "Robot.Greeting", -10.f ), +}; + +#define ROBOT_DEATH_EXPLOSION "RD.BotDeathExplosion" +#define SCORING_POINTS_PARTICLE_EFFECT "bot_radio_waves" +#define DAMAGED_ROBOT_PARTICLE_EFFECT "sentrydamage_4" +#define DEATH_PARTICLE_EFFECT "rd_robot_explosion" + + +void RobotData_t::Precache() +{ + if ( GetStringData( MODEL_KEY ) ) + { + CBaseEntity::PrecacheModel( GetStringData( MODEL_KEY ) ); + PrecacheGibsForModel( modelinfo->GetModelIndex( GetStringData( MODEL_KEY ) ) ); + PrecachePropsForModel( modelinfo->GetModelIndex( GetStringData( MODEL_KEY ) ), "spawn" ); + } + if ( GetStringData( DAMAGED_MODEL_KEY ) ) CBaseEntity::PrecacheModel( GetStringData( DAMAGED_MODEL_KEY ) ); + if ( GetStringData( HURT_SOUND_KEY ) ) CBaseEntity::PrecacheScriptSound( GetStringData( HURT_SOUND_KEY ) ); + if ( GetStringData( DEATH_SOUND_KEY ) ) CBaseEntity::PrecacheScriptSound( GetStringData( DEATH_SOUND_KEY ) ); + if ( GetStringData( COLLIDE_SOUND_KEY ) ) CBaseEntity::PrecacheScriptSound( GetStringData( COLLIDE_SOUND_KEY ) ); + if ( GetStringData( IDLE_SOUND_KEY ) ) CBaseEntity::PrecacheScriptSound( GetStringData( IDLE_SOUND_KEY ) ); +} + + +CTFRobotDestruction_RobotAnimController::CTFRobotDestruction_RobotAnimController( CTFRobotDestruction_Robot *pOuter ) + : m_vecOldOrigin( vec3_origin ) + , m_vecLean( vec3_origin ) + , m_pOuter( pOuter ) + , m_vecImpulse( vec3_origin ) +{} + +void CTFRobotDestruction_RobotAnimController::Update() +{ + if( !m_pOuter ) + return; + + CStudioHdr *pStudioHdr = m_pOuter->GetModelPtr(); + if ( !pStudioHdr ) + return; + + const Vector vecNewOrigin = m_pOuter->GetAbsOrigin(); + const Vector vecVelocity = m_vecOldOrigin - vecNewOrigin; + m_vecOldOrigin = vecNewOrigin; + + Approach( m_vecLean, vecVelocity + m_vecImpulse, 2.f ); + GetPoseParams(); + Approach( m_vecImpulse, vec3_origin, 200.f ); + + Vector vecForward, vecRight; + AngleVectors( m_pOuter->GetAbsAngles(), &vecForward, &vecRight, NULL ); + + float flRightLean = vecRight.Dot( m_vecLean ); + float flForwardLean = vecForward.Dot( m_vecLean ); + + m_pOuter->SetPoseParameter( m_poseParams.m_nMoveX, flForwardLean ); + m_pOuter->SetPoseParameter( m_poseParams.m_nMoveY, flRightLean ); +} + +void CTFRobotDestruction_RobotAnimController::Impulse( const Vector& vecImpulse ) +{ + m_vecImpulse += vecImpulse * 5; +} + +void CTFRobotDestruction_RobotAnimController::Approach( Vector& vecIn, const Vector& vecTarget, float flRate ) +{ + Vector vecApproach = ( vecTarget - vecIn ) * flRate * gpGlobals->frametime; + if ( vecApproach.LengthSqr() > ( vecIn - vecTarget ).LengthSqr() ) + vecIn = vecTarget; + else + vecIn += vecApproach; +} + +void CTFRobotDestruction_RobotAnimController::GetPoseParams() +{ + m_poseParams.m_nMoveX = m_pOuter->LookupPoseParameter( "move_x" ); + m_poseParams.m_nMoveY = m_pOuter->LookupPoseParameter( "move_y" ); +} + +IMPLEMENT_NETWORKCLASS_ALIASED( TFRobotDestruction_Robot, DT_TFRobotDestruction_Robot ) + +BEGIN_NETWORK_TABLE( CTFRobotDestruction_Robot, DT_TFRobotDestruction_Robot ) +#ifdef CLIENT_DLL + RecvPropInt( RECVINFO(m_iHealth) ), + RecvPropInt( RECVINFO(m_iMaxHealth) ), + RecvPropInt( RECVINFO(m_eType) ), +#else + SendPropInt(SENDINFO(m_iHealth), -1, SPROP_VARINT ), + SendPropInt(SENDINFO(m_iMaxHealth), -1, SPROP_VARINT ), + SendPropInt(SENDINFO(m_eType), -1, SPROP_VARINT ), +#endif +END_NETWORK_TABLE() + +BEGIN_DATADESC( CTFRobotDestruction_Robot ) +#ifdef GAME_DLL + DEFINE_INPUTFUNC( FIELD_VOID, "StopAndUseComputer", InputStopAndUseComputer ), +#endif +END_DATADESC() + +LINK_ENTITY_TO_CLASS( tf_robot_destruction_robot, CTFRobotDestruction_Robot ); + +CTFRobotDestruction_Robot::CTFRobotDestruction_Robot() + : m_animController( this ) +{ +#ifdef GAME_DLL + m_nPointsSpewed = 0; + + m_intention = new CRobotIntention( this ); + m_locomotor = new CRobotLocomotion( this ); + m_body = new CHeadlessHatmanBody( this ); + m_bIsPanicked = false; +#else + ListenForGameEvent( "rd_robot_impact" ); +#endif +} + +CTFRobotDestruction_Robot::~CTFRobotDestruction_Robot() +{ +#ifdef CLIENT_DLL + m_hDamagedParticleEffect = NULL; +#else + if ( m_hSpawn ) + m_hSpawn->ClearRobot(); + if ( m_intention ) + delete m_intention; + if ( m_locomotor ) + delete m_locomotor; + if ( m_body ) + delete m_body; +#endif +} + +void CTFRobotDestruction_Robot::StaticPrecache() +{ + PrecacheParticleSystem( DEATH_PARTICLE_EFFECT ); + PrecacheParticleSystem( SCORING_POINTS_PARTICLE_EFFECT ); + PrecacheParticleSystem( DAMAGED_ROBOT_PARTICLE_EFFECT ); + PrecacheScriptSound( ROBOT_DEATH_EXPLOSION ); + + for( int i=0; i < ARRAYSIZE( g_RobotData ); ++i ) + { + g_RobotData[i]->Precache(); + } +} + +void CTFRobotDestruction_Robot::Precache() +{ + BaseClass::Precache(); + StaticPrecache(); +} + +void CTFRobotDestruction_Robot::Spawn() +{ + // Clear out the gib list and create a new one. + m_aGibs.Purge(); + BuildGibList( m_aGibs, GetModelIndex(), 1.0f, COLLISION_GROUP_NONE ); + BuildPropList( "spawn", m_aSpawnProps, GetModelIndex(), 1.f, COLLISION_GROUP_NONE ); + + BaseClass::Spawn(); + + SetSolid( SOLID_BBOX ); + + m_takedamage = DAMAGE_YES; + m_nSkin = ( GetTeamNumber() == TF_TEAM_RED ) ? 0 : 1; +#ifdef GAME_DLL + SetContextThink( &CTFRobotDestruction_Robot::UpdateAnimsThink, gpGlobals->curtime, ANIMS_THINK ); + + if ( m_hGroup ) + { + m_hGroup->UpdateState(); + } + + m_hNextPath.Set( dynamic_cast<CPathTrack*>( gEntList.FindEntityByName( NULL, m_spawnData.m_pszPathName ) ) ); + // The path needs to exist + if ( !m_hNextPath ) + { + UTIL_Remove( this ); + } + + if ( CTFRobotDestructionLogic::GetRobotDestructionLogic() ) + CTFRobotDestructionLogic::GetRobotDestructionLogic()->RobotCreated( this ); + + // Create our dispenser + m_pDispenser = dynamic_cast<CRobotDispenser*>( CreateEntityByName( "rd_robot_dispenser" ) ); + Assert( m_pDispenser ); + m_pDispenser->SetParent( this ); + m_pDispenser->Spawn(); + m_pDispenser->ChangeTeam( GetTeamNumber() ); + m_pDispenser->OnGoActive(); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: Dont collide with players +//----------------------------------------------------------------------------- +bool CTFRobotDestruction_Robot::ShouldCollide( int collisionGroup, int contentsMask ) const +{ + if ( collisionGroup == COLLISION_GROUP_PLAYER_MOVEMENT ) + { + return false; + } + + return BaseClass::ShouldCollide( collisionGroup, contentsMask ); +} + + +#ifdef CLIENT_DLL + +//----------------------------------------------------------------------------- +// Purpose: Specify where our healthbars should go over our heads +//----------------------------------------------------------------------------- +float CTFRobotDestruction_Robot::GetHealthBarHeightOffset() const +{ + return g_RobotData[ m_eType ]->GetFloatData( RobotData_t::HEALTH_BAR_OFFSET_KEY ); +} + + +void CTFRobotDestruction_Robot::OnDataChanged( DataUpdateType_t type ) +{ + BaseClass::OnDataChanged( type ); + + if ( type == DATA_UPDATE_CREATED ) + { + SetNextClientThink( CLIENT_THINK_ALWAYS ); + } + + UpdateDamagedEffects(); +} + +//----------------------------------------------------------------------------- +// Purpose: Play damaged effects, similar to sentries +//----------------------------------------------------------------------------- +void CTFRobotDestruction_Robot::UpdateDamagedEffects() +{ + // Start playing our damaged particle if we're damaged + bool bLowHealth = GetHealth() <= (GetMaxHealth() * 0.5); + if ( bLowHealth && !m_hDamagedParticleEffect ) + { + m_hDamagedParticleEffect = ParticleProp()->Create( DAMAGED_ROBOT_PARTICLE_EFFECT, + PATTACH_ABSORIGIN_FOLLOW, + INVALID_PARTICLE_ATTACHMENT, + Vector(0,0,50) ); + + } + else if ( !bLowHealth && m_hDamagedParticleEffect ) + { + ParticleProp()->StopEmission( m_hDamagedParticleEffect ); + m_hDamagedParticleEffect = NULL; + } +} + +void CTFRobotDestruction_Robot::UpdateClientSideAnimation( void ) +{ + m_animController.Update(); + + BaseClass::UpdateClientSideAnimation(); +} + +void CTFRobotDestruction_Robot::FireEvent( const Vector& origin, const QAngle& angles, int event, const char *options ) +{ + if ( event == AE_RD_ROBOT_POP_PANELS_OFF ) + { + CUtlVector<breakmodel_t> vecProp; + FOR_EACH_VEC( m_aSpawnProps, i ) + { + char pstrLowerName[ MAX_PATH ]; + memset( pstrLowerName, 0, sizeof(pstrLowerName) ); + Q_snprintf( pstrLowerName, sizeof(pstrLowerName), "%s", options ); + Q_strlower( pstrLowerName ); + if ( Q_strstr( m_aSpawnProps[i].modelName, pstrLowerName ) ) + { + vecProp.AddToTail( m_aSpawnProps[i] ); + } + } + + if ( vecProp.Count() ) + { + Vector vForward, vRight, vUp; + AngleVectors( GetAbsAngles(), &vForward, &vRight, &vUp ); + + Vector vecBreakVelocity = Vector(0,0,200); + AngularImpulse angularImpulse( RandomFloat( 0.0f, 120.0f ), RandomFloat( 0.0f, 120.0f ), 0.0 ); + Vector vecOrigin = GetAbsOrigin() + vForward*70 + vUp*10; + QAngle vecAngles = GetAbsAngles(); + breakablepropparams_t breakParams( vecOrigin, vecAngles, vecBreakVelocity, angularImpulse ); + breakParams.impactEnergyScale = 1.0f; + breakParams.defBurstScale = 3.f; + int nModelIndex = GetModelIndex(); + + CreateGibsFromList( vecProp, nModelIndex, NULL, breakParams, this, -1 , false, true ); + } + } + + BaseClass::FireEvent( origin, angles, event, options ); +} + +void CTFRobotDestruction_Robot::FireGameEvent( IGameEvent *pEvent ) +{ + const char *pszName = pEvent->GetName(); + if ( FStrEq( pszName, "rd_robot_impact" ) ) + { + const int index_ = pEvent->GetInt( "entindex" ); + if ( index_ == entindex() ) + { + Vector vecImpulse( pEvent->GetFloat( "impulse_x" ), pEvent->GetFloat( "impulse_y" ), pEvent->GetFloat( "impulse_z" ) ); + m_animController.Impulse( vecImpulse.Normalized() * 20 ); + } + } +} + + CStudioHdr* CTFRobotDestruction_Robot::OnNewModel() + { + CStudioHdr *hdr = BaseClass::OnNewModel(); + BuildPropList( "spawn", m_aSpawnProps, GetModelIndex(), 1.f, COLLISION_GROUP_NONE ); + + return hdr; + } + +#endif + + +#ifdef GAME_DLL +void CTFRobotDestruction_Robot::HandleAnimEvent( animevent_t *pEvent ) +{ + if ((pEvent->type & AE_TYPE_NEWEVENTSYSTEM) && (pEvent->type & AE_TYPE_SERVER)) + { + /* if ( pEvent->event == AE_RD_ROBOT_POP_PANELS_OFF ) + { + CUtlVector<breakmodel_t> vecProp; + FOR_EACH_VEC( m_aSpawnProps, i ) + { + char pstrLowerName[ MAX_PATH ]; + memset( pstrLowerName, 0, sizeof(pstrLowerName) ); + Q_snprintf( pstrLowerName, sizeof(pstrLowerName), "%s", pEvent->options ); + Q_strlower( pstrLowerName ); + if ( Q_strstr( m_aSpawnProps[i].modelName, pstrLowerName ) ) + { + vecProp.AddToTail( m_aSpawnProps[i] ); + } + } + + if ( vecProp.Count() ) + { + Vector vForward, vRight, vUp; + AngleVectors( GetAbsAngles(), &vForward, &vRight, &vUp ); + + Vector vecBreakVelocity = Vector(0,0,200); + AngularImpulse angularImpulse( RandomFloat( 0.0f, 120.0f ), RandomFloat( 0.0f, 120.0f ), 0.0 ); + Vector vecOrigin = GetAbsOrigin() + vForward*70 + vUp*10; + QAngle vecAngles = GetAbsAngles(); + breakablepropparams_t breakParams( vecOrigin, vecAngles, vecBreakVelocity, angularImpulse ); + breakParams.impactEnergyScale = 1.0f; + + int nModelIndex = modelinfo->GetModelIndex( STRING(GetModelName()) ); + CreateGibsFromList( vecProp, nModelIndex, NULL, breakParams, NULL, -1 , false, true ); + } + }*/ + } +} + +//----------------------------------------------------------------------------- +// Purpose: Tell the game logic we're gone +//----------------------------------------------------------------------------- +void CTFRobotDestruction_Robot::UpdateOnRemove( void ) +{ + BaseClass::UpdateOnRemove(); + + if ( CTFRobotDestructionLogic::GetRobotDestructionLogic() ) + { + CTFRobotDestructionLogic::GetRobotDestructionLogic()->RobotRemoved( this ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Play our death visual and audio effects +//----------------------------------------------------------------------------- +void CTFRobotDestruction_Robot::PlayDeathEffects() +{ + EmitSound( g_RobotData[ GetRobotSpawnData().m_eType ]->GetStringData( RobotData_t::DEATH_SOUND_KEY ) ); + EmitSound( ROBOT_DEATH_EXPLOSION ); + DispatchParticleEffect( DEATH_PARTICLE_EFFECT, GetAbsOrigin(), QAngle( 0,0,0 ) ); +} + +//----------------------------------------------------------------------------- +// Purpose: Handle getting killed +//----------------------------------------------------------------------------- +void CTFRobotDestruction_Robot::Event_Killed( const CTakeDamageInfo &info ) +{ + // Let the game logic know that we died + if ( CTFRobotDestructionLogic::GetRobotDestructionLogic() ) + { + CTFRobotDestructionLogic::GetRobotDestructionLogic()->RobotRemoved( this ); + } + + PlayDeathEffects(); + + // Find the killer & the scorer + CBaseEntity *pInflictor = info.GetInflictor(); + CBaseEntity *pKiller = info.GetAttacker(); + CTFPlayer *pScorer = ToTFPlayer( TFGameRules()->GetDeathScorer( pKiller, pInflictor, this ) ); + CTFPlayer *pAssister = NULL; + + if ( pScorer ) + { + // If a player is healing the scorer, give that player credit for the assist + CTFPlayer *pHealer = ToTFPlayer( static_cast<CBaseEntity *>( pScorer->m_Shared.GetFirstHealer() ) ); + // Must be a medic to receive a healing assist, otherwise engineers get credit for assists from dispensers doing healing. + // Also don't give an assist for healing if the inflictor was a sentry gun, otherwise medics healing engineers get assists for the engineer's sentry kills. + if ( pHealer && ( pHealer->GetPlayerClass()->GetClassIndex() == TF_CLASS_MEDIC ) ) + { + pAssister = pHealer; + } + + // Work out what killed the player, and send a message to all clients about it + int iWeaponID; + const char *killer_weapon_name = TFGameRules()->GetKillingWeaponName( info, NULL, &iWeaponID ); + const char *killer_weapon_log_name = killer_weapon_name; + + CTFWeaponBase *pWeapon = dynamic_cast< CTFWeaponBase * >( pScorer->Weapon_OwnsThisID( iWeaponID ) ); + if ( pWeapon ) + { + CEconItemView *pItem = pWeapon->GetAttributeContainer()->GetItem(); + + if ( pItem ) + { + if ( pItem->GetStaticData()->GetIconClassname() ) + { + killer_weapon_name = pItem->GetStaticData()->GetIconClassname(); + } + + if ( pItem->GetStaticData()->GetLogClassname() ) + { + killer_weapon_log_name = pItem->GetStaticData()->GetLogClassname(); + } + } + } + + IGameEvent *event = gameeventmanager->CreateEvent( "rd_robot_killed" ); + if ( event ) + { + if ( pAssister && ( pAssister != pScorer ) ) + { + event->SetInt( "assister", pAssister->GetUserID() ); + } + + event->SetInt( "attacker", pScorer->GetUserID() ); // attacker + event->SetString( "weapon", killer_weapon_name ); + event->SetString( "weapon_logclassname", killer_weapon_log_name ); + event->SetInt( "weaponid", iWeaponID ); + event->SetInt( "priority", 6 ); // HLTV event priority, not transmitted + + gameeventmanager->FireEvent( event ); + } + } + + if ( m_hSpawn ) + { + m_hSpawn->OnRobotKilled(); + } + + if ( m_hGroup ) + { + m_hGroup->OnRobotKilled(); + } + + // Kings dont die right away. Their head cracks open and they spew points out over time, then self-destruct + if ( m_spawnData.m_eType == ROBOT_TYPE_KING ) + { + // Change our model to be the damage king model + SetModel( g_RobotData[ m_spawnData.m_eType ]->GetStringData( RobotData_t::DAMAGED_MODEL_KEY ) ); + ResetSequence( LookupSequence("idle") ); + + m_takedamage = DAMAGE_NO; + SetContextThink( &CTFRobotDestruction_Robot::SpewBarsThink, gpGlobals->curtime, SPEW_BARS_CONTEXT ); + SetContextThink( &CTFRobotDestruction_Robot::SelfDestructThink, gpGlobals->curtime + 5.f, SELF_DESTRUCT_THINK ); + + return; + } + + // Spew our points + SpewBars( m_spawnData.m_nNumGibs ); + + // Spew ammo gibs out + SpewGibs(); + + CBaseAnimating::Event_Killed( info ); +} + +//----------------------------------------------------------------------------- +// Purpose: Spew out robot gibs that give ammo +//----------------------------------------------------------------------------- +void CTFRobotDestruction_Robot::SpewGibs() +{ + for ( int i=0; i<m_aGibs.Count(); i++ ) + { + CTFAmmoPack *pAmmoPack = CTFAmmoPack::Create( GetAbsOrigin() + m_aGibs[i].offset, GetAbsAngles(), this, m_aGibs[i].modelName ); + Assert( pAmmoPack ); + if ( pAmmoPack ) + { + pAmmoPack->ActivateWhenAtRest(); + + // Calculate the initial impulse on the weapon. + Vector vecImpulse( random->RandomFloat( -0.5f, 0.5f ), random->RandomFloat( -0.5f, 0.5f ), random->RandomFloat( 0.75f, 1.25f ) ); + VectorNormalize( vecImpulse ); + // Detect the head model + bool bIsTheHead = FStrEq( "models/bots/bot_worker/bot_worker_head_gib.mdl", m_aGibs[i].modelName ); + if ( bIsTheHead ) + { + // Pop more up than anything + vecImpulse[2] = 3.f; + vecImpulse *= random->RandomFloat( tf_obj_gib_velocity_max.GetFloat() * 0.75, tf_obj_gib_velocity_max.GetFloat() ); + } + else + { + vecImpulse *= random->RandomFloat( tf_obj_gib_velocity_min.GetFloat(), tf_obj_gib_velocity_max.GetFloat() ); + } + + + // Cap the impulse. + float flSpeed = vecImpulse.Length(); + if ( flSpeed > tf_obj_gib_maxspeed.GetFloat() ) + { + VectorScale( vecImpulse, tf_obj_gib_maxspeed.GetFloat() / flSpeed, vecImpulse ); + } + + if ( pAmmoPack->VPhysicsGetObject() ) + { + AngularImpulse angImpulse( 0.f, random->RandomFloat( 0.f, 100.f ), 0.f ); + if ( bIsTheHead ) + { + // Make the head spin around like a top + angImpulse = AngularImpulse( RandomFloat(-60.f,60.f), RandomFloat(-60.f,60.f), 100000.f ); + } + pAmmoPack->VPhysicsGetObject()->SetVelocityInstantaneous( &vecImpulse, &angImpulse ); + } + + pAmmoPack->SetInitialVelocity( vecImpulse ); + + pAmmoPack->m_nSkin = ( GetTeamNumber() == TF_TEAM_RED ) ? 0 : 1; + + // Give the ammo pack some health, so that trains can destroy it. + pAmmoPack->SetCollisionGroup( COLLISION_GROUP_DEBRIS ); + pAmmoPack->m_takedamage = DAMAGE_YES; + pAmmoPack->SetHealth( 900 ); + pAmmoPack->m_bObjGib = true; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Do some special effects when we take damage +//----------------------------------------------------------------------------- +int CTFRobotDestruction_Robot::OnTakeDamage( const CTakeDamageInfo &info ) +{ + // Check teams + if ( info.GetAttacker() ) + { + if ( InSameTeam(info.GetAttacker()) ) + return 0; + + CBasePlayer *pAttacker = ToBasePlayer( info.GetAttacker() ); + if ( pAttacker ) + { + pAttacker->SetLastObjectiveTime( gpGlobals->curtime ); + } + } + + SetContextThink( &CTFRobotDestruction_Robot::RepairSelfThink, gpGlobals->curtime + 5.f, "RepairSelfThink" ); + + if ( m_bShielded ) + { + return 0; + } + + CTakeDamageInfo newInfo; + newInfo = info; + ModifyDamage( &newInfo ); + + // Get our attack vectors + Vector vecDamagePos = newInfo.GetDamagePosition(); + QAngle vecDamageAngles; + VectorAngles( -newInfo.GetDamageForce(), vecDamageAngles ); + // Use worldspace center if no damage position (happens with flamethrowers) + if ( vecDamagePos == vec3_origin ) + { + vecDamagePos = WorldSpaceCenter(); + } + + // Play a spark effect + DispatchParticleEffect( "rd_bot_impact_sparks", vecDamagePos, vecDamageAngles ); + + // Send an impulse event to the client for this bot + Vector vecImpulse( newInfo.GetDamageForce() ); + m_animController.Impulse( vecImpulse.Normalized() * 20.f ); + + IGameEvent *event = gameeventmanager->CreateEvent( "rd_robot_impact" ); + if ( event ) + { + event->SetInt( "entindex", entindex() ); + event->SetInt( "impulse_x", vecImpulse.x ); + event->SetInt( "impulse_y", vecImpulse.y ); + event->SetInt( "impulse_z", vecImpulse.z ); + + gameeventmanager->FireEvent( event ); + } + + if( m_hGroup ) + { + m_hGroup->OnRobotAttacked(); + } + + int nBaseResult = BaseClass::OnTakeDamage( newInfo ); + + // Let the game logic know that we got hurt + if ( CTFRobotDestructionLogic::GetRobotDestructionLogic() ) + CTFRobotDestructionLogic::GetRobotDestructionLogic()->RobotAttacked( this ); + + return nBaseResult; +} + + +void CTFRobotDestruction_Robot::ModifyDamage( CTakeDamageInfo *info ) const +{ + CTFPlayer *pAttacker = ToTFPlayer( info->GetAttacker() ); + if ( pAttacker ) + { + float flScale = 1.f; + + if ( pAttacker->IsPlayerClass( TF_CLASS_SCOUT ) ) + flScale = 1.5f; + else if( pAttacker->IsPlayerClass( TF_CLASS_SNIPER ) ) + flScale = 2.25f; + else if ( pAttacker->IsPlayerClass( TF_CLASS_SPY ) ) + flScale = 2.f; + else if ( pAttacker->IsPlayerClass( TF_CLASS_PYRO ) ) + flScale = 0.75; + else if ( pAttacker->IsPlayerClass( TF_CLASS_HEAVYWEAPONS ) ) + flScale = 0.75; + else if ( pAttacker->IsPlayerClass( TF_CLASS_MEDIC ) ) + flScale = 2.f; + + info->SetDamage( info->GetDamage() * flScale ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Override base traceattack to prevent visible effects from team members shooting me +//----------------------------------------------------------------------------- +void CTFRobotDestruction_Robot::TraceAttack( const CTakeDamageInfo &inputInfo, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator ) +{ + // Prevent team damage here so blood doesn't appear + if ( inputInfo.GetAttacker() && InSameTeam(inputInfo.GetAttacker()) ) + return; + + AddMultiDamage( inputInfo, this ); +} + +void CTFRobotDestruction_Robot::UpdateAnimsThink( void ) +{ + m_animController.Update(); + + SetContextThink( &CTFRobotDestruction_Robot::UpdateAnimsThink, gpGlobals->curtime, ANIMS_THINK ); +} + +//----------------------------------------------------------------------------- +// Purpose: Change our AI state to use a computer +//----------------------------------------------------------------------------- +void CTFRobotDestruction_Robot::InputStopAndUseComputer( inputdata_t &inputdata ) +{ + // TODO: Fire off into the AI +} + +//----------------------------------------------------------------------------- +// Purpose: Shoot bars out as we die +//----------------------------------------------------------------------------- +void CTFRobotDestruction_Robot::SpewBars( int nNumToSpew ) +{ + for( int i=0; i < nNumToSpew; ++i ) + { + CBonusPack *pBonusPack = assert_cast< CBonusPack* >( CreateEntityByName( "item_bonuspack" ) ); + if ( pBonusPack ) + { + pBonusPack->ChangeTeam( GetEnemyTeam( GetTeamNumber() ) ); + pBonusPack->SetDisabled( false ); + pBonusPack->SetAbsOrigin( GetAbsOrigin() + Vector(0,0,20) ); + pBonusPack->SetAbsAngles( QAngle( 0.f, RandomFloat( 0, 360.f ), 0.f ) ); + // Calculate the initial impulse on the cores + Vector vecImpulse( random->RandomFloat( -0.5, 0.5 ), random->RandomFloat( -0.5, 0.5 ), random->RandomFloat( 1.0, 1.25 ) ); + VectorNormalize( vecImpulse ); + vecImpulse *= random->RandomFloat( 125.f, 150.f ); + + // Cap the impulse. + float flSpeed = vecImpulse.Length(); + if ( flSpeed > tf_obj_gib_maxspeed.GetFloat() ) + { + VectorScale( vecImpulse, tf_obj_gib_maxspeed.GetFloat() / flSpeed, vecImpulse ); + } + + pBonusPack->SetCollisionGroup( COLLISION_GROUP_DEBRIS ); + pBonusPack->AddSpawnFlags( SF_NORESPAWN ); + pBonusPack->m_nSkin = GetTeamNumber() == TF_TEAM_RED ? 0 : 1; + + DispatchSpawn( pBonusPack ); + pBonusPack->DropSingleInstance( vecImpulse, NULL, 0, 0 ); + pBonusPack->SetCycle( RandomFloat( 0.f, 1.f ) ); + pBonusPack->SetGravity( 0.2f ); + } + } +} + +void CTFRobotDestruction_Robot::SpewBarsThink() +{ + int nNumToSpew = 1; + m_nPointsSpewed += nNumToSpew; + SpewBars( nNumToSpew ); + + if ( m_nPointsSpewed >= m_spawnData.m_nNumGibs ) + { + SelfDestructThink(); + } + else + { + SetContextThink( &CTFRobotDestruction_Robot::SpewBarsThink, gpGlobals->curtime + 0.1f, SPEW_BARS_CONTEXT ); + } +} + +void CTFRobotDestruction_Robot::SelfDestructThink() +{ + SpewGibs(); + SpewBars( m_spawnData.m_nNumGibs - m_nPointsSpewed ); + PlayDeathEffects(); + UTIL_Remove( this ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Repair ourselves! +//----------------------------------------------------------------------------- +void CTFRobotDestruction_Robot::RepairSelfThink() +{ + // Heal! + int nHealth = GetHealth(); + if ( tf_rd_robot_repair_rate.GetFloat() != 0.f ) + { + nHealth += GetMaxHealth() / tf_rd_robot_repair_rate.GetFloat(); + } + nHealth = Min( nHealth, GetMaxHealth() ); + SetHealth( nHealth ); + + // Continue to heal if we're still hurt + if ( GetHealth() != GetMaxHealth() ) + { + SetContextThink( &CTFRobotDestruction_Robot::RepairSelfThink, gpGlobals->curtime + 1.f, "RepairSelfThink" ); + } +} + +void CTFRobotDestruction_Robot::ArriveAtPath() +{ + m_hNextPath->AcceptInput( "InPass", this, this, variant_t(), 0 ); + m_hNextPath = m_hNextPath->GetNext(); +} + +void CTFRobotDestruction_Robot::EnableUber() +{ + m_bShielded = true; + m_nSkin = GetTeamNumber() == TF_TEAM_RED ? 2 : 3; + + if ( m_hGroup ) + { + m_hGroup->UpdateState(); + } +} + +void CTFRobotDestruction_Robot::DisableUber() +{ + m_bShielded = false; + m_nSkin = GetTeamNumber() == TF_TEAM_RED ? 0 : 1; + + if ( m_hGroup ) + { + m_hGroup->UpdateState(); + } +} + +void CTFRobotDestruction_Robot::SetNewActivity( Activity activity ) +{ + int nSequence = SelectWeightedSequence( activity ); + if ( nSequence ) + { + SetSequence( nSequence ); + SetPlaybackRate( 1.0f ); + SetCycle( 0 ); + ResetSequenceInfo(); + } +} + +#define CLOSE_ENOUGH_TO_PATH 50.f + +//--------------------------------------------------------------------------------------------- +class CRobotPatrol : public Action< CTFRobotDestruction_Robot > +{ +public: + void PlayIdleActivity( CTFRobotDestruction_Robot *pMe ) + { + pMe->SetNewActivity( ACT_BOT_PRIMARY_MOVEMENT ); + } + + virtual ActionResult< CTFRobotDestruction_Robot > OnStart( CTFRobotDestruction_Robot *pMe, Action< CTFRobotDestruction_Robot > *priorAction ) + { + PlayIdleActivity( pMe ); + + return Continue(); + } + + virtual ActionResult< CTFRobotDestruction_Robot > OnResume( CTFRobotDestruction_Robot *pMe, Action< CTFRobotDestruction_Robot > *interruptingAction ) + { + PlayIdleActivity( pMe ); + + return Continue(); + } + + virtual ActionResult< CTFRobotDestruction_Robot > Update( CTFRobotDestruction_Robot *pMe, float interval ) + { + CPathTrack* pNextPath = pMe->GetNextPath(); + + if ( pMe->IsRangeGreaterThan( pNextPath, CLOSE_ENOUGH_TO_PATH ) ) + { + if ( m_path.GetAge() > 0.5f ) + { + CRobotPathCost cost( pMe ); + m_path.Compute( pMe, pNextPath->GetAbsOrigin(), cost ); + } + + m_path.Update( pMe ); + } + else + { + pMe->ArriveAtPath(); + } + + return Continue(); + } + + virtual const char *GetName( void ) const { return "Patrol"; } // return name of this action + PathFollower m_path; +}; + +class CRobotSpawn : public Action< CTFRobotDestruction_Robot > +{ + virtual ActionResult< CTFRobotDestruction_Robot > OnStart( CTFRobotDestruction_Robot *pMe, Action< CTFRobotDestruction_Robot > *priorAction ) + { + pMe->SetNewActivity( ACT_BOT_SPAWN ); + return Continue(); + } + + virtual ActionResult< CTFRobotDestruction_Robot > Update( CTFRobotDestruction_Robot *pMe, float interval ) + { + if ( pMe->IsActivityFinished() ) + { + return ChangeTo( new CRobotPatrol, "I've finished my spawn sequence" ); + } + + return Continue(); + } + + EventDesiredResult< CTFRobotDestruction_Robot > OnInjured( CTFRobotDestruction_Robot *pMe, const CTakeDamageInfo &info ) + { + return TryToSustain( RESULT_CRITICAL, "I'm spawning and being attacked" ); + } + + virtual void OnEnd( CTFRobotDestruction_Robot *pMe, Action< CTFRobotDestruction_Robot > *nextAction ) + { + pMe->SetBodygroup( pMe->FindBodygroupByName( "head_shell" ), 1 ); + pMe->SetBodygroup( pMe->FindBodygroupByName( "body_shell" ), 1 ); + } + + virtual const char *GetName( void ) const { return "Spawn"; } // return name of this action +}; + +class CRobotMaterialize : public Action< CTFRobotDestruction_Robot > +{ +public: + virtual ActionResult< CTFRobotDestruction_Robot > OnStart( CTFRobotDestruction_Robot *pMe, Action< CTFRobotDestruction_Robot > *priorAction ) + { + // TODO: Play the materialize anim and effects + /*int nSequence = pMe->SelectWeightedSequence( ACT_BOT_MATERIALIZE ); + pMe->ResetSequence( nSequence );*/ + return Continue(); + } + + virtual ActionResult< CTFRobotDestruction_Robot > Update( CTFRobotDestruction_Robot *pMe, float interval ) + { + // TODO: Check if materialize activity is finished + //if ( pMe->IsActivityFinished() ) + { + return ChangeTo( new CRobotSpawn, "I've fully materialized" ); + } + + // TODO: Control the materialize anim + + return Continue(); + } + + virtual const char *GetName( void ) const { return "Materialize"; } // return name of this action + PathFollower m_path; +}; + + +class CRobotPanic : public Action< CTFRobotDestruction_Robot > +{ +public: + virtual ActionResult< CTFRobotDestruction_Robot > OnStart( CTFRobotDestruction_Robot *pMe, Action< CTFRobotDestruction_Robot > *priorAction ); + virtual ActionResult< CTFRobotDestruction_Robot > Update( CTFRobotDestruction_Robot *pMe, float interval ); + virtual EventDesiredResult< CTFRobotDestruction_Robot > OnInjured( CTFRobotDestruction_Robot *pMe, const CTakeDamageInfo &info ); + virtual void OnEnd( CTFRobotDestruction_Robot *pMe, Action< CTFRobotDestruction_Robot > *nextAction ); + + virtual const char *GetName( void ) const { return "Panic"; } + +private: + + CountdownTimer m_SpeakTimer; + CountdownTimer m_attackedTimer; + CountdownTimer m_spinTimer; + bool m_bSpinRight; +}; + +class CRobotEnterPanic : public Action< CTFRobotDestruction_Robot > +{ + virtual ActionResult< CTFRobotDestruction_Robot > OnStart( CTFRobotDestruction_Robot *pMe, Action< CTFRobotDestruction_Robot > *priorAction ) + { + pMe->SetNewActivity( ACT_BOT_PANIC_START ); + return Continue(); + } + + virtual ActionResult< CTFRobotDestruction_Robot > Update( CTFRobotDestruction_Robot *pMe, float interval ) + { + if ( pMe->IsActivityFinished() ) + { + return ChangeTo( new CRobotPanic, "I've finished my enter panic sequence" ); + } + + return Continue(); + } + + EventDesiredResult< CTFRobotDestruction_Robot > OnInjured( CTFRobotDestruction_Robot *pMe, const CTakeDamageInfo &info ) + { + return TryToSustain( RESULT_CRITICAL, "I'm entering panic and being attacked" ); + } + + virtual const char *GetName( void ) const { return "Enter Panic"; } // return name of this action +}; + +class CRobotLeavePanic : public Action< CTFRobotDestruction_Robot > +{ + virtual ActionResult< CTFRobotDestruction_Robot > OnStart( CTFRobotDestruction_Robot *pMe, Action< CTFRobotDestruction_Robot > *priorAction ) + { + pMe->SetNewActivity( ACT_BOT_PANIC_END ); + return Continue(); + } + + virtual ActionResult< CTFRobotDestruction_Robot > Update( CTFRobotDestruction_Robot *pMe, float interval ) + { + if ( pMe->IsActivityFinished() ) + { + return Done( "I've finished my leave panic sequence" ); + } + + return Continue(); + } + + EventDesiredResult< CTFRobotDestruction_Robot > OnInjured( CTFRobotDestruction_Robot *pMe, const CTakeDamageInfo &info ) + { + return TryToSustain( RESULT_CRITICAL, "I'm leaving panic and being attacked" ); + } + + virtual const char *GetName( void ) const { return "Leave Panic"; } // return name of this action +}; + +//--------------------------------------------------------------------------------------------- +ActionResult< CTFRobotDestruction_Robot > CRobotPanic::OnStart( CTFRobotDestruction_Robot *pMe, Action< CTFRobotDestruction_Robot > *priorAction ) +{ + m_bSpinRight = RandomInt(0,1) == 1; // Randomly pick which way to spin + pMe->SetIsPanicked( true ); // Let our bot know he's panicked + m_attackedTimer.Start( 5.f ); // We panic for 5 seconds + m_spinTimer.Start( RandomFloat( 0.75f, 1.25f ) ); // Spin for a little bit + pMe->GetLocomotionInterface()->SetDesiredSpeed( 150.f ); // We go fast when panicked + DispatchParticleEffect( "rocketjump_smoke", PATTACH_POINT_FOLLOW, pMe, "wheel" ); // Smoke trails on our tire when panicked + pMe->SetNewActivity( ACT_BOT_PANIC ); // Play panicked activity + + m_SpeakTimer.Start( 3.f ); + const RobotSpawnData_t & data = pMe->GetRobotSpawnData(); + pMe->EmitSound( g_RobotData[ data.m_eType ]->GetStringData( RobotData_t::HURT_SOUND_KEY ) ); + + return Continue(); +} + +ActionResult< CTFRobotDestruction_Robot > CRobotPanic::Update( CTFRobotDestruction_Robot *pMe, float interval ) +{ + // If we haven't been attacked in awhile, then we're done panicking + if ( m_attackedTimer.IsElapsed() ) + { + return ChangeTo( new CRobotLeavePanic, "I'm done panicking" ); + } + + QAngle angles = pMe->GetLocalAngles(); + + // If our spin timer is still going, then spin! + if ( m_spinTimer.GetRemainingTime() < ( m_spinTimer.GetCountdownDuration() * 0.5f ) ) + { + float flSpinAmt = 2500.f * gpGlobals->frametime; + flSpinAmt *= m_bSpinRight ? 1.f : -1.f; + angles.y += flSpinAmt; + pMe->SetLocalAngles( angles ); + } + + // We just drive forard + Vector vForward; + AngleVectors( angles, &vForward ); + Vector vArrivePosition = pMe->GetAbsOrigin() + vForward * 30; + + trace_t tr; + // See if we hit anything solid a little bit below the robot. We dont want to jump off cliffs + UTIL_TraceLine( vArrivePosition, vArrivePosition + Vector(0,0,-30), MASK_PLAYERSOLID, pMe, COLLISION_GROUP_PLAYER_MOVEMENT, &tr ); + if ( tr.fraction < 1.0f ) + { + pMe->GetLocomotionInterface()->Approach( vArrivePosition ); + } + + // It's time change our spin direction, choose when to spin next, and reapply our smoke particle + if ( m_spinTimer.IsElapsed() ) + { + m_bSpinRight = RandomInt(0,1) == 1; + m_spinTimer.Start( RandomFloat( 0.75f, 1.25f ) ); + DispatchParticleEffect( "rocketjump_smoke", PATTACH_POINT_FOLLOW, pMe, "wheel" ); + } + + return Continue(); +} + +EventDesiredResult< CTFRobotDestruction_Robot > CRobotPanic::OnInjured( CTFRobotDestruction_Robot *pMe, const CTakeDamageInfo &info ) +{ + if ( m_SpeakTimer.IsElapsed() ) + { + m_SpeakTimer.Start( RandomFloat( 1.5f, 2.f ) ); + const RobotSpawnData_t & data = pMe->GetRobotSpawnData(); + pMe->EmitSound( g_RobotData[ data.m_eType ]->GetStringData( RobotData_t::HURT_SOUND_KEY ) ); + } + + m_attackedTimer.Start( m_attackedTimer.GetCountdownDuration() ); + return TryToSustain( RESULT_IMPORTANT, "I'm panicking and getting attacked" ); +} + +void CRobotPanic::OnEnd( CTFRobotDestruction_Robot *pMe, Action< CTFRobotDestruction_Robot > *nextAction ) +{ + pMe->SetIsPanicked( false ); + pMe->GetLocomotionInterface()->SetDesiredSpeed( 80.f ); +} + + +//--------------------------------------------------------------------------------------------- +Action< CTFRobotDestruction_Robot > *CRobotBehavior::InitialContainedAction( CTFRobotDestruction_Robot *pMe ) +{ + return new CRobotMaterialize; +} + +ActionResult< CTFRobotDestruction_Robot > CRobotBehavior::OnStart( CTFRobotDestruction_Robot *pMe, Action< CTFRobotDestruction_Robot > *priorAction ) +{ + return Continue(); +} + +#ifdef STAGING_ONLY +ConVar sv_rd_bots_STFU( "sv_rd_bots_STFU", "0", FCVAR_ARCHIVE ); +#endif +ActionResult< CTFRobotDestruction_Robot > CRobotBehavior::Update( CTFRobotDestruction_Robot *pMe, float interval ) +{ + //const CKnownEntity *pThreat = pMe->GetVisionInterface()->GetPrimaryKnownThreat(); + //if ( pThreat ) + //{ + // return SuspendFor( new CRobotAttackEnemy, "I see an enemy!" ); + //} +#ifdef STAGING_ONLY + if ( !sv_rd_bots_STFU.GetBool() ) +#endif + { + // We've been wandering for a bit. Speak! + if ( m_IdleSpeakTimer.IsElapsed() && m_SpeakTimer.IsElapsed() ) + { + m_SpeakTimer.Start( 1.f ); + m_IdleSpeakTimer.Start( RandomFloat( 6.f, 10.f ) ); + const RobotSpawnData_t & data = pMe->GetRobotSpawnData(); + pMe->EmitSound( g_RobotData[ data.m_eType ]->GetStringData( RobotData_t::IDLE_SOUND_KEY ) ); + } + } + + // Do stuff! + return Continue(); +} + +EventDesiredResult< CTFRobotDestruction_Robot > CRobotBehavior::OnInjured( CTFRobotDestruction_Robot *pMe, const CTakeDamageInfo &info ) +{ + return TrySuspendFor( new CRobotEnterPanic, RESULT_TRY, "I've been attacked" ); +} + + +EventDesiredResult< CTFRobotDestruction_Robot > CRobotBehavior::OnContact( CTFRobotDestruction_Robot *pMe, CBaseEntity *pOther, CGameTrace *result ) +{ + if ( m_SpeakTimer.IsElapsed() && ( pOther->IsPlayer() || dynamic_cast< CTFRobotDestruction_Robot * >( pOther ) ) ) + { + m_SpeakTimer.Start( 3.f ); + +#ifdef STAGING_ONLY + if ( !sv_rd_bots_STFU.GetBool() ) +#endif + { + const RobotSpawnData_t & data = pMe->GetRobotSpawnData(); + pMe->EmitSound( g_RobotData[ data.m_eType ]->GetStringData( RobotData_t::COLLIDE_SOUND_KEY ) ); + } + } + + return TryContinue( RESULT_TRY ); +} + + +//--------------------------------------------------------------------------------------------- +CRobotIntention::CRobotIntention( CTFRobotDestruction_Robot *pMe ) : IIntention( pMe ) +{ + m_behavior = new Behavior< CTFRobotDestruction_Robot >( new CRobotBehavior ); +} + +CRobotIntention::~CRobotIntention() +{ + delete m_behavior; +} + +void CRobotIntention::Reset( void ) +{ + delete m_behavior; + m_behavior = new Behavior< CTFRobotDestruction_Robot >( new CRobotBehavior ); +} + +void CRobotIntention::Update( void ) +{ + m_behavior->Update( static_cast< CTFRobotDestruction_Robot * >( GetBot() ), GetUpdateInterval() ); +} + +// is this a place we can be? +QueryResultType CRobotIntention::IsPositionAllowed( const INextBot *pMeBot, const Vector &pos ) const +{ + return ANSWER_YES; +} + + + +//--------------------------------------------------------------------------------------------- +float CRobotLocomotion::GetRunSpeed( void ) const +{ + CTFRobotDestruction_Robot *pRobotMe = static_cast< CTFRobotDestruction_Robot *>( GetBot()->GetEntity() ); + return pRobotMe->GetIsPanicked() ? 150.f : 80.f; +} + +float CRobotLocomotion::GetGroundSpeed() const +{ + CTFRobotDestruction_Robot *pRobotMe = static_cast< CTFRobotDestruction_Robot *>( GetBot()->GetEntity() ); + return pRobotMe->GetIsPanicked() ? 150.f : 80.f; +} + +//--------------------------------------------------------------------------------------------- +// if delta Z is greater than this, we have to jump to get up +float CRobotLocomotion::GetStepHeight( void ) const +{ + return 24.0f; +} + + +//--------------------------------------------------------------------------------------------- +// return maximum height of a jump +float CRobotLocomotion::GetMaxJumpHeight( void ) const +{ + return 40.0f; +} + + +//--------------------------------------------------------------------------------------------- +// Return max rate of yaw rotation +float CRobotLocomotion::GetMaxYawRate( void ) const +{ + return 200.0f; +} + + +//--------------------------------------------------------------------------------------------- +bool CRobotLocomotion::ShouldCollideWith( const CBaseEntity *object ) const +{ + return false; +} + +#endif + +//----------------------------------------------------------------------------- +// Robot Dispenser +//----------------------------------------------------------------------------- + +BEGIN_DATADESC( CRobotDispenser ) +END_DATADESC() + +IMPLEMENT_NETWORKCLASS_ALIASED( RobotDispenser, DT_RobotDispenser ) +LINK_ENTITY_TO_CLASS( rd_robot_dispenser, CRobotDispenser ); + +BEGIN_NETWORK_TABLE( CRobotDispenser, DT_RobotDispenser ) +END_NETWORK_TABLE() + +#ifdef GAME_DLL +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CRobotDispenser::CRobotDispenser() +{ + m_bUseGenerateMetalSound = false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CRobotDispenser::Spawn( void ) +{ + // This cast is for the benefit of GCC + m_fObjectFlags |= (int)OF_DOESNT_HAVE_A_MODEL; + m_takedamage = DAMAGE_NO; + m_iUpgradeLevel = 1; + + TFGameRules()->OnDispenserBuilt( this ); +} + +//----------------------------------------------------------------------------- +// Purpose: Finished building +//----------------------------------------------------------------------------- +void CRobotDispenser::OnGoActive( void ) +{ + BaseClass::OnGoActive(); + + if ( m_hTouchTrigger ) + { + m_hTouchTrigger->SetParent( GetParent() ); + } + + SetModel( "" ); +} + +//----------------------------------------------------------------------------- +// Spawn the vgui control screens on the object +//----------------------------------------------------------------------------- +void CRobotDispenser::GetControlPanelInfo( int nPanelIndex, const char *&pPanelName ) +{ + // no panels + return; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CRobotDispenser::SetModel( const char *pModel ) +{ + CBaseObject::SetModel( pModel ); +} + + +#endif |