From 06a40e6dc02e8198ab7e1add41bdbee02a832119 Mon Sep 17 00:00:00 2001 From: 1upD <1upderek@gmail.com> Date: Thu, 27 Sep 2018 11:19:40 -0400 Subject: [PATCH 01/36] Created Shadow Walker NPC by copying experimental npc_collaborator code. WIP --- sp/src/game/server/mod/npc_shadow_walker.cpp | 273 +++++++++++++++++++ 1 file changed, 273 insertions(+) create mode 100644 sp/src/game/server/mod/npc_shadow_walker.cpp diff --git a/sp/src/game/server/mod/npc_shadow_walker.cpp b/sp/src/game/server/mod/npc_shadow_walker.cpp new file mode 100644 index 00000000..ba675e78 --- /dev/null +++ b/sp/src/game/server/mod/npc_shadow_walker.cpp @@ -0,0 +1,273 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// This is a skeleton file for use when creating a new +// NPC. Copy and rename this file for the new +// NPC and add the copy to the build. +// +// Leave this file in the build until we ship! Allowing +// this file to be rebuilt with the rest of the game ensures +// that it stays up to date with the rest of the NPC code. +// +// Replace occurances of CNPC_ShadowWalker with the new NPC's +// classname. Don't forget the lower-case occurance in +// LINK_ENTITY_TO_CLASS() +// +// +// ASSUMPTIONS MADE: +// +// You're making a character based on CAI_BaseNPC. If this +// is not true, make sure you replace all occurances +// of 'CAI_BaseNPC' in this file with the appropriate +// parent class. +// +// You're making a human-sized NPC that walks. +// +//=============================================================================// +#include "cbase.h" +#include "ai_default.h" +#include "ai_task.h" +#include "ai_schedule.h" +#include "ai_hull.h" +#include "soundent.h" +#include "game.h" +#include "npcevent.h" +#include "entitylist.h" +#include "activitylist.h" +#include "ai_basenpc.h" +#include "engine/IEngineSound.h" +#include "basehlcombatweapon_shared.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//========================================================= +// Private activities +//========================================================= +//int ACT_MYCUSTOMACTIVITY = -1; + +//========================================================= +// Custom schedules +//========================================================= +//enum +//{ +// SCHED_MYCUSTOMSCHEDULE = LAST_SHARED_SCHEDULE, +//}; + +//========================================================= +// Custom tasks +//========================================================= +//enum +//{ +// TASK_MYCUSTOMTASK = LAST_SHARED_TASK, +//}; + + +//========================================================= +// Custom Conditions +//========================================================= +//enum +//{ +// COND_MYCUSTOMCONDITION = LAST_SHARED_CONDITION, +//}; + + +//========================================================= +//========================================================= +class CNPC_ShadowWalker : public CAI_BaseNPC +{ + DECLARE_CLASS( CNPC_ShadowWalker, CAI_BaseNPC ); + +public: + void Precache( void ); + void Spawn( void ); + Class_T Classify( void ); + int SelectFailSchedule(int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode); + int SelectScheduleRetrieveItem(); + int SelectSchedule(); + + DECLARE_DATADESC(); + + // This is a dummy field. In order to provide save/restore + // code in this file, we must have at least one field + // for the code to operate on. Delete this field when + // you are ready to do your own save/restore for this + // character. + bool m_bHasWeapon; + + DEFINE_CUSTOM_AI; + +}; + + +LINK_ENTITY_TO_CLASS( npc_shadow_walker, CNPC_ShadowWalker ); +IMPLEMENT_CUSTOM_AI( npc_citizen,CNPC_ShadowWalker ); + + +//--------------------------------------------------------- +// Save/Restore +//--------------------------------------------------------- +BEGIN_DATADESC( CNPC_ShadowWalker ) + + DEFINE_FIELD(m_bHasWeapon, FIELD_BOOLEAN), + +END_DATADESC() + +//----------------------------------------------------------------------------- +// Purpose: Initialize the custom schedules +// Input : +// Output : +//----------------------------------------------------------------------------- +void CNPC_ShadowWalker::InitCustomSchedules(void) +{ + INIT_CUSTOM_AI(CNPC_ShadowWalker); + + //ADD_CUSTOM_TASK(CNPC_ShadowWalker, TASK_MYCUSTOMTASK); + + //ADD_CUSTOM_SCHEDULE(CNPC_ShadowWalker, SCHED_MYCUSTOMSCHEDULE); + + //ADD_CUSTOM_ACTIVITY(CNPC_ShadowWalker, ACT_MYCUSTOMACTIVITY); + + //ADD_CUSTOM_CONDITION(CNPC_ShadowWalker, COND_MYCUSTOMCONDITION); +} + +//----------------------------------------------------------------------------- +// Purpose: +// +// +//----------------------------------------------------------------------------- +void CNPC_ShadowWalker::Precache( void ) +{ + PrecacheModel( "models/monster/subject.mdl" ); // Replace this with setting from Hammer + + BaseClass::Precache(); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// +// +//----------------------------------------------------------------------------- +void CNPC_ShadowWalker::Spawn( void ) +{ + Precache(); + + SetModel( "models/monster/subject.mdl" ); // Replace this with setting from Hammer + SetHullType(HULL_HUMAN); + SetHullSizeNormal(); + + SetSolid( SOLID_BBOX ); + AddSolidFlags( FSOLID_NOT_STANDABLE ); + SetMoveType( MOVETYPE_STEP ); + SetBloodColor( BLOOD_COLOR_RED ); + m_iHealth = 50; // Replace this with setting from Hammer + m_flFieldOfView = 0.5; + m_NPCState = NPC_STATE_NONE; + + CapabilitiesClear(); + + if (!HasSpawnFlags(SF_NPC_START_EFFICIENT)) + { + // CapabilitiesAdd(bits_CAP_ANIMATEDFACE); + CapabilitiesAdd(bits_CAP_TURN_HEAD); + CapabilitiesAdd(bits_CAP_USE_WEAPONS | bits_CAP_AIM_GUN | bits_CAP_MOVE_SHOOT); + CapabilitiesAdd(bits_CAP_INNATE_MELEE_ATTACK1); + CapabilitiesAdd(bits_CAP_DUCK | bits_CAP_DOORS_GROUP); + CapabilitiesAdd(bits_CAP_USE_SHOT_REGULATOR); + } + + CapabilitiesAdd(bits_CAP_MOVE_GROUND); + SetMoveType(MOVETYPE_STEP); + + NPCInit(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Choose a schedule after schedule failed +// Copied from npc_citizen +//----------------------------------------------------------------------------- +int CNPC_ShadowWalker::SelectFailSchedule(int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode) +{ + switch (failedSchedule) + { + case SCHED_NEW_WEAPON: + // If failed trying to pick up a weapon, try again in one second. This is because other AI code + // has put this off for 10 seconds under the assumption that the citizen would be able to + // pick up the weapon that they found. + m_flNextWeaponSearchTime = gpGlobals->curtime + 1.0f; + break; + case SCHED_TAKE_COVER_FROM_ENEMY: + // I can't take cover, so I need to run away! + return SCHED_RUN_FROM_ENEMY; + } + + return BaseClass::SelectFailSchedule(failedSchedule, failedTask, taskFailCode); +} + +//----------------------------------------------------------------------------- +// Copied from npc_citizen +//----------------------------------------------------------------------------- +int CNPC_ShadowWalker::SelectScheduleRetrieveItem() +{ + if (HasCondition(COND_BETTER_WEAPON_AVAILABLE)) + { + CBaseHLCombatWeapon *pWeapon = dynamic_cast(Weapon_FindUsable(WEAPON_SEARCH_DELTA)); + if (pWeapon) + { + m_flNextWeaponSearchTime = gpGlobals->curtime + 10.0; + // Now lock the weapon for several seconds while we go to pick it up. + pWeapon->Lock(10.0, this); + SetTarget(pWeapon); + return SCHED_NEW_WEAPON; + } + } + + return SCHED_NONE; +} + +//----------------------------------------------------------------------------- +// Copied from npc_citizen - used as SelectSchedule instead of SelectPriorityAction +//----------------------------------------------------------------------------- +int CNPC_ShadowWalker::SelectSchedule() +{ + // Top priority - If there is a weapon available, grab it! + int schedule = SelectScheduleRetrieveItem(); + if (schedule != SCHED_NONE) + return schedule; + + // Can I see the enemy? + if (HasCondition(COND_SEE_ENEMY) && HasCondition(COND_ENEMY_FACING_ME)) + { + // Enemy can't see me + if (!HasCondition(COND_HAVE_ENEMY_LOS)) { + return SCHED_CHASE_ENEMY; + } + + return SCHED_RUN_FROM_ENEMY; + } + + schedule = BaseClass::SelectSchedule(); + if (schedule != SCHED_NONE) + return schedule; + + // I musn't run away! I musn't run away! + // If BaseNPC chooses to run from enemy, take cover instead + if (schedule == SCHED_RUN_FROM_ENEMY &&!HasCondition(COND_NO_WEAPON)) { + return SCHED_TAKE_COVER_FROM_ENEMY; + } + + return SCHED_IDLE_STAND; +} + + + +//----------------------------------------------------------------------------- +// Purpose: +// +// +// Output : +//----------------------------------------------------------------------------- +Class_T CNPC_ShadowWalker::Classify( void ) +{ + return CLASS_ZOMBIE; +} From bcaa781ad926baac0472ec36c7bf199920a91397 Mon Sep 17 00:00:00 2001 From: 1upD <1upderek@gmail.com> Date: Thu, 27 Sep 2018 12:02:59 -0400 Subject: [PATCH 02/36] Added idle and alert schedule selection to npc_shadow_walker --- sp/src/game/server/mod/npc_shadow_walker.cpp | 110 +++++++++++++++---- 1 file changed, 87 insertions(+), 23 deletions(-) diff --git a/sp/src/game/server/mod/npc_shadow_walker.cpp b/sp/src/game/server/mod/npc_shadow_walker.cpp index ba675e78..392d38c7 100644 --- a/sp/src/game/server/mod/npc_shadow_walker.cpp +++ b/sp/src/game/server/mod/npc_shadow_walker.cpp @@ -83,7 +83,9 @@ public: int SelectFailSchedule(int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode); int SelectScheduleRetrieveItem(); int SelectSchedule(); - + int SelectIdleSchedule(); + int SelectAlertSchedule(); + int SelectCombatSchedule(); DECLARE_DATADESC(); // This is a dummy field. In order to provide save/restore @@ -94,7 +96,6 @@ public: bool m_bHasWeapon; DEFINE_CUSTOM_AI; - }; @@ -230,36 +231,99 @@ int CNPC_ShadowWalker::SelectScheduleRetrieveItem() //----------------------------------------------------------------------------- int CNPC_ShadowWalker::SelectSchedule() { + int schedule; // Top priority - If there is a weapon available, grab it! - int schedule = SelectScheduleRetrieveItem(); - if (schedule != SCHED_NONE) - return schedule; + //schedule = SelectScheduleRetrieveItem(); + //if (schedule != SCHED_NONE) + // return schedule; // Can I see the enemy? - if (HasCondition(COND_SEE_ENEMY) && HasCondition(COND_ENEMY_FACING_ME)) + //if (HasCondition(COND_SEE_ENEMY) && HasCondition(COND_ENEMY_FACING_ME)) + //{ + // // Enemy can't see me + // if (!HasCondition(COND_HAVE_ENEMY_LOS)) { + // return SCHED_CHASE_ENEMY; + // } + // + // return SCHED_RUN_FROM_ENEMY; + //} + switch (m_NPCState) { - // Enemy can't see me - if (!HasCondition(COND_HAVE_ENEMY_LOS)) { - return SCHED_CHASE_ENEMY; - } + case NPC_STATE_IDLE: + AssertMsgOnce(GetEnemy() == NULL, "NPC has enemy but is not in combat state?"); + return SelectIdleSchedule(); - return SCHED_RUN_FROM_ENEMY; + 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(); } - - schedule = BaseClass::SelectSchedule(); - if (schedule != SCHED_NONE) - return schedule; - - // I musn't run away! I musn't run away! - // If BaseNPC chooses to run from enemy, take cover instead - if (schedule == SCHED_RUN_FROM_ENEMY &&!HasCondition(COND_NO_WEAPON)) { - return SCHED_TAKE_COVER_FROM_ENEMY; - } - - return SCHED_IDLE_STAND; } +//----------------------------------------------------------------------------- +// Idle schedule selection +//----------------------------------------------------------------------------- +int CNPC_ShadowWalker::SelectIdleSchedule() +{ + int nSched = SelectFlinchSchedule(); + if (nSched != SCHED_NONE) + return nSched; + if (HasCondition(COND_HEAR_DANGER) || + HasCondition(COND_HEAR_COMBAT) || + HasCondition(COND_HEAR_WORLD) || + HasCondition(COND_HEAR_BULLET_IMPACT) || + HasCondition(COND_HEAR_PLAYER)) + { + // Investigate sound source + return SCHED_INVESTIGATE_SOUND; + } + + // no valid route! Wander instead + if (GetNavigator()->GetGoalType() == GOALTYPE_NONE) + return SCHED_IDLE_WANDER; + + // valid route. Get moving + return SCHED_IDLE_WALK; +} + +//----------------------------------------------------------------------------- +// Alert schedule selection +// Copied from baseNPC +//----------------------------------------------------------------------------- +int CNPC_ShadowWalker::SelectAlertSchedule() +{ + // Per default base NPC, check flinch schedule first + int nSched = SelectFlinchSchedule(); + if (nSched != SCHED_NONE) + return nSched; + + // Scan around for new enemies + if (HasCondition(COND_ENEMY_DEAD) && SelectWeightedSequence(ACT_VICTORY_DANCE) != ACTIVITY_NOT_AVAILABLE) + return SCHED_ALERT_SCAN; + + if (HasCondition(COND_HEAR_DANGER) || + HasCondition(COND_HEAR_PLAYER) || + HasCondition(COND_HEAR_WORLD) || + HasCondition(COND_HEAR_BULLET_IMPACT) || + HasCondition(COND_HEAR_COMBAT)) + { + // Investigate sound source + return SCHED_INVESTIGATE_SOUND; + } + + // no valid route! Wander instead + if (GetNavigator()->GetGoalType() == GOALTYPE_NONE) + return SCHED_IDLE_WANDER; + + // valid route. Get moving + return SCHED_ALERT_WALK; +} //----------------------------------------------------------------------------- // Purpose: From 0b42a245d1b46e1979c4af637c50227f97a2673c Mon Sep 17 00:00:00 2001 From: 1upD <1upderek@gmail.com> Date: Thu, 27 Sep 2018 14:13:32 -0400 Subject: [PATCH 03/36] Shadow Walker: WIP combat function --- sp/src/game/server/mod/npc_shadow_walker.cpp | 108 +++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/sp/src/game/server/mod/npc_shadow_walker.cpp b/sp/src/game/server/mod/npc_shadow_walker.cpp index 392d38c7..60367d2e 100644 --- a/sp/src/game/server/mod/npc_shadow_walker.cpp +++ b/sp/src/game/server/mod/npc_shadow_walker.cpp @@ -35,6 +35,7 @@ #include "ai_basenpc.h" #include "engine/IEngineSound.h" #include "basehlcombatweapon_shared.h" +#include "ai_squadslot.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -96,6 +97,9 @@ public: bool m_bHasWeapon; DEFINE_CUSTOM_AI; + +private: + bool HasRangedWeapon(); }; @@ -325,6 +329,110 @@ int CNPC_ShadowWalker::SelectAlertSchedule() return SCHED_ALERT_WALK; } +//----------------------------------------------------------------------------- +// Combat schedule selection +//----------------------------------------------------------------------------- +int CNPC_ShadowWalker::SelectCombatSchedule() +{ + // Check flinch first + int nSched = SelectFlinchSchedule(); + if (nSched != SCHED_NONE) + return nSched; + + // Check enemy death + if (HasCondition(COND_ENEMY_DEAD)) + { + // clear the current (dead) enemy and try to find another. + SetEnemy(NULL); + + if (ChooseEnemy()) + { + ClearCondition(COND_ENEMY_DEAD); + return SelectSchedule(); + } + + SetState(NPC_STATE_ALERT); + return SelectSchedule(); + } + + // If I'm scared of this enemy and he's looking at me, run away + if (IRelationType(GetEnemy()) == D_FR) + { + if (HasCondition(COND_SEE_ENEMY) && HasCondition(COND_ENEMY_FACING_ME)) + { + // TODO: Check if silent + FearSound(); + return SCHED_RUN_FROM_ENEMY; + } + } + + // Reloading conditions are necessary just in case for some reason somebody gives the Shadow Walker a gun + if (HasCondition(COND_LOW_PRIMARY_AMMO) || HasCondition(COND_NO_PRIMARY_AMMO)) + { + return SCHED_HIDE_AND_RELOAD; + } + + // Can we see the enemy? + if (!HasCondition(COND_SEE_ENEMY)) + { + // chase! + return SCHED_CHASE_ENEMY; + } + + if (HasCondition(COND_TOO_CLOSE_TO_ATTACK)) + return SCHED_BACK_AWAY_FROM_ENEMY; + + + // we can see the enemy + if (HasCondition(COND_CAN_MELEE_ATTACK1)) + return SCHED_MELEE_ATTACK1; + + if (HasCondition(COND_CAN_MELEE_ATTACK2)) + return SCHED_MELEE_ATTACK2; + + if (HasRangedWeapon() && GetShotRegulator()->IsInRestInterval()) + { + if (HasCondition(COND_CAN_RANGE_ATTACK1)) + return SCHED_COMBAT_FACE; + } + + if (HasRangedWeapon() && HasCondition(COND_CAN_RANGE_ATTACK1)) + { + if (OccupyStrategySlotRange(SQUAD_SLOT_ATTACK1, SQUAD_SLOT_ATTACK2)) + return SCHED_RANGE_ATTACK1; + return SCHED_RUN_FROM_ENEMY; + } + + if (HasRangedWeapon() && HasCondition(COND_CAN_RANGE_ATTACK2)) + return SCHED_RANGE_ATTACK2; + + + if (HasCondition(COND_NOT_FACING_ATTACK)) + return SCHED_COMBAT_FACE; + + if (!HasCondition(COND_CAN_RANGE_ATTACK1) && !HasCondition(COND_CAN_MELEE_ATTACK1)) + { + // if we can see enemy but can't use either attack type, we must need to get closer to enemy + if (HasRangedWeapon()) + return SCHED_MOVE_TO_WEAPON_RANGE; + + return SCHED_CHASE_ENEMY; + } + + DevWarning(2, "No suitable combat schedule!\n"); + return SCHED_FAIL; +} + +bool CNPC_ShadowWalker::HasRangedWeapon() +{ + CBaseCombatWeapon *pWeapon = GetActiveWeapon(); + + if (pWeapon) + return !(FClassnameIs(pWeapon, "weapon_crowbar") || FClassnameIs(pWeapon, "weapon_stunstick")); + + return false; +} + //----------------------------------------------------------------------------- // Purpose: // From 86bb8f7c7e6564fa6ab573e7907c8790b2450ef4 Mon Sep 17 00:00:00 2001 From: 1upD <1upderek@gmail.com> Date: Thu, 27 Sep 2018 14:29:34 -0400 Subject: [PATCH 04/36] Shadow Walker: Added experimental squad behavior --- sp/src/game/server/mod/npc_shadow_walker.cpp | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/sp/src/game/server/mod/npc_shadow_walker.cpp b/sp/src/game/server/mod/npc_shadow_walker.cpp index 60367d2e..ce2d08ea 100644 --- a/sp/src/game/server/mod/npc_shadow_walker.cpp +++ b/sp/src/game/server/mod/npc_shadow_walker.cpp @@ -356,9 +356,10 @@ int CNPC_ShadowWalker::SelectCombatSchedule() } // If I'm scared of this enemy and he's looking at me, run away - if (IRelationType(GetEnemy()) == D_FR) + // If in a squad, all but one Shadow walker must be afraid of the player + if (IRelationType(GetEnemy()) == D_FR || !OccupyStrategySlotRange(SQUAD_SLOT_ATTACK1)) { - if (HasCondition(COND_SEE_ENEMY) && HasCondition(COND_ENEMY_FACING_ME)) + if (HasCondition(COND_SEE_ENEMY) && HasCondition(COND_ENEMY_FACING_ME) && HasCondition(COND_HAVE_ENEMY_LOS)) { // TODO: Check if silent FearSound(); @@ -384,11 +385,17 @@ int CNPC_ShadowWalker::SelectCombatSchedule() // we can see the enemy - if (HasCondition(COND_CAN_MELEE_ATTACK1)) - return SCHED_MELEE_ATTACK1; + if (HasCondition(COND_CAN_MELEE_ATTACK1)) { + //if (OccupyStrategySlotRange(SQUAD_SLOT_ATTACK1, SQUAD_SLOT_ATTACK2)) + return SCHED_MELEE_ATTACK1; + //return SCHED_RUN_FROM_ENEMY; + } - if (HasCondition(COND_CAN_MELEE_ATTACK2)) - return SCHED_MELEE_ATTACK2; + if (HasCondition(COND_CAN_MELEE_ATTACK2)) { + //if (OccupyStrategySlotRange(SQUAD_SLOT_ATTACK1, SQUAD_SLOT_ATTACK2)) + return SCHED_MELEE_ATTACK2; + //return SCHED_RUN_FROM_ENEMY; + } if (HasRangedWeapon() && GetShotRegulator()->IsInRestInterval()) { From 4e56c1205bc27ce7e7011dae4ca9bb4f7f7b140d Mon Sep 17 00:00:00 2001 From: 1upD <1upderek@gmail.com> Date: Thu, 27 Sep 2018 17:05:14 -0400 Subject: [PATCH 05/36] Lost Soul: Added new NPC based on manhack. Inspired by Doom --- sp/src/game/server/mod/npc_lost_soul.cpp | 381 +++++++++++++++++++ sp/src/game/server/mod/npc_shadow_walker.cpp | 28 +- 2 files changed, 390 insertions(+), 19 deletions(-) create mode 100644 sp/src/game/server/mod/npc_lost_soul.cpp diff --git a/sp/src/game/server/mod/npc_lost_soul.cpp b/sp/src/game/server/mod/npc_lost_soul.cpp new file mode 100644 index 00000000..00b63452 --- /dev/null +++ b/sp/src/game/server/mod/npc_lost_soul.cpp @@ -0,0 +1,381 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// This is a skeleton file for use when creating a new +// NPC. Copy and rename this file for the new +// NPC and add the copy to the build. +// +// Leave this file in the build until we ship! Allowing +// this file to be rebuilt with the rest of the game ensures +// that it stays up to date with the rest of the NPC code. +// +// Replace occurances of CNPC_ShadowWalker with the new NPC's +// classname. Don't forget the lower-case occurance in +// LINK_ENTITY_TO_CLASS() +// +// +// ASSUMPTIONS MADE: +// +// You're making a character based on CAI_BaseNPC. If this +// is not true, make sure you replace all occurances +// of 'CAI_BaseNPC' in this file with the appropriate +// parent class. +// +// You're making a human-sized NPC that walks. +// +//=============================================================================// +#include "cbase.h" +#include "soundenvelope.h" +#include "npc_manhack.h" +#include "ai_default.h" +#include "ai_node.h" +#include "ai_navigator.h" +#include "ai_pathfinder.h" +#include "ai_moveprobe.h" +#include "ai_memory.h" +#include "ai_squad.h" +#include "ai_route.h" +#include "explode.h" +#include "basegrenade_shared.h" +#include "ndebugoverlay.h" +#include "decals.h" +#include "gib.h" +#include "game.h" +#include "ai_interactions.h" +#include "IEffects.h" +#include "vstdlib/random.h" +#include "engine/IEngineSound.h" +#include "movevars_shared.h" +#include "npcevent.h" +#include "props.h" +#include "te_effect_dispatch.h" +#include "ai_squadslot.h" +#include "world.h" +#include "smoke_trail.h" +#include "func_break.h" +#include "physics_impact_damage.h" +#include "weapon_physcannon.h" +#include "physics_prop_ragdoll.h" +#include "soundent.h" +#include "ammodef.h" +#include "EntityFlame.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//========================================================= +// Private activities +//========================================================= +//int ACT_MYCUSTOMACTIVITY = -1; + +//========================================================= +// Custom schedules +//========================================================= +//enum +//{ +// SCHED_MYCUSTOMSCHEDULE = LAST_SHARED_SCHEDULE, +//}; + +//========================================================= +// Custom tasks +//========================================================= +//enum +//{ +// TASK_MYCUSTOMTASK = LAST_SHARED_TASK, +//}; + + +//========================================================= +// Custom Conditions +//========================================================= +//enum +//{ +// COND_MYCUSTOMCONDITION = LAST_SHARED_CONDITION, +//}; + + +ConVar sk_lostsoul_health("sk_lostsoul_health", "0"); +ConVar sk_lostsoul_melee_dmg("sk_lostsoul_melee_dmg", "0"); + +#define MANHACK_NOISEMOD_HIDE 5000 +//========================================================= +//========================================================= +class CNPC_LostSoul : public CNPC_Manhack +{ + DECLARE_CLASS( CNPC_LostSoul, CNPC_Manhack); + +public: + void Precache( void ); + void Spawn( void ); + Class_T Classify( void ); + + virtual void DeathSound(const CTakeDamageInfo &info); + virtual bool ShouldGib(const CTakeDamageInfo &info); + + void BladesInit(); + void SoundInit(void); + void PlayFlySound(void); + void StartEye(void); + + void Slice(CBaseEntity *pHitEntity, float flInterval, trace_t &tr); + //void Bump(CBaseEntity *pHitEntity, float flInterval, trace_t &tr); + //void Splash(const Vector &vecSplashPos); + + virtual int OnTakeDamage_Alive(const CTakeDamageInfo &info); + + void Ignite(float flFlameLifetime, bool bNPCOnly, float flSize, bool bCalledByLevelDesigner) { return; } + + DECLARE_DATADESC(); + + + DEFINE_CUSTOM_AI; +}; + + +LINK_ENTITY_TO_CLASS( npc_lost_soul, CNPC_LostSoul ); +IMPLEMENT_CUSTOM_AI( npc_manhack,CNPC_LostSoul ); + + +//--------------------------------------------------------- +// Save/Restore +//--------------------------------------------------------- +BEGIN_DATADESC( CNPC_LostSoul ) + + //DEFINE_FIELD(m_bHasWeapon, FIELD_BOOLEAN), + +END_DATADESC() + +//----------------------------------------------------------------------------- +// Purpose: Initialize the custom schedules +// Input : +// Output : +//----------------------------------------------------------------------------- +void CNPC_LostSoul::InitCustomSchedules(void) +{ + INIT_CUSTOM_AI(CNPC_LostSoul); +} + +//----------------------------------------------------------------------------- +// Purpose: +// +// +//----------------------------------------------------------------------------- +void CNPC_LostSoul::Precache( void ) +{ + PrecacheModel( "models/skeleton/skeleton_torso3.mdl" ); // Replace this with setting from Hammer + + BaseClass::Precache(); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// +// +//----------------------------------------------------------------------------- +void CNPC_LostSoul::Spawn( void ) +{ + BaseClass::Spawn(); + + Precache(); + + SetModel("models/skeleton/skeleton_torso3.mdl"); + SetHullType(HULL_HUMAN); // I guess? + SetHullSizeNormal(); + + SetSolid(SOLID_BBOX); + AddSolidFlags(FSOLID_NOT_STANDABLE); + + if (HasSpawnFlags(SF_MANHACK_CARRIED)) + { + AddSolidFlags(FSOLID_NOT_SOLID); + SetMoveType(MOVETYPE_NONE); + } + else + { + SetMoveType(MOVETYPE_VPHYSICS); + } + + m_iHealth = sk_lostsoul_health.GetFloat(); + SetViewOffset(Vector(0, 0, 10)); // Position of the eyes relative to NPC's origin. + m_flFieldOfView = VIEW_FIELD_FULL; + m_NPCState = NPC_STATE_NONE; + + if (m_spawnflags & SF_MANHACK_USE_AIR_NODES) + { + SetNavType(NAV_FLY); + } + else + { + SetNavType(NAV_GROUND); + } + + AddEFlags(EFL_NO_DISSOLVE | EFL_NO_MEGAPHYSCANNON_RAGDOLL); + AddEffects(EF_NOSHADOW); + + SetBloodColor(BLOOD_COLOR_RED); + SetCurrentVelocity(vec3_origin); + + CapabilitiesAdd(bits_CAP_INNATE_MELEE_ATTACK1 | bits_CAP_MOVE_FLY | bits_CAP_SQUAD); + + // Set the noise mod to huge numbers right now, in case this manhack starts out waiting for a script + // for instance, we don't want him to bob whilst he's waiting for a script. This allows designers + // to 'hide' manhacks in small places. (sjb) + SetNoiseMod(MANHACK_NOISEMOD_HIDE, MANHACK_NOISEMOD_HIDE, MANHACK_NOISEMOD_HIDE); + + m_fHeadYaw = 0; + + NPCInit(); + + // Manhacks are designed to slam into things, so don't take much damage from it! + SetImpactEnergyScale(0.001); + + // Manhacks get 30 seconds worth of free knowledge. + GetEnemies()->SetFreeKnowledgeDuration(30.0); + + // don't be an NPC, we want to collide with debris stuff + SetCollisionGroup(COLLISION_GROUP_NONE); + + CEntityFlame *pFlame = CEntityFlame::Create(this); + if (pFlame) + { + pFlame->SetLifetime(HUGE_VAL); + //AddFlag(FL_ONFIRE); + SetEffectEntity(pFlame); + pFlame->SetSize(8); + pFlame->SetDamage(0.0f); + } +} + +int CNPC_LostSoul::OnTakeDamage_Alive(const CTakeDamageInfo &info) +{ + // Don't take burning damage! + if (info.GetDamageType() & 8) { + return 0; + } + + return BaseClass::OnTakeDamage_Alive(info); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CNPC_LostSoul::DeathSound(const CTakeDamageInfo &info) +{ + StopSound("NPC_Lost_Soul.Stunned"); + CPASAttenuationFilter filter2(this, "NPC_Lost_Soul.Die"); + EmitSound(filter2, entindex(), "NPC_Lost_Soul.Die"); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CNPC_LostSoul::ShouldGib(const CTakeDamageInfo &info) +{ + // TODO: Add gibs to lost soul + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Overloads Manhack method. Lost Souls have no blades +//----------------------------------------------------------------------------- +void CNPC_LostSoul::BladesInit() +{ + SetActivity(ACT_FLY); +} + +//----------------------------------------------------------------------------- +// Purpose: Overloads manhack engine sound. +//----------------------------------------------------------------------------- +void CNPC_LostSoul::SoundInit(void) +{ + // Just don't even worry about it. +} + +void CNPC_LostSoul::PlayFlySound(void) +{ + // Shhh, don't do anything. +} + +void CNPC_LostSoul::StartEye(void) +{ + // No eyes +} + +//----------------------------------------------------------------------------- +// Purpose: We've touched something that we can hurt. Slice it! +// Input : +// Output : +//----------------------------------------------------------------------------- +#define MANHACK_SMASH_TIME 0.35 // How long after being thrown from a physcannon that a manhack is eligible to die from impact +void CNPC_LostSoul::Slice(CBaseEntity *pHitEntity, float flInterval, trace_t &tr) +{ + // Don't hurt the player if I'm in water + if (GetWaterLevel() > 0 && pHitEntity->IsPlayer()) + return; + + if (pHitEntity->m_takedamage == DAMAGE_NO) + return; + + // Damage must be scaled by flInterval so framerate independent + float flDamage = sk_lostsoul_melee_dmg.GetFloat() * flInterval; + + if (pHitEntity->IsPlayer()) + { + flDamage *= 1.0f; + } + + if (dynamic_cast(pHitEntity)) { + dynamic_cast(pHitEntity)->Ignite(10.0f, false); + } + + else if (pHitEntity->IsNPC()) + { + dynamic_cast(pHitEntity)->Ignite(2.0f); + } + + if (flDamage < 1.0f) + { + flDamage = 1.0f; + } + + CTakeDamageInfo info(this, this, flDamage, DMG_SLASH); + + // check for actual "ownership" of damage + CBasePlayer *pPlayer = HasPhysicsAttacker(MANHACK_SMASH_TIME); + if (pPlayer) + { + info.SetAttacker(pPlayer); + } + + Vector dir = (tr.endpos - tr.startpos); + if (dir == vec3_origin) + { + dir = tr.m_pEnt->GetAbsOrigin() - GetAbsOrigin(); + } + CalculateMeleeDamageForce(&info, dir, tr.endpos); + pHitEntity->TakeDamage(info); + + // Play burning sound + EmitSound("NPC_LostSoul.Burn"); + + // Pop back a little bit after hitting the player + // ComputeSliceBounceVelocity(pHitEntity, tr); + + // Save off when we last hit something + m_flLastDamageTime = gpGlobals->curtime; + + // Reset our state and give the player time to react + // StopBurst(true); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// +// +// Output : +//----------------------------------------------------------------------------- +Class_T CNPC_LostSoul::Classify( void ) +{ + return CLASS_HEADCRAB; +} diff --git a/sp/src/game/server/mod/npc_shadow_walker.cpp b/sp/src/game/server/mod/npc_shadow_walker.cpp index ce2d08ea..eecb0a9e 100644 --- a/sp/src/game/server/mod/npc_shadow_walker.cpp +++ b/sp/src/game/server/mod/npc_shadow_walker.cpp @@ -204,6 +204,12 @@ int CNPC_ShadowWalker::SelectFailSchedule(int failedSchedule, int failedTask, AI case SCHED_TAKE_COVER_FROM_ENEMY: // I can't take cover, so I need to run away! return SCHED_RUN_FROM_ENEMY; + case SCHED_CHASE_ENEMY: + // I can't run away, so I will just run randomly! + return SCHED_CHASE_ENEMY_FAILED; + case SCHED_RUN_FROM_ENEMY: + // I can't run away, so I will just run randomly! + return SCHED_RUN_RANDOM; } return BaseClass::SelectFailSchedule(failedSchedule, failedTask, taskFailCode); @@ -235,22 +241,6 @@ int CNPC_ShadowWalker::SelectScheduleRetrieveItem() //----------------------------------------------------------------------------- int CNPC_ShadowWalker::SelectSchedule() { - int schedule; - // Top priority - If there is a weapon available, grab it! - //schedule = SelectScheduleRetrieveItem(); - //if (schedule != SCHED_NONE) - // return schedule; - - // Can I see the enemy? - //if (HasCondition(COND_SEE_ENEMY) && HasCondition(COND_ENEMY_FACING_ME)) - //{ - // // Enemy can't see me - // if (!HasCondition(COND_HAVE_ENEMY_LOS)) { - // return SCHED_CHASE_ENEMY; - // } - // - // return SCHED_RUN_FROM_ENEMY; - //} switch (m_NPCState) { case NPC_STATE_IDLE: @@ -261,8 +251,8 @@ int CNPC_ShadowWalker::SelectSchedule() AssertMsgOnce(GetEnemy() == NULL, "NPC has enemy but is not in combat state?"); return SelectAlertSchedule(); - //case NPC_STATE_COMBAT: - //return SelectCombatSchedule(); + case NPC_STATE_COMBAT: + return SelectCombatSchedule(); default: return BaseClass::SelectSchedule(); @@ -357,7 +347,7 @@ int CNPC_ShadowWalker::SelectCombatSchedule() // If I'm scared of this enemy and he's looking at me, run away // If in a squad, all but one Shadow walker must be afraid of the player - if (IRelationType(GetEnemy()) == D_FR || !OccupyStrategySlotRange(SQUAD_SLOT_ATTACK1)) + if (IRelationType(GetEnemy()) == D_FR || !OccupyStrategySlotRange(SQUAD_SLOT_ATTACK1, SQUAD_SLOT_ATTACK2)) { if (HasCondition(COND_SEE_ENEMY) && HasCondition(COND_ENEMY_FACING_ME) && HasCondition(COND_HAVE_ENEMY_LOS)) { From 544e3606407b4eae5637deb86585a0680a5a2d2e Mon Sep 17 00:00:00 2001 From: 1upD <1upderek@gmail.com> Date: Thu, 27 Sep 2018 21:38:54 -0400 Subject: [PATCH 06/36] Lost Soul: Removed all manhack sounds except for blade open / close sound. Had to make some manhack methods virtual. --- sp/src/game/server/hl2/npc_manhack.h | 12 +- sp/src/game/server/mod/npc_lost_soul.cpp | 151 +++++++++++++++++++++++ 2 files changed, 157 insertions(+), 6 deletions(-) diff --git a/sp/src/game/server/hl2/npc_manhack.h b/sp/src/game/server/hl2/npc_manhack.h index 17a3cedb..02d05962 100644 --- a/sp/src/game/server/hl2/npc_manhack.h +++ b/sp/src/game/server/hl2/npc_manhack.h @@ -102,9 +102,9 @@ public: virtual float GetHeadTurnRate( void ) { return 45.0f; } // Degrees per second - void CheckCollisions(float flInterval); + virtual void CheckCollisions(float flInterval); virtual void GatherEnemyConditions( CBaseEntity *pEnemy ); - void PlayFlySound(void); + virtual void PlayFlySound(void); virtual void StopLoopingSounds(void); void Precache(void); @@ -113,9 +113,9 @@ public: void Activate(); void StartTask( const Task_t *pTask ); - void BladesInit(); - void SoundInit( void ); - void StartEye( void ); + virtual void BladesInit(); + virtual void SoundInit( void ); + virtual void StartEye( void ); bool HandleInteraction(int interactionType, void* data, CBaseCombatCharacter* sourceEnt); @@ -126,7 +126,7 @@ public: void SpinBlades(float flInterval); - void Slice( CBaseEntity *pHitEntity, float flInterval, trace_t &tr ); + virtual void Slice( CBaseEntity *pHitEntity, float flInterval, trace_t &tr ); void Bump( CBaseEntity *pHitEntity, float flInterval, trace_t &tr ); void Splash( const Vector &vecSplashPos ); diff --git a/sp/src/game/server/mod/npc_lost_soul.cpp b/sp/src/game/server/mod/npc_lost_soul.cpp index 00b63452..752cf66c 100644 --- a/sp/src/game/server/mod/npc_lost_soul.cpp +++ b/sp/src/game/server/mod/npc_lost_soul.cpp @@ -115,6 +115,9 @@ public: void PlayFlySound(void); void StartEye(void); + + void CheckCollisions(float flInterval); + void Slice(CBaseEntity *pHitEntity, float flInterval, trace_t &tr); //void Bump(CBaseEntity *pHitEntity, float flInterval, trace_t &tr); //void Splash(const Vector &vecSplashPos); @@ -123,6 +126,19 @@ public: void Ignite(float flFlameLifetime, bool bNPCOnly, float flSize, bool bCalledByLevelDesigner) { return; } + // INPCInteractive Functions + virtual bool CanInteractWith(CAI_BaseNPC *pUser) { return false; } // Disabled for now (sjb) + virtual bool HasBeenInteractedWith() { return false; } + virtual void NotifyInteraction(CAI_BaseNPC *pUser){} + + virtual void InputPowerdown(inputdata_t &inputdata) + { + m_iHealth = 0; + } + + void MoveToTarget(float flInterval, const Vector &MoveTarget); + + DECLARE_DATADESC(); @@ -368,6 +384,141 @@ void CNPC_LostSoul::Slice(CBaseEntity *pHitEntity, float flInterval, trace_t &tr // StopBurst(true); } +//----------------------------------------------------------------------------- +// Purpose: +// Input : +// Output : +//----------------------------------------------------------------------------- +void CNPC_LostSoul::MoveToTarget(float flInterval, const Vector &vMoveTarget) +{ + if (flInterval <= 0) + { + return; + } + + // ----------------------------------------- + // Don't steer if engine's have stalled + // ----------------------------------------- + if (m_iHealth <= 0) + return; + + if (GetEnemy() != NULL) + { + TurnHeadToTarget(flInterval, GetEnemy()->EyePosition()); + } + else + { + TurnHeadToTarget(flInterval, vMoveTarget); + } + + // ------------------------------------- + // Move towards our target + // ------------------------------------- + float myAccel; + float myZAccel = 300.0f; + float myDecay = 0.3f; + + Vector targetDir; + float flDist; + + + Vector vecCurrentDir = GetCurrentVelocity(); + VectorNormalize(vecCurrentDir); + + targetDir = vMoveTarget - GetAbsOrigin(); + flDist = VectorNormalize(targetDir); + + float flDot = DotProduct(targetDir, vecCurrentDir); + + // Otherwise we should steer towards our goal + if (flDot > 0.25) + { + // If my target is in front of me, my flight model is a bit more accurate. + myAccel = 300; + } + else + { + // Have a harder time correcting my course if I'm currently flying away from my target. + myAccel = 200; + } + + // Clamp lateral acceleration + if (myAccel > (flDist / flInterval)) + { + myAccel = flDist / flInterval; + } + + /* + // Boost vertical movement + if ( targetDir.z > 0 ) + { + // Z acceleration is faster when we thrust upwards. + // This is to help keep manhacks out of water. + myZAccel *= 5.0; + } + */ + + // Clamp vertical movement + if (myZAccel > flDist / flInterval) + { + myZAccel = flDist / flInterval; + } + + // Scale by our engine force + //myAccel *= m_fEnginePowerScale; + //myZAccel *= m_fEnginePowerScale; + + MoveInDirection(flInterval, targetDir, myAccel, myZAccel, myDecay); + + // calc relative banking targets + Vector forward, right; + GetVectors(&forward, &right, NULL); + //m_vTargetBanking.x = 40 * DotProduct(forward, targetDir); + //m_vTargetBanking.z = 40 * DotProduct(right, targetDir); + //m_vTargetBanking.y = 0.0; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : +// Output : +//----------------------------------------------------------------------------- +void CNPC_LostSoul::CheckCollisions(float flInterval) +{ + // Trace forward to see if I hit anything. But trace forward along the + // owner's view direction if you're being carried. + Vector vecTraceDir, vecCheckPos; + VPhysicsGetObject()->GetVelocity(&vecTraceDir, NULL); + vecTraceDir *= flInterval; + + VectorAdd(GetAbsOrigin(), vecTraceDir, vecCheckPos); + + trace_t tr; + CBaseEntity* pHitEntity = NULL; + + AI_TraceHull(GetAbsOrigin(), + vecCheckPos, + GetHullMins(), + GetHullMaxs(), + MoveCollisionMask(), + this, + COLLISION_GROUP_NONE, + &tr); + + if ((tr.fraction != 1.0 || tr.startsolid) && tr.m_pEnt) + { + PhysicsMarkEntitiesAsTouching(tr.m_pEnt, tr); + pHitEntity = tr.m_pEnt; + + if (pHitEntity != NULL && + pHitEntity->m_takedamage == DAMAGE_YES && + pHitEntity->Classify() != CLASS_MANHACK) + { + // Slice this thing + Slice(pHitEntity, flInterval, tr); + } + } +} //----------------------------------------------------------------------------- // Purpose: From ca02f50e58c485d61940fbac7667b4e6a4b94635 Mon Sep 17 00:00:00 2001 From: 1upD <1upderek@gmail.com> Date: Sun, 30 Sep 2018 23:54:08 -0400 Subject: [PATCH 07/36] Shadow Walker: Fixed up melee attack (sort of), squad behavior, made model Hammer configurable --- sp/src/game/server/mod/npc_shadow_walker.cpp | 259 ++++++++++++++++++- 1 file changed, 246 insertions(+), 13 deletions(-) diff --git a/sp/src/game/server/mod/npc_shadow_walker.cpp b/sp/src/game/server/mod/npc_shadow_walker.cpp index eecb0a9e..0d1e6a8c 100644 --- a/sp/src/game/server/mod/npc_shadow_walker.cpp +++ b/sp/src/game/server/mod/npc_shadow_walker.cpp @@ -36,6 +36,8 @@ #include "engine/IEngineSound.h" #include "basehlcombatweapon_shared.h" #include "ai_squadslot.h" +//#include "npc_BaseZombie.h" +//#include "vehicle_base.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -70,6 +72,11 @@ // COND_MYCUSTOMCONDITION = LAST_SHARED_CONDITION, //}; +//========================================================= +// Convars +//========================================================= + +ConVar sk_shadow_walker_dmg_innate_melee("sk_zombie_dmg_one_slash", "0"); //========================================================= //========================================================= @@ -81,12 +88,22 @@ public: void Precache( void ); void Spawn( void ); Class_T Classify( void ); - int SelectFailSchedule(int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode); - int SelectScheduleRetrieveItem(); - int SelectSchedule(); - int SelectIdleSchedule(); - int SelectAlertSchedule(); - int SelectCombatSchedule(); + virtual int SelectFailSchedule(int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode); + virtual int SelectScheduleRetrieveItem(); + virtual int SelectSchedule(); + virtual int SelectIdleSchedule(); + virtual int SelectAlertSchedule(); + virtual int SelectCombatSchedule(); + virtual bool CanPickkUpWeapons() { return true; } + + // Citizen methods + Activity NPC_TranslateActivity(Activity eNewActivity); + + // Zombie methods + //void HandleAnimEvent(animevent_t *pEvent); + //virtual CBaseEntity *ClawAttack(float flDist, int iDamage, QAngle &qaViewPunch, Vector &vecVelocityPunch, int BloodOrigin); + //virtual float GetClawAttackRange() const { return ZOMBIE_MELEE_REACH; } + DECLARE_DATADESC(); // This is a dummy field. In order to provide save/restore @@ -141,8 +158,12 @@ void CNPC_ShadowWalker::InitCustomSchedules(void) //----------------------------------------------------------------------------- void CNPC_ShadowWalker::Precache( void ) { - PrecacheModel( "models/monster/subject.mdl" ); // Replace this with setting from Hammer + if (!GetModelName()) + { + SetModelName(MAKE_STRING("models/monster/subject.mdl")); + } + PrecacheModel(STRING(GetModelName())); BaseClass::Precache(); } @@ -156,7 +177,7 @@ void CNPC_ShadowWalker::Spawn( void ) { Precache(); - SetModel( "models/monster/subject.mdl" ); // Replace this with setting from Hammer + SetModel(STRING(GetModelName())); SetHullType(HULL_HUMAN); SetHullSizeNormal(); @@ -174,8 +195,10 @@ void CNPC_ShadowWalker::Spawn( void ) { // CapabilitiesAdd(bits_CAP_ANIMATEDFACE); CapabilitiesAdd(bits_CAP_TURN_HEAD); + CapabilitiesAdd(bits_CAP_SQUAD); CapabilitiesAdd(bits_CAP_USE_WEAPONS | bits_CAP_AIM_GUN | bits_CAP_MOVE_SHOOT); - CapabilitiesAdd(bits_CAP_INNATE_MELEE_ATTACK1); + CapabilitiesAdd(bits_CAP_WEAPON_MELEE_ATTACK1 || bits_CAP_WEAPON_MELEE_ATTACK2); + CapabilitiesAdd(bits_CAP_INNATE_MELEE_ATTACK1 || bits_CAP_INNATE_MELEE_ATTACK2); CapabilitiesAdd(bits_CAP_DUCK | bits_CAP_DOORS_GROUP); CapabilitiesAdd(bits_CAP_USE_SHOT_REGULATOR); } @@ -183,6 +206,8 @@ void CNPC_ShadowWalker::Spawn( void ) CapabilitiesAdd(bits_CAP_MOVE_GROUND); SetMoveType(MOVETYPE_STEP); + + NPCInit(); } @@ -278,6 +303,12 @@ int CNPC_ShadowWalker::SelectIdleSchedule() return SCHED_INVESTIGATE_SOUND; } + if (CanPickkUpWeapons() && HasCondition(COND_BETTER_WEAPON_AVAILABLE)) { + nSched = SelectScheduleRetrieveItem(); + if (nSched != SCHED_NONE) + return nSched; + } + // no valid route! Wander instead if (GetNavigator()->GetGoalType() == GOALTYPE_NONE) return SCHED_IDLE_WANDER; @@ -311,6 +342,12 @@ int CNPC_ShadowWalker::SelectAlertSchedule() return SCHED_INVESTIGATE_SOUND; } + if (CanPickkUpWeapons() && HasCondition(COND_BETTER_WEAPON_AVAILABLE)) { + nSched = SelectScheduleRetrieveItem(); + if (nSched != SCHED_NONE) + return nSched; + } + // no valid route! Wander instead if (GetNavigator()->GetGoalType() == GOALTYPE_NONE) return SCHED_IDLE_WANDER; @@ -376,15 +413,11 @@ int CNPC_ShadowWalker::SelectCombatSchedule() // we can see the enemy if (HasCondition(COND_CAN_MELEE_ATTACK1)) { - //if (OccupyStrategySlotRange(SQUAD_SLOT_ATTACK1, SQUAD_SLOT_ATTACK2)) return SCHED_MELEE_ATTACK1; - //return SCHED_RUN_FROM_ENEMY; } if (HasCondition(COND_CAN_MELEE_ATTACK2)) { - //if (OccupyStrategySlotRange(SQUAD_SLOT_ATTACK1, SQUAD_SLOT_ATTACK2)) return SCHED_MELEE_ATTACK2; - //return SCHED_RUN_FROM_ENEMY; } if (HasRangedWeapon() && GetShotRegulator()->IsInRestInterval()) @@ -430,6 +463,206 @@ bool CNPC_ShadowWalker::HasRangedWeapon() return false; } +//----------------------------------------------------------------------------- +// Originally copied from Citizen +// Purpose: Override base class activiites +//----------------------------------------------------------------------------- +Activity CNPC_ShadowWalker::NPC_TranslateActivity(Activity activity) +{ + if (activity == ACT_MELEE_ATTACK1) + { + return ACT_MELEE_ATTACK_SWING; + } + + // !!!HACK - Citizens don't have the required animations for shotguns, + // so trick them into using the rifle counterparts for now (sjb) + if (activity == ACT_RUN_AIM_SHOTGUN) + return ACT_RUN_AIM_RIFLE; + if (activity == ACT_WALK_AIM_SHOTGUN) + return ACT_WALK_AIM_RIFLE; + if (activity == ACT_IDLE_ANGRY_SHOTGUN) + return ACT_IDLE_ANGRY_SMG1; + if (activity == ACT_RANGE_ATTACK_SHOTGUN_LOW) + return ACT_RANGE_ATTACK_SMG1_LOW; + + return BaseClass::NPC_TranslateActivity(activity); +} + +//----------------------------------------------------------------------------- +// Originally copied from BaseZombie +// Purpose: Catches the monster-specific events that occur when tagged animation +// frames are played. +// Input : pEvent - +//----------------------------------------------------------------------------- +//void CNPC_ShadowWalker::HandleAnimEvent(animevent_t *pEvent) +//{ +// if (pEvent->event == AE_ZOMBIE_ATTACK_RIGHT) +// { +// Vector right, forward; +// AngleVectors(GetLocalAngles(), &forward, &right, NULL); +// +// right = right * 100; +// forward = forward * 200; +// +// QAngle qa(-15, -20, -10); +// Vector vec = right + forward; +// ClawAttack(GetClawAttackRange(), sk_shadow_walker_dmg_innate_melee.GetFloat(), qa, vec, ZOMBIE_BLOOD_RIGHT_HAND); +// return; +// } +// +// if (pEvent->event == AE_ZOMBIE_ATTACK_LEFT) +// { +// Vector right, forward; +// AngleVectors(GetLocalAngles(), &forward, &right, NULL); +// +// right = right * -100; +// forward = forward * 200; +// +// QAngle qa(-15, 20, -10); +// Vector vec = right + forward; +// ClawAttack(GetClawAttackRange(), sk_shadow_walker_dmg_innate_melee.GetFloat(), qa, vec, ZOMBIE_BLOOD_LEFT_HAND); +// return; +// } +// +// if (pEvent->event == AE_ZOMBIE_ATTACK_BOTH) +// { +// Vector forward; +// QAngle qaPunch(45, random->RandomInt(-5, 5), random->RandomInt(-5, 5)); +// AngleVectors(GetLocalAngles(), &forward); +// forward = forward * 200; +// ClawAttack(GetClawAttackRange(), sk_shadow_walker_dmg_innate_melee.GetFloat(), qaPunch, forward, ZOMBIE_BLOOD_BOTH_HANDS); +// return; +// } +// +// BaseClass::HandleAnimEvent(pEvent); +//} + +//----------------------------------------------------------------------------- +// Originally copied from basezombie +// Purpose: Look in front and see if the claw hit anything. +// +// Input : flDist distance to trace +// iDamage damage to do if attack hits +// vecViewPunch camera punch (if attack hits player) +// vecVelocityPunch velocity punch (if attack hits player) +// +// Output : The entity hit by claws. NULL if nothing. +//----------------------------------------------------------------------------- +//CBaseEntity *CNPC_ShadowWalker::ClawAttack(float flDist, int iDamage, QAngle &qaViewPunch, Vector &vecVelocityPunch, int BloodOrigin) +//{ +// DevWarning(1, "Claw attack!\n"); +// +// // Added test because claw attack anim sometimes used when for cases other than melee +// int iDriverInitialHealth = -1; +// CBaseEntity *pDriver = NULL; +// if (GetEnemy()) +// { +// trace_t tr; +// AI_TraceHull(WorldSpaceCenter(), GetEnemy()->WorldSpaceCenter(), -Vector(8, 8, 8), Vector(8, 8, 8), MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr); +// +// if (tr.fraction < 1.0f) +// return NULL; +// +// // CheckTraceHullAttack() can damage player in vehicle as side effect of melee attack damaging physics objects, which the car forwards to the player +// // need to detect this to get correct damage effects +// CBaseCombatCharacter *pCCEnemy = (GetEnemy() != NULL) ? GetEnemy()->MyCombatCharacterPointer() : NULL; +// CBaseEntity *pVehicleEntity; +// if (pCCEnemy != NULL && (pVehicleEntity = pCCEnemy->GetVehicleEntity()) != NULL) +// { +// if (pVehicleEntity->GetServerVehicle() && dynamic_cast(pVehicleEntity)) +// { +// pDriver = static_cast(pVehicleEntity)->GetDriver(); +// if (pDriver && pDriver->IsPlayer()) +// { +// iDriverInitialHealth = pDriver->GetHealth(); +// } +// else +// { +// pDriver = NULL; +// } +// } +// } +// } +// +// // +// // Trace out a cubic section of our hull and see what we hit. +// // +// Vector vecMins = GetHullMins(); +// Vector vecMaxs = GetHullMaxs(); +// vecMins.z = vecMins.x; +// vecMaxs.z = vecMaxs.x; +// +// CBaseEntity *pHurt = NULL; +// if (GetEnemy() && GetEnemy()->Classify() == CLASS_BULLSEYE) +// { +// // We always hit bullseyes we're targeting +// pHurt = GetEnemy(); +// CTakeDamageInfo info(this, this, vec3_origin, GetAbsOrigin(), iDamage, DMG_SLASH); +// pHurt->TakeDamage(info); +// } +// else +// { +// // Try to hit them with a trace +// pHurt = CheckTraceHullAttack(flDist, vecMins, vecMaxs, iDamage, DMG_SLASH); +// } +// +// if (pDriver && iDriverInitialHealth != pDriver->GetHealth()) +// { +// pHurt = pDriver; +// } +// +// if (pHurt) +// { +// //AttackHitSound(); +// +// CBasePlayer *pPlayer = ToBasePlayer(pHurt); +// +// if (pPlayer != NULL && !(pPlayer->GetFlags() & FL_GODMODE)) +// { +// pPlayer->ViewPunch(qaViewPunch); +// +// pPlayer->VelocityPunch(vecVelocityPunch); +// } +// else if (!pPlayer && UTIL_ShouldShowBlood(pHurt->BloodColor())) +// { +// // Hit an NPC. Bleed them! +// Vector vecBloodPos; +// +// switch (BloodOrigin) +// { +// case ZOMBIE_BLOOD_LEFT_HAND: +// if (GetAttachment("blood_left", vecBloodPos)) +// SpawnBlood(vecBloodPos, g_vecAttackDir, pHurt->BloodColor(), MIN(iDamage, 30)); +// break; +// +// case ZOMBIE_BLOOD_RIGHT_HAND: +// if (GetAttachment("blood_right", vecBloodPos)) +// SpawnBlood(vecBloodPos, g_vecAttackDir, pHurt->BloodColor(), MIN(iDamage, 30)); +// break; +// +// case ZOMBIE_BLOOD_BOTH_HANDS: +// if (GetAttachment("blood_left", vecBloodPos)) +// SpawnBlood(vecBloodPos, g_vecAttackDir, pHurt->BloodColor(), MIN(iDamage, 30)); +// +// if (GetAttachment("blood_right", vecBloodPos)) +// SpawnBlood(vecBloodPos, g_vecAttackDir, pHurt->BloodColor(), MIN(iDamage, 30)); +// break; +// +// case ZOMBIE_BLOOD_BITE: +// // No blood for these. +// break; +// } +// } +// } +// else +// { +// //AttackMissSound(); +// } +// +// +// return pHurt; +//} + //----------------------------------------------------------------------------- // Purpose: // From 6b0c4554c0ae331581c43ca2868365178e36bd34 Mon Sep 17 00:00:00 2001 From: Derek Dik Date: Mon, 1 Oct 2018 13:09:09 -0400 Subject: [PATCH 08/36] Shadow Walker: Added Hammer configurable sound scripts --- sp/src/game/server/mod/npc_shadow_walker.cpp | 49 +++++++++++++++++--- 1 file changed, 43 insertions(+), 6 deletions(-) diff --git a/sp/src/game/server/mod/npc_shadow_walker.cpp b/sp/src/game/server/mod/npc_shadow_walker.cpp index 0d1e6a8c..9317390e 100644 --- a/sp/src/game/server/mod/npc_shadow_walker.cpp +++ b/sp/src/game/server/mod/npc_shadow_walker.cpp @@ -96,6 +96,16 @@ public: virtual int SelectCombatSchedule(); virtual bool CanPickkUpWeapons() { return true; } + // Sounds + virtual void PlaySound(string_t soundname); + virtual void DeathSound(const CTakeDamageInfo &info) { PlaySound(m_iszDeathSound); } + virtual void AlertSound(void) { PlaySound(m_iszAlertSound); }; + virtual void IdleSound(void) { PlaySound(m_iszIdleSound); }; + virtual void PainSound(const CTakeDamageInfo &info) { PlaySound(m_iszPainSound); }; + virtual void FearSound(void) { PlaySound(m_iszFearSound); }; + virtual void LostEnemySound(void) { PlaySound(m_iszLostEnemySound); }; + virtual void FoundEnemySound(void) { PlaySound(m_iszFoundEnemySound); }; + // Citizen methods Activity NPC_TranslateActivity(Activity eNewActivity); @@ -111,7 +121,13 @@ public: // for the code to operate on. Delete this field when // you are ready to do your own save/restore for this // character. - bool m_bHasWeapon; + string_t m_iszFearSound; // Path/filename of WAV file to play. + string_t m_iszDeathSound; // Path/filename of WAV file to play. + string_t m_iszIdleSound; // Path/filename of WAV file to play. + string_t m_iszPainSound; // Path/filename of WAV file to play. + string_t m_iszAlertSound; // Path/filename of WAV file to play. + string_t m_iszLostEnemySound; // Path/filename of WAV file to play. + string_t m_iszFoundEnemySound; // Path/filename of WAV file to play. DEFINE_CUSTOM_AI; @@ -128,9 +144,13 @@ IMPLEMENT_CUSTOM_AI( npc_citizen,CNPC_ShadowWalker ); // Save/Restore //--------------------------------------------------------- BEGIN_DATADESC( CNPC_ShadowWalker ) - - DEFINE_FIELD(m_bHasWeapon, FIELD_BOOLEAN), - + DEFINE_KEYFIELD(m_iszFearSound, FIELD_SOUNDNAME, "FearSound"), + DEFINE_KEYFIELD(m_iszDeathSound, FIELD_SOUNDNAME, "DeathSound"), + DEFINE_KEYFIELD(m_iszIdleSound, FIELD_SOUNDNAME, "IdleSound"), + DEFINE_KEYFIELD(m_iszPainSound, FIELD_SOUNDNAME, "PainSound"), + DEFINE_KEYFIELD(m_iszAlertSound, FIELD_SOUNDNAME, "AlertSound"), + DEFINE_KEYFIELD(m_iszLostEnemySound, FIELD_SOUNDNAME, "LostEnemySound"), + DEFINE_KEYFIELD(m_iszFoundEnemySound, FIELD_SOUNDNAME, "FoundEnemySound"), END_DATADESC() //----------------------------------------------------------------------------- @@ -163,7 +183,17 @@ void CNPC_ShadowWalker::Precache( void ) SetModelName(MAKE_STRING("models/monster/subject.mdl")); } + if (!m_iszFearSound) { + m_iszFearSound = MAKE_STRING("NPC_Shadow_Walker.Fear"); + } + + if (!m_iszDeathSound) { + m_iszDeathSound = MAKE_STRING("NPC_Shadow_Walker.Die"); + } + PrecacheModel(STRING(GetModelName())); + PrecacheScriptSound(STRING(m_iszFearSound)); + PrecacheScriptSound(STRING(m_iszDeathSound)); BaseClass::Precache(); } @@ -206,8 +236,6 @@ void CNPC_ShadowWalker::Spawn( void ) CapabilitiesAdd(bits_CAP_MOVE_GROUND); SetMoveType(MOVETYPE_STEP); - - NPCInit(); } @@ -663,6 +691,15 @@ Activity CNPC_ShadowWalker::NPC_TranslateActivity(Activity activity) // return pHurt; //} +//----------------------------------------------------------------------------- +// Purpose: Play NPC soundscript +//----------------------------------------------------------------------------- +void CNPC_ShadowWalker::PlaySound(string_t soundname) +{ + CPASAttenuationFilter filter2(this, STRING(soundname)); + EmitSound(filter2, entindex(), STRING(soundname)); +} + //----------------------------------------------------------------------------- // Purpose: // From d3d4bc5cccbc31ed8caa8b3def4654fc8902b427 Mon Sep 17 00:00:00 2001 From: 1upD <1upderek@gmail.com> Date: Mon, 1 Oct 2018 22:11:33 -0400 Subject: [PATCH 09/36] Shadow Walker: Configurable health and sound precaching --- sp/src/game/server/mod/npc_shadow_walker.cpp | 70 +++++++++++++++----- 1 file changed, 55 insertions(+), 15 deletions(-) diff --git a/sp/src/game/server/mod/npc_shadow_walker.cpp b/sp/src/game/server/mod/npc_shadow_walker.cpp index 9317390e..e3b07b27 100644 --- a/sp/src/game/server/mod/npc_shadow_walker.cpp +++ b/sp/src/game/server/mod/npc_shadow_walker.cpp @@ -76,7 +76,7 @@ // Convars //========================================================= -ConVar sk_shadow_walker_dmg_innate_melee("sk_zombie_dmg_one_slash", "0"); +//ConVar sk_shadow_walker_dmg_innate_melee("sk_zombie_dmg_one_slash", "0"); //========================================================= //========================================================= @@ -133,6 +133,9 @@ public: private: bool HasRangedWeapon(); + void PrecacheNPCSoundScript(string_t* SoundName, string_t defaultSoundName); + + bool m_bWanderToggle; // Boolean to toggle wandering / standing every think cycle }; @@ -144,6 +147,7 @@ IMPLEMENT_CUSTOM_AI( npc_citizen,CNPC_ShadowWalker ); // Save/Restore //--------------------------------------------------------- BEGIN_DATADESC( CNPC_ShadowWalker ) + DEFINE_KEYFIELD(m_iHealth, FIELD_INTEGER, "Health"), DEFINE_KEYFIELD(m_iszFearSound, FIELD_SOUNDNAME, "FearSound"), DEFINE_KEYFIELD(m_iszDeathSound, FIELD_SOUNDNAME, "DeathSound"), DEFINE_KEYFIELD(m_iszIdleSound, FIELD_SOUNDNAME, "IdleSound"), @@ -151,6 +155,9 @@ BEGIN_DATADESC( CNPC_ShadowWalker ) DEFINE_KEYFIELD(m_iszAlertSound, FIELD_SOUNDNAME, "AlertSound"), DEFINE_KEYFIELD(m_iszLostEnemySound, FIELD_SOUNDNAME, "LostEnemySound"), DEFINE_KEYFIELD(m_iszFoundEnemySound, FIELD_SOUNDNAME, "FoundEnemySound"), + + + DEFINE_FIELD(m_bWanderToggle, FIELD_BOOLEAN) END_DATADESC() //----------------------------------------------------------------------------- @@ -183,17 +190,20 @@ void CNPC_ShadowWalker::Precache( void ) SetModelName(MAKE_STRING("models/monster/subject.mdl")); } - if (!m_iszFearSound) { - m_iszFearSound = MAKE_STRING("NPC_Shadow_Walker.Fear"); - } - - if (!m_iszDeathSound) { - m_iszDeathSound = MAKE_STRING("NPC_Shadow_Walker.Die"); - } - PrecacheModel(STRING(GetModelName())); - PrecacheScriptSound(STRING(m_iszFearSound)); + + PrecacheNPCSoundScript(&m_iszFearSound, MAKE_STRING("NPC_Shadow_Walker.Fear")); + PrecacheNPCSoundScript(&m_iszIdleSound, MAKE_STRING("NPC_Shadow_Walker.Idle")); + PrecacheNPCSoundScript(&m_iszAlertSound, MAKE_STRING("NPC_Shadow_Walker.Alert")); + PrecacheNPCSoundScript(&m_iszPainSound, MAKE_STRING("NPC_Shadow_Walker.Pain")); + PrecacheNPCSoundScript(&m_iszLostEnemySound, MAKE_STRING("NPC_Shadow_Walker.LostEnemy")); + PrecacheNPCSoundScript(&m_iszFoundEnemySound, MAKE_STRING("NPC_Shadow_Walker.FoundEnemy")); + PrecacheNPCSoundScript(&m_iszDeathSound, MAKE_STRING("NPC_Shadow_Walker.Death")); + PrecacheScriptSound(STRING(m_iszDeathSound)); + + m_bWanderToggle = false; + BaseClass::Precache(); } @@ -215,7 +225,12 @@ void CNPC_ShadowWalker::Spawn( void ) AddSolidFlags( FSOLID_NOT_STANDABLE ); SetMoveType( MOVETYPE_STEP ); SetBloodColor( BLOOD_COLOR_RED ); - m_iHealth = 50; // Replace this with setting from Hammer + + if (m_iHealth < 1) + { + m_iHealth = 50; + } + m_flFieldOfView = 0.5; m_NPCState = NPC_STATE_NONE; @@ -338,8 +353,15 @@ int CNPC_ShadowWalker::SelectIdleSchedule() } // no valid route! Wander instead - if (GetNavigator()->GetGoalType() == GOALTYPE_NONE) - return SCHED_IDLE_WANDER; + if (GetNavigator()->GetGoalType() == GOALTYPE_NONE) { + m_bWanderToggle = !m_bWanderToggle; + if (m_bWanderToggle) { + return SCHED_IDLE_WANDER; + } + else { + return SCHED_IDLE_STAND; + } + } // valid route. Get moving return SCHED_IDLE_WALK; @@ -367,6 +389,7 @@ int CNPC_ShadowWalker::SelectAlertSchedule() HasCondition(COND_HEAR_COMBAT)) { // Investigate sound source + AlertSound(); return SCHED_INVESTIGATE_SOUND; } @@ -377,8 +400,15 @@ int CNPC_ShadowWalker::SelectAlertSchedule() } // no valid route! Wander instead - if (GetNavigator()->GetGoalType() == GOALTYPE_NONE) - return SCHED_IDLE_WANDER; + if (GetNavigator()->GetGoalType() == GOALTYPE_NONE) { + m_bWanderToggle = !m_bWanderToggle; + if (m_bWanderToggle) { + return SCHED_IDLE_WANDER; + } + else { + return SCHED_ALERT_STAND; + } + } // valid route. Get moving return SCHED_ALERT_WALK; @@ -402,6 +432,7 @@ int CNPC_ShadowWalker::SelectCombatSchedule() if (ChooseEnemy()) { + FoundEnemySound(); ClearCondition(COND_ENEMY_DEAD); return SelectSchedule(); } @@ -431,6 +462,7 @@ int CNPC_ShadowWalker::SelectCombatSchedule() // Can we see the enemy? if (!HasCondition(COND_SEE_ENEMY)) { + FoundEnemySound(); // chase! return SCHED_CHASE_ENEMY; } @@ -700,6 +732,14 @@ void CNPC_ShadowWalker::PlaySound(string_t soundname) EmitSound(filter2, entindex(), STRING(soundname)); } +void CNPC_ShadowWalker::PrecacheNPCSoundScript(string_t * SoundName, string_t defaultSoundName) +{ + if (!SoundName) { + *SoundName = defaultSoundName; + } + PrecacheScriptSound(STRING(*SoundName)); +} + //----------------------------------------------------------------------------- // Purpose: // From d0f4fccefacac230a6d3dc271fea62a1f8400ef5 Mon Sep 17 00:00:00 2001 From: Derek Dik Date: Wed, 3 Oct 2018 13:18:04 -0400 Subject: [PATCH 10/36] Lost Soul: Configurable health, should play sounds from soundscript --- sp/src/game/server/mod/npc_lost_soul.cpp | 65 ++++++++++++++++++++---- 1 file changed, 56 insertions(+), 9 deletions(-) diff --git a/sp/src/game/server/mod/npc_lost_soul.cpp b/sp/src/game/server/mod/npc_lost_soul.cpp index 752cf66c..6bea48e2 100644 --- a/sp/src/game/server/mod/npc_lost_soul.cpp +++ b/sp/src/game/server/mod/npc_lost_soul.cpp @@ -119,8 +119,6 @@ public: void CheckCollisions(float flInterval); void Slice(CBaseEntity *pHitEntity, float flInterval, trace_t &tr); - //void Bump(CBaseEntity *pHitEntity, float flInterval, trace_t &tr); - //void Splash(const Vector &vecSplashPos); virtual int OnTakeDamage_Alive(const CTakeDamageInfo &info); @@ -138,11 +136,14 @@ public: void MoveToTarget(float flInterval, const Vector &MoveTarget); + void ShowHostile(bool hostile = true); DECLARE_DATADESC(); DEFINE_CUSTOM_AI; +private: + float m_flNextEngineSoundTime; }; @@ -154,9 +155,9 @@ IMPLEMENT_CUSTOM_AI( npc_manhack,CNPC_LostSoul ); // Save/Restore //--------------------------------------------------------- BEGIN_DATADESC( CNPC_LostSoul ) - - //DEFINE_FIELD(m_bHasWeapon, FIELD_BOOLEAN), - + DEFINE_KEYFIELD(m_iHealth, FIELD_INTEGER, "Health"), + + DEFINE_FIELD(m_flNextEngineSoundTime, FIELD_TIME) END_DATADESC() //----------------------------------------------------------------------------- @@ -178,6 +179,13 @@ void CNPC_LostSoul::Precache( void ) { PrecacheModel( "models/skeleton/skeleton_torso3.mdl" ); // Replace this with setting from Hammer + PrecacheScriptSound("NPC_LostSoul.Die"); + PrecacheScriptSound("NPC_LostSoul.Burn"); + PrecacheScriptSound("NPC_LostSoul.Float"); + PrecacheScriptSound("NPC_LostSoul.ChargeAnnounce"); + PrecacheScriptSound("NPC_LostSoul.ChargeEnd"); + PrecacheScriptSound("NPC_LostSoul.Stunned"); + BaseClass::Precache(); } @@ -194,7 +202,7 @@ void CNPC_LostSoul::Spawn( void ) Precache(); SetModel("models/skeleton/skeleton_torso3.mdl"); - SetHullType(HULL_HUMAN); // I guess? + SetHullType(HULL_TINY_CENTERED); // There was an error being thrown about collision model being smaller than nav hull - need to look into SetHullSizeNormal(); SetSolid(SOLID_BBOX); @@ -210,7 +218,12 @@ void CNPC_LostSoul::Spawn( void ) SetMoveType(MOVETYPE_VPHYSICS); } - m_iHealth = sk_lostsoul_health.GetFloat(); + // Use the convar if health is less than 1 + if (m_iHealth < 1) + { + m_iHealth = sk_lostsoul_health.GetFloat(); + } + SetViewOffset(Vector(0, 0, 10)); // Position of the eyes relative to NPC's origin. m_flFieldOfView = VIEW_FIELD_FULL; m_NPCState = NPC_STATE_NONE; @@ -254,11 +267,12 @@ void CNPC_LostSoul::Spawn( void ) if (pFlame) { pFlame->SetLifetime(HUGE_VAL); - //AddFlag(FL_ONFIRE); SetEffectEntity(pFlame); pFlame->SetSize(8); pFlame->SetDamage(0.0f); } + + m_flNextEngineSoundTime = gpGlobals->curtime; } int CNPC_LostSoul::OnTakeDamage_Alive(const CTakeDamageInfo &info) @@ -309,7 +323,24 @@ void CNPC_LostSoul::SoundInit(void) void CNPC_LostSoul::PlayFlySound(void) { - // Shhh, don't do anything. + float flEnemyDist; + + if (GetEnemy()) + { + flEnemyDist = (GetAbsOrigin() - GetEnemy()->GetAbsOrigin()).Length(); + } + else + { + flEnemyDist = FLT_MAX; + } + + // Play special engine every once in a while + if (gpGlobals->curtime > m_flNextEngineSoundTime && flEnemyDist < 48) + { + m_flNextEngineSoundTime = gpGlobals->curtime + random->RandomFloat(3.0, 10.0); + + EmitSound("NPC_LostSoul.Float"); + } } void CNPC_LostSoul::StartEye(void) @@ -520,6 +551,22 @@ void CNPC_LostSoul::CheckCollisions(float flInterval) } } +//----------------------------------------------------------------------------- +// Purpose: +// Input : hostile - +//----------------------------------------------------------------------------- +void CNPC_LostSoul::ShowHostile(bool hostile /*= true*/) +{ + if (hostile) + { + EmitSound("NPC_LostSoul.ChargeAnnounce"); + } + else + { + EmitSound("NPC_LostSoul.ChargeEnd"); + } +} + //----------------------------------------------------------------------------- // Purpose: // From f7ad644200d013dbe5639c661800aab456517523 Mon Sep 17 00:00:00 2001 From: 1upD <1upderek@gmail.com> Date: Wed, 3 Oct 2018 22:08:55 -0400 Subject: [PATCH 11/36] Shadow Walker: Added adjustable squad slots and sound delay times --- sp/src/game/server/mod/npc_shadow_walker.cpp | 49 ++++++++++++++------ 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/sp/src/game/server/mod/npc_shadow_walker.cpp b/sp/src/game/server/mod/npc_shadow_walker.cpp index e3b07b27..c2c494c1 100644 --- a/sp/src/game/server/mod/npc_shadow_walker.cpp +++ b/sp/src/game/server/mod/npc_shadow_walker.cpp @@ -97,14 +97,14 @@ public: virtual bool CanPickkUpWeapons() { return true; } // Sounds - virtual void PlaySound(string_t soundname); - virtual void DeathSound(const CTakeDamageInfo &info) { PlaySound(m_iszDeathSound); } - virtual void AlertSound(void) { PlaySound(m_iszAlertSound); }; - virtual void IdleSound(void) { PlaySound(m_iszIdleSound); }; - virtual void PainSound(const CTakeDamageInfo &info) { PlaySound(m_iszPainSound); }; - virtual void FearSound(void) { PlaySound(m_iszFearSound); }; - virtual void LostEnemySound(void) { PlaySound(m_iszLostEnemySound); }; - virtual void FoundEnemySound(void) { PlaySound(m_iszFoundEnemySound); }; + virtual void PlaySound(string_t soundname, bool optional); + virtual void DeathSound(const CTakeDamageInfo &info) { PlaySound(m_iszDeathSound, true); } + virtual void AlertSound(void) { PlaySound(m_iszAlertSound, false); }; + virtual void IdleSound(void) { PlaySound(m_iszIdleSound, false); }; + 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); }; // Citizen methods Activity NPC_TranslateActivity(Activity eNewActivity); @@ -135,7 +135,10 @@ private: bool HasRangedWeapon(); void PrecacheNPCSoundScript(string_t* SoundName, string_t defaultSoundName); + + bool m_bUseBothSquadSlots; // If true use two squad slots, if false use one squad slot 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 }; @@ -155,9 +158,10 @@ BEGIN_DATADESC( CNPC_ShadowWalker ) DEFINE_KEYFIELD(m_iszAlertSound, FIELD_SOUNDNAME, "AlertSound"), DEFINE_KEYFIELD(m_iszLostEnemySound, FIELD_SOUNDNAME, "LostEnemySound"), DEFINE_KEYFIELD(m_iszFoundEnemySound, FIELD_SOUNDNAME, "FoundEnemySound"), + DEFINE_KEYFIELD(m_bUseBothSquadSlots, FIELD_BOOLEAN, "UseBothSquadSlots"), - - DEFINE_FIELD(m_bWanderToggle, FIELD_BOOLEAN) + DEFINE_FIELD(m_bWanderToggle, FIELD_BOOLEAN), + DEFINE_FIELD(m_flNextSoundTime, FIELD_TIME) END_DATADESC() //----------------------------------------------------------------------------- @@ -232,6 +236,7 @@ void CNPC_ShadowWalker::Spawn( void ) } m_flFieldOfView = 0.5; + m_flNextSoundTime = gpGlobals->curtime; m_NPCState = NPC_STATE_NONE; CapabilitiesClear(); @@ -441,9 +446,18 @@ int CNPC_ShadowWalker::SelectCombatSchedule() return SelectSchedule(); } + bool bCanChase = false; + if (m_bUseBothSquadSlots) { + bCanChase = OccupyStrategySlotRange(SQUAD_SLOT_ATTACK1, SQUAD_SLOT_ATTACK2); + } + else { + bCanChase = OccupyStrategySlot(SQUAD_SLOT_ATTACK1); + } + + // If I'm scared of this enemy and he's looking at me, run away // If in a squad, all but one Shadow walker must be afraid of the player - if (IRelationType(GetEnemy()) == D_FR || !OccupyStrategySlotRange(SQUAD_SLOT_ATTACK1, SQUAD_SLOT_ATTACK2)) + if (IRelationType(GetEnemy()) == D_FR || !bCanChase) { if (HasCondition(COND_SEE_ENEMY) && HasCondition(COND_ENEMY_FACING_ME) && HasCondition(COND_HAVE_ENEMY_LOS)) { @@ -726,10 +740,17 @@ Activity CNPC_ShadowWalker::NPC_TranslateActivity(Activity activity) //----------------------------------------------------------------------------- // Purpose: Play NPC soundscript //----------------------------------------------------------------------------- -void CNPC_ShadowWalker::PlaySound(string_t soundname) +void CNPC_ShadowWalker::PlaySound(string_t soundname, bool required /*= false */) { - CPASAttenuationFilter filter2(this, STRING(soundname)); - EmitSound(filter2, entindex(), STRING(soundname)); + if (required || gpGlobals->curtime > m_flNextSoundTime) + { + m_flNextSoundTime = gpGlobals->curtime + random->RandomFloat(1.0, 2.0); + CPASAttenuationFilter filter2(this, STRING(soundname)); + EmitSound(filter2, entindex(), STRING(soundname)); + } + + + } void CNPC_ShadowWalker::PrecacheNPCSoundScript(string_t * SoundName, string_t defaultSoundName) From b7873d0529ba4d3e8a75440ab2a1c68984d94c9e Mon Sep 17 00:00:00 2001 From: Derek Dik Date: Thu, 4 Oct 2018 17:11:34 -0400 Subject: [PATCH 12/36] Shadow Walker: Refactoring. Removed found enemy sound on chase. --- sp/src/game/server/mod/npc_shadow_walker.cpp | 324 ++----------------- 1 file changed, 33 insertions(+), 291 deletions(-) diff --git a/sp/src/game/server/mod/npc_shadow_walker.cpp b/sp/src/game/server/mod/npc_shadow_walker.cpp index c2c494c1..5e0ad6af 100644 --- a/sp/src/game/server/mod/npc_shadow_walker.cpp +++ b/sp/src/game/server/mod/npc_shadow_walker.cpp @@ -1,25 +1,13 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// This is a skeleton file for use when creating a new -// NPC. Copy and rename this file for the new -// NPC and add the copy to the build. +//=//=============================================================================// // -// Leave this file in the build until we ship! Allowing -// this file to be rebuilt with the rest of the game ensures -// that it stays up to date with the rest of the NPC code. +// Purpose: A malevolent being from a parallel universe which at one point +// may have been human. // -// Replace occurances of CNPC_ShadowWalker with the new NPC's -// classname. Don't forget the lower-case occurance in -// LINK_ENTITY_TO_CLASS() +// npc_shadow_walker is designed to be reusable as a generic horror +// game style npc. Its model and sound files may be configured through +// the hammer editor using keyfields. // -// -// ASSUMPTIONS MADE: -// -// You're making a character based on CAI_BaseNPC. If this -// is not true, make sure you replace all occurances -// of 'CAI_BaseNPC' in this file with the appropriate -// parent class. -// -// You're making a human-sized NPC that walks. +// Author: 1upD // //=============================================================================// #include "cbase.h" @@ -36,48 +24,10 @@ #include "engine/IEngineSound.h" #include "basehlcombatweapon_shared.h" #include "ai_squadslot.h" -//#include "npc_BaseZombie.h" -//#include "vehicle_base.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" -//========================================================= -// Private activities -//========================================================= -//int ACT_MYCUSTOMACTIVITY = -1; - -//========================================================= -// Custom schedules -//========================================================= -//enum -//{ -// SCHED_MYCUSTOMSCHEDULE = LAST_SHARED_SCHEDULE, -//}; - -//========================================================= -// Custom tasks -//========================================================= -//enum -//{ -// TASK_MYCUSTOMTASK = LAST_SHARED_TASK, -//}; - - -//========================================================= -// Custom Conditions -//========================================================= -//enum -//{ -// COND_MYCUSTOMCONDITION = LAST_SHARED_CONDITION, -//}; - -//========================================================= -// Convars -//========================================================= - -//ConVar sk_shadow_walker_dmg_innate_melee("sk_zombie_dmg_one_slash", "0"); - //========================================================= //========================================================= class CNPC_ShadowWalker : public CAI_BaseNPC @@ -95,6 +45,7 @@ public: virtual int SelectAlertSchedule(); virtual int SelectCombatSchedule(); virtual bool CanPickkUpWeapons() { return true; } + Activity NPC_TranslateActivity(Activity eNewActivity); // Sounds virtual void PlaySound(string_t soundname, bool optional); @@ -105,29 +56,16 @@ public: virtual void FearSound(void) { PlaySound(m_iszFearSound, false); }; virtual void LostEnemySound(void) { PlaySound(m_iszLostEnemySound, false); }; virtual void FoundEnemySound(void) { PlaySound(m_iszFoundEnemySound, false); }; - - // Citizen methods - Activity NPC_TranslateActivity(Activity eNewActivity); - - // Zombie methods - //void HandleAnimEvent(animevent_t *pEvent); - //virtual CBaseEntity *ClawAttack(float flDist, int iDamage, QAngle &qaViewPunch, Vector &vecVelocityPunch, int BloodOrigin); - //virtual float GetClawAttackRange() const { return ZOMBIE_MELEE_REACH; } DECLARE_DATADESC(); - // This is a dummy field. In order to provide save/restore - // code in this file, we must have at least one field - // for the code to operate on. Delete this field when - // you are ready to do your own save/restore for this - // character. string_t m_iszFearSound; // Path/filename of WAV file to play. string_t m_iszDeathSound; // Path/filename of WAV file to play. string_t m_iszIdleSound; // Path/filename of WAV file to play. string_t m_iszPainSound; // Path/filename of WAV file to play. string_t m_iszAlertSound; // Path/filename of WAV file to play. string_t m_iszLostEnemySound; // Path/filename of WAV file to play. - string_t m_iszFoundEnemySound; // Path/filename of WAV file to play. + string_t m_iszFoundEnemySound; // Path/filename of WAV file to play. DEFINE_CUSTOM_AI; @@ -136,9 +74,9 @@ private: void PrecacheNPCSoundScript(string_t* SoundName, string_t defaultSoundName); - bool m_bUseBothSquadSlots; // If true use two squad slots, if false use one squad slot - 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 + bool m_bUseBothSquadSlots; // If true use two squad slots, if false use one squad slot + 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 }; @@ -172,14 +110,6 @@ END_DATADESC() void CNPC_ShadowWalker::InitCustomSchedules(void) { INIT_CUSTOM_AI(CNPC_ShadowWalker); - - //ADD_CUSTOM_TASK(CNPC_ShadowWalker, TASK_MYCUSTOMTASK); - - //ADD_CUSTOM_SCHEDULE(CNPC_ShadowWalker, SCHED_MYCUSTOMSCHEDULE); - - //ADD_CUSTOM_ACTIVITY(CNPC_ShadowWalker, ACT_MYCUSTOMACTIVITY); - - //ADD_CUSTOM_CONDITION(CNPC_ShadowWalker, COND_MYCUSTOMCONDITION); } //----------------------------------------------------------------------------- @@ -189,13 +119,13 @@ void CNPC_ShadowWalker::InitCustomSchedules(void) //----------------------------------------------------------------------------- void CNPC_ShadowWalker::Precache( void ) { + // If no model name is supplied, use the default Shadow Walker model if (!GetModelName()) { SetModelName(MAKE_STRING("models/monster/subject.mdl")); } PrecacheModel(STRING(GetModelName())); - PrecacheNPCSoundScript(&m_iszFearSound, MAKE_STRING("NPC_Shadow_Walker.Fear")); PrecacheNPCSoundScript(&m_iszIdleSound, MAKE_STRING("NPC_Shadow_Walker.Idle")); PrecacheNPCSoundScript(&m_iszAlertSound, MAKE_STRING("NPC_Shadow_Walker.Alert")); @@ -204,8 +134,6 @@ void CNPC_ShadowWalker::Precache( void ) PrecacheNPCSoundScript(&m_iszFoundEnemySound, MAKE_STRING("NPC_Shadow_Walker.FoundEnemy")); PrecacheNPCSoundScript(&m_iszDeathSound, MAKE_STRING("NPC_Shadow_Walker.Death")); - PrecacheScriptSound(STRING(m_iszDeathSound)); - m_bWanderToggle = false; BaseClass::Precache(); @@ -230,6 +158,7 @@ void CNPC_ShadowWalker::Spawn( void ) SetMoveType( MOVETYPE_STEP ); SetBloodColor( BLOOD_COLOR_RED ); + // If the health has not been set through Hammer, use a default health value of 50 if (m_iHealth < 1) { m_iHealth = 50; @@ -243,7 +172,6 @@ void CNPC_ShadowWalker::Spawn( void ) if (!HasSpawnFlags(SF_NPC_START_EFFICIENT)) { - // CapabilitiesAdd(bits_CAP_ANIMATEDFACE); CapabilitiesAdd(bits_CAP_TURN_HEAD); CapabilitiesAdd(bits_CAP_SQUAD); CapabilitiesAdd(bits_CAP_USE_WEAPONS | bits_CAP_AIM_GUN | bits_CAP_MOVE_SHOOT); @@ -259,10 +187,8 @@ void CNPC_ShadowWalker::Spawn( void ) NPCInit(); } - //----------------------------------------------------------------------------- // Purpose: Choose a schedule after schedule failed -// Copied from npc_citizen //----------------------------------------------------------------------------- int CNPC_ShadowWalker::SelectFailSchedule(int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode) { @@ -278,7 +204,7 @@ int CNPC_ShadowWalker::SelectFailSchedule(int failedSchedule, int failedTask, AI // I can't take cover, so I need to run away! return SCHED_RUN_FROM_ENEMY; case SCHED_CHASE_ENEMY: - // I can't run away, so I will just run randomly! + // I can't run towards the enemy, so I will just run randomly! return SCHED_CHASE_ENEMY_FAILED; case SCHED_RUN_FROM_ENEMY: // I can't run away, so I will just run randomly! @@ -289,7 +215,7 @@ int CNPC_ShadowWalker::SelectFailSchedule(int failedSchedule, int failedTask, AI } //----------------------------------------------------------------------------- -// Copied from npc_citizen +// Purpose: Select a schedule to retrieve better weapons if they are available. //----------------------------------------------------------------------------- int CNPC_ShadowWalker::SelectScheduleRetrieveItem() { @@ -310,7 +236,8 @@ int CNPC_ShadowWalker::SelectScheduleRetrieveItem() } //----------------------------------------------------------------------------- -// Copied from npc_citizen - used as SelectSchedule instead of SelectPriorityAction +// Purpose: Select a schedule to execute based on conditions. +// This is the most critical AI method. //----------------------------------------------------------------------------- int CNPC_ShadowWalker::SelectSchedule() { @@ -374,7 +301,6 @@ int CNPC_ShadowWalker::SelectIdleSchedule() //----------------------------------------------------------------------------- // Alert schedule selection -// Copied from baseNPC //----------------------------------------------------------------------------- int CNPC_ShadowWalker::SelectAlertSchedule() { @@ -446,6 +372,7 @@ int CNPC_ShadowWalker::SelectCombatSchedule() return SelectSchedule(); } + // 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) { bCanChase = OccupyStrategySlotRange(SQUAD_SLOT_ATTACK1, SQUAD_SLOT_ATTACK2); @@ -454,17 +381,12 @@ int CNPC_ShadowWalker::SelectCombatSchedule() bCanChase = OccupyStrategySlot(SQUAD_SLOT_ATTACK1); } - // If I'm scared of this enemy and he's looking at me, run away - // If in a squad, all but one Shadow walker must be afraid of the player - if (IRelationType(GetEnemy()) == D_FR || !bCanChase) + if ((IRelationType(GetEnemy()) == D_FR || !bCanChase) && (HasCondition(COND_SEE_ENEMY) && HasCondition(COND_ENEMY_FACING_ME) && HasCondition(COND_HAVE_ENEMY_LOS))) { - if (HasCondition(COND_SEE_ENEMY) && HasCondition(COND_ENEMY_FACING_ME) && HasCondition(COND_HAVE_ENEMY_LOS)) - { // TODO: Check if silent FearSound(); return SCHED_RUN_FROM_ENEMY; - } } // Reloading conditions are necessary just in case for some reason somebody gives the Shadow Walker a gun @@ -476,8 +398,7 @@ int CNPC_ShadowWalker::SelectCombatSchedule() // Can we see the enemy? if (!HasCondition(COND_SEE_ENEMY)) { - FoundEnemySound(); - // chase! + // Chase! return SCHED_CHASE_ENEMY; } @@ -538,205 +459,26 @@ bool CNPC_ShadowWalker::HasRangedWeapon() } //----------------------------------------------------------------------------- -// Originally copied from Citizen // Purpose: Override base class activiites //----------------------------------------------------------------------------- Activity CNPC_ShadowWalker::NPC_TranslateActivity(Activity activity) { - if (activity == ACT_MELEE_ATTACK1) - { + switch (activity) { + case ACT_MELEE_ATTACK1: return ACT_MELEE_ATTACK_SWING; - } - - // !!!HACK - Citizens don't have the required animations for shotguns, - // so trick them into using the rifle counterparts for now (sjb) - if (activity == ACT_RUN_AIM_SHOTGUN) + case ACT_RUN_AIM_SHOTGUN: return ACT_RUN_AIM_RIFLE; - if (activity == ACT_WALK_AIM_SHOTGUN) + case ACT_WALK_AIM_SHOTGUN: return ACT_WALK_AIM_RIFLE; - if (activity == ACT_IDLE_ANGRY_SHOTGUN) + case ACT_IDLE_ANGRY_SHOTGUN: return ACT_IDLE_ANGRY_SMG1; - if (activity == ACT_RANGE_ATTACK_SHOTGUN_LOW) + case ACT_RANGE_ATTACK_SHOTGUN_LOW: return ACT_RANGE_ATTACK_SMG1_LOW; - - return BaseClass::NPC_TranslateActivity(activity); + default: + return BaseClass::NPC_TranslateActivity(activity); + } } -//----------------------------------------------------------------------------- -// Originally copied from BaseZombie -// Purpose: Catches the monster-specific events that occur when tagged animation -// frames are played. -// Input : pEvent - -//----------------------------------------------------------------------------- -//void CNPC_ShadowWalker::HandleAnimEvent(animevent_t *pEvent) -//{ -// if (pEvent->event == AE_ZOMBIE_ATTACK_RIGHT) -// { -// Vector right, forward; -// AngleVectors(GetLocalAngles(), &forward, &right, NULL); -// -// right = right * 100; -// forward = forward * 200; -// -// QAngle qa(-15, -20, -10); -// Vector vec = right + forward; -// ClawAttack(GetClawAttackRange(), sk_shadow_walker_dmg_innate_melee.GetFloat(), qa, vec, ZOMBIE_BLOOD_RIGHT_HAND); -// return; -// } -// -// if (pEvent->event == AE_ZOMBIE_ATTACK_LEFT) -// { -// Vector right, forward; -// AngleVectors(GetLocalAngles(), &forward, &right, NULL); -// -// right = right * -100; -// forward = forward * 200; -// -// QAngle qa(-15, 20, -10); -// Vector vec = right + forward; -// ClawAttack(GetClawAttackRange(), sk_shadow_walker_dmg_innate_melee.GetFloat(), qa, vec, ZOMBIE_BLOOD_LEFT_HAND); -// return; -// } -// -// if (pEvent->event == AE_ZOMBIE_ATTACK_BOTH) -// { -// Vector forward; -// QAngle qaPunch(45, random->RandomInt(-5, 5), random->RandomInt(-5, 5)); -// AngleVectors(GetLocalAngles(), &forward); -// forward = forward * 200; -// ClawAttack(GetClawAttackRange(), sk_shadow_walker_dmg_innate_melee.GetFloat(), qaPunch, forward, ZOMBIE_BLOOD_BOTH_HANDS); -// return; -// } -// -// BaseClass::HandleAnimEvent(pEvent); -//} - -//----------------------------------------------------------------------------- -// Originally copied from basezombie -// Purpose: Look in front and see if the claw hit anything. -// -// Input : flDist distance to trace -// iDamage damage to do if attack hits -// vecViewPunch camera punch (if attack hits player) -// vecVelocityPunch velocity punch (if attack hits player) -// -// Output : The entity hit by claws. NULL if nothing. -//----------------------------------------------------------------------------- -//CBaseEntity *CNPC_ShadowWalker::ClawAttack(float flDist, int iDamage, QAngle &qaViewPunch, Vector &vecVelocityPunch, int BloodOrigin) -//{ -// DevWarning(1, "Claw attack!\n"); -// -// // Added test because claw attack anim sometimes used when for cases other than melee -// int iDriverInitialHealth = -1; -// CBaseEntity *pDriver = NULL; -// if (GetEnemy()) -// { -// trace_t tr; -// AI_TraceHull(WorldSpaceCenter(), GetEnemy()->WorldSpaceCenter(), -Vector(8, 8, 8), Vector(8, 8, 8), MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr); -// -// if (tr.fraction < 1.0f) -// return NULL; -// -// // CheckTraceHullAttack() can damage player in vehicle as side effect of melee attack damaging physics objects, which the car forwards to the player -// // need to detect this to get correct damage effects -// CBaseCombatCharacter *pCCEnemy = (GetEnemy() != NULL) ? GetEnemy()->MyCombatCharacterPointer() : NULL; -// CBaseEntity *pVehicleEntity; -// if (pCCEnemy != NULL && (pVehicleEntity = pCCEnemy->GetVehicleEntity()) != NULL) -// { -// if (pVehicleEntity->GetServerVehicle() && dynamic_cast(pVehicleEntity)) -// { -// pDriver = static_cast(pVehicleEntity)->GetDriver(); -// if (pDriver && pDriver->IsPlayer()) -// { -// iDriverInitialHealth = pDriver->GetHealth(); -// } -// else -// { -// pDriver = NULL; -// } -// } -// } -// } -// -// // -// // Trace out a cubic section of our hull and see what we hit. -// // -// Vector vecMins = GetHullMins(); -// Vector vecMaxs = GetHullMaxs(); -// vecMins.z = vecMins.x; -// vecMaxs.z = vecMaxs.x; -// -// CBaseEntity *pHurt = NULL; -// if (GetEnemy() && GetEnemy()->Classify() == CLASS_BULLSEYE) -// { -// // We always hit bullseyes we're targeting -// pHurt = GetEnemy(); -// CTakeDamageInfo info(this, this, vec3_origin, GetAbsOrigin(), iDamage, DMG_SLASH); -// pHurt->TakeDamage(info); -// } -// else -// { -// // Try to hit them with a trace -// pHurt = CheckTraceHullAttack(flDist, vecMins, vecMaxs, iDamage, DMG_SLASH); -// } -// -// if (pDriver && iDriverInitialHealth != pDriver->GetHealth()) -// { -// pHurt = pDriver; -// } -// -// if (pHurt) -// { -// //AttackHitSound(); -// -// CBasePlayer *pPlayer = ToBasePlayer(pHurt); -// -// if (pPlayer != NULL && !(pPlayer->GetFlags() & FL_GODMODE)) -// { -// pPlayer->ViewPunch(qaViewPunch); -// -// pPlayer->VelocityPunch(vecVelocityPunch); -// } -// else if (!pPlayer && UTIL_ShouldShowBlood(pHurt->BloodColor())) -// { -// // Hit an NPC. Bleed them! -// Vector vecBloodPos; -// -// switch (BloodOrigin) -// { -// case ZOMBIE_BLOOD_LEFT_HAND: -// if (GetAttachment("blood_left", vecBloodPos)) -// SpawnBlood(vecBloodPos, g_vecAttackDir, pHurt->BloodColor(), MIN(iDamage, 30)); -// break; -// -// case ZOMBIE_BLOOD_RIGHT_HAND: -// if (GetAttachment("blood_right", vecBloodPos)) -// SpawnBlood(vecBloodPos, g_vecAttackDir, pHurt->BloodColor(), MIN(iDamage, 30)); -// break; -// -// case ZOMBIE_BLOOD_BOTH_HANDS: -// if (GetAttachment("blood_left", vecBloodPos)) -// SpawnBlood(vecBloodPos, g_vecAttackDir, pHurt->BloodColor(), MIN(iDamage, 30)); -// -// if (GetAttachment("blood_right", vecBloodPos)) -// SpawnBlood(vecBloodPos, g_vecAttackDir, pHurt->BloodColor(), MIN(iDamage, 30)); -// break; -// -// case ZOMBIE_BLOOD_BITE: -// // No blood for these. -// break; -// } -// } -// } -// else -// { -// //AttackMissSound(); -// } -// -// -// return pHurt; -//} - //----------------------------------------------------------------------------- // Purpose: Play NPC soundscript //----------------------------------------------------------------------------- @@ -748,11 +490,11 @@ void CNPC_ShadowWalker::PlaySound(string_t soundname, bool required /*= false */ CPASAttenuationFilter filter2(this, STRING(soundname)); EmitSound(filter2, entindex(), STRING(soundname)); } - - - } +//----------------------------------------------------------------------------- +// Purpose: Assign a default soundscript if none is provided, then precache +//----------------------------------------------------------------------------- void CNPC_ShadowWalker::PrecacheNPCSoundScript(string_t * SoundName, string_t defaultSoundName) { if (!SoundName) { From 4ac6828023cb1ab78b188c93a079c242ceaa88cc Mon Sep 17 00:00:00 2001 From: 1upD <1upderek@gmail.com> Date: Sat, 6 Oct 2018 22:34:08 -0400 Subject: [PATCH 13/36] Lost Soul / Shadow Walker: Refactoring, cleanup --- sp/src/game/server/mod/npc_lost_soul.cpp | 80 +++----------------- sp/src/game/server/mod/npc_shadow_walker.cpp | 6 +- 2 files changed, 12 insertions(+), 74 deletions(-) diff --git a/sp/src/game/server/mod/npc_lost_soul.cpp b/sp/src/game/server/mod/npc_lost_soul.cpp index 6bea48e2..f87ebe0f 100644 --- a/sp/src/game/server/mod/npc_lost_soul.cpp +++ b/sp/src/game/server/mod/npc_lost_soul.cpp @@ -1,25 +1,12 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// This is a skeleton file for use when creating a new -// NPC. Copy and rename this file for the new -// NPC and add the copy to the build. +//=//=============================================================================// // -// Leave this file in the build until we ship! Allowing -// this file to be rebuilt with the rest of the game ensures -// that it stays up to date with the rest of the NPC code. +// Purpose: A frightening flying creature with the appearance of a burning skull. +// May be familiar to first person shooter fans. // -// Replace occurances of CNPC_ShadowWalker with the new NPC's -// classname. Don't forget the lower-case occurance in -// LINK_ENTITY_TO_CLASS() +// npc_lost_soul is a modified version of a manhack inspired by Mallikas' +// HalloweenVilleFour entry, Satanophobia, as well as Doom. // -// -// ASSUMPTIONS MADE: -// -// You're making a character based on CAI_BaseNPC. If this -// is not true, make sure you replace all occurances -// of 'CAI_BaseNPC' in this file with the appropriate -// parent class. -// -// You're making a human-sized NPC that walks. +// Author: 1upD // //=============================================================================// #include "cbase.h" @@ -61,37 +48,6 @@ // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" -//========================================================= -// Private activities -//========================================================= -//int ACT_MYCUSTOMACTIVITY = -1; - -//========================================================= -// Custom schedules -//========================================================= -//enum -//{ -// SCHED_MYCUSTOMSCHEDULE = LAST_SHARED_SCHEDULE, -//}; - -//========================================================= -// Custom tasks -//========================================================= -//enum -//{ -// TASK_MYCUSTOMTASK = LAST_SHARED_TASK, -//}; - - -//========================================================= -// Custom Conditions -//========================================================= -//enum -//{ -// COND_MYCUSTOMCONDITION = LAST_SHARED_CONDITION, -//}; - - ConVar sk_lostsoul_health("sk_lostsoul_health", "0"); ConVar sk_lostsoul_melee_dmg("sk_lostsoul_melee_dmg", "0"); @@ -290,9 +246,8 @@ int CNPC_LostSoul::OnTakeDamage_Alive(const CTakeDamageInfo &info) //----------------------------------------------------------------------------- void CNPC_LostSoul::DeathSound(const CTakeDamageInfo &info) { - StopSound("NPC_Lost_Soul.Stunned"); - CPASAttenuationFilter filter2(this, "NPC_Lost_Soul.Die"); - EmitSound(filter2, entindex(), "NPC_Lost_Soul.Die"); + CPASAttenuationFilter filter2(this, "NPC_LostSoul.Die"); + EmitSound(filter2, entindex(), "NPC_LostSoul.Die"); } //----------------------------------------------------------------------------- @@ -337,7 +292,7 @@ void CNPC_LostSoul::PlayFlySound(void) // Play special engine every once in a while if (gpGlobals->curtime > m_flNextEngineSoundTime && flEnemyDist < 48) { - m_flNextEngineSoundTime = gpGlobals->curtime + random->RandomFloat(3.0, 10.0); + m_flNextEngineSoundTime = gpGlobals->curtime + random->RandomFloat(0.5, 2.0); EmitSound("NPC_LostSoul.Float"); } @@ -479,34 +434,17 @@ void CNPC_LostSoul::MoveToTarget(float flInterval, const Vector &vMoveTarget) myAccel = flDist / flInterval; } - /* - // Boost vertical movement - if ( targetDir.z > 0 ) - { - // Z acceleration is faster when we thrust upwards. - // This is to help keep manhacks out of water. - myZAccel *= 5.0; - } - */ - // Clamp vertical movement if (myZAccel > flDist / flInterval) { myZAccel = flDist / flInterval; } - // Scale by our engine force - //myAccel *= m_fEnginePowerScale; - //myZAccel *= m_fEnginePowerScale; - MoveInDirection(flInterval, targetDir, myAccel, myZAccel, myDecay); // calc relative banking targets Vector forward, right; GetVectors(&forward, &right, NULL); - //m_vTargetBanking.x = 40 * DotProduct(forward, targetDir); - //m_vTargetBanking.z = 40 * DotProduct(right, targetDir); - //m_vTargetBanking.y = 0.0; } //----------------------------------------------------------------------------- diff --git a/sp/src/game/server/mod/npc_shadow_walker.cpp b/sp/src/game/server/mod/npc_shadow_walker.cpp index 5e0ad6af..27c29488 100644 --- a/sp/src/game/server/mod/npc_shadow_walker.cpp +++ b/sp/src/game/server/mod/npc_shadow_walker.cpp @@ -486,9 +486,9 @@ void CNPC_ShadowWalker::PlaySound(string_t soundname, bool required /*= false */ { if (required || gpGlobals->curtime > m_flNextSoundTime) { - m_flNextSoundTime = gpGlobals->curtime + random->RandomFloat(1.0, 2.0); - CPASAttenuationFilter filter2(this, STRING(soundname)); - EmitSound(filter2, entindex(), STRING(soundname)); + m_flNextSoundTime = gpGlobals->curtime + random->RandomFloat(0.5, 1.0); + //CPASAttenuationFilter filter2(this, STRING(soundname)); + EmitSound(STRING(soundname)); } } From 617fd485464d83419982b5126dbaa99a795f5df0 Mon Sep 17 00:00:00 2001 From: 1upD <1upderek@gmail.com> Date: Sat, 6 Oct 2018 23:35:14 -0400 Subject: [PATCH 14/36] Shadow Walker / Lost Soul: Committing fgd file and sound scripts --- sp/game/mod_episodic/halloween.fgd | 52 +++++ .../scripts/game_sounds_manifest.txt | 126 +++++++++++ .../scripts/npc_sounds_lost_soul.txt | 86 ++++++++ .../scripts/npc_sounds_shadow_walker.txt | 200 ++++++++++++++++++ 4 files changed, 464 insertions(+) create mode 100644 sp/game/mod_episodic/halloween.fgd create mode 100644 sp/game/mod_episodic/scripts/game_sounds_manifest.txt create mode 100644 sp/game/mod_episodic/scripts/npc_sounds_lost_soul.txt create mode 100644 sp/game/mod_episodic/scripts/npc_sounds_shadow_walker.txt diff --git a/sp/game/mod_episodic/halloween.fgd b/sp/game/mod_episodic/halloween.fgd new file mode 100644 index 00000000..7be8f616 --- /dev/null +++ b/sp/game/mod_episodic/halloween.fgd @@ -0,0 +1,52 @@ +//============================================================================= +// +// Purpose: Half-Life 2 mod game definition file (.fgd) +// Defines new entities for Halloween themed mods +// +//============================================================================= + +@include "base.fgd" +@include "halflife2" + +//------------------------------------------------------------------------- +// +// NPCs +// +//------------------------------------------------------------------------- +@NPCClass base(BaseNPC) studio("models/monster/subject.mdl") = npc_shadow_walker : "Shadow Walker" +[ + additionalequipment(choices) : "Weapons" : "0" = + [ + "weapon_smg1" : "SMG1" + "weapon_ar2" : "AR2" + "weapon_stunstick" : "Stun Stick" + "weapon_crowbar" : "Crow Bar" + "weapon_shotgun" : "Shotgun" + "weapon_beerbottle" : "Beer Bottle" + "weapon_beerbottle2" : "Beer Bottle2" + "weapon_rpg" : "RPG" + "0" : "Nothing" + ] + + model(studio) : "World Model" : "models/monster/subject.mdl" + + Health(integer) : "Health" : 75 : "Starting health of the NPC" + + FearSound(sound) : "Fear Sound" : "" : "Must be a sound script (cannot use WAV)" + DeathSound(sound) : "Death Sound" : "NPC_ShadowWalker.Death" : "Must be a sound script (cannot use WAV)" + IdleSound(sound) : "Idle Sound" : "NPC_ShadowWalker.Idle" : "Must be a sound script (cannot use WAV)" + PainSound(sound) : "Pain Sound" : "NPC_ShadowWalker.Pain" : "Must be a sound script (cannot use WAV)" + AlertSound(sound) : "Alert Sound" : "NPC_ShadowWalker.Alert" : "Must be a sound script (cannot use WAV)" + LostEnemySound(sound) : "Lost Enemy Sound" : "" : "Must be a sound script (cannot use WAV)" + FoundEnemySound(sound) : "Found Enemy Sound" : "" : "Must be a sound script (cannot use WAV)" + FoundEnemySound(sound) : "Found Enemy Sound" : "" : "Must be a sound script (cannot use WAV)" + UseBothSquadSlots(choices) : "Use Both Squad Slots" : 0 = + [ + 0 : "Use One Squad Slot" + 1 : "Use Both Squad Slots" + ] +] + +@NPCClass base(npc_manhack) studio("models/skeleton/skeleton_torso3.mdl") = npc_lost_soul : "Lost Soul" +[ +] \ No newline at end of file diff --git a/sp/game/mod_episodic/scripts/game_sounds_manifest.txt b/sp/game/mod_episodic/scripts/game_sounds_manifest.txt new file mode 100644 index 00000000..4819b6ff --- /dev/null +++ b/sp/game/mod_episodic/scripts/game_sounds_manifest.txt @@ -0,0 +1,126 @@ +game_sounds_manifest +{ + + "precache_file" "scripts/game_sounds.txt" + "precache_file" "scripts/game_sounds_ui.txt" + "precache_file" "scripts/game_sounds_player.txt" + + // Weapon sounds + "precache_file" "scripts/game_sounds_weapons.txt" + "precache_file" "scripts/game_sounds_weapons_ep2.txt" + + // HL2 World, Ambient Generic, Items, Physics, Vehicles + "precache_file" "scripts/game_sounds_world.txt" + "precache_file" "scripts/game_sounds_ambient_generic.txt" + "precache_file" "scripts/game_sounds_items.txt" + "precache_file" "scripts/game_sounds_physics.txt" + "precache_file" "scripts/game_sounds_vehicles.txt" + + // E3 level sounds + "precache_file" "scripts/level_sounds_e3_c17.txt" + "precache_file" "scripts/level_sounds_e3_town.txt" + "precache_file" "scripts/level_sounds_e3_bugbait.txt" + + // Episodic content sounds + "precache_file" "scripts/npc_sounds_alyx_episodic.txt" + "precache_file" "scripts/npc_sounds_strider_episodic.txt" + "precache_file" "scripts/npc_sounds_strider_episodic2.txt" + "precache_file" "scripts/npc_sounds_turret_episodic.txt" + "precache_file" "scripts/npc_sounds_soldier_episodic.txt" + "precache_file" "scripts/npc_sounds_ministrider_episodic.txt" + "precache_file" "scripts/npc_sounds_roller_episodic.txt" + "precache_file" "scripts/npc_sounds_combine_ball_episodic.txt" + "precache_file" "scripts/npc_sounds_citizen_episodic.txt" + "precache_file" "scripts/npc_sounds_citizen_episodic2.txt" + "precache_file" "scripts/npc_sounds_citizen_ep1.txt" + "precache_file" "scripts/npc_sounds_zombine.txt" + "precache_file" "scripts/npc_sounds_dog_episodic.txt" + "precache_file" "scripts/npc_sounds_antlion_episodic.txt" + "precache_file" "scripts/npc_sounds_antlionguard_episodic.txt" + "precache_file" "scripts/npc_sounds_antlionguard_episodic2.txt" + "precache_file" "scripts/npc_sounds_advisor.txt" + "precache_file" "scripts/npc_sounds_advisor_episodic2.txt" + "precache_file" "scripts/npc_sounds_hunter.txt" + "precache_file" "scripts/npc_sounds_antlion_grub_episodic.txt" + "precache_file" "scripts/npc_sounds_attackheli_episodic2.txt" + "precache_file" "scripts/npc_sounds_fastzombie_episodic2.txt" + "precache_file" "scripts/game_sounds_addendum_ep2.txt" + + + "precache_file" "scripts/level_sounds_music_episodic.txt" + "precache_file" "scripts/level_sounds_music_episodic2.txt" + "precache_file" "scripts/game_sounds_weapons_episodic.txt" + "precache_file" "scripts/level_voices_episode_01.txt" + "precache_file" "scripts/level_sounds_aftermath_episodic.txt" + "precache_file" "scripts/level_sounds_outland_episodic.txt" + "precache_file" "scripts/level_sounds_c17_02a.txt" + + "precache_file" "scripts/level_voices_episode_02.txt" + "precache_file" "scripts/game_sounds_vehicles_ep2.txt" + + // EP2 content sounds + "precache_file" "scripts/game_sounds_physics_ep2.txt" + + // Game level sounds + "precache_file" "scripts/level_sounds_eli_lab.txt" + "precache_file" "scripts/level_sounds_trainyard.txt" + "precache_file" "scripts/level_sounds_k_lab.txt" + "precache_file" "scripts/level_sounds_k_lab2.txt" + "precache_file" "scripts/level_sounds_coast.txt" + "precache_file" "scripts/level_sounds_novaprospekt.txt" + "precache_file" "scripts/level_sounds_streetwar.txt" + "precache_file" "scripts/level_sounds_streetwar2.txt" + "precache_file" "scripts/level_sounds_breencast.txt" + "precache_file" "scripts/level_sounds_citadel.txt" + "precache_file" "scripts/level_sounds_canals.txt" + "precache_file" "scripts/level_sounds_ravenholm.txt" + "precache_file" "scripts/level_sounds_ravenholm2.txt" + "precache_file" "scripts/level_sounds_canals2.txt" + + //Music Tracks + "precache_file" "scripts/level_sounds_music.txt" + + // NPC Sounds + "precache_file" "scripts/npc_sounds_eli.txt" + "precache_file" "scripts/npc_sounds_alyx.txt" + "precache_file" "scripts/npc_sounds_dog.txt" + "precache_file" "scripts/npc_sounds_citizen.txt" + "precache_file" "scripts/npc_sounds_barney.txt" + "precache_file" "scripts/npc_sounds_soldier.txt" + "precache_file" "scripts/npc_sounds_strider.txt" + "precache_file" "scripts/npc_sounds_zombie.txt" + "precache_file" "scripts/npc_sounds_vortigaunt.txt" + "precache_file" "scripts/npc_sounds_turret.txt" + "precache_file" "scripts/npc_sounds_scanner.txt" + "precache_file" "scripts/npc_sounds_rollermine.txt" + "precache_file" "scripts/npc_sounds_poisonzombie.txt" + "precache_file" "scripts/npc_sounds_metropolice.txt" + "precache_file" "scripts/npc_sounds_combinecamera.txt" + "precache_file" "scripts/npc_sounds_manhack.txt" + "precache_file" "scripts/npc_sounds_ichthyosaur.txt" + "precache_file" "scripts/npc_sounds_blackheadcrab.txt" + "precache_file" "scripts/npc_sounds_fastheadcrab.txt" + "precache_file" "scripts/npc_sounds_headcrab.txt" + "precache_file" "scripts/npc_sounds_fastzombie.txt" + "precache_file" "scripts/npc_sounds_birds.txt" + "precache_file" "scripts/npc_sounds_gunship.txt" + "precache_file" "scripts/npc_sounds_dropship.txt" + "precache_file" "scripts/npc_sounds_barnacle.txt" + "precache_file" "scripts/npc_sounds_attackheli.txt" + "precache_file" "scripts/npc_sounds_antlionguard.txt" + "precache_file" "scripts/npc_sounds_antlion.txt" + "precache_file" "scripts/npc_sounds_env_headcrabcanister.txt" + "precache_file" "scripts/npc_sounds_combine_ball.txt" + "precache_file" "scripts/npc_sounds_combine_mine.txt" + "precache_file" "scripts/npc_sounds_sniper.txt" + "precache_file" "scripts/npc_sounds_stalker.txt" + "precache_file" "scripts/npc_sounds_gman.txt" + "precache_file" "scripts/npc_sounds_combine_cannon.txt" + "precache_file" "scripts/npc_sounds_alyx_episodic2.txt" + "precache_file" "scripts/npc_sounds_turret_episodic2.txt" + + // 1upD's Halloween NPCs + "precache_file" "scripts/npc_sounds_shadow_walker.txt" + "precache_file" "scripts/npc_sounds_lost_soul.txt" + +} \ No newline at end of file diff --git a/sp/game/mod_episodic/scripts/npc_sounds_lost_soul.txt b/sp/game/mod_episodic/scripts/npc_sounds_lost_soul.txt new file mode 100644 index 00000000..8bda8246 --- /dev/null +++ b/sp/game/mod_episodic/scripts/npc_sounds_lost_soul.txt @@ -0,0 +1,86 @@ +// Default sound scripts for npc_lost_soul +// Author: 1upD + +"NPC_LostSoul.Die" +{ + "channel" "CHAN_BODY" + "volume" "VOL_NORM" + "pitch" "PITCH_NORM" + + "soundlevel" "SNDLVL_NORM" + + "wave" "physics/flesh/flesh_bloody_break.wav" +} + + +"NPC_LostSoul.Burn" +{ + "channel" "CHAN_ITEM" + "volume" "VOL_NORM" + "pitch" "PITCH_NORM" + + "soundlevel" "SNDLVL_70dB" + + "rndwave" + { + "wave" "player/pl_burnpain1.wav" + "wave" "player/pl_burnpain2.wav" + "wave" "player/pl_burnpain3.wav" + } +} + + +"NPC_LostSoul.Float" +{ + "channel" "CHAN_ITEM" + "volume" "VOL_NORM" + "pitch" "PITCH_NORM" + + "soundlevel" "SNDLVL_NORM" + + "rndwave" + { + "wave" "ambient\levels\citadel\datatransfmalevx01.wav" + "wave" "ambient\levels\citadel\datatransmalevx01.wav" + "wave" "ambient\levels\citadel\datatransmalevx02.wav" + "wave" "ambient\levels\citadel\datatransmalevx02.wav" + "wave" "ambient\levels\citadel\datatransrandom03.wav" + } +} + + +"NPC_LostSoul.ChargeAnnounce" +{ + "channel" "CHAN_WEAPON" + "volume" "VOL_NORM" + "pitch" "120, 125" + "soundlevel" "SNDLVL_70dB" + "rndwave" + { + "wave" "npc/zombie_poison/pz_alert1.wav" + "wave" "npc/zombie_poison/pz_alert1.wav" + } +} + +"NPC_LostSoul.ChargeEnd" +{ + "channel" "CHAN_WEAPON" + "volume" "VOL_NORM" + "pitch" "120, 125" + "soundlevel" "SNDLVL_70dB" + "rndwave" + { + "wave" "npc/zombie_poison/pz_warn1.wav" + "wave" "npc/zombie_poison/pz_warn1.wav" + } +} + +"NPC_LostSoul.Stunned" +{ + "channel" "CHAN_VOICE" + "volume" "0.80" + "pitch" "145,150" + "soundlevel" "SNDLVL_70dB" + "wave" "npc/scanner/scanner_siren2.wav" +} + diff --git a/sp/game/mod_episodic/scripts/npc_sounds_shadow_walker.txt b/sp/game/mod_episodic/scripts/npc_sounds_shadow_walker.txt new file mode 100644 index 00000000..f31ef80c --- /dev/null +++ b/sp/game/mod_episodic/scripts/npc_sounds_shadow_walker.txt @@ -0,0 +1,200 @@ +// Default sound scripts for npc_shadow_walker +// Author: 1upD + +"NPC_ShadowWalker.Pain" +{ + "channel" "CHAN_VOICE" + "volume" "0.95" + "pitch" "65, 70" + + "soundlevel" "SNDLVL_NORM" + + "rndwave" + { + "wave" "npc\stalker\stalker_pain1.wav" + "wave" "npc\stalker\stalker_pain2.wav" + "wave" "npc\stalker\stalker_pain3.wav" + + } +} + +"NPC_ShadowWalker.Idle" +{ + "channel" "CHAN_VOICE" + "volume" "0.95" + "pitch" "80, 90" + + "soundlevel" "SNDLVL_NORM" + + "rndwave" + { + "wave" "npc\stalker\stalker_scream1.wav" + "wave" "npc\stalker\stalker_scream2.wav" + "wave" "npc\stalker\stalker_scream3.wav" + "wave" "npc\stalker\stalker_scream4.wav" + } +} + +"NPC_ShadowWalker.Vocal.Idle" +{ + "channel" "CHAN_VOICE" + "volume" "0.95" + "pitch" "62, 62" + + "soundlevel" "SNDLVL_NORM" + + "rndwave" + { + "wave" "vo\episode_1\citadel\al_stalk_pleasestopscreaming01.wav" + "wave" "vo\episode_1\citadel\al_stalk_pleasestopscreaming02.wav" + "wave" "vo\episode_1\citadel\al_stalk_pleasestopscreaming03.wav" + "wave" "vo\episode_1\citadel\al_stalk_pleasestopscreaming04.wav" + "wave" "vo\episode_1\citadel\al_stalk_pleasestopscreaming05.wav" + "wave" "vo\k_lab2\al_optimism.wav" + "wave" "vo\episode_1\c17\al_elev_whereselev.wav" + "wave" "vo\episode_1\intro\al_wheredoeshethink.wav" + "wave" "vo\outland_02\griggs_everywhere.wav" + "wave" "vo\outland_11a\silo\kl_silo_wheredata01.wav" + "wave" "vo\episode_1\intro\al_cantbelieveit.wav" + "wave" "vo\episode_1\npc\alyx\al_deaf_canthearanything.wav" + "wave" "vo\episode_1\npc\alyx\al_deaf_canthearyou.wav" + "wave" "vo\k_lab\ba_cantlook.wav" + + } +} + +"NPC_ShadowWalker.Fear" +{ + "channel" "CHAN_VOICE" + "volume" "0.95" + "pitch" "62, 62" + + "soundlevel" "SNDLVL_NORM" + + "rndwave" + { + // Replace these + "wave" "npc\stalker\stalker_die2.wav" + "wave" "npc\stalker\stalker_pain1.wav" + "wave" "npc\stalker\stalker_pain2.wav" + "wave" "npc\stalker\stalker_pain3.wav" + + } +} + +"NPC_ShadowWalker.Alert" +{ + "channel" "CHAN_VOICE" + "volume" "0.95" + "pitch" "80, 90" + + "soundlevel" "SNDLVL_NORM" + + "rndwave" + { + "wave" "npc\stalker\stalker_alert1b.wav" + "wave" "npc\stalker\stalker_alert2b.wav" + "wave" "npc\stalker\stalker_alert3b.wav" + } +} + +"NPC_ShadowWalker.Vocal.Alert" +{ + "channel" "CHAN_VOICE" + "volume" "0.95" + "pitch" "62, 62" + + "soundlevel" "SNDLVL_NORM" + + "rndwave" + { + "wave" "vo\citadel\al_bitofit.wav" + "wave" "vo\outland_01\intro\al_rbed_notalone.wav" + + } +} + +"NPC_ShadowWalker.LostEnemy" +{ + "channel" "CHAN_VOICE" + "volume" "0.95" + "pitch" "80, 90" + + "soundlevel" "SNDLVL_NORM" + + "rndwave" + { + "wave" "npc\zombie_poison\pz_alert2.wav" + "wave" "npc\stalker\stalker_die2.wav" + "wave" "npc\stalker\stalker_scream4.wav" + } +} + +"NPC_ShadowWalker.Vocal.LostEnemy" +{ + "channel" "CHAN_VOICE" + "volume" "0.95" + "pitch" "62, 62" + + "soundlevel" "SNDLVL_NORM" + + "rndwave" + { + "wave" "vo\citadel\al_notagain02.wav" + "wave" "vo\episode_1\npc\alyx\al_light_lost05.wav" + "wave" "vo\episode_1\npc\alyx\al_light_lost11.wav" + "wave" "vo\citadel\al_wonderwhere.wav" + "wave" "vo\outland_12a\launch\al_launch_damnit.wav" + "wave" "vo\streetwar\alyx_gate\al_no.wav" + "wave" "vo\k_lab\kl_thenwhere.wav" + } +} + +"NPC_ShadowWalker.FoundEnemy" +{ + "channel" "CHAN_VOICE" + "volume" "0.95" + "pitch" "75, 85" + + "soundlevel" "SNDLVL_NORM" + + "rndwave" + { + "wave" "npc\stalker\go_alert2.wav" + "wave" "npc\stalker\go_alert2a.wav" + } +} + +"NPC_ShadowWalker.Vocal.FoundEnemy" +{ + "channel" "CHAN_VOICE" + "volume" "0.95" + "pitch" "62, 62" + + "soundlevel" "SNDLVL_NORM" + + "rndwave" + { + "wave" "npc\stalker\stalker_scream1.wav" + "wave" "vo\citadel\al_thatshim.wav" + "wave" "vo\citadel\al_thereheis.wav" + "wave" "vo\episode_1\npc\alyx\al_rejoin02.wav" + "wave" "vo\episode_1\npc\alyx\al_seemanyfoe03.wav" + "wave" "vo\episode_1\npc\alyx\al_zombie_liveone02.wav" + + } +} + +"NPC_ShadowWalker.Death" +{ + "channel" "CHAN_VOICE" + "volume" "0.95" + "pitch" "60, 65" + + "soundlevel" "SNDLVL_NORM" + + "rndwave" + { + "wave" "ambient\creatures\town_child_scream1.wav" + } +} \ No newline at end of file From f681520ef2dac3d745671d26ea91eb50cd8698cf Mon Sep 17 00:00:00 2001 From: 1upD <1upderek@gmail.com> Date: Sun, 7 Oct 2018 11:55:45 -0400 Subject: [PATCH 15/36] Lost Soul / Shadow Walker: Added new NPC source files to VPC --- sp/src/game/server/server_episodic.vpc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sp/src/game/server/server_episodic.vpc b/sp/src/game/server/server_episodic.vpc index 81c9297b..4be25dfe 100644 --- a/sp/src/game/server/server_episodic.vpc +++ b/sp/src/game/server/server_episodic.vpc @@ -50,6 +50,11 @@ $Project "Server (Episodic)" $Folder "HL2 DLL" { + $Folder "Mod" + { + $File "mod\npc_lost_soul.cpp" + $File "mod\npc_shadow_walker.cpp" + } $File "$SRCDIR\game\shared\episodic\achievements_ep1.cpp" $File "$SRCDIR\game\shared\episodic\achievements_ep2.cpp" $File "$SRCDIR\game\shared\episodic\achievements_epx.cpp" From 77a3f1d837960bdfa0030dd51982a94c8554d36a Mon Sep 17 00:00:00 2001 From: 1upD <1upderek@gmail.com> Date: Sun, 7 Oct 2018 12:37:26 -0400 Subject: [PATCH 16/36] Shadow Walker: Improved fgd file to be more mapper friendly. --- sp/game/mod_episodic/halloween.fgd | 37 +++++++++++++++--------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/sp/game/mod_episodic/halloween.fgd b/sp/game/mod_episodic/halloween.fgd index 7be8f616..880928f2 100644 --- a/sp/game/mod_episodic/halloween.fgd +++ b/sp/game/mod_episodic/halloween.fgd @@ -13,34 +13,33 @@ // NPCs // //------------------------------------------------------------------------- -@NPCClass base(BaseNPC) studio("models/monster/subject.mdl") = npc_shadow_walker : "Shadow Walker" +@NPCClass base(BaseNPC) studioprop() = npc_shadow_walker : "Shadow Walker" [ - additionalequipment(choices) : "Weapons" : "0" = + additionalequipment(choices) : "Weapons" : "weapon_crowbar" : "It is recommended that the Shadow Walker be equipped with a melee weapon such as the crowbar or stunstick. The shotgun and Annabelle also work surprisingly well, but firing patterns have not been set up for automatic weapons." = [ - "weapon_smg1" : "SMG1" - "weapon_ar2" : "AR2" - "weapon_stunstick" : "Stun Stick" "weapon_crowbar" : "Crow Bar" + "weapon_stunstick" : "Stun Stick" "weapon_shotgun" : "Shotgun" - "weapon_beerbottle" : "Beer Bottle" - "weapon_beerbottle2" : "Beer Bottle2" - "weapon_rpg" : "RPG" + "weapon_annabelle" : "Annabelle" + "weapon_pistol" : "Pistol" + "weapon_ar2" : "AR2" + "weapon_smg1" : "SMG1" + "weapon_alyxgun" : "Alyx Gun" "0" : "Nothing" ] - model(studio) : "World Model" : "models/monster/subject.mdl" + model(studio) : "World Model" : "models/monster/subject.mdl" : "You may specify any model for this NPC. However, the NPC was written with standard human animations in mind. Missing animations may cause errors. Be aware the animations of the model you choose will affect the NPC's behavior." - Health(integer) : "Health" : 75 : "Starting health of the NPC" + Health(integer) : "Health" : 75 : "Starting health of the NPC." - FearSound(sound) : "Fear Sound" : "" : "Must be a sound script (cannot use WAV)" - DeathSound(sound) : "Death Sound" : "NPC_ShadowWalker.Death" : "Must be a sound script (cannot use WAV)" - IdleSound(sound) : "Idle Sound" : "NPC_ShadowWalker.Idle" : "Must be a sound script (cannot use WAV)" - PainSound(sound) : "Pain Sound" : "NPC_ShadowWalker.Pain" : "Must be a sound script (cannot use WAV)" - AlertSound(sound) : "Alert Sound" : "NPC_ShadowWalker.Alert" : "Must be a sound script (cannot use WAV)" - LostEnemySound(sound) : "Lost Enemy Sound" : "" : "Must be a sound script (cannot use WAV)" - FoundEnemySound(sound) : "Found Enemy Sound" : "" : "Must be a sound script (cannot use WAV)" - FoundEnemySound(sound) : "Found Enemy Sound" : "" : "Must be a sound script (cannot use WAV)" - UseBothSquadSlots(choices) : "Use Both Squad Slots" : 0 = + FearSound(sound) : "Fear Sound" : "NPC_ShadowWalker.Fear" : "The NPC will play this sound when retreating." + DeathSound(sound) : "Death Sound" : "NPC_ShadowWalker.Death" : "The NPC will play this sound on death." + IdleSound(sound) : "Idle Sound" : "NPC_ShadowWalker.Idle" : "The NPC will play this sound while wandering idly." + PainSound(sound) : "Pain Sound" : "NPC_ShadowWalker.Pain" : "The NPC will play this sound when damaged." + AlertSound(sound) : "Alert Sound" : "NPC_ShadowWalker.Alert" : "The NPC will play this sound while in an alert state." + LostEnemySound(sound) : "Lost Enemy Sound" : "NPC_ShadowWalker.LostEnemy" : "The NPC will play this sound if it loses sight of its enemy." + FoundEnemySound(sound) : "Found Enemy Sound" : "NPC_ShadowWalker.FoundEnemy" : "The NPC will play this sound if it finds an enemy again after previously losing it." + UseBothSquadSlots(choices) : "Use Both Squad Slots" : 0 : "When in a squad, how many shadow walkers should be able to chase the player at one time? Additional squadmates will run away from the player in the hopes of setting up an ambush." = [ 0 : "Use One Squad Slot" 1 : "Use Both Squad Slots" From 9602325bbb3396414597fc8546966b5cd2cc6e27 Mon Sep 17 00:00:00 2001 From: 1upD <1upderek@gmail.com> Date: Sun, 7 Oct 2018 16:29:47 -0400 Subject: [PATCH 17/36] Shadow Walker: Added default 'custom melee weapon'. If no weapons are supplied, the Shadow Walker will spawn with a crowbar with a custom model applied. --- sp/game/mod_episodic/halloween.fgd | 5 +- sp/src/game/server/mod/npc_shadow_walker.cpp | 77 +++++++++++++++++--- 2 files changed, 69 insertions(+), 13 deletions(-) diff --git a/sp/game/mod_episodic/halloween.fgd b/sp/game/mod_episodic/halloween.fgd index 880928f2..93bf8c79 100644 --- a/sp/game/mod_episodic/halloween.fgd +++ b/sp/game/mod_episodic/halloween.fgd @@ -15,8 +15,9 @@ //------------------------------------------------------------------------- @NPCClass base(BaseNPC) studioprop() = npc_shadow_walker : "Shadow Walker" [ - additionalequipment(choices) : "Weapons" : "weapon_crowbar" : "It is recommended that the Shadow Walker be equipped with a melee weapon such as the crowbar or stunstick. The shotgun and Annabelle also work surprisingly well, but firing patterns have not been set up for automatic weapons." = + additionalequipment(choices) : "Weapons" : "0" : "It is recommended that the Shadow Walker be equipped with a melee weapon such as the crowbar or stunstick. The shotgun and Annabelle also work surprisingly well, but firing patterns have not been set up for automatic weapons." = [ + "0" : "Custom Melee Weapon" "weapon_crowbar" : "Crow Bar" "weapon_stunstick" : "Stun Stick" "weapon_shotgun" : "Shotgun" @@ -25,10 +26,10 @@ "weapon_ar2" : "AR2" "weapon_smg1" : "SMG1" "weapon_alyxgun" : "Alyx Gun" - "0" : "Nothing" ] model(studio) : "World Model" : "models/monster/subject.mdl" : "You may specify any model for this NPC. However, the NPC was written with standard human animations in mind. Missing animations may cause errors. Be aware the animations of the model you choose will affect the NPC's behavior." + WeaponModel(studio) : "Weapon Model Override" : "models/props_canal/mattpipe.mdl" : "If the Shadow Walker is set to use 'Custom Melee Weapon', you may supply a world model to use. I would recommend using the pipe model, the crowbar model, or the stunstick model." Health(integer) : "Health" : 75 : "Starting health of the NPC." diff --git a/sp/src/game/server/mod/npc_shadow_walker.cpp b/sp/src/game/server/mod/npc_shadow_walker.cpp index 27c29488..0f46b322 100644 --- a/sp/src/game/server/mod/npc_shadow_walker.cpp +++ b/sp/src/game/server/mod/npc_shadow_walker.cpp @@ -24,6 +24,7 @@ #include "engine/IEngineSound.h" #include "basehlcombatweapon_shared.h" #include "ai_squadslot.h" +#include "weapon_crowbar.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -45,7 +46,7 @@ public: virtual int SelectAlertSchedule(); virtual int SelectCombatSchedule(); virtual bool CanPickkUpWeapons() { return true; } - Activity NPC_TranslateActivity(Activity eNewActivity); + Activity NPC_TranslateActivity(Activity eNewActivity); // Sounds virtual void PlaySound(string_t soundname, bool optional); @@ -56,9 +57,14 @@ public: virtual void FearSound(void) { PlaySound(m_iszFearSound, false); }; virtual void LostEnemySound(void) { PlaySound(m_iszLostEnemySound, false); }; virtual void FoundEnemySound(void) { PlaySound(m_iszFoundEnemySound, false); }; + + void Activate(); + void FixupWeapon(); DECLARE_DATADESC(); + string_t m_iszWeaponModelName; // Path/filename of model to override weapon model. + string_t m_iszFearSound; // Path/filename of WAV file to play. string_t m_iszDeathSound; // Path/filename of WAV file to play. string_t m_iszIdleSound; // Path/filename of WAV file to play. @@ -87,7 +93,8 @@ IMPLEMENT_CUSTOM_AI( npc_citizen,CNPC_ShadowWalker ); //--------------------------------------------------------- // Save/Restore //--------------------------------------------------------- -BEGIN_DATADESC( CNPC_ShadowWalker ) +BEGIN_DATADESC(CNPC_ShadowWalker) + DEFINE_KEYFIELD(m_iszWeaponModelName, FIELD_STRING, "WeaponModel"), DEFINE_KEYFIELD(m_iHealth, FIELD_INTEGER, "Health"), DEFINE_KEYFIELD(m_iszFearSound, FIELD_SOUNDNAME, "FearSound"), DEFINE_KEYFIELD(m_iszDeathSound, FIELD_SOUNDNAME, "DeathSound"), @@ -112,6 +119,18 @@ void CNPC_ShadowWalker::InitCustomSchedules(void) INIT_CUSTOM_AI(CNPC_ShadowWalker); } +//----------------------------------------------------------------------------- +// Purpose: Inner class for default weapon +// TODO: Merge this with the Matt weapon in npc_citizen +//----------------------------------------------------------------------------- +class CWeaponCustomMelee : public CWeaponCrowbar +{ + DECLARE_CLASS(CWeaponCustomMelee, CWeaponCrowbar); + + const char *GetWorldModel() const { return GetModelName().ToCStr(); } + void SetPickupTouch(void) { /* do nothing */ } +}; + //----------------------------------------------------------------------------- // Purpose: // @@ -125,14 +144,18 @@ void CNPC_ShadowWalker::Precache( void ) SetModelName(MAKE_STRING("models/monster/subject.mdl")); } + if (&m_iszWeaponModelName && m_iszWeaponModelName != MAKE_STRING("")) { + PrecacheModel(STRING(m_iszWeaponModelName)); + } + PrecacheModel(STRING(GetModelName())); - PrecacheNPCSoundScript(&m_iszFearSound, MAKE_STRING("NPC_Shadow_Walker.Fear")); - PrecacheNPCSoundScript(&m_iszIdleSound, MAKE_STRING("NPC_Shadow_Walker.Idle")); - PrecacheNPCSoundScript(&m_iszAlertSound, MAKE_STRING("NPC_Shadow_Walker.Alert")); - PrecacheNPCSoundScript(&m_iszPainSound, MAKE_STRING("NPC_Shadow_Walker.Pain")); - PrecacheNPCSoundScript(&m_iszLostEnemySound, MAKE_STRING("NPC_Shadow_Walker.LostEnemy")); - PrecacheNPCSoundScript(&m_iszFoundEnemySound, MAKE_STRING("NPC_Shadow_Walker.FoundEnemy")); - PrecacheNPCSoundScript(&m_iszDeathSound, MAKE_STRING("NPC_Shadow_Walker.Death")); + PrecacheNPCSoundScript(&m_iszFearSound, MAKE_STRING("NPC_ShadowWalker.Fear")); + PrecacheNPCSoundScript(&m_iszIdleSound, MAKE_STRING("NPC_ShadowWalker.Idle")); + PrecacheNPCSoundScript(&m_iszAlertSound, MAKE_STRING("NPC_ShadowWalker.Alert")); + PrecacheNPCSoundScript(&m_iszPainSound, MAKE_STRING("NPC_ShadowWalker.Pain")); + PrecacheNPCSoundScript(&m_iszLostEnemySound, MAKE_STRING("NPC_ShadowWalker.LostEnemy")); + PrecacheNPCSoundScript(&m_iszFoundEnemySound, MAKE_STRING("NPC_ShadowWalker.FoundEnemy")); + PrecacheNPCSoundScript(&m_iszDeathSound, MAKE_STRING("NPC_ShadowWalker.Death")); m_bWanderToggle = false; @@ -187,6 +210,40 @@ void CNPC_ShadowWalker::Spawn( void ) NPCInit(); } +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CNPC_ShadowWalker::FixupWeapon() +{ + // If no weapons supplied, give a crowbar + CBaseCombatWeapon *pWeapon = GetActiveWeapon(); + if (pWeapon == NULL) { + pWeapon = (CBaseCombatWeapon *)CREATE_UNSAVED_ENTITY(CWeaponCustomMelee, "weapon_crowbar"); + + // Apply weapon model override + if (&m_iszWeaponModelName && m_iszWeaponModelName != MAKE_STRING("")) { + pWeapon->SetModel(STRING(m_iszWeaponModelName)); + } + else { + pWeapon->SetModel("models/props_canal/mattpipe.mdl"); + } + + DispatchSpawn(pWeapon); + Weapon_Equip(pWeapon); + } + + +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +void CNPC_ShadowWalker::Activate() +{ + BaseClass::Activate(); + FixupWeapon(); +} + + //----------------------------------------------------------------------------- // Purpose: Choose a schedule after schedule failed //----------------------------------------------------------------------------- @@ -464,8 +521,6 @@ bool CNPC_ShadowWalker::HasRangedWeapon() Activity CNPC_ShadowWalker::NPC_TranslateActivity(Activity activity) { switch (activity) { - case ACT_MELEE_ATTACK1: - return ACT_MELEE_ATTACK_SWING; case ACT_RUN_AIM_SHOTGUN: return ACT_RUN_AIM_RIFLE; case ACT_WALK_AIM_SHOTGUN: From 3c9871111b7e43fae4deb6f26d5362b37274053d Mon Sep 17 00:00:00 2001 From: 1upD <1upderek@gmail.com> Date: Sun, 7 Oct 2018 22:36:29 -0400 Subject: [PATCH 18/36] Shadow Walker: Fixed bug where spawning Shadow Walker through console crashes the game. Set default health to 75. --- sp/game/mod_episodic/cfg/skill.cfg | 256 +++++++++++++++++++ sp/game/mod_episodic/cfg/skill_episodic.cfg | 2 + sp/src/game/server/mod/npc_shadow_walker.cpp | 7 +- 3 files changed, 263 insertions(+), 2 deletions(-) create mode 100644 sp/game/mod_episodic/cfg/skill.cfg diff --git a/sp/game/mod_episodic/cfg/skill.cfg b/sp/game/mod_episodic/cfg/skill.cfg new file mode 100644 index 00000000..ad7b853d --- /dev/null +++ b/sp/game/mod_episodic/cfg/skill.cfg @@ -0,0 +1,256 @@ +// =========== +// NPCs +// ============ +// Barnacle +sk_barnacle_health "35" + +// Barney +sk_barney_health "35" + +// Bullseye +sk_bullseye_health "35" + +// Bullsquid +//sk_bullsquid_health "120" +//sk_bullsquid_dmg_bite "15" +//sk_bullsquid_dmg_whip "25" + +// Citizen +sk_citizen_health "40" + +// Combine Soldier +sk_combine_s_health "50" +sk_combine_s_kick "10" + +// Combine Guard +sk_combine_guard_health "70" +sk_combine_guard_kick "15" + +// strider +sk_strider_health "350" +sk_strider_num_missiles1 "5" +sk_strider_num_missiles2 "7" +sk_strider_num_missiles3 "7" + +// Headcrab +sk_headcrab_health "10" +sk_headcrab_melee_dmg "5" + +// Fast Headcrab +sk_headcrab_fast_health "10" + +// Poison Headcrab +sk_headcrab_poison_health "35" + +// Houndeye +//sk_houndeye_health "80" +//sk_houndeye_dmg_blast "25" + +// Manhack +sk_manhack_health "25" +sk_manhack_melee_dmg "20" + +// Metropolice +sk_metropolice_health "40" +sk_metropolice_stitch_reaction "1.0" +sk_metropolice_stitch_tight_hitcount "2" +sk_metropolice_stitch_at_hitcount "1" +sk_metropolice_stitch_behind_hitcount "3" +sk_metropolice_stitch_along_hitcount "2" + +// Rollermine +sk_rollermine_shock "10" +sk_rollermine_stun_delay "3" +sk_rollermine_vehicle_intercept "1" + +// Scanner (City) +sk_scanner_health "30" +sk_scanner_dmg_dive "25" + +// Stalker +sk_stalker_health "50" +sk_stalker_melee_dmg "5" + +// Vortigaunt +sk_vortigaunt_health "100" +sk_vortigaunt_dmg_claw "10" +sk_vortigaunt_dmg_rake "25" +sk_vortigaunt_dmg_zap "50" +sk_vortigaunt_armor_charge "30" + +// Zombie +sk_zombie_health "50" +sk_zombie_dmg_one_slash "10" +sk_zombie_dmg_both_slash "25" + +// Poison Zombie +sk_zombie_poison_health "175" +sk_zombie_poison_dmg_spit "20" + +//Antlion +sk_antlion_health "30" +sk_antlion_swipe_damage "5" +sk_antlion_jump_damage "5" + +//Antlion Guard +sk_antlionguard_health "500" +sk_antlionguard_dmg_charge "20" +sk_antlionguard_dmg_shove "10" + +//Antlion Grub +//sk_antliongrub_health "5" + +//Ichthyosaur +sk_ichthyosaur_health "200" +sk_ichthyosaur_melee_dmg "8" + +// Combine Gunship +sk_gunship_burst_size "15" +sk_gunship_health_increments "5" +sk_npc_dmg_gunship "40" +sk_npc_dmg_gunship_to_plr "3" + +// Combine Helicopter +sk_npc_dmg_helicopter "6" +sk_npc_dmg_helicopter_to_plr "3" +sk_helicopter_grenadedamage "30" +sk_helicopter_grenaderadius "275" +sk_helicopter_grenadeforce "55000" + +// Combine Dropship +sk_npc_dmg_dropship "2" + +// Combine APC +sk_apc_health "750" + + +// ================= +// WEAPONS +// ================= + +sk_plr_dmg_ar2 "8" +sk_npc_dmg_ar2 "3" +sk_max_ar2 "60" +sk_max_ar2_altfire "3" + +sk_plr_dmg_alyxgun "5" +sk_npc_dmg_alyxgun "3" +sk_max_alyxgun "150" + +sk_plr_dmg_pistol "5" +sk_npc_dmg_pistol "3" +sk_max_pistol "150" + +sk_plr_dmg_smg1 "4" +sk_npc_dmg_smg1 "3" +sk_max_smg1 "225" + +sk_plr_dmg_buckshot "8" +sk_npc_dmg_buckshot "3" +sk_max_buckshot "30" + +sk_plr_dmg_rpg_round "100" +sk_npc_dmg_rpg_round "50" +sk_max_rpg_round "3" + +sk_plr_dmg_smg1_grenade "100" +sk_npc_dmg_smg1_grenade "50" +sk_max_smg1_grenade "3" +sk_smg1_grenade_radius "250" + +//sk_plr_dmg_gauss "25" +//sk_plr_max_dmg_gauss "30" + +sk_plr_dmg_sniper_round "20" +sk_npc_dmg_sniper_round "100" +sk_max_sniper_round "30" + +sk_plr_dmg_357 "40" +sk_npc_dmg_357 "30" +sk_max_357 "12" + +sk_plr_dmg_crossbow "100" +sk_npc_dmg_crossbow "10" +sk_max_crossbow "10" + +sk_plr_dmg_airboat "3" +sk_npc_dmg_airboat "3" + +//sk_dmg_sniper_penetrate_plr "10" +//sk_dmg_sniper_penetrate_npc "100" + +sk_plr_dmg_grenade "150" +sk_npc_dmg_grenade "75" +sk_max_grenade "5" + +sk_plr_dmg_crowbar "10" +sk_npc_dmg_crowbar "5" + +sk_plr_dmg_stunstick "10" +sk_npc_dmg_stunstick "40" // Kill a citizen in one hit + +//sk_plr_dmg_satchel "150" +//sk_npc_dmg_satchel "75" +//sk_satchel_radius "150" + +// Mortar Synth projectile +//sk_dmg_energy_grenade "2" +//sk_energy_grenade_radius "100" + +sk_dmg_homer_grenade "20" +sk_homer_grenade_radius "100" + +// Bullsquid spit +//sk_dmg_spit_grenade "5" +//sk_spit_grenade_radius "50" + +//sk_plr_dmg_tripmine "150" +//sk_npc_dmg_tripmine "125" +//sk_tripmine_radius "200" + +sk_plr_dmg_fraggrenade "125" +sk_npc_dmg_fraggrenade "75" +sk_fraggrenade_radius "250" + +// HEALTH/SUIT CHARGE DISTRIBUTION +sk_suitcharger "75" +sk_suitcharger_citadel "500" +sk_suitcharger_citadel_maxarmor "200" +sk_battery "15" +sk_healthcharger "50" +sk_healthkit "25" +sk_healthvial "10" + +// Combine balls +sk_combineball_seek_angle "15" +sk_combineball_guidefactor "1.0" + +// NPC damage adjusters +sk_npc_head "3" +sk_npc_chest "1" +sk_npc_stomach "1" +sk_npc_arm "1" +sk_npc_leg "1" + +// player damage adjusters +sk_player_head "3" +sk_player_chest "1" +sk_player_stomach "1" +sk_player_arm "1" +sk_player_leg "1" + +// Allies +sk_ally_regen_time "0.2" + +// Jeep +sk_max_gauss_round "30" + +// ================= +// RTSL +// ================= +//Max health +sk_mechacrab_health "40" +//Damage inflicted by the mechacrab +sk_mechacrab_melee_dmg "10" +//Damage taken by crowbar hits +sk_mechacrab_crowbar_dmg "20" \ No newline at end of file diff --git a/sp/game/mod_episodic/cfg/skill_episodic.cfg b/sp/game/mod_episodic/cfg/skill_episodic.cfg index 19eedc25..0434d0d5 100644 --- a/sp/game/mod_episodic/cfg/skill_episodic.cfg +++ b/sp/game/mod_episodic/cfg/skill_episodic.cfg @@ -7,3 +7,5 @@ sk_apc_missile_damage "50" // Antlion air attack sk_antlion_air_attack_dmg "10" +sk_lostsoul_health "25" +sk_lostsoul_melee_dmg "5" \ No newline at end of file diff --git a/sp/src/game/server/mod/npc_shadow_walker.cpp b/sp/src/game/server/mod/npc_shadow_walker.cpp index 0f46b322..1e868d9c 100644 --- a/sp/src/game/server/mod/npc_shadow_walker.cpp +++ b/sp/src/game/server/mod/npc_shadow_walker.cpp @@ -147,6 +147,9 @@ void CNPC_ShadowWalker::Precache( void ) if (&m_iszWeaponModelName && m_iszWeaponModelName != MAKE_STRING("")) { PrecacheModel(STRING(m_iszWeaponModelName)); } + else { + PrecacheModel("models/props_canal/mattpipe.mdl"); + } PrecacheModel(STRING(GetModelName())); PrecacheNPCSoundScript(&m_iszFearSound, MAKE_STRING("NPC_ShadowWalker.Fear")); @@ -181,10 +184,10 @@ void CNPC_ShadowWalker::Spawn( void ) SetMoveType( MOVETYPE_STEP ); SetBloodColor( BLOOD_COLOR_RED ); - // If the health has not been set through Hammer, use a default health value of 50 + // If the health has not been set through Hammer, use a default health value of 75 if (m_iHealth < 1) { - m_iHealth = 50; + m_iHealth = 75; } m_flFieldOfView = 0.5; From 88a8cf9ea77ca0ec4ecac474eb574525994426d7 Mon Sep 17 00:00:00 2001 From: Derek Dik Date: Tue, 16 Oct 2018 16:34:14 -0400 Subject: [PATCH 19/36] Lost Soul: ShowHostile function in manhack must be virtual to be overridden --- sp/src/game/server/hl2/npc_manhack.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sp/src/game/server/hl2/npc_manhack.h b/sp/src/game/server/hl2/npc_manhack.h index 02d05962..e3457e36 100644 --- a/sp/src/game/server/hl2/npc_manhack.h +++ b/sp/src/game/server/hl2/npc_manhack.h @@ -170,6 +170,7 @@ public: m_iHealth = 0; } + virtual void ShowHostile(bool hostile = true); DEFINE_CUSTOM_AI; @@ -186,7 +187,7 @@ private: void UpdatePanels( void ); void SetEyeState( int state ); - void ShowHostile( bool hostile = true ); + bool IsFlyingActivity( Activity baseAct ); From f85c30d5474a78707d30f4d476ebcf5d329659ea Mon Sep 17 00:00:00 2001 From: 1upD <1upderek@gmail.com> Date: Tue, 16 Oct 2018 19:10:02 -0400 Subject: [PATCH 20/36] Lost Soul: Fixed manhack sounds playing on charge and stun --- sp/src/game/server/hl2/npc_manhack.cpp | 17 ++++++++++++++--- sp/src/game/server/hl2/npc_manhack.h | 11 ++++++----- sp/src/game/server/mod/npc_lost_soul.cpp | 14 ++++++++++++-- 3 files changed, 32 insertions(+), 10 deletions(-) diff --git a/sp/src/game/server/hl2/npc_manhack.cpp b/sp/src/game/server/hl2/npc_manhack.cpp index feef84f8..f04d08cb 100644 --- a/sp/src/game/server/hl2/npc_manhack.cpp +++ b/sp/src/game/server/hl2/npc_manhack.cpp @@ -3135,16 +3135,27 @@ void CNPC_Manhack::ShowHostile( bool hostile /*= true*/) //TODO: Open the manhack panels or close them, depending on the state m_bShowingHostile = hostile; - if ( hostile ) + PlayAttackSound(hostile); +} + +//----------------------------------------------------------------------------- +// Purpose: Play a sound before charging at the player +// Input : hostile - +//----------------------------------------------------------------------------- +void CNPC_Manhack::PlayAttackSound(bool hostile /*= true*/) +{ + if (hostile) { - EmitSound( "NPC_Manhack.ChargeAnnounce" ); + EmitSound("NPC_Manhack.ChargeAnnounce"); } else { - EmitSound( "NPC_Manhack.ChargeEnd" ); + EmitSound("NPC_Manhack.ChargeEnd"); } } + + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- diff --git a/sp/src/game/server/hl2/npc_manhack.h b/sp/src/game/server/hl2/npc_manhack.h index e3457e36..c994469b 100644 --- a/sp/src/game/server/hl2/npc_manhack.h +++ b/sp/src/game/server/hl2/npc_manhack.h @@ -102,9 +102,10 @@ public: virtual float GetHeadTurnRate( void ) { return 45.0f; } // Degrees per second - virtual void CheckCollisions(float flInterval); + virtual void CheckCollisions(float flInterval); virtual void GatherEnemyConditions( CBaseEntity *pEnemy ); - virtual void PlayFlySound(void); + virtual void PlayFlySound(void); + virtual void PlayAttackSound(bool bHostile); virtual void StopLoopingSounds(void); void Precache(void); @@ -126,7 +127,7 @@ public: void SpinBlades(float flInterval); - virtual void Slice( CBaseEntity *pHitEntity, float flInterval, trace_t &tr ); + virtual void Slice( CBaseEntity *pHitEntity, float flInterval, trace_t &tr ); void Bump( CBaseEntity *pHitEntity, float flInterval, trace_t &tr ); void Splash( const Vector &vecSplashPos ); @@ -170,7 +171,7 @@ public: m_iHealth = 0; } - virtual void ShowHostile(bool hostile = true); + virtual void SetEyeState(int state); DEFINE_CUSTOM_AI; @@ -185,7 +186,6 @@ private: void StopBurst( bool bInterruptSchedule = false ); void UpdatePanels( void ); - void SetEyeState( int state ); @@ -206,6 +206,7 @@ private: // Are we being held by the physcannon? bool IsHeldByPhyscannon( ); + void ShowHostile(bool hostile = true); void StartLoitering( const Vector &vecLoiterPosition ); void StopLoitering() { m_vecLoiterPosition = vec3_invalid; m_fTimeNextLoiterPulse = gpGlobals->curtime; } bool IsLoitering() { return m_vecLoiterPosition != vec3_invalid; } diff --git a/sp/src/game/server/mod/npc_lost_soul.cpp b/sp/src/game/server/mod/npc_lost_soul.cpp index f87ebe0f..29664f7e 100644 --- a/sp/src/game/server/mod/npc_lost_soul.cpp +++ b/sp/src/game/server/mod/npc_lost_soul.cpp @@ -92,7 +92,8 @@ public: void MoveToTarget(float flInterval, const Vector &MoveTarget); - void ShowHostile(bool hostile = true); + void PlayAttackSound(bool hostile = true); + void SetEyeState(int state); DECLARE_DATADESC(); @@ -493,7 +494,7 @@ void CNPC_LostSoul::CheckCollisions(float flInterval) // Purpose: // Input : hostile - //----------------------------------------------------------------------------- -void CNPC_LostSoul::ShowHostile(bool hostile /*= true*/) +void CNPC_LostSoul::PlayAttackSound(bool hostile /*= true*/) { if (hostile) { @@ -505,6 +506,15 @@ void CNPC_LostSoul::ShowHostile(bool hostile /*= true*/) } } +//----------------------------------------------------------------------------- +// Purpose: +// Input : state - +//----------------------------------------------------------------------------- +void CNPC_LostSoul::SetEyeState(int state) +{ + // Do nothing for now +} + //----------------------------------------------------------------------------- // Purpose: // From f625b145d58bd3dc546908afb0305990b342fa15 Mon Sep 17 00:00:00 2001 From: 1upD <1upderek@gmail.com> Date: Tue, 16 Oct 2018 20:10:27 -0400 Subject: [PATCH 21/36] Lost Soul: Replaced manhack pain sound --- sp/src/game/server/hl2/npc_manhack.cpp | 11 ++++++++++- sp/src/game/server/hl2/npc_manhack.h | 1 + sp/src/game/server/mod/npc_lost_soul.cpp | 10 ++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/sp/src/game/server/hl2/npc_manhack.cpp b/sp/src/game/server/hl2/npc_manhack.cpp index f04d08cb..f7dd372d 100644 --- a/sp/src/game/server/hl2/npc_manhack.cpp +++ b/sp/src/game/server/hl2/npc_manhack.cpp @@ -764,7 +764,7 @@ int CNPC_Manhack::OnTakeDamage_Alive( const CTakeDamageInfo &info ) m_vForceVelocity = vecBestDir * info.GetDamage() * 0.5f; m_flBladeSpeed = 10.0; - EmitSound( "NPC_Manhack.Bat" ); + PlayDamagedSound(); // tdInfo.SetDamage( 1.0 ); @@ -3155,6 +3155,15 @@ void CNPC_Manhack::PlayAttackSound(bool hostile /*= true*/) } +//----------------------------------------------------------------------------- +// Purpose: Play a sound before charging at the player +//----------------------------------------------------------------------------- +void CNPC_Manhack::PlayDamagedSound(void) +{ + EmitSound("NPC_Manhack.Bat"); +} + + //----------------------------------------------------------------------------- // Purpose: diff --git a/sp/src/game/server/hl2/npc_manhack.h b/sp/src/game/server/hl2/npc_manhack.h index c994469b..4554d56c 100644 --- a/sp/src/game/server/hl2/npc_manhack.h +++ b/sp/src/game/server/hl2/npc_manhack.h @@ -106,6 +106,7 @@ public: virtual void GatherEnemyConditions( CBaseEntity *pEnemy ); virtual void PlayFlySound(void); virtual void PlayAttackSound(bool bHostile); + virtual void PlayDamagedSound(void); virtual void StopLoopingSounds(void); void Precache(void); diff --git a/sp/src/game/server/mod/npc_lost_soul.cpp b/sp/src/game/server/mod/npc_lost_soul.cpp index 29664f7e..154522a2 100644 --- a/sp/src/game/server/mod/npc_lost_soul.cpp +++ b/sp/src/game/server/mod/npc_lost_soul.cpp @@ -93,6 +93,7 @@ public: void MoveToTarget(float flInterval, const Vector &MoveTarget); void PlayAttackSound(bool hostile = true); + void PlayDamagedSound(void); void SetEyeState(int state); DECLARE_DATADESC(); @@ -142,6 +143,7 @@ void CNPC_LostSoul::Precache( void ) PrecacheScriptSound("NPC_LostSoul.ChargeAnnounce"); PrecacheScriptSound("NPC_LostSoul.ChargeEnd"); PrecacheScriptSound("NPC_LostSoul.Stunned"); + PrecacheScriptSound("NPC_LostSoul.Bat"); BaseClass::Precache(); } @@ -506,6 +508,14 @@ void CNPC_LostSoul::PlayAttackSound(bool hostile /*= true*/) } } +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CNPC_LostSoul::PlayDamagedSound(void) +{ + EmitSound("NPC_LostSoul.Bat"); +} + //----------------------------------------------------------------------------- // Purpose: // Input : state - From 628bc797288be5bdc163da34e165d9dc825dea8f Mon Sep 17 00:00:00 2001 From: 1upD <1upderek@gmail.com> Date: Tue, 16 Oct 2018 20:47:12 -0400 Subject: [PATCH 22/36] Lost Soul: Improved soundscripts --- .../scripts/npc_sounds_lost_soul.txt | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/sp/game/mod_episodic/scripts/npc_sounds_lost_soul.txt b/sp/game/mod_episodic/scripts/npc_sounds_lost_soul.txt index 8bda8246..4e1681ef 100644 --- a/sp/game/mod_episodic/scripts/npc_sounds_lost_soul.txt +++ b/sp/game/mod_episodic/scripts/npc_sounds_lost_soul.txt @@ -29,6 +29,17 @@ } } +"NPC_LostSoul.Bat" +{ + "channel" "CHAN_ITEM" + "volume" "VOL_NORM" + "pitch" "150, 175" + + "soundlevel" "SNDLVL_70dB" + + "wave" "npc\stalker\stalker_alert3b.wav" +} + "NPC_LostSoul.Float" { @@ -53,7 +64,8 @@ { "channel" "CHAN_WEAPON" "volume" "VOL_NORM" - "pitch" "120, 125" + "pitch" "150, 175" + //"pitch" "120, 125" "soundlevel" "SNDLVL_70dB" "rndwave" { @@ -66,7 +78,8 @@ { "channel" "CHAN_WEAPON" "volume" "VOL_NORM" - "pitch" "120, 125" + "pitch" "150, 175" + //"pitch" "120, 125" "soundlevel" "SNDLVL_70dB" "rndwave" { @@ -81,6 +94,7 @@ "volume" "0.80" "pitch" "145,150" "soundlevel" "SNDLVL_70dB" - "wave" "npc/scanner/scanner_siren2.wav" + "wave" "NPC_Antlion.PoisonBurstScream" + } From 3ef753b341916cbb7efe0a49dbb632a907990800 Mon Sep 17 00:00:00 2001 From: Derek Dik Date: Wed, 17 Oct 2018 16:40:25 -0400 Subject: [PATCH 23/36] Added key field to disable door opening behavior --- sp/src/game/server/mod/npc_shadow_walker.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sp/src/game/server/mod/npc_shadow_walker.cpp b/sp/src/game/server/mod/npc_shadow_walker.cpp index 1e868d9c..9cbeb8e4 100644 --- a/sp/src/game/server/mod/npc_shadow_walker.cpp +++ b/sp/src/game/server/mod/npc_shadow_walker.cpp @@ -81,6 +81,7 @@ 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_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 }; @@ -104,6 +105,7 @@ BEGIN_DATADESC(CNPC_ShadowWalker) DEFINE_KEYFIELD(m_iszLostEnemySound, FIELD_SOUNDNAME, "LostEnemySound"), DEFINE_KEYFIELD(m_iszFoundEnemySound, FIELD_SOUNDNAME, "FoundEnemySound"), DEFINE_KEYFIELD(m_bUseBothSquadSlots, FIELD_BOOLEAN, "UseBothSquadSlots"), + DEFINE_KEYFIELD(m_bCannotOpenDoors, FIELD_BOOLEAN, "CannotOpenDoors"), DEFINE_FIELD(m_bWanderToggle, FIELD_BOOLEAN), DEFINE_FIELD(m_flNextSoundTime, FIELD_TIME) @@ -203,8 +205,12 @@ void CNPC_ShadowWalker::Spawn( void ) CapabilitiesAdd(bits_CAP_USE_WEAPONS | bits_CAP_AIM_GUN | bits_CAP_MOVE_SHOOT); CapabilitiesAdd(bits_CAP_WEAPON_MELEE_ATTACK1 || bits_CAP_WEAPON_MELEE_ATTACK2); CapabilitiesAdd(bits_CAP_INNATE_MELEE_ATTACK1 || bits_CAP_INNATE_MELEE_ATTACK2); - CapabilitiesAdd(bits_CAP_DUCK | bits_CAP_DOORS_GROUP); + CapabilitiesAdd(bits_CAP_DUCK); CapabilitiesAdd(bits_CAP_USE_SHOT_REGULATOR); + + if (!m_bCannotOpenDoors) { + CapabilitiesAdd(bits_CAP_DOORS_GROUP); + } } CapabilitiesAdd(bits_CAP_MOVE_GROUND); From 1007a6434a2818ff5265635c9efbcca91d1e9eec Mon Sep 17 00:00:00 2001 From: Derek Dik Date: Wed, 17 Oct 2018 17:09:39 -0400 Subject: [PATCH 24/36] Shadow Walker: Added new key field for open / close doors and corresponding inputs. --- sp/src/game/server/mod/npc_shadow_walker.cpp | 45 +++++++++++++++++++- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/sp/src/game/server/mod/npc_shadow_walker.cpp b/sp/src/game/server/mod/npc_shadow_walker.cpp index 9cbeb8e4..86037ffd 100644 --- a/sp/src/game/server/mod/npc_shadow_walker.cpp +++ b/sp/src/game/server/mod/npc_shadow_walker.cpp @@ -60,7 +60,12 @@ public: void Activate(); void FixupWeapon(); - + + // Inputs + virtual void InputSetSpeed(inputdata_t &inputdata); + virtual void InputEnableOpenDoors(inputdata_t &inputdata); + virtual void InputDisableOpenDoors(inputdata_t &inputdata); + DECLARE_DATADESC(); string_t m_iszWeaponModelName; // Path/filename of model to override weapon model. @@ -108,7 +113,11 @@ BEGIN_DATADESC(CNPC_ShadowWalker) DEFINE_KEYFIELD(m_bCannotOpenDoors, FIELD_BOOLEAN, "CannotOpenDoors"), DEFINE_FIELD(m_bWanderToggle, FIELD_BOOLEAN), - DEFINE_FIELD(m_flNextSoundTime, FIELD_TIME) + DEFINE_FIELD(m_flNextSoundTime, FIELD_TIME), + + DEFINE_INPUTFUNC(FIELD_FLOAT, "SetSpeed", InputSetSpeed), + DEFINE_INPUTFUNC(FIELD_VOID, "EnableOpenDoors", InputEnableOpenDoors), + DEFINE_INPUTFUNC(FIELD_VOID, "DisableOpenDoors", InputDisableOpenDoors) END_DATADESC() //----------------------------------------------------------------------------- @@ -567,6 +576,38 @@ void CNPC_ShadowWalker::PrecacheNPCSoundScript(string_t * SoundName, string_t de PrecacheScriptSound(STRING(*SoundName)); } +//----------------------------------------------------------------------------- +// Purpose: Hammer input to change the speed of the NPC +//----------------------------------------------------------------------------- +void CNPC_ShadowWalker::InputSetSpeed(inputdata_t &inputdata) +{ + this->m_flSpeed = inputdata.value.Float(); +} + +//----------------------------------------------------------------------------- +// Purpose: Hammer input to enable opening doors +//----------------------------------------------------------------------------- +void CNPC_ShadowWalker::InputEnableOpenDoors(inputdata_t &inputdata) +{ + m_bCannotOpenDoors = true; + if (!HasSpawnFlags(SF_NPC_START_EFFICIENT)) + { + CapabilitiesAdd(bits_CAP_DOORS_GROUP); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Hammer input to enable opening doors +//----------------------------------------------------------------------------- +void CNPC_ShadowWalker::InputDisableOpenDoors(inputdata_t &inputdata) +{ + m_bCannotOpenDoors = false; + if (!HasSpawnFlags(SF_NPC_START_EFFICIENT)) + { + CapabilitiesRemove(bits_CAP_DOORS_GROUP); + } +} + //----------------------------------------------------------------------------- // Purpose: // From e7cf130d0893750021ac4473899b299087d2bf0d Mon Sep 17 00:00:00 2001 From: Derek Dik Date: Wed, 17 Oct 2018 16:40:25 -0400 Subject: [PATCH 25/36] Added key field to disable door opening behavior --- sp/src/game/server/mod/npc_shadow_walker.cpp | 45 +------------------- 1 file changed, 2 insertions(+), 43 deletions(-) diff --git a/sp/src/game/server/mod/npc_shadow_walker.cpp b/sp/src/game/server/mod/npc_shadow_walker.cpp index 86037ffd..9cbeb8e4 100644 --- a/sp/src/game/server/mod/npc_shadow_walker.cpp +++ b/sp/src/game/server/mod/npc_shadow_walker.cpp @@ -60,12 +60,7 @@ public: void Activate(); void FixupWeapon(); - - // Inputs - virtual void InputSetSpeed(inputdata_t &inputdata); - virtual void InputEnableOpenDoors(inputdata_t &inputdata); - virtual void InputDisableOpenDoors(inputdata_t &inputdata); - + DECLARE_DATADESC(); string_t m_iszWeaponModelName; // Path/filename of model to override weapon model. @@ -113,11 +108,7 @@ BEGIN_DATADESC(CNPC_ShadowWalker) DEFINE_KEYFIELD(m_bCannotOpenDoors, FIELD_BOOLEAN, "CannotOpenDoors"), DEFINE_FIELD(m_bWanderToggle, FIELD_BOOLEAN), - DEFINE_FIELD(m_flNextSoundTime, FIELD_TIME), - - DEFINE_INPUTFUNC(FIELD_FLOAT, "SetSpeed", InputSetSpeed), - DEFINE_INPUTFUNC(FIELD_VOID, "EnableOpenDoors", InputEnableOpenDoors), - DEFINE_INPUTFUNC(FIELD_VOID, "DisableOpenDoors", InputDisableOpenDoors) + DEFINE_FIELD(m_flNextSoundTime, FIELD_TIME) END_DATADESC() //----------------------------------------------------------------------------- @@ -576,38 +567,6 @@ void CNPC_ShadowWalker::PrecacheNPCSoundScript(string_t * SoundName, string_t de PrecacheScriptSound(STRING(*SoundName)); } -//----------------------------------------------------------------------------- -// Purpose: Hammer input to change the speed of the NPC -//----------------------------------------------------------------------------- -void CNPC_ShadowWalker::InputSetSpeed(inputdata_t &inputdata) -{ - this->m_flSpeed = inputdata.value.Float(); -} - -//----------------------------------------------------------------------------- -// Purpose: Hammer input to enable opening doors -//----------------------------------------------------------------------------- -void CNPC_ShadowWalker::InputEnableOpenDoors(inputdata_t &inputdata) -{ - m_bCannotOpenDoors = true; - if (!HasSpawnFlags(SF_NPC_START_EFFICIENT)) - { - CapabilitiesAdd(bits_CAP_DOORS_GROUP); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Hammer input to enable opening doors -//----------------------------------------------------------------------------- -void CNPC_ShadowWalker::InputDisableOpenDoors(inputdata_t &inputdata) -{ - m_bCannotOpenDoors = false; - if (!HasSpawnFlags(SF_NPC_START_EFFICIENT)) - { - CapabilitiesRemove(bits_CAP_DOORS_GROUP); - } -} - //----------------------------------------------------------------------------- // Purpose: // From b8fb4ba435b36db9170cfee184c1720988cf4b81 Mon Sep 17 00:00:00 2001 From: Derek Dik Date: Wed, 17 Oct 2018 17:09:39 -0400 Subject: [PATCH 26/36] Shadow Walker: Added new key field for open / close doors and corresponding inputs. --- sp/src/game/server/mod/npc_shadow_walker.cpp | 45 +++++++++++++++++++- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/sp/src/game/server/mod/npc_shadow_walker.cpp b/sp/src/game/server/mod/npc_shadow_walker.cpp index 9cbeb8e4..86037ffd 100644 --- a/sp/src/game/server/mod/npc_shadow_walker.cpp +++ b/sp/src/game/server/mod/npc_shadow_walker.cpp @@ -60,7 +60,12 @@ public: void Activate(); void FixupWeapon(); - + + // Inputs + virtual void InputSetSpeed(inputdata_t &inputdata); + virtual void InputEnableOpenDoors(inputdata_t &inputdata); + virtual void InputDisableOpenDoors(inputdata_t &inputdata); + DECLARE_DATADESC(); string_t m_iszWeaponModelName; // Path/filename of model to override weapon model. @@ -108,7 +113,11 @@ BEGIN_DATADESC(CNPC_ShadowWalker) DEFINE_KEYFIELD(m_bCannotOpenDoors, FIELD_BOOLEAN, "CannotOpenDoors"), DEFINE_FIELD(m_bWanderToggle, FIELD_BOOLEAN), - DEFINE_FIELD(m_flNextSoundTime, FIELD_TIME) + DEFINE_FIELD(m_flNextSoundTime, FIELD_TIME), + + DEFINE_INPUTFUNC(FIELD_FLOAT, "SetSpeed", InputSetSpeed), + DEFINE_INPUTFUNC(FIELD_VOID, "EnableOpenDoors", InputEnableOpenDoors), + DEFINE_INPUTFUNC(FIELD_VOID, "DisableOpenDoors", InputDisableOpenDoors) END_DATADESC() //----------------------------------------------------------------------------- @@ -567,6 +576,38 @@ void CNPC_ShadowWalker::PrecacheNPCSoundScript(string_t * SoundName, string_t de PrecacheScriptSound(STRING(*SoundName)); } +//----------------------------------------------------------------------------- +// Purpose: Hammer input to change the speed of the NPC +//----------------------------------------------------------------------------- +void CNPC_ShadowWalker::InputSetSpeed(inputdata_t &inputdata) +{ + this->m_flSpeed = inputdata.value.Float(); +} + +//----------------------------------------------------------------------------- +// Purpose: Hammer input to enable opening doors +//----------------------------------------------------------------------------- +void CNPC_ShadowWalker::InputEnableOpenDoors(inputdata_t &inputdata) +{ + m_bCannotOpenDoors = true; + if (!HasSpawnFlags(SF_NPC_START_EFFICIENT)) + { + CapabilitiesAdd(bits_CAP_DOORS_GROUP); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Hammer input to enable opening doors +//----------------------------------------------------------------------------- +void CNPC_ShadowWalker::InputDisableOpenDoors(inputdata_t &inputdata) +{ + m_bCannotOpenDoors = false; + if (!HasSpawnFlags(SF_NPC_START_EFFICIENT)) + { + CapabilitiesRemove(bits_CAP_DOORS_GROUP); + } +} + //----------------------------------------------------------------------------- // Purpose: // From a66998cc9c5eb8f9a724ab846136b01fd8cecaa4 Mon Sep 17 00:00:00 2001 From: 1upD <1upderek@gmail.com> Date: Wed, 17 Oct 2018 19:39:13 -0400 Subject: [PATCH 27/36] Shadow Walker: Replaced nonfunctioning SetSpeed input with SetSpeedModifier input --- sp/src/game/server/mod/npc_shadow_walker.cpp | 33 ++++++++++++++++---- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/sp/src/game/server/mod/npc_shadow_walker.cpp b/sp/src/game/server/mod/npc_shadow_walker.cpp index 86037ffd..eb57b5c2 100644 --- a/sp/src/game/server/mod/npc_shadow_walker.cpp +++ b/sp/src/game/server/mod/npc_shadow_walker.cpp @@ -46,6 +46,7 @@ public: virtual int SelectAlertSchedule(); virtual int SelectCombatSchedule(); virtual bool CanPickkUpWeapons() { return true; } + virtual float GetSequenceGroundSpeed(CStudioHdr *pStudioHdr, int iSequence); Activity NPC_TranslateActivity(Activity eNewActivity); // Sounds @@ -62,7 +63,7 @@ public: void FixupWeapon(); // Inputs - virtual void InputSetSpeed(inputdata_t &inputdata); + virtual void InputSetSpeedModifier(inputdata_t &inputdata); virtual void InputEnableOpenDoors(inputdata_t &inputdata); virtual void InputDisableOpenDoors(inputdata_t &inputdata); @@ -89,6 +90,7 @@ private: bool m_bCannotOpenDoors; // If true, this NPC cannot open doors. The condition is reversed because originally it could. 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_flSpeedModifier; // Modifier to apply to move distance }; @@ -114,8 +116,9 @@ BEGIN_DATADESC(CNPC_ShadowWalker) DEFINE_FIELD(m_bWanderToggle, FIELD_BOOLEAN), DEFINE_FIELD(m_flNextSoundTime, FIELD_TIME), + DEFINE_FIELD(m_flSpeedModifier, FIELD_TIME), - DEFINE_INPUTFUNC(FIELD_FLOAT, "SetSpeed", InputSetSpeed), + DEFINE_INPUTFUNC(FIELD_FLOAT, "SetSpeedModifier", InputSetSpeedModifier), DEFINE_INPUTFUNC(FIELD_VOID, "EnableOpenDoors", InputEnableOpenDoors), DEFINE_INPUTFUNC(FIELD_VOID, "DisableOpenDoors", InputDisableOpenDoors) END_DATADESC() @@ -204,6 +207,7 @@ void CNPC_ShadowWalker::Spawn( void ) m_flFieldOfView = 0.5; m_flNextSoundTime = gpGlobals->curtime; m_NPCState = NPC_STATE_NONE; + m_flSpeedModifier = 1.0f; CapabilitiesClear(); @@ -576,12 +580,29 @@ void CNPC_ShadowWalker::PrecacheNPCSoundScript(string_t * SoundName, string_t de PrecacheScriptSound(STRING(*SoundName)); } +//----------------------------------------------------------------------------- +// Purpose: Get movement speed, multipled by modifier +//----------------------------------------------------------------------------- +float CNPC_ShadowWalker::GetSequenceGroundSpeed(CStudioHdr *pStudioHdr, int iSequence) +{ + float t = SequenceDuration(pStudioHdr, iSequence); + + if (t > 0) + { + return (GetSequenceMoveDist(pStudioHdr, iSequence) * m_flSpeedModifier / t); + } + else + { + return 0; + } +} + //----------------------------------------------------------------------------- // Purpose: Hammer input to change the speed of the NPC //----------------------------------------------------------------------------- -void CNPC_ShadowWalker::InputSetSpeed(inputdata_t &inputdata) +void CNPC_ShadowWalker::InputSetSpeedModifier(inputdata_t &inputdata) { - this->m_flSpeed = inputdata.value.Float(); + this->m_flSpeedModifier = inputdata.value.Float(); } //----------------------------------------------------------------------------- @@ -589,7 +610,7 @@ void CNPC_ShadowWalker::InputSetSpeed(inputdata_t &inputdata) //----------------------------------------------------------------------------- void CNPC_ShadowWalker::InputEnableOpenDoors(inputdata_t &inputdata) { - m_bCannotOpenDoors = true; + m_bCannotOpenDoors = false; if (!HasSpawnFlags(SF_NPC_START_EFFICIENT)) { CapabilitiesAdd(bits_CAP_DOORS_GROUP); @@ -601,7 +622,7 @@ void CNPC_ShadowWalker::InputEnableOpenDoors(inputdata_t &inputdata) //----------------------------------------------------------------------------- void CNPC_ShadowWalker::InputDisableOpenDoors(inputdata_t &inputdata) { - m_bCannotOpenDoors = false; + m_bCannotOpenDoors = true; if (!HasSpawnFlags(SF_NPC_START_EFFICIENT)) { CapabilitiesRemove(bits_CAP_DOORS_GROUP); From fdffc94a135af6dc75f619e98d6e0dd4f0194241 Mon Sep 17 00:00:00 2001 From: 1upD <1upderek@gmail.com> Date: Wed, 17 Oct 2018 20:01:30 -0400 Subject: [PATCH 28/36] Shadow Walker: Added new field and inputs to FGD --- sp/game/mod_episodic/halloween.fgd | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/sp/game/mod_episodic/halloween.fgd b/sp/game/mod_episodic/halloween.fgd index 93bf8c79..876c75b2 100644 --- a/sp/game/mod_episodic/halloween.fgd +++ b/sp/game/mod_episodic/halloween.fgd @@ -6,7 +6,7 @@ //============================================================================= @include "base.fgd" -@include "halflife2" +@include "halflife2.fgd" //------------------------------------------------------------------------- // @@ -45,6 +45,14 @@ 0 : "Use One Squad Slot" 1 : "Use Both Squad Slots" ] + CannotOpenDoors(choices) : "Can Open Doors?" : 0 : "Is this NPC able to open doors? You can change this after spawning with EnableOpenDoors and DisableOpenDoors, but it doesn't always seem to work well with pathfinding." = + [ + 0 : "Can Open Doors" + 1 : "Cannot Open Doors" + ] + 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." ] @NPCClass base(npc_manhack) studio("models/skeleton/skeleton_torso3.mdl") = npc_lost_soul : "Lost Soul" From 029944b409bfb0dc7bf7cd64296824ebab334edd Mon Sep 17 00:00:00 2001 From: Derek Dik Date: Mon, 22 Oct 2018 16:54:06 -0400 Subject: [PATCH 29/36] Shadow Walker: Behavior bugfixes including melee attacks being interruptable, perpetual screaming, and incorrect squad behavior --- sp/game/mod_episodic/halloween.fgd | 18 +- sp/src/game/server/mod/npc_shadow_walker.cpp | 202 +++++++++++++++---- 2 files changed, 185 insertions(+), 35 deletions(-) diff --git a/sp/game/mod_episodic/halloween.fgd b/sp/game/mod_episodic/halloween.fgd index 93bf8c79..ee5d5e27 100644 --- a/sp/game/mod_episodic/halloween.fgd +++ b/sp/game/mod_episodic/halloween.fgd @@ -6,7 +6,7 @@ //============================================================================= @include "base.fgd" -@include "halflife2" +@include "halflife2.fgd" //------------------------------------------------------------------------- // @@ -45,6 +45,22 @@ 0 : "Use One Squad Slot" 1 : "Use Both Squad Slots" ] + CannotOpenDoors(choices) : "Can Open Doors?" : 0 : "Is this NPC able to open doors? You can change this after spawning with EnableOpenDoors and DisableOpenDoors, but it doesn't always seem to work well with pathfinding." = + [ + 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 86037ffd..d33530ac 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,12 +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; } + Activity NPC_TranslateActivity(Activity eNewActivity); + virtual int TranslateSchedule(int scheduleType); // Sounds virtual void PlaySound(string_t soundname, bool optional); @@ -56,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(); @@ -65,6 +77,8 @@ public: virtual void InputSetSpeed(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(); @@ -85,10 +99,12 @@ private: void PrecacheNPCSoundScript(string_t* SoundName, string_t defaultSoundName); - 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_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 + 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 }; @@ -111,15 +127,67 @@ 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_INPUTFUNC(FIELD_FLOAT, "SetSpeed", InputSetSpeed), 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 : @@ -203,6 +271,7 @@ void CNPC_ShadowWalker::Spawn( void ) m_flFieldOfView = 0.5; m_flNextSoundTime = gpGlobals->curtime; + m_flNextFoundEnemySoundTime = gpGlobals->curtime; m_NPCState = NPC_STATE_NONE; CapabilitiesClear(); @@ -252,9 +321,6 @@ void CNPC_ShadowWalker::FixupWeapon() } -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- - void CNPC_ShadowWalker::Activate() { BaseClass::Activate(); @@ -310,6 +376,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. @@ -353,7 +433,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; @@ -361,13 +441,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 @@ -399,7 +475,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; @@ -407,13 +483,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 @@ -439,6 +511,7 @@ int CNPC_ShadowWalker::SelectCombatSchedule() if (ChooseEnemy()) { FoundEnemySound(); + ClearCondition(COND_ENEMY_DEAD); return SelectSchedule(); } @@ -447,21 +520,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 @@ -533,12 +617,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: @@ -552,14 +654,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)); } @@ -608,6 +726,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: // 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 30/36] 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: // From 289a9b4f6e5da76c31dd09b60ce61ceb1dcddefb Mon Sep 17 00:00:00 2001 From: 1upD <1upderek@gmail.com> Date: Mon, 22 Oct 2018 19:06:05 -0400 Subject: [PATCH 31/36] Shadow Walker 2: Correctly implemented custom schedules --- sp/src/game/server/mod/npc_shadow_walker.cpp | 34 +++++++++----------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/sp/src/game/server/mod/npc_shadow_walker.cpp b/sp/src/game/server/mod/npc_shadow_walker.cpp index 606f749c..00d0a66f 100644 --- a/sp/src/game/server/mod/npc_shadow_walker.cpp +++ b/sp/src/game/server/mod/npc_shadow_walker.cpp @@ -34,8 +34,10 @@ //========================================================= enum { - SCHED_MELEE_ATTACK_NOINTERRUPT, - SCHED_HIDE + SCHED_MELEE_ATTACK_NOINTERRUPT = LAST_SHARED_SCHEDULE, + SCHED_HIDE, + + LAST_SHADOW_WALKER_SCHED }; //========================================================= @@ -110,7 +112,7 @@ private: LINK_ENTITY_TO_CLASS( npc_shadow_walker, CNPC_ShadowWalker ); -IMPLEMENT_CUSTOM_AI( npc_citizen,CNPC_ShadowWalker ); +//IMPLEMENT_CUSTOM_AI( npc_citizen,CNPC_ShadowWalker ); //--------------------------------------------------------- @@ -142,10 +144,12 @@ BEGIN_DATADESC(CNPC_ShadowWalker) DEFINE_INPUTFUNC(FIELD_VOID, "DisablePickupWeapons", InputDisablePickupWeapons) END_DATADESC() + +AI_BEGIN_CUSTOM_NPC(npc_shadow_walker, CNPC_ShadowWalker) //========================================================= // > Melee_Attack_NoInterrupt //========================================================= -AI_DEFINE_SCHEDULE +DEFINE_SCHEDULE ( SCHED_MELEE_ATTACK_NOINTERRUPT, @@ -163,7 +167,7 @@ AI_DEFINE_SCHEDULE //========================================================= // SCHED_HIDE //========================================================= -AI_DEFINE_SCHEDULE +DEFINE_SCHEDULE ( SCHED_HIDE, @@ -181,6 +185,9 @@ AI_DEFINE_SCHEDULE " COND_NEW_ENEMY" " COND_ENEMY_DEAD" ); +AI_END_CUSTOM_NPC() + + //--------------------------------------------------------- // Constants @@ -190,16 +197,6 @@ 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 : -// Output : -//----------------------------------------------------------------------------- -void CNPC_ShadowWalker::InitCustomSchedules(void) -{ - INIT_CUSTOM_AI(CNPC_ShadowWalker); -} - //----------------------------------------------------------------------------- // Purpose: Inner class for default weapon // TODO: Merge this with the Matt weapon in npc_citizen @@ -445,8 +442,9 @@ int CNPC_ShadowWalker::SelectIdleSchedule() // no valid route! Wander instead if (GetNavigator()->GetGoalType() == GOALTYPE_NONE) { nSched = SelectScheduleWander(); - if (nSched == SCHED_NONE) - return SCHED_IDLE_STAND; + if (nSched != SCHED_NONE) + return nSched; + return SCHED_IDLE_STAND; } // valid route. Get moving @@ -552,7 +550,7 @@ int CNPC_ShadowWalker::SelectCombatSchedule() } // Reloading conditions are necessary just in case for some reason somebody gives the Shadow Walker a gun - if (HasCondition(COND_LOW_PRIMARY_AMMO) || HasCondition(COND_NO_PRIMARY_AMMO)) + if (HasRangedWeapon() && (HasCondition(COND_LOW_PRIMARY_AMMO) || HasCondition(COND_NO_PRIMARY_AMMO))) { return SCHED_HIDE_AND_RELOAD; } From 9f999c4c87186dd1cb3ceaff147fb689bc4a0190 Mon Sep 17 00:00:00 2001 From: 1upD <1upderek@gmail.com> Date: Mon, 22 Oct 2018 20:59:32 -0400 Subject: [PATCH 32/36] Shadow Walker: Animation fixes. Deprecated weapon pickup in FGD; not working currently. --- sp/game/mod_episodic/halloween.fgd | 15 ++++---- sp/src/game/server/mod/npc_shadow_walker.cpp | 40 ++++++++++---------- 2 files changed, 29 insertions(+), 26 deletions(-) diff --git a/sp/game/mod_episodic/halloween.fgd b/sp/game/mod_episodic/halloween.fgd index ee5d5e27..319f5710 100644 --- a/sp/game/mod_episodic/halloween.fgd +++ b/sp/game/mod_episodic/halloween.fgd @@ -50,16 +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" - ] + // // Weapon pickup feature not working yet + // 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." + // 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." ] diff --git a/sp/src/game/server/mod/npc_shadow_walker.cpp b/sp/src/game/server/mod/npc_shadow_walker.cpp index 00d0a66f..eb0fa283 100644 --- a/sp/src/game/server/mod/npc_shadow_walker.cpp +++ b/sp/src/game/server/mod/npc_shadow_walker.cpp @@ -21,6 +21,8 @@ #include "entitylist.h" #include "activitylist.h" #include "ai_basenpc.h" +#include "ai_blended_movement.h" +#include "ai_behavior_actbusy.h" #include "engine/IEngineSound.h" #include "basehlcombatweapon_shared.h" #include "ai_squadslot.h" @@ -42,9 +44,11 @@ enum //========================================================= //========================================================= -class CNPC_ShadowWalker : public CAI_BaseNPC +typedef CAI_BlendingHost< CAI_BehaviorHost > CAI_CustomNPCBase; + +class CNPC_ShadowWalker : public CAI_CustomNPCBase { - DECLARE_CLASS( CNPC_ShadowWalker, CAI_BaseNPC ); + DECLARE_CLASS( CNPC_ShadowWalker, CAI_CustomNPCBase); public: void Precache( void ); @@ -57,9 +61,8 @@ public: virtual int SelectIdleSchedule(); virtual int SelectAlertSchedule(); virtual int SelectCombatSchedule(); - virtual bool CanPickkUpWeapons() { return m_bCanPickupWeapons; } virtual float GetSequenceGroundSpeed(CStudioHdr *pStudioHdr, int iSequence); - Activity NPC_TranslateActivity(Activity eNewActivity); + virtual Activity NPC_TranslateActivity(Activity eNewActivity); virtual int TranslateSchedule(int scheduleType); // Sounds @@ -360,7 +363,7 @@ int CNPC_ShadowWalker::SelectFailSchedule(int failedSchedule, int failedTask, AI //----------------------------------------------------------------------------- int CNPC_ShadowWalker::SelectScheduleRetrieveItem() { - if (HasCondition(COND_BETTER_WEAPON_AVAILABLE)) + if (m_bCanPickupWeapons && HasCondition(COND_BETTER_WEAPON_AVAILABLE)) { CBaseHLCombatWeapon *pWeapon = dynamic_cast(Weapon_FindUsable(WEAPON_SEARCH_DELTA)); if (pWeapon) @@ -433,11 +436,9 @@ int CNPC_ShadowWalker::SelectIdleSchedule() return SCHED_INVESTIGATE_SOUND; } - if (CanPickkUpWeapons()) { - nSched = SelectScheduleRetrieveItem(); - if (nSched != SCHED_NONE) - return nSched; - } + nSched = SelectScheduleRetrieveItem(); + if (nSched != SCHED_NONE) + return nSched; // no valid route! Wander instead if (GetNavigator()->GetGoalType() == GOALTYPE_NONE) { @@ -476,17 +477,16 @@ int CNPC_ShadowWalker::SelectAlertSchedule() return SCHED_INVESTIGATE_SOUND; } - if (CanPickkUpWeapons()) { - nSched = SelectScheduleRetrieveItem(); - if (nSched != SCHED_NONE) - return nSched; - } + nSched = SelectScheduleRetrieveItem(); + if (nSched != SCHED_NONE) + return nSched; // no valid route! Wander instead if (GetNavigator()->GetGoalType() == GOALTYPE_NONE) { nSched = SelectScheduleWander(); - if (nSched == SCHED_NONE) - return SCHED_IDLE_STAND; + if (nSched != SCHED_NONE) + return nSched; + return SCHED_IDLE_STAND; } // valid route. Get moving @@ -640,8 +640,6 @@ int CNPC_ShadowWalker::TranslateSchedule(int scheduleType) 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: @@ -650,6 +648,10 @@ Activity CNPC_ShadowWalker::NPC_TranslateActivity(Activity activity) return ACT_IDLE_ANGRY_SMG1; case ACT_RANGE_ATTACK_SHOTGUN_LOW: return ACT_RANGE_ATTACK_SMG1_LOW; + case ACT_IDLE_MELEE: + case ACT_IDLE_ANGRY_MELEE: // If the walker has a melee weapon but is in an idle state, don't raise the weapon + if (m_NPCState == NPC_STATE_IDLE) + return ACT_IDLE_SUITCASE; default: return BaseClass::NPC_TranslateActivity(activity); } From f39b752adcbc2bd2af18200e1953214bd2ef2886 Mon Sep 17 00:00:00 2001 From: Derek Dik Date: Tue, 23 Oct 2018 16:59:23 -0400 Subject: [PATCH 33/36] Shadow Walker: WIP fix for melee weapon model bug. Code refactor. --- sp/src/game/server/mod/npc_shadow_walker.cpp | 197 +------------------ sp/src/game/server/mod/npc_shadow_walker.h | 179 +++++++++++++++++ sp/src/game/server/mod/weapon_custom_melee.h | 28 +++ sp/src/game/server/server_episodic.vpc | 2 + 4 files changed, 218 insertions(+), 188 deletions(-) create mode 100644 sp/src/game/server/mod/npc_shadow_walker.h create mode 100644 sp/src/game/server/mod/weapon_custom_melee.h diff --git a/sp/src/game/server/mod/npc_shadow_walker.cpp b/sp/src/game/server/mod/npc_shadow_walker.cpp index eb0fa283..628772b6 100644 --- a/sp/src/game/server/mod/npc_shadow_walker.cpp +++ b/sp/src/game/server/mod/npc_shadow_walker.cpp @@ -11,187 +11,20 @@ // //=============================================================================// #include "cbase.h" -#include "ai_default.h" -#include "ai_task.h" -#include "ai_schedule.h" +#include "npc_shadow_walker.h" #include "ai_hull.h" #include "soundent.h" #include "game.h" #include "npcevent.h" -#include "entitylist.h" -#include "activitylist.h" -#include "ai_basenpc.h" -#include "ai_blended_movement.h" -#include "ai_behavior_actbusy.h" #include "engine/IEngineSound.h" #include "basehlcombatweapon_shared.h" #include "ai_squadslot.h" -#include "weapon_crowbar.h" +#include "weapon_custom_melee.h" + // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" -//========================================================= -// schedules -//========================================================= -enum -{ - SCHED_MELEE_ATTACK_NOINTERRUPT = LAST_SHARED_SCHEDULE, - SCHED_HIDE, - - LAST_SHADOW_WALKER_SCHED -}; - -//========================================================= -//========================================================= -typedef CAI_BlendingHost< CAI_BehaviorHost > CAI_CustomNPCBase; - -class CNPC_ShadowWalker : public CAI_CustomNPCBase -{ - DECLARE_CLASS( CNPC_ShadowWalker, CAI_CustomNPCBase); - -public: - void Precache( void ); - void Spawn( void ); - 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 float GetSequenceGroundSpeed(CStudioHdr *pStudioHdr, int iSequence); - virtual Activity NPC_TranslateActivity(Activity eNewActivity); - virtual int TranslateSchedule(int scheduleType); - - // Sounds - virtual void PlaySound(string_t soundname, bool optional); - virtual void DeathSound(const CTakeDamageInfo &info) { PlaySound(m_iszDeathSound, true); } - virtual void AlertSound(void) { PlaySound(m_iszAlertSound, false); }; - virtual void IdleSound(void) { PlaySound(m_iszIdleSound, false); }; - 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); - - void Activate(); - void FixupWeapon(); - - // Inputs - 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(); - - string_t m_iszWeaponModelName; // Path/filename of model to override weapon model. - - string_t m_iszFearSound; // Path/filename of WAV file to play. - string_t m_iszDeathSound; // Path/filename of WAV file to play. - string_t m_iszIdleSound; // Path/filename of WAV file to play. - string_t m_iszPainSound; // Path/filename of WAV file to play. - string_t m_iszAlertSound; // Path/filename of WAV file to play. - string_t m_iszLostEnemySound; // Path/filename of WAV file to play. - string_t m_iszFoundEnemySound; // Path/filename of WAV file to play. - - DEFINE_CUSTOM_AI; - -private: - bool HasRangedWeapon(); - void PrecacheNPCSoundScript(string_t* SoundName, string_t defaultSoundName); - - - 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 -}; - - -LINK_ENTITY_TO_CLASS( npc_shadow_walker, CNPC_ShadowWalker ); -//IMPLEMENT_CUSTOM_AI( npc_citizen,CNPC_ShadowWalker ); - - -//--------------------------------------------------------- -// Save/Restore -//--------------------------------------------------------- -BEGIN_DATADESC(CNPC_ShadowWalker) - DEFINE_KEYFIELD(m_iszWeaponModelName, FIELD_STRING, "WeaponModel"), - DEFINE_KEYFIELD(m_iHealth, FIELD_INTEGER, "Health"), - DEFINE_KEYFIELD(m_iszFearSound, FIELD_SOUNDNAME, "FearSound"), - DEFINE_KEYFIELD(m_iszDeathSound, FIELD_SOUNDNAME, "DeathSound"), - DEFINE_KEYFIELD(m_iszIdleSound, FIELD_SOUNDNAME, "IdleSound"), - DEFINE_KEYFIELD(m_iszPainSound, FIELD_SOUNDNAME, "PainSound"), - DEFINE_KEYFIELD(m_iszAlertSound, FIELD_SOUNDNAME, "AlertSound"), - DEFINE_KEYFIELD(m_iszLostEnemySound, FIELD_SOUNDNAME, "LostEnemySound"), - 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, "EnablePickupWeapons", InputEnablePickupWeapons), - DEFINE_INPUTFUNC(FIELD_VOID, "DisablePickupWeapons", InputDisablePickupWeapons) -END_DATADESC() - - -AI_BEGIN_CUSTOM_NPC(npc_shadow_walker, CNPC_ShadowWalker) -//========================================================= -// > Melee_Attack_NoInterrupt -//========================================================= -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 -//========================================================= -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" -); -AI_END_CUSTOM_NPC() - - - //--------------------------------------------------------- // Constants //--------------------------------------------------------- @@ -200,18 +33,6 @@ 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: Inner class for default weapon -// TODO: Merge this with the Matt weapon in npc_citizen -//----------------------------------------------------------------------------- -class CWeaponCustomMelee : public CWeaponCrowbar -{ - DECLARE_CLASS(CWeaponCustomMelee, CWeaponCrowbar); - - const char *GetWorldModel() const { return GetModelName().ToCStr(); } - void SetPickupTouch(void) { /* do nothing */ } -}; - //----------------------------------------------------------------------------- // Purpose: // @@ -281,11 +102,10 @@ void CNPC_ShadowWalker::Spawn( void ) if (!HasSpawnFlags(SF_NPC_START_EFFICIENT)) { - CapabilitiesAdd(bits_CAP_TURN_HEAD); + CapabilitiesAdd(bits_CAP_ANIMATEDFACE | bits_CAP_TURN_HEAD); // The default model has no face animations, but a custom model might CapabilitiesAdd(bits_CAP_SQUAD); CapabilitiesAdd(bits_CAP_USE_WEAPONS | bits_CAP_AIM_GUN | bits_CAP_MOVE_SHOOT); CapabilitiesAdd(bits_CAP_WEAPON_MELEE_ATTACK1 || bits_CAP_WEAPON_MELEE_ATTACK2); - CapabilitiesAdd(bits_CAP_INNATE_MELEE_ATTACK1 || bits_CAP_INNATE_MELEE_ATTACK2); CapabilitiesAdd(bits_CAP_DUCK); CapabilitiesAdd(bits_CAP_USE_SHOT_REGULATOR); @@ -307,21 +127,22 @@ void CNPC_ShadowWalker::FixupWeapon() // If no weapons supplied, give a crowbar CBaseCombatWeapon *pWeapon = GetActiveWeapon(); if (pWeapon == NULL) { - pWeapon = (CBaseCombatWeapon *)CREATE_UNSAVED_ENTITY(CWeaponCustomMelee, "weapon_crowbar"); + CWeaponCustomMelee * pMeleeWeapon = (CWeaponCustomMelee *)CREATE_UNSAVED_ENTITY(CWeaponCustomMelee, "weapon_crowbar"); // Apply weapon model override if (&m_iszWeaponModelName && m_iszWeaponModelName != MAKE_STRING("")) { - pWeapon->SetModel(STRING(m_iszWeaponModelName)); + pMeleeWeapon->m_iszWeaponModelName = this->m_iszWeaponModelName; } else { - pWeapon->SetModel("models/props_canal/mattpipe.mdl"); + pMeleeWeapon->m_iszWeaponModelName = MAKE_STRING("models/props_canal/mattpipe.mdl"); } + pWeapon = (CBaseCombatWeapon *)pMeleeWeapon; + DispatchSpawn(pWeapon); Weapon_Equip(pWeapon); } - } void CNPC_ShadowWalker::Activate() diff --git a/sp/src/game/server/mod/npc_shadow_walker.h b/sp/src/game/server/mod/npc_shadow_walker.h new file mode 100644 index 00000000..33bbbab6 --- /dev/null +++ b/sp/src/game/server/mod/npc_shadow_walker.h @@ -0,0 +1,179 @@ +//=//=============================================================================// +// +// Purpose: A malevolent being from a parallel universe which at one point +// may have been human. +// +// npc_shadow_walker is designed to be reusable as a generic horror +// game style npc. Its model and sound files may be configured through +// the hammer editor using keyfields. +// +// Author: 1upD +// +//=============================================================================// +#include "cbase.h" +#include "ai_default.h" +#include "ai_task.h" +#include "ai_schedule.h" +#include "entitylist.h" +#include "activitylist.h" +#include "ai_basenpc.h" +#include "ai_blended_movement.h" +#include "ai_behavior_actbusy.h" + +//========================================================= +// schedules +//========================================================= +enum +{ + SCHED_MELEE_ATTACK_NOINTERRUPT = LAST_SHARED_SCHEDULE, + SCHED_HIDE, + + LAST_SHADOW_WALKER_SCHED +}; + +//========================================================= +//========================================================= +typedef CAI_BlendingHost< CAI_BehaviorHost > CAI_CustomNPCBase; + +class CNPC_ShadowWalker : public CAI_CustomNPCBase +{ + DECLARE_CLASS(CNPC_ShadowWalker, CAI_CustomNPCBase); + +public: + void Precache(void); + void Spawn(void); + 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 float GetSequenceGroundSpeed(CStudioHdr *pStudioHdr, int iSequence); + virtual Activity NPC_TranslateActivity(Activity eNewActivity); + virtual int TranslateSchedule(int scheduleType); + + // Sounds + virtual void PlaySound(string_t soundname, bool optional); + virtual void DeathSound(const CTakeDamageInfo &info) { PlaySound(m_iszDeathSound, true); } + virtual void AlertSound(void) { PlaySound(m_iszAlertSound, false); }; + virtual void IdleSound(void) { PlaySound(m_iszIdleSound, false); }; + 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); + + void Activate(); + void FixupWeapon(); + + // Inputs + 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(); + + string_t m_iszWeaponModelName; // Path/filename of model to override weapon model. + + string_t m_iszFearSound; // Path/filename of WAV file to play. + string_t m_iszDeathSound; // Path/filename of WAV file to play. + string_t m_iszIdleSound; // Path/filename of WAV file to play. + string_t m_iszPainSound; // Path/filename of WAV file to play. + string_t m_iszAlertSound; // Path/filename of WAV file to play. + string_t m_iszLostEnemySound; // Path/filename of WAV file to play. + string_t m_iszFoundEnemySound; // Path/filename of WAV file to play. + + DEFINE_CUSTOM_AI; + +private: + bool HasRangedWeapon(); + void PrecacheNPCSoundScript(string_t* SoundName, string_t defaultSoundName); + + + 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 +}; + + +LINK_ENTITY_TO_CLASS(npc_shadow_walker, CNPC_ShadowWalker); + +//--------------------------------------------------------- +// Save/Restore +//--------------------------------------------------------- +BEGIN_DATADESC(CNPC_ShadowWalker) +DEFINE_KEYFIELD(m_iszWeaponModelName, FIELD_STRING, "WeaponModel"), +DEFINE_KEYFIELD(m_iHealth, FIELD_INTEGER, "Health"), +DEFINE_KEYFIELD(m_iszFearSound, FIELD_SOUNDNAME, "FearSound"), +DEFINE_KEYFIELD(m_iszDeathSound, FIELD_SOUNDNAME, "DeathSound"), +DEFINE_KEYFIELD(m_iszIdleSound, FIELD_SOUNDNAME, "IdleSound"), +DEFINE_KEYFIELD(m_iszPainSound, FIELD_SOUNDNAME, "PainSound"), +DEFINE_KEYFIELD(m_iszAlertSound, FIELD_SOUNDNAME, "AlertSound"), +DEFINE_KEYFIELD(m_iszLostEnemySound, FIELD_SOUNDNAME, "LostEnemySound"), +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, "EnablePickupWeapons", InputEnablePickupWeapons), +DEFINE_INPUTFUNC(FIELD_VOID, "DisablePickupWeapons", InputDisablePickupWeapons) +END_DATADESC() + + +AI_BEGIN_CUSTOM_NPC(npc_shadow_walker, CNPC_ShadowWalker) +//========================================================= +// > Melee_Attack_NoInterrupt +//========================================================= +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 +//========================================================= +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" +); +AI_END_CUSTOM_NPC() + diff --git a/sp/src/game/server/mod/weapon_custom_melee.h b/sp/src/game/server/mod/weapon_custom_melee.h new file mode 100644 index 00000000..acc70a92 --- /dev/null +++ b/sp/src/game/server/mod/weapon_custom_melee.h @@ -0,0 +1,28 @@ +//=//=============================================================================// +// +// Purpose: Melee weapon for custom NPCs. Its model can be overridden. +// +// Author: 1upD +// +//=============================================================================// +#include "cbase.h" +#include "basehlcombatweapon_shared.h" +#include "weapon_crowbar.h" + + +class CWeaponCustomMelee : public CWeaponCrowbar +{ + DECLARE_CLASS(CWeaponCustomMelee, CWeaponCrowbar); + + const char *GetWorldModel() const { return m_iszWeaponModelName.ToCStr(); } + void SetPickupTouch(void) { /* do nothing */ } +public: + string_t m_iszWeaponModelName; + + DECLARE_DATADESC(); + +}; + +BEGIN_DATADESC(CWeaponCustomMelee) +DEFINE_FIELD(m_iszWeaponModelName, FIELD_STRING) +END_DATADESC() diff --git a/sp/src/game/server/server_episodic.vpc b/sp/src/game/server/server_episodic.vpc index 4be25dfe..2b3c5c6f 100644 --- a/sp/src/game/server/server_episodic.vpc +++ b/sp/src/game/server/server_episodic.vpc @@ -53,7 +53,9 @@ $Project "Server (Episodic)" $Folder "Mod" { $File "mod\npc_lost_soul.cpp" + $File "mod\npc_shadow_walker.h" $File "mod\npc_shadow_walker.cpp" + $File "mod\weapon_custom_melee.h" } $File "$SRCDIR\game\shared\episodic\achievements_ep1.cpp" $File "$SRCDIR\game\shared\episodic\achievements_ep2.cpp" From 4368e27adf7f87e0f3d75e0ffcaab55c7b896a93 Mon Sep 17 00:00:00 2001 From: 1upD <1upderek@gmail.com> Date: Sun, 28 Oct 2018 22:41:12 -0400 Subject: [PATCH 34/36] Shadow Walker: Fixed bug that replaced custom weapons with crowbars upon loading a saved game --- .../scripts/weapon_custommelee.txt | 22 ++++++ .../mod_episodic/scripts/weapon_manifest.txt | 24 ++++++ sp/src/game/server/mod/npc_shadow_walker.cpp | 3 +- sp/src/game/server/mod/npc_shadow_walker.h | 74 +++++++++---------- sp/src/game/server/mod/weapon_custom_melee.h | 13 +++- 5 files changed, 95 insertions(+), 41 deletions(-) create mode 100644 sp/game/mod_episodic/scripts/weapon_custommelee.txt create mode 100644 sp/game/mod_episodic/scripts/weapon_manifest.txt diff --git a/sp/game/mod_episodic/scripts/weapon_custommelee.txt b/sp/game/mod_episodic/scripts/weapon_custommelee.txt new file mode 100644 index 00000000..5f190461 --- /dev/null +++ b/sp/game/mod_episodic/scripts/weapon_custommelee.txt @@ -0,0 +1,22 @@ +// Custom melee weapon - used by npc_shadow_walker for weapon model overrides +WeaponData +{ + // This will be replaced in code + "playermodel" "models/weapons/w_crowbar.mdl" + + "anim_prefix" "crowbar" + + "clip_size" "-1" + "primary_ammo" "None" + "secondary_ammo" "None" + + "weight" "0" + "item_flags" "0" + + // Sounds for the weapon. There is a max of 16 sounds per category (i.e. max 16 "single_shot" sounds) + SoundData + { + "melee_hit_world" "Weapon_Crowbar.Melee_HitWorld" + } + +} \ No newline at end of file diff --git a/sp/game/mod_episodic/scripts/weapon_manifest.txt b/sp/game/mod_episodic/scripts/weapon_manifest.txt new file mode 100644 index 00000000..0e6351bd --- /dev/null +++ b/sp/game/mod_episodic/scripts/weapon_manifest.txt @@ -0,0 +1,24 @@ +weapon_manifest +{ + "file" "scripts/weapon_357.txt" + "file" "scripts/weapon_alyxgun.txt" + "file" "scripts/weapon_annabelle.txt" + "file" "scripts/weapon_ar2.txt" + "file" "scripts/weapon_bugbait.txt" + "file" "scripts/weapon_citizenpackage.txt" + "file" "scripts/weapon_citizensuitcase.txt" + "file" "scripts/weapon_crossbow.txt" + "file" "scripts/weapon_crowbar.txt" + "file" "scripts/weapon_cubemap.txt" + "file" "scripts/weapon_frag.txt" + "file" "scripts/weapon_physcannon.txt" + "file" "scripts/weapon_physgun.txt" + "file" "scripts/weapon_pistol.txt" + "file" "scripts/weapon_rpg.txt" + "file" "scripts/weapon_shotgun.txt" + "file" "scripts/weapon_smg1.txt" + "file" "scripts/weapon_stunstick.txt" + + // Custom + "file" "scripts/weapon_custommelee.txt" +} \ No newline at end of file diff --git a/sp/src/game/server/mod/npc_shadow_walker.cpp b/sp/src/game/server/mod/npc_shadow_walker.cpp index 628772b6..7dfe11cd 100644 --- a/sp/src/game/server/mod/npc_shadow_walker.cpp +++ b/sp/src/game/server/mod/npc_shadow_walker.cpp @@ -127,12 +127,13 @@ void CNPC_ShadowWalker::FixupWeapon() // If no weapons supplied, give a crowbar CBaseCombatWeapon *pWeapon = GetActiveWeapon(); if (pWeapon == NULL) { - CWeaponCustomMelee * pMeleeWeapon = (CWeaponCustomMelee *)CREATE_UNSAVED_ENTITY(CWeaponCustomMelee, "weapon_crowbar"); + CWeaponCustomMelee *pMeleeWeapon = (CWeaponCustomMelee*)CreateEntityByName("weapon_custommelee"); // Apply weapon model override if (&m_iszWeaponModelName && m_iszWeaponModelName != MAKE_STRING("")) { pMeleeWeapon->m_iszWeaponModelName = this->m_iszWeaponModelName; } + // Default custom weapon model else { pMeleeWeapon->m_iszWeaponModelName = MAKE_STRING("models/props_canal/mattpipe.mdl"); } diff --git a/sp/src/game/server/mod/npc_shadow_walker.h b/sp/src/game/server/mod/npc_shadow_walker.h index 33bbbab6..82f8bea9 100644 --- a/sp/src/game/server/mod/npc_shadow_walker.h +++ b/sp/src/game/server/mod/npc_shadow_walker.h @@ -40,19 +40,19 @@ class CNPC_ShadowWalker : public CAI_CustomNPCBase DECLARE_CLASS(CNPC_ShadowWalker, CAI_CustomNPCBase); public: - void Precache(void); - void Spawn(void); - 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 float GetSequenceGroundSpeed(CStudioHdr *pStudioHdr, int iSequence); - virtual Activity NPC_TranslateActivity(Activity eNewActivity); - virtual int TranslateSchedule(int scheduleType); + void Precache(void); + void Spawn(void); + 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 float GetSequenceGroundSpeed(CStudioHdr *pStudioHdr, int iSequence); + virtual Activity NPC_TranslateActivity(Activity eNewActivity); + virtual int TranslateSchedule(int scheduleType); // Sounds virtual void PlaySound(string_t soundname, bool optional); @@ -64,42 +64,42 @@ public: virtual void LostEnemySound(void) { PlaySound(m_iszLostEnemySound, false); }; virtual void FoundEnemySound(void); - void Activate(); - void FixupWeapon(); + void Activate(); + void FixupWeapon(); // Inputs - 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); + 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(); - string_t m_iszWeaponModelName; // Path/filename of model to override weapon model. + string_t m_iszWeaponModelName; // Path/filename of model to override weapon model. - string_t m_iszFearSound; // Path/filename of WAV file to play. - string_t m_iszDeathSound; // Path/filename of WAV file to play. - string_t m_iszIdleSound; // Path/filename of WAV file to play. - string_t m_iszPainSound; // Path/filename of WAV file to play. - string_t m_iszAlertSound; // Path/filename of WAV file to play. - string_t m_iszLostEnemySound; // Path/filename of WAV file to play. - string_t m_iszFoundEnemySound; // Path/filename of WAV file to play. + string_t m_iszFearSound; // Path/filename of WAV file to play. + string_t m_iszDeathSound; // Path/filename of WAV file to play. + string_t m_iszIdleSound; // Path/filename of WAV file to play. + string_t m_iszPainSound; // Path/filename of WAV file to play. + string_t m_iszAlertSound; // Path/filename of WAV file to play. + string_t m_iszLostEnemySound; // Path/filename of WAV file to play. + string_t m_iszFoundEnemySound; // Path/filename of WAV file to play. DEFINE_CUSTOM_AI; private: - bool HasRangedWeapon(); - void PrecacheNPCSoundScript(string_t* SoundName, string_t defaultSoundName); + bool HasRangedWeapon(); + void PrecacheNPCSoundScript(string_t* SoundName, string_t defaultSoundName); - 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 + 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 }; diff --git a/sp/src/game/server/mod/weapon_custom_melee.h b/sp/src/game/server/mod/weapon_custom_melee.h index acc70a92..37cfe6ec 100644 --- a/sp/src/game/server/mod/weapon_custom_melee.h +++ b/sp/src/game/server/mod/weapon_custom_melee.h @@ -15,14 +15,21 @@ class CWeaponCustomMelee : public CWeaponCrowbar DECLARE_CLASS(CWeaponCustomMelee, CWeaponCrowbar); const char *GetWorldModel() const { return m_iszWeaponModelName.ToCStr(); } - void SetPickupTouch(void) { /* do nothing */ } + virtual void Drop(const Vector &vecVelocity); public: string_t m_iszWeaponModelName; DECLARE_DATADESC(); - }; +void CWeaponCustomMelee::Drop(const Vector &vecVelocity) +{ + BaseClass::Drop(vecVelocity); + AddSpawnFlags(SF_WEAPON_NO_PLAYER_PICKUP); +} + +LINK_ENTITY_TO_CLASS(weapon_custommelee, CWeaponCustomMelee); + BEGIN_DATADESC(CWeaponCustomMelee) -DEFINE_FIELD(m_iszWeaponModelName, FIELD_STRING) + DEFINE_FIELD(m_iszWeaponModelName, FIELD_STRING) END_DATADESC() From 35f875da9be69667a10f4ce578d8cb5186e24b4f Mon Sep 17 00:00:00 2001 From: 1upD <1upderek@gmail.com> Date: Mon, 29 Oct 2018 14:40:35 -0400 Subject: [PATCH 35/36] Shadow Walker: Fixed bug where custom melee weapon would be seen as a ranged weapon during schedule selection. --- sp/src/game/server/mod/npc_shadow_walker.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sp/src/game/server/mod/npc_shadow_walker.cpp b/sp/src/game/server/mod/npc_shadow_walker.cpp index 7dfe11cd..b17b4222 100644 --- a/sp/src/game/server/mod/npc_shadow_walker.cpp +++ b/sp/src/game/server/mod/npc_shadow_walker.cpp @@ -435,7 +435,7 @@ bool CNPC_ShadowWalker::HasRangedWeapon() CBaseCombatWeapon *pWeapon = GetActiveWeapon(); if (pWeapon) - return !(FClassnameIs(pWeapon, "weapon_crowbar") || FClassnameIs(pWeapon, "weapon_stunstick")); + return !(FClassnameIs(pWeapon, "weapon_crowbar") || FClassnameIs(pWeapon, "weapon_stunstick") || FClassnameIs(pWeapon, "weapon_custommelee")); return false; } From c1c4f09a7b684ad3d530e8a807176342a36a77eb Mon Sep 17 00:00:00 2001 From: 1upD <1upderek@gmail.com> Date: Tue, 23 Oct 2018 22:13:17 -0400 Subject: [PATCH 36/36] Shadow Walker: If SCHED_INVESTIGATE_SOUND fails, default to SCHED_IDLE_WANDER --- sp/src/game/server/mod/npc_shadow_walker.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sp/src/game/server/mod/npc_shadow_walker.cpp b/sp/src/game/server/mod/npc_shadow_walker.cpp index b17b4222..730064ec 100644 --- a/sp/src/game/server/mod/npc_shadow_walker.cpp +++ b/sp/src/game/server/mod/npc_shadow_walker.cpp @@ -175,6 +175,9 @@ int CNPC_ShadowWalker::SelectFailSchedule(int failedSchedule, int failedTask, AI case SCHED_RUN_FROM_ENEMY: // I can't run away, so I will just run randomly! return SCHED_RUN_RANDOM; + case SCHED_INVESTIGATE_SOUND: + // I can't investigate a sound I heard. + return SCHED_IDLE_WANDER; } return BaseClass::SelectFailSchedule(failedSchedule, failedTask, taskFailCode);