From f56bb35301836e56582a575a75864392a0177875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20P=2E=20Tjern=C3=B8?= Date: Mon, 2 Dec 2013 19:31:46 -0800 Subject: Fix line endings. WHAMMY. --- mp/src/game/server/hl2/weapon_crossbow.cpp | 1964 ++++++++++++++-------------- 1 file changed, 982 insertions(+), 982 deletions(-) (limited to 'mp/src/game/server/hl2/weapon_crossbow.cpp') diff --git a/mp/src/game/server/hl2/weapon_crossbow.cpp b/mp/src/game/server/hl2/weapon_crossbow.cpp index 0ed6910c..badd50d8 100644 --- a/mp/src/game/server/hl2/weapon_crossbow.cpp +++ b/mp/src/game/server/hl2/weapon_crossbow.cpp @@ -1,982 +1,982 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -//=============================================================================// - -#include "cbase.h" -#include "npcevent.h" -#include "basehlcombatweapon_shared.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 "IEffects.h" -#include "te_effect_dispatch.h" -#include "Sprite.h" -#include "SpriteTrail.h" -#include "beam_shared.h" -#include "rumble_shared.h" -#include "gamestats.h" -#include "decals.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; - -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 BoltTouch( CBaseEntity *pOther ); - bool CreateVPhysics( void ); - unsigned int PhysicsSolidMaskForEntity() const; - static CCrossbowBolt *BoltCreate( const Vector &vecOrigin, const QAngle &angAngles, CBasePlayer *pentOwner = NULL ); - -protected: - - bool CreateSprites( void ); - - CHandle m_pGlowSprite; - //CHandle m_pGlowTrail; - - DECLARE_DATADESC(); - DECLARE_SERVERCLASS(); -}; -LINK_ENTITY_TO_CLASS( crossbow_bolt, CCrossbowBolt ); - -BEGIN_DATADESC( CCrossbowBolt ) - // Function Pointers - DEFINE_FUNCTION( BubbleThink ), - DEFINE_FUNCTION( BoltTouch ), - - // 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 ) -{ - // 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 ); - - 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(); - - SetTouch( &CCrossbowBolt::BoltTouch ); - - SetThink( &CCrossbowBolt::BubbleThink ); - SetNextThink( gpGlobals->curtime + 0.1f ); - - CreateSprites(); - - // Make us glow until we've hit the wall - m_nSkin = BOLT_SKIN_GLOW; -} - - -void CCrossbowBolt::Precache( void ) -{ - PrecacheModel( BOLT_MODEL ); - - // This is used by C_TEStickyBolt, despte being different from above!!! - PrecacheModel( "models/crossbow_bolt.mdl" ); - - PrecacheModel( "sprites/light_glow02_noz.vmt" ); -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -void CCrossbowBolt::BoltTouch( 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 ); - } - - } - 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" ); - - 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 ); - } - } - - // 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 ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CCrossbowBolt::BubbleThink( void ) -{ - 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 ); -} - - -//----------------------------------------------------------------------------- -// CWeaponCrossbow -//----------------------------------------------------------------------------- - -class CWeaponCrossbow : public CBaseHLCombatWeapon -{ - DECLARE_CLASS( CWeaponCrossbow, CBaseHLCombatWeapon ); -public: - - CWeaponCrossbow( void ); - - virtual void Precache( void ); - virtual void PrimaryAttack( void ); - virtual void SecondaryAttack( void ); - virtual bool Deploy( void ); - virtual void Drop( const Vector &vecVelocity ); - virtual bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL ); - virtual bool Reload( void ); - virtual void ItemPostFrame( void ); - virtual void ItemBusyFrame( void ); - virtual void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ); - virtual bool SendWeaponAnim( int iActivity ); - virtual bool IsWeaponZoomed() { return m_bInZoom; } - - bool ShouldDisplayHUDHint() { return true; } - - - DECLARE_SERVERCLASS(); - DECLARE_DATADESC(); - -private: - - void StopEffects( void ); - void SetSkin( int skinNum ); - void CheckZoomToggle( void ); - void FireBolt( void ); - void ToggleZoom( void ); - - // Various states for the crossbow's charger - enum ChargerState_t - { - CHARGER_STATE_START_LOAD, - CHARGER_STATE_START_CHARGE, - CHARGER_STATE_READY, - CHARGER_STATE_DISCHARGE, - CHARGER_STATE_OFF, - }; - - void CreateChargerEffects( void ); - void SetChargerState( ChargerState_t state ); - void DoLoadEffect( void ); - -private: - - // Charger effects - ChargerState_t m_nChargeState; - CHandle m_hChargerSprite; - - bool m_bInZoom; - bool m_bMustReload; -}; - -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( m_bMustReload, FIELD_BOOLEAN ), - DEFINE_FIELD( m_nChargeState, FIELD_INTEGER ), - DEFINE_FIELD( m_hChargerSprite, FIELD_EHANDLE ), - -END_DATADESC() - -//----------------------------------------------------------------------------- -// Purpose: Constructor -//----------------------------------------------------------------------------- -CWeaponCrossbow::CWeaponCrossbow( void ) -{ - m_bReloadsSingly = true; - m_bFiresUnderwater = true; - m_bAltFiresUnderwater = true; - m_bInZoom = false; - m_bMustReload = 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" ); - - PrecacheModel( CROSSBOW_GLOW_SPRITE ); - PrecacheModel( CROSSBOW_GLOW_SPRITE2 ); - - BaseClass::Precache(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CWeaponCrossbow::PrimaryAttack( void ) -{ - if ( m_bInZoom && g_pGameRules->IsMultiplayer() ) - { -// FireSniperBolt(); - FireBolt(); - } - else - { - FireBolt(); - } - - // Signal a reload - m_bMustReload = true; - - SetWeaponIdleTime( gpGlobals->curtime + SequenceDuration( ACT_VM_PRIMARYATTACK ) ); - - CBasePlayer *pPlayer = ToBasePlayer( GetOwner() ); - if ( pPlayer ) - { - m_iPrimaryAttacks++; - gamestats->Event_WeaponFired( pPlayer, true, GetClassname() ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CWeaponCrossbow::SecondaryAttack( void ) -{ - //NOTENOTE: The zooming is handled by the post/busy frames -} - -//----------------------------------------------------------------------------- -// Purpose: -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool CWeaponCrossbow::Reload( void ) -{ - if ( BaseClass::Reload() ) - { - m_bMustReload = false; - return true; - } - - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CWeaponCrossbow::CheckZoomToggle( void ) -{ - CBasePlayer *pPlayer = ToBasePlayer( GetOwner() ); - - if ( pPlayer->m_afButtonPressed & IN_ATTACK2 ) - { - ToggleZoom(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CWeaponCrossbow::ItemBusyFrame( void ) -{ - // Allow zoom toggling even when we're reloading - CheckZoomToggle(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CWeaponCrossbow::ItemPostFrame( void ) -{ - // Allow zoom toggling - CheckZoomToggle(); - - if ( m_bMustReload && HasWeaponIdleTimeElapsed() ) - { - Reload(); - } - - BaseClass::ItemPostFrame(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CWeaponCrossbow::FireBolt( void ) -{ - if ( m_iClip1 <= 0 ) - { - if ( !m_bFireOnEmpty ) - { - Reload(); - } - 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; - VectorAngles( vecAiming, angAiming ); - -#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( vecSrc, 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( vecSrc, angAiming, pOwner ); - - if ( pOwner->GetWaterLevel() == 3 ) - { - pBolt->SetAbsVelocity( vecAiming * BOLT_WATER_VELOCITY ); - } - else - { - pBolt->SetAbsVelocity( vecAiming * BOLT_AIR_VELOCITY ); - } - - m_iClip1--; - - pOwner->ViewPunch( QAngle( -2, 0, 0 ) ); - - WeaponSound( SINGLE ); - WeaponSound( SPECIAL2 ); - - CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), 200, 0.2 ); - - SendWeaponAnim( ACT_VM_PRIMARYATTACK ); - - 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 ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool CWeaponCrossbow::Deploy( void ) -{ - 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(); - return BaseClass::Holster( pSwitchingTo ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CWeaponCrossbow::ToggleZoom( void ) -{ - CBasePlayer *pPlayer = ToBasePlayer( GetOwner() ); - - if ( pPlayer == NULL ) - return; - - if ( m_bInZoom ) - { - if ( pPlayer->SetFOV( this, 0, 0.2f ) ) - { - m_bInZoom = false; - } - } - else - { - if ( pPlayer->SetFOV( this, 20, 0.1f ) ) - { - m_bInZoom = true; - } - } -} - -#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 ); -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "npcevent.h" +#include "basehlcombatweapon_shared.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 "IEffects.h" +#include "te_effect_dispatch.h" +#include "Sprite.h" +#include "SpriteTrail.h" +#include "beam_shared.h" +#include "rumble_shared.h" +#include "gamestats.h" +#include "decals.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; + +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 BoltTouch( CBaseEntity *pOther ); + bool CreateVPhysics( void ); + unsigned int PhysicsSolidMaskForEntity() const; + static CCrossbowBolt *BoltCreate( const Vector &vecOrigin, const QAngle &angAngles, CBasePlayer *pentOwner = NULL ); + +protected: + + bool CreateSprites( void ); + + CHandle m_pGlowSprite; + //CHandle m_pGlowTrail; + + DECLARE_DATADESC(); + DECLARE_SERVERCLASS(); +}; +LINK_ENTITY_TO_CLASS( crossbow_bolt, CCrossbowBolt ); + +BEGIN_DATADESC( CCrossbowBolt ) + // Function Pointers + DEFINE_FUNCTION( BubbleThink ), + DEFINE_FUNCTION( BoltTouch ), + + // 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 ) +{ + // 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 ); + + 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(); + + SetTouch( &CCrossbowBolt::BoltTouch ); + + SetThink( &CCrossbowBolt::BubbleThink ); + SetNextThink( gpGlobals->curtime + 0.1f ); + + CreateSprites(); + + // Make us glow until we've hit the wall + m_nSkin = BOLT_SKIN_GLOW; +} + + +void CCrossbowBolt::Precache( void ) +{ + PrecacheModel( BOLT_MODEL ); + + // This is used by C_TEStickyBolt, despte being different from above!!! + PrecacheModel( "models/crossbow_bolt.mdl" ); + + PrecacheModel( "sprites/light_glow02_noz.vmt" ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CCrossbowBolt::BoltTouch( 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 ); + } + + } + 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" ); + + 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 ); + } + } + + // 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 ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCrossbowBolt::BubbleThink( void ) +{ + 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 ); +} + + +//----------------------------------------------------------------------------- +// CWeaponCrossbow +//----------------------------------------------------------------------------- + +class CWeaponCrossbow : public CBaseHLCombatWeapon +{ + DECLARE_CLASS( CWeaponCrossbow, CBaseHLCombatWeapon ); +public: + + CWeaponCrossbow( void ); + + virtual void Precache( void ); + virtual void PrimaryAttack( void ); + virtual void SecondaryAttack( void ); + virtual bool Deploy( void ); + virtual void Drop( const Vector &vecVelocity ); + virtual bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL ); + virtual bool Reload( void ); + virtual void ItemPostFrame( void ); + virtual void ItemBusyFrame( void ); + virtual void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ); + virtual bool SendWeaponAnim( int iActivity ); + virtual bool IsWeaponZoomed() { return m_bInZoom; } + + bool ShouldDisplayHUDHint() { return true; } + + + DECLARE_SERVERCLASS(); + DECLARE_DATADESC(); + +private: + + void StopEffects( void ); + void SetSkin( int skinNum ); + void CheckZoomToggle( void ); + void FireBolt( void ); + void ToggleZoom( void ); + + // Various states for the crossbow's charger + enum ChargerState_t + { + CHARGER_STATE_START_LOAD, + CHARGER_STATE_START_CHARGE, + CHARGER_STATE_READY, + CHARGER_STATE_DISCHARGE, + CHARGER_STATE_OFF, + }; + + void CreateChargerEffects( void ); + void SetChargerState( ChargerState_t state ); + void DoLoadEffect( void ); + +private: + + // Charger effects + ChargerState_t m_nChargeState; + CHandle m_hChargerSprite; + + bool m_bInZoom; + bool m_bMustReload; +}; + +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( m_bMustReload, FIELD_BOOLEAN ), + DEFINE_FIELD( m_nChargeState, FIELD_INTEGER ), + DEFINE_FIELD( m_hChargerSprite, FIELD_EHANDLE ), + +END_DATADESC() + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CWeaponCrossbow::CWeaponCrossbow( void ) +{ + m_bReloadsSingly = true; + m_bFiresUnderwater = true; + m_bAltFiresUnderwater = true; + m_bInZoom = false; + m_bMustReload = 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" ); + + PrecacheModel( CROSSBOW_GLOW_SPRITE ); + PrecacheModel( CROSSBOW_GLOW_SPRITE2 ); + + BaseClass::Precache(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponCrossbow::PrimaryAttack( void ) +{ + if ( m_bInZoom && g_pGameRules->IsMultiplayer() ) + { +// FireSniperBolt(); + FireBolt(); + } + else + { + FireBolt(); + } + + // Signal a reload + m_bMustReload = true; + + SetWeaponIdleTime( gpGlobals->curtime + SequenceDuration( ACT_VM_PRIMARYATTACK ) ); + + CBasePlayer *pPlayer = ToBasePlayer( GetOwner() ); + if ( pPlayer ) + { + m_iPrimaryAttacks++; + gamestats->Event_WeaponFired( pPlayer, true, GetClassname() ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponCrossbow::SecondaryAttack( void ) +{ + //NOTENOTE: The zooming is handled by the post/busy frames +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CWeaponCrossbow::Reload( void ) +{ + if ( BaseClass::Reload() ) + { + m_bMustReload = false; + return true; + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponCrossbow::CheckZoomToggle( void ) +{ + CBasePlayer *pPlayer = ToBasePlayer( GetOwner() ); + + if ( pPlayer->m_afButtonPressed & IN_ATTACK2 ) + { + ToggleZoom(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponCrossbow::ItemBusyFrame( void ) +{ + // Allow zoom toggling even when we're reloading + CheckZoomToggle(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponCrossbow::ItemPostFrame( void ) +{ + // Allow zoom toggling + CheckZoomToggle(); + + if ( m_bMustReload && HasWeaponIdleTimeElapsed() ) + { + Reload(); + } + + BaseClass::ItemPostFrame(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponCrossbow::FireBolt( void ) +{ + if ( m_iClip1 <= 0 ) + { + if ( !m_bFireOnEmpty ) + { + Reload(); + } + 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; + VectorAngles( vecAiming, angAiming ); + +#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( vecSrc, 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( vecSrc, angAiming, pOwner ); + + if ( pOwner->GetWaterLevel() == 3 ) + { + pBolt->SetAbsVelocity( vecAiming * BOLT_WATER_VELOCITY ); + } + else + { + pBolt->SetAbsVelocity( vecAiming * BOLT_AIR_VELOCITY ); + } + + m_iClip1--; + + pOwner->ViewPunch( QAngle( -2, 0, 0 ) ); + + WeaponSound( SINGLE ); + WeaponSound( SPECIAL2 ); + + CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), 200, 0.2 ); + + SendWeaponAnim( ACT_VM_PRIMARYATTACK ); + + 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 ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CWeaponCrossbow::Deploy( void ) +{ + 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(); + return BaseClass::Holster( pSwitchingTo ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponCrossbow::ToggleZoom( void ) +{ + CBasePlayer *pPlayer = ToBasePlayer( GetOwner() ); + + if ( pPlayer == NULL ) + return; + + if ( m_bInZoom ) + { + if ( pPlayer->SetFOV( this, 0, 0.2f ) ) + { + m_bInZoom = false; + } + } + else + { + if ( pPlayer->SetFOV( this, 20, 0.1f ) ) + { + m_bInZoom = true; + } + } +} + +#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 ); +} -- cgit v1.2.3