Lost Soul: Added new NPC based on manhack. Inspired by Doom

This commit is contained in:
1upD 2018-09-27 17:05:14 -04:00
parent 86bb8f7c7e
commit 4e56c1205b
2 changed files with 390 additions and 19 deletions

View File

@ -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<CBreakableProp*>(pHitEntity)) {
dynamic_cast<CBreakableProp*>(pHitEntity)->Ignite(10.0f, false);
}
else if (pHitEntity->IsNPC())
{
dynamic_cast<CAI_BaseNPC*>(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;
}

View File

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