From cd0e28488bf70a6298b94af0804d78ac36b3af7e Mon Sep 17 00:00:00 2001
From: Roman Chistokhodov <freeslave93@gmail.com>
Date: Thu, 28 Sep 2017 13:19:16 +0300
Subject: [PATCH] Fix shock effect (#9)

* Fix shock effect

* Additional fixes for shock beam
---
 dlls/combat.cpp              |   4 +-
 dlls/gamerules.cpp           |   4 +-
 dlls/gearbox/shock.cpp       | 220 ++++++++++++++++++-----------------
 dlls/gearbox/shockrifle.cpp  |  16 ++-
 dlls/gearbox/strooper.cpp    |   5 +-
 dlls/monsters.cpp            |   5 +-
 dlls/monsterstate.cpp        |   2 -
 dlls/multiplay_gamerules.cpp |   4 +-
 dlls/skill.h                 |   4 +-
 9 files changed, 138 insertions(+), 126 deletions(-)

diff --git a/dlls/combat.cpp b/dlls/combat.cpp
index d5eb9c7a..1b573243 100644
--- a/dlls/combat.cpp
+++ b/dlls/combat.cpp
@@ -910,7 +910,7 @@ int CBaseMonster::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, f
 		return 0;
 	}
 
-	if( ( bitsDamageType & DMG_ENERGYBEAM ) && FClassnameIs( pevAttacker, "shock" ) )
+	if( ( bitsDamageType & DMG_ENERGYBEAM ) && FClassnameIs( pevAttacker, "shock_beam" ) )
 	{
 		GlowShellOn( Vector( 0, 220, 255 ), .5f );
 	}
@@ -986,7 +986,7 @@ int CBaseMonster::DeadTakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacke
 	}
 #endif
 	// kill the corpse if enough damage was done to destroy the corpse and the damage is of a type that is allowed to destroy the corpse.
