CBaseEntity *CBaseMonster::BestVisibleEnemy(void) { int iNearest = 8192; CBaseEntity *pNextEnt = m_pLink; CBaseEntity *pReturn = NULL; int iBestRelationship = R_NO; while (pNextEnt) { if (pNextEnt->IsAlive()) { if (IRelationship(pNextEnt) > iBestRelationship) { iBestRelationship = IRelationship(pNextEnt); iNearest = (int)((pNextEnt->pev->origin - pev->origin).Length()); pReturn = pNextEnt; } else if (IRelationship(pNextEnt) == iBestRelationship) { int iDist = (int)((pNextEnt->pev->origin - pev->origin).Length()); if (iDist <= iNearest) { iNearest = iDist; iBestRelationship = IRelationship(pNextEnt); pReturn = pNextEnt; } } } pNextEnt = pNextEnt->m_pLink; } return pReturn; }
void CBaseMonster::Look(int iDistance) { int iSighted = 0; ClearConditions(bits_COND_SEE_HATE | bits_COND_SEE_DISLIKE | bits_COND_SEE_ENEMY | bits_COND_SEE_FEAR | bits_COND_SEE_NEMESIS | bits_COND_SEE_CLIENT); m_pLink = NULL; CBaseEntity *pSightEnt = NULL; CBaseEntity *pList[100]; Vector delta = Vector(iDistance, iDistance, iDistance); int count = UTIL_EntitiesInBox(pList, 100, pev->origin - delta, pev->origin + delta, FL_CLIENT | FL_MONSTER); for (int i = 0; i < count; i++) { pSightEnt = pList[i]; if (pSightEnt != this && pSightEnt->pev->health > 0) { if (IRelationship(pSightEnt) != R_NO && FInViewCone(pSightEnt) && !FBitSet(pSightEnt->pev->flags, FL_NOTARGET) && FVisible(pSightEnt)) { if (pSightEnt->IsPlayer()) iSighted |= bits_COND_SEE_CLIENT; pSightEnt->m_pLink = m_pLink; m_pLink = pSightEnt; if (pSightEnt == m_hEnemy) iSighted |= bits_COND_SEE_ENEMY; switch (IRelationship(pSightEnt)) { case R_NM: iSighted |= bits_COND_SEE_NEMESIS; break; case R_HT: iSighted |= bits_COND_SEE_HATE; break; case R_DL: iSighted |= bits_COND_SEE_DISLIKE; break; case R_FR: iSighted |= bits_COND_SEE_FEAR; break; case R_AL: break; } } } } SetConditions(iSighted); }
/* <fc317> ../cstrike/dlls/mpstubb.cpp:220 */ CBaseEntity *CBaseMonster::__MAKE_VHOOK(BestVisibleEnemy)(void) { CBaseEntity *pReturn; CBaseEntity *pNextEnt; int iNearest; int iDist; int iBestRelationship; // so first visible entity will become the closest. iNearest = 8192; pNextEnt = m_pLink; pReturn = NULL; iBestRelationship = R_NO; while (pNextEnt != NULL) { if (pNextEnt->IsAlive()) { if (IRelationship(pNextEnt) > iBestRelationship) { // this entity is disliked MORE than the entity that we // currently think is the best visible enemy. No need to do // a distance check, just get mad at this one for now. iBestRelationship = IRelationship(pNextEnt); iNearest = (pNextEnt->pev->origin - pev->origin).Length(); pReturn = pNextEnt; } else if (IRelationship(pNextEnt) == iBestRelationship) { // this entity is disliked just as much as the entity that // we currently think is the best visible enemy, so we only // get mad at it if it is closer. iDist = (pNextEnt->pev->origin - pev->origin).Length(); if (iDist <= iNearest) { iNearest = iDist; iBestRelationship = IRelationship(pNextEnt); pReturn = pNextEnt; } } } pNextEnt = pNextEnt->m_pLink; } return pReturn; }
//========================================================= // Tracking Hornet hit something //========================================================= void CHornet :: TrackTouch ( CBaseEntity *pOther ) { if ( pOther->edict() == pev->owner || pOther->pev->modelindex == pev->modelindex ) {// bumped into the guy that shot it. pev->solid = SOLID_NOT; return; } if ( IRelationship( pOther ) <= R_NO ) { // hit something we don't want to hurt, so turn around. Vector vecVelocity = GetAbsVelocity(); vecVelocity = vecVelocity.Normalize(); vecVelocity.x *= -1; vecVelocity.y *= -1; SetAbsVelocity( vecVelocity ); SetAbsOrigin( GetAbsOrigin() + GetAbsVelocity() * 4 ); // bounce the hornet off a bit. SetAbsVelocity( GetAbsVelocity() * m_flFlySpeed ); return; } DieTouch( pOther ); }
//========================================================= // Tracking Hornet hit something //========================================================= void CHornet :: TrackTouch ( CBaseEntity *pOther ) { if ( pOther->edict() == pev->owner || pOther->pev->modelindex == pev->modelindex ) {// bumped into the guy that shot it. pev->solid = SOLID_NOT; return; } if ( IRelationship( pOther ) <= R_NO ) { // hit something we don't want to hurt, so turn around. pev->velocity = pev->velocity.Normalize(); pev->velocity.x *= -1; pev->velocity.y *= -1; pev->origin = pev->origin + pev->velocity * 4; // bounce the hornet off a bit. pev->velocity = pev->velocity * m_flFlySpeed; return; } DieTouch( pOther ); }
//========================================================= // Tracking Hornet hit something //========================================================= void CMHornet :: TrackTouch ( edict_t *pOther ) { if ( (pOther == pev->owner) || pOther->v.modelindex == pev->modelindex ) {// bumped into the guy that shot it. pev->solid = SOLID_NOT; return; } // is this NOT a player and IS a monster? if (!UTIL_IsPlayer(pOther) && (pOther->v.euser4 != NULL)) { CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther)); if ( IRelationship( pMonster ) <= R_NO ) { // hit something we don't want to hurt, so turn around. pev->velocity = pev->velocity.Normalize(); pev->velocity.x *= -1; pev->velocity.y *= -1; pev->origin = pev->origin + pev->velocity * 4; // bounce the hornet off a bit. pev->velocity = pev->velocity * m_flFlySpeed; return; } } DieTouch( pOther ); }
CBaseEntity *CBarnacle :: TongueTouchEnt ( float *pflLength ) { TraceResult tr; float length; // trace once to hit architecture and see if the tongue needs to change position. UTIL_TraceLine ( pev->origin, pev->origin - Vector ( 0 , 0 , 2048 ), ignore_monsters, ENT(pev), &tr ); length = fabs( pev->origin.z - tr.vecEndPos.z ); if ( pflLength ) { *pflLength = length; } Vector delta = Vector( BARNACLE_CHECK_SPACING, BARNACLE_CHECK_SPACING, 0 ); Vector mins = pev->origin - delta; Vector maxs = pev->origin + delta; maxs.z = pev->origin.z; mins.z -= length; CBaseEntity *pList[10]; int count = UTIL_EntitiesInBox( pList, 10, mins, maxs, (FL_CLIENT|FL_MONSTER) ); if ( count ) { for ( int i = 0; i < count; i++ ) { // only clients and monsters if ( pList[i] != this && IRelationship( pList[i] ) > R_NO && pList[ i ]->pev->deadflag == DEAD_NO ) // this ent is one of our enemies. Barnacle tries to eat it. { return pList[i]; } } } return NULL; }
int CISlave::TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType) { // don't slash one of your own if((bitsDamageType & DMG_SLASH) && pevAttacker && IRelationship(Instance(pevAttacker)) < R_DL) return 0; m_afMemory |= bits_MEMORY_PROVOKED; return CSquadMonster::TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType); }
int CISlave::TakeDamage( CBaseEntity* pInflictor, CBaseEntity* pAttacker, float flDamage, int bitsDamageType ) { // don't slash one of your own if ((bitsDamageType & DMG_SLASH) && pAttacker && IRelationship( pAttacker ) < R_DL) return 0; m_afMemory |= bits_MEMORY_PROVOKED; return CSquadMonster::TakeDamage( pInflictor, pAttacker, flDamage, bitsDamageType ); }
//========================================================= // Look - overriden for the roach, which can virtually see // 360 degrees. //========================================================= void CRoach :: Look ( int iDistance ) { CBaseEntity *pSightEnt = NULL;// the current visible entity that we're dealing with CBaseEntity *pPreviousEnt;// the last entity added to the link list int iSighted = 0; // DON'T let visibility information from last frame sit around! ClearConditions( bits_COND_SEE_HATE |bits_COND_SEE_DISLIKE | bits_COND_SEE_ENEMY | bits_COND_SEE_FEAR ); // don't let monsters outside of the player's PVS act up, or most of the interesting // things will happen before the player gets there! if ( FNullEnt( FIND_CLIENT_IN_PVS( edict() ) ) ) { return; } m_pLink = NULL; pPreviousEnt = this; // Does sphere also limit itself to PVS? // Examine all entities within a reasonable radius // !!!PERFORMANCE - let's trivially reject the ent list before radius searching! while ((pSightEnt = UTIL_FindEntityInSphere( pSightEnt, pev->origin, iDistance )) != NULL) { // only consider ents that can be damaged. !!!temporarily only considering other monsters and clients if ( pSightEnt->IsPlayer() || FBitSet ( pSightEnt->pev->flags, FL_MONSTER ) ) { if ( /*FVisible( pSightEnt ) &&*/ !FBitSet( pSightEnt->pev->flags, FL_NOTARGET ) && pSightEnt->pev->health > 0 ) { // NULL the Link pointer for each ent added to the link list. If other ents follow, the will overwrite // this value. If this ent happens to be the last, the list will be properly terminated. pPreviousEnt->m_pLink = pSightEnt; pSightEnt->m_pLink = NULL; pPreviousEnt = pSightEnt; // don't add the Enemy's relationship to the conditions. We only want to worry about conditions when // we see monsters other than the Enemy. switch ( IRelationship ( pSightEnt ) ) { case R_FR: iSighted |= bits_COND_SEE_FEAR; break; case R_NO: break; default: ALERT ( at_console, "%s can't asses %s\n", STRING(pev->classname), STRING(pSightEnt->pev->classname ) ); break; } } } } SetConditions( iSighted ); }
int CISlave :: TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType) { // don't slash one of your own if ((bitsDamageType & DMG_SLASH) && pevAttacker && IRelationship( Instance(pevAttacker) ) < R_DL) return 0; //LRC - if my player reaction has been overridden, leave this alone if (m_iPlayerReact == 0) m_afMemory |= bits_MEMORY_PROVOKED; return CSquadMonster::TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType); }
//========================================================= // Look - Base class monster function to find enemies or // food by sight. iDistance is distance ( in units ) that the // monster can see. // // Sets the sight bits of the m_afConditions mask to indicate // which types of entities were sighted. // Function also sets the Looker's m_pLink // to the head of a link list that contains all visible ents. // (linked via each ent's m_pLink field) // //========================================================= void CBaseMonster :: Look ( int iDistance ) { int iSighted = 0; // DON'T let visibility information from last frame sit around! ClearConditions(bits_COND_SEE_HATE | bits_COND_SEE_DISLIKE | bits_COND_SEE_ENEMY | bits_COND_SEE_FEAR | bits_COND_SEE_NEMESIS | bits_COND_SEE_CLIENT); m_pLink = NULL; CBaseEntity *pSightEnt = NULL;// the current visible entity that we're dealing with CBaseEntity *pList[100]; Vector delta = Vector( iDistance, iDistance, iDistance ); // Find only monsters/clients in box, NOT limited to PVS int count = UTIL_EntitiesInBox( pList, 100, pev->origin - delta, pev->origin + delta, FL_CLIENT|FL_MONSTER ); for ( int i = 0; i < count; i++ ) { pSightEnt = pList[i]; if ( pSightEnt != this && pSightEnt->pev->health > 0 ) { // the looker will want to consider this entity // don't check anything else about an entity that can't be seen, or an entity that you don't care about. if ( IRelationship( pSightEnt ) != R_NO && FInViewCone( pSightEnt ) && !FBitSet( pSightEnt->pev->flags, FL_NOTARGET ) && FVisible( pSightEnt ) ) { if ( pSightEnt->IsPlayer() ) { // if we see a client, remember that (mostly for scripted AI) iSighted |= bits_COND_SEE_CLIENT; } pSightEnt->m_pLink = m_pLink; m_pLink = pSightEnt; if ( pSightEnt == m_hEnemy ) { // we know this ent is visible, so if it also happens to be our enemy, store that now. iSighted |= bits_COND_SEE_ENEMY; } // don't add the Enemy's relationship to the conditions. We only want to worry about conditions when // we see monsters other than the Enemy. switch ( IRelationship ( pSightEnt ) ) { case R_NM: iSighted |= bits_COND_SEE_NEMESIS; break; case R_HT: iSighted |= bits_COND_SEE_HATE; break; case R_DL: iSighted |= bits_COND_SEE_DISLIKE; break; case R_FR: iSighted |= bits_COND_SEE_FEAR; break; case R_AL: break; default: ALERT ( at_aiconsole, "%s can't assess %s\n", STRING(pev->classname), STRING(pSightEnt->pev->classname ) ); break; } } } } SetConditions( iSighted ); }
Schedule_t *CScientist :: GetSchedule ( void ) { // so we don't keep calling through the EHANDLE stuff CBaseEntity *pEnemy = m_hEnemy; if ( HasConditions( bits_COND_HEAR_SOUND ) ) { CSound *pSound; pSound = PBestSound(); ASSERT( pSound != NULL ); if ( pSound && (pSound->m_iType & bits_SOUND_DANGER) ) return GetScheduleOfType( SCHED_TAKE_COVER_FROM_BEST_SOUND ); } switch( m_MonsterState ) { case MONSTERSTATE_ALERT: case MONSTERSTATE_IDLE: if ( pEnemy ) { if ( HasConditions( bits_COND_SEE_ENEMY ) ) m_fearTime = gpGlobals->time; else if ( DisregardEnemy( pEnemy ) ) // After 15 seconds of being hidden, return to alert { m_hEnemy = NULL; pEnemy = NULL; } } if ( HasConditions(bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE)) { // flinch if hurt return GetScheduleOfType( SCHED_SMALL_FLINCH ); } // Cower when you hear something scary if ( HasConditions( bits_COND_HEAR_SOUND ) ) { CSound *pSound; pSound = PBestSound(); ASSERT( pSound != NULL ); if ( pSound ) { if ( pSound->m_iType & (bits_SOUND_DANGER | bits_SOUND_COMBAT) ) { if ( gpGlobals->time - m_fearTime > 3 ) // Only cower every 3 seconds or so { m_fearTime = gpGlobals->time; // Update last fear return GetScheduleOfType( SCHED_STARTLE ); // This will just duck for a second } } } } // Behavior for following the player if ( IsFollowing() ) { if ( !m_hTargetEnt->IsAlive() ) { // UNDONE: Comment about the recently dead player here? StopFollowing( FALSE ); break; } int relationship = R_NO; // Nothing scary, just me and the player if ( pEnemy != NULL ) relationship = IRelationship( pEnemy ); // UNDONE: Model fear properly, fix R_FR and add multiple levels of fear if ( relationship != R_DL && relationship != R_HT ) { // If I'm already close enough to my target if ( TargetDistance() <= 128 ) { if ( CanHeal() ) // Heal opportunistically return slHeal; if ( HasConditions( bits_COND_CLIENT_PUSH ) ) // Player wants me to move return GetScheduleOfType( SCHED_MOVE_AWAY_FOLLOW ); } return GetScheduleOfType( SCHED_TARGET_FACE ); // Just face and follow. } else // UNDONE: When afraid, scientist won't move out of your way. Keep This? If not, write move away scared { if ( HasConditions( bits_COND_NEW_ENEMY ) ) // I just saw something new and scary, react return GetScheduleOfType( SCHED_FEAR ); // React to something scary return GetScheduleOfType( SCHED_TARGET_FACE_SCARED ); // face and follow, but I'm scared! } } if ( HasConditions( bits_COND_CLIENT_PUSH ) ) // Player wants me to move return GetScheduleOfType( SCHED_MOVE_AWAY ); // try to say something about smells TrySmellTalk(); break; case MONSTERSTATE_COMBAT: if ( HasConditions( bits_COND_NEW_ENEMY ) ) return slFear; // Point and scream! if ( HasConditions( bits_COND_SEE_ENEMY ) ) return slScientistCover; // Take Cover if ( HasConditions( bits_COND_HEAR_SOUND ) ) return slTakeCoverFromBestSound; // Cower and panic from the scary sound! return slScientistCover; // Run & Cower break; } return CTalkMonster::GetSchedule(); }
MONSTERSTATE CScientist :: GetIdealState ( void ) { switch ( m_MonsterState ) { case MONSTERSTATE_ALERT: case MONSTERSTATE_IDLE: if ( HasConditions( bits_COND_NEW_ENEMY ) ) { if ( IsFollowing() ) { int relationship = IRelationship( m_hEnemy ); if ( relationship != R_FR || relationship != R_HT && !HasConditions( bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE ) ) { // Don't go to combat if you're following the player m_IdealMonsterState = MONSTERSTATE_ALERT; return m_IdealMonsterState; } StopFollowing( TRUE ); } } else if ( HasConditions( bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE ) ) { // Stop following if you take damage if ( IsFollowing() ) StopFollowing( TRUE ); } break; case MONSTERSTATE_COMBAT: { CBaseEntity *pEnemy = m_hEnemy; if ( pEnemy != NULL ) { if ( DisregardEnemy( pEnemy ) ) // After 15 seconds of being hidden, return to alert { // Strip enemy when going to alert m_IdealMonsterState = MONSTERSTATE_ALERT; m_hEnemy = NULL; return m_IdealMonsterState; } // Follow if only scared a little if ( m_hTargetEnt != NULL ) { m_IdealMonsterState = MONSTERSTATE_ALERT; return m_IdealMonsterState; } if ( HasConditions ( bits_COND_SEE_ENEMY ) ) { m_fearTime = gpGlobals->time; m_IdealMonsterState = MONSTERSTATE_COMBAT; return m_IdealMonsterState; } } } break; } return CTalkMonster::GetIdealState(); }
//========================================================= // GetSchedule //========================================================= Schedule_t *CBullsquid :: GetSchedule( void ) { switch ( m_MonsterState ) { case MONSTERSTATE_ALERT: { if ( HasConditions(bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE) ) { return GetScheduleOfType ( SCHED_SQUID_HURTHOP ); } if ( HasConditions(bits_COND_SMELL_FOOD) ) { CSound *pSound; pSound = PBestScent(); if ( pSound && (!FInViewCone ( &pSound->m_vecOrigin ) || !FVisible ( pSound->m_vecOrigin )) ) { // scent is behind or occluded return GetScheduleOfType( SCHED_SQUID_SNIFF_AND_EAT ); } // food is right out in the open. Just go get it. return GetScheduleOfType( SCHED_SQUID_EAT ); } if ( HasConditions(bits_COND_SMELL) ) { // there's something stinky. CSound *pSound; pSound = PBestScent(); if ( pSound ) return GetScheduleOfType( SCHED_SQUID_WALLOW); } break; } case MONSTERSTATE_COMBAT: { // dead enemy if ( HasConditions( bits_COND_ENEMY_DEAD ) ) { // call base class, all code to handle dead enemies is centralized there. return CBaseMonster :: GetSchedule(); } if ( HasConditions(bits_COND_NEW_ENEMY) ) { if ( m_fCanThreatDisplay && IRelationship( m_hEnemy ) == R_HT ) { // this means squid sees a headcrab! m_fCanThreatDisplay = FALSE;// only do the headcrab dance once per lifetime. return GetScheduleOfType ( SCHED_SQUID_SEECRAB ); } else { return GetScheduleOfType ( SCHED_WAKE_ANGRY ); } } if ( HasConditions(bits_COND_SMELL_FOOD) ) { CSound *pSound; pSound = PBestScent(); if ( pSound && (!FInViewCone ( &pSound->m_vecOrigin ) || !FVisible ( pSound->m_vecOrigin )) ) { // scent is behind or occluded return GetScheduleOfType( SCHED_SQUID_SNIFF_AND_EAT ); } // food is right out in the open. Just go get it. return GetScheduleOfType( SCHED_SQUID_EAT ); } if ( HasConditions( bits_COND_CAN_RANGE_ATTACK1 ) ) { return GetScheduleOfType ( SCHED_RANGE_ATTACK1 ); } if ( HasConditions( bits_COND_CAN_MELEE_ATTACK1 ) ) { return GetScheduleOfType ( SCHED_MELEE_ATTACK1 ); } if ( HasConditions( bits_COND_CAN_MELEE_ATTACK2 ) ) { return GetScheduleOfType ( SCHED_MELEE_ATTACK2 ); } return GetScheduleOfType ( SCHED_CHASE_ENEMY ); break; } } return CBaseMonster :: GetSchedule(); }