Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
190 changes: 187 additions & 3 deletions sp/src/game/server/hl2/proto_sniper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,26 @@ ConVar showsniperdist("showsniperdist", "0" );
ConVar sniperspeak( "sniperspeak", "0" );
ConVar sniper_xbox_delay( "sniper_xbox_delay", "1" );

#ifdef MAPBASE
// ConVars for configuring Unhidden Snipers
ConVar npc_sniper_riflemodel("npc_sniper_riflemodel", "models/weapons/w_irifle.mdl");
ConVar npc_sniper_rifle_offset_x("npc_sniper_rifle_offset_x", "10.5");
ConVar npc_sniper_rifle_offset_y("npc_sniper_rifle_offset_y", "-2.5");
ConVar npc_sniper_rifle_offset_z("npc_sniper_rifle_offset_z", "45.0");
ConVar npc_sniper_rifle_despawn_t("npc_sniper_rifle_despawn_t", "5");
ConVar npc_sniper_rifle_use_attachment("npc_sniper_rifle_use_attachment", "1");
ConVar npc_sniper_rifle_attachment_point("npc_sniper_rifle_attachment_point", "lefthand");

ConVar npc_sniper_rifle_offset_x_attachment("npc_sniper_rifle_offset_x_attachment", "-3.5");
ConVar npc_sniper_rifle_offset_y_attachment("npc_sniper_rifle_offset_y_attachment", "-7.5");
ConVar npc_sniper_rifle_offset_z_attachment("npc_sniper_rifle_offset_z_attachment", "0");

ConVar npc_sniper_use_aiming_anims("npc_sniper_use_aiming_anims", "1");
ConVar npc_sniper_should_rotate_body("npc_sniper_should_rotate_body", "1");

ConVar sk_npc_sniper_easy_delay("sk_npc_sniper_easy_delay", "5.0");
#endif

// Moved to HL2_SharedGameRules because these are referenced by shared AmmoDef functions
extern ConVar sk_dmg_sniper_penetrate_plr;
extern ConVar sk_dmg_sniper_penetrate_npc;
Expand All @@ -71,6 +91,7 @@ extern ConVar sk_dmg_sniper_penetrate_npc;
#define SF_SNIPER_NOSWEEP (1 << 21) ///< This sniper doesn't sweep to the target or use decoys.
#ifdef MAPBASE
#define SF_SNIPER_DIE_ON_FIRE (1 << 22) // This sniper dies on fire.
#define SF_SNIPER_DELAY_FIRE_ON_EASY (1 << 23) // This sniper will delay fire by 5 seconds on Easy.
#endif

// If the last time I fired at someone was between 0 and this many seconds, draw
Expand Down Expand Up @@ -407,6 +428,10 @@ class CProtoSniper : public CAI_BaseNPC
#endif

COutputEvent m_OnShotFired;

#ifdef MAPBASE
CBaseEntity* m_FakeRifle;
#endif

DEFINE_CUSTOM_AI;

Expand Down Expand Up @@ -755,7 +780,31 @@ void CProtoSniper::LaserOn( const Vector &vecTarget, const Vector &vecDeviance )

