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.
1108 lines
26 KiB
1108 lines
26 KiB
/*** |
|
* |
|
* Copyright (c) 1996-2002, 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. |
|
* |
|
****/ |
|
#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) |
|
|
|
//========================================================= |
|
// icthyosaur - evin, satan fish monster |
|
//========================================================= |
|
|
|
#include "extdll.h" |
|
#include "util.h" |
|
#include "cbase.h" |
|
#include "monsters.h" |
|
#include "schedule.h" |
|
#include "flyingmonster.h" |
|
#include "nodes.h" |
|
#include "soundent.h" |
|
#include "animation.h" |
|
#include "effects.h" |
|
#include "weapons.h" |
|
|
|
#define SEARCH_RETRY 16 |
|
|
|
#define ICHTHYOSAUR_SPEED 150 |
|
|
|
extern CGraph WorldGraph; |
|
|
|
#define EYE_MAD 0 |
|
#define EYE_BASE 1 |
|
#define EYE_CLOSED 2 |
|
#define EYE_BACK 3 |
|
#define EYE_LOOK 4 |
|
|
|
|
|
|
|
//========================================================= |
|
// Monster's Anim Events Go Here |
|
//========================================================= |
|
|
|
// UNDONE: Save/restore here |
|
class CIchthyosaur : public CFlyingMonster |
|
{ |
|
public: |
|
void Spawn( void ); |
|
void Precache( void ); |
|
void SetYawSpeed( void ); |
|
int Classify( void ); |
|
void HandleAnimEvent( MonsterEvent_t *pEvent ); |
|
CUSTOM_SCHEDULES; |
|
|
|
int Save( CSave &save ); |
|
int Restore( CRestore &restore ); |
|
static TYPEDESCRIPTION m_SaveData[]; |
|
|
|
Schedule_t *GetSchedule( void ); |
|
Schedule_t *GetScheduleOfType ( int Type ); |
|
|
|
void Killed( entvars_t *pevAttacker, int iGib ); |
|
void BecomeDead( void ); |
|
|
|
void EXPORT CombatUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); |
|
void EXPORT BiteTouch( CBaseEntity *pOther ); |
|
|
|
void StartTask( Task_t *pTask ); |
|
void RunTask( Task_t *pTask ); |
|
|
|
BOOL CheckMeleeAttack1 ( float flDot, float flDist ); |
|
BOOL CheckRangeAttack1 ( float flDot, float flDist ); |
|
|
|
float ChangeYaw( int speed ); |
|
Activity GetStoppedActivity( void ); |
|
|
|
void Move( float flInterval ); |
|
void MoveExecute( CBaseEntity *pTargetEnt, const Vector &vecDir, float flInterval ); |
|
void MonsterThink( void ); |
|
void Stop( void ); |
|
void Swim( void ); |
|
Vector DoProbe(const Vector &Probe); |
|
|
|
float VectorToPitch( const Vector &vec); |
|
float FlPitchDiff( void ); |
|
float ChangePitch( int speed ); |
|
|
|
Vector m_SaveVelocity; |
|
float m_idealDist; |
|
|
|
float m_flBlink; |
|
|
|
float m_flEnemyTouched; |
|
BOOL m_bOnAttack; |
|
|
|
float m_flMaxSpeed; |
|
float m_flMinSpeed; |
|
float m_flMaxDist; |
|
|
|
CBeam *m_pBeam; |
|
|
|
float m_flNextAlert; |
|
|
|
static const char *pIdleSounds[]; |
|
static const char *pAlertSounds[]; |
|
static const char *pAttackSounds[]; |
|
static const char *pBiteSounds[]; |
|
static const char *pDieSounds[]; |
|
static const char *pPainSounds[]; |
|
|
|
void IdleSound( void ); |
|
void AlertSound( void ); |
|
void AttackSound( void ); |
|
void BiteSound( void ); |
|
void DeathSound( void ); |
|
void PainSound( void ); |
|
}; |
|
|
|
LINK_ENTITY_TO_CLASS( monster_ichthyosaur, CIchthyosaur ); |
|
|
|
TYPEDESCRIPTION CIchthyosaur::m_SaveData[] = |
|
{ |
|
DEFINE_FIELD( CIchthyosaur, m_SaveVelocity, FIELD_VECTOR ), |
|
DEFINE_FIELD( CIchthyosaur, m_idealDist, FIELD_FLOAT ), |
|
DEFINE_FIELD( CIchthyosaur, m_flBlink, FIELD_FLOAT ), |
|
DEFINE_FIELD( CIchthyosaur, m_flEnemyTouched, FIELD_FLOAT ), |
|
DEFINE_FIELD( CIchthyosaur, m_bOnAttack, FIELD_BOOLEAN ), |
|
DEFINE_FIELD( CIchthyosaur, m_flMaxSpeed, FIELD_FLOAT ), |
|
DEFINE_FIELD( CIchthyosaur, m_flMinSpeed, FIELD_FLOAT ), |
|
DEFINE_FIELD( CIchthyosaur, m_flMaxDist, FIELD_FLOAT ), |
|
DEFINE_FIELD( CIchthyosaur, m_flNextAlert, FIELD_TIME ), |
|
}; |
|
|
|
IMPLEMENT_SAVERESTORE( CIchthyosaur, CFlyingMonster ); |
|
|
|
|
|
const char *CIchthyosaur::pIdleSounds[] = |
|
{ |
|
"ichy/ichy_idle1.wav", |
|
"ichy/ichy_idle2.wav", |
|
"ichy/ichy_idle3.wav", |
|
"ichy/ichy_idle4.wav", |
|
}; |
|
|
|
const char *CIchthyosaur::pAlertSounds[] = |
|
{ |
|
"ichy/ichy_alert2.wav", |
|
"ichy/ichy_alert3.wav", |
|
}; |
|
|
|
const char *CIchthyosaur::pAttackSounds[] = |
|
{ |
|
"ichy/ichy_attack1.wav", |
|
"ichy/ichy_attack2.wav", |
|
}; |
|
|
|
const char *CIchthyosaur::pBiteSounds[] = |
|
{ |
|
"ichy/ichy_bite1.wav", |
|
"ichy/ichy_bite2.wav", |
|
}; |
|
|
|
const char *CIchthyosaur::pPainSounds[] = |
|
{ |
|
"ichy/ichy_pain2.wav", |
|
"ichy/ichy_pain3.wav", |
|
"ichy/ichy_pain5.wav", |
|
}; |
|
|
|
const char *CIchthyosaur::pDieSounds[] = |
|
{ |
|
"ichy/ichy_die2.wav", |
|
"ichy/ichy_die4.wav", |
|
}; |
|
|
|
#define EMIT_ICKY_SOUND( chan, array ) \ |
|
EMIT_SOUND_DYN ( ENT(pev), chan , array [ RANDOM_LONG(0,ARRAYSIZE( array )-1) ], 1.0, 0.6, 0, RANDOM_LONG(95,105) ); |
|
|
|
|
|
void CIchthyosaur :: IdleSound( void ) |
|
{ |
|
EMIT_ICKY_SOUND( CHAN_VOICE, pIdleSounds ); |
|
} |
|
|
|
void CIchthyosaur :: AlertSound( void ) |
|
{ |
|
EMIT_ICKY_SOUND( CHAN_VOICE, pAlertSounds ); |
|
} |
|
|
|
void CIchthyosaur :: AttackSound( void ) |
|
{ |
|
EMIT_ICKY_SOUND( CHAN_VOICE, pAttackSounds ); |
|
} |
|
|
|
void CIchthyosaur :: BiteSound( void ) |
|
{ |
|
EMIT_ICKY_SOUND( CHAN_WEAPON, pBiteSounds ); |
|
} |
|
|
|
void CIchthyosaur :: DeathSound( void ) |
|
{ |
|
EMIT_ICKY_SOUND( CHAN_VOICE, pDieSounds ); |
|
} |
|
|
|
void CIchthyosaur :: PainSound( void ) |
|
{ |
|
EMIT_ICKY_SOUND( CHAN_VOICE, pPainSounds ); |
|
} |
|
|
|
//========================================================= |
|
// monster-specific tasks and states |
|
//========================================================= |
|
enum |
|
{ |
|
TASK_ICHTHYOSAUR_CIRCLE_ENEMY = LAST_COMMON_TASK + 1, |
|
TASK_ICHTHYOSAUR_SWIM, |
|
TASK_ICHTHYOSAUR_FLOAT, |
|
}; |
|
|
|
//========================================================= |
|
// AI Schedules Specific to this monster |
|
//========================================================= |
|
|
|
static Task_t tlSwimAround[] = |
|
{ |
|
{ TASK_SET_ACTIVITY, (float)ACT_WALK }, |
|
{ TASK_ICHTHYOSAUR_SWIM, 0.0 }, |
|
}; |
|
|
|
static Schedule_t slSwimAround[] = |
|
{ |
|
{ |
|
tlSwimAround, |
|
ARRAYSIZE(tlSwimAround), |
|
bits_COND_LIGHT_DAMAGE | |
|
bits_COND_HEAVY_DAMAGE | |
|
bits_COND_SEE_ENEMY | |
|
bits_COND_NEW_ENEMY | |
|
bits_COND_HEAR_SOUND, |
|
bits_SOUND_PLAYER | |
|
bits_SOUND_COMBAT, |
|
"SwimAround" |
|
}, |
|
}; |
|
|
|
static Task_t tlSwimAgitated[] = |
|
{ |
|
{ TASK_STOP_MOVING, (float) 0 }, |
|
{ TASK_SET_ACTIVITY, (float)ACT_RUN }, |
|
{ TASK_WAIT, (float)2.0 }, |
|
}; |
|
|
|
static Schedule_t slSwimAgitated[] = |
|
{ |
|
{ |
|
tlSwimAgitated, |
|
ARRAYSIZE(tlSwimAgitated), |
|
0, |
|
0, |
|
"SwimAgitated" |
|
}, |
|
}; |
|
|
|
|
|
static Task_t tlCircleEnemy[] = |
|
{ |
|
{ TASK_SET_ACTIVITY, (float)ACT_WALK }, |
|
{ TASK_ICHTHYOSAUR_CIRCLE_ENEMY, 0.0 }, |
|
}; |
|
|
|
static Schedule_t slCircleEnemy[] = |
|
{ |
|
{ |
|
tlCircleEnemy, |
|
ARRAYSIZE(tlCircleEnemy), |
|
bits_COND_NEW_ENEMY | |
|
bits_COND_LIGHT_DAMAGE | |
|
bits_COND_HEAVY_DAMAGE | |
|
bits_COND_CAN_MELEE_ATTACK1 | |
|
bits_COND_CAN_RANGE_ATTACK1, |
|
0, |
|
"CircleEnemy" |
|
}, |
|
}; |
|
|
|
|
|
Task_t tlTwitchDie[] = |
|
{ |
|
{ TASK_STOP_MOVING, 0 }, |
|
{ TASK_SOUND_DIE, (float)0 }, |
|
{ TASK_DIE, (float)0 }, |
|
{ TASK_ICHTHYOSAUR_FLOAT, (float)0 }, |
|
}; |
|
|
|
Schedule_t slTwitchDie[] = |
|
{ |
|
{ |
|
tlTwitchDie, |
|
ARRAYSIZE( tlTwitchDie ), |
|
0, |
|
0, |
|
"Die" |
|
}, |
|
}; |
|
|
|
|
|
DEFINE_CUSTOM_SCHEDULES(CIchthyosaur) |
|
{ |
|
slSwimAround, |
|
slSwimAgitated, |
|
slCircleEnemy, |
|
slTwitchDie, |
|
}; |
|
IMPLEMENT_CUSTOM_SCHEDULES(CIchthyosaur, CFlyingMonster); |
|
|
|
//========================================================= |
|
// Classify - indicates this monster's place in the |
|
// relationship table. |
|
//========================================================= |
|
int CIchthyosaur :: Classify ( void ) |
|
{ |
|
return CLASS_ALIEN_MONSTER; |
|
} |
|
|
|
|
|
//========================================================= |
|
// CheckMeleeAttack1 |
|
//========================================================= |
|
BOOL CIchthyosaur :: CheckMeleeAttack1 ( float flDot, float flDist ) |
|
{ |
|
if ( flDot >= 0.7 && m_flEnemyTouched > gpGlobals->time - 0.2 ) |
|
{ |
|
return TRUE; |
|
} |
|
return FALSE; |
|
} |
|
|
|
void CIchthyosaur::BiteTouch( CBaseEntity *pOther ) |
|
{ |
|
// bite if we hit who we want to eat |
|
if ( pOther == m_hEnemy ) |
|
{ |
|
m_flEnemyTouched = gpGlobals->time; |
|
m_bOnAttack = TRUE; |
|
} |
|
} |
|
|
|
void CIchthyosaur::CombatUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) |
|
{ |
|
if ( !ShouldToggle( useType, m_bOnAttack ) ) |
|
return; |
|
|
|
if (m_bOnAttack) |
|
{ |
|
m_bOnAttack = 0; |
|
} |
|
else |
|
{ |
|
m_bOnAttack = 1; |
|
} |
|
} |
|
|
|
//========================================================= |
|
// CheckRangeAttack1 - swim in for a chomp |
|
// |
|
//========================================================= |
|
BOOL CIchthyosaur :: CheckRangeAttack1 ( float flDot, float flDist ) |
|
{ |
|
if ( flDot > -0.7 && (m_bOnAttack || ( flDist <= 192 && m_idealDist <= 192))) |
|
{ |
|
return TRUE; |
|
} |
|
|
|
return FALSE; |
|
} |
|
|
|
//========================================================= |
|
// SetYawSpeed - allows each sequence to have a different |
|
// turn rate associated with it. |
|
//========================================================= |
|
void CIchthyosaur :: SetYawSpeed ( void ) |
|
{ |
|
pev->yaw_speed = 100; |
|
} |
|
|
|
|
|
|
|
//========================================================= |
|
// Killed - overrides CFlyingMonster. |
|
// |
|
void CIchthyosaur :: Killed( entvars_t *pevAttacker, int iGib ) |
|
{ |
|
CBaseMonster::Killed( pevAttacker, iGib ); |
|
pev->velocity = Vector( 0, 0, 0 ); |
|
} |
|
|
|
void CIchthyosaur::BecomeDead( void ) |
|
{ |
|
pev->takedamage = DAMAGE_YES;// don't let autoaim aim at corpses. |
|
|
|
// give the corpse half of the monster's original maximum health. |
|
pev->health = pev->max_health / 2; |
|
pev->max_health = 5; // max_health now becomes a counter for how many blood decals the corpse can place. |
|
} |
|
|
|
#define ICHTHYOSAUR_AE_SHAKE_RIGHT 1 |
|
#define ICHTHYOSAUR_AE_SHAKE_LEFT 2 |
|
|
|
|
|
//========================================================= |
|
// HandleAnimEvent - catches the monster-specific messages |
|
// that occur when tagged animation frames are played. |
|
//========================================================= |
|
void CIchthyosaur :: HandleAnimEvent( MonsterEvent_t *pEvent ) |
|
{ |
|
int bDidAttack = FALSE; |
|
switch( pEvent->event ) |
|
{ |
|
case ICHTHYOSAUR_AE_SHAKE_RIGHT: |
|
case ICHTHYOSAUR_AE_SHAKE_LEFT: |
|
{ |
|
if (m_hEnemy != NULL && FVisible( m_hEnemy )) |
|
{ |
|
CBaseEntity *pHurt = m_hEnemy; |
|
|
|
if (m_flEnemyTouched < gpGlobals->time - 0.2 && (m_hEnemy->BodyTarget( pev->origin ) - pev->origin).Length() > (32+16+32)) |
|
break; |
|
|
|
Vector vecShootDir = ShootAtEnemy( pev->origin ); |
|
UTIL_MakeAimVectors ( pev->angles ); |
|
|
|
if (DotProduct( vecShootDir, gpGlobals->v_forward ) > 0.707) |
|
{ |
|
m_bOnAttack = TRUE; |
|
pHurt->pev->punchangle.z = -18; |
|
pHurt->pev->punchangle.x = 5; |
|
pHurt->pev->velocity = pHurt->pev->velocity - gpGlobals->v_right * 300; |
|
if (pHurt->IsPlayer()) |
|
{ |
|
pHurt->pev->angles.x += RANDOM_FLOAT( -35, 35 ); |
|
pHurt->pev->angles.y += RANDOM_FLOAT( -90, 90 ); |
|
pHurt->pev->angles.z = 0; |
|
pHurt->pev->fixangle = TRUE; |
|
} |
|
pHurt->TakeDamage( pev, pev, gSkillData.ichthyosaurDmgShake, DMG_SLASH ); |
|
} |
|
} |
|
BiteSound(); |
|
|
|
bDidAttack = TRUE; |
|
} |
|
break; |
|
default: |
|
CFlyingMonster::HandleAnimEvent( pEvent ); |
|
break; |
|
} |
|
|
|
if (bDidAttack) |
|
{ |
|
Vector vecSrc = pev->origin + gpGlobals->v_forward * 32; |
|
UTIL_Bubbles( vecSrc - Vector( 8, 8, 8 ), vecSrc + Vector( 8, 8, 8 ), 16 ); |
|
} |
|
} |
|
|
|
//========================================================= |
|
// Spawn |
|
//========================================================= |
|
void CIchthyosaur :: Spawn() |
|
{ |
|
Precache( ); |
|
|
|
SET_MODEL(ENT(pev), "models/icky.mdl"); |
|
UTIL_SetSize( pev, Vector( -32, -32, -32 ), Vector( 32, 32, 32 ) ); |
|
|
|
pev->solid = SOLID_BBOX; |
|
pev->movetype = MOVETYPE_FLY; |
|
m_bloodColor = BLOOD_COLOR_GREEN; |
|
pev->health = gSkillData.ichthyosaurHealth; |
|
pev->view_ofs = Vector ( 0, 0, 16 ); |
|
m_flFieldOfView = VIEW_FIELD_WIDE; |
|
m_MonsterState = MONSTERSTATE_NONE; |
|
SetBits(pev->flags, FL_SWIM); |
|
SetFlyingSpeed( ICHTHYOSAUR_SPEED ); |
|
SetFlyingMomentum( 2.5 ); // Set momentum constant |
|
|
|
m_afCapability = bits_CAP_RANGE_ATTACK1 | bits_CAP_SWIM; |
|
|
|
MonsterInit(); |
|
|
|
SetTouch( BiteTouch ); |
|
SetUse( CombatUse ); |
|
|
|
m_idealDist = 384; |
|
m_flMinSpeed = 80; |
|
m_flMaxSpeed = 300; |
|
m_flMaxDist = 384; |
|
|
|
Vector Forward; |
|
UTIL_MakeVectorsPrivate(pev->angles, Forward, 0, 0); |
|
pev->velocity = m_flightSpeed * Forward.Normalize(); |
|
m_SaveVelocity = pev->velocity; |
|
} |
|
|
|
//========================================================= |
|
// Precache - precaches all resources this monster needs |
|
//========================================================= |
|
void CIchthyosaur :: Precache() |
|
{ |
|
PRECACHE_MODEL("models/icky.mdl"); |
|
|
|
PRECACHE_SOUND_ARRAY( pIdleSounds ); |
|
PRECACHE_SOUND_ARRAY( pAlertSounds ); |
|
PRECACHE_SOUND_ARRAY( pAttackSounds ); |
|
PRECACHE_SOUND_ARRAY( pBiteSounds ); |
|
PRECACHE_SOUND_ARRAY( pDieSounds ); |
|
PRECACHE_SOUND_ARRAY( pPainSounds ); |
|
} |
|
|
|
//========================================================= |
|
// GetSchedule |
|
//========================================================= |
|
Schedule_t* CIchthyosaur::GetSchedule() |
|
{ |
|
// ALERT( at_console, "GetSchedule( )\n" ); |
|
switch(m_MonsterState) |
|
{ |
|
case MONSTERSTATE_IDLE: |
|
m_flightSpeed = 80; |
|
return GetScheduleOfType( SCHED_IDLE_WALK ); |
|
|
|
case MONSTERSTATE_ALERT: |
|
m_flightSpeed = 150; |
|
return GetScheduleOfType( SCHED_IDLE_WALK ); |
|
|
|
case MONSTERSTATE_COMBAT: |
|
m_flMaxSpeed = 400; |
|
// eat them |
|
if ( HasConditions( bits_COND_CAN_MELEE_ATTACK1 ) ) |
|
{ |
|
return GetScheduleOfType( SCHED_MELEE_ATTACK1 ); |
|
} |
|
// chase them down and eat them |
|
if ( HasConditions( bits_COND_CAN_RANGE_ATTACK1 ) ) |
|
{ |
|
return GetScheduleOfType( SCHED_CHASE_ENEMY ); |
|
} |
|
if ( HasConditions( bits_COND_HEAVY_DAMAGE ) ) |
|
{ |
|
m_bOnAttack = TRUE; |
|
} |
|
if ( pev->health < pev->max_health - 20 ) |
|
{ |
|
m_bOnAttack = TRUE; |
|
} |
|
|
|
return GetScheduleOfType( SCHED_STANDOFF ); |
|
} |
|
|
|
return CFlyingMonster :: GetSchedule(); |
|
} |
|
|
|
|
|
//========================================================= |
|
//========================================================= |
|
Schedule_t* CIchthyosaur :: GetScheduleOfType ( int Type ) |
|
{ |
|
// ALERT( at_console, "GetScheduleOfType( %d ) %d\n", Type, m_bOnAttack ); |
|
switch ( Type ) |
|
{ |
|
case SCHED_IDLE_WALK: |
|
return slSwimAround; |
|
case SCHED_STANDOFF: |
|
return slCircleEnemy; |
|
case SCHED_FAIL: |
|
return slSwimAgitated; |
|
case SCHED_DIE: |
|
return slTwitchDie; |
|
case SCHED_CHASE_ENEMY: |
|
AttackSound( ); |
|
} |
|
|
|
return CBaseMonster :: GetScheduleOfType( Type ); |
|
} |
|
|
|
|
|
|
|
//========================================================= |
|
// Start task - selects the correct activity and performs |
|
// any necessary calculations to start the next task on the |
|
// schedule. |
|
//========================================================= |
|
void CIchthyosaur::StartTask(Task_t *pTask) |
|
{ |
|
switch (pTask->iTask) |
|
{ |
|
case TASK_ICHTHYOSAUR_CIRCLE_ENEMY: |
|
break; |
|
case TASK_ICHTHYOSAUR_SWIM: |
|
break; |
|
case TASK_SMALL_FLINCH: |
|
if (m_idealDist > 128) |
|
{ |
|
m_flMaxDist = 512; |
|
m_idealDist = 512; |
|
} |
|
else |
|
{ |
|
m_bOnAttack = TRUE; |
|
} |
|
CFlyingMonster::StartTask(pTask); |
|
break; |
|
|
|
case TASK_ICHTHYOSAUR_FLOAT: |
|
pev->skin = EYE_BASE; |
|
SetSequenceByName( "bellyup" ); |
|
break; |
|
|
|
default: |
|
CFlyingMonster::StartTask(pTask); |
|
break; |
|
} |
|
} |
|
|
|
void CIchthyosaur :: RunTask ( Task_t *pTask ) |
|
{ |
|
switch ( pTask->iTask ) |
|
{ |
|
case TASK_ICHTHYOSAUR_CIRCLE_ENEMY: |
|
if (m_hEnemy == NULL) |
|
{ |
|
TaskComplete( ); |
|
} |
|
else if (FVisible( m_hEnemy )) |
|
{ |
|
Vector vecFrom = m_hEnemy->EyePosition( ); |
|
|
|
Vector vecDelta = (pev->origin - vecFrom).Normalize( ); |
|
Vector vecSwim = CrossProduct( vecDelta, Vector( 0, 0, 1 ) ).Normalize( ); |
|
|
|
if (DotProduct( vecSwim, m_SaveVelocity ) < 0) |
|
vecSwim = vecSwim * -1.0; |
|
|
|
Vector vecPos = vecFrom + vecDelta * m_idealDist + vecSwim * 32; |
|
|
|
// ALERT( at_console, "vecPos %.0f %.0f %.0f\n", vecPos.x, vecPos.y, vecPos.z ); |
|
|
|
TraceResult tr; |
|
|
|
UTIL_TraceHull( vecFrom, vecPos, ignore_monsters, large_hull, m_hEnemy->edict(), &tr ); |
|
|
|
if (tr.flFraction > 0.5) |
|
vecPos = tr.vecEndPos; |
|
|
|
m_SaveVelocity = m_SaveVelocity * 0.8 + 0.2 * (vecPos - pev->origin).Normalize() * m_flightSpeed; |
|
|
|
// ALERT( at_console, "m_SaveVelocity %.2f %.2f %.2f\n", m_SaveVelocity.x, m_SaveVelocity.y, m_SaveVelocity.z ); |
|
|
|
if (HasConditions( bits_COND_ENEMY_FACING_ME ) && m_hEnemy->FVisible( this )) |
|
{ |
|
m_flNextAlert -= 0.1; |
|
|
|
if (m_idealDist < m_flMaxDist) |
|
{ |
|
m_idealDist += 4; |
|
} |
|
if (m_flightSpeed > m_flMinSpeed) |
|
{ |
|
m_flightSpeed -= 2; |
|
} |
|
else if (m_flightSpeed < m_flMinSpeed) |
|
{ |
|
m_flightSpeed += 2; |
|
} |
|
if (m_flMinSpeed < m_flMaxSpeed) |
|
{ |
|
m_flMinSpeed += 0.5; |
|
} |
|
} |
|
else |
|
{ |
|
m_flNextAlert += 0.1; |
|
|
|
if (m_idealDist > 128) |
|
{ |
|
m_idealDist -= 4; |
|
} |
|
if (m_flightSpeed < m_flMaxSpeed) |
|
{ |
|
m_flightSpeed += 4; |
|
} |
|
} |
|
// ALERT( at_console, "%.0f\n", m_idealDist ); |
|
} |
|
else |
|
{ |
|
m_flNextAlert = gpGlobals->time + 0.2; |
|
} |
|
|
|
if (m_flNextAlert < gpGlobals->time) |
|
{ |
|
// ALERT( at_console, "AlertSound()\n"); |
|
AlertSound( ); |
|
m_flNextAlert = gpGlobals->time + RANDOM_FLOAT( 3, 5 ); |
|
} |
|
|
|
break; |
|
case TASK_ICHTHYOSAUR_SWIM: |
|
if (m_fSequenceFinished) |
|
{ |
|
TaskComplete( ); |
|
} |
|
break; |
|
case TASK_DIE: |
|
if ( m_fSequenceFinished ) |
|
{ |
|
pev->deadflag = DEAD_DEAD; |
|
|
|
TaskComplete( ); |
|
} |
|
break; |
|
|
|
case TASK_ICHTHYOSAUR_FLOAT: |
|
pev->angles.x = UTIL_ApproachAngle( 0, pev->angles.x, 20 ); |
|
pev->velocity = pev->velocity * 0.8; |
|
if (pev->waterlevel > 1 && pev->velocity.z < 64) |
|
{ |
|
pev->velocity.z += 8; |
|
} |
|
else |
|
{ |
|
pev->velocity.z -= 8; |
|
} |
|
// ALERT( at_console, "%f\n", pev->velocity.z ); |
|
break; |
|
|
|
default: |
|
CFlyingMonster :: RunTask ( pTask ); |
|
break; |
|
} |
|
} |
|
|
|
|
|
|
|
float CIchthyosaur::VectorToPitch( const Vector &vec ) |
|
{ |
|
float pitch; |
|
if (vec.z == 0 && vec.x == 0) |
|
pitch = 0; |
|
else |
|
{ |
|
pitch = (int) (atan2(vec.z, sqrt(vec.x*vec.x+vec.y*vec.y)) * 180 / M_PI); |
|
if (pitch < 0) |
|
pitch += 360; |
|
} |
|
return pitch; |
|
} |
|
|
|
//========================================================= |
|
void CIchthyosaur::Move(float flInterval) |
|
{ |
|
CFlyingMonster::Move( flInterval ); |
|
} |
|
|
|
float CIchthyosaur::FlPitchDiff( void ) |
|
{ |
|
float flPitchDiff; |
|
float flCurrentPitch; |
|
|
|
flCurrentPitch = UTIL_AngleMod( pev->angles.z ); |
|
|
|
if ( flCurrentPitch == pev->idealpitch ) |
|
{ |
|
return 0; |
|
} |
|
|
|
flPitchDiff = pev->idealpitch - flCurrentPitch; |
|
|
|
if ( pev->idealpitch > flCurrentPitch ) |
|
{ |
|
if (flPitchDiff >= 180) |
|
flPitchDiff = flPitchDiff - 360; |
|
} |
|
else |
|
{ |
|
if (flPitchDiff <= -180) |
|
flPitchDiff = flPitchDiff + 360; |
|
} |
|
return flPitchDiff; |
|
} |
|
|
|
float CIchthyosaur :: ChangePitch( int speed ) |
|
{ |
|
if ( pev->movetype == MOVETYPE_FLY ) |
|
{ |
|
float diff = FlPitchDiff(); |
|
float target = 0; |
|
if ( m_IdealActivity != GetStoppedActivity() ) |
|
{ |
|
if (diff < -20) |
|
target = 45; |
|
else if (diff > 20) |
|
target = -45; |
|
} |
|
pev->angles.x = UTIL_Approach(target, pev->angles.x, 220.0 * 0.1 ); |
|
} |
|
return 0; |
|
} |
|
|
|
float CIchthyosaur::ChangeYaw( int speed ) |
|
{ |
|
if ( pev->movetype == MOVETYPE_FLY ) |
|
{ |
|
float diff = FlYawDiff(); |
|
float target = 0; |
|
|
|
if ( m_IdealActivity != GetStoppedActivity() ) |
|
{ |
|
if ( diff < -20 ) |
|
target = 20; |
|
else if ( diff > 20 ) |
|
target = -20; |
|
} |
|
pev->angles.z = UTIL_Approach( target, pev->angles.z, 220.0 * 0.1 ); |
|
} |
|
return CFlyingMonster::ChangeYaw( speed ); |
|
} |
|
|
|
|
|
Activity CIchthyosaur:: GetStoppedActivity( void ) |
|
{ |
|
if ( pev->movetype != MOVETYPE_FLY ) // UNDONE: Ground idle here, IDLE may be something else |
|
return ACT_IDLE; |
|
return ACT_WALK; |
|
} |
|
|
|
void CIchthyosaur::MoveExecute( CBaseEntity *pTargetEnt, const Vector &vecDir, float flInterval ) |
|
{ |
|
m_SaveVelocity = vecDir * m_flightSpeed; |
|
} |
|
|
|
|
|
void CIchthyosaur::MonsterThink ( void ) |
|
{ |
|
CFlyingMonster::MonsterThink( ); |
|
|
|
if (pev->deadflag == DEAD_NO) |
|
{ |
|
if (m_MonsterState != MONSTERSTATE_SCRIPT) |
|
{ |
|
Swim( ); |
|
|
|
// blink the eye |
|
if (m_flBlink < gpGlobals->time) |
|
{ |
|
pev->skin = EYE_CLOSED; |
|
if (m_flBlink + 0.2 < gpGlobals->time) |
|
{ |
|
m_flBlink = gpGlobals->time + RANDOM_FLOAT( 3, 4 ); |
|
if (m_bOnAttack) |
|
pev->skin = EYE_MAD; |
|
else |
|
pev->skin = EYE_BASE; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
void CIchthyosaur :: Stop( void ) |
|
{ |
|
if (!m_bOnAttack) |
|
m_flightSpeed = 80.0; |
|
} |
|
|
|
void CIchthyosaur::Swim( ) |
|
{ |
|
int retValue = 0; |
|
|
|
Vector start = pev->origin; |
|
|
|
Vector Angles; |
|
Vector Forward, Right, Up; |
|
|
|
if (FBitSet( pev->flags, FL_ONGROUND)) |
|
{ |
|
pev->angles.x = 0; |
|
pev->angles.y += RANDOM_FLOAT( -45, 45 ); |
|
ClearBits( pev->flags, FL_ONGROUND ); |
|
|
|
Angles = Vector( -pev->angles.x, pev->angles.y, pev->angles.z ); |
|
UTIL_MakeVectorsPrivate(Angles, Forward, Right, Up); |
|
|
|
pev->velocity = Forward * 200 + Up * 200; |
|
|
|
return; |
|
} |
|
|
|
if (m_bOnAttack && m_flightSpeed < m_flMaxSpeed) |
|
{ |
|
m_flightSpeed += 40; |
|
} |
|
if (m_flightSpeed < 180) |
|
{ |
|
if (m_IdealActivity == ACT_RUN) |
|
SetActivity( ACT_WALK ); |
|
if (m_IdealActivity == ACT_WALK) |
|
pev->framerate = m_flightSpeed / 150.0; |
|
// ALERT( at_console, "walk %.2f\n", pev->framerate ); |
|
} |
|
else |
|
{ |
|
if (m_IdealActivity == ACT_WALK) |
|
SetActivity( ACT_RUN ); |
|
if (m_IdealActivity == ACT_RUN) |
|
pev->framerate = m_flightSpeed / 150.0; |
|
// ALERT( at_console, "run %.2f\n", pev->framerate ); |
|
} |
|
|
|
/* |
|
if (!m_pBeam) |
|
{ |
|
m_pBeam = CBeam::BeamCreate( "sprites/laserbeam.spr", 80 ); |
|
m_pBeam->PointEntInit( pev->origin + m_SaveVelocity, entindex( ) ); |
|
m_pBeam->SetEndAttachment( 1 ); |
|
m_pBeam->SetColor( 255, 180, 96 ); |
|
m_pBeam->SetBrightness( 192 ); |
|
} |
|
*/ |
|
#define PROBE_LENGTH 150 |
|
Angles = UTIL_VecToAngles( m_SaveVelocity ); |
|
Angles.x = -Angles.x; |
|
UTIL_MakeVectorsPrivate(Angles, Forward, Right, Up); |
|
|
|
Vector f, u, l, r, d; |
|
f = DoProbe(start + PROBE_LENGTH * Forward); |
|
r = DoProbe(start + PROBE_LENGTH/3 * Forward+Right); |
|
l = DoProbe(start + PROBE_LENGTH/3 * Forward-Right); |
|
u = DoProbe(start + PROBE_LENGTH/3 * Forward+Up); |
|
d = DoProbe(start + PROBE_LENGTH/3 * Forward-Up); |
|
|
|
Vector SteeringVector = f+r+l+u+d; |
|
m_SaveVelocity = (m_SaveVelocity + SteeringVector/2).Normalize(); |
|
|
|
Angles = Vector( -pev->angles.x, pev->angles.y, pev->angles.z ); |
|
UTIL_MakeVectorsPrivate(Angles, Forward, Right, Up); |
|
// ALERT( at_console, "%f : %f\n", Angles.x, Forward.z ); |
|
|
|
float flDot = DotProduct( Forward, m_SaveVelocity ); |
|
if (flDot > 0.5) |
|
pev->velocity = m_SaveVelocity = m_SaveVelocity * m_flightSpeed; |
|
else if (flDot > 0) |
|
pev->velocity = m_SaveVelocity = m_SaveVelocity * m_flightSpeed * (flDot + 0.5); |
|
else |
|
pev->velocity = m_SaveVelocity = m_SaveVelocity * 80; |
|
|
|
// ALERT( at_console, "%.0f %.0f\n", m_flightSpeed, pev->velocity.Length() ); |
|
|
|
|
|
// ALERT( at_console, "Steer %f %f %f\n", SteeringVector.x, SteeringVector.y, SteeringVector.z ); |
|
|
|
/* |
|
m_pBeam->SetStartPos( pev->origin + pev->velocity ); |
|
m_pBeam->RelinkBeam( ); |
|
*/ |
|
|
|
// ALERT( at_console, "speed %f\n", m_flightSpeed ); |
|
|
|
Angles = UTIL_VecToAngles( m_SaveVelocity ); |
|
|
|
// Smooth Pitch |
|
// |
|
if (Angles.x > 180) |
|
Angles.x = Angles.x - 360; |
|
pev->angles.x = UTIL_Approach(Angles.x, pev->angles.x, 50 * 0.1 ); |
|
if (pev->angles.x < -80) pev->angles.x = -80; |
|
if (pev->angles.x > 80) pev->angles.x = 80; |
|
|
|
// Smooth Yaw and generate Roll |
|
// |
|
float turn = 360; |
|
// ALERT( at_console, "Y %.0f %.0f\n", Angles.y, pev->angles.y ); |
|
|
|
if (fabs(Angles.y - pev->angles.y) < fabs(turn)) |
|
{ |
|
turn = Angles.y - pev->angles.y; |
|
} |
|
if (fabs(Angles.y - pev->angles.y + 360) < fabs(turn)) |
|
{ |
|
turn = Angles.y - pev->angles.y + 360; |
|
} |
|
if (fabs(Angles.y - pev->angles.y - 360) < fabs(turn)) |
|
{ |
|
turn = Angles.y - pev->angles.y - 360; |
|
} |
|
|
|
float speed = m_flightSpeed * 0.1; |
|
|
|
// ALERT( at_console, "speed %.0f %f\n", turn, speed ); |
|
if (fabs(turn) > speed) |
|
{ |
|
if (turn < 0.0) |
|
{ |
|
turn = -speed; |
|
} |
|
else |
|
{ |
|
turn = speed; |
|
} |
|
} |
|
pev->angles.y += turn; |
|
pev->angles.z -= turn; |
|
pev->angles.y = fmod((pev->angles.y + 360.0), 360.0); |
|
|
|
static float yaw_adj; |
|
|
|
yaw_adj = yaw_adj * 0.8 + turn; |
|
|
|
// ALERT( at_console, "yaw %f : %f\n", turn, yaw_adj ); |
|
|
|
SetBoneController( 0, -yaw_adj / 4.0 ); |
|
|
|
// Roll Smoothing |
|
// |
|
turn = 360; |
|
if (fabs(Angles.z - pev->angles.z) < fabs(turn)) |
|
{ |
|
turn = Angles.z - pev->angles.z; |
|
} |
|
if (fabs(Angles.z - pev->angles.z + 360) < fabs(turn)) |
|
{ |
|
turn = Angles.z - pev->angles.z + 360; |
|
} |
|
if (fabs(Angles.z - pev->angles.z - 360) < fabs(turn)) |
|
{ |
|
turn = Angles.z - pev->angles.z - 360; |
|
} |
|
speed = m_flightSpeed/2 * 0.1; |
|
if (fabs(turn) < speed) |
|
{ |
|
pev->angles.z += turn; |
|
} |
|
else |
|
{ |
|
if (turn < 0.0) |
|
{ |
|
pev->angles.z -= speed; |
|
} |
|
else |
|
{ |
|
pev->angles.z += speed; |
|
} |
|
} |
|
if (pev->angles.z < -20) pev->angles.z = -20; |
|
if (pev->angles.z > 20) pev->angles.z = 20; |
|
|
|
UTIL_MakeVectorsPrivate( Vector( -Angles.x, Angles.y, Angles.z ), Forward, Right, Up); |
|
|
|
// UTIL_MoveToOrigin ( ENT(pev), pev->origin + Forward * speed, speed, MOVE_STRAFE ); |
|
} |
|
|
|
|
|
Vector CIchthyosaur::DoProbe(const Vector &Probe) |
|
{ |
|
Vector WallNormal = Vector(0,0,-1); // WATER normal is Straight Down for fish. |
|
float frac; |
|
BOOL bBumpedSomething = ProbeZ(pev->origin, Probe, &frac); |
|
|
|
TraceResult tr; |
|
TRACE_MONSTER_HULL(edict(), pev->origin, Probe, dont_ignore_monsters, edict(), &tr); |
|
if ( tr.fAllSolid || tr.flFraction < 0.99 ) |
|
{ |
|
if (tr.flFraction < 0.0) tr.flFraction = 0.0; |
|
if (tr.flFraction > 1.0) tr.flFraction = 1.0; |
|
if (tr.flFraction < frac) |
|
{ |
|
frac = tr.flFraction; |
|
bBumpedSomething = TRUE; |
|
WallNormal = tr.vecPlaneNormal; |
|
} |
|
} |
|
|
|
if (bBumpedSomething && (m_hEnemy == NULL || tr.pHit != m_hEnemy->edict())) |
|
{ |
|
Vector ProbeDir = Probe - pev->origin; |
|
|
|
Vector NormalToProbeAndWallNormal = CrossProduct(ProbeDir, WallNormal); |
|
Vector SteeringVector = CrossProduct( NormalToProbeAndWallNormal, ProbeDir); |
|
|
|
float SteeringForce = m_flightSpeed * (1-frac) * (DotProduct(WallNormal.Normalize(), m_SaveVelocity.Normalize())); |
|
if (SteeringForce < 0.0) |
|
{ |
|
SteeringForce = -SteeringForce; |
|
} |
|
SteeringVector = SteeringForce * SteeringVector.Normalize(); |
|
|
|
return SteeringVector; |
|
} |
|
return Vector(0, 0, 0); |
|
} |
|
|
|
#endif |