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] 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)) {