2022-12-17 13:32:43 +01:00
|
|
|
/***
|
2013-08-30 13:34:05 -07:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
****/
|
|
|
|
//=========================================================
|
2021-11-28 16:54:48 +01:00
|
|
|
// monsterstate.cpp - base class monster functions for
|
2013-08-30 13:34:05 -07:00
|
|
|
// controlling core AI.
|
|
|
|
//=========================================================
|
|
|
|
|
|
|
|
#include "extdll.h"
|
|
|
|
#include "util.h"
|
|
|
|
#include "cbase.h"
|
|
|
|
#include "monsters.h"
|
|
|
|
#include "animation.h"
|
|
|
|
#include "saverestore.h"
|
|
|
|
#include "soundent.h"
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// SetState
|
|
|
|
//=========================================================
|
2021-11-29 20:31:17 +01:00
|
|
|
void CBaseMonster::SetState(MONSTERSTATE State)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
/*
|
2013-08-30 13:34:05 -07:00
|
|
|
if ( State != m_MonsterState )
|
|
|
|
{
|
|
|
|
ALERT ( at_aiconsole, "State Changed to %d\n", State );
|
|
|
|
}
|
|
|
|
*/
|
2021-11-28 16:54:48 +01:00
|
|
|
|
|
|
|
switch (State)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
// Drop enemy pointers when going to idle
|
|
|
|
case MONSTERSTATE_IDLE:
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (m_hEnemy != NULL)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
m_hEnemy = NULL; // not allowed to have an enemy anymore.
|
|
|
|
ALERT(at_aiconsole, "Stripped\n");
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_MonsterState = State;
|
|
|
|
m_IdealMonsterState = State;
|
|
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// RunAI
|
|
|
|
//=========================================================
|
2021-11-29 20:31:17 +01:00
|
|
|
void CBaseMonster::RunAI()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
// 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.
|
2021-11-28 16:54:48 +01:00
|
|
|
if ((m_MonsterState == MONSTERSTATE_IDLE || m_MonsterState == MONSTERSTATE_ALERT) && RANDOM_LONG(0, 99) == 0 && (pev->spawnflags & SF_MONSTER_GAG) == 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
IdleSound();
|
|
|
|
}
|
|
|
|
|
2021-11-28 16:54:48 +01:00
|
|
|
if (m_MonsterState != MONSTERSTATE_NONE &&
|
|
|
|
m_MonsterState != MONSTERSTATE_PRONE &&
|
|
|
|
m_MonsterState != MONSTERSTATE_DEAD) // don't bother with this crap if monster is prone.
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
// 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!
|
2021-11-28 16:54:48 +01:00
|
|
|
// UPDATE: We now let COMBAT state monsters think and act fully outside of player PVS. This allows the player to leave
|
2013-08-30 13:34:05 -07:00
|
|
|
// an area where monsters are fighting, and the fight will continue.
|
2021-11-28 16:54:48 +01:00
|
|
|
if (!FNullEnt(FIND_CLIENT_IN_PVS(edict())) || (m_MonsterState == MONSTERSTATE_COMBAT))
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
Look(m_flDistLook);
|
|
|
|
Listen(); // check for audible sounds.
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
// now filter conditions.
|
2021-11-28 16:54:48 +01:00
|
|
|
ClearConditions(IgnoreConditions());
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
GetEnemy();
|
|
|
|
}
|
|
|
|
|
|
|
|
// do these calculations if monster has an enemy.
|
2021-11-28 16:54:48 +01:00
|
|
|
if (m_hEnemy != NULL)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
CheckEnemy(m_hEnemy);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
2021-11-28 16:54:48 +01:00
|
|
|
// that doesn't use them.
|
|
|
|
m_afConditions &= ~(bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
// GetIdealState - surveys the Conditions information available
|
|
|
|
// and finds the best new state for a monster.
|
|
|
|
//=========================================================
|
2021-11-29 20:31:17 +01:00
|
|
|
MONSTERSTATE CBaseMonster::GetIdealState()
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
int iConditions;
|
2013-08-30 13:34:05 -07:00
|
|
|
|
|
|
|
iConditions = IScheduleFlags();
|
2021-11-28 16:54:48 +01:00
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
// If no schedule conditions, the new ideal state is probably the reason we're in here.
|
2021-11-28 16:54:48 +01:00
|
|
|
switch (m_MonsterState)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
case MONSTERSTATE_IDLE:
|
2021-11-28 16:54:48 +01:00
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
/*
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
if ((iConditions & bits_COND_NEW_ENEMY) != 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
// new enemy! This means an idle monster has seen someone it dislikes, or
|
2013-08-30 13:34:05 -07:00
|
|
|
// that a monster in combat has found a more suitable target to attack
|
|
|
|
m_IdealMonsterState = MONSTERSTATE_COMBAT;
|
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
else if ((iConditions & bits_COND_LIGHT_DAMAGE) != 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
MakeIdealYaw(m_vecEnemyLKP);
|
2013-08-30 13:34:05 -07:00
|
|
|
m_IdealMonsterState = MONSTERSTATE_ALERT;
|
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
else if ((iConditions & bits_COND_HEAVY_DAMAGE) != 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
MakeIdealYaw(m_vecEnemyLKP);
|
2013-08-30 13:34:05 -07:00
|
|
|
m_IdealMonsterState = MONSTERSTATE_ALERT;
|
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
else if ((iConditions & bits_COND_HEAR_SOUND) != 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
CSound* pSound;
|
|
|
|
|
2013-08-30 13:34:05 -07:00
|
|
|
pSound = PBestSound();
|
2021-11-28 16:54:48 +01:00
|
|
|
ASSERT(pSound != NULL);
|
|
|
|
if (pSound)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
MakeIdealYaw(pSound->m_vecOrigin);
|
|
|
|
if ((pSound->m_iType & (bits_SOUND_COMBAT | bits_SOUND_DANGER)) != 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
m_IdealMonsterState = MONSTERSTATE_ALERT;
|
|
|
|
}
|
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
else if ((iConditions & (bits_COND_SMELL | bits_COND_SMELL_FOOD)) != 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
if ((iConditions & (bits_COND_NEW_ENEMY | bits_COND_SEE_ENEMY)) != 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
// see an enemy we MUST attack
|
|
|
|
m_IdealMonsterState = MONSTERSTATE_COMBAT;
|
|
|
|
}
|
2021-11-28 16:54:48 +01:00
|
|
|
else if ((iConditions & bits_COND_HEAR_SOUND) != 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
m_IdealMonsterState = MONSTERSTATE_ALERT;
|
2021-11-28 16:54:48 +01:00
|
|
|
CSound* pSound = PBestSound();
|
|
|
|
ASSERT(pSound != NULL);
|
|
|
|
if (pSound)
|
|
|
|
MakeIdealYaw(pSound->m_vecOrigin);
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case MONSTERSTATE_COMBAT:
|
|
|
|
/*
|
|
|
|
COMBAT goes to HUNT upon losing sight of enemy
|
|
|
|
COMBAT goes to ALERT upon death of enemy
|
|
|
|
*/
|
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
if (m_hEnemy == NULL)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
|
|
|
m_IdealMonsterState = MONSTERSTATE_ALERT;
|
|
|
|
// pev->effects = EF_BRIGHTFIELD;
|
2021-11-28 16:54:48 +01:00
|
|
|
ALERT(at_aiconsole, "***Combat state with no enemy!\n");
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
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:
|
2021-11-28 16:54:48 +01:00
|
|
|
if ((iConditions & (bits_COND_TASK_FAILED | bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE)) != 0)
|
2013-08-30 13:34:05 -07:00
|
|
|
{
|
2021-11-28 16:54:48 +01:00
|
|
|
ExitScriptedSequence(); // This will set the ideal state
|
2013-08-30 13:34:05 -07:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MONSTERSTATE_DEAD:
|
|
|
|
m_IdealMonsterState = MONSTERSTATE_DEAD;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return m_IdealMonsterState;
|
|
|
|
}
|