-	if( (bitsDamageType & DMG_GIB_CORPSE) || (( bitsDamageType & DMG_ENERGYBEAM ) && FClassnameIs( pevAttacker, "shock" )) )
+	if( (bitsDamageType & DMG_GIB_CORPSE) || (( bitsDamageType & DMG_ENERGYBEAM ) && FClassnameIs( pevInflictor, "shock_beam" )) )
 	{
 		if( pev->health <= flDamage )
 		{
diff --git a/dlls/gamerules.cpp b/dlls/gamerules.cpp
index 725a93ba..909244e8 100644
--- a/dlls/gamerules.cpp
+++ b/dlls/gamerules.cpp
@@ -376,7 +376,7 @@ void CGameRules::RefreshSkillData ( void )
 	gSkillData.plrDmgGrapple = GetSkillCvar( "sk_plr_grapple" );
 	gSkillData.plrDmgEagle = GetSkillCvar( "sk_plr_eagle" );
 	gSkillData.plrDmgDisplacer = GetSkillCvar( "sk_plr_displacer_self" );
-	gSkillData.plrDmgShockroach = GetSkillCvar( "sk_plr_shockroachs" );
+	gSkillData.plrDmgShockroachSingleplayer = GetSkillCvar( "sk_plr_shockroachs" );
 	gSkillData.plrDmgSpore = GetSkillCvar( "sk_plr_spore" );
 	gSkillData.plrDmg762 = GetSkillCvar( "sk_plr_762_bullet" );
 	gSkillData.plrDmg556 = GetSkillCvar( "sk_plr_556_bullet" );
@@ -384,7 +384,7 @@ void CGameRules::RefreshSkillData ( void )
 	gSkillData.monDmg762 = GetSkillCvar( "sk_plr_762_bullet" );
 	gSkillData.monDmg556 = GetSkillCvar( "sk_plr_556_bullet" );
 	gSkillData.monDmgDisplacer = GetSkillCvar( "sk_plr_displacer_other" );
-	gSkillData.monDmgShockroach = GetSkillCvar( "sk_plr_shockroachm" );
+	gSkillData.plrDmgShockroachMultiplayer = GetSkillCvar( "sk_plr_shockroachm" );
 
 	// HEALTH/CHARGE
 	gSkillData.suitchargerCapacity = GetSkillCvar( "sk_suitcharger" );
diff --git a/dlls/gearbox/shock.cpp b/dlls/gearbox/shock.cpp
index e596436d..40c34f20 100644
--- a/dlls/gearbox/shock.cpp
+++ b/dlls/gearbox/shock.cpp
@@ -28,87 +28,93 @@
 #include	"soundent.h"
 #include	"game.h"
 #include	"weapons.h"
-
-#define SHOCK_BEAM_LENGTH		64
-#define SHOCK_BEAM_LENGTH_HALF	SHOCK_BEAM_LENGTH * 0.5f
-
-#define SHOCK_BEAM_WIDTH		50
+#include	"gamerules.h"
+#include	"customentity.h"
 
 //=========================================================
 // Shockrifle projectile
 //=========================================================
-class CShock : public CBaseEntity
+class CShock : public CBaseAnimating
 {
 public:
 	void Spawn(void);
+	void Precache();
 
-	static void Shoot(entvars_t *pevOwner, Vector vecStart, Vector vecVelocity);
+	static void Shoot(entvars_t *pevOwner, const Vector angles, const Vector vecStart, const Vector vecVelocity);
 	void Touch(CBaseEntity *pOther);
-	void EXPORT ShockThink(void);
+	void EXPORT FlyThink();
 
 	virtual int		Save(CSave &save);
 	virtual int		Restore(CRestore &restore);
 	static	TYPEDESCRIPTION m_SaveData[];
 
-	void CreateBeam(const Vector& start, const Vector& end, int width);
-	void ClearBeam();
-	void UpdateBeam(const Vector& start, const Vector& end);
-	void ComputeBeamPositions(Vector* pos1, Vector* pos2);
+	void CreateEffects();
+	void ClearEffects();
 
 	CBeam *m_pBeam;
-	Vector m_vecBeamStart, m_vecBeamEnd;
+	CBeam *m_pNoise;
+	CSprite *m_pSprite;
 };
 
-LINK_ENTITY_TO_CLASS(shock, CShock);
+LINK_ENTITY_TO_CLASS(shock_beam, CShock)
 
 TYPEDESCRIPTION	CShock::m_SaveData[] =
 {
-	DEFINE_ARRAY(CShock, m_pBeam, FIELD_CLASSPTR, 1),
-	DEFINE_FIELD(CShock, m_vecBeamStart, FIELD_POSITION_VECTOR),
-	DEFINE_FIELD(CShock, m_vecBeamEnd, FIELD_POSITION_VECTOR),
+	DEFINE_FIELD(CShock, m_pBeam, FIELD_CLASSPTR),
+	DEFINE_FIELD(CShock, m_pNoise, FIELD_CLASSPTR),
+	DEFINE_FIELD(CShock, m_pSprite, FIELD_CLASSPTR),
 };
 
-IMPLEMENT_SAVERESTORE(CShock, CBaseEntity);
+IMPLEMENT_SAVERESTORE(CShock, CBaseAnimating)
 
 void CShock::Spawn(void)
 {
+	Precache();
 	pev->movetype = MOVETYPE_FLY;
-	pev->classname = MAKE_STRING("shock");
-
 	pev->solid = SOLID_BBOX;
-	pev->rendermode = kRenderTransAdd;
-	pev->renderamt = 170;
-	pev->renderfx = kRenderFxNoDissipation;
-
-	SET_MODEL(ENT(pev), "sprites/flare3.spr");
-	pev->frame = 0;
-	pev->scale = 0.4;
+	pev->classname = MAKE_STRING("shock_beam");
+	SET_MODEL(ENT(pev), "models/shock_effect.mdl");
+	UTIL_SetOrigin(pev, pev->origin);
 
+	if ( g_pGameRules->IsMultiplayer() )
+		pev->dmg = gSkillData.plrDmgShockroachMultiplayer;
+	else
+		pev->dmg = gSkillData.plrDmgShockroachSingleplayer;
 	UTIL_SetSize(pev, Vector(-4, -4, -4), Vector(4, 4, 4));
 
-	// Make beam NULL to avoid assertions.
-	m_pBeam = 0;
-
-	ComputeBeamPositions(&m_vecBeamStart, &m_vecBeamEnd);
-
-	// Create the beam.
-	//CreateBeam(m_vecBeamStart, m_vecBeamEnd, SHOCK_BEAM_WIDTH);
-
-	SetThink(&CShock::ShockThink);
-	pev->nextthink = gpGlobals->time + 0.1f;
+	CreateEffects();
+	SetThink( &CShock::FlyThink );
+	pev->nextthink = gpGlobals->time;
 }
 
-void CShock::ShockThink(void)
+void CShock::Precache()
 {
-	pev->nextthink  = gpGlobals->time + 0.01f;
-
-	ComputeBeamPositions(&m_vecBeamStart, &m_vecBeamEnd);
-
-	// Update the beam.
-	UpdateBeam(m_vecBeamStart, m_vecBeamEnd);
+	PRECACHE_MODEL("sprites/flare3.spr");
+	PRECACHE_MODEL("sprites/lgtning.spr");
+	PRECACHE_MODEL("models/shock_effect.mdl");
+	PRECACHE_SOUND("weapons/shock_impact.wav");
 }
 
-void CShock::Shoot(entvars_t *pevOwner, Vector vecStart, Vector vecVelocity)
+void CShock::FlyThink()
+{
+	if (pev->waterlevel == 3)
+	{
+		entvars_t *pevOwner = VARS(pev->owner);
+		const int iVolume = RANDOM_FLOAT(0.8f, 1);
+		const int iPitch = RANDOM_FLOAT(80, 110);
+		EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "weapons/shock_impact.wav", iVolume, ATTN_NORM, 0, iPitch);
+		RadiusDamage(pev->origin, pev, pevOwner ? pevOwner : pev, pev->dmg * 3, 144, CLASS_NONE, DMG_SHOCK | DMG_ALWAYSGIB );
+		ClearEffects();
+		SetThink( &CBaseEntity::SUB_Remove );
+		pev->nextthink = gpGlobals->time;
+	}
+	else
+	{
+		pev->nextthink = gpGlobals->time + 0.05;
+	}
+}
+
+void CShock::Shoot(entvars_t *pevOwner, const Vector angles, const Vector vecStart, const Vector vecVelocity)
 {
 	CShock *pShock = GetClassPtr((CShock *)NULL);
 	pShock->Spawn();
@@ -116,8 +122,8 @@ void CShock::Shoot(entvars_t *pevOwner, Vector vecStart, Vector vecVelocity)
 	UTIL_SetOrigin(pShock->pev, vecStart);
 	pShock->pev->velocity = vecVelocity;
 	pShock->pev->owner = ENT(pevOwner);
+	pShock->pev->angles = angles;
 
-	pShock->SetThink(&CShock::ShockThink);
 	pShock->pev->nextthink = gpGlobals->time;
 }
 
@@ -152,7 +158,7 @@ void CShock::Touch(CBaseEntity *pOther)
 		WRITE_BYTE( 10 );		// decay * 0.1
 	MESSAGE_END( );
 
-	ClearBeam();
+	ClearEffects();
 	if (!pOther->pev->takedamage)
 	{
 		// make a splat on the wall
@@ -160,22 +166,29 @@ void CShock::Touch(CBaseEntity *pOther)
 
 		int iContents = UTIL_PointContents(pev->origin);
 
-		// Create sparks 
+		// Create sparks
 		if (iContents != CONTENTS_WATER)
 		{
 			UTIL_Sparks(tr.vecEndPos);
 		}
-		UTIL_Remove(this);
 	}
 	else
 	{
 		ClearMultiDamage();
 		entvars_t *pevOwner = VARS(pev->owner);
-		float damage = gSkillData.monDmgShockroach;
-		if (pevOwner && (pevOwner->flags | FL_CLIENT))
-			damage = gSkillData.plrDmgShockroach;
-		pOther->TraceAttack(pev, damage, pev->velocity.Normalize(), &tr, DMG_ENERGYBEAM );
-		ApplyMultiDamage(pev, pev);
+		int damageType = DMG_ENERGYBEAM;
+		if (pOther->pev->deadflag == DEAD_DEAD)
+		{
+			damageType |= DMG_ALWAYSGIB;
+		}
+		else
+		{
+			CBaseMonster* pMonster = pOther->MyMonsterPointer();
+			if (pMonster)
+				pMonster->GlowShellOn( Vector( 0, 220, 255 ), .5f );
+		}
+		pOther->TraceAttack(pev, pev->dmg, pev->velocity.Normalize(), &tr, damageType );
+		ApplyMultiDamage(pev, pevOwner ? pevOwner : pev);
 		if (pOther->IsPlayer() && (UTIL_PointContents(pev->origin) != CONTENTS_WATER))
 		{
 			const Vector position = tr.vecEndPos;
@@ -187,69 +200,60 @@ void CShock::Touch(CBaseEntity *pOther)
 			MESSAGE_END();
 		}
 	}
-	UTIL_Remove(this);
+	SetThink( &CBaseEntity::SUB_Remove );
+	pev->nextthink = gpGlobals->time;
 }
 
