Files
HL2Overcharged/game/server/overcharged/weapon_egon.cpp
2025-05-21 21:20:08 +03:00

710 lines
19 KiB
C++

//========= Copyright © 1996-2018, Valve & Overcharged ============
//
// Purpose: Egon (enhanced & modern)
//
// $NoKeywords: $
//=============================================================================
#include "cbase.h"
#include "NPCEvent.h"
#include "basehlcombatweapon.h"
#include "basecombatcharacter.h"
#include "AI_BaseNPC.h"
#include "player.h"
#include "gamerules.h"
#include "in_buttons.h"
#include "soundent.h"
#include "game.h"
#include "vstdlib/random.h"
#include "engine/IEngineSound.h"
#include "player.h"
#include "shake.h"
#include "AmmoDef.h"
#include "particle_system.h"
#include "soundenvelope.h"
#include "weapon_egon.h"
//#define EGON_CHARGE_TIME 0.2f
#define EGON_AMMOUSING_TIME 0.1
#define EGON_DAMAGE_TIME 0.02
#define EGON_BEAM_TEXTURE "effects/egon/xbeam3.vmt"
#define MAX_BURN_RADIUS 256
#define RADIUS_GROW_RATE 50.0
#define EGON_TARGET_INVALID Vector( FLT_MAX, FLT_MAX, FLT_MAX )
#define ADDITIONAL_SPRITE "effects/egon/HitSprite.vmt"
//extern ConVar sk_plr_dmg_egon_wide;
ConVar sk_plr_dmg_egon_wide("sk_plr_dmg_egon_wide", "0", (FCVAR_REPLICATED), "Egon damage.");
ConVar oc_weapon_egon_beams_life_time("oc_weapon_egon_beams_life_time", "2", (FCVAR_REPLICATED), "Egon damage.");
ConVar oc_weapon_egon_firewater_frequency("oc_weapon_egon_firewater_frequency", "0", (FCVAR_REPLICATED), "Egon damage.");
LINK_ENTITY_TO_CLASS(weapon_egon, CWeaponEgon);
PRECACHE_WEAPON_REGISTER(weapon_egon);
IMPLEMENT_SERVERCLASS_ST(CWeaponEgon, DT_WeaponEgon)
END_SEND_TABLE()
BEGIN_DATADESC(CWeaponEgon)
DEFINE_FIELD(m_bFirstPlay, FIELD_BOOLEAN),
DEFINE_FIELD(m_beamIndex, FIELD_INTEGER),
DEFINE_FIELD(m_flBurnRadius, FIELD_FLOAT),
DEFINE_FIELD(m_vecLaserTarget, FIELD_VECTOR),
DEFINE_FIELD(m_BeamFireState, FIELD_INTEGER),
DEFINE_FIELD(m_SSprite, FIELD_EHANDLE),
DEFINE_FIELD(m_flChargeTime, FIELD_TIME),
DEFINE_FIELD(m_flDmgTime, FIELD_TIME),
DEFINE_FIELD(m_flAmmoTime, FIELD_TIME),
DEFINE_FIELD(m_hBeam, FIELD_EHANDLE),
DEFINE_FIELD(m_hCurveBeam, FIELD_EHANDLE),
END_DATADESC()
acttable_t CWeaponEgon::m_acttable[] = // BriJee: Egon act's over here
{
{ ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_PHYSGUN, false },
{ ACT_HL2MP_RUN, ACT_HL2MP_RUN_PHYSGUN, false },
{ ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_PHYSGUN, false },
{ ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_PHYSGUN, false },
{ ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_PHYSGUN, false },
{ ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_PHYSGUN, false },
{ ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_PHYSGUN, false },
{ ACT_IDLE, ACT_IDLE_SMG1, true },
{ ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SHOTGUN, true },
{ ACT_RELOAD, ACT_RELOAD_SHOTGUN, false },
{ ACT_WALK, ACT_WALK_RIFLE, true },
{ ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_SHOTGUN, true },
// Readiness activities (not aiming)
{ ACT_IDLE_RELAXED, ACT_IDLE_SHOTGUN_RELAXED, false },//never aims
{ ACT_IDLE_STIMULATED, ACT_IDLE_SHOTGUN_STIMULATED, false },
{ ACT_IDLE_AGITATED, ACT_IDLE_SHOTGUN_AGITATED, false },//always aims
{ ACT_WALK_RELAXED, ACT_WALK_RIFLE_RELAXED, false },//never aims
{ ACT_WALK_STIMULATED, ACT_WALK_RIFLE_STIMULATED, false },
{ ACT_WALK_AGITATED, ACT_WALK_AIM_RIFLE, false },//always aims
{ ACT_RUN_RELAXED, ACT_RUN_RIFLE_RELAXED, false },//never aims
{ ACT_RUN_STIMULATED, ACT_RUN_RIFLE_STIMULATED, false },
{ ACT_RUN_AGITATED, ACT_RUN_AIM_RIFLE, false },//always aims
// Readiness activities (aiming)
{ ACT_IDLE_AIM_RELAXED, ACT_IDLE_SMG1_RELAXED, false },//never aims
{ ACT_IDLE_AIM_STIMULATED, ACT_IDLE_AIM_RIFLE_STIMULATED, false },
{ ACT_IDLE_AIM_AGITATED, ACT_IDLE_ANGRY_SMG1, false },//always aims
{ ACT_WALK_AIM_RELAXED, ACT_WALK_RIFLE_RELAXED, false },//never aims
{ ACT_WALK_AIM_STIMULATED, ACT_WALK_AIM_RIFLE_STIMULATED, false },
{ ACT_WALK_AIM_AGITATED, ACT_WALK_AIM_RIFLE, false },//always aims
{ ACT_RUN_AIM_RELAXED, ACT_RUN_RIFLE_RELAXED, false },//never aims
{ ACT_RUN_AIM_STIMULATED, ACT_RUN_AIM_RIFLE_STIMULATED, false },
{ ACT_RUN_AIM_AGITATED, ACT_RUN_AIM_RIFLE, false },//always aims
//End readiness activities
{ ACT_WALK_AIM, ACT_WALK_AIM_SHOTGUN, true },
{ ACT_WALK_CROUCH, ACT_WALK_CROUCH_RIFLE, true },
{ ACT_WALK_CROUCH_AIM, ACT_WALK_CROUCH_AIM_RIFLE, true },
{ ACT_RUN, ACT_RUN_RIFLE, true },
{ ACT_RUN_AIM, ACT_RUN_AIM_SHOTGUN, true },
{ ACT_RUN_CROUCH, ACT_RUN_CROUCH_RIFLE, true },
{ ACT_RUN_CROUCH_AIM, ACT_RUN_CROUCH_AIM_RIFLE, true },
{ ACT_GESTURE_RANGE_ATTACK1, ACT_GESTURE_RANGE_ATTACK_SHOTGUN, true },
{ ACT_RANGE_ATTACK1_LOW, ACT_RANGE_ATTACK_SHOTGUN_LOW, true },
{ ACT_RELOAD_LOW, ACT_RELOAD_SHOTGUN_LOW, false },
{ ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_SHOTGUN, false },
};
IMPLEMENT_ACTTABLE(CWeaponEgon);
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CWeaponEgon::CWeaponEgon(void)
{
m_BeamFireState = FIRE_NONE;
m_bFirstPlay = true;
m_flChargeTime = gpGlobals->curtime;
m_flDmgTime = gpGlobals->curtime;
m_flAmmoTime = gpGlobals->curtime;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CWeaponEgon::Precache(void)
{
m_beamIndex = PrecacheModel(EGON_BEAM_TEXTURE);
PrecacheMaterial(EGON_BEAM_TEXTURE);
PrecacheMaterial(ADDITIONAL_SPRITE);
BaseClass::Precache();
}
void CWeaponEgon::UseAmmo(int count)
{
CBasePlayer *pPlayer = ToBasePlayer(GetOwner());
if (!pPlayer)
{
return;
}
if (pPlayer->GetAmmoCount(m_iPrimaryAmmoType) >= count)
RemoveAmmo(GetPrimaryAmmoType(), count); //pPlayer->RemoveAmmo(count, m_iPrimaryAmmoType);
else
RemoveAmmo(GetPrimaryAmmoType(), pPlayer->GetAmmoCount(m_iPrimaryAmmoType)); //pPlayer->RemoveAmmo(pPlayer->GetAmmoCount(m_iPrimaryAmmoType), m_iPrimaryAmmoType);
}
Activity CWeaponEgon::GetPrimaryAttackActivity(void)
{
if (m_BeamFireState == FIRE_STARTUP)
return BaseClass::GetPrimaryAttackActivity();
else if (m_BeamFireState == FIRE_LOOP)
return GetWpnData().animData[m_bFireMode].FirePrimarySpecial;
return BaseClass::GetPrimaryAttackActivity();
}
void CWeaponEgon::PrimaryAttack(void)
{
CBasePlayer *pPlayer = ToBasePlayer(GetOwner());
if (!pPlayer)
return;
if (GetActivity() != GetPrimaryAttackActivity())
SendWeaponAnim(GetPrimaryAttackActivity());
switch (m_BeamFireState)
{
case FIRE_NONE:
{
m_BeamFireState = FIRE_STARTUP;
}
break;
case FIRE_STARTUP:
{
if (m_flNextPrimaryAttack <= gpGlobals->curtime)
{
if (m_BeamFireState == FIRE_STARTUP)
Charge();
WeaponSound(SPECIAL1);
m_BeamFireState = FIRE_LOOP;
m_bFirstPlay = true;
m_flNextSecondaryAttack = m_flNextPrimaryAttack = m_flDmgTime = m_flAmmoTime = gpGlobals->curtime + GetWpnData().mode_fire_rate_automatic_startup_delay;
}
}
break;
case FIRE_LOOP:
{
m_flNextSecondaryAttack = m_flNextPrimaryAttack = gpGlobals->curtime;// +GetFireRate();
if (pPlayer->GetWaterLevel() != 3)
{
if (m_bFirstPlay)
{
m_flDmgTime = m_flAmmoTime = gpGlobals->curtime + GetFireRate();
WeaponSound(SINGLE);
StopWeaponSound(SPECIAL1);
m_bFirstPlay = false;
//DispatchParticleEffect("gluon_beam_startup", PATTACH_POINT_FOLLOW, pPlayer->GetViewModel(), 1, false);
//DispatchParticleEffect("gluon_beam_burst", PATTACH_POINT_FOLLOW, pPlayer->GetViewModel(), 1, false);
DispatchParticleEffect(STRING(GetWpnData().iMuzzleFlashShortBurst), PATTACH_POINT_FOLLOW, pPlayer->GetViewModel(), GetWpnData().iAttachment, false);
}
Vector vecAiming = pPlayer->GetAutoaimVector(0);
Vector vecSrc = pPlayer->EyePosition();
Fire(vecSrc, vecAiming, pPlayer);
FireClientPrimaryAttack();
}
else
DryFire();
if (GetWpnData().allowLoopSound)
StartLoopSound();
}
break;
case FIRE_END:
{
StopWeaponSound(SPECIAL1);
DestroyBeam();
StopLoopSound();
}
break;
default:
break;
}
/*if (pPlayer->GetAmmoCount(m_iPrimaryAmmoType) <= 0)
{
// HEV suit - indicate out of ammo condition
pPlayer->SetSuitUpdate("!HEV_AMO0", FALSE, 0);
}*/
}
void CWeaponEgon::ApplyDamage(const Vector &vecDir, trace_t &tr, CBasePlayer *pPlayer)
{
if (m_flDmgTime <= gpGlobals->curtime)
{
// wide mode does damage to the ent, and radius damage
if (tr.m_pEnt && tr.m_pEnt->m_takedamage != DAMAGE_NO)
{
ClearMultiDamage();
if (tr.m_pEnt && tr.m_pEnt->IsAlive() && (tr.m_pEnt->GetFlags() & FL_ONFIRE) == 0)
{
CTakeDamageInfo info(this, pPlayer, sk_plr_dmg_egon_wide.GetFloat(), DMG_ENERGYBEAM | /*DMG_REMOVENORAGDOLL | */ DMG_DISSOLVE_EGON_ELECTRICAL | DMG_DISSOLVE_EGON);
CalculateMeleeDamageForce(&info, vecDir, tr.endpos);
tr.m_pEnt->DispatchTraceAttack(info, vecDir, &tr);
ApplyMultiDamage();
}
if (tr.m_pEnt && tr.m_pEnt->IsAlive() && (tr.m_pEnt->GetFlags() & FL_ONFIRE) != 0)
{
CTakeDamageInfo info(this, pPlayer, sk_plr_dmg_egon_wide.GetFloat(), DMG_ENERGYBEAM |/* DMG_REMOVENORAGDOLL | */ DMG_DISSOLVE_EGON_ELECTRICAL | DMG_DISSOLVE_EGON);
CalculateMeleeDamageForce(&info, vecDir, tr.endpos);
tr.m_pEnt->DispatchTraceAttack(info, vecDir, &tr);
ApplyMultiDamage();
}
}
RadiusDamage(CTakeDamageInfo(this, this, sk_plr_dmg_egon_wide.GetFloat() / 4, DMG_BURN), tr.endpos, 10.0f, CLASS_NONE, NULL);
UTIL_DecalTrace(&tr, "FadingScorch"); //Ãàðü îò ëó÷à
ShouldDrawWaterImpacts(tr);
m_flDmgTime = gpGlobals->curtime + GetFireRate();
}
}
void CWeaponEgon::AmmoUsing()
{
// Wide mode uses 10 charges per second in single player
if (m_flAmmoTime <= gpGlobals->curtime)
{
UseAmmo(1);
m_flAmmoTime = gpGlobals->curtime + EGON_AMMOUSING_TIME;
}
}
void CWeaponEgon::Fire(const Vector &vecOrigSrc, const Vector &vecDir, CBasePlayer *pPlayer)
{
Vector vecDest = vecOrigSrc + (vecDir * MAX_TRACE_LENGTH);
trace_t tr;
UTIL_TraceLine(vecOrigSrc, vecDest, MASK_SHOT, pPlayer, COLLISION_GROUP_NONE, &tr);
ApplyDamage(vecDir, tr, pPlayer);
AmmoUsing();
AddViewKick();
Vector vecUp, vecRight;
QAngle angDir;
VectorAngles(vecDir, angDir);
AngleVectors(angDir, NULL, &vecRight, &vecUp);
Vector tmpSrc = vecOrigSrc + (vecUp * -8) + (vecRight * 3);
UpdateBeam(tmpSrc, tr.endpos, pPlayer);
if (pPlayer->GetViewModel())
{
CEffectData data;
data.m_nEntIndex = pPlayer->GetViewModel()->entindex();
data.m_nAttachmentIndex = 1;
DispatchEffect("EgonLightning", data);
}
//DispatchParticleEffect("gluon_beam_startup", PATTACH_POINT_FOLLOW, pPlayer->GetViewModel(), 1, false);//OverCharged
//DispatchParticleEffect("gluon_beam_burst", PATTACH_POINT_FOLLOW, pPlayer->GetViewModel(), 1, false);//OverCharged
/*CPASFilter filter(endPoint);
te->DynamicLight(filter, 0.0, &endPoint, 80, 22, 255, 2, 40, 0.07, 0);*/
if (pPlayer)
{
CEffectData data;
data.m_nEntIndex = pPlayer->entindex();
data.m_vOrigin = tr.endpos;
data.m_vStart.x = GetWpnData().iMuzzleFlashLightR;
data.m_vStart.y = GetWpnData().iMuzzleFlashLightG;
data.m_vStart.z = GetWpnData().iMuzzleFlashLightB;
DispatchEffect("EgonWeaponLight", data);
}
if (tr.DidHit())
{
//int beams;
/*CPASFilter filter(GetAbsOrigin());
te->DynamicLight(filter, 0.0, &tr.endpos, 80, 22, 255, 2, 40, 0.07, 0);*/
/*for (beams = 0; beams < 6; beams++)
{
// Vector vecDest;
// Random unit vector
vecDest.x = random->RandomFloat(-1, 1);
vecDest.y = random->RandomFloat(-1, 1);
vecDest.z = random->RandomFloat(-1, 1);
// Push out to radius dist.
vecDest = tr.endpos + vecDest * 35.0f;
DispatchParticleEffect("gluon_beam_end_inst", tr.endpos, vec3_angle);
UTIL_Beam(tr.endpos,
vecDest,
m_beamIndex,
0, //halo index
0, //frame start
2.0f, //framerate
0.08f, //life
1.3, // width
0.75, // endwidth
0.75, // fadelength,
2, // noise
138, // red
112, // green
234, // blue,
155, // bright
150 // speed
);
}*/
}
if (tr.m_pEnt != NULL && !tr.m_pEnt->IsWorld())
{
CSingleUserRecipientFilter filter(pPlayer);
const char *shootsound = GetShootSound(SPECIAL2);
if (shootsound && shootsound[0] &&
random->RandomInt(0, 3) == 3)
{
EmitSound(filter, pPlayer->entindex(), shootsound, &tr.endpos);
}
}
}
void CWeaponEgon::UpdateBeam(const Vector &startPoint, const Vector &endPoint, CBasePlayer *pPlayer)
{
if (!m_hBeam && !m_hCurveBeam)
{
CreateBeam(pPlayer);
}
if (m_hBeam)
{
m_hBeam->SetStartPos(endPoint);
}
if (m_hCurveBeam)
{
m_hCurveBeam->SetStartPos(endPoint);
}
if (m_SSprite)
{
m_SSprite->SetAbsOrigin(endPoint);
m_SSprite->SetBrightness(random->RandomFloat(120.f, 255.f));
m_SSprite->SetScale(random->RandomFloat(0.35f, 0.55f));
}
}
void CWeaponEgon::CreateBeam(CBasePlayer *pPlayer)
{
if (!m_hBeam)
{
m_hBeam = CBeam::BeamCreate(EGON_BEAM_TEXTURE, 7.0);
if (cvar->FindVar("oc_player_draw_body")->GetInt() && cvar->FindVar("oc_player_true_firstperson_vm")->GetInt())
m_hBeam->PointEntInit(this->GetAbsOrigin(), this);
else
m_hBeam->PointEntInit(pPlayer->GetViewModel()->GetAbsOrigin(), pPlayer->GetViewModel());
m_hBeam->SetEndAttachment(1);
m_hBeam->SetScrollRate(950);
m_hBeam->SetBrightness(200);
m_hBeam->SetNoise(0.4);
m_hBeam->AddSpawnFlags(SF_BEAM_TEMPORARY);
if (cvar->FindVar("oc_player_draw_body")->GetInt() && cvar->FindVar("oc_player_true_firstperson_vm")->GetInt())
m_hBeam->SetOwnerEntity(this);
else
m_hBeam->SetOwnerEntity(pPlayer->GetViewModel());
m_hBeam->SetBrightness(2255);
m_hBeam->SetColor(138, 112, 234);
}
if (!m_hCurveBeam)
{
float width = 3.2f;
m_hCurveBeam = CBeam::BeamCreate(EGON_BEAM_TEXTURE, width);
m_hCurveBeam->SetBeamFlags(FBEAM_SINENOISE);
m_hCurveBeam->SetWidth(width*1.5);
m_hCurveBeam->SetEndWidth(width / 3.0f);
m_hCurveBeam->SetScrollRate(2000);
m_hCurveBeam->SetNoise(RandomFloat(0.1f, 0.12f));
if (cvar->FindVar("oc_player_draw_body")->GetInt() && cvar->FindVar("oc_player_true_firstperson_vm")->GetInt())
m_hCurveBeam->PointEntInit(this->GetAbsOrigin(), this);
else
m_hCurveBeam->PointEntInit(pPlayer->GetViewModel()->GetAbsOrigin(), pPlayer->GetViewModel());
m_hCurveBeam->SetEndAttachment(1);
m_hCurveBeam->AddSpawnFlags(SF_BEAM_TEMPORARY);
if (cvar->FindVar("oc_player_draw_body")->GetInt() && cvar->FindVar("oc_player_true_firstperson_vm")->GetInt())
m_hCurveBeam->SetOwnerEntity(this);
else
m_hCurveBeam->SetOwnerEntity(pPlayer->GetViewModel());
m_hCurveBeam->SetBrightness(2255);
m_hCurveBeam->SetColor(30, 30, 255);
}
if (!m_SSprite)
{
m_SSprite = CSprite::SpriteCreate(ADDITIONAL_SPRITE, m_hBeam->GetAbsEndPos(), false);
m_SSprite->SetParent(m_hBeam);
//m_SSprite->SetScale(0.25);
m_SSprite->SetTransparency(kRenderGlow, 255, 255, 255, 255, kRenderFxNoDissipation);
m_SSprite->AddSpawnFlags(SF_SPRITE_TEMPORARY);
}
}
void CWeaponEgon::DestroyBeam(void)
{
m_BeamFireState = FIRE_NONE;
if (m_bFirstPlay)
m_bFirstPlay = false;
m_flNextSecondaryAttack = m_flDmgTime = m_flChargeTime = m_flNextPrimaryAttack = gpGlobals->curtime + GetFireRate();
StopLoopSound();
CBasePlayer *pPlayer = ToBasePlayer(GetOwner());
if (pPlayer)
{
StopParticleEffects(pPlayer->GetViewModel());
if (pPlayer->GetAmmoCount(m_iPrimaryAmmoType) <= 0)
m_flNextSecondaryAttack = m_flNextPrimaryAttack = gpGlobals->curtime + 0.3f;
}
if (m_hBeam)
{
UTIL_Remove(m_hBeam);
m_hBeam = NULL;
}
if (m_hCurveBeam)
{
UTIL_Remove(m_hCurveBeam);
m_hCurveBeam = NULL;
}
if (m_SSprite)
{
m_SSprite->Expand(10, 500);
m_SSprite = NULL;
}
}
bool CWeaponEgon::Holster(CBaseCombatWeapon *pSwitchingTo)
{
bool bRet;
pSwitchingTo = NULL;
bRet = BaseClass::Holster(pSwitchingTo);
CBasePlayer *pPlayer = ToBasePlayer(GetOwner());
if (!pPlayer)
return false;
StopWeaponSound(SINGLE);
StopWeaponSound(SPECIAL1);
m_BeamFireState = FIRE_NONE;
DestroyBeam();
if (bRet)
{
pSwitchingTo = NULL;
}
return bRet;
}
bool CWeaponEgon::WeaponLOSCondition(const Vector &ownerPos, const Vector &targetPos, bool bSetConditions)
{
CAI_BaseNPC* npcOwner = GetOwner()->MyNPCPointer();
if (!npcOwner)
{
return false;
}
m_vecLaserTarget = targetPos;
return true;
}
int CWeaponEgon::WeaponRangeAttack1Condition(float flDot, float flDist)
{
if (m_flNextPrimaryAttack > gpGlobals->curtime)
{
return COND_NONE;
}
if (m_vecLaserTarget == EGON_TARGET_INVALID)
{
// No target!
return COND_NONE;
}
if (flDist > m_fMaxRange1)
{
return COND_TOO_FAR_TO_ATTACK;
}
else if (flDot < 0.5f) // UNDONE: Why check this here? Isn't the AI checking this already?
{
return COND_NOT_FACING_ATTACK;
}
return COND_CAN_RANGE_ATTACK1;
}
void CWeaponEgon::Charge(void)
{
}
void CWeaponEgon::ItemPostFrame(void)
{
BaseClass::ItemPostFrame();
CBasePlayer *pPlayer = ToBasePlayer(GetOwner());
if (!pPlayer)
return;
if (!m_bReloadComplete &&
!m_bInReload &&
(IsNearWall() || GetOwnerIsRunning()))
{
DestroyBeam();
StopWeaponSound(SPECIAL1);
return;
}
if (pPlayer->m_nButtons & IN_ZOOM)
{
if (m_BeamFireState != FIRE_END)
m_BeamFireState = FIRE_END;
}
if (pPlayer->GetWaterLevel() == 3)
{
if (m_BeamFireState != FIRE_END)
m_BeamFireState = FIRE_END;
}
if (pPlayer->GetAmmoCount(m_iPrimaryAmmoType) <= 0 && (m_hBeam || m_hCurveBeam || m_SSprite))
{
DestroyBeam();
StopWeaponSound(SPECIAL1);
return;
}
switch (m_BeamFireState)
{
case FIRE_STARTUP:
{
if (pPlayer->m_afButtonReleased & IN_ATTACK)
{
StopWeaponSound(SPECIAL1);
StopLoopSound();
m_BeamFireState = FIRE_END;
}
}
break;
case FIRE_LOOP:
{
if (pPlayer->m_afButtonReleased & IN_ATTACK)
{
StopWeaponSound(SPECIAL1);
StopLoopSound();
m_BeamFireState = FIRE_END;
}
}
break;
case FIRE_END:
{
StopWeaponSound(SPECIAL1);
DestroyBeam();
}
break;
default:
break;
}
}
//----------------------------------------------------------------------------------
// Purpose: Check for water
//----------------------------------------------------------------------------------
#define FSetBit(iBitVector, bits) ((iBitVector) |= (bits))
#define FBitSet(iBitVector, bit) ((iBitVector) & (bit))
#define TraceContents( vec ) ( enginetrace->GetPointContents( vec ) )
#define WaterContents( vec ) ( FBitSet( TraceContents( vec ), CONTENTS_WATER|CONTENTS_SLIME ) )
bool CWeaponEgon::ShouldDrawWaterImpacts(const trace_t &shot_trace)
{
//FIXME: This doesn't handle the case of trying to splash while being underwater, but that's not going to look good
// right now anyway...
// We must start outside the water
if (WaterContents(shot_trace.startpos))
return false;
// We must end inside of water
if (!WaterContents(shot_trace.endpos))
return false;
trace_t waterTrace;
UTIL_TraceLine(shot_trace.startpos, shot_trace.endpos, (CONTENTS_WATER | CONTENTS_SLIME), UTIL_GetLocalPlayer(), COLLISION_GROUP_NONE, &waterTrace);
if (waterTrace.fraction < 1.0f)
{
CEffectData data;
data.m_fFlags = 0;
data.m_vOrigin = waterTrace.endpos;
data.m_vNormal = waterTrace.plane.normal;
data.m_flScale = random->RandomFloat(2.0, 4.0f); // Water effect scale
// See if we hit slime
if (FBitSet(waterTrace.contents, CONTENTS_SLIME))
{
FSetBit(data.m_fFlags, FX_WATER_IN_SLIME);
}
CPASFilter filter(data.m_vOrigin);
te->DispatchEffect(filter, 0.0, data.m_vOrigin, "watersplash", data);
}
return true;
}