From ef5a5ef39fdbccde7c613d902075188cda2904f3 Mon Sep 17 00:00:00 2001 From: 1upD <1upderek@gmail.com> Date: Mon, 22 Oct 2018 18:21:41 -0400 Subject: [PATCH] Merged changes made in my local MapLabs repo with changes made to my own repo --- sp/game/mod_episodic/halloween.fgd | 8 + sp/src/game/server/mod/npc_shadow_walker.cpp | 193 ++++++++++++++++--- 2 files changed, 171 insertions(+), 30 deletions(-) diff --git a/sp/game/mod_episodic/halloween.fgd b/sp/game/mod_episodic/halloween.fgd index 876c75b2..ee5d5e27 100644 --- a/sp/game/mod_episodic/halloween.fgd +++ b/sp/game/mod_episodic/halloween.fgd @@ -50,9 +50,17 @@ 0 : "Can Open Doors" 1 : "Cannot Open Doors" ] + CanPickupWeapons(choices) : "Can Pick Up Guns?" : 0 : "Is this NPC able to pick up guns? You can change this after spawning with EnablePickupWeapons and DisablePickupWeapons." = + [ + 0 : "Cannot Pick Up Guns" + 1 : "Can Pick Up Guns" + ] input SetSpeedModifier(float) : "Set a float value to multiple distance traveled by." input EnableOpenDoors(void) : "Allow this NPC to open doors. (Warning: Doesn't always seem to update pathfinding / AI)" input DisableOpenDoors(void) : "Prevent this NPC from opening doors." + input EnablePickupWeapons(void) : "Allow this NPC to pick up any weapon off of the ground." + input DisablePickupWeapons(void) : "Prevent this NPC from picking up weapons." + ] @NPCClass base(npc_manhack) studio("models/skeleton/skeleton_torso3.mdl") = npc_lost_soul : "Lost Soul" diff --git a/sp/src/game/server/mod/npc_shadow_walker.cpp b/sp/src/game/server/mod/npc_shadow_walker.cpp index eb57b5c2..606f749c 100644 --- a/sp/src/game/server/mod/npc_shadow_walker.cpp +++ b/sp/src/game/server/mod/npc_shadow_walker.cpp @@ -29,6 +29,15 @@ // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" +//========================================================= +// schedules +//========================================================= +enum +{ + SCHED_MELEE_ATTACK_NOINTERRUPT, + SCHED_HIDE +}; + //========================================================= //========================================================= class CNPC_ShadowWalker : public CAI_BaseNPC @@ -41,13 +50,15 @@ public: Class_T Classify( void ); virtual int SelectFailSchedule(int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode); virtual int SelectScheduleRetrieveItem(); + virtual int SelectScheduleWander(); virtual int SelectSchedule(); virtual int SelectIdleSchedule(); virtual int SelectAlertSchedule(); virtual int SelectCombatSchedule(); - virtual bool CanPickkUpWeapons() { return true; } + virtual bool CanPickkUpWeapons() { return m_bCanPickupWeapons; } virtual float GetSequenceGroundSpeed(CStudioHdr *pStudioHdr, int iSequence); Activity NPC_TranslateActivity(Activity eNewActivity); + virtual int TranslateSchedule(int scheduleType); // Sounds virtual void PlaySound(string_t soundname, bool optional); @@ -57,7 +68,7 @@ public: virtual void PainSound(const CTakeDamageInfo &info) { PlaySound(m_iszPainSound, true); }; virtual void FearSound(void) { PlaySound(m_iszFearSound, false); }; virtual void LostEnemySound(void) { PlaySound(m_iszLostEnemySound, false); }; - virtual void FoundEnemySound(void) { PlaySound(m_iszFoundEnemySound, false); }; + virtual void FoundEnemySound(void); void Activate(); void FixupWeapon(); @@ -66,6 +77,8 @@ public: virtual void InputSetSpeedModifier(inputdata_t &inputdata); virtual void InputEnableOpenDoors(inputdata_t &inputdata); virtual void InputDisableOpenDoors(inputdata_t &inputdata); + virtual void InputEnablePickupWeapons(inputdata_t &inputdata); + virtual void InputDisablePickupWeapons(inputdata_t &inputdata); DECLARE_DATADESC(); @@ -88,8 +101,10 @@ private: 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_bCanPickupWeapons; // If true, this NPC is able to pick up weapons off of the ground just like npc_citizen. bool m_bWanderToggle; // Boolean to toggle wandering / standing every think cycle float m_flNextSoundTime; // Next time at which this NPC is allowed to play an NPC sound + float m_flNextFoundEnemySoundTime; // Next time at which this NPC is allowed to play an NPC sound float m_flSpeedModifier; // Modifier to apply to move distance }; @@ -113,16 +128,68 @@ BEGIN_DATADESC(CNPC_ShadowWalker) DEFINE_KEYFIELD(m_iszFoundEnemySound, FIELD_SOUNDNAME, "FoundEnemySound"), DEFINE_KEYFIELD(m_bUseBothSquadSlots, FIELD_BOOLEAN, "UseBothSquadSlots"), DEFINE_KEYFIELD(m_bCannotOpenDoors, FIELD_BOOLEAN, "CannotOpenDoors"), + DEFINE_KEYFIELD(m_bCanPickupWeapons, FIELD_BOOLEAN, "CanPickupWeapons"), DEFINE_FIELD(m_bWanderToggle, FIELD_BOOLEAN), DEFINE_FIELD(m_flNextSoundTime, FIELD_TIME), + DEFINE_FIELD(m_flNextFoundEnemySoundTime, FIELD_TIME), DEFINE_FIELD(m_flSpeedModifier, FIELD_TIME), DEFINE_INPUTFUNC(FIELD_FLOAT, "SetSpeedModifier", InputSetSpeedModifier), DEFINE_INPUTFUNC(FIELD_VOID, "EnableOpenDoors", InputEnableOpenDoors), - DEFINE_INPUTFUNC(FIELD_VOID, "DisableOpenDoors", InputDisableOpenDoors) + DEFINE_INPUTFUNC(FIELD_VOID, "DisableOpenDoors", InputDisableOpenDoors), + DEFINE_INPUTFUNC(FIELD_VOID, "EnablePickupWeapons", InputEnablePickupWeapons), + DEFINE_INPUTFUNC(FIELD_VOID, "DisablePickupWeapons", InputDisablePickupWeapons) END_DATADESC() +//========================================================= +// > Melee_Attack_NoInterrupt +//========================================================= +AI_DEFINE_SCHEDULE +( + SCHED_MELEE_ATTACK_NOINTERRUPT, + + " Tasks" + " TASK_STOP_MOVING 0" + " TASK_FACE_ENEMY 0" + " TASK_ANNOUNCE_ATTACK 1" // 1 = primary attack + " TASK_MELEE_ATTACK1 0" + "" + " Interrupts" + " COND_ENEMY_DEAD" + " COND_ENEMY_OCCLUDED" +); + +//========================================================= +// SCHED_HIDE +//========================================================= +AI_DEFINE_SCHEDULE +( + SCHED_HIDE, + + " Tasks" + " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_COMBAT_FACE" + " TASK_STOP_MOVING 0" + " TASK_FIND_COVER_FROM_ENEMY 0" + " TASK_RUN_PATH 0" + " TASK_WAIT_FOR_MOVEMENT 0" + " TASK_REMEMBER MEMORY:INCOVER" + " TASK_FACE_ENEMY 0" + "" + " Interrupts" + " COND_HEAR_DANGER" + " COND_NEW_ENEMY" + " COND_ENEMY_DEAD" +); + +//--------------------------------------------------------- +// Constants +//--------------------------------------------------------- +const float MIN_TIME_NEXT_SOUND = 0.5f; +const float MAX_TIME_NEXT_SOUND = 1.0f; +const float MIN_TIME_NEXT_FOUNDENEMY_SOUND = 2.0f; +const float MAX_TIME_NEXT_FOUNDENEMY_SOUND = 5.0f; + //----------------------------------------------------------------------------- // Purpose: Initialize the custom schedules // Input : @@ -206,6 +273,7 @@ void CNPC_ShadowWalker::Spawn( void ) m_flFieldOfView = 0.5; m_flNextSoundTime = gpGlobals->curtime; + m_flNextFoundEnemySoundTime = gpGlobals->curtime; m_NPCState = NPC_STATE_NONE; m_flSpeedModifier = 1.0f; @@ -256,9 +324,6 @@ void CNPC_ShadowWalker::FixupWeapon() } -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- - void CNPC_ShadowWalker::Activate() { BaseClass::Activate(); @@ -314,6 +379,20 @@ int CNPC_ShadowWalker::SelectScheduleRetrieveItem() return SCHED_NONE; } +//----------------------------------------------------------------------------- +// Purpose: Select a schedule to retrieve better weapons if they are available. +//----------------------------------------------------------------------------- +int CNPC_ShadowWalker::SelectScheduleWander() +{ + m_bWanderToggle = !m_bWanderToggle; + if (m_bWanderToggle) { + return SCHED_IDLE_WANDER; + } + else { + return SCHED_NONE; + } +} + //----------------------------------------------------------------------------- // Purpose: Select a schedule to execute based on conditions. // This is the most critical AI method. @@ -357,7 +436,7 @@ int CNPC_ShadowWalker::SelectIdleSchedule() return SCHED_INVESTIGATE_SOUND; } - if (CanPickkUpWeapons() && HasCondition(COND_BETTER_WEAPON_AVAILABLE)) { + if (CanPickkUpWeapons()) { nSched = SelectScheduleRetrieveItem(); if (nSched != SCHED_NONE) return nSched; @@ -365,13 +444,9 @@ int CNPC_ShadowWalker::SelectIdleSchedule() // no valid route! Wander instead if (GetNavigator()->GetGoalType() == GOALTYPE_NONE) { - m_bWanderToggle = !m_bWanderToggle; - if (m_bWanderToggle) { - return SCHED_IDLE_WANDER; - } - else { + nSched = SelectScheduleWander(); + if (nSched == SCHED_NONE) return SCHED_IDLE_STAND; - } } // valid route. Get moving @@ -403,7 +478,7 @@ int CNPC_ShadowWalker::SelectAlertSchedule() return SCHED_INVESTIGATE_SOUND; } - if (CanPickkUpWeapons() && HasCondition(COND_BETTER_WEAPON_AVAILABLE)) { + if (CanPickkUpWeapons()) { nSched = SelectScheduleRetrieveItem(); if (nSched != SCHED_NONE) return nSched; @@ -411,13 +486,9 @@ int CNPC_ShadowWalker::SelectAlertSchedule() // no valid route! Wander instead if (GetNavigator()->GetGoalType() == GOALTYPE_NONE) { - m_bWanderToggle = !m_bWanderToggle; - if (m_bWanderToggle) { - return SCHED_IDLE_WANDER; - } - else { - return SCHED_ALERT_STAND; - } + nSched = SelectScheduleWander(); + if (nSched == SCHED_NONE) + return SCHED_IDLE_STAND; } // valid route. Get moving @@ -443,6 +514,7 @@ int CNPC_ShadowWalker::SelectCombatSchedule() if (ChooseEnemy()) { FoundEnemySound(); + ClearCondition(COND_ENEMY_DEAD); return SelectSchedule(); } @@ -451,21 +523,32 @@ int CNPC_ShadowWalker::SelectCombatSchedule() return SelectSchedule(); } + // Can any enemies see me? + bool bEnemyCanSeeMe = HasCondition(COND_SEE_ENEMY) && HasCondition(COND_ENEMY_FACING_ME) && HasCondition(COND_HAVE_ENEMY_LOS); + + + // If I'm scared of this enemy and he's looking at me, run away + if ((IRelationType(GetEnemy()) == D_FR) && bEnemyCanSeeMe) + { + FearSound(); + return SCHED_RUN_FROM_ENEMY; + } + // If in a squad, only one or two shadow walkers can chase the player. This is configurable through Hammer. - bool bCanChase = false; - if (m_bUseBothSquadSlots) { + bool bCanChase = true; + if (bEnemyCanSeeMe && m_bUseBothSquadSlots) { bCanChase = OccupyStrategySlotRange(SQUAD_SLOT_ATTACK1, SQUAD_SLOT_ATTACK2); } - else { + else if (bEnemyCanSeeMe){ bCanChase = OccupyStrategySlot(SQUAD_SLOT_ATTACK1); } - // If I'm scared of this enemy and he's looking at me, run away - if ((IRelationType(GetEnemy()) == D_FR || !bCanChase) && (HasCondition(COND_SEE_ENEMY) && HasCondition(COND_ENEMY_FACING_ME) && HasCondition(COND_HAVE_ENEMY_LOS))) + // If I'm not allowed to chase this enemy of this enemy and he's looking at me, set up an ambush + if (!bCanChase) { - // TODO: Check if silent - FearSound(); - return SCHED_RUN_FROM_ENEMY; + FearSound(); + return SCHED_HIDE; + } // Reloading conditions are necessary just in case for some reason somebody gives the Shadow Walker a gun @@ -537,12 +620,30 @@ bool CNPC_ShadowWalker::HasRangedWeapon() return false; } +//----------------------------------------------------------------------------- +// Purpose: Override base class schedules +//----------------------------------------------------------------------------- +int CNPC_ShadowWalker::TranslateSchedule(int scheduleType) +{ + switch (scheduleType) + { + case SCHED_MELEE_ATTACK1: + return SCHED_MELEE_ATTACK_NOINTERRUPT; + case SCHED_IDLE_WANDER: // We want idle wandering to be interruptible - patrol walk is a better schedule + return SCHED_PATROL_WALK; + } + + return BaseClass::TranslateSchedule(scheduleType); +} + //----------------------------------------------------------------------------- // Purpose: Override base class activiites //----------------------------------------------------------------------------- Activity CNPC_ShadowWalker::NPC_TranslateActivity(Activity activity) { switch (activity) { + case ACT_IDLE_MELEE: + return ACT_IDLE; // If the walker has a melee weapon but is in an idle state, don't raise the weapon case ACT_RUN_AIM_SHOTGUN: return ACT_RUN_AIM_RIFLE; case ACT_WALK_AIM_SHOTGUN: @@ -556,14 +657,30 @@ Activity CNPC_ShadowWalker::NPC_TranslateActivity(Activity activity) } } +//----------------------------------------------------------------------------- +// Purpose: Play sound when an enemy is spotted. This sound has a separate +// timer from other sounds to prevent looping if the NPC gets caught +// in a 'found enemy' condition. +//----------------------------------------------------------------------------- +void CNPC_ShadowWalker::FoundEnemySound(void) +{ + if (gpGlobals->curtime > m_flNextFoundEnemySoundTime) + { + m_flNextFoundEnemySoundTime = gpGlobals->curtime + random->RandomFloat(MIN_TIME_NEXT_FOUNDENEMY_SOUND, MAX_TIME_NEXT_FOUNDENEMY_SOUND); + PlaySound(m_iszFoundEnemySound, true); + } +} + + //----------------------------------------------------------------------------- // Purpose: Play NPC soundscript //----------------------------------------------------------------------------- void CNPC_ShadowWalker::PlaySound(string_t soundname, bool required /*= false */) { + // TODO: Check if silent if (required || gpGlobals->curtime > m_flNextSoundTime) { - m_flNextSoundTime = gpGlobals->curtime + random->RandomFloat(0.5, 1.0); + m_flNextSoundTime = gpGlobals->curtime + random->RandomFloat(MIN_TIME_NEXT_SOUND, MAX_TIME_NEXT_SOUND); //CPASAttenuationFilter filter2(this, STRING(soundname)); EmitSound(STRING(soundname)); } @@ -629,6 +746,22 @@ void CNPC_ShadowWalker::InputDisableOpenDoors(inputdata_t &inputdata) } } +//----------------------------------------------------------------------------- +// Purpose: Hammer input to enable weapon pickup behavior +//----------------------------------------------------------------------------- +void CNPC_ShadowWalker::InputEnablePickupWeapons(inputdata_t &inputdata) +{ + m_bCanPickupWeapons = true; +} + +//----------------------------------------------------------------------------- +// Purpose: Hammer input to enable weapon pickup behavior +//----------------------------------------------------------------------------- +void CNPC_ShadowWalker::InputDisablePickupWeapons(inputdata_t &inputdata) +{ + m_bCanPickupWeapons = false; +} + //----------------------------------------------------------------------------- // Purpose: //