-//=========================================================
-// Purpose:
-//=========================================================
-void CShock::CreateBeam(const Vector& start, const Vector& end, int width)
+void CShock::CreateEffects()
 {
+	m_pSprite = CSprite::SpriteCreate( "sprites/flare3.spr", pev->origin, FALSE );
+	m_pSprite->SetAttachment( edict(), 0 );
+	m_pSprite->pev->scale = 0.4;
+	m_pSprite->SetTransparency( kRenderTransAdd, 255, 255, 255, 170, kRenderFxNoDissipation );
+	m_pSprite->pev->spawnflags |= SF_SPRITE_TEMPORARY;
+	m_pSprite->pev->flags |= FL_SKIPLOCALHOST;
+
+	m_pBeam = CBeam::BeamCreate( "sprites/lgtning.spr", 30 );
+
 	if (m_pBeam)
 	{
-		ClearBeam();
-	}
-
-	m_pBeam = CBeam::BeamCreate("sprites/lgtning.spr", width);
-	if (!m_pBeam)
-		return;
-
-	m_pBeam->PointsInit(start, end);
-	m_pBeam->SetColor(140, 255, 220);
-	m_pBeam->SetBrightness(RANDOM_LONG(24, 25) * 10);
-	m_pBeam->SetFrame(0);
-	m_pBeam->SetScrollRate(10);
-	m_pBeam->SetNoise(40);
-	m_pBeam->SetFlags(SF_BEAM_SHADEIN | SF_BEAM_SHADEOUT);
-}
-
-
-//=========================================================
-// Purpose:
-//=========================================================
-void CShock::ClearBeam()
-{
-	if (m_pBeam)
-	{
-		UTIL_Remove(m_pBeam);
-		m_pBeam = NULL;
-	}
-}
-
-void CShock::UpdateBeam(const Vector& start, const Vector& end)
-{
-	if (!m_pBeam)
-	{
-		// Create the beam if not already created.
-		CreateBeam(start, end, SHOCK_BEAM_WIDTH);
-	}
-	else
-	{
-		m_pBeam->SetStartPos(start);
-		m_pBeam->SetEndPos(end);
+		m_pBeam->EntsInit( entindex(), entindex() );
+		m_pBeam->SetStartAttachment( 1 );
+		m_pBeam->SetEndAttachment( 2 );
+		m_pBeam->SetBrightness( 190 );
+		m_pBeam->SetScrollRate( 20 );
+		m_pBeam->SetNoise( 20 );
+		m_pBeam->SetFlags( BEAM_FSHADEOUT );
+		m_pBeam->SetColor( 0, 255, 255 );
+		m_pBeam->pev->spawnflags = SF_BEAM_TEMPORARY;
 		m_pBeam->RelinkBeam();
 	}
+
+	m_pNoise = CBeam::BeamCreate( "sprites/lgtning.spr", 30 );
+
+	if (m_pNoise)
+	{
+		m_pNoise->EntsInit( entindex(), entindex() );
+		m_pNoise->SetStartAttachment( 1 );
+		m_pNoise->SetEndAttachment( 2 );
+		m_pNoise->SetBrightness( 190 );
+		m_pNoise->SetScrollRate( 20 );
+		m_pNoise->SetNoise( 65 );
+		m_pNoise->SetFlags( BEAM_FSHADEOUT );
+		m_pNoise->SetColor( 255, 255, 173 );
+		m_pNoise->pev->spawnflags = SF_BEAM_TEMPORARY;
+		m_pNoise->RelinkBeam();
+	}
 }
 
