mirror of
https://github.com/Gigaslav/HL2Overcharged.git
synced 2026-01-05 06:10:21 +03:00
1725 lines
49 KiB
C++
1725 lines
49 KiB
C++
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||
//
|
||
// Purpose:
|
||
//
|
||
//=============================================================================//
|
||
|
||
#include "cbase.h"
|
||
#include "npcevent.h"
|
||
#include "weapon_crossbow.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 "rumble_shared.h"
|
||
#include "gamestats.h"
|
||
#include "decals.h"
|
||
#include "baseviewmodel_shared.h"///
|
||
#include "baseviewmodel.h"///
|
||
#include "ragdoll_shared.h"
|
||
#include "vphysics/constraints.h"
|
||
#include "physics_prop_ragdoll.h"
|
||
#include "saverestore_utlvector.h"
|
||
#ifdef PORTAL
|
||
#include "portal_util_shared.h"
|
||
#endif
|
||
|
||
// memdbgon must be the last include file in a .cpp file!!!
|
||
#include "tier0/memdbgon.h"
|
||
|
||
//#define BOLT_MODEL "models/crossbow_bolt.mdl"
|
||
#define BOLT_MODEL "models/weapons/w_missile_closed.mdl"
|
||
|
||
#define BOLT_AIR_VELOCITY 2500
|
||
#define BOLT_WATER_VELOCITY 1500
|
||
|
||
extern ConVar sk_plr_dmg_crossbow;
|
||
extern ConVar sk_npc_dmg_crossbow;
|
||
|
||
/*ConVar oc_crossbow_x("oc_crossbow_x", "0", FCVAR_REPLICATED, "Set to values other than 0 to select a bonus map challenge type.");
|
||
ConVar oc_crossbow_y("oc_crossbow_y", "0", FCVAR_REPLICATED, "Set to values other than 0 to select a bonus map challenge type.");
|
||
ConVar oc_crossbow_z("oc_crossbow_z", "0", FCVAR_REPLICATED, "Set to values other than 0 to select a bonus map challenge type.");*/
|
||
ConVar oc_weapon_crossbow_explobolt("oc_weapon_crossbow_explobolt", "0", FCVAR_ARCHIVE, "Crossbow explode bolts");
|
||
ConVar oc_weapon_crossbow_bolt_lifetime("oc_weapon_crossbow_bolt_lifetime", "25", FCVAR_ARCHIVE, "bolt lifespan");
|
||
|
||
void TE_StickyBolt(IRecipientFilter& filter, float delay, Vector vecDirection, const Vector *origin);
|
||
|
||
#define BOLT_SKIN_NORMAL 0
|
||
#define BOLT_SKIN_GLOW 1
|
||
|
||
//-----------------------------------------------------------------------------
|
||
// Crossbow Bolt
|
||
//-----------------------------------------------------------------------------
|
||
class CCrossbowBolt : public CBaseCombatCharacter
|
||
{
|
||
DECLARE_CLASS(CCrossbowBolt, CBaseCombatCharacter);
|
||
|
||
public:
|
||
CCrossbowBolt() { };
|
||
~CCrossbowBolt();
|
||
|
||
Class_T Classify(void) { return CLASS_NONE; }
|
||
|
||
public:
|
||
|
||
void Spawn(void);
|
||
void Precache(void);
|
||
void BubbleThink(void);
|
||
void ExploBoltTouch(CBaseEntity *pOther);
|
||
void BoltTouch(CBaseEntity *pOther);
|
||
void TryAttachBolt(CBaseEntity *pOther, const Vector &vForward, trace_t &tr, bool isNpc);
|
||
bool CreateVPhysics(void);
|
||
int VPhysicsGetObjectList(IPhysicsObject **pList, int listMax, ragdoll_t &pRagdoll);
|
||
unsigned int PhysicsSolidMaskForEntity() const;
|
||
static CCrossbowBolt *BoltCreate(const Vector &vecOrigin, const QAngle &angAngles, CBasePlayer *pentOwner = NULL, bool exploBolts = false);
|
||
bool exploBolts;
|
||
|
||
protected:
|
||
bool CreateSprites(void);
|
||
int m_iStopped;
|
||
CHandle<CSprite> m_pGlowSprite;
|
||
CUtlVector<IPhysicsConstraint*> m_aConstraints;
|
||
//CHandle<CSpriteTrail> m_pGlowTrail;
|
||
IPhysicsConstraint* m_oCurrentConstraint;
|
||
|
||
DECLARE_DATADESC();
|
||
DECLARE_SERVERCLASS();
|
||
};
|
||
LINK_ENTITY_TO_CLASS(crossbow_bolt, CCrossbowBolt);
|
||
|
||
BEGIN_DATADESC(CCrossbowBolt)
|
||
// Function Pointers
|
||
DEFINE_FUNCTION(BubbleThink),
|
||
DEFINE_FUNCTION(ExploBoltTouch),
|
||
DEFINE_FUNCTION(BoltTouch),
|
||
//DEFINE_UTLVECTOR(m_aConstraints, FIELD_EMBEDDED),
|
||
DEFINE_FIELD(m_oCurrentConstraint, FIELD_CLASSPTR),
|
||
DEFINE_AUTO_ARRAY(m_aConstraints, FIELD_EHANDLE),
|
||
DEFINE_FIELD(exploBolts, FIELD_BOOLEAN),
|
||
DEFINE_FIELD(m_iStopped, FIELD_INTEGER),
|
||
// These are recreated on reload, they don't need storage
|
||
DEFINE_FIELD(m_pGlowSprite, FIELD_EHANDLE),
|
||
//DEFINE_FIELD( m_pGlowTrail, FIELD_EHANDLE ),
|
||
|
||
END_DATADESC()
|
||
|
||
IMPLEMENT_SERVERCLASS_ST(CCrossbowBolt, DT_CrossbowBolt)
|
||
END_SEND_TABLE()
|
||
|
||
CCrossbowBolt *CCrossbowBolt::BoltCreate(const Vector &vecOrigin, const QAngle &angAngles, CBasePlayer *pentOwner, bool exploBolts)
|
||
{
|
||
// Create a new entity with CCrossbowBolt private data
|
||
CCrossbowBolt *pBolt = (CCrossbowBolt *)CreateEntityByName("crossbow_bolt");
|
||
UTIL_SetOrigin(pBolt, vecOrigin);
|
||
pBolt->SetAbsAngles(angAngles);
|
||
pBolt->Spawn();
|
||
pBolt->SetOwnerEntity(pentOwner);
|
||
pBolt->exploBolts = exploBolts;
|
||
|
||
return pBolt;
|
||
}
|
||
|
||
//-----------------------------------------------------------------------------
|
||
// Purpose:
|
||
//-----------------------------------------------------------------------------
|
||
CCrossbowBolt::~CCrossbowBolt(void)
|
||
{
|
||
if (m_pGlowSprite)
|
||
{
|
||
UTIL_Remove(m_pGlowSprite);
|
||
}
|
||
}
|
||
|
||
//-----------------------------------------------------------------------------
|
||
// Purpose:
|
||
// Output : Returns true on success, false on failure.
|
||
//-----------------------------------------------------------------------------
|
||
bool CCrossbowBolt::CreateVPhysics(void)
|
||
{
|
||
// Create the object in the physics system
|
||
VPhysicsInitNormal(SOLID_BBOX, FSOLID_NOT_STANDABLE, false);
|
||
|
||
return true;
|
||
}
|
||
|
||
//-----------------------------------------------------------------------------
|
||
//-----------------------------------------------------------------------------
|
||
unsigned int CCrossbowBolt::PhysicsSolidMaskForEntity() const
|
||
{
|
||
return (BaseClass::PhysicsSolidMaskForEntity() | CONTENTS_HITBOX) & ~CONTENTS_GRATE;
|
||
}
|
||
|
||
//-----------------------------------------------------------------------------
|
||
// Purpose:
|
||
// Output : Returns true on success, false on failure.
|
||
//-----------------------------------------------------------------------------
|
||
bool CCrossbowBolt::CreateSprites(void)
|
||
{
|
||
// Start up the eye glow
|
||
m_pGlowSprite = CSprite::SpriteCreate("sprites/light_glow02_noz.vmt", GetLocalOrigin(), false);
|
||
|
||
if (m_pGlowSprite != NULL)
|
||
{
|
||
m_pGlowSprite->FollowEntity(this);
|
||
m_pGlowSprite->SetTransparency(kRenderGlow, 255, 255, 255, 128, kRenderFxNoDissipation);
|
||
m_pGlowSprite->SetScale(0.2f);
|
||
m_pGlowSprite->TurnOff();
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
//-----------------------------------------------------------------------------
|
||
// Purpose:
|
||
//-----------------------------------------------------------------------------
|
||
void CCrossbowBolt::Spawn(void)
|
||
{
|
||
Precache();
|
||
|
||
SetModel("models/crossbow_bolt.mdl");
|
||
SetMoveType(MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_CUSTOM);
|
||
UTIL_SetSize(this, -Vector(0.3f, 0.3f, 0.3f), Vector(0.3f, 0.3f, 0.3f));
|
||
SetSolid(SOLID_BBOX);
|
||
SetGravity(0.05f);
|
||
|
||
// Make sure we're updated if we're underwater
|
||
UpdateWaterState();
|
||
|
||
/*if (exploBolts)
|
||
SetTouch( &CCrossbowBolt::ExploBoltTouch );
|
||
else
|
||
SetTouch( &CCrossbowBolt::BoltTouch );*/
|
||
|
||
SetTouch(&CCrossbowBolt::BoltTouch);
|
||
|
||
SetThink(&CCrossbowBolt::BubbleThink);
|
||
SetNextThink(gpGlobals->curtime + 0.1f);
|
||
|
||
//CreateSprites();
|
||
|
||
DispatchParticleEffect("bolt_exhaust", PATTACH_ABSORIGIN_FOLLOW, this); //<2F><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>
|
||
|
||
// Make us glow until we've hit the wall
|
||
m_nSkin = BOLT_SKIN_GLOW;
|
||
|
||
m_iStopped = 0;
|
||
}
|
||
|
||
|
||
void CCrossbowBolt::Precache(void)
|
||
{
|
||
PrecacheModel(BOLT_MODEL);
|
||
|
||
// This is used by C_TEStickyBolt, despte being different from above!!!
|
||
PrecacheModel("models/crossbow_bolt_physic.mdl");//("models/Weapons/w_grenade.mdl");
|
||
|
||
PrecacheModel("models/crossbow_bolt.mdl");
|
||
|
||
PrecacheModel("sprites/light_glow02_noz.vmt");
|
||
|
||
PrecacheScriptSound("Weapon_Crossbow.BoltSkewer");
|
||
}
|
||
|
||
//-----------------------------------------------------------------------------
|
||
//-----------------------------------------------------------------------------
|
||
void CCrossbowBolt::ExploBoltTouch(CBaseEntity *pOther)
|
||
{
|
||
if (pOther->IsSolidFlagSet(FSOLID_VOLUME_CONTENTS | FSOLID_TRIGGER))
|
||
{
|
||
// Some NPCs are triggers that can take damage (like antlion grubs). We should hit them.
|
||
if ((pOther->m_takedamage == DAMAGE_NO) || (pOther->m_takedamage == DAMAGE_EVENTS_ONLY))
|
||
return;
|
||
}
|
||
|
||
if (pOther->m_takedamage != DAMAGE_NO)
|
||
{
|
||
trace_t tr, tr2;
|
||
tr = BaseClass::GetTouchTrace();
|
||
Vector vecNormalizedVel = GetAbsVelocity();
|
||
|
||
ClearMultiDamage();
|
||
VectorNormalize(vecNormalizedVel);
|
||
|
||
#if defined(HL2_EPISODIC)
|
||
//!!!HACKHACK - specific hack for ep2_outland_10 to allow crossbow bolts to pass through her bounding box when she's crouched in front of the player
|
||
// (the player thinks they have clear line of sight because Alyx is crouching, but her BBOx is still full-height and blocks crossbow bolts.
|
||
if (GetOwnerEntity() && GetOwnerEntity()->IsPlayer() && pOther->Classify() == CLASS_PLAYER_ALLY_VITAL && FStrEq(STRING(gpGlobals->mapname), "ep2_outland_10"))
|
||
{
|
||
// Change the owner to stop further collisions with Alyx. We do this by making her the owner.
|
||
// The player won't get credit for this kill but at least the bolt won't magically disappear!
|
||
SetOwnerEntity(pOther);
|
||
return;
|
||
}
|
||
#endif//HL2_EPISODIC
|
||
|
||
if (GetOwnerEntity() && GetOwnerEntity()->IsPlayer() && pOther->IsNPC())
|
||
{
|
||
CTakeDamageInfo dmgInfo(this, GetOwnerEntity(), sk_plr_dmg_crossbow.GetFloat(), DMG_NEVERGIB);
|
||
dmgInfo.AdjustPlayerDamageInflictedForSkillLevel();
|
||
CalculateMeleeDamageForce(&dmgInfo, vecNormalizedVel, tr.endpos, 0.7f);
|
||
dmgInfo.SetDamagePosition(tr.endpos);
|
||
pOther->DispatchTraceAttack(dmgInfo, vecNormalizedVel, &tr);
|
||
|
||
CBasePlayer *pPlayer = ToBasePlayer(GetOwnerEntity());
|
||
if (pPlayer)
|
||
{
|
||
gamestats->Event_WeaponHit(pPlayer, true, "weapon_crossbow", dmgInfo);
|
||
}
|
||
|
||
int Rndm = RandomInt(1, 2);
|
||
if (oc_weapon_crossbow_explobolt.GetInt())
|
||
{
|
||
if (Rndm == 1)
|
||
DispatchParticleEffect("Explosion_2", tr.endpos, GetAbsAngles());
|
||
else if (Rndm == 2)
|
||
DispatchParticleEffect("ExplosionCore", tr.endpos, GetAbsAngles());
|
||
|
||
DispatchParticleEffect("Blood_explosion_shower", tr.endpos, GetAbsAngles());
|
||
DispatchParticleEffect((cvar->FindVar("oc_fx_particle_crossbowbolt_explosion_effect_name")->GetString()), tr.endpos, GetAbsAngles()); //<2F><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
//DispatchParticleEffect("explosion_turret_break", tr.endpos, GetAbsAngles());
|
||
RadiusDamage(CTakeDamageInfo(this, NULL, 100, DMG_BLAST), GetAbsOrigin(), 130, CLASS_NONE, NULL);
|
||
EmitSound("Weapon_Crossbow.BoltExplFlesh");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
CTakeDamageInfo dmgInfo(this, GetOwnerEntity(), sk_plr_dmg_crossbow.GetFloat(), DMG_BULLET | DMG_NEVERGIB);
|
||
CalculateMeleeDamageForce(&dmgInfo, vecNormalizedVel, tr.endpos, 0.7f);
|
||
dmgInfo.SetDamagePosition(tr.endpos);
|
||
pOther->DispatchTraceAttack(dmgInfo, vecNormalizedVel, &tr);
|
||
|
||
int Rndm = RandomInt(1, 2);
|
||
if (oc_weapon_crossbow_explobolt.GetInt())
|
||
{
|
||
if (Rndm == 1)
|
||
DispatchParticleEffect("Explosion_2", tr.endpos, GetAbsAngles());
|
||
else if (Rndm == 2)
|
||
DispatchParticleEffect("ExplosionCore", tr.endpos, GetAbsAngles());
|
||
|
||
DispatchParticleEffect((cvar->FindVar("oc_fx_particle_crossbowbolt_explosion_effect_name")->GetString()), tr.endpos, GetAbsAngles()); //<2F><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
//DispatchParticleEffect("explosion_turret_break", tr.endpos, GetAbsAngles());
|
||
RadiusDamage(CTakeDamageInfo(this, NULL, 100, DMG_BLAST), GetAbsOrigin(), 130, CLASS_NONE, NULL);
|
||
EmitSound("Weapon_Crossbow.BoltExpl");
|
||
}
|
||
}
|
||
|
||
ApplyMultiDamage();
|
||
|
||
//Adrian: keep going through the glass.
|
||
if (pOther->GetCollisionGroup() == COLLISION_GROUP_BREAKABLE_GLASS)
|
||
return;
|
||
|
||
if (!pOther->IsAlive())
|
||
{
|
||
// We killed it!
|
||
const surfacedata_t *pdata = physprops->GetSurfaceData(tr.surface.surfaceProps);
|
||
if (pdata->game.material == CHAR_TEX_GLASS)
|
||
{
|
||
return;
|
||
}
|
||
}
|
||
|
||
SetAbsVelocity(Vector(0, 0, 0));
|
||
|
||
// play body "thwack" sound
|
||
EmitSound("Weapon_Crossbow.BoltHitBody");
|
||
|
||
Vector vForward;
|
||
|
||
AngleVectors(GetAbsAngles(), &vForward);
|
||
VectorNormalize(vForward);
|
||
|
||
UTIL_TraceLine(GetAbsOrigin(), GetAbsOrigin() + vForward * 128, MASK_BLOCKLOS, pOther, COLLISION_GROUP_NONE, &tr2);
|
||
|
||
if (tr2.fraction != 1.0f)
|
||
{
|
||
// NDebugOverlay::Box( tr2.endpos, Vector( -16, -16, -16 ), Vector( 16, 16, 16 ), 0, 255, 0, 0, 10 );
|
||
// NDebugOverlay::Box( GetAbsOrigin(), Vector( -16, -16, -16 ), Vector( 16, 16, 16 ), 0, 0, 255, 0, 10 );
|
||
|
||
if (tr2.m_pEnt == NULL || (tr2.m_pEnt && tr2.m_pEnt->GetMoveType() == MOVETYPE_NONE))
|
||
{
|
||
CEffectData data;
|
||
|
||
data.m_vOrigin = tr2.endpos;
|
||
data.m_vNormal = vForward;
|
||
data.m_nEntIndex = tr2.fraction != 1.0f;
|
||
|
||
DispatchEffect("BoltImpact", data);
|
||
|
||
}
|
||
}
|
||
|
||
SetTouch(NULL);
|
||
SetThink(NULL);
|
||
|
||
if (!g_pGameRules->IsMultiplayer())
|
||
{
|
||
UTIL_Remove(this);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
trace_t tr;
|
||
tr = BaseClass::GetTouchTrace();
|
||
|
||
// See if we struck the world
|
||
if (pOther->GetMoveType() == MOVETYPE_NONE && !(tr.surface.flags & SURF_SKY))
|
||
{
|
||
EmitSound("Weapon_Crossbow.BoltHitWorld");
|
||
|
||
// if what we hit is static architecture, can stay around for a while.
|
||
Vector vecDir = GetAbsVelocity();
|
||
float speed = VectorNormalize(vecDir);
|
||
|
||
// See if we should reflect off this surface
|
||
float hitDot = DotProduct(tr.plane.normal, -vecDir);
|
||
|
||
if ((hitDot < 0.5f) && (speed > 100))
|
||
{
|
||
Vector vReflection = 2.0f * tr.plane.normal * hitDot + vecDir;
|
||
|
||
QAngle reflectAngles;
|
||
|
||
VectorAngles(vReflection, reflectAngles);
|
||
|
||
SetLocalAngles(reflectAngles);
|
||
|
||
SetAbsVelocity(vReflection * speed * 0.75f);
|
||
|
||
// Start to sink faster
|
||
SetGravity(1.0f);
|
||
}
|
||
else
|
||
{
|
||
SetThink(&CCrossbowBolt::SUB_Remove);
|
||
SetNextThink(gpGlobals->curtime + 2.0f);
|
||
|
||
//FIXME: We actually want to stick (with hierarchy) to what we've hit
|
||
SetMoveType(MOVETYPE_NONE);
|
||
|
||
Vector vForward;
|
||
|
||
AngleVectors(GetAbsAngles(), &vForward);
|
||
VectorNormalize(vForward);
|
||
|
||
CEffectData data;
|
||
|
||
data.m_vOrigin = tr.endpos;
|
||
data.m_vNormal = vForward;
|
||
data.m_nEntIndex = 0;
|
||
|
||
DispatchEffect("BoltImpact", data);
|
||
|
||
UTIL_ImpactTrace(&tr, DMG_BULLET);
|
||
|
||
AddEffects(EF_NODRAW);
|
||
SetTouch(NULL);
|
||
SetThink(&CCrossbowBolt::SUB_Remove);
|
||
SetNextThink(gpGlobals->curtime + 2.0f);
|
||
|
||
if (m_pGlowSprite != NULL)
|
||
{
|
||
m_pGlowSprite->TurnOn();
|
||
m_pGlowSprite->FadeAndDie(3.0f);
|
||
}
|
||
|
||
int Rndm2 = RandomInt(1, 2);
|
||
if (oc_weapon_crossbow_explobolt.GetInt())
|
||
{
|
||
if (Rndm2 == 1)
|
||
DispatchParticleEffect("Explosion_2", tr.endpos, GetAbsAngles());
|
||
else if (Rndm2 == 2)
|
||
DispatchParticleEffect("ExplosionCore", tr.endpos, GetAbsAngles());
|
||
|
||
DispatchParticleEffect((cvar->FindVar("oc_fx_particle_crossbowbolt_explosion_effect_name")->GetString()), tr.endpos, GetAbsAngles()); //<2F><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
DispatchParticleEffect("explosion_turret_break", tr.endpos, GetAbsAngles());
|
||
RadiusDamage(CTakeDamageInfo(this, NULL, 100, DMG_BLAST), GetAbsOrigin(), 130, CLASS_NONE, NULL);
|
||
EmitSound("Weapon_Crossbow.BoltExpl");
|
||
}
|
||
}
|
||
|
||
// Shoot some sparks
|
||
if (UTIL_PointContents(GetAbsOrigin()) != CONTENTS_WATER)
|
||
{
|
||
g_pEffects->Sparks(GetAbsOrigin());
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// Put a mark unless we've hit the sky
|
||
if ((tr.surface.flags & SURF_SKY) == false)
|
||
{
|
||
UTIL_ImpactTrace(&tr, DMG_BULLET);
|
||
}
|
||
|
||
UTIL_Remove(this);
|
||
}
|
||
}
|
||
|
||
|
||
if (g_pGameRules->IsMultiplayer())
|
||
{
|
||
// SetThink( &CCrossbowBolt::ExplodeThink );
|
||
// SetNextThink( gpGlobals->curtime + 0.1f );
|
||
}
|
||
}
|
||
|
||
void CCrossbowBolt::BoltTouch(CBaseEntity *pOther)
|
||
{
|
||
if (exploBolts)
|
||
{
|
||
ExploBoltTouch(pOther);
|
||
return;
|
||
}
|
||
if (pOther->IsSolidFlagSet(FSOLID_VOLUME_CONTENTS | FSOLID_TRIGGER))
|
||
{
|
||
// Some NPCs are triggers that can take damage (like antlion grubs). We should hit them.
|
||
if ((pOther->m_takedamage == DAMAGE_NO) || (pOther->m_takedamage == DAMAGE_EVENTS_ONLY))
|
||
return;
|
||
}
|
||
|
||
if (pOther->m_takedamage != DAMAGE_NO)
|
||
{
|
||
Vector vForward;
|
||
|
||
AngleVectors(GetAbsAngles(), &vForward);
|
||
VectorNormalize(vForward);
|
||
|
||
trace_t tr, tr2;
|
||
//tr = BaseClass::GetTouchTrace();
|
||
|
||
UTIL_TraceHull(GetAbsOrigin() - vForward * 3, GetAbsOrigin() + vForward * 128, Vector(-5, -5, -5), Vector(5, 5, 5), MASK_SHOT, this, COLLISION_GROUP_NONE, &tr);
|
||
|
||
Vector vecNormalizedVel = GetAbsVelocity();
|
||
|
||
bool isNpc = (pOther->IsNPC() && pOther->IsAlive()) || FStrEq(pOther->GetClassname(), "prop_ragdoll") || FStrEq(pOther->GetClassname(), "physics_prop_ragdoll") || FStrEq(pOther->GetClassname(), "prop_ragdoll_attached");
|
||
|
||
ClearMultiDamage();
|
||
VectorNormalize(vecNormalizedVel);
|
||
|
||
#if defined(HL2_EPISODIC)
|
||
//!!!HACKHACK - specific hack for ep2_outland_10 to allow crossbow bolts to pass through her bounding box when she's crouched in front of the player
|
||
// (the player thinks they have clear line of sight because Alyx is crouching, but her BBOx is still full-height and blocks crossbow bolts.
|
||
if (GetOwnerEntity() && GetOwnerEntity()->IsPlayer() && pOther->Classify() == CLASS_PLAYER_ALLY_VITAL && FStrEq(STRING(gpGlobals->mapname), "ep2_outland_10"))
|
||
{
|
||
// Change the owner to stop further collisions with Alyx. We do this by making her the owner.
|
||
// The player won't get credit for this kill but at least the bolt won't magically disappear!
|
||
SetOwnerEntity(pOther);
|
||
return;
|
||
}
|
||
#endif//HL2_EPISODIC
|
||
|
||
if (GetOwnerEntity() && GetOwnerEntity()->IsPlayer() && pOther->IsNPC())
|
||
{
|
||
CTakeDamageInfo dmgInfo(this, GetOwnerEntity(), sk_plr_dmg_crossbow.GetFloat(), DMG_NEVERGIB);
|
||
dmgInfo.AdjustPlayerDamageInflictedForSkillLevel();
|
||
CalculateMeleeDamageForce(&dmgInfo, vecNormalizedVel, tr.endpos, 0.7f);
|
||
dmgInfo.SetDamagePosition(tr.endpos);
|
||
pOther->DispatchTraceAttack(dmgInfo, vecNormalizedVel, &tr);
|
||
|
||
CBasePlayer *pPlayer = ToBasePlayer(GetOwnerEntity());
|
||
if (pPlayer)
|
||
{
|
||
gamestats->Event_WeaponHit(pPlayer, true, "weapon_crossbow", dmgInfo);
|
||
}
|
||
|
||
}
|
||
else
|
||
{
|
||
CTakeDamageInfo dmgInfo(this, GetOwnerEntity(), sk_plr_dmg_crossbow.GetFloat(), DMG_BULLET | DMG_NEVERGIB);
|
||
CalculateMeleeDamageForce(&dmgInfo, vecNormalizedVel, tr.endpos, 0.7f);
|
||
dmgInfo.SetDamagePosition(tr.endpos);
|
||
pOther->DispatchTraceAttack(dmgInfo, vecNormalizedVel, &tr);
|
||
}
|
||
|
||
ApplyMultiDamage();
|
||
|
||
//Adrian: keep going through the glass.
|
||
if (pOther->GetCollisionGroup() == COLLISION_GROUP_BREAKABLE_GLASS)
|
||
return;
|
||
|
||
if (!pOther->IsAlive())
|
||
{
|
||
// We killed it!
|
||
const surfacedata_t *pdata = physprops->GetSurfaceData(tr.surface.surfaceProps);
|
||
if (pdata->game.material == CHAR_TEX_GLASS)
|
||
{
|
||
return;
|
||
}
|
||
}
|
||
|
||
SetAbsVelocity(Vector(0, 0, 0));
|
||
|
||
// play body "thwack" sound
|
||
EmitSound("Weapon_Crossbow.BoltHitBody");
|
||
|
||
UTIL_TraceLine(GetAbsOrigin(), GetAbsOrigin() + vForward * 128, MASK_BLOCKLOS, pOther, COLLISION_GROUP_NONE, &tr2);
|
||
|
||
m_iStopped = 0;
|
||
|
||
TryAttachBolt(pOther, vForward, tr, isNpc);
|
||
|
||
if (!m_iStopped)
|
||
{
|
||
if (tr2.fraction != 1.0f)
|
||
{
|
||
// NDebugOverlay::Box( tr2.endpos, Vector( -16, -16, -16 ), Vector( 16, 16, 16 ), 0, 255, 0, 0, 10 );
|
||
// NDebugOverlay::Box( GetAbsOrigin(), Vector( -16, -16, -16 ), Vector( 16, 16, 16 ), 0, 0, 255, 0, 10 );
|
||
|
||
if (tr2.m_pEnt == NULL || (tr2.m_pEnt && tr2.m_pEnt->GetMoveType() == MOVETYPE_NONE))
|
||
{
|
||
CEffectData data;
|
||
|
||
data.m_vOrigin = tr2.endpos;
|
||
data.m_vNormal = vForward;
|
||
data.m_nEntIndex = tr2.fraction != 1.0f;
|
||
|
||
DispatchEffect("BoltImpact", data);
|
||
}
|
||
}
|
||
}
|
||
|
||
SetTouch(NULL);
|
||
|
||
if (!m_iStopped)
|
||
SetThink(NULL);
|
||
|
||
if (!g_pGameRules->IsMultiplayer())
|
||
{
|
||
if (!m_iStopped)
|
||
UTIL_Remove(this);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
trace_t tr;
|
||
tr = BaseClass::GetTouchTrace();
|
||
|
||
// See if we struck the world
|
||
if (pOther->GetMoveType() == MOVETYPE_NONE && !(tr.surface.flags & SURF_SKY))
|
||
{
|
||
EmitSound("Weapon_Crossbow.BoltHitWorld");
|
||
|
||
// if what we hit is static architecture, can stay around for a while.
|
||
Vector vecDir = GetAbsVelocity();
|
||
float speed = VectorNormalize(vecDir);
|
||
|
||
// See if we should reflect off this surface
|
||
float hitDot = DotProduct(tr.plane.normal, -vecDir);
|
||
|
||
if ((hitDot < 0.5f) && (speed > 100))
|
||
{
|
||
Vector vReflection = 2.0f * tr.plane.normal * hitDot + vecDir;
|
||
|
||
QAngle reflectAngles;
|
||
|
||
VectorAngles(vReflection, reflectAngles);
|
||
|
||
SetLocalAngles(reflectAngles);
|
||
|
||
SetAbsVelocity(vReflection * speed * 0.75f);
|
||
|
||
// Start to sink faster
|
||
SetGravity(1.0f);
|
||
}
|
||
else
|
||
{
|
||
if (!m_iStopped)
|
||
{
|
||
SetThink(&CCrossbowBolt::SUB_Remove);
|
||
SetNextThink(gpGlobals->curtime + 2.0f);
|
||
}
|
||
//FIXME: We actually want to stick (with hierarchy) to what we've hit
|
||
SetMoveType(MOVETYPE_NONE);
|
||
|
||
Vector vForward;
|
||
|
||
AngleVectors(GetAbsAngles(), &vForward);
|
||
VectorNormalize(vForward);
|
||
|
||
CEffectData data;
|
||
|
||
data.m_vOrigin = tr.endpos;
|
||
data.m_vNormal = vForward;
|
||
data.m_nEntIndex = 0;
|
||
|
||
DispatchEffect("BoltImpact", data);
|
||
|
||
UTIL_ImpactTrace(&tr, DMG_BULLET);
|
||
|
||
AddEffects(EF_NODRAW);
|
||
SetTouch(NULL);
|
||
|
||
if (!m_iStopped)
|
||
{
|
||
SetThink(&CCrossbowBolt::SUB_Remove);
|
||
SetNextThink(gpGlobals->curtime + 2.0f);
|
||
}
|
||
|
||
if (m_pGlowSprite != NULL)
|
||
{
|
||
m_pGlowSprite->TurnOn();
|
||
m_pGlowSprite->FadeAndDie(3.0f);
|
||
}
|
||
}
|
||
|
||
// Shoot some sparks
|
||
if (UTIL_PointContents(GetAbsOrigin()) != CONTENTS_WATER)
|
||
{
|
||
g_pEffects->Sparks(GetAbsOrigin());
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// Put a mark unless we've hit the sky
|
||
if ((tr.surface.flags & SURF_SKY) == false)
|
||
{
|
||
UTIL_ImpactTrace(&tr, DMG_BULLET);
|
||
}
|
||
|
||
if (!m_iStopped)
|
||
UTIL_Remove(this);
|
||
}
|
||
}
|
||
|
||
if (g_pGameRules->IsMultiplayer())
|
||
{
|
||
// SetThink( &CCrossbowBolt::ExplodeThink );
|
||
// SetNextThink( gpGlobals->curtime + 0.1f );
|
||
}
|
||
}
|
||
|
||
void CCrossbowBolt::TryAttachBolt(CBaseEntity *pOther, const Vector &vForward, trace_t &tr, bool isNpc)
|
||
{
|
||
if (!cvar->FindVar("oc_ragdoll_serverside")->GetBool())
|
||
return;
|
||
|
||
if (!isNpc)
|
||
return;
|
||
|
||
bool attach_arrow = false;
|
||
//m_iStopped = 1;
|
||
|
||
if (pOther)
|
||
{
|
||
CTraceFilterSimpleList *m_pIgnoreList = new CTraceFilterSimpleList(COLLISION_GROUP_NONE);
|
||
m_pIgnoreList->AddEntityToIgnore(this);
|
||
m_pIgnoreList->AddEntityToIgnore(pOther);
|
||
|
||
trace_t Check;
|
||
UTIL_TraceLine(GetAbsOrigin() - vForward * 3, GetAbsOrigin() + vForward * 128, MASK_SHOT, m_pIgnoreList, &Check);
|
||
|
||
CRagdollProp *pRagdoll = dynamic_cast<CRagdollProp*>(Check.m_pEnt);
|
||
if (pRagdoll)
|
||
m_pIgnoreList->AddEntityToIgnore(pRagdoll);
|
||
|
||
UTIL_TraceLine(GetAbsOrigin() - vForward * 3, GetAbsOrigin() + vForward * 128, MASK_SHOT, m_pIgnoreList, &Check);
|
||
|
||
delete m_pIgnoreList;
|
||
|
||
if (!Check.DidHit())
|
||
attach_arrow = true;
|
||
//return;
|
||
}
|
||
|
||
//this->SUB_StartFadeOut(15, false);
|
||
|
||
if (isNpc && !pOther->IsAlive())
|
||
{
|
||
trace_t tr3;
|
||
|
||
UTIL_TraceLine(GetAbsOrigin() - vForward * 3, GetAbsOrigin() + vForward * 128, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr3);
|
||
|
||
bool hit = attach_arrow ? true : tr3.DidHit();
|
||
|
||
if (hit)
|
||
{
|
||
UTIL_TraceHull(GetAbsOrigin() - vForward * 3, GetAbsOrigin() + vForward * 128, Vector(-5, -5, -5), Vector(5, 5, 5), MASK_SHOT, this, COLLISION_GROUP_NONE, &tr3);
|
||
//UTIL_TraceLine(GetAbsOrigin(), pOther->GetAbsOrigin(), MASK_SHOT, this, COLLISION_GROUP_NONE, &tr3);
|
||
|
||
CBaseAnimating *pAnimating = pOther->GetBaseAnimating();
|
||
if (pAnimating && tr3.m_pEnt)
|
||
{
|
||
CRagdollProp *pRagdoll = dynamic_cast<CRagdollProp*>(tr3.m_pEnt);
|
||
|
||
if (!pRagdoll)
|
||
return;
|
||
|
||
IPhysicsObject *pBonePhysics = pRagdoll->VPhysicsGetObject();
|
||
if (pRagdoll && pBonePhysics)
|
||
{
|
||
IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT];
|
||
//VPhysicsGetObjectList(pList, ARRAYSIZE(pList), *pRagdoll->GetRagdoll());
|
||
pRagdoll->VPhysicsGetObjectList(pList, ARRAYSIZE(pList));
|
||
pBonePhysics = pList[tr3.physicsbone];
|
||
if (pBonePhysics != NULL)
|
||
{
|
||
/*float mass = pBonePhysics->GetMass();
|
||
if (mass >= 70)
|
||
{
|
||
DevMsg("Bolt: bone mass is too big to attach \n");
|
||
return;
|
||
}*/
|
||
|
||
Vector bonePos;
|
||
QAngle boneAng;
|
||
pRagdoll->GetBonePosition(tr3.physicsbone, bonePos, boneAng);
|
||
|
||
float minBound = /*pBonePhysics != nullptr ? pBonePhysics->GetSphereRadius() : */pOther->BoundingRadius();
|
||
|
||
bool isSmall = minBound <= 25.f;
|
||
|
||
float mult = 0.5f;
|
||
|
||
switch (tr3.hitgroup)
|
||
{
|
||
//Do normal ragdoll stuff if no specific hitgroup was hit.
|
||
case HITGROUP_GENERIC:
|
||
|
||
break;
|
||
|
||
default:
|
||
{
|
||
|
||
}
|
||
|
||
case HITGROUP_HEAD:
|
||
{
|
||
mult = isSmall ? 2.f : 0.17f;
|
||
break;
|
||
}
|
||
case HITGROUP_LEFTARM:
|
||
mult = isSmall ? 2.f : 0.17f;
|
||
break;
|
||
|
||
case HITGROUP_RIGHTARM:
|
||
mult = isSmall ? 2.f : 0.17f;
|
||
break;
|
||
|
||
case HITGROUP_CHEST:
|
||
mult = isSmall ? 2.f : 0.17f;
|
||
break;
|
||
|
||
case HITGROUP_STOMACH:
|
||
mult = isSmall ? 2.f : 0.17f;
|
||
break;
|
||
|
||
case HITGROUP_LEFTLEG:
|
||
mult = isSmall ? 2.f : 0.17f;
|
||
break;
|
||
|
||
case HITGROUP_RIGHTLEG:
|
||
mult = isSmall ? 2.f : 0.17f;
|
||
break;
|
||
}
|
||
|
||
float minVal = minBound * mult;
|
||
|
||
minVal = Clamp(minVal, 5.f, 200.f);
|
||
|
||
//CHAR_FLESH STOMACH HEAD LEG
|
||
|
||
//trace_t rt2;
|
||
trace_t trEnd;
|
||
|
||
Vector vForward;
|
||
AngleVectors(GetAbsAngles(), &vForward);
|
||
VectorNormalize(vForward);
|
||
|
||
CTraceFilterSimpleList *m_pIgnoreList = new CTraceFilterSimpleList(COLLISION_GROUP_NONE);
|
||
m_pIgnoreList->AddEntityToIgnore(pRagdoll);
|
||
m_pIgnoreList->AddEntityToIgnore(this);
|
||
UTIL_TraceLine(GetAbsOrigin(), GetAbsOrigin() + vForward * MAX_TRACE_LENGTH, MASK_SHOT, m_pIgnoreList, &trEnd);
|
||
delete m_pIgnoreList;
|
||
m_pIgnoreList = NULL;
|
||
|
||
/*UTIL_TraceLine(GetAbsOrigin() + vForward * 30, GetAbsOrigin() + vForward * 128, MASK_SHOT, this, COLLISION_GROUP_NONE, &rt2);
|
||
|
||
int i = 0;
|
||
while ((!rt2.m_pEnt || (rt2.m_pEnt && rt2.m_pEnt == pRagdoll)))
|
||
{
|
||
UTIL_TraceLine(GetAbsOrigin() + vForward * i, GetAbsOrigin() + vForward * 128, MASK_SHOT, this, COLLISION_GROUP_NONE, &rt2);
|
||
|
||
if (i >= 100)
|
||
break;
|
||
i++;
|
||
}*/
|
||
|
||
if (trEnd.m_pEnt)
|
||
{
|
||
m_iStopped = 1;
|
||
|
||
if (m_pGlowSprite != NULL)
|
||
{
|
||
m_pGlowSprite->FollowEntity(NULL);
|
||
m_pGlowSprite->SetScale(0.0f);
|
||
m_pGlowSprite->TurnOff();
|
||
m_pGlowSprite->FadeAndDie(0.1f);
|
||
m_pGlowSprite->Remove();
|
||
}
|
||
|
||
trace_t tr2;
|
||
CTraceFilterSimpleList *m_pIgnoreList = new CTraceFilterSimpleList(COLLISION_GROUP_NONE);
|
||
m_pIgnoreList->AddEntityToIgnore(pRagdoll);
|
||
m_pIgnoreList->AddEntityToIgnore(this);
|
||
UTIL_TraceLine(GetAbsOrigin(), GetAbsOrigin() + vForward * MAX_TRACE_LENGTH, MASK_SHOT, m_pIgnoreList, &tr2);
|
||
delete m_pIgnoreList;
|
||
m_pIgnoreList = NULL;
|
||
//float minVal = isSmall ? minBound : minBound * 0.3f;// +(rt2.DidHitWorld() ? 10.f : 15.f);
|
||
|
||
//Vector endPos = tr.endpos - vForward * minVal;
|
||
Vector endPos = tr2.endpos - vForward * minVal;
|
||
//NDebugOverlay::Line(tr2.endpos, tr2.endpos - vForward * minVal, 255, 0, 0, true, 10.0f);
|
||
//NDebugOverlay::Line(tr.endpos, rt2.endpos - vForward * minVal, 0, 255, 0, true, 10.0f);
|
||
//NDebugOverlay::Line(rt2.endpos, rt2.endpos + vForward * -minVal, 0, 0, 255, true, 10.0f);
|
||
//NDebugOverlay::Line(rt2.endpos, rt2.startpos, 255, 255, 255, true, 10.0f);
|
||
|
||
if (!attach_arrow)
|
||
{
|
||
this->SetAbsOrigin(endPos);
|
||
|
||
pBonePhysics->SetPosition(endPos, boneAng, true);
|
||
|
||
CharacterEmitSound("Weapon_Crossbow.BoltSkewer", endPos, CHAN_WEAPON);
|
||
}
|
||
|
||
if (this)
|
||
StopParticleEffects(this);
|
||
|
||
SetModel("models/crossbow_bolt_physic.mdl");//("models/crossbow_bolt_physic.mdl");
|
||
|
||
this->SetCollisionGroup(COLLISION_GROUP_DEBRIS);
|
||
//SetModelScale(0.f);
|
||
|
||
attach_arrow = attach_arrow ? true : pRagdoll->VPhysicsGetObject()->GetMass() >= 100;
|
||
|
||
if (trEnd.DidHitWorld() && !attach_arrow)
|
||
{
|
||
this->SetMoveType(MOVETYPE_NONE);
|
||
this->SetSolid(SOLID_VPHYSICS);
|
||
this->VPhysicsInitStatic();
|
||
|
||
IPhysicsObject *pArrowPhysics = VPhysicsGetObject();
|
||
if (pArrowPhysics)
|
||
{
|
||
IPhysicsConstraint *m_pConstraint;
|
||
|
||
constraint_fixedparams_t constraintParams;
|
||
constraintParams.Defaults();
|
||
constraintParams.InitWithCurrentObjectState(pBonePhysics, pArrowPhysics);
|
||
constraintParams.constraint.Defaults();
|
||
constraintParams.constraint.forceLimit = ImpulseScale(pBonePhysics->GetMass(), 20);
|
||
constraintParams.constraint.torqueLimit = ImpulseScale(pBonePhysics->GetMass(), 80);
|
||
m_pConstraint = physenv->CreateFixedConstraint(pBonePhysics, pArrowPhysics, NULL, constraintParams);
|
||
m_pConstraint->SetGameData((void *)this);
|
||
|
||
m_aConstraints.AddToTail(m_pConstraint);
|
||
|
||
m_oCurrentConstraint = m_pConstraint;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
//this->SetParent(rt2.m_pEnt);
|
||
this->SetMoveType(MOVETYPE_NONE);
|
||
this->SetSolid(SOLID_VPHYSICS);
|
||
this->CreateVPhysics();
|
||
AddSolidFlags(FSOLID_NOT_STANDABLE);
|
||
//this->SetParent(pRagdoll);
|
||
|
||
IPhysicsObject *pArrowPhysics = VPhysicsGetObject();
|
||
if (pArrowPhysics)
|
||
{
|
||
IPhysicsConstraint *m_pConstraint;
|
||
|
||
constraint_fixedparams_t constraintParams;
|
||
constraintParams.Defaults();
|
||
constraintParams.InitWithCurrentObjectState(pArrowPhysics, pBonePhysics);
|
||
constraintParams.constraint.Defaults();
|
||
constraintParams.constraint.forceLimit = ImpulseScale(pBonePhysics->GetMass(), 20);
|
||
constraintParams.constraint.torqueLimit = ImpulseScale(pBonePhysics->GetMass(), 80);
|
||
m_pConstraint = physenv->CreateFixedConstraint(pArrowPhysics, pBonePhysics, NULL, constraintParams);
|
||
m_pConstraint->SetGameData((void *)this);
|
||
|
||
m_aConstraints.AddToTail(m_pConstraint);
|
||
|
||
m_oCurrentConstraint = m_pConstraint;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
int CCrossbowBolt::VPhysicsGetObjectList(IPhysicsObject **pList, int listMax, ragdoll_t &pRagdoll)
|
||
{
|
||
|
||
for (int i = 0; i < pRagdoll.listCount; i++)
|
||
{
|
||
if (i < listMax)
|
||
{
|
||
pList[i] = pRagdoll.list[i].pObject;
|
||
}
|
||
}
|
||
|
||
return pRagdoll.listCount;
|
||
}
|
||
|
||
|
||
//-----------------------------------------------------------------------------
|
||
// Purpose:
|
||
//-----------------------------------------------------------------------------
|
||
void CCrossbowBolt::BubbleThink(void)
|
||
{
|
||
if (m_iStopped == 2)
|
||
{
|
||
UTIL_Remove(this);
|
||
return;
|
||
}
|
||
|
||
if (m_iStopped == 1)
|
||
{
|
||
SetNextThink(gpGlobals->curtime + oc_weapon_crossbow_bolt_lifetime.GetInt());
|
||
m_iStopped = 2;
|
||
}
|
||
else
|
||
{
|
||
QAngle angNewAngles;
|
||
|
||
VectorAngles(GetAbsVelocity(), angNewAngles);
|
||
SetAbsAngles(angNewAngles);
|
||
|
||
SetNextThink(gpGlobals->curtime + 0.1f);
|
||
}
|
||
|
||
// Make danger sounds out in front of me, to scare snipers back into their hole
|
||
CSoundEnt::InsertSound(SOUND_DANGER_SNIPERONLY, GetAbsOrigin() + GetAbsVelocity() * 0.2, 120.0f, 0.5f, this, SOUNDENT_CHANNEL_REPEATED_DANGER);
|
||
|
||
if (GetWaterLevel() == 0)
|
||
return;
|
||
|
||
UTIL_BubbleTrail(GetAbsOrigin() - GetAbsVelocity() * 0.1f, GetAbsOrigin(), 5);
|
||
}
|
||
|
||
|
||
|
||
|
||
LINK_ENTITY_TO_CLASS(weapon_crossbow, CWeaponCrossbow);
|
||
|
||
PRECACHE_WEAPON_REGISTER(weapon_crossbow);
|
||
|
||
IMPLEMENT_SERVERCLASS_ST(CWeaponCrossbow, DT_WeaponCrossbow)
|
||
END_SEND_TABLE()
|
||
|
||
BEGIN_DATADESC(CWeaponCrossbow)
|
||
|
||
//DEFINE_FIELD( m_bInZoom, FIELD_BOOLEAN ),
|
||
DEFINE_FIELD(exploBolts, FIELD_BOOLEAN),
|
||
DEFINE_FIELD(m_bMustReload, FIELD_BOOLEAN),
|
||
DEFINE_FIELD(m_nChargeState, FIELD_INTEGER),
|
||
DEFINE_FIELD(m_hChargerSprite, FIELD_EHANDLE),
|
||
|
||
END_DATADESC()
|
||
|
||
acttable_t CWeaponCrossbow::m_acttable[] =
|
||
{
|
||
{ ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_CROSSBOW, false },
|
||
{ ACT_HL2MP_RUN, ACT_HL2MP_RUN_CROSSBOW, false },
|
||
{ ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_CROSSBOW, false },
|
||
{ ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_CROSSBOW, false },
|
||
{ ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_SHOTGUN, false },
|
||
{ ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_SHOTGUN, false },
|
||
{ ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_CROSSBOW, false },
|
||
//{ ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SHOTGUN, false }, // END
|
||
|
||
{ ACT_IDLE_RELAXED, ACT_IDLE_SHOTGUN_RELAXED, false },//never aims
|
||
{ ACT_IDLE_STIMULATED, ACT_IDLE_SHOTGUN_STIMULATED, false },
|
||
|
||
{ 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
|
||
|
||
{ 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
|
||
|
||
{ 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_CROUCH, ACT_RUN_CROUCH_RIFLE, true },
|
||
{ ACT_RUN_CROUCH_AIM, ACT_RUN_CROUCH_AIM_RIFLE, true },
|
||
|
||
{ ACT_IDLE, ACT_IDLE_SMG1, true },
|
||
{ ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_SHOTGUN, true },
|
||
{ ACT_IDLE_AGITATED, ACT_IDLE_ANGRY_SHOTGUN, true },
|
||
{ ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SHOTGUN, true },
|
||
{ ACT_RELOAD, ACT_RELOAD_SMG1, true },
|
||
{ ACT_WALK_AIM, ACT_WALK_AIM_SHOTGUN, true },
|
||
{ ACT_RUN_AIM, ACT_RUN_AIM_SHOTGUN, true },
|
||
{ ACT_GESTURE_RANGE_ATTACK1, ACT_GESTURE_RANGE_ATTACK_SHOTGUN, true },
|
||
{ ACT_RELOAD_LOW, ACT_RELOAD_SHOTGUN_LOW, false },
|
||
{ ACT_RANGE_ATTACK1_LOW, ACT_RANGE_ATTACK_SHOTGUN_LOW, false },
|
||
{ ACT_COVER_LOW, ACT_COVER_SMG1_LOW, false },
|
||
{ ACT_RANGE_AIM_LOW, ACT_RANGE_AIM_SMG1_LOW, false },
|
||
{ ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_SMG1, false },
|
||
{ ACT_WALK, ACT_WALK_RIFLE, false },
|
||
};
|
||
|
||
IMPLEMENT_ACTTABLE(CWeaponCrossbow);
|
||
|
||
//-----------------------------------------------------------------------------
|
||
// Purpose: Constructor
|
||
//-----------------------------------------------------------------------------
|
||
CWeaponCrossbow::CWeaponCrossbow(void)
|
||
{
|
||
m_bReloadsSingly = true;
|
||
//m_bInZoom = false;
|
||
m_bMustReload = false;
|
||
Once = false;
|
||
exploBolts = false;
|
||
}
|
||
|
||
#define CROSSBOW_GLOW_SPRITE "sprites/light_glow02_noz.vmt"
|
||
#define CROSSBOW_GLOW_SPRITE2 "sprites/blueflare1.vmt"
|
||
|
||
//-----------------------------------------------------------------------------
|
||
// Purpose:
|
||
//-----------------------------------------------------------------------------
|
||
void CWeaponCrossbow::Precache(void)
|
||
{
|
||
UTIL_PrecacheOther("crossbow_bolt");
|
||
|
||
PrecacheScriptSound("Weapon_Crossbow.BoltHitBody");
|
||
PrecacheScriptSound("Weapon_Crossbow.BoltHitWorld");
|
||
PrecacheScriptSound("Weapon_Crossbow.BoltSkewer");
|
||
PrecacheScriptSound("Weapon_Crossbow.BoltExpl");
|
||
PrecacheScriptSound("Weapon_Crossbow.BoltExplFlesh");
|
||
PrecacheScriptSound("Weapon_SniperRifle.Draw");
|
||
PrecacheModel(CROSSBOW_GLOW_SPRITE);
|
||
PrecacheModel(CROSSBOW_GLOW_SPRITE2);
|
||
|
||
BaseClass::Precache();
|
||
}
|
||
|
||
//-----------------------------------------------------------------------------
|
||
// Purpose:
|
||
//-----------------------------------------------------------------------------
|
||
void CWeaponCrossbow::PrimaryAttack(void)
|
||
{
|
||
CBasePlayer *pPlayer = ToBasePlayer(GetOwner());
|
||
if (!pPlayer)
|
||
return;
|
||
|
||
if (IsNearWall() || GetOwnerIsRunning())
|
||
{
|
||
return;
|
||
}
|
||
|
||
if ( /*m_bInZoom*/IsScopeSighted() && g_pGameRules->IsMultiplayer())
|
||
{
|
||
// FireSniperBolt();
|
||
FireBolt();
|
||
}
|
||
else
|
||
{
|
||
FireBolt();
|
||
}
|
||
|
||
// Signal a reload
|
||
m_bMustReload = true;
|
||
|
||
SetWeaponIdleTime(gpGlobals->curtime + SequenceDuration(ACT_VM_PRIMARYATTACK));
|
||
|
||
|
||
if (pPlayer)
|
||
{
|
||
m_iPrimaryAttacks++;
|
||
gamestats->Event_WeaponFired(pPlayer, true, GetClassname());
|
||
}
|
||
}
|
||
|
||
//-----------------------------------------------------------------------------
|
||
// Purpose:
|
||
//-----------------------------------------------------------------------------
|
||
void CWeaponCrossbow::SecondaryAttack(void)
|
||
{
|
||
SendWeaponAnim(GetWpnData().animData[m_bFireMode].WeaponSwitchAnim);
|
||
//SendWeaponAnim(ACT_VM_RELOAD);
|
||
|
||
if (!exploBolts)
|
||
exploBolts = true;
|
||
else
|
||
exploBolts = false;
|
||
|
||
m_flNextSwitchTime = gpGlobals->curtime + GetViewModelSequenceDuration();
|
||
m_flNextPrimaryAttack = gpGlobals->curtime + GetViewModelSequenceDuration();
|
||
m_flNextSecondaryAttack = gpGlobals->curtime + GetViewModelSequenceDuration();
|
||
|
||
|
||
if (IsScopeSighted())//(m_bInZoom)
|
||
ToggleZoom(ToBasePlayer(GetOwner()), false);
|
||
}
|
||
|
||
//-----------------------------------------------------------------------------
|
||
// Purpose:
|
||
// Output : Returns true on success, false on failure.
|
||
//-----------------------------------------------------------------------------
|
||
bool CWeaponCrossbow::Reload(void)
|
||
{
|
||
if (BaseClass::Reload())
|
||
{
|
||
m_bMustReload = false;
|
||
return true;
|
||
|
||
CBasePlayer *pPlayer = ToBasePlayer(GetOwner());
|
||
if (!pPlayer)
|
||
return false;
|
||
|
||
/*if (pPlayer->SetFOV(this, 0, 0.2f))
|
||
{
|
||
m_bInZoom = false;
|
||
// Send a message to hide the scope
|
||
//pPlayer->SetFOV(this, 0, 0.1f);
|
||
CSingleUserRecipientFilter filter(pPlayer);
|
||
UserMessageBegin(filter, "ShowScope");
|
||
WRITE_BYTE(0);
|
||
MessageEnd();
|
||
cvar->FindVar("oc_state_InSecondFire")->SetValue(0);
|
||
cvar->FindVar("oc_state_InSecondFire_crossbow")->SetValue(0);
|
||
}*/
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
//-----------------------------------------------------------------------------
|
||
// Purpose:
|
||
//-----------------------------------------------------------------------------
|
||
/*void CWeaponCrossbow::ItemBusyFrame( void )
|
||
{
|
||
// Allow zoom toggling even when we're reloading
|
||
CheckZoomToggle();
|
||
}*/
|
||
|
||
//-----------------------------------------------------------------------------
|
||
// Purpose:
|
||
//-----------------------------------------------------------------------------
|
||
void CWeaponCrossbow::ItemPostFrame(void)
|
||
{
|
||
/*if (IsNearWall() || GetOwnerIsRunning())
|
||
{
|
||
|
||
|
||
if (cvar->FindVar("oc_state_InSecondFire")->GetInt() && !Once)
|
||
{
|
||
ToggleZoom();
|
||
Once = true;
|
||
}
|
||
}
|
||
else
|
||
Once = false;
|
||
// Allow zoom toggling
|
||
CheckZoomToggle();*/
|
||
|
||
if (m_bMustReload && HasWeaponIdleTimeElapsed())
|
||
{
|
||
Reload();
|
||
}
|
||
|
||
BaseClass::ItemPostFrame();
|
||
}
|
||
|
||
/*class CTraceFilterSkipPlayerAndViewModel : public CTraceFilter
|
||
{
|
||
public:
|
||
virtual bool ShouldHitEntity(IHandleEntity *pServerEntity, int contentsMask)
|
||
{
|
||
// Test against the vehicle too?
|
||
// FLASHLIGHTFIXME: how do you know that you are actually inside of the vehicle?
|
||
CBaseEntity *pEntity = EntityFromEntityHandle(pServerEntity);
|
||
if (!pEntity)
|
||
return true;
|
||
|
||
if ((dynamic_cast<CBaseViewModel *>(pEntity) != NULL) ||
|
||
(dynamic_cast<CBasePlayer *>(pEntity) != NULL) ||
|
||
pEntity->GetCollisionGroup() == COLLISION_GROUP_DEBRIS ||
|
||
pEntity->GetCollisionGroup() == COLLISION_GROUP_INTERACTIVE_DEBRIS)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
};*/
|
||
//-----------------------------------------------------------------------------
|
||
// Purpose:
|
||
//-----------------------------------------------------------------------------
|
||
void CWeaponCrossbow::FireBolt(void)
|
||
{
|
||
if (m_iClip1 <= 0)
|
||
{
|
||
if (!m_bFireOnEmpty)
|
||
{
|
||
Reload();
|
||
|
||
cvar->FindVar("oc_state_InSecondFire")->SetValue(0);
|
||
cvar->FindVar("oc_state_InSecondFire_crossbow")->SetValue(0);
|
||
}
|
||
else
|
||
{
|
||
WeaponSound(EMPTY);
|
||
m_flNextPrimaryAttack = 0.15;
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
CBasePlayer *pOwner = ToBasePlayer(GetOwner());
|
||
|
||
if (pOwner == NULL)
|
||
return;
|
||
|
||
pOwner->RumbleEffect(RUMBLE_357, 0, RUMBLE_FLAG_RESTART);
|
||
|
||
Vector vecAiming = pOwner->GetAutoaimVector(0);
|
||
Vector vecSrc = pOwner->Weapon_ShootPosition();
|
||
|
||
QAngle angAiming, WeapAngle;
|
||
VectorAngles(vecAiming, angAiming);
|
||
|
||
CBasePlayer *pPlayer = ToBasePlayer(GetOwner());
|
||
Vector vecThrow;
|
||
// Don't autoaim on grenade tosses
|
||
AngleVectors(pPlayer->EyeAngles() + pPlayer->GetPunchAngle(), &vecThrow);
|
||
//VectorScale(vecThrow, 1000.0f, vecThrow);
|
||
Vector vForward, vRight, vUp, vThrowPos, vThrowVel;
|
||
pPlayer->EyeVectors(&vForward, &vRight, &vUp);
|
||
|
||
vThrowPos = GetClientTracerVector();
|
||
/*vThrowPos.x = cvar->FindVar("oc_muzzle_tracer_x")->GetFloat();
|
||
vThrowPos.y = cvar->FindVar("oc_muzzle_tracer_y")->GetFloat();
|
||
vThrowPos.z = cvar->FindVar("oc_muzzle_tracer_z")->GetFloat();*/
|
||
trace_t tr;
|
||
UTIL_TraceLine(pOwner->EyePosition(), vThrowPos, MASK_SHOT, pOwner, COLLISION_GROUP_NONE, &tr);
|
||
|
||
WeapAngle = GetClientMuzzleAngles();
|
||
/*WeapAngle.x = cvar->FindVar("oc_muzzle_angle_x")->GetFloat();
|
||
WeapAngle.y = cvar->FindVar("oc_muzzle_angle_x")->GetFloat();
|
||
WeapAngle.z = cvar->FindVar("oc_muzzle_angle_x")->GetFloat();*/
|
||
|
||
Vector Muzzle = pOwner->Weapon_ShootPosition(1);
|
||
|
||
#if defined(HL2_EPISODIC)
|
||
// !!!HACK - the other piece of the Alyx crossbow bolt hack for Outland_10 (see ::BoltTouch() for more detail)
|
||
if (FStrEq(STRING(gpGlobals->mapname), "ep2_outland_10"))
|
||
{
|
||
trace_t tr;
|
||
UTIL_TraceLine(tr.endpos, vecSrc + vecAiming * 24.0f, MASK_SOLID, pOwner, COLLISION_GROUP_NONE, &tr);
|
||
|
||
if (tr.m_pEnt != NULL && tr.m_pEnt->Classify() == CLASS_PLAYER_ALLY_VITAL)
|
||
{
|
||
// If Alyx is right in front of the player, make sure the bolt starts outside of the player's BBOX, or the bolt
|
||
// will instantly collide with the player after the owner of the bolt is switched to Alyx in ::BoltTouch(). We
|
||
// avoid this altogether by making it impossible for the bolt to collide with the player.
|
||
vecSrc += vecAiming * 24.0f;
|
||
}
|
||
}
|
||
#endif
|
||
|
||
CCrossbowBolt *pBolt = CCrossbowBolt::BoltCreate(tr.endpos, angAiming, pOwner, exploBolts);
|
||
|
||
pBolt->exploBolts = exploBolts;
|
||
|
||
pBolt->SetLocalOrigin(tr.endpos);
|
||
pBolt->SetLocalVelocity(tr.endpos);
|
||
|
||
if (pOwner->GetWaterLevel() == 3)
|
||
{
|
||
pBolt->SetAbsVelocity(vecAiming * BOLT_WATER_VELOCITY);
|
||
}
|
||
else
|
||
{
|
||
pBolt->SetAbsVelocity(vecAiming * BOLT_AIR_VELOCITY);
|
||
}
|
||
|
||
m_iClip1--;
|
||
|
||
float X = -2.f;
|
||
if (abs(pOwner->LocalEyeAngles().x) + abs(X) >= 89.0f)
|
||
{
|
||
X = X < 0 ? (89.0f - abs(pOwner->LocalEyeAngles().x)) * -1.f : 89.0f - abs(pOwner->LocalEyeAngles().x);
|
||
}
|
||
pOwner->ViewPunch(QAngle(X, 0, 0));
|
||
|
||
WeaponSound(SINGLE);
|
||
WeaponSound(SPECIAL2);
|
||
|
||
CSoundEnt::InsertSound(SOUND_COMBAT, GetAbsOrigin(), 200, 0.2);
|
||
|
||
SendWeaponAnim(GetPrimaryAttackActivity());
|
||
|
||
/*if (!m_iClip1 && pOwner->GetAmmoCount(m_iPrimaryAmmoType) <= 0)
|
||
{
|
||
// HEV suit - indicate out of ammo condition
|
||
pOwner->SetSuitUpdate("!HEV_AMO0", FALSE, 0);
|
||
}*/
|
||
|
||
m_flNextPrimaryAttack = m_flNextSecondaryAttack = gpGlobals->curtime + 0.75;
|
||
|
||
DoLoadEffect();
|
||
SetChargerState(CHARGER_STATE_DISCHARGE);
|
||
}
|
||
|
||
void CWeaponCrossbow::Activate(void)
|
||
{
|
||
BaseClass::Activate();
|
||
|
||
SetSkin(BOLT_SKIN_GLOW);
|
||
}
|
||
//-----------------------------------------------------------------------------
|
||
// Purpose:
|
||
// Output : Returns true on success, false on failure.
|
||
//-----------------------------------------------------------------------------
|
||
bool CWeaponCrossbow::Deploy(void)
|
||
{
|
||
/*SetBodygroup(0,0);
|
||
SetBodygroup(1, 0);
|
||
SetBodygroup(2, 0);
|
||
SetBodygroup(3, 0);
|
||
SetBodygroup(4, 0);
|
||
SetBodygroup(5, 0);
|
||
SetBodygroup(6, 0);
|
||
SetBodygroup(7, 0);
|
||
SetBodygroup(8, 0);*/
|
||
|
||
if (m_iClip1 <= 0)
|
||
{
|
||
return DefaultDeploy((char*)GetViewModel(), (char*)GetWorldModel(), ACT_CROSSBOW_DRAW_UNLOADED, (char*)GetAnimPrefix());
|
||
}
|
||
|
||
SetSkin(BOLT_SKIN_GLOW);
|
||
|
||
return BaseClass::Deploy();
|
||
}
|
||
|
||
//-----------------------------------------------------------------------------
|
||
// Purpose:
|
||
// Input : *pSwitchingTo -
|
||
// Output : Returns true on success, false on failure.
|
||
//-----------------------------------------------------------------------------
|
||
bool CWeaponCrossbow::Holster(CBaseCombatWeapon *pSwitchingTo)
|
||
{
|
||
StopEffects();
|
||
|
||
SetSkin(BOLT_SKIN_NORMAL);
|
||
|
||
/*CBasePlayer *pPlayer = ToBasePlayer(GetOwner());
|
||
if (!pPlayer)
|
||
return false;
|
||
|
||
//pPlayer->SetFOV(this, 0, 0.1f);
|
||
CSingleUserRecipientFilter filter(pPlayer);
|
||
UserMessageBegin(filter, "ShowScope");
|
||
WRITE_BYTE(0);
|
||
MessageEnd();
|
||
cvar->FindVar("oc_state_InSecondFire")->SetValue(0);
|
||
cvar->FindVar("oc_state_InSecondFire_crossbow")->SetValue(0);*/
|
||
|
||
return BaseClass::Holster(pSwitchingTo);
|
||
}
|
||
|
||
//-----------------------------------------------------------------------------
|
||
// Purpose:
|
||
//-----------------------------------------------------------------------------
|
||
/*void CWeaponCrossbow::ToggleZoom( void )
|
||
{
|
||
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
|
||
|
||
if ( pPlayer == NULL )
|
||
return;
|
||
|
||
if (cvar->FindVar("oc_state_IRsight_on")->GetInt() == 1)
|
||
return;
|
||
|
||
if (m_bInReload)
|
||
return;
|
||
|
||
if ( m_bInZoom )
|
||
{
|
||
if ( pPlayer->SetFOV( this, 0, 0.2f ) )
|
||
{
|
||
m_bInZoom = false;
|
||
// Send a message to hide the scope
|
||
CSingleUserRecipientFilter filter(pPlayer);
|
||
UserMessageBegin(filter, "ShowScope");
|
||
WRITE_BYTE(0);
|
||
MessageEnd();
|
||
cvar->FindVar("oc_state_InSecondFire")->SetValue(0);
|
||
cvar->FindVar("oc_state_InSecondFire_crossbow")->SetValue(0);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if ((pPlayer->SetFOV(this, 20, 0.1f)) && (m_iClip1 != 0))
|
||
{
|
||
m_bInZoom = true;
|
||
// Send a message to Show the scope
|
||
CSingleUserRecipientFilter filter(pPlayer);
|
||
UserMessageBegin(filter, "ShowScope");
|
||
WRITE_BYTE(1);
|
||
MessageEnd();
|
||
cvar->FindVar("oc_state_InSecondFire")->SetValue(1);
|
||
cvar->FindVar("oc_state_InSecondFire_crossbow")->SetValue(1);
|
||
}
|
||
}
|
||
}*/
|
||
|
||
#define BOLT_TIP_ATTACHMENT 2
|
||
|
||
//-----------------------------------------------------------------------------
|
||
// Purpose:
|
||
//-----------------------------------------------------------------------------
|
||
void CWeaponCrossbow::CreateChargerEffects(void)
|
||
{
|
||
CBasePlayer *pOwner = ToBasePlayer(GetOwner());
|
||
|
||
if (m_hChargerSprite != NULL)
|
||
return;
|
||
|
||
m_hChargerSprite = CSprite::SpriteCreate(CROSSBOW_GLOW_SPRITE, GetAbsOrigin(), false);
|
||
|
||
if (m_hChargerSprite)
|
||
{
|
||
m_hChargerSprite->SetAttachment(pOwner->GetViewModel(), BOLT_TIP_ATTACHMENT);
|
||
m_hChargerSprite->SetTransparency(kRenderTransAdd, 255, 128, 0, 255, kRenderFxNoDissipation);
|
||
m_hChargerSprite->SetBrightness(0);
|
||
m_hChargerSprite->SetScale(0.1f);
|
||
m_hChargerSprite->TurnOff();
|
||
}
|
||
}
|
||
|
||
//-----------------------------------------------------------------------------
|
||
// Purpose:
|
||
// Input : skinNum -
|
||
//-----------------------------------------------------------------------------
|
||
/*void CWeaponCrossbow::SetSkin(int skinNum)
|
||
{
|
||
CBasePlayer *pOwner = ToBasePlayer(GetOwner());
|
||
|
||
if (pOwner == NULL)
|
||
return;
|
||
|
||
CBaseViewModel *pViewModel = pOwner->GetViewModel();
|
||
|
||
if (pViewModel == NULL)
|
||
return;
|
||
|
||
pViewModel->m_nSkin = skinNum;
|
||
}*/
|
||
|
||
//-----------------------------------------------------------------------------
|
||
// Purpose:
|
||
//-----------------------------------------------------------------------------
|
||
void CWeaponCrossbow::DoLoadEffect(void)
|
||
{
|
||
SetSkin(BOLT_SKIN_GLOW);
|
||
|
||
CBasePlayer *pOwner = ToBasePlayer(GetOwner());
|
||
|
||
if (pOwner == NULL)
|
||
return;
|
||
|
||
CBaseViewModel *pViewModel = pOwner->GetViewModel();
|
||
|
||
if (pViewModel == NULL)
|
||
return;
|
||
|
||
CEffectData data;
|
||
|
||
data.m_nEntIndex = pViewModel->entindex();
|
||
data.m_nAttachmentIndex = 1;
|
||
|
||
DispatchEffect("CrossbowLoad", data);
|
||
|
||
CSprite *pBlast = CSprite::SpriteCreate(CROSSBOW_GLOW_SPRITE2, GetAbsOrigin(), false);
|
||
|
||
if (pBlast)
|
||
{
|
||
pBlast->SetAttachment(pOwner->GetViewModel(), 1);
|
||
pBlast->SetTransparency(kRenderTransAdd, 255, 255, 255, 255, kRenderFxNone);
|
||
pBlast->SetBrightness(128);
|
||
pBlast->SetScale(0.2f);
|
||
pBlast->FadeOutFromSpawn();
|
||
}
|
||
}
|
||
|
||
//-----------------------------------------------------------------------------
|
||
// Purpose:
|
||
// Input : state -
|
||
//-----------------------------------------------------------------------------
|
||
void CWeaponCrossbow::SetChargerState(ChargerState_t state)
|
||
{
|
||
// Make sure we're setup
|
||
CreateChargerEffects();
|
||
|
||
// Don't do this twice
|
||
if (state == m_nChargeState)
|
||
return;
|
||
|
||
m_nChargeState = state;
|
||
|
||
switch (m_nChargeState)
|
||
{
|
||
case CHARGER_STATE_START_LOAD:
|
||
|
||
WeaponSound(SPECIAL1);
|
||
|
||
// Shoot some sparks and draw a beam between the two outer points
|
||
DoLoadEffect();
|
||
|
||
break;
|
||
|
||
case CHARGER_STATE_START_CHARGE:
|
||
{
|
||
if (m_hChargerSprite == NULL)
|
||
break;
|
||
|
||
m_hChargerSprite->SetBrightness(32, 0.5f);
|
||
m_hChargerSprite->SetScale(0.025f, 0.5f);
|
||
m_hChargerSprite->TurnOn();
|
||
}
|
||
|
||
break;
|
||
|
||
case CHARGER_STATE_READY:
|
||
{
|
||
// Get fully charged
|
||
if (m_hChargerSprite == NULL)
|
||
break;
|
||
|
||
m_hChargerSprite->SetBrightness(80, 1.0f);
|
||
m_hChargerSprite->SetScale(0.1f, 0.5f);
|
||
m_hChargerSprite->TurnOn();
|
||
}
|
||
|
||
break;
|
||
|
||
case CHARGER_STATE_DISCHARGE:
|
||
{
|
||
SetSkin(BOLT_SKIN_NORMAL);
|
||
|
||
if (m_hChargerSprite == NULL)
|
||
break;
|
||
|
||
m_hChargerSprite->SetBrightness(0);
|
||
m_hChargerSprite->TurnOff();
|
||
}
|
||
|
||
break;
|
||
|
||
case CHARGER_STATE_OFF:
|
||
{
|
||
SetSkin(BOLT_SKIN_NORMAL);
|
||
|
||
if (m_hChargerSprite == NULL)
|
||
break;
|
||
|
||
m_hChargerSprite->SetBrightness(0);
|
||
m_hChargerSprite->TurnOff();
|
||
}
|
||
break;
|
||
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
//-----------------------------------------------------------------------------
|
||
// Purpose:
|
||
// Input : *pEvent -
|
||
// *pOperator -
|
||
//-----------------------------------------------------------------------------
|
||
void CWeaponCrossbow::Operator_HandleAnimEvent(animevent_t *pEvent, CBaseCombatCharacter *pOperator)
|
||
{
|
||
switch (pEvent->event)
|
||
{
|
||
case EVENT_WEAPON_THROW:
|
||
SetChargerState(CHARGER_STATE_START_LOAD);
|
||
break;
|
||
|
||
case EVENT_WEAPON_THROW2:
|
||
SetChargerState(CHARGER_STATE_START_CHARGE);
|
||
break;
|
||
|
||
case EVENT_WEAPON_THROW3:
|
||
SetChargerState(CHARGER_STATE_READY);
|
||
break;
|
||
|
||
default:
|
||
BaseClass::Operator_HandleAnimEvent(pEvent, pOperator);
|
||
break;
|
||
}
|
||
}
|
||
|
||
//-----------------------------------------------------------------------------
|
||
// Purpose: Set the desired activity for the weapon and its viewmodel counterpart
|
||
// Input : iActivity - activity to play
|
||
//-----------------------------------------------------------------------------
|
||
bool CWeaponCrossbow::SendWeaponAnim(int iActivity)
|
||
{
|
||
int newActivity = iActivity;
|
||
|
||
// The last shot needs a non-loaded activity
|
||
if ((newActivity == ACT_VM_IDLE) && (m_iClip1 <= 0))
|
||
{
|
||
newActivity = ACT_VM_FIDGET;
|
||
}
|
||
|
||
//For now, just set the ideal activity and be done with it
|
||
return BaseClass::SendWeaponAnim(newActivity);
|
||
}
|
||
|
||
//-----------------------------------------------------------------------------
|
||
// Purpose: Stop all zooming and special effects on the viewmodel
|
||
//-----------------------------------------------------------------------------
|
||
void CWeaponCrossbow::StopEffects(void)
|
||
{
|
||
// Stop zooming
|
||
/*if ( m_bInZoom )
|
||
{
|
||
ToggleZoom();
|
||
}*/
|
||
|
||
// Turn off our sprites
|
||
SetChargerState(CHARGER_STATE_OFF);
|
||
}
|
||
|
||
//-----------------------------------------------------------------------------
|
||
// Purpose:
|
||
//-----------------------------------------------------------------------------
|
||
/*void CWeaponCrossbow::Drop( const Vector &vecVelocity )
|
||
{
|
||
StopEffects();
|
||
BaseClass::Drop( vecVelocity );
|
||
}*/
|