Portable Half-Life SDK. GoldSource and Xash3D. Crossplatform.
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.

1183 lines
26 KiB

//-------------------------------------------------
//- ---
//- luciole.cpp ---
//- ---
//-------------------------------------------------
// par Julien -----------------------
//-------------------------------------------------
//- code de la luciole -------------
//-------------------------------------------------
//----------------------------------------
// inclusions
#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "monsters.h"
#include "effects.h"
#include "schedule.h"
#include "weapons.h"
#include "squadmonster.h"
#include "customentity.h"
// alertes consoles et beams
#define DEBUG_ALERTS
extern Vector Intersect( Vector vecSrc, Vector vecDst, Vector vecMove, float flSpeed );
#define MAX_LUCIOLE_SQUAD_MEMBERS 20
#define ATTACK_DELAY 0.7
#define WAYPOINT_DELAY 0.5
#define ATTACK_DIST 60
#define CHANGEMOVE_DIST 60
#define MOUCHARD_OFF 0
#define MOUCHARD_ON 1
#define MOUCHARD_LANCEMENT 2
enum
{
TASK_FLYBEE_WAIT_FOR_MOVEMENT = LAST_COMMON_TASK + 1,
TASK_LUCIOLE_GET_PATH,
};
//----------------------------------------------
// d
class CLuciole : public CBaseMonster
{
public:
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
static TYPEDESCRIPTION m_SaveData[];
void Spawn( void );
void Precache( void );
void SetYawSpeed( void );
int Classify ( void );
BOOL CheckRangeAttack1 ( float flDot, float flDist ); // balls
Schedule_t* GetSchedule ( void );
Schedule_t* GetScheduleOfType ( int Type );
void StartTask ( Task_t *pTask );
void RunTask ( Task_t *pTask );
CUSTOM_SCHEDULES;
void Stop( void );
void Move ( float flInterval );
int CheckLocalMove ( const Vector &vecStart, const Vector &vecEnd, CBaseEntity *pTarget, float *pflDist );
void MoveExecute( CBaseEntity *pTargetEnt, const Vector &vecDir, float flInterval );
void SetActivity ( Activity NewActivity );
BOOL ShouldAdvanceRoute( float flWaypointDist );
float m_flNextFlinch;
float m_flShootTime;
float m_flShootEnd;
void AlertSound( void );
void IdleSound( void );
static const char *pIdleSounds[];
static const char *pAlertSounds[];
void Killed( entvars_t *pevAttacker, int iGib );
Vector m_velocity;
int m_fInCombat;
void ReorganiseSquad ( void );
float m_flLastAttack;
Vector m_vecRoute;
Vector m_vecMouchard1;
Vector m_vecMouchard2;
int m_iMouchard;
};
LINK_ENTITY_TO_CLASS( monster_luciole, CLuciole );
TYPEDESCRIPTION CLuciole::m_SaveData[] =
{
DEFINE_FIELD( CLuciole, m_flLastAttack, FIELD_TIME ),
DEFINE_FIELD( CLuciole, m_vecRoute, FIELD_VECTOR ),
DEFINE_FIELD( CLuciole, m_vecMouchard1, FIELD_VECTOR ),
DEFINE_FIELD( CLuciole, m_vecMouchard2, FIELD_VECTOR ),
DEFINE_FIELD( CLuciole, m_iMouchard, FIELD_INTEGER ),
};
IMPLEMENT_SAVERESTORE( CLuciole, CBaseMonster );
const char *CLuciole::pIdleSounds[] =
{
"controller/con_idle1.wav",
"controller/con_idle2.wav",
"controller/con_idle3.wav",
"controller/con_idle4.wav",
"controller/con_idle5.wav",
};
const char *CLuciole::pAlertSounds[] =
{
"controller/con_alert1.wav",
"controller/con_alert2.wav",
"controller/con_alert3.wav",
};
//=========================================================
// Classify - indicates this monster's place in the
// relationship table.
//=========================================================
int CLuciole :: Classify ( void )
{
return CLASS_ALIEN_MILITARY;
}
//=========================================================
// SetYawSpeed - allows each sequence to have a different
// turn rate associated with it.
//=========================================================
void CLuciole :: SetYawSpeed ( void )
{
pev->yaw_speed = 360;
}
//=========================================================
// Killed - en avertit le groupe
void CLuciole::Killed( entvars_t *pevAttacker, int iGib )
{
CBaseMonster::Killed( pevAttacker, GIB_ALWAYS ); // explose toujours
}
void CLuciole :: AlertSound( void )
{
EMIT_SOUND_ARRAY_DYN( CHAN_VOICE, pAlertSounds );
}
void CLuciole :: IdleSound( void )
{
EMIT_SOUND_ARRAY_DYN( CHAN_VOICE, pIdleSounds );
}
//=========================================================
// Spawn
//=========================================================
void CLuciole :: Spawn()
{
Precache( );
SET_MODEL(ENT(pev), "models/gorg.mdl");
UTIL_SetSize( pev, Vector( -4, -4, -0 ), Vector( 4, 4, 8 ));
pev->solid = SOLID_SLIDEBOX; //SOLID_SLIDEBOX;
pev->movetype = MOVETYPE_FLY;
pev->flags |= FL_FLY;
m_bloodColor = BLOOD_COLOR_GREEN;
pev->health = gSkillData.LucioleHealth;
pev->view_ofs = Vector( 0, 0, 3 );// position of the eyes relative to monster's origin.
m_flFieldOfView = VIEW_FIELD_FULL;// indicates the width of this monster's forward view cone ( as a dotproduct result )
m_MonsterState = MONSTERSTATE_NONE;
m_flLastAttack = 0;
m_vecRoute = pev->origin;
m_iMouchard = MOUCHARD_OFF;
MonsterInit();
}
//=========================================================
// Precache - precaches all resources this monster needs
//=========================================================
void CLuciole :: Precache()
{
PRECACHE_MODEL("models/gorg.mdl");
PRECACHE_SOUND_ARRAY( pIdleSounds );
PRECACHE_SOUND_ARRAY( pAlertSounds );
PRECACHE_SOUND("debris/beamstart14.wav");
}
//=========================================================
// AI Schedules Specific to this monster
//=========================================================
// Chase enemy schedule
Task_t tlLucioleChaseEnemy[] =
{
{ TASK_LUCIOLE_GET_PATH, (float)0 },
};
Schedule_t slLucioleChaseEnemy[] =
{
{
tlLucioleChaseEnemy,
ARRAYSIZE ( tlLucioleChaseEnemy ),
bits_COND_NEW_ENEMY |
bits_COND_TASK_FAILED,
0,
"LucioleChaseEnemy"
},
};
Task_t tlLucioleTakeCover[] =
{
{ TASK_FIND_COVER_FROM_ENEMY, (float)0 },
{ TASK_RUN_PATH, (float)0 },
{ TASK_WAIT_FOR_MOVEMENT, (float)0 },
};
Schedule_t slLucioleTakeCover[] =
{
{
tlLucioleTakeCover,
ARRAYSIZE ( tlLucioleTakeCover ),
bits_COND_NEW_ENEMY,
0,
"LucioleTakeCover"
},
};
Task_t tlLucioleStrafe[] =
{
{ TASK_WAIT, (float)0.2 },
{ TASK_GET_PATH_TO_ENEMY, (float)128 },
{ TASK_WAIT_FOR_MOVEMENT, (float)0 },
{ TASK_WAIT, (float)1 },
};
Schedule_t slLucioleStrafe[] =
{
{
tlLucioleStrafe,
ARRAYSIZE ( tlLucioleStrafe ),
bits_COND_NEW_ENEMY,
0,
"LucioleStrafe"
},
};
Task_t tlLucioleFail[] =
{
{ TASK_STOP_MOVING, 0 },
// { TASK_SET_ACTIVITY, (float)ACT_IDLE },
// { TASK_WAIT, (float)1 },
{ TASK_WAIT_PVS, (float)0 },
};
Schedule_t slLucioleFail[] =
{
{
tlLucioleFail,
ARRAYSIZE ( tlLucioleFail ),
0,
0,
"LucioleFail"
},
};
// modif de julien
Task_t tlLucioleBurn[] =
{
{ TASK_FIND_COVER_FROM_ENEMY, (float)0 },
{ TASK_FLYBEE_WAIT_FOR_MOVEMENT, (float)0 },
};
Schedule_t slLucioleBurn[] =
{
{
tlLucioleBurn,
ARRAYSIZE ( tlLucioleBurn ),
0,
0,
"LucioleBurn"
},
};
DEFINE_CUSTOM_SCHEDULES( CLuciole )
{
slLucioleChaseEnemy,
slLucioleStrafe,
slLucioleTakeCover,
slLucioleFail,
slLucioleBurn, // modif de julien
};
IMPLEMENT_CUSTOM_SCHEDULES( CLuciole, CBaseMonster );
//=========================================================
// StartTask
//=========================================================
void CLuciole :: StartTask ( Task_t *pTask )
{
switch ( pTask->iTask )
{
case TASK_RANGE_ATTACK1:
CBaseMonster :: StartTask ( pTask );
break;
case TASK_GET_PATH_TO_ENEMY_LKP:
{
if (BuildNearestRoute( m_vecEnemyLKP, pev->view_ofs, pTask->flData, (m_vecEnemyLKP - pev->origin).Length() + 1024 ))
{
TaskComplete();
}
else
{
// no way to get there =(
ALERT ( at_aiconsole, "GetPathToEnemyLKP failed!!\n" );
TaskFail();
}
break;
}
case TASK_GET_PATH_TO_ENEMY:
{
CBaseEntity *pEnemy = m_hEnemy;
if ( pEnemy == NULL )
{
TaskFail();
return;
}
if (BuildNearestRoute( pEnemy->pev->origin, pEnemy->pev->view_ofs, pTask->flData, (pEnemy->pev->origin - pev->origin).Length() + 1024 ))
{
TaskComplete();
}
else
{
// no way to get there =(
ALERT ( at_aiconsole, "GetPathToEnemy failed!!\n" );
TaskFail();
}
break;
}
// modif de julien
case TASK_FLYBEE_WAIT_FOR_MOVEMENT:
{
if (FRouteClear())
{
TaskComplete();
}
break;
}
// trouve son chemin vers le joueur
case TASK_LUCIOLE_GET_PATH:
break;
case TASK_FIND_COVER_FROM_ENEMY:
{
entvars_t *pevCover;
if ( m_hEnemy == NULL )
{
// Find cover from self if no enemy available
pevCover = pev;
}
else
pevCover = m_hEnemy->pev;
if ( FindLateralCover( pevCover->origin, pevCover->view_ofs ) )
{
// try lateral first
m_flMoveWaitFinished = gpGlobals->time + pTask->flData;
TaskComplete();
}
else if ( FindCover( pevCover->origin, pevCover->view_ofs, 0, CoverRadius() ) )
{
// then try for plain ole cover
m_flMoveWaitFinished = gpGlobals->time + pTask->flData;
TaskComplete();
}
// passe de l'autre cot
else if (BuildNearestRoute( pev->origin + (pevCover->origin-pev->origin).Normalize() * 128, pev->view_ofs, 0, 4096) )
{
TaskComplete();
}
else
{
// no coverwhatsoever.
TaskFail();
}
break;
}
default:
CBaseMonster :: StartTask ( pTask );
break;
}
}
//=========================================================
// RunTask
//=========================================================
void CLuciole :: RunTask ( Task_t *pTask )
{
// petite loupiote
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, Center() );
WRITE_BYTE(TE_DLIGHT);
WRITE_COORD(Center().x); // X
WRITE_COORD(Center().y); // Y
WRITE_COORD(Center().z); // Z
WRITE_BYTE( 7 ); // radius * 0.1
WRITE_BYTE( 150 ); // r
WRITE_BYTE( 71 ); // g
WRITE_BYTE( 245 ); // b
WRITE_BYTE( 3 ); // time * 10
WRITE_BYTE( 0 ); // decay * 0.1
MESSAGE_END( );
// position de tir
if ( m_hEnemy != NULL && (Center()-m_hEnemy->Center()).Length() < ATTACK_DIST && gpGlobals->time - m_flLastAttack > ATTACK_DELAY )
{
m_flLastAttack = gpGlobals->time;
EMIT_SOUND ( edict(), CHAN_ITEM, "debris/beamstart14.wav", 1.0, ATTN_NORM );
for ( int i=0; i<3; i++ )
{
CBeam *pBeam = CBeam::BeamCreate( "sprites/laserbeam.spr", 2 );
if ( RANDOM_LONG(0,1) )
pBeam->SetColor( 206,118, 254 );
else
pBeam->SetColor( 223,224, 255 );
pBeam->SetBrightness( 192 );
pBeam->PointEntInit( m_hEnemy->Center(), entindex( ) );
pBeam->SetEndAttachment( 0 );
pBeam->RelinkBeam( );
pBeam->SetNoise ( 30 );
pBeam->LiveForTime ( 0.4 );
m_hEnemy->TakeDamage ( pev, pev, gSkillData.LucioleDamage, DMG_SHOCK );
}
return;
}
MakeIdealYaw( m_vecEnemyLKP );
ChangeYaw( pev->yaw_speed );
// run task classique
switch ( pTask->iTask )
{
case TASK_FLYBEE_WAIT_FOR_MOVEMENT:
{
if (MovementIsComplete())
{
TaskComplete();
RouteClear(); // Stop moving
}
break;
}
case TASK_LUCIOLE_GET_PATH:
{
// consid
BOOL bFinDeRoute = FALSE;
if ( (pev->origin-m_vecRoute).Length() < 10 )
{
bFinDeRoute = TRUE;
}
// actualise la position ennemie
if ( m_hEnemy == NULL )
{
TaskComplete ();
break;
}
Vector vecEnemy = m_hEnemy->Center();
// v
TraceResult tr;
UTIL_TraceLine ( pev->origin, vecEnemy, dont_ignore_monsters, dont_ignore_glass, edict(), &tr );
if ( tr.flFraction == 1.0 || FClassnameIs(tr.pHit, "player") )
{
// champ libre jusqu'au joueur
m_vecRoute = vecEnemy;
m_iMouchard = MOUCHARD_OFF;
}
// joueur invisible
else
{
// trajectoire non finie - on continue
if ( bFinDeRoute == FALSE )
{
// active le mouchard
if ( m_iMouchard == MOUCHARD_OFF )
{
m_iMouchard = MOUCHARD_LANCEMENT;
m_vecMouchard1 = m_vecRoute;
}
}
// pas de trajectoire d
else
{
// tente d'utiliser le mouchard
TraceResult trMouchard;
UTIL_TraceLine ( pev->origin, m_vecMouchard2, dont_ignore_monsters, dont_ignore_glass, edict(), &trMouchard );
if ( m_iMouchard == MOUCHARD_ON && ( trMouchard.flFraction == 1.0 || FClassnameIs(trMouchard.pHit, "player") ) )
{
// c parti
m_vecRoute = m_vecMouchard2;
// mouchard obsol
m_iMouchard = MOUCHARD_LANCEMENT;
}
else
{
TraceResult trTete;
UTIL_TraceLine ( pev->origin, m_hEnemy->pev->view_ofs, dont_ignore_monsters, dont_ignore_glass, edict(), &trTete );
TraceResult trPieds;
UTIL_TraceLine ( pev->origin, m_hEnemy->pev->origin, dont_ignore_monsters, dont_ignore_glass, edict(), &trPieds );
if ( trTete.flFraction == 1.0 || FClassnameIs(trTete.pHit, "player") )
{
// champ libre jusqu'au joueur
m_vecRoute = m_hEnemy->pev->view_ofs;
}
else if ( trPieds.flFraction == 1.0 || FClassnameIs(trPieds.pHit, "player") )
{
// champ libre jusqu'au joueur
m_vecRoute = m_hEnemy->pev->origin;
}
// ennemi totalement invisible
else
{
// bloqu
TraceResult trEvite [2];
UTIL_MakeVectors ( pev->angles );
int ordre [4];
switch ( RANDOM_LONG(0,3) )
{
case 0: ordre[0] = 0; ordre[1] = 1; ordre[2] = 2; ordre[3] = 3; break;
case 1: ordre[0] = 0; ordre[1] = 3; ordre[2] = 2; ordre[3] = 1; break;
case 2: ordre[0] = 2; ordre[1] = 3; ordre[2] = 0; ordre[3] = 1; break;
case 3: ordre[0] = 3; ordre[1] = 1; ordre[2] = 0; ordre[3] = 2; break;
}
UTIL_TraceLine ( pev->origin, pev->origin + Vector (0,0,RANDOM_FLOAT(40,60)), dont_ignore_monsters, dont_ignore_glass, edict(), &trEvite[ordre[0]] );
UTIL_TraceLine ( pev->origin, pev->origin - Vector (0,0,RANDOM_FLOAT(40,60)), dont_ignore_monsters, dont_ignore_glass, edict(), &trEvite[ordre[1]] );
UTIL_TraceLine ( pev->origin, pev->origin + gpGlobals->v_right * RANDOM_FLOAT(40,60), dont_ignore_monsters, dont_ignore_glass, edict(), &trEvite[ordre[2]] );
UTIL_TraceLine ( pev->origin, pev->origin - gpGlobals->v_right * RANDOM_FLOAT(40,60), dont_ignore_monsters, dont_ignore_glass, edict(), &trEvite[ordre[3]] );
for ( int i=0; i<4; i++ )
{
if ( trEvite[i].flFraction >= 0.5 )
m_vecRoute = trEvite[i].vecEndPos - (trEvite[i].vecEndPos-pev->origin).Normalize() * 5;
break;
}
}
}
}
}
// actualise le mouchard
if ( m_iMouchard != MOUCHARD_OFF )
{
if ( m_iMouchard == MOUCHARD_LANCEMENT )
{
m_vecMouchard2 = m_vecMouchard1;
}
// v
TraceResult trMouchard;
UTIL_TraceLine ( m_vecMouchard1, vecEnemy, dont_ignore_monsters, dont_ignore_glass, edict(), &trMouchard );
if ( trMouchard.flFraction == 1.0 || FClassnameIs(trMouchard.pHit, "player") )
{
m_vecMouchard2 = vecEnemy;
m_iMouchard = MOUCHARD_ON;
}
}
// trajectoire
float flDot = DotProduct ( pev->velocity.Normalize(), (m_vecRoute-pev->origin).Normalize() );
float flRatio = 0.6 + (flDot+1)*0.6;
float speed = pev->velocity.Length() * flRatio;
speed = Q_max ( 100, speed );
speed = Q_min ( speed, 250 );
pev->velocity = (m_vecRoute - pev->origin).Normalize() * speed;
break;
}
default:
CBaseMonster :: RunTask ( pTask );
break;
}
}
//=========================================================
// GetSchedule - Decides which type of schedule best suits
// the monster's current state and conditions. Then calls
// monster's member function to get a pointer to a schedule
// of the proper type.
//=========================================================
Schedule_t *CLuciole :: GetSchedule ( void )
{
switch ( m_MonsterState )
{
case MONSTERSTATE_IDLE:
break;
case MONSTERSTATE_ALERT:
break;
case MONSTERSTATE_COMBAT:
{
if ( HasConditions(bits_COND_NEW_ENEMY) ) // une seule r
{
// if ( VARS(m_hEnemy) != VARS(Leader()->m_hEnemy) )
{
ReorganiseSquad ();
}
}
/* if ( gpGlobals->time - m_flLastAttack < ATTACK_DELAY && IsLeader() )
return GetScheduleOfType ( SCHED_TAKE_COVER_FROM_ENEMY );
else
*/ return GetScheduleOfType ( SCHED_CHASE_ENEMY );
}
break;
}
return CBaseMonster :: GetSchedule();
}
//=========================================================
//=========================================================
Schedule_t* CLuciole :: GetScheduleOfType ( int Type )
{
// ALERT( at_console, "%d\n", m_iFrustration );
switch ( Type )
{
case SCHED_CHASE_ENEMY:
return slLucioleChaseEnemy;
case SCHED_TAKE_COVER_FROM_ENEMY:
return slLucioleTakeCover;
case SCHED_FAIL:
return slLucioleFail;
// modif de julien
case SCHED_BURNT:
return slLucioleBurn;
}
return CBaseMonster :: GetScheduleOfType( Type );
}
//=========================================================
// CheckRangeAttack1 - shoot a bigass energy ball out of their head
//
//=========================================================
BOOL CLuciole :: CheckRangeAttack1 ( float flDot, float flDist )
{
if ( flDot > 0.5 && flDist > 256 && flDist <= 2048 )
{
return TRUE;
}
return FALSE;
}
void CLuciole :: SetActivity ( Activity NewActivity )
{
CBaseMonster::SetActivity( NewActivity );
switch ( m_Activity)
{
case ACT_WALK:
m_flGroundSpeed = 100;
break;
default:
m_flGroundSpeed = 100;
break;
}
}
extern void DrawRoute( entvars_t *pev, WayPoint_t *m_Route, int m_iRouteIndex, int r, int g, int b );
void CLuciole::Stop( void )
{
m_IdealActivity = GetStoppedActivity();
}
#define DIST_TO_CHECK 200
void CLuciole :: Move ( float flInterval )
{
float flWaypointDist;
float flCheckDist;
float flDist;// how far the lookahead check got before hitting an object.
float flMoveDist;
Vector vecDir;
Vector vecApex;
CBaseEntity *pTargetEnt;
// Don't move if no valid route
if ( FRouteClear() )
{
ALERT( at_aiconsole, "Tried to move with no route!\n" );
TaskFail();
return;
}
if ( m_flMoveWaitFinished > gpGlobals->time )
return;
// if the monster is moving directly towards an entity (enemy for instance), we'll set this pointer
// to that entity for the CheckLocalMove and Triangulate functions.
pTargetEnt = NULL;
if (m_flGroundSpeed == 0)
{
m_flGroundSpeed = 100;
}
flMoveDist = m_flGroundSpeed * flInterval;
do
{
// local move to waypoint.
vecDir = ( m_Route[ m_iRouteIndex ].vecLocation - pev->origin ).Normalize();
flWaypointDist = ( m_Route[ m_iRouteIndex ].vecLocation - pev->origin ).Length();
// MakeIdealYaw ( m_Route[ m_iRouteIndex ].vecLocation );
// ChangeYaw ( pev->yaw_speed );
// if the waypoint is closer than CheckDist, CheckDist is the dist to waypoint
if ( flWaypointDist < DIST_TO_CHECK )
{
flCheckDist = flWaypointDist;
}
else
{
flCheckDist = DIST_TO_CHECK;
}
if ( (m_Route[ m_iRouteIndex ].iType & (~bits_MF_NOT_TO_MASK)) == bits_MF_TO_ENEMY )
{
// only on a PURE move to enemy ( i.e., ONLY MF_TO_ENEMY set, not MF_TO_ENEMY and DETOUR )
pTargetEnt = m_hEnemy;
}
else if ( (m_Route[ m_iRouteIndex ].iType & ~bits_MF_NOT_TO_MASK) == bits_MF_TO_TARGETENT )
{
pTargetEnt = m_hTargetEnt;
}
// !!!BUGBUG - CheckDist should be derived from ground speed.
// If this fails, it should be because of some dynamic entity blocking this guy.
// We've already checked this path, so we should wait and time out if the entity doesn't move
flDist = 0;
if ( CheckLocalMove ( pev->origin, pev->origin + vecDir * flCheckDist, pTargetEnt, &flDist ) != LOCALMOVE_VALID )
{
CBaseEntity *pBlocker;
// Can't move, stop
Stop();
// Blocking entity is in global trace_ent
pBlocker = CBaseEntity::Instance( gpGlobals->trace_ent );
if (pBlocker)
{
DispatchBlocked( edict(), pBlocker->edict() );
}
if ( pBlocker && m_moveWaitTime > 0 && pBlocker->IsMoving() && !pBlocker->IsPlayer() && (gpGlobals->time-m_flMoveWaitFinished) > 3.0 )
{
// Can we still move toward our target?
if ( flDist < m_flGroundSpeed )
{
// Wait for a second
m_flMoveWaitFinished = gpGlobals->time + m_moveWaitTime;
// ALERT( at_aiconsole, "Move %s!!!\n", STRING( pBlocker->pev->classname ) );
return;
}
}
else
{
// try to triangulate around whatever is in the way.
if ( FTriangulate( pev->origin, m_Route[ m_iRouteIndex ].vecLocation, flDist, pTargetEnt, &vecApex ) )
{
InsertWaypoint( vecApex, bits_MF_TO_DETOUR );
RouteSimplify( pTargetEnt );
}
else
{
ALERT ( at_aiconsole, "Couldn't Triangulate\n" );
Stop();
if ( m_moveWaitTime > 0 )
{
FRefreshRoute();
m_flMoveWaitFinished = gpGlobals->time + m_moveWaitTime * 0.5;
}
else
{
TaskFail();
ALERT( at_aiconsole, "Failed to move!\n" );
//ALERT( at_aiconsole, "%f, %f, %f\n", pev->origin.z, (pev->origin + (vecDir * flCheckDist)).z, m_Route[m_iRouteIndex].vecLocation.z );
}
return;
}
}
}
// UNDONE: this is a hack to quit moving farther than it has looked ahead.
if (flCheckDist < flMoveDist)
{
MoveExecute( pTargetEnt, vecDir, flCheckDist / m_flGroundSpeed );
// ALERT( at_console, "%.02f\n", flInterval );
AdvanceRoute( flWaypointDist );
flMoveDist -= flCheckDist;
}
else
{
MoveExecute( pTargetEnt, vecDir, flMoveDist / m_flGroundSpeed );
if ( ShouldAdvanceRoute( flWaypointDist - flMoveDist ) )
{
AdvanceRoute( flWaypointDist );
}
flMoveDist = 0;
}
if ( MovementIsComplete() )
{
Stop();
RouteClear();
}
} while (flMoveDist > 0 && flCheckDist > 0);
// cut corner?
if (flWaypointDist < 128)
{
if ( m_movementGoal == MOVEGOAL_ENEMY )
RouteSimplify( m_hEnemy );
else
RouteSimplify( m_hTargetEnt );
FRefreshRoute();
if (m_flGroundSpeed > 100)
m_flGroundSpeed -= 40;
}
else
{
if (m_flGroundSpeed < 400)
m_flGroundSpeed += 10;
}
}
BOOL CLuciole:: ShouldAdvanceRoute( float flWaypointDist )
{
if ( flWaypointDist <= 32 )
{
return TRUE;
}
return FALSE;
}
int CLuciole :: CheckLocalMove ( const Vector &vecStart, const Vector &vecEnd, CBaseEntity *pTarget, float *pflDist )
{
TraceResult tr;
UTIL_TraceHull( vecStart + Vector( 0, 0, 32), vecEnd + Vector( 0, 0, 32), dont_ignore_monsters, large_hull, edict(), &tr );
// ALERT( at_console, "%.0f %.0f %.0f : ", vecStart.x, vecStart.y, vecStart.z );
// ALERT( at_console, "%.0f %.0f %.0f\n", vecEnd.x, vecEnd.y, vecEnd.z );
if (pflDist)
{
*pflDist = ( (tr.vecEndPos - Vector( 0, 0, 32 )) - vecStart ).Length();// get the distance.
}
// ALERT( at_console, "check %d %d %f\n", tr.fStartSolid, tr.fAllSolid, tr.flFraction );
if (tr.fStartSolid || tr.flFraction < 1.0)
{
if ( pTarget && pTarget->edict() == gpGlobals->trace_ent )
return LOCALMOVE_VALID;
return LOCALMOVE_INVALID;
}
return LOCALMOVE_VALID;
}
void CLuciole::MoveExecute( CBaseEntity *pTargetEnt, const Vector &vecDir, float flInterval )
{
if ( m_IdealActivity != m_movementActivity )
m_IdealActivity = m_movementActivity;
m_velocity = m_velocity * 0.8 + m_flGroundSpeed * vecDir * 0.5;
m_velocity = m_velocity.Normalize() * Q_max ( m_velocity.Length(), 100 );
m_velocity = m_velocity.Normalize() * Q_min ( 300, m_velocity.Length() );
UTIL_MoveToOrigin ( ENT(pev), pev->origin + m_velocity, m_velocity.Length() * flInterval, MOVE_STRAFE );
}
//------------------------------------------
// squad
//------------------------------------------
void CLuciole :: ReorganiseSquad ( void )
{
/*
#if defined DEBUG_ALERTS
ALERT ( at_console, "reorganisesquad\n" );
UTIL_ParticleEffect ( pev->origin, Vector(90,0,0), 0x0000FF, 128 );
#endif
if ( m_hEnemy == NULL )
{
#if defined DEBUG_ALERTS
ALERT ( at_console, "pas d'ennemi pour la reorganisation\n" );
#endif
return;
}
// cherche nouveau leader
CLuciole *pLeader = NULL;
float mindist = 4096;
CLuciole *pLuciole = (CLuciole*)UTIL_FindEntityByClassname ( NULL, "monster_luciole" );
while ( pLuciole != NULL )
{
if ( pLuciole->IsAlive() && (pLuciole->pev->origin - m_hEnemy->pev->origin).Length() < mindist )
{
mindist = (pLuciole->pev->origin - m_hEnemy->pev->origin).Length();
pLeader = pLuciole;
}
pLuciole = (CLuciole*)UTIL_FindEntityByClassname ( pLuciole, "monster_luciole" );
}
if ( pLeader == NULL )
{
#if defined DEBUG_ALERTS
ALERT ( at_error, "pLeader a NULL\n" );
#endif
return;
}
// nouveau leader
pLuciole = NULL;
pLuciole = (CLuciole*)UTIL_FindEntityByClassname ( NULL, "monster_luciole" );
while ( pLuciole != NULL )
{
if ( pLuciole->IsAlive() )
{
pLuciole->SetLeader ( pLeader );
pLuciole->m_pFollow = NULL;
pLuciole->m_hEnemy = m_hEnemy;
}
pLuciole = (CLuciole*)UTIL_FindEntityByClassname ( pLuciole, "monster_luciole" );
}
// r
pLuciole = Leader();
while ( pLuciole != NULL )
{
CLuciole *pPres = NULL;
mindist = 4096;
// cherche le plus pr
CLuciole *pFind = (CLuciole*)UTIL_FindEntityByClassname ( NULL, "monster_luciole" );
while ( pFind != NULL )
{
// luciole encore solitaires
if ( pLuciole->IsAlive() && pFind != pLuciole && pFind->m_pFollow == NULL && pFind->IsLeader() == FALSE )
{
if ( (pLuciole->pev->origin - pFind->pev->origin).Length() < mindist )
{
mindist = (pLuciole->pev->origin - pFind->pev->origin).Length();
pPres = pFind;
}
}
pFind = (CLuciole*)UTIL_FindEntityByClassname ( pFind, "monster_luciole" );
}
// le plus pres le suit
if ( pPres != NULL )
pPres->m_pFollow = pLuciole;
pLuciole->SquadChanged (); // notification de changement de route etc
pLuciole = pPres;
}
*/
// nouvel ennemi
CLuciole *pLuciole = NULL;
pLuciole = (CLuciole*)UTIL_FindEntityByClassname ( NULL, "monster_luciole" );
while ( pLuciole != NULL )
{
if ( pLuciole->IsAlive() )
{
pLuciole->m_hEnemy = m_hEnemy;
}
pLuciole = (CLuciole*)UTIL_FindEntityByClassname ( pLuciole, "monster_luciole" );
}
}