/*** * * 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. * ****/ #include "extdll.h" #include "util.h" #include "cbase.h" #include "monsters.h" #include "schedule.h" #include "nodes.h" #include "soundent.h" #include "animation.h" #include "effects.h" #include "explode.h" int gRobocopGibModel, gWaveSprite; void SpawnExplosion( Vector center, float randomRange, float time, int magnitude ); #define ROBOCOP_DAMAGE (DMG_ENERGYBEAM|DMG_CRUSH|DMG_MORTAR|DMG_BLAST) #define ROBOCOP_EYE_SPRITE_NAME "sprites/gargeye1.spr" #define ROBOCOP_EYE_BEAM_NAME "sprites/smoke.spr" #define ROBOCOP_MELEE_ATTACK_DIST 60.0f #define ROBOCOP_DEATH_DURATION 2.1f #define LF_ROBOCOP_LASER 1 #define LF_ROBOCOP_BEAMSPOT 2 #define LF_ROBOCOP_BEAM 4 #define LF_ROBOCOP_LOWBRIGHTNESS 8 #define LF_ROBOCOP_HIGHBRIGHTNESS 16 #define LF_ROBOCOP_FULLBRIGHTNESS 32 //========================================================= // Monster's Anim Events Go Here //========================================================= #define ROBOCOP_AE_RIGHT_FOOT 0x03 #define ROBOCOP_AE_LEFT_FOOT 0x04 #define ROBOCOP_AE_FIST 0x05 class CRoboCop : public CBaseMonster { public: void Spawn( void ); void Precache( void ); void UpdateOnRemove(); void SetYawSpeed( void ); int Classify( void ); void HandleAnimEvent( MonsterEvent_t *pEvent ); void SetActivity( Activity NewActivity ); BOOL CheckMeleeAttack1( float flDot, float flDist ); BOOL CheckMeleeAttack2( float flDot, float flDist ) { return FALSE; } BOOL CheckRangeAttack1( float flDot, float flDist ); BOOL CheckRangeAttack2( float flDot, float flDist ) { return FALSE; } void SetObjectCollisionBox( void ) { pev->absmin = pev->origin + Vector( -80, -80, 0 ); pev->absmax = pev->origin + Vector( 80, 80, 214 ); } void PrescheduleThink( void ); BOOL ShouldGibMonster( int iGib ) { return FALSE; } void Killed( entvars_t *pevAttacker, int iGib ); Schedule_t *GetScheduleOfType( int Type ); void StartTask( Task_t *pTask ); void RunTask( Task_t *pTask ); void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType ); int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); void FistAttack( void ); void CreateLaser( void ); void ChangeLaserState( void ); void HeadControls( float angleX, float angleY, bool zeropoint ); int Save( CSave &save ); int Restore( CRestore &restore ); CUSTOM_SCHEDULES; static TYPEDESCRIPTION m_SaveData[]; CSprite *m_pLaserPointer; CBeam *m_pBeam; CSprite *m_pBeamSpot; int m_iLaserFlags; // int m_iLaserAlpha; float m_flHeadX; float m_flHeadY; float m_flLaserTime; float m_flSparkTime; Vector m_vecAimPos; }; LINK_ENTITY_TO_CLASS( monster_robocop, CRoboCop ) TYPEDESCRIPTION CRoboCop::m_SaveData[] = { DEFINE_FIELD( CRoboCop, m_pLaserPointer, FIELD_CLASSPTR ), DEFINE_FIELD( CRoboCop, m_pBeam, FIELD_CLASSPTR ), DEFINE_FIELD( CRoboCop, m_pBeamSpot, FIELD_CLASSPTR ), DEFINE_FIELD( CRoboCop, m_iLaserFlags, FIELD_INTEGER ), DEFINE_FIELD( CRoboCop, m_flHeadX, FIELD_FLOAT ), DEFINE_FIELD( CRoboCop, m_flHeadY, FIELD_FLOAT ), DEFINE_FIELD( CRoboCop, m_vecAimPos, FIELD_POSITION_VECTOR ), }; IMPLEMENT_SAVERESTORE( CRoboCop, CBaseMonster ) //========================================================= // AI Schedules Specific to this monster //========================================================= enum { SCHED_ROBOCOP_LASERFAIL = LAST_COMMON_SCHEDULE + 1, }; enum { TASK_ROBOCOP_LASER_SOUND = LAST_COMMON_TASK + 1, TASK_ROBOCOP_LASER_CHARGE, TASK_ROBOCOP_LASER_ON, TASK_ROBOCOP_MORTAR_SPAWN, TASK_ROBOCOP_LASER_OFF }; Task_t tlRoboCopLaser[] = { { TASK_SET_FAIL_SCHEDULE, (float)SCHED_ROBOCOP_LASERFAIL }, { TASK_STOP_MOVING, 0.0f }, { TASK_FACE_ENEMY, 0.0f }, { TASK_SET_ACTIVITY, (float)ACT_RANGE_ATTACK1 }, { TASK_ROBOCOP_LASER_CHARGE, 2.0f }, { TASK_ROBOCOP_LASER_SOUND, 0.0f }, { TASK_ROBOCOP_LASER_ON, 1.0f }, { TASK_ROBOCOP_MORTAR_SPAWN, 0.1f }, { TASK_ROBOCOP_LASER_OFF, 1.0f } }; Schedule_t slRoboCopLaser[] = { { tlRoboCopLaser, ARRAYSIZE( tlRoboCopLaser ), bits_COND_TASK_FAILED | bits_COND_NEW_ENEMY | bits_COND_CAN_MELEE_ATTACK1 | bits_COND_HEAVY_DAMAGE, 0, "RoboCopLaser" } }; Task_t tlRoboCopLaserFail[] = { { TASK_ROBOCOP_LASER_OFF, 0.0f }, { TASK_SET_ACTIVITY, (float)ACT_IDLE }, { TASK_WAIT, 2.0f }, { TASK_WAIT_PVS, 0.0f } }; Schedule_t slRoboCopLaserFail[] = { { tlRoboCopLaserFail, ARRAYSIZE( tlRoboCopLaserFail ), bits_COND_CAN_RANGE_ATTACK1 | bits_COND_CAN_MELEE_ATTACK1 | bits_COND_CAN_RANGE_ATTACK2 | bits_COND_CAN_MELEE_ATTACK2, 0, "RoboCopLaserFail" } }; DEFINE_CUSTOM_SCHEDULES( CRoboCop ) { slRoboCopLaser, slRoboCopLaserFail }; IMPLEMENT_CUSTOM_SCHEDULES( CRoboCop, CBaseMonster ) void CRoboCop::FistAttack( void ) { int i; unsigned char r, g, b; TraceResult trace; Vector vecDist; float flDist, flAdjustedDamage; UTIL_MakeVectors( pev->angles ); Vector vecSrc = pev->origin + 12 * gpGlobals->v_right + 95 * gpGlobals->v_forward; for( i = 0; i < 3; i++ ) { switch( i ) { case 0: r = 101; g = 133; b = 221; break; case 1: r = 67; g = 85; b = 255; break; case 2: r = 62; g = 33; b = 211; break; } // blast circles MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin ); WRITE_BYTE( TE_BEAMCYLINDER ); WRITE_COORD( vecSrc.x ); WRITE_COORD( vecSrc.y ); WRITE_COORD( vecSrc.z + 16 ); WRITE_COORD( vecSrc.x ); WRITE_COORD( vecSrc.y ); WRITE_COORD( vecSrc.z + gSkillData.robocopSWRadius / ( ( i + 1 ) * 0.2f ) ); // reach damage radius over .3 seconds WRITE_SHORT( gWaveSprite ); WRITE_BYTE( 0 ); // startframe WRITE_BYTE( 10 ); // framerate WRITE_BYTE( i + 2 ); // life WRITE_BYTE( 32 ); // width WRITE_BYTE( 0 ); // noise WRITE_BYTE( r ); // r WRITE_BYTE( g ); // g WRITE_BYTE( b ); // b WRITE_BYTE( 255 ); //brightness WRITE_BYTE( 0 ); // speed MESSAGE_END(); } CBaseEntity *pEntity = NULL; // iterate on all entities in the vicinity. while( ( pEntity = UTIL_FindEntityInSphere( pEntity, pev->origin, gSkillData.robocopSWRadius ) ) != NULL ) { if( pEntity->pev->takedamage != DAMAGE_NO ) { // Robocop does not take damage from it's own attacks. if( pEntity != this ) { // houndeyes do FULL damage if the ent in question is visible. Half damage otherwise. // This means that you must get out of the houndeye's attack range entirely to avoid damage. // Calculate full damage first vecDist = pEntity->Center() - vecSrc; flDist = Q_max( 0, gSkillData.robocopSWRadius - vecDist.Length() ); flDist = flDist / gSkillData.robocopSWRadius; if( !FVisible( pEntity ) ) { if( pEntity->IsPlayer() ) { // if this entity is a client, and is not in full view, inflict half damage. We do this so that players still // take the residual damage if they don't totally leave the houndeye's effective radius. We restrict it to clients // so that monsters in other parts of the level don't take the damage and get pissed. flDist *= 0.5f; } else if( !FClassnameIs( pEntity->pev, "func_breakable" ) && !FClassnameIs( pEntity->pev, "func_pushable" ) ) { // do not hurt nonclients through walls, but allow damage to be done to breakables flDist = 0; } } flAdjustedDamage = gSkillData.robocopDmgFist * flDist; //ALERT ( at_aiconsole, "Damage: %f\n", flAdjustedDamage ); if( flAdjustedDamage > 0 ) { pEntity->TakeDamage( pev, pev, flAdjustedDamage, DMG_SONIC ); } if( pEntity->IsPlayer() ) { vecDist = vecDist.Normalize(); vecDist.x = vecDist.x * flDist * 600.0f; vecDist.y = vecDist.y * flDist * 600.0f; vecDist.z = flDist * 450.0f; pEntity->pev->velocity = vecDist + pEntity->pev->velocity; pEntity->pev->punchangle.x = 5; } } } } UTIL_ScreenShake( pev->origin, 12.0f, 100.0f, 2.0f, 1000 ); EMIT_SOUND_DYN( edict(), CHAN_WEAPON, "robocop/rc_fist.wav", 1.0f, ATTN_NORM, 0, PITCH_NORM + RANDOM_LONG( -10, 10 ) ); } void CRoboCop::CreateLaser( void ) { m_pLaserPointer = CSprite::SpriteCreate( ROBOCOP_EYE_SPRITE_NAME, pev->origin, FALSE ); m_pLaserPointer->SetTransparency( kRenderTransAdd, 255, 255, 255, 0, kRenderFxNone ); m_pLaserPointer->SetAttachment( edict(), 1 ); m_pLaserPointer->SetScale( 0.5f ); m_pBeamSpot = CSprite::SpriteCreate( ROBOCOP_EYE_SPRITE_NAME, pev->origin, FALSE ); m_pBeamSpot->pev->origin = pev->origin; m_pBeamSpot->SetTransparency( kRenderTransAdd, 255, 255, 255, 0, kRenderFxNone ); m_pBeamSpot->SetScale( 0.3f ); m_pBeam = CBeam::BeamCreate( ROBOCOP_EYE_BEAM_NAME, 30 ); m_pBeam->PointEntInit( pev->origin, entindex() ); m_pBeam->SetEndAttachment( 1 ); m_pBeam->SetBrightness( 0 ); m_pBeam->SetColor( 255, 0, 0 ); m_pBeam->SetScrollRate( 15 ); ChangeLaserState(); } void CRoboCop::ChangeLaserState( void ) { float time; int brightness; if( m_pBeam ) m_pBeam->SetEndAttachment( 1 ); if( !m_iLaserFlags ) { if( m_pBeam ) m_pBeam->SetBrightness( 0 ); if( m_pLaserPointer ) m_pLaserPointer->SetBrightness( 0 ); if( m_pBeamSpot ) m_pBeamSpot->SetBrightness( 0 ); return; } if( m_iLaserFlags & LF_ROBOCOP_LOWBRIGHTNESS ) { time = gpGlobals->time; } else { if( !( m_iLaserFlags & LF_ROBOCOP_HIGHBRIGHTNESS ) ) { if( m_iLaserFlags & LF_ROBOCOP_FULLBRIGHTNESS ) { brightness = 224; goto end; } } time = gpGlobals->time * 5.0f; } brightness = fabs( sin( time ) * 255.0f ); end: if( m_iLaserFlags & LF_ROBOCOP_LASER ) { if( m_pLaserPointer ) m_pLaserPointer->SetBrightness( brightness ); } if( m_iLaserFlags & LF_ROBOCOP_BEAM ) { if( m_pBeam ) m_pBeam->SetBrightness( brightness ); } if( m_iLaserFlags & LF_ROBOCOP_BEAMSPOT ) { if( m_pBeamSpot ) m_pBeamSpot->SetBrightness( brightness ); } } void CRoboCop::HeadControls( float angleX, float angleY, bool zeropoint ) { if( angleY < -180 ) angleY += 360; else if( angleY > 180 ) angleY -= 360; if( angleY < -45 ) angleY = -45; else if( angleY > 45 ) angleY = 45; if( zeropoint ) { m_flHeadX = angleX; m_flHeadY = angleY; } else { m_flHeadX = UTIL_ApproachAngle( angleX, m_flHeadX, 4 ); m_flHeadY = UTIL_ApproachAngle( angleY, m_flHeadY, 8 ); } SetBoneController( 0, m_flHeadY ); SetBoneController( 1, m_flHeadX ); } void CRoboCop::PrescheduleThink( void ) { if( m_flLaserTime < gpGlobals->time && m_iLaserFlags != ( LF_ROBOCOP_LASER | LF_ROBOCOP_LOWBRIGHTNESS ) ) { m_iLaserFlags = 0; ChangeLaserState(); m_iLaserFlags = ( LF_ROBOCOP_LASER | LF_ROBOCOP_LOWBRIGHTNESS ); } ChangeLaserState(); } //========================================================= // Classify - indicates this monster's place in the // relationship table. //========================================================= int CRoboCop::Classify( void ) { return CLASS_ALIEN_MONSTER; } //========================================================= // SetYawSpeed - allows each sequence to have a different // turn rate associated with it. //========================================================= void CRoboCop::SetYawSpeed( void ) { int ys; switch( m_Activity ) { case ACT_TURN_LEFT: case ACT_TURN_RIGHT: ys = 180; break; default: ys = 90; break; } pev->yaw_speed = ys; } //========================================================= // Spawn //========================================================= void CRoboCop::Spawn() { Precache(); SET_MODEL( ENT( pev ), "models/robocop.mdl" ); UTIL_SetSize( pev, Vector( -32, -32, 0 ), Vector( 32, 32, 128 ) ); pev->solid = SOLID_SLIDEBOX; pev->movetype = MOVETYPE_STEP; m_bloodColor = DONT_BLEED; pev->health = gSkillData.robocopHealth; m_flFieldOfView = -0.7;// width of forward view cone ( as a dotproduct result ) m_MonsterState = MONSTERSTATE_NONE; MonsterInit(); CreateLaser(); m_flSparkTime = gpGlobals->time + 2.0f; } //========================================================= // Precache - precaches all resources this monster needs //========================================================= void CRoboCop::Precache() { PRECACHE_MODEL( "models/robocop.mdl" ); PRECACHE_MODEL( ROBOCOP_EYE_SPRITE_NAME ); PRECACHE_MODEL( ROBOCOP_EYE_BEAM_NAME ); gWaveSprite = PRECACHE_MODEL( "sprites/xbeam3.spr" ); gRobocopGibModel = PRECACHE_MODEL( "models/metalplategibs.mdl" ); PRECACHE_SOUND( "weapons/mortar.wav" ); PRECACHE_SOUND( "ambience/sparks.wav" ); PRECACHE_SOUND( "robocop/rc_charge.wav" ); PRECACHE_SOUND( "robocop/rc_fist.wav" ); PRECACHE_SOUND( "robocop/rc_laser.wav" ); PRECACHE_SOUND( "robocop/rc_step1.wav" ); PRECACHE_SOUND( "robocop/rc_step2.wav" ); UTIL_PrecacheOther( "monster_mortar" ); } void CRoboCop::UpdateOnRemove() { CBaseEntity::UpdateOnRemove(); if( m_pLaserPointer ) { UTIL_Remove( m_pLaserPointer ); m_pLaserPointer = 0; } if( m_pBeam ) { UTIL_Remove( m_pBeam ); m_pBeam = 0; } if( m_pBeamSpot ) { UTIL_Remove( m_pBeamSpot ); m_pBeamSpot = 0; } } void CRoboCop::TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType ) { bitsDamageType &= ROBOCOP_DAMAGE; if( IsAlive() && !FBitSet( bitsDamageType, ROBOCOP_DAMAGE ) ) { if( pev->dmgtime != gpGlobals->time || (RANDOM_LONG( 0, 100 ) < 20 ) ) { UTIL_Ricochet( ptr->vecEndPos, RANDOM_FLOAT( 0.5f, 1.5f ) ); pev->dmgtime = gpGlobals->time; } flDamage = 0.0f; } CBaseMonster::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType ); } int CRoboCop::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) { ALERT( at_aiconsole, "RoboCop: TakeDamage\n" ); if( IsAlive() ) { if( !( bitsDamageType & ROBOCOP_DAMAGE ) ) flDamage *= 0.01f; if( bitsDamageType & DMG_BLAST ) SetConditions( bits_COND_LIGHT_DAMAGE ); } return CBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType ); } void CRoboCop::Killed( entvars_t *pevAttacker, int iGib ) { UTIL_Remove( m_pLaserPointer ); UTIL_Remove( m_pBeam ); UTIL_Remove( m_pBeamSpot ); m_pLaserPointer = NULL; m_pBeam = NULL; m_pBeamSpot = NULL; CBaseMonster::Killed( pevAttacker, GIB_NEVER ); } BOOL CRoboCop::CheckMeleeAttack1( float flDot, float flDist ) { //ALERT( at_aiconsole, "CheckMelee(%f, %f)\n", flDot, flDist ); if( gpGlobals->time > m_flLaserTime ) { if( flDot >= 0.8f && flDist < gSkillData.robocopSWRadius ) { return TRUE; } } return FALSE; } //========================================================= // CheckRangeAttack1 // flDot is the cos of the angle of the cone within which // the attack can occur. //========================================================= BOOL CRoboCop::CheckRangeAttack1( float flDot, float flDist ) { if( gpGlobals->time > m_flLaserTime && gpGlobals->time > m_flSparkTime ) { if( flDot >= 0.8f && flDist > gSkillData.robocopSWRadius ) { if( flDist < 4096.0f ) return TRUE; } } return FALSE; } void CRoboCop::HandleAnimEvent( MonsterEvent_t *pEvent ) { switch( pEvent->event ) { case ROBOCOP_AE_RIGHT_FOOT: case ROBOCOP_AE_LEFT_FOOT: UTIL_ScreenShake( pev->origin, 4.0f, 3.0f, 1.0f, 250.0f ); EMIT_SOUND_DYN( ENT( pev ), CHAN_BODY, RANDOM_LONG( 0, 1 ) ? "robocop/rc_step2.wav" : "robocop/rc_step1.wav", 1, ATTN_NORM, 0, PITCH_NORM + RANDOM_LONG( -10, 10 ) ); break; case ROBOCOP_AE_FIST: FistAttack(); m_flLaserTime = gpGlobals->time + 2.0f; break; default: CBaseMonster::HandleAnimEvent( pEvent ); break; } } Schedule_t *CRoboCop::GetScheduleOfType( int Type ) { switch( Type ) { case SCHED_RANGE_ATTACK1: return slRoboCopLaser; case SCHED_ROBOCOP_LASERFAIL: return slRoboCopLaserFail; } return CBaseMonster::GetScheduleOfType( Type ); } void CRoboCop::StartTask( Task_t *pTask ) { TraceResult tr; switch( pTask->iTask ) { case TASK_ROBOCOP_LASER_CHARGE: // 92 { ALERT( at_aiconsole, "RoboCop: Charge Laser\n" ); EMIT_SOUND_DYN( ENT( pev ), CHAN_BODY, "robocop/rc_charge.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM ); m_iLaserFlags = ( LF_ROBOCOP_LASER | LF_ROBOCOP_HIGHBRIGHTNESS ); m_flWaitFinished = gpGlobals->time + pTask->flData; m_flSparkTime = m_flLaserTime = gpGlobals->time + 10.0f; } break; case TASK_ROBOCOP_LASER_ON: // 93: { ALERT( at_aiconsole, "RoboCop: Laser on\n" ); m_iLaserFlags = ( LF_ROBOCOP_LASER | LF_ROBOCOP_BEAM | LF_ROBOCOP_BEAMSPOT | LF_ROBOCOP_FULLBRIGHTNESS ); m_flWaitFinished = gpGlobals->time + pTask->flData; if( m_pBeam ) { m_pBeam->pev->origin = m_vecAimPos; m_pBeam->SetEndAttachment( 1 ); } if( m_pBeamSpot ) m_pBeamSpot->pev->origin = m_vecAimPos; m_failSchedule = 0; } break; case TASK_ROBOCOP_MORTAR_SPAWN: //94: { ALERT( at_aiconsole, "RoboCop: Spawn mortar\n" ); UTIL_TraceLine( m_vecAimPos, m_vecAimPos - Vector( 0, 0, 1024 ), ignore_monsters, ENT( pev ), &tr ); CBaseEntity *pMortar = Create( "monster_mortar", tr.vecEndPos, g_vecZero, 0 ); pMortar->pev->nextthink = gpGlobals->time + 0.1f; pMortar->pev->dmg = gSkillData.robocopDmgMortar; m_flWaitFinished = gpGlobals->time + pTask->flData; } break; case TASK_ROBOCOP_LASER_SOUND: // 91 EMIT_SOUND_DYN( ENT( pev ), CHAN_BODY, "robocop/rc_laser.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM ); TaskComplete(); break; case TASK_ROBOCOP_LASER_OFF: // 95 HeadControls( 0.0f, 0.0f, true ); m_iLaserFlags = 0; ChangeLaserState(); ALERT( at_aiconsole, "RoboCop: Laser off\n" ); m_flSparkTime = gpGlobals->time + 2.0f; m_flLaserTime = gpGlobals->time + 1.0f; EMIT_SOUND_DYN( ENT( pev ), CHAN_BODY, "common/null.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM ); TaskComplete(); break; case TASK_DIE: m_flWaitFinished = gpGlobals->time + ROBOCOP_DEATH_DURATION; ALERT( at_aiconsole, "RoboCop: Die\n" ); m_iLaserFlags = ( LF_ROBOCOP_LASER | LF_ROBOCOP_LOWBRIGHTNESS ); pev->renderamt = 15; pev->renderfx = kRenderFxGlowShell; pev->rendercolor = Vector( 67, 85, 255 ); pev->health = 0; EMIT_SOUND_DYN( ENT( pev ), CHAN_BODY, "ambience/sparks.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM ); m_flSparkTime = gpGlobals->time + 0.3f; default: CBaseMonster::StartTask( pTask ); break; } } void CRoboCop::RunTask( Task_t *pTask ) { TraceResult tr; switch( pTask->iTask ) { case TASK_ROBOCOP_LASER_ON: case TASK_ROBOCOP_MORTAR_SPAWN: if( gpGlobals->time > m_flWaitFinished ) TaskComplete(); break; case TASK_ROBOCOP_LASER_CHARGE: { if( gpGlobals->time > m_flWaitFinished ) TaskComplete(); if( m_hEnemy == 0 ) { TaskFail(); return; } Vector vecSrc = m_hEnemy->pev->origin; Vector vecDir, vecAngle; UTIL_TraceLine( vecSrc + Vector( 0, 0, 1024 ), vecSrc - Vector( 0, 0, 1024 ), ignore_monsters, ENT( pev ), &tr ); GetAttachment( 0, vecSrc, vecAngle ); vecDir = ( tr.vecEndPos - vecSrc ).Normalize(); UTIL_TraceLine( vecSrc, vecSrc + vecDir * 4096, dont_ignore_monsters, ENT( pev ), &tr ); m_vecAimPos = tr.vecEndPos; vecAngle = UTIL_VecToAngles( vecDir ); vecAngle.y = UTIL_AngleDiff( vecAngle.y, pev->angles.y ); vecAngle.x = -vecAngle.x; if( fabs( vecAngle.y ) > ROBOCOP_MELEE_ATTACK_DIST ) { ALERT( at_aiconsole, "RoboCop: Lost - FOV\n" ); TaskFail(); return; } if( ( m_vecAimPos - vecSrc ).Length() < gSkillData.robocopSWRadius ) { ALERT( at_aiconsole, "RoboCop: Lost - Proximity\n" ); TaskFail(); return; } HeadControls( vecAngle.x, vecAngle.y, false ); } break; case TASK_DIE: if( m_flWaitFinished <= gpGlobals->time ) { if( m_fSequenceFinished && pev->frame >= 255.0f ) { EMIT_SOUND_DYN( ENT( pev ), CHAN_VOICE, "common/null.wav", 1.0, ATTN_NORM, 0, 100 ); 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( gRobocopGibModel ); //model id# // # of shards WRITE_BYTE( 20 ); // duration WRITE_BYTE( 20 );// 3.0 seconds // flags WRITE_BYTE( BREAK_FLESH ); MESSAGE_END(); SpawnExplosion( pev->origin, 70, 0, 150 ); int trailCount = RANDOM_LONG( 2, 4 ); for( int i = 0; i < trailCount; i++ ) Create( "fire_trail", pev->origin, Vector( 0, 0, 1 ), NULL ); SetBodygroup( 0, 1 ); CBaseMonster::RunTask( pTask ); return; } } if( gpGlobals->time <= m_flSparkTime ) { Create( "spark_shower", pev->origin, Vector( 0, 0, 1 ), NULL ); EMIT_SOUND_DYN( ENT( pev ), CHAN_VOICE, "ambience/sparks.wav", 1.0, ATTN_NORM, 0, 100 ); m_flSparkTime = gpGlobals->time + 0.3f; } default: CBaseMonster::RunTask( pTask ); break; } } void CRoboCop::SetActivity( Activity NewActivity ) { CBaseMonster::SetActivity( NewActivity ); switch( m_Activity ) { case ACT_WALK: m_flGroundSpeed = 220.0f; break; default: m_flGroundSpeed = 220.0f; break; } }