// The beam is backwards, sortof. The endpoint is the sniper. This is
// so that the beam can be tapered to very thin where it emits from the sniper.
m_pBeam->PointsInit( vecInitialAim, GetBulletOrigin() );
#ifndef MAPBASE
m_pBeam->PointsInit(vecInitialAim, GetBulletOrigin());
#endif
#ifdef MAPBASE
if (HasSpawnFlags(SF_SNIPER_HIDDEN))
{
m_pBeam->PointsInit(vecInitialAim, GetBulletOrigin());
}
else
{
// Try to make the laser emit from the rifle or the NPC origin
if (GetNavigator() != NULL && GetNavigator()->IsGoalActive())
{
vecInitialAim = vecTarget;
}
if (m_FakeRifle != NULL)
{
m_pBeam->PointsInit(vecInitialAim, m_FakeRifle->GetAbsOrigin());
}
else
{
m_pBeam->PointsInit(vecInitialAim, GetAbsOrigin());
}
}
#endif
m_pBeam->SetBrightness( 255 );
m_pBeam->SetNoise( 0 );
m_pBeam->SetWidth( 1.0f );
Expand Down Expand Up @@ -1107,6 +1156,57 @@ void CProtoSniper::Spawn( void )
AddEffects( EF_NODRAW );
AddSolidFlags( FSOLID_NOT_SOLID );
}
#ifdef MAPBASE
else
{
m_FakeRifle = CreateEntityByName("prop_dynamic_ornament");
if (m_FakeRifle)
{
bool shouldUseAimingVariables = false;
Vector offset;
if (npc_sniper_rifle_use_attachment.GetBool())
{
int attachment = LookupAttachment(npc_sniper_rifle_attachment_point.GetString());
{
if (attachment)
{
offset.Init(npc_sniper_rifle_offset_x_attachment.GetFloat(), npc_sniper_rifle_offset_y_attachment.GetFloat(), npc_sniper_rifle_offset_z_attachment.GetFloat());
m_FakeRifle->SetParent(this, attachment);
shouldUseAimingVariables = true;
}
else
{
Warning("Failed to attach to attachment, attachment doesn't exist.\n");
offset.Init(npc_sniper_rifle_offset_x.GetFloat(), npc_sniper_rifle_offset_y.GetFloat(), npc_sniper_rifle_offset_z.GetFloat());
m_FakeRifle->SetParent(this);
}
}
}
else
{
offset.Init(npc_sniper_rifle_offset_x.GetFloat(), npc_sniper_rifle_offset_y.GetFloat(), npc_sniper_rifle_offset_z.GetFloat());
m_FakeRifle->SetParent(this);
}
m_FakeRifle->SetLocalOrigin(offset);
m_FakeRifle->SetModel(npc_sniper_riflemodel.GetString());
m_FakeRifle->SetOwnerEntity(this);
m_FakeRifle->Spawn();
m_FakeRifle->Activate();

m_bloodColor = BLOOD_COLOR_RED;
SetMoveType(MOVETYPE_STEP);
CapabilitiesAdd(bits_CAP_ANIMATEDFACE);
CapabilitiesAdd(bits_CAP_DOORS_GROUP);
CapabilitiesAdd(bits_CAP_FRIENDLY_DMG_IMMUNE);
CapabilitiesAdd(bits_CAP_MOVE_GROUND);
if (npc_sniper_use_aiming_anims.GetBool())
{
CapabilitiesAdd(bits_CAP_AIM_GUN);
}
CapabilitiesAdd(bits_CAP_TURN_HEAD);
}
}
#endif

// Point the cursor straight ahead so that the sniper's
// first sweep of the laser doesn't look weird.
Expand Down Expand Up @@ -1420,6 +1520,14 @@ int CProtoSniper::OnTakeDamage_Alive( const CTakeDamageInfo &info )
if ( info.GetDamageType() == DMG_GENERIC && info.GetInflictor() == this )
return CAI_BaseNPC::OnTakeDamage_Alive( newInfo );

#ifdef MAPBASE
// Allow unhidden snipers to be damaged by normal means
if (!HasSpawnFlags(SF_SNIPER_HIDDEN))
{
return CAI_BaseNPC::OnTakeDamage_Alive(newInfo);
}
#endif

