mirror of
https://github.com/mapbase-source/source-sdk-2013.git
synced 2025-05-31 16:07:41 +03:00
Shadow Walker: Added 'ambush' state to improve squad behavior.
This commit is contained in:
parent
64841073d9
commit
91556205fb
@ -17,6 +17,7 @@
|
|||||||
#include "engine/IEngineSound.h"
|
#include "engine/IEngineSound.h"
|
||||||
#include "basehlcombatweapon_shared.h"
|
#include "basehlcombatweapon_shared.h"
|
||||||
#include "ai_squadslot.h"
|
#include "ai_squadslot.h"
|
||||||
|
#include "ai_squad.h"
|
||||||
|
|
||||||
|
|
||||||
// memdbgon must be the last include file in a .cpp file!!!
|
// memdbgon must be the last include file in a .cpp file!!!
|
||||||
@ -48,6 +49,7 @@ BEGIN_DATADESC(CNPC_BaseCustomNPC)
|
|||||||
DEFINE_KEYFIELD(m_bCannotOpenDoors, FIELD_BOOLEAN, "CannotOpenDoors"),
|
DEFINE_KEYFIELD(m_bCannotOpenDoors, FIELD_BOOLEAN, "CannotOpenDoors"),
|
||||||
DEFINE_KEYFIELD(m_bCanPickupWeapons, FIELD_BOOLEAN, "CanPickupWeapons"),
|
DEFINE_KEYFIELD(m_bCanPickupWeapons, FIELD_BOOLEAN, "CanPickupWeapons"),
|
||||||
|
|
||||||
|
DEFINE_FIELD(m_iNumSquadmates, FIELD_INTEGER),
|
||||||
DEFINE_FIELD(m_bWanderToggle, FIELD_BOOLEAN),
|
DEFINE_FIELD(m_bWanderToggle, FIELD_BOOLEAN),
|
||||||
DEFINE_FIELD(m_flNextSoundTime, FIELD_TIME),
|
DEFINE_FIELD(m_flNextSoundTime, FIELD_TIME),
|
||||||
DEFINE_FIELD(m_flNextFoundEnemySoundTime, FIELD_TIME),
|
DEFINE_FIELD(m_flNextFoundEnemySoundTime, FIELD_TIME),
|
||||||
@ -245,6 +247,54 @@ int CNPC_BaseCustomNPC::SelectScheduleRetrieveItem()
|
|||||||
return SCHED_NONE;
|
return SCHED_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Purpose: Select ideal state.
|
||||||
|
// Conditions for custom states are defined here.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
NPC_STATE CNPC_BaseCustomNPC::SelectIdealState(void)
|
||||||
|
{
|
||||||
|
switch ((int)this->m_NPCState) {
|
||||||
|
case NPC_STATE_AMBUSH:
|
||||||
|
return SelectAmbushIdealState();
|
||||||
|
case NPC_STATE_SURRENDER:
|
||||||
|
return SelectSurrenderIdealState();
|
||||||
|
default:
|
||||||
|
return BaseClass::SelectIdealState();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
NPC_STATE CNPC_BaseCustomNPC::SelectAmbushIdealState()
|
||||||
|
{
|
||||||
|
// AMBUSH goes to ALERT upon death of enemy
|
||||||
|
if (GetEnemy() == NULL)
|
||||||
|
{
|
||||||
|
return NPC_STATE_ALERT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If I am not in a squad, there is no reason to ambush
|
||||||
|
if (!m_pSquad) {
|
||||||
|
return NPC_STATE_COMBAT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If I am the last in a squad, attack!
|
||||||
|
if (m_pSquad->NumMembers() == 1) {
|
||||||
|
return NPC_STATE_COMBAT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (OccupyStrategySlotRange(SQUAD_SLOT_CHASE_1, SQUAD_SLOT_CHASE_2)) {
|
||||||
|
return NPC_STATE_COMBAT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The best ideal state is the current ideal state.
|
||||||
|
return (NPC_STATE)NPC_STATE_AMBUSH;
|
||||||
|
}
|
||||||
|
|
||||||
|
NPC_STATE CNPC_BaseCustomNPC::SelectSurrenderIdealState()
|
||||||
|
{
|
||||||
|
return NPC_STATE_ALERT;
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Purpose: Select a schedule to retrieve better weapons if they are available.
|
// Purpose: Select a schedule to retrieve better weapons if they are available.
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
@ -265,7 +315,7 @@ int CNPC_BaseCustomNPC::SelectScheduleWander()
|
|||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
int CNPC_BaseCustomNPC::SelectSchedule()
|
int CNPC_BaseCustomNPC::SelectSchedule()
|
||||||
{
|
{
|
||||||
switch (m_NPCState)
|
switch ((int)m_NPCState)
|
||||||
{
|
{
|
||||||
case NPC_STATE_IDLE:
|
case NPC_STATE_IDLE:
|
||||||
AssertMsgOnce(GetEnemy() == NULL, "NPC has enemy but is not in combat state?");
|
AssertMsgOnce(GetEnemy() == NULL, "NPC has enemy but is not in combat state?");
|
||||||
@ -277,7 +327,10 @@ int CNPC_BaseCustomNPC::SelectSchedule()
|
|||||||
|
|
||||||
case NPC_STATE_COMBAT:
|
case NPC_STATE_COMBAT:
|
||||||
return SelectCombatSchedule();
|
return SelectCombatSchedule();
|
||||||
|
case NPC_STATE_AMBUSH:
|
||||||
|
return SelectAmbushSchedule();
|
||||||
|
case NPC_STATE_SURRENDER:
|
||||||
|
return SelectSurrenderSchedule();
|
||||||
default:
|
default:
|
||||||
return BaseClass::SelectSchedule();
|
return BaseClass::SelectSchedule();
|
||||||
}
|
}
|
||||||
@ -361,6 +414,66 @@ int CNPC_BaseCustomNPC::SelectCombatSchedule()
|
|||||||
return BaseClass::SelectSchedule(); // Let Base NPC handle it
|
return BaseClass::SelectSchedule(); // Let Base NPC handle it
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Combat schedule selection
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
int CNPC_BaseCustomNPC::SelectAmbushSchedule()
|
||||||
|
{
|
||||||
|
// Check enemy death
|
||||||
|
if (HasCondition(COND_ENEMY_DEAD))
|
||||||
|
{
|
||||||
|
// clear the current (dead) enemy and try to find another.
|
||||||
|
SetEnemy(NULL);
|
||||||
|
|
||||||
|
if (ChooseEnemy())
|
||||||
|
{
|
||||||
|
SetState(NPC_STATE_COMBAT);
|
||||||
|
FoundEnemySound();
|
||||||
|
ClearCondition(COND_ENEMY_DEAD);
|
||||||
|
return SelectSchedule();
|
||||||
|
}
|
||||||
|
|
||||||
|
SetState(NPC_STATE_ALERT);
|
||||||
|
return SelectSchedule();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CBaseEntity* pEnemy = GetEnemy();
|
||||||
|
if (pEnemy && EnemyDistance(pEnemy) < 128)
|
||||||
|
{
|
||||||
|
SetState(NPC_STATE_COMBAT);
|
||||||
|
return SelectSchedule();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pEnemy == NULL || HasCondition(COND_LOST_ENEMY)) {
|
||||||
|
SetState(NPC_STATE_ALERT);
|
||||||
|
return SelectSchedule();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If I am the last in a squad, attack!
|
||||||
|
if (m_iNumSquadmates > m_pSquad->NumMembers())
|
||||||
|
SetState(SelectAmbushIdealState());
|
||||||
|
|
||||||
|
if (HasCondition(COND_LIGHT_DAMAGE)) {
|
||||||
|
SetState(NPC_STATE_COMBAT);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HasCondition(COND_SEE_ENEMY) && HasCondition(COND_ENEMY_FACING_ME) && HasCondition(COND_HAVE_ENEMY_LOS)) {
|
||||||
|
if(GetState() != NPC_STATE_COMBAT)
|
||||||
|
SetState(SelectAmbushIdealState());
|
||||||
|
return SCHED_HIDE;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_iNumSquadmates = m_pSquad->NumMembers();
|
||||||
|
|
||||||
|
return SCHED_COMBAT_FACE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CNPC_BaseCustomNPC::SelectSurrenderSchedule()
|
||||||
|
{
|
||||||
|
return BaseClass::SelectSchedule();
|
||||||
|
}
|
||||||
|
|
||||||
bool CNPC_BaseCustomNPC::HasRangedWeapon()
|
bool CNPC_BaseCustomNPC::HasRangedWeapon()
|
||||||
{
|
{
|
||||||
CBaseCombatWeapon *pWeapon = GetActiveWeapon();
|
CBaseCombatWeapon *pWeapon = GetActiveWeapon();
|
||||||
|
@ -29,6 +29,28 @@ enum
|
|||||||
LAST_BASE_CUSTOM_SCHED
|
LAST_BASE_CUSTOM_SCHED
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
// states
|
||||||
|
//=========================================================
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
NPC_STATE_FIRST = NPC_STATE_DEAD,
|
||||||
|
NPC_STATE_AMBUSH,
|
||||||
|
NPC_STATE_SURRENDER,
|
||||||
|
NPC_STATE_LAST_CUSTOM
|
||||||
|
};
|
||||||
|
|
||||||
|
// -----------------------------------------------
|
||||||
|
// Squad slots
|
||||||
|
// -----------------------------------------------
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
LAST_SQUADSLOT = 100, // Custom NPCs might share a squad with any NPC, so let's just be safe and skip to a high number
|
||||||
|
SQUAD_SLOT_CHASE_1,
|
||||||
|
SQUAD_SLOT_CHASE_2,
|
||||||
|
LAST_CUSTOM_SQUADSLOT
|
||||||
|
};
|
||||||
|
|
||||||
//=========================================================
|
//=========================================================
|
||||||
//=========================================================
|
//=========================================================
|
||||||
typedef CAI_BlendingHost< CAI_BehaviorHost<CAI_BaseNPC> > CAI_CustomNPCBase;
|
typedef CAI_BlendingHost< CAI_BehaviorHost<CAI_BaseNPC> > CAI_CustomNPCBase;
|
||||||
@ -48,10 +70,17 @@ public:
|
|||||||
virtual int SelectIdleSchedule();
|
virtual int SelectIdleSchedule();
|
||||||
virtual int SelectAlertSchedule();
|
virtual int SelectAlertSchedule();
|
||||||
virtual int SelectCombatSchedule();
|
virtual int SelectCombatSchedule();
|
||||||
|
virtual int SelectAmbushSchedule();
|
||||||
|
virtual int SelectSurrenderSchedule();
|
||||||
virtual float GetSequenceGroundSpeed(CStudioHdr *pStudioHdr, int iSequence);
|
virtual float GetSequenceGroundSpeed(CStudioHdr *pStudioHdr, int iSequence);
|
||||||
virtual Activity NPC_TranslateActivity(Activity eNewActivity);
|
virtual Activity NPC_TranslateActivity(Activity eNewActivity);
|
||||||
virtual int TranslateSchedule(int scheduleType);
|
virtual int TranslateSchedule(int scheduleType);
|
||||||
|
|
||||||
|
// Custom states
|
||||||
|
virtual NPC_STATE SelectIdealState(void);
|
||||||
|
NPC_STATE SelectAmbushIdealState();
|
||||||
|
NPC_STATE SelectSurrenderIdealState();
|
||||||
|
|
||||||
// Sounds
|
// Sounds
|
||||||
virtual void PlaySound(string_t soundname, bool optional);
|
virtual void PlaySound(string_t soundname, bool optional);
|
||||||
virtual void DeathSound(const CTakeDamageInfo &info) { PlaySound(m_iszDeathSound, true); }
|
virtual void DeathSound(const CTakeDamageInfo &info) { PlaySound(m_iszDeathSound, true); }
|
||||||
@ -90,7 +119,7 @@ protected:
|
|||||||
bool HasRangedWeapon();
|
bool HasRangedWeapon();
|
||||||
void PrecacheNPCSoundScript(string_t* SoundName, string_t defaultSoundName);
|
void PrecacheNPCSoundScript(string_t* SoundName, string_t defaultSoundName);
|
||||||
|
|
||||||
|
int m_iNumSquadmates;
|
||||||
bool m_bUseBothSquadSlots; // If true use two squad slots, if false use one squad slot
|
bool m_bUseBothSquadSlots; // If true use two squad slots, if false use one squad slot
|
||||||
bool m_bCannotOpenDoors; // If true, this NPC cannot open doors. The condition is reversed because originally it could.
|
bool m_bCannotOpenDoors; // If true, this NPC cannot open doors. The condition is reversed because originally it could.
|
||||||
bool m_bCanPickupWeapons; // If true, this NPC is able to pick up weapons off of the ground just like npc_citizen.
|
bool m_bCanPickupWeapons; // If true, this NPC is able to pick up weapons off of the ground just like npc_citizen.
|
||||||
|
@ -186,30 +186,6 @@ int CNPC_ShadowWalker::SelectFailSchedule(int failedSchedule, int failedTask, AI
|
|||||||
return BaseClass::SelectFailSchedule(failedSchedule, failedTask, taskFailCode);
|
return BaseClass::SelectFailSchedule(failedSchedule, failedTask, taskFailCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Purpose: Select a schedule to execute based on conditions.
|
|
||||||
// This is the most critical AI method.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
int CNPC_ShadowWalker::SelectSchedule()
|
|
||||||
{
|
|
||||||
switch (m_NPCState)
|
|
||||||
{
|
|
||||||
case NPC_STATE_IDLE:
|
|
||||||
AssertMsgOnce(GetEnemy() == NULL, "NPC has enemy but is not in combat state?");
|
|
||||||
return SelectIdleSchedule();
|
|
||||||
|
|
||||||
case NPC_STATE_ALERT:
|
|
||||||
AssertMsgOnce(GetEnemy() == NULL, "NPC has enemy but is not in combat state?");
|
|
||||||
return SelectAlertSchedule();
|
|
||||||
|
|
||||||
case NPC_STATE_COMBAT:
|
|
||||||
return SelectCombatSchedule();
|
|
||||||
|
|
||||||
default:
|
|
||||||
return BaseClass::SelectSchedule();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Idle schedule selection
|
// Idle schedule selection
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
@ -305,7 +281,6 @@ int CNPC_ShadowWalker::SelectCombatSchedule()
|
|||||||
if (ChooseEnemy())
|
if (ChooseEnemy())
|
||||||
{
|
{
|
||||||
FoundEnemySound();
|
FoundEnemySound();
|
||||||
|
|
||||||
ClearCondition(COND_ENEMY_DEAD);
|
ClearCondition(COND_ENEMY_DEAD);
|
||||||
return SelectSchedule();
|
return SelectSchedule();
|
||||||
}
|
}
|
||||||
@ -327,17 +302,20 @@ int CNPC_ShadowWalker::SelectCombatSchedule()
|
|||||||
|
|
||||||
// If in a squad, only one or two shadow walkers can chase the player. This is configurable through Hammer.
|
// If in a squad, only one or two shadow walkers can chase the player. This is configurable through Hammer.
|
||||||
bool bCanChase = true;
|
bool bCanChase = true;
|
||||||
if (bEnemyCanSeeMe && m_bUseBothSquadSlots) {
|
if (m_bUseBothSquadSlots) {
|
||||||
bCanChase = OccupyStrategySlotRange(SQUAD_SLOT_ATTACK1, SQUAD_SLOT_ATTACK2);
|
bCanChase = OccupyStrategySlotRange(SQUAD_SLOT_CHASE_1, SQUAD_SLOT_CHASE_2);
|
||||||
}
|
}
|
||||||
else if (bEnemyCanSeeMe){
|
else {
|
||||||
bCanChase = OccupyStrategySlot(SQUAD_SLOT_ATTACK1);
|
bCanChase = OccupyStrategySlot(SQUAD_SLOT_CHASE_1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bCanChase = bCanChase || EnemyDistance(GetEnemy()) < 128;
|
||||||
|
|
||||||
// If I'm not allowed to chase this enemy of this enemy and he's looking at me, set up an ambush
|
// If I'm not allowed to chase this enemy of this enemy and he's looking at me, set up an ambush
|
||||||
if (!bCanChase)
|
if (!bCanChase)
|
||||||
{
|
{
|
||||||
FearSound();
|
FearSound();
|
||||||
|
SetState((NPC_STATE)NPC_STATE_AMBUSH);
|
||||||
return SCHED_HIDE;
|
return SCHED_HIDE;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,6 @@ public:
|
|||||||
void Spawn(void);
|
void Spawn(void);
|
||||||
Class_T Classify(void);
|
Class_T Classify(void);
|
||||||
virtual int SelectFailSchedule(int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode);
|
virtual int SelectFailSchedule(int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode);
|
||||||
virtual int SelectSchedule();
|
|
||||||
virtual int SelectIdleSchedule();
|
virtual int SelectIdleSchedule();
|
||||||
virtual int SelectAlertSchedule();
|
virtual int SelectAlertSchedule();
|
||||||
virtual int SelectCombatSchedule();
|
virtual int SelectCombatSchedule();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user