mirror of
https://github.com/Gigaslav/HL2Overcharged.git
synced 2026-01-02 17:48:11 +03:00
710 lines
19 KiB
C++
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;
|
|
}
|
|
|