if( !(info.GetDamageType() & (DMG_BLAST|DMG_BURN) ) )
{
// Only blasts and burning hurt
Expand Down Expand Up @@ -1461,6 +1569,52 @@ void CProtoSniper::Event_Killed( const CTakeDamageInfo &info )
{
if( !(m_spawnflags & SF_SNIPER_NOCORPSE) )
{
#ifdef MAPBASE
// If we're not hidden, spawn a physics version of our rifle
if (!HasSpawnFlags(SF_SNIPER_HIDDEN))
{
// Clear the rifle
if (m_FakeRifle != NULL)
{
m_FakeRifle->SetParent(NULL);
UTIL_Remove(m_FakeRifle);
m_FakeRifle = NULL;
}

CBaseEntity* pGunProp = CreateEntityByName("prop_physics");
if (pGunProp)
{
Vector offset;
offset.Init(npc_sniper_rifle_offset_x.GetFloat(), npc_sniper_rifle_offset_y.GetFloat(), npc_sniper_rifle_offset_z.GetFloat());
pGunProp->SetModel(npc_sniper_riflemodel.GetString());
pGunProp->SetAbsOrigin(GetAbsOrigin() + offset);
pGunProp->SetAbsAngles(GetAbsAngles());
pGunProp->SetCollisionGroup(COLLISION_GROUP_WEAPON);
pGunProp->Spawn();
pGunProp->Activate();

variant_t variant;
variant.SetFloat(npc_sniper_rifle_despawn_t.GetFloat());
pGunProp->AcceptInput("KillWhenNotVisible", NULL, NULL, variant, -1);

IPhysicsObject* pPhys = pGunProp->VPhysicsGetObject();
if (pPhys)
{
// Add an extra push in a random direction
Vector vel = RandomVector(-64.0f, 64.0f);
AngularImpulse angImp = RandomAngularImpulse(-300.0f, 300.0f);
vel[2] = 0.0f;
pPhys->AddVelocity(&vel, &angImp);
}
else
{
// taken from DropItem code
pGunProp->ApplyAbsVelocityImpulse(GetAbsVelocity());
pGunProp->ApplyLocalAngularVelocityImpulse(AngularImpulse(0, random->RandomFloat(0, 100), 0));
}
}
#endif
}
#ifdef MAPBASE
Vector vecForce;

Expand Down Expand Up @@ -2056,9 +2210,21 @@ Activity CProtoSniper::NPC_TranslateActivity( Activity eNewActivity )
{
// ACT_IDLE is now just the soldier's unarmed idle animation.
// Use a gun-holding animation like what unhidden snipers were using before.
if (!HasSpawnFlags( SF_SNIPER_HIDDEN ) && eNewActivity == ACT_IDLE)
if (!HasSpawnFlags( SF_SNIPER_HIDDEN ))
{
eNewActivity = ACT_IDLE_SMG1;
if (eNewActivity == ACT_IDLE)
{
// Now uses aiming animations for snipers that don't have Hidden spawnflag
eNewActivity = ACT_IDLE_ANGRY_SMG1; // ACT_IDLE_SMG1
}
else if (eNewActivity == ACT_WALK)
{
eNewActivity = ACT_WALK_AIM_RIFLE;
}
else if (eNewActivity == ACT_RUN)
{
eNewActivity = ACT_RUN_AIM_RIFLE;
}
}

return BaseClass::NPC_TranslateActivity( eNewActivity );
Expand Down Expand Up @@ -2246,6 +2412,14 @@ void CProtoSniper::StartTask( const Task_t *pTask )
delay += sniper_xbox_delay.GetFloat();
#endif

#ifdef MAPBASE
// Add a delay on easy difficulty
if (g_pGameRules->IsSkillLevel(SKILL_EASY) && !HasSpawnFlags(SF_SNIPER_HIDDEN) && HasSpawnFlags(SF_SNIPER_DELAY_FIRE_ON_EASY))
{
delay += sk_npc_sniper_easy_delay.GetFloat();
}
#endif

if( gpGlobals->curtime - m_flTimeLastAttackedPlayer <= SNIPER_FASTER_ATTACK_PERIOD )
{
SetWait( SNIPER_SUBSEQUENT_PAINT_TIME + delay );
Expand Down Expand Up @@ -2605,6 +2779,16 @@ void CProtoSniper::PrescheduleThink( void )
// If the enemy has been out of sight for a full second, mark him eluded.
if( GetEnemy() != NULL )
{
#ifdef MAPBASE
if (npc_sniper_should_rotate_body.GetBool() && !HasSpawnFlags(SF_SNIPER_HIDDEN))
{
// Rotate and face the target
Vector posToLookAt = GetEnemy()->GetAbsOrigin() - GetAbsOrigin();
QAngle angles;
VectorAngles(posToLookAt, angles);
SetAbsAngles(QAngle(0, angles.y, 0));
}
#endif
if( gpGlobals->curtime - GetEnemies()->LastTimeSeen( GetEnemy() ) > 30 )
{
// Stop pestering enemies after 30 seconds of frustration.
Expand Down