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
170 changes: 167 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,24 @@ ConVar showsniperdist("showsniperdist", "0" );
ConVar sniperspeak( "sniperspeak", "0" );
ConVar sniper_xbox_delay( "sniper_xbox_delay", "1" );

// ConVars for configuring Unhidden Snipers
ConVar sniper_riflemodel("sniper_riflemodel", "models/weapons/w_irifle.mdl"); //models/weapons/w_snip_sg550.mdl
ConVar sniperrifle_offset_x("sniperrifle_offset_x", "10.5");
ConVar sniperrifle_offset_y("sniperrifle_offset_y", "-2.5");
ConVar sniperrifle_offset_z("sniperrifle_offset_z", "45.0");
ConVar sniperrifle_despawn_t("sniperrifle_despawn_t", "5");
ConVar sniperrifle_use_attachment("sniperrifle_use_attachment", "1");
ConVar sniperrifle_attachment_point("sniperrifle_attachment_point", "lefthand");

ConVar sniperrifle_offset_x_attachment("sniperrifle_offset_x_attachment", "-3.5");
ConVar sniperrifle_offset_y_attachment("sniperrifle_offset_y_attachment", "-7.5");
ConVar sniperrifle_offset_z_attachment("sniperrifle_offset_z_attachment", "0");

ConVar sv_npc_sniper_use_aiming_anims("sv_npc_sniper_use_aiming_anims", "1");
ConVar sv_npc_sniper_should_rotate_body("sv_npc_sniper_should_rotate_body", "1");

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

// 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 Down Expand Up @@ -407,6 +425,8 @@ class CProtoSniper : public CAI_BaseNPC
#endif

COutputEvent m_OnShotFired;

CBaseEntity* m_FakeRifle;

DEFINE_CUSTOM_AI;

Expand Down Expand Up @@ -755,7 +775,26 @@ 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() );
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());
}
}
m_pBeam->SetBrightness( 255 );
m_pBeam->SetNoise( 0 );
m_pBeam->SetWidth( 1.0f );
Expand Down Expand Up @@ -1107,6 +1146,56 @@ void CProtoSniper::Spawn( void )
AddEffects( EF_NODRAW );
AddSolidFlags( FSOLID_NOT_SOLID );
}
else
{
m_FakeRifle = CreateEntityByName("prop_dynamic_override");
if (m_FakeRifle)
{
bool shouldUseAimingVariables = false;
Vector offset;
if (sniperrifle_use_attachment.GetBool())
{
int attachment = LookupAttachment(sniperrifle_attachment_point.GetString());
{
if (attachment)
{
offset.Init(sniperrifle_offset_x_attachment.GetFloat(), sniperrifle_offset_y_attachment.GetFloat(), sniperrifle_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(sniperrifle_offset_x.GetFloat(), sniperrifle_offset_y.GetFloat(), sniperrifle_offset_z.GetFloat());
m_FakeRifle->SetParent(this);
}
}
}
else
{
offset.Init(sniperrifle_offset_x.GetFloat(), sniperrifle_offset_y.GetFloat(), sniperrifle_offset_z.GetFloat());
m_FakeRifle->SetParent(this);
}
m_FakeRifle->SetLocalOrigin(offset);
m_FakeRifle->SetModel(sniper_riflemodel.GetString());
m_FakeRifle->SetCollisionGroup(COLLISION_GROUP_WEAPON);
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 (sv_npc_sniper_use_aiming_anims.GetBool())
{
CapabilitiesAdd(bits_CAP_AIM_GUN);
}
CapabilitiesAdd(bits_CAP_TURN_HEAD);
}
}

// 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 +1509,12 @@ int CProtoSniper::OnTakeDamage_Alive( const CTakeDamageInfo &info )
if ( info.GetDamageType() == DMG_GENERIC && info.GetInflictor() == this )
return CAI_BaseNPC::OnTakeDamage_Alive( newInfo );

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

if( !(info.GetDamageType() & (DMG_BLAST|DMG_BURN) ) )
{
// Only blasts and burning hurt
Expand Down Expand Up @@ -1461,6 +1556,50 @@ void CProtoSniper::Event_Killed( const CTakeDamageInfo &info )
{
if( !(m_spawnflags & SF_SNIPER_NOCORPSE) )
{
// 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(sniperrifle_offset_x.GetFloat(), sniperrifle_offset_y.GetFloat(), sniperrifle_offset_z.GetFloat());
pGunProp->SetModel(sniper_riflemodel.GetString());
pGunProp->SetAbsOrigin(GetAbsOrigin() + offset);
pGunProp->SetAbsAngles(GetAbsAngles());
pGunProp->SetCollisionGroup(COLLISION_GROUP_WEAPON);
pGunProp->Spawn();
pGunProp->Activate();

variant_t variant;
variant.SetFloat(sniperrifle_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));
}
}
}
#ifdef MAPBASE
Vector vecForce;

Expand Down Expand Up @@ -2056,9 +2195,20 @@ 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)
{
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 +2396,12 @@ void CProtoSniper::StartTask( const Task_t *pTask )
delay += sniper_xbox_delay.GetFloat();
#endif

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

if( gpGlobals->curtime - m_flTimeLastAttackedPlayer <= SNIPER_FASTER_ATTACK_PERIOD )
{
SetWait( SNIPER_SUBSEQUENT_PAINT_TIME + delay );
Expand Down Expand Up @@ -2605,6 +2761,14 @@ void CProtoSniper::PrescheduleThink( void )
// If the enemy has been out of sight for a full second, mark him eluded.
if( GetEnemy() != NULL )
{
if (sv_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));
}
if( gpGlobals->curtime - GetEnemies()->LastTimeSeen( GetEnemy() ) > 30 )
{
// Stop pestering enemies after 30 seconds of frustration.
Expand Down