/*** * * 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. * ****/ //========================================================= // monsterstate.cpp - base class monster functions for // controlling core AI. //========================================================= #include "extdll.h" #include "util.h" #include "cbase.h" #include "nodes.h" #include "monsters.h" #include "animation.h" #include "saverestore.h" #include "soundent.h" //========================================================= // SetState //========================================================= void CBaseMonster :: SetState ( MONSTERSTATE State ) { /* if ( State != m_MonsterState ) { ALERT ( at_aiconsole, "State Changed to %d\n", State ); } */ switch( State ) { // Drop enemy pointers when going to idle case MONSTERSTATE_IDLE: if ( m_hEnemy != NULL ) { m_hEnemy = NULL;// not allowed to have an enemy anymore. ALERT ( at_aiconsole, "Stripped\n" ); } break; } m_MonsterState = State; m_IdealMonsterState = State; } //========================================================= // RunAI //========================================================= void CBaseMonster :: RunAI ( void ) { // to test model's eye height //UTIL_ParticleEffect ( pev->origin + pev->view_ofs, g_vecZero, 255, 10 ); // IDLE sound permitted in ALERT state is because monsters were silent in ALERT state. Only play IDLE sound in IDLE state // once we have sounds for that state. if ( ( m_MonsterState == MONSTERSTATE_IDLE || m_MonsterState == MONSTERSTATE_ALERT ) && RANDOM_LONG(0,99) == 0 && !(pev->flags & SF_MONSTER_GAG) ) { IdleSound(); } if ( m_MonsterState != MONSTERSTATE_NONE && m_MonsterState != MONSTERSTATE_PRONE && m_MonsterState != MONSTERSTATE_DEAD )// don't bother with this crap if monster is prone. { // collect some sensory Condition information. // 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! // UPDATE: We now let COMBAT state monsters think and act fully outside of player PVS. This allows the player to leave // an area where monsters are fighting, and the fight will continue. // if ( !FNullEnt( FIND_CLIENT_IN_PVS( edict() ) ) || ( m_MonsterState == MONSTERSTATE_COMBAT ) ) // { Look( m_flDistLook ); Listen();// check for audible sounds. // now filter conditions. ClearConditions( IgnoreConditions() ); GetEnemy(); // } buz: monsters can act anytime - for rush scripts // do these calculations if monster has an enemy. if ( m_hEnemy != NULL ) { CheckEnemy( m_hEnemy ); } CheckAmmo(); } FCheckAITrigger(); PrescheduleThink(); MaintainSchedule(); // if the monster didn't use these conditions during the above call to MaintainSchedule() or CheckAITrigger() // we throw them out cause we don't want them sitting around through the lifespan of a schedule // that doesn't use them. m_afConditions &= ~( bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE ); // buz if (m_MonsterState == MONSTERSTATE_COMBAT || m_MonsterState == MONSTERSTATE_ALERT || m_MonsterState == MONSTERSTATE_HUNT) { m_iUseAlertAnims = 1; } } //========================================================= // GetIdealState - surveys the Conditions information available // and finds the best new state for a monster. //========================================================= MONSTERSTATE CBaseMonster :: GetIdealState ( void ) { int iConditions; iConditions = IScheduleFlags(); // If no schedule conditions, the new ideal state is probably the reason we're in here. switch ( m_MonsterState ) { case MONSTERSTATE_IDLE: /* IDLE goes to ALERT upon hearing a sound -IDLE goes to ALERT upon being injured IDLE goes to ALERT upon seeing food -IDLE goes to COMBAT upon sighting an enemy IDLE goes to HUNT upon smelling food */ { if ( iConditions & bits_COND_NEW_ENEMY ) { // new enemy! This means an idle monster has seen someone it dislikes, or // that a monster in combat has found a more suitable target to attack m_IdealMonsterState = MONSTERSTATE_COMBAT; } else if ( iConditions & bits_COND_LIGHT_DAMAGE ) { SetIdealYawToTargetAndUpdate( m_vecEnemyLKP ); m_IdealMonsterState = MONSTERSTATE_ALERT; } else if ( iConditions & bits_COND_HEAVY_DAMAGE ) { SetIdealYawToTargetAndUpdate( m_vecEnemyLKP ); m_IdealMonsterState = MONSTERSTATE_ALERT; } else if ( iConditions & bits_COND_HEAR_SOUND ) { CSound *pSound; pSound = PBestSound(); ASSERT( pSound != NULL ); if ( pSound ) { SetIdealYawToTargetAndUpdate( pSound->m_vecOrigin ); if ( pSound->m_iType & (bits_SOUND_COMBAT|bits_SOUND_DANGER) ) m_IdealMonsterState = MONSTERSTATE_ALERT; } } else if ( iConditions & (bits_COND_SMELL | bits_COND_SMELL_FOOD) ) { m_IdealMonsterState = MONSTERSTATE_ALERT; } break; } case MONSTERSTATE_ALERT: /* ALERT goes to IDLE upon becoming bored -ALERT goes to COMBAT upon sighting an enemy ALERT goes to HUNT upon hearing a noise */ { if ( iConditions & (bits_COND_NEW_ENEMY|bits_COND_SEE_ENEMY) ) { // see an enemy we MUST attack m_IdealMonsterState = MONSTERSTATE_COMBAT; } else if ( iConditions & bits_COND_HEAR_SOUND ) { m_IdealMonsterState = MONSTERSTATE_ALERT; CSound *pSound = PBestSound(); ASSERT( pSound != NULL ); if ( pSound ) SetIdealYawToTargetAndUpdate( pSound->m_vecOrigin ); } break; } case MONSTERSTATE_COMBAT: /* COMBAT goes to HUNT upon losing sight of enemy COMBAT goes to ALERT upon death of enemy */ { if ( m_hEnemy == NULL ) { m_IdealMonsterState = MONSTERSTATE_ALERT; ALERT ( at_aiconsole, "***Combat state with no enemy!\n" ); } break; } case MONSTERSTATE_HUNT: /* HUNT goes to ALERT upon seeing food HUNT goes to ALERT upon being injured HUNT goes to IDLE if goal touched HUNT goes to COMBAT upon seeing enemy */ { break; } case MONSTERSTATE_SCRIPT: if ( iConditions & (bits_COND_TASK_FAILED|bits_COND_LIGHT_DAMAGE|bits_COND_HEAVY_DAMAGE) ) { ExitScriptedSequence(); // This will set the ideal state } break; case MONSTERSTATE_DEAD: m_IdealMonsterState = MONSTERSTATE_DEAD; break; } return m_IdealMonsterState; }