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.
856 lines
22 KiB
856 lines
22 KiB
/*** |
|
* |
|
* 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; |
|
} |
|
} |
|
|
|
|