-void CShock::ComputeBeamPositions(Vector* pos1, Vector* pos2)
+void CShock::ClearEffects()
 {
-	const Vector vel = pev->velocity.Normalize();
-	UTIL_MakeVectors( pev->angles );
+	UTIL_Remove( m_pBeam );
+	m_pBeam = NULL;
 
-	/* Little aside so beam and sprite won't blend into white ball when looking right into shock beam direction.
-	 * This should replicate the Opposing Force behavior
-	 */
-	const Vector origin = pev->origin - gpGlobals->v_right * 6;
-	*pos1 = origin + (vel *  SHOCK_BEAM_LENGTH_HALF);
-	*pos2 = origin + (vel * -SHOCK_BEAM_LENGTH_HALF/2);
+	UTIL_Remove( m_pNoise );
+	m_pNoise = NULL;
+
+	UTIL_Remove( m_pSprite );
+	m_pSprite = NULL;
 }
diff --git a/dlls/gearbox/shockrifle.cpp b/dlls/gearbox/shockrifle.cpp
index 01f0abc6..b49f81f0 100644
--- a/dlls/gearbox/shockrifle.cpp
+++ b/dlls/gearbox/shockrifle.cpp
@@ -67,7 +67,7 @@ void CShockrifle::Precache(void)
 
 	m_usShockFire = PRECACHE_EVENT(1, "events/shock.sc");
 
-	UTIL_PrecacheOther("shock");
+	UTIL_PrecacheOther("shock_beam");
 }
 
 int CShockrifle::AddToPlayer(CBasePlayer *pPlayer)
@@ -136,7 +136,17 @@ void CShockrifle::PrimaryAttack()
 		return;
 	}
 
+	if (m_pPlayer->pev->waterlevel == 3)
+	{
 #ifndef CLIENT_DLL
+		RadiusDamage(m_pPlayer->pev->origin, m_pPlayer->pev, m_pPlayer->pev, 300, 144, CLASS_NONE, DMG_SHOCK | DMG_ALWAYSGIB );
+#endif
+		return;
+	}
+
+#ifndef CLIENT_DLL
+	Vector anglesAim = m_pPlayer->pev->v_angle + m_pPlayer->pev->punchangle;
+	anglesAim.x		= -anglesAim.x;
 	UTIL_MakeVectors(m_pPlayer->pev->v_angle);
 
 	Vector vecSrc;
@@ -146,8 +156,8 @@ void CShockrifle::PrimaryAttack()
 	vecSrc = vecSrc + gpGlobals->v_right * 8;
 	vecSrc = vecSrc + gpGlobals->v_up * -12;
 
-	CBaseEntity *pShock = CBaseEntity::Create("shock", vecSrc, m_pPlayer->pev->v_angle, m_pPlayer->edict());
-	pShock->pev->velocity = gpGlobals->v_forward * 900;
+	CBaseEntity *pShock = CBaseEntity::Create("shock_beam", vecSrc, anglesAim, m_pPlayer->edict());
+	pShock->pev->velocity = gpGlobals->v_forward * 2000;
 
 	m_flRechargeTime = gpGlobals->time + 0.5;
 #endif
diff --git a/dlls/gearbox/strooper.cpp b/dlls/gearbox/strooper.cpp
index 7ff68164..320fc97b 100644
--- a/dlls/gearbox/strooper.cpp
+++ b/dlls/gearbox/strooper.cpp
@@ -362,9 +362,10 @@ void CStrooper::HandleAnimEvent(MonsterEvent_t *pEvent)
 			vecGunAngles = (m_vecEnemyLKP - vecGunPos).Normalize();
 		}
 
-		CBaseEntity *pShock = CBaseEntity::Create("shock", vecGunPos, pev->angles, edict());
+		CBaseEntity *pShock = CBaseEntity::Create("shock_beam", vecGunPos, pev->angles, edict());
 		vecGunAngles.z += RANDOM_FLOAT( -0.05, 0 );
-		pShock->pev->velocity = vecGunAngles * 900;
+		pShock->pev->velocity = vecGunAngles * 2000;
+		pShock->pev->nextthink = gpGlobals->time;
 
 		// Play fire sound.
 		EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/shock_fire.wav", 1, ATTN_NORM);
diff --git a/dlls/monsters.cpp b/dlls/monsters.cpp
index 3b122ce5..5c967c72 100644
--- a/dlls/monsters.cpp
+++ b/dlls/monsters.cpp
@@ -527,9 +527,8 @@ void CBaseMonster::MonsterThink( void )
 {
 	pev->nextthink = gpGlobals->time + 0.1;// keep monster thinking.
 
-	GlowShellUpdate();
-
 	RunAI();
+	GlowShellUpdate();
 
 	float flInterval = StudioFrameAdvance( ); // animate
 
@@ -3429,7 +3428,7 @@ void CBaseMonster::GlowShellUpdate( void )
 {
 	if( m_glowShellUpdate )
 	{
-		if( gpGlobals->time > m_glowShellTime )
+		if( gpGlobals->time > m_glowShellTime || pev->deadflag == DEAD_DEAD )
 			GlowShellOff();
 	}
 }
diff --git a/dlls/monsterstate.cpp b/dlls/monsterstate.cpp
index b830ee4f..48419ebd 100644
--- a/dlls/monsterstate.cpp
+++ b/dlls/monsterstate.cpp
@@ -99,8 +99,6 @@ void CBaseMonster::RunAI( void )
 
 		CheckAmmo();
 	}
-	if (m_glowShellUpdate && pev->deadflag == DEAD_DEAD)
-		GlowShellOff();
 	FCheckAITrigger();
 
 	PrescheduleThink();
diff --git a/dlls/multiplay_gamerules.cpp b/dlls/multiplay_gamerules.cpp
index f8599408..4f643e4b 100644
--- a/dlls/multiplay_gamerules.cpp
+++ b/dlls/multiplay_gamerules.cpp
@@ -196,7 +196,7 @@ void CHalfLifeMultiplay::RefreshSkillData( void )
 	gSkillData.plrDmgDisplacer = 5;
 
 	// Shockroach
-	gSkillData.plrDmgShockroach = 10;
+	gSkillData.plrDmgShockroachSingleplayer = 10;
 
 	// Spore
 	gSkillData.plrDmgSpore = 50;
@@ -217,7 +217,7 @@ void CHalfLifeMultiplay::RefreshSkillData( void )
 	gSkillData.monDmgDisplacer = 250;
 
 	// Shockroach
-	gSkillData.monDmgShockroach = 15;
+	gSkillData.plrDmgShockroachMultiplayer = 15;
 
 	// Displacer radius
 	gSkillData.displacerDmgRadius = 300;
diff --git a/dlls/skill.h b/dlls/skill.h
index a24f43f6..b68a8fd8 100644
--- a/dlls/skill.h
+++ b/dlls/skill.h
@@ -207,7 +207,7 @@ struct skilldata_t
 	float plrDmgGrapple;
 	float plrDmgEagle;
 	float plrDmgDisplacer;
-	float plrDmgShockroach;
+	float plrDmgShockroachSingleplayer;
 	float plrDmgSpore;
 	float plrDmg762;
 	float plrDmg556;
@@ -216,7 +216,7 @@ struct skilldata_t
 	float monDmg762;
 	float monDmg556;
 	float monDmgDisplacer;
-	float monDmgShockroach;
+	float plrDmgShockroachMultiplayer;
 
 	float displacerDmgRadius;
 };