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/basecombatweapon.cpp | 1468 +++++++++++++++---------------- 1 file changed, 734 insertions(+), 734 deletions(-) (limited to 'mp/src/game/server/basecombatweapon.cpp') diff --git a/mp/src/game/server/basecombatweapon.cpp b/mp/src/game/server/basecombatweapon.cpp index 8a44673f..c05cb33d 100644 --- a/mp/src/game/server/basecombatweapon.cpp +++ b/mp/src/game/server/basecombatweapon.cpp @@ -1,734 +1,734 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -//=============================================================================// - -#include "cbase.h" -#include "ai_basenpc.h" -#include "animation.h" -#include "basecombatweapon.h" -#include "player.h" // For gEvilImpulse101 / CBasePlayer -#include "gamerules.h" // For g_pGameRules -#include -#include "ammodef.h" -#include "baseviewmodel.h" -#include "in_buttons.h" -#include "soundent.h" -#include "weapon_parse.h" -#include "game.h" -#include "engine/IEngineSound.h" -#include "sendproxy.h" -#include "tier1/strtools.h" -#include "vphysics/constraints.h" -#include "npcevent.h" -#include "igamesystem.h" -#include "collisionutils.h" -#include "iservervehicle.h" -#include "func_break.h" - -#ifdef HL2MP - #include "hl2mp_gamerules.h" -#endif - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -extern int gEvilImpulse101; // In Player.h - -// ----------------------------------------- -// Sprite Index info -// ----------------------------------------- -short g_sModelIndexLaser; // holds the index for the laser beam -const char *g_pModelNameLaser = "sprites/laserbeam.vmt"; -short g_sModelIndexLaserDot; // holds the index for the laser beam dot -short g_sModelIndexFireball; // holds the index for the fireball -short g_sModelIndexSmoke; // holds the index for the smoke cloud -short g_sModelIndexWExplosion; // holds the index for the underwater explosion -short g_sModelIndexBubbles; // holds the index for the bubbles model -short g_sModelIndexBloodDrop; // holds the sprite index for the initial blood -short g_sModelIndexBloodSpray; // holds the sprite index for splattered blood - - -ConVar weapon_showproficiency( "weapon_showproficiency", "0" ); -extern ConVar ai_debug_shoot_positions; - -//----------------------------------------------------------------------------- -// Purpose: Precache global weapon sounds -//----------------------------------------------------------------------------- -void W_Precache(void) -{ - PrecacheFileWeaponInfoDatabase( filesystem, g_pGameRules->GetEncryptionKey() ); - - - -#ifdef HL1_DLL - g_sModelIndexWExplosion = CBaseEntity::PrecacheModel ("sprites/WXplo1.vmt");// underwater fireball - g_sModelIndexBloodSpray = CBaseEntity::PrecacheModel ("sprites/bloodspray.vmt"); // initial blood - g_sModelIndexBloodDrop = CBaseEntity::PrecacheModel ("sprites/blood.vmt"); // splattered blood - g_sModelIndexLaserDot = CBaseEntity::PrecacheModel("sprites/laserdot.vmt"); -#endif // HL1_DLL - -#ifndef TF_DLL - g_sModelIndexFireball = CBaseEntity::PrecacheModel ("sprites/zerogxplode.vmt");// fireball - - g_sModelIndexSmoke = CBaseEntity::PrecacheModel ("sprites/steam1.vmt");// smoke - g_sModelIndexBubbles = CBaseEntity::PrecacheModel ("sprites/bubble.vmt");//bubbles - g_sModelIndexLaser = CBaseEntity::PrecacheModel( (char *)g_pModelNameLaser ); - - PrecacheParticleSystem( "blood_impact_red_01" ); - PrecacheParticleSystem( "blood_impact_green_01" ); - PrecacheParticleSystem( "blood_impact_yellow_01" ); - - CBaseEntity::PrecacheModel ("effects/bubble.vmt");//bubble trails - - CBaseEntity::PrecacheModel("models/weapons/w_bullet.mdl"); -#endif - - CBaseEntity::PrecacheScriptSound( "BaseCombatWeapon.WeaponDrop" ); - CBaseEntity::PrecacheScriptSound( "BaseCombatWeapon.WeaponMaterialize" ); -} - -//----------------------------------------------------------------------------- -// Purpose: Transmit weapon data -//----------------------------------------------------------------------------- -int CBaseCombatWeapon::UpdateTransmitState( void) -{ - // If the weapon is being carried by a CBaseCombatCharacter, let the combat character do the logic - // about whether or not to transmit it. - if ( GetOwner() ) - { - return SetTransmitState( FL_EDICT_PVSCHECK ); - } - else - { - // If it's just lying around, then use CBaseEntity's visibility test to see if it should be sent. - return BaseClass::UpdateTransmitState(); - } -} - - -void CBaseCombatWeapon::Operator_FrameUpdate( CBaseCombatCharacter *pOperator ) -{ - StudioFrameAdvance( ); // animate - - if ( IsSequenceFinished() ) - { - if ( SequenceLoops() ) - { - // animation does loop, which means we're playing subtle idle. Might need to fidget. - int iSequence = SelectWeightedSequence( GetActivity() ); - if ( iSequence != ACTIVITY_NOT_AVAILABLE ) - { - ResetSequence( iSequence ); // Set to new anim (if it's there) - } - } -#if 0 - else - { - // animation that just ended doesn't loop! That means we just finished a fidget - // and should return to our heaviest weighted idle (the subtle one) - SelectHeaviestSequence( GetActivity() ); - } -#endif - } - - // Animation events are passed back to the weapon's owner/operator - DispatchAnimEvents( pOperator ); - - // Update and dispatch the viewmodel events - CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); - - if ( pOwner == NULL ) - return; - - CBaseViewModel *vm = pOwner->GetViewModel( m_nViewModelIndex ); - - if ( vm != NULL ) - { - vm->StudioFrameAdvance(); - vm->DispatchAnimEvents( this ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *pEvent - -// *pOperator - -//----------------------------------------------------------------------------- -void CBaseCombatWeapon::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ) -{ - if ( (pEvent->type & AE_TYPE_NEWEVENTSYSTEM) && (pEvent->type & AE_TYPE_SERVER) ) - { - if ( pEvent->event == AE_NPC_WEAPON_FIRE ) - { - bool bSecondary = (atoi( pEvent->options ) != 0); - Operator_ForceNPCFire( pOperator, bSecondary ); - return; - } - else if ( pEvent->event == AE_WPN_PLAYWPNSOUND ) - { - int iSnd = GetWeaponSoundFromString(pEvent->options); - if ( iSnd != -1 ) - { - WeaponSound( (WeaponSound_t)iSnd ); - } - } - } - - DevWarning( 2, "Unhandled animation event %d from %s --> %s\n", pEvent->event, pOperator->GetClassname(), GetClassname() ); -} - -// NOTE: This should never be called when a character is operating the weapon. Animation events should be -// routed through the character, and then back into CharacterAnimEvent() -void CBaseCombatWeapon::HandleAnimEvent( animevent_t *pEvent ) -{ - //If the player is receiving this message, pass it through - CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); - - if ( pOwner != NULL ) - { - Operator_HandleAnimEvent( pEvent, pOwner ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Make the weapon visible and tangible -//----------------------------------------------------------------------------- -CBaseEntity* CBaseCombatWeapon::Respawn( void ) -{ - // make a copy of this weapon that is invisible and inaccessible to players (no touch function). The weapon spawn/respawn code - // will decide when to make the weapon visible and touchable. - CBaseEntity *pNewWeapon = CBaseEntity::Create( GetClassname(), g_pGameRules->VecWeaponRespawnSpot( this ), GetLocalAngles(), GetOwnerEntity() ); - - if ( pNewWeapon ) - { - pNewWeapon->AddEffects( EF_NODRAW );// invisible for now - pNewWeapon->SetTouch( NULL );// no touch - pNewWeapon->SetThink( &CBaseCombatWeapon::AttemptToMaterialize ); - - UTIL_DropToFloor( this, MASK_SOLID ); - - // not a typo! We want to know when the weapon the player just picked up should respawn! This new entity we created is the replacement, - // but when it should respawn is based on conditions belonging to the weapon that was taken. - pNewWeapon->SetNextThink( gpGlobals->curtime + g_pGameRules->FlWeaponRespawnTime( this ) ); - } - else - { - Warning("Respawn failed to create %s!\n", GetClassname() ); - } - - return pNewWeapon; -} - -//----------------------------------------------------------------------------- -// Purpose: Weapons ignore other weapons when LOS tracing -//----------------------------------------------------------------------------- -class CWeaponLOSFilter : public CTraceFilterSkipTwoEntities -{ - DECLARE_CLASS( CWeaponLOSFilter, CTraceFilterSkipTwoEntities ); -public: - CWeaponLOSFilter( IHandleEntity *pHandleEntity, IHandleEntity *pHandleEntity2, int collisionGroup ) : - CTraceFilterSkipTwoEntities( pHandleEntity, pHandleEntity2, collisionGroup ), m_pVehicle( NULL ) - { - // If the tracing entity is in a vehicle, then ignore it - if ( pHandleEntity != NULL ) - { - CBaseCombatCharacter *pBCC = ((CBaseEntity *)pHandleEntity)->MyCombatCharacterPointer(); - if ( pBCC != NULL ) - { - m_pVehicle = pBCC->GetVehicleEntity(); - } - } - } - virtual bool ShouldHitEntity( IHandleEntity *pServerEntity, int contentsMask ) - { - CBaseEntity *pEntity = (CBaseEntity *)pServerEntity; - - if ( pEntity->GetCollisionGroup() == COLLISION_GROUP_WEAPON ) - return false; - - // Don't collide with the tracing entity's vehicle (if it exists) - if ( pServerEntity == m_pVehicle ) - return false; - - if ( pEntity->GetHealth() > 0 ) - { - CBreakable *pBreakable = dynamic_cast(pEntity); - if ( pBreakable && pBreakable->IsBreakable() && pBreakable->GetMaterialType() == matGlass) - { - return false; - } - } - - return BaseClass::ShouldHitEntity( pServerEntity, contentsMask ); - } - -private: - CBaseEntity *m_pVehicle; -}; - -//----------------------------------------------------------------------------- -// Purpose: Check the weapon LOS for an owner at an arbitrary position -// If bSetConditions is true, LOS related conditions will also be set -//----------------------------------------------------------------------------- -bool CBaseCombatWeapon::WeaponLOSCondition( const Vector &ownerPos, const Vector &targetPos, bool bSetConditions ) -{ - // -------------------- - // Check for occlusion - // -------------------- - CAI_BaseNPC* npcOwner = m_hOwner.Get()->MyNPCPointer(); - - // Find its relative shoot position - Vector vecRelativeShootPosition; - VectorSubtract( npcOwner->Weapon_ShootPosition(), npcOwner->GetAbsOrigin(), vecRelativeShootPosition ); - Vector barrelPos = ownerPos + vecRelativeShootPosition; - - // FIXME: If we're in a vehicle, we need some sort of way to handle shooting out of them - - // Use the custom LOS trace filter - CWeaponLOSFilter traceFilter( m_hOwner.Get(), npcOwner->GetEnemy(), COLLISION_GROUP_BREAKABLE_GLASS ); - trace_t tr; - UTIL_TraceLine( barrelPos, targetPos, MASK_SHOT, &traceFilter, &tr ); - - // See if we completed the trace without interruption - if ( tr.fraction == 1.0 ) - { - if ( ai_debug_shoot_positions.GetBool() ) - { - NDebugOverlay::Line( barrelPos, targetPos, 0, 255, 0, false, 1.0 ); - } - - return true; - } - - CBaseEntity *pHitEnt = tr.m_pEnt; - - CBasePlayer *pEnemyPlayer = ToBasePlayer( npcOwner->GetEnemy() ); - - // is player in a vehicle? if so, verify vehicle is target and return if so (so npc shoots at vehicle) - if ( pEnemyPlayer && pEnemyPlayer->IsInAVehicle() ) - { - // Ok, player in vehicle, check if vehicle is target we're looking at, fire if it is - // Also, check to see if the owner of the entity is the vehicle, in which case it's valid too. - // This catches vehicles that use bone followers. - CBaseEntity *pVehicle = pEnemyPlayer->GetVehicle()->GetVehicleEnt(); - if ( pHitEnt == pVehicle || pHitEnt->GetOwnerEntity() == pVehicle ) - return true; - } - - // Hitting our enemy is a success case - if ( pHitEnt == npcOwner->GetEnemy() ) - { - if ( ai_debug_shoot_positions.GetBool() ) - { - NDebugOverlay::Line( barrelPos, targetPos, 0, 255, 0, false, 1.0 ); - } - - return true; - } - - // If a vehicle is blocking the view, grab its driver and use that as the combat character - CBaseCombatCharacter *pBCC; - IServerVehicle *pVehicle = pHitEnt->GetServerVehicle(); - if ( pVehicle ) - { - pBCC = pVehicle->GetPassenger( ); - } - else - { - pBCC = ToBaseCombatCharacter( pHitEnt ); - } - - if ( pBCC ) - { - if ( npcOwner->IRelationType( pBCC ) == D_HT ) - return true; - - if ( bSetConditions ) - { - npcOwner->SetCondition( COND_WEAPON_BLOCKED_BY_FRIEND ); - } - } - else if ( bSetConditions ) - { - npcOwner->SetCondition( COND_WEAPON_SIGHT_OCCLUDED ); - npcOwner->SetEnemyOccluder( pHitEnt ); - - if( ai_debug_shoot_positions.GetBool() ) - { - NDebugOverlay::Line( tr.startpos, tr.endpos, 255, 0, 0, false, 1.0 ); - } - } - - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: Base class always returns not bits -//----------------------------------------------------------------------------- -int CBaseCombatWeapon::WeaponRangeAttack1Condition( float flDot, float flDist ) -{ - if ( UsesPrimaryAmmo() && !HasPrimaryAmmo() ) - { - return COND_NO_PRIMARY_AMMO; - } - else if ( flDist < m_fMinRange1) - { - return COND_TOO_CLOSE_TO_ATTACK; - } - else if (flDist > m_fMaxRange1) - { - return COND_TOO_FAR_TO_ATTACK; - } - else if (flDot < 0.5) // UNDONE: Why check this here? Isn't the AI checking this already? - { - return COND_NOT_FACING_ATTACK; - } - - return COND_CAN_RANGE_ATTACK1; -} - -//----------------------------------------------------------------------------- -// Purpose: Base class always returns not bits -//----------------------------------------------------------------------------- -int CBaseCombatWeapon::WeaponRangeAttack2Condition( float flDot, float flDist ) -{ - // currently disabled - return COND_NONE; - - if ( m_bReloadsSingly ) - { - if (m_iClip2 <=0) - { - return COND_NO_SECONDARY_AMMO; - } - else if ( flDist < m_fMinRange2) - { - return COND_TOO_CLOSE_TO_ATTACK; - } - else if (flDist > m_fMaxRange2) - { - return COND_TOO_FAR_TO_ATTACK; - } - else if (flDot < 0.5) - { - return COND_NOT_FACING_ATTACK; - } - return COND_CAN_RANGE_ATTACK2; - } - - return COND_NONE; -} - -//----------------------------------------------------------------------------- -// Purpose: Base class always returns not bits -//----------------------------------------------------------------------------- -int CBaseCombatWeapon::WeaponMeleeAttack1Condition( float flDot, float flDist ) -{ - return COND_NONE; -} - -//----------------------------------------------------------------------------- -// Purpose: Base class always returns not bits -//----------------------------------------------------------------------------- -int CBaseCombatWeapon::WeaponMeleeAttack2Condition( float flDot, float flDist ) -{ - return COND_NONE; -} - -//==================================================================================== -// WEAPON DROPPING / DESTRUCTION -//==================================================================================== -void CBaseCombatWeapon::Delete( void ) -{ - SetTouch( NULL ); - // FIXME: why doesn't this just remove itself now? - SetThink(&CBaseCombatWeapon::SUB_Remove); - SetNextThink( gpGlobals->curtime + 0.1f ); -} - -void CBaseCombatWeapon::DestroyItem( void ) -{ - CBaseCombatCharacter *pOwner = m_hOwner.Get(); - - if ( pOwner ) - { - // if attached to a player, remove. - pOwner->RemovePlayerItem( this ); - } - - Kill( ); -} - -void CBaseCombatWeapon::Kill( void ) -{ - SetTouch( NULL ); - // FIXME: why doesn't this just remove itself now? - // FIXME: how is this different than Delete(), and why do they have the same code in them? - SetThink(&CBaseCombatWeapon::SUB_Remove); - SetNextThink( gpGlobals->curtime + 0.1f ); -} - -//==================================================================================== -// FALL TO GROUND -//==================================================================================== -//----------------------------------------------------------------------------- -// Purpose: Setup for the fall -//----------------------------------------------------------------------------- -void CBaseCombatWeapon::FallInit( void ) -{ - SetModel( GetWorldModel() ); - VPhysicsDestroyObject(); - - if ( !VPhysicsInitNormal( SOLID_BBOX, GetSolidFlags() | FSOLID_TRIGGER, false ) ) - { - SetMoveType( MOVETYPE_FLYGRAVITY ); - SetSolid( SOLID_BBOX ); - AddSolidFlags( FSOLID_TRIGGER ); - } - else - { -#if !defined( CLIENT_DLL ) - // Constrained start? - if ( HasSpawnFlags( SF_WEAPON_START_CONSTRAINED ) ) - { - //Constrain the weapon in place - IPhysicsObject *pReferenceObject, *pAttachedObject; - - pReferenceObject = g_PhysWorldObject; - pAttachedObject = VPhysicsGetObject(); - - if ( pReferenceObject && pAttachedObject ) - { - constraint_fixedparams_t fixed; - fixed.Defaults(); - fixed.InitWithCurrentObjectState( pReferenceObject, pAttachedObject ); - - fixed.constraint.forceLimit = lbs2kg( 10000 ); - fixed.constraint.torqueLimit = lbs2kg( 10000 ); - - m_pConstraint = physenv->CreateFixedConstraint( pReferenceObject, pAttachedObject, NULL, fixed ); - - m_pConstraint->SetGameData( (void *) this ); - } - } -#endif //CLIENT_DLL - } - - SetPickupTouch(); - - SetThink( &CBaseCombatWeapon::FallThink ); - - SetNextThink( gpGlobals->curtime + 0.1f ); -} - -//----------------------------------------------------------------------------- -// Purpose: Items that have just spawned run this think to catch them when -// they hit the ground. Once we're sure that the object is grounded, -// we change its solid type to trigger and set it in a large box that -// helps the player get it. -//----------------------------------------------------------------------------- -void CBaseCombatWeapon::FallThink ( void ) -{ - SetNextThink( gpGlobals->curtime + 0.1f ); - - bool shouldMaterialize = false; - IPhysicsObject *pPhysics = VPhysicsGetObject(); - if ( pPhysics ) - { - shouldMaterialize = pPhysics->IsAsleep(); - } - else - { - shouldMaterialize = (GetFlags() & FL_ONGROUND) ? true : false; - } - - if ( shouldMaterialize ) - { - // clatter if we have an owner (i.e., dropped by someone) - // don't clatter if the gun is waiting to respawn (if it's waiting, it is invisible!) - if ( GetOwnerEntity() ) - { - EmitSound( "BaseCombatWeapon.WeaponDrop" ); - } - Materialize(); - } -} - -//==================================================================================== -// WEAPON SPAWNING -//==================================================================================== -//----------------------------------------------------------------------------- -// Purpose: Make a weapon visible and tangible -//-----------------------------------------------------------------------------// -void CBaseCombatWeapon::Materialize( void ) -{ - if ( IsEffectActive( EF_NODRAW ) ) - { - // changing from invisible state to visible. -#ifdef HL2MP - EmitSound( "AlyxEmp.Charge" ); -#else - EmitSound( "BaseCombatWeapon.WeaponMaterialize" ); -#endif - - RemoveEffects( EF_NODRAW ); - DoMuzzleFlash(); - } -#ifdef HL2MP - if ( HasSpawnFlags( SF_NORESPAWN ) == false ) - { - VPhysicsInitNormal( SOLID_BBOX, GetSolidFlags() | FSOLID_TRIGGER, false ); - SetMoveType( MOVETYPE_VPHYSICS ); - - HL2MPRules()->AddLevelDesignerPlacedObject( this ); - } -#else - SetSolid( SOLID_BBOX ); - AddSolidFlags( FSOLID_TRIGGER ); -#endif - - SetPickupTouch(); - - SetThink (NULL); -} - -//----------------------------------------------------------------------------- -// Purpose: See if the game rules will let this weapon respawn -//----------------------------------------------------------------------------- -void CBaseCombatWeapon::AttemptToMaterialize( void ) -{ - float time = g_pGameRules->FlWeaponTryRespawn( this ); - - if ( time == 0 ) - { - Materialize(); - return; - } - - SetNextThink( gpGlobals->curtime + time ); -} - -//----------------------------------------------------------------------------- -// Purpose: Weapon has been picked up, should it respawn? -//----------------------------------------------------------------------------- -void CBaseCombatWeapon::CheckRespawn( void ) -{ - switch ( g_pGameRules->WeaponShouldRespawn( this ) ) - { - case GR_WEAPON_RESPAWN_YES: - Respawn(); - break; - case GR_WEAPON_RESPAWN_NO: - return; - break; - } -} - -class CWeaponList : public CAutoGameSystem -{ -public: - CWeaponList( char const *name ) : CAutoGameSystem( name ) - { - } - - - virtual void LevelShutdownPostEntity() - { - m_list.Purge(); - } - - void AddWeapon( CBaseCombatWeapon *pWeapon ) - { - m_list.AddToTail( pWeapon ); - } - - void RemoveWeapon( CBaseCombatWeapon *pWeapon ) - { - m_list.FindAndRemove( pWeapon ); - } - CUtlLinkedList< CBaseCombatWeapon * > m_list; -}; - -CWeaponList g_WeaponList( "CWeaponList" ); - -void OnBaseCombatWeaponCreated( CBaseCombatWeapon *pWeapon ) -{ - g_WeaponList.AddWeapon( pWeapon ); -} - -void OnBaseCombatWeaponDestroyed( CBaseCombatWeapon *pWeapon ) -{ - g_WeaponList.RemoveWeapon( pWeapon ); -} - -int CBaseCombatWeapon::GetAvailableWeaponsInBox( CBaseCombatWeapon **pList, int listMax, const Vector &mins, const Vector &maxs ) -{ - // linear search all weapons - int count = 0; - int index = g_WeaponList.m_list.Head(); - while ( index != g_WeaponList.m_list.InvalidIndex() ) - { - CBaseCombatWeapon *pWeapon = g_WeaponList.m_list[index]; - // skip any held weapon - if ( !pWeapon->GetOwner() ) - { - // restrict to mins/maxs - if ( IsPointInBox( pWeapon->GetAbsOrigin(), mins, maxs ) ) - { - if ( count < listMax ) - { - pList[count] = pWeapon; - count++; - } - } - } - index = g_WeaponList.m_list.Next( index ); - } - - return count; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -int CBaseCombatWeapon::ObjectCaps( void ) -{ - int caps = BaseClass::ObjectCaps(); - if ( !IsFollowingEntity() && !HasSpawnFlags(SF_WEAPON_NO_PLAYER_PICKUP) ) - { - caps |= FCAP_IMPULSE_USE; - } - - return caps; -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CBaseCombatWeapon::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - CBasePlayer *pPlayer = ToBasePlayer( pActivator ); - - if ( pPlayer ) - { - m_OnPlayerUse.FireOutput( pActivator, pCaller ); - - // - // Bump the weapon to try equipping it before picking it up physically. This is - // important in a few spots in the game where the player could potentially +use pickup - // and then THROW AWAY a vital weapon, rendering them unable to continue the game. - // - if ( pPlayer->BumpWeapon( this ) ) - { - OnPickedUp( pPlayer ); - } - else - { - pPlayer->PickupObject( this ); - } - } -} - +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "ai_basenpc.h" +#include "animation.h" +#include "basecombatweapon.h" +#include "player.h" // For gEvilImpulse101 / CBasePlayer +#include "gamerules.h" // For g_pGameRules +#include +#include "ammodef.h" +#include "baseviewmodel.h" +#include "in_buttons.h" +#include "soundent.h" +#include "weapon_parse.h" +#include "game.h" +#include "engine/IEngineSound.h" +#include "sendproxy.h" +#include "tier1/strtools.h" +#include "vphysics/constraints.h" +#include "npcevent.h" +#include "igamesystem.h" +#include "collisionutils.h" +#include "iservervehicle.h" +#include "func_break.h" + +#ifdef HL2MP + #include "hl2mp_gamerules.h" +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +extern int gEvilImpulse101; // In Player.h + +// ----------------------------------------- +// Sprite Index info +// ----------------------------------------- +short g_sModelIndexLaser; // holds the index for the laser beam +const char *g_pModelNameLaser = "sprites/laserbeam.vmt"; +short g_sModelIndexLaserDot; // holds the index for the laser beam dot +short g_sModelIndexFireball; // holds the index for the fireball +short g_sModelIndexSmoke; // holds the index for the smoke cloud +short g_sModelIndexWExplosion; // holds the index for the underwater explosion +short g_sModelIndexBubbles; // holds the index for the bubbles model +short g_sModelIndexBloodDrop; // holds the sprite index for the initial blood +short g_sModelIndexBloodSpray; // holds the sprite index for splattered blood + + +ConVar weapon_showproficiency( "weapon_showproficiency", "0" ); +extern ConVar ai_debug_shoot_positions; + +//----------------------------------------------------------------------------- +// Purpose: Precache global weapon sounds +//----------------------------------------------------------------------------- +void W_Precache(void) +{ + PrecacheFileWeaponInfoDatabase( filesystem, g_pGameRules->GetEncryptionKey() ); + + + +#ifdef HL1_DLL + g_sModelIndexWExplosion = CBaseEntity::PrecacheModel ("sprites/WXplo1.vmt");// underwater fireball + g_sModelIndexBloodSpray = CBaseEntity::PrecacheModel ("sprites/bloodspray.vmt"); // initial blood + g_sModelIndexBloodDrop = CBaseEntity::PrecacheModel ("sprites/blood.vmt"); // splattered blood + g_sModelIndexLaserDot = CBaseEntity::PrecacheModel("sprites/laserdot.vmt"); +#endif // HL1_DLL + +#ifndef TF_DLL + g_sModelIndexFireball = CBaseEntity::PrecacheModel ("sprites/zerogxplode.vmt");// fireball + + g_sModelIndexSmoke = CBaseEntity::PrecacheModel ("sprites/steam1.vmt");// smoke + g_sModelIndexBubbles = CBaseEntity::PrecacheModel ("sprites/bubble.vmt");//bubbles + g_sModelIndexLaser = CBaseEntity::PrecacheModel( (char *)g_pModelNameLaser ); + + PrecacheParticleSystem( "blood_impact_red_01" ); + PrecacheParticleSystem( "blood_impact_green_01" ); + PrecacheParticleSystem( "blood_impact_yellow_01" ); + + CBaseEntity::PrecacheModel ("effects/bubble.vmt");//bubble trails + + CBaseEntity::PrecacheModel("models/weapons/w_bullet.mdl"); +#endif + + CBaseEntity::PrecacheScriptSound( "BaseCombatWeapon.WeaponDrop" ); + CBaseEntity::PrecacheScriptSound( "BaseCombatWeapon.WeaponMaterialize" ); +} + +//----------------------------------------------------------------------------- +// Purpose: Transmit weapon data +//----------------------------------------------------------------------------- +int CBaseCombatWeapon::UpdateTransmitState( void) +{ + // If the weapon is being carried by a CBaseCombatCharacter, let the combat character do the logic + // about whether or not to transmit it. + if ( GetOwner() ) + { + return SetTransmitState( FL_EDICT_PVSCHECK ); + } + else + { + // If it's just lying around, then use CBaseEntity's visibility test to see if it should be sent. + return BaseClass::UpdateTransmitState(); + } +} + + +void CBaseCombatWeapon::Operator_FrameUpdate( CBaseCombatCharacter *pOperator ) +{ + StudioFrameAdvance( ); // animate + + if ( IsSequenceFinished() ) + { + if ( SequenceLoops() ) + { + // animation does loop, which means we're playing subtle idle. Might need to fidget. + int iSequence = SelectWeightedSequence( GetActivity() ); + if ( iSequence != ACTIVITY_NOT_AVAILABLE ) + { + ResetSequence( iSequence ); // Set to new anim (if it's there) + } + } +#if 0 + else + { + // animation that just ended doesn't loop! That means we just finished a fidget + // and should return to our heaviest weighted idle (the subtle one) + SelectHeaviestSequence( GetActivity() ); + } +#endif + } + + // Animation events are passed back to the weapon's owner/operator + DispatchAnimEvents( pOperator ); + + // Update and dispatch the viewmodel events + CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); + + if ( pOwner == NULL ) + return; + + CBaseViewModel *vm = pOwner->GetViewModel( m_nViewModelIndex ); + + if ( vm != NULL ) + { + vm->StudioFrameAdvance(); + vm->DispatchAnimEvents( this ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pEvent - +// *pOperator - +//----------------------------------------------------------------------------- +void CBaseCombatWeapon::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ) +{ + if ( (pEvent->type & AE_TYPE_NEWEVENTSYSTEM) && (pEvent->type & AE_TYPE_SERVER) ) + { + if ( pEvent->event == AE_NPC_WEAPON_FIRE ) + { + bool bSecondary = (atoi( pEvent->options ) != 0); + Operator_ForceNPCFire( pOperator, bSecondary ); + return; + } + else if ( pEvent->event == AE_WPN_PLAYWPNSOUND ) + { + int iSnd = GetWeaponSoundFromString(pEvent->options); + if ( iSnd != -1 ) + { + WeaponSound( (WeaponSound_t)iSnd ); + } + } + } + + DevWarning( 2, "Unhandled animation event %d from %s --> %s\n", pEvent->event, pOperator->GetClassname(), GetClassname() ); +} + +// NOTE: This should never be called when a character is operating the weapon. Animation events should be +// routed through the character, and then back into CharacterAnimEvent() +void CBaseCombatWeapon::HandleAnimEvent( animevent_t *pEvent ) +{ + //If the player is receiving this message, pass it through + CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); + + if ( pOwner != NULL ) + { + Operator_HandleAnimEvent( pEvent, pOwner ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Make the weapon visible and tangible +//----------------------------------------------------------------------------- +CBaseEntity* CBaseCombatWeapon::Respawn( void ) +{ + // make a copy of this weapon that is invisible and inaccessible to players (no touch function). The weapon spawn/respawn code + // will decide when to make the weapon visible and touchable. + CBaseEntity *pNewWeapon = CBaseEntity::Create( GetClassname(), g_pGameRules->VecWeaponRespawnSpot( this ), GetLocalAngles(), GetOwnerEntity() ); + + if ( pNewWeapon ) + { + pNewWeapon->AddEffects( EF_NODRAW );// invisible for now + pNewWeapon->SetTouch( NULL );// no touch + pNewWeapon->SetThink( &CBaseCombatWeapon::AttemptToMaterialize ); + + UTIL_DropToFloor( this, MASK_SOLID ); + + // not a typo! We want to know when the weapon the player just picked up should respawn! This new entity we created is the replacement, + // but when it should respawn is based on conditions belonging to the weapon that was taken. + pNewWeapon->SetNextThink( gpGlobals->curtime + g_pGameRules->FlWeaponRespawnTime( this ) ); + } + else + { + Warning("Respawn failed to create %s!\n", GetClassname() ); + } + + return pNewWeapon; +} + +//----------------------------------------------------------------------------- +// Purpose: Weapons ignore other weapons when LOS tracing +//----------------------------------------------------------------------------- +class CWeaponLOSFilter : public CTraceFilterSkipTwoEntities +{ + DECLARE_CLASS( CWeaponLOSFilter, CTraceFilterSkipTwoEntities ); +public: + CWeaponLOSFilter( IHandleEntity *pHandleEntity, IHandleEntity *pHandleEntity2, int collisionGroup ) : + CTraceFilterSkipTwoEntities( pHandleEntity, pHandleEntity2, collisionGroup ), m_pVehicle( NULL ) + { + // If the tracing entity is in a vehicle, then ignore it + if ( pHandleEntity != NULL ) + { + CBaseCombatCharacter *pBCC = ((CBaseEntity *)pHandleEntity)->MyCombatCharacterPointer(); + if ( pBCC != NULL ) + { + m_pVehicle = pBCC->GetVehicleEntity(); + } + } + } + virtual bool ShouldHitEntity( IHandleEntity *pServerEntity, int contentsMask ) + { + CBaseEntity *pEntity = (CBaseEntity *)pServerEntity; + + if ( pEntity->GetCollisionGroup() == COLLISION_GROUP_WEAPON ) + return false; + + // Don't collide with the tracing entity's vehicle (if it exists) + if ( pServerEntity == m_pVehicle ) + return false; + + if ( pEntity->GetHealth() > 0 ) + { + CBreakable *pBreakable = dynamic_cast(pEntity); + if ( pBreakable && pBreakable->IsBreakable() && pBreakable->GetMaterialType() == matGlass) + { + return false; + } + } + + return BaseClass::ShouldHitEntity( pServerEntity, contentsMask ); + } + +private: + CBaseEntity *m_pVehicle; +}; + +//----------------------------------------------------------------------------- +// Purpose: Check the weapon LOS for an owner at an arbitrary position +// If bSetConditions is true, LOS related conditions will also be set +//----------------------------------------------------------------------------- +bool CBaseCombatWeapon::WeaponLOSCondition( const Vector &ownerPos, const Vector &targetPos, bool bSetConditions ) +{ + // -------------------- + // Check for occlusion + // -------------------- + CAI_BaseNPC* npcOwner = m_hOwner.Get()->MyNPCPointer(); + + // Find its relative shoot position + Vector vecRelativeShootPosition; + VectorSubtract( npcOwner->Weapon_ShootPosition(), npcOwner->GetAbsOrigin(), vecRelativeShootPosition ); + Vector barrelPos = ownerPos + vecRelativeShootPosition; + + // FIXME: If we're in a vehicle, we need some sort of way to handle shooting out of them + + // Use the custom LOS trace filter + CWeaponLOSFilter traceFilter( m_hOwner.Get(), npcOwner->GetEnemy(), COLLISION_GROUP_BREAKABLE_GLASS ); + trace_t tr; + UTIL_TraceLine( barrelPos, targetPos, MASK_SHOT, &traceFilter, &tr ); + + // See if we completed the trace without interruption + if ( tr.fraction == 1.0 ) + { + if ( ai_debug_shoot_positions.GetBool() ) + { + NDebugOverlay::Line( barrelPos, targetPos, 0, 255, 0, false, 1.0 ); + } + + return true; + } + + CBaseEntity *pHitEnt = tr.m_pEnt; + + CBasePlayer *pEnemyPlayer = ToBasePlayer( npcOwner->GetEnemy() ); + + // is player in a vehicle? if so, verify vehicle is target and return if so (so npc shoots at vehicle) + if ( pEnemyPlayer && pEnemyPlayer->IsInAVehicle() ) + { + // Ok, player in vehicle, check if vehicle is target we're looking at, fire if it is + // Also, check to see if the owner of the entity is the vehicle, in which case it's valid too. + // This catches vehicles that use bone followers. + CBaseEntity *pVehicle = pEnemyPlayer->GetVehicle()->GetVehicleEnt(); + if ( pHitEnt == pVehicle || pHitEnt->GetOwnerEntity() == pVehicle ) + return true; + } + + // Hitting our enemy is a success case + if ( pHitEnt == npcOwner->GetEnemy() ) + { + if ( ai_debug_shoot_positions.GetBool() ) + { + NDebugOverlay::Line( barrelPos, targetPos, 0, 255, 0, false, 1.0 ); + } + + return true; + } + + // If a vehicle is blocking the view, grab its driver and use that as the combat character + CBaseCombatCharacter *pBCC; + IServerVehicle *pVehicle = pHitEnt->GetServerVehicle(); + if ( pVehicle ) + { + pBCC = pVehicle->GetPassenger( ); + } + else + { + pBCC = ToBaseCombatCharacter( pHitEnt ); + } + + if ( pBCC ) + { + if ( npcOwner->IRelationType( pBCC ) == D_HT ) + return true; + + if ( bSetConditions ) + { + npcOwner->SetCondition( COND_WEAPON_BLOCKED_BY_FRIEND ); + } + } + else if ( bSetConditions ) + { + npcOwner->SetCondition( COND_WEAPON_SIGHT_OCCLUDED ); + npcOwner->SetEnemyOccluder( pHitEnt ); + + if( ai_debug_shoot_positions.GetBool() ) + { + NDebugOverlay::Line( tr.startpos, tr.endpos, 255, 0, 0, false, 1.0 ); + } + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Base class always returns not bits +//----------------------------------------------------------------------------- +int CBaseCombatWeapon::WeaponRangeAttack1Condition( float flDot, float flDist ) +{ + if ( UsesPrimaryAmmo() && !HasPrimaryAmmo() ) + { + return COND_NO_PRIMARY_AMMO; + } + else if ( flDist < m_fMinRange1) + { + return COND_TOO_CLOSE_TO_ATTACK; + } + else if (flDist > m_fMaxRange1) + { + return COND_TOO_FAR_TO_ATTACK; + } + else if (flDot < 0.5) // UNDONE: Why check this here? Isn't the AI checking this already? + { + return COND_NOT_FACING_ATTACK; + } + + return COND_CAN_RANGE_ATTACK1; +} + +//----------------------------------------------------------------------------- +// Purpose: Base class always returns not bits +//----------------------------------------------------------------------------- +int CBaseCombatWeapon::WeaponRangeAttack2Condition( float flDot, float flDist ) +{ + // currently disabled + return COND_NONE; + + if ( m_bReloadsSingly ) + { + if (m_iClip2 <=0) + { + return COND_NO_SECONDARY_AMMO; + } + else if ( flDist < m_fMinRange2) + { + return COND_TOO_CLOSE_TO_ATTACK; + } + else if (flDist > m_fMaxRange2) + { + return COND_TOO_FAR_TO_ATTACK; + } + else if (flDot < 0.5) + { + return COND_NOT_FACING_ATTACK; + } + return COND_CAN_RANGE_ATTACK2; + } + + return COND_NONE; +} + +//----------------------------------------------------------------------------- +// Purpose: Base class always returns not bits +//----------------------------------------------------------------------------- +int CBaseCombatWeapon::WeaponMeleeAttack1Condition( float flDot, float flDist ) +{ + return COND_NONE; +} + +//----------------------------------------------------------------------------- +// Purpose: Base class always returns not bits +//----------------------------------------------------------------------------- +int CBaseCombatWeapon::WeaponMeleeAttack2Condition( float flDot, float flDist ) +{ + return COND_NONE; +} + +//==================================================================================== +// WEAPON DROPPING / DESTRUCTION +//==================================================================================== +void CBaseCombatWeapon::Delete( void ) +{ + SetTouch( NULL ); + // FIXME: why doesn't this just remove itself now? + SetThink(&CBaseCombatWeapon::SUB_Remove); + SetNextThink( gpGlobals->curtime + 0.1f ); +} + +void CBaseCombatWeapon::DestroyItem( void ) +{ + CBaseCombatCharacter *pOwner = m_hOwner.Get(); + + if ( pOwner ) + { + // if attached to a player, remove. + pOwner->RemovePlayerItem( this ); + } + + Kill( ); +} + +void CBaseCombatWeapon::Kill( void ) +{ + SetTouch( NULL ); + // FIXME: why doesn't this just remove itself now? + // FIXME: how is this different than Delete(), and why do they have the same code in them? + SetThink(&CBaseCombatWeapon::SUB_Remove); + SetNextThink( gpGlobals->curtime + 0.1f ); +} + +//==================================================================================== +// FALL TO GROUND +//==================================================================================== +//----------------------------------------------------------------------------- +// Purpose: Setup for the fall +//----------------------------------------------------------------------------- +void CBaseCombatWeapon::FallInit( void ) +{ + SetModel( GetWorldModel() ); + VPhysicsDestroyObject(); + + if ( !VPhysicsInitNormal( SOLID_BBOX, GetSolidFlags() | FSOLID_TRIGGER, false ) ) + { + SetMoveType( MOVETYPE_FLYGRAVITY ); + SetSolid( SOLID_BBOX ); + AddSolidFlags( FSOLID_TRIGGER ); + } + else + { +#if !defined( CLIENT_DLL ) + // Constrained start? + if ( HasSpawnFlags( SF_WEAPON_START_CONSTRAINED ) ) + { + //Constrain the weapon in place + IPhysicsObject *pReferenceObject, *pAttachedObject; + + pReferenceObject = g_PhysWorldObject; + pAttachedObject = VPhysicsGetObject(); + + if ( pReferenceObject && pAttachedObject ) + { + constraint_fixedparams_t fixed; + fixed.Defaults(); + fixed.InitWithCurrentObjectState( pReferenceObject, pAttachedObject ); + + fixed.constraint.forceLimit = lbs2kg( 10000 ); + fixed.constraint.torqueLimit = lbs2kg( 10000 ); + + m_pConstraint = physenv->CreateFixedConstraint( pReferenceObject, pAttachedObject, NULL, fixed ); + + m_pConstraint->SetGameData( (void *) this ); + } + } +#endif //CLIENT_DLL + } + + SetPickupTouch(); + + SetThink( &CBaseCombatWeapon::FallThink ); + + SetNextThink( gpGlobals->curtime + 0.1f ); +} + +//----------------------------------------------------------------------------- +// Purpose: Items that have just spawned run this think to catch them when +// they hit the ground. Once we're sure that the object is grounded, +// we change its solid type to trigger and set it in a large box that +// helps the player get it. +//----------------------------------------------------------------------------- +void CBaseCombatWeapon::FallThink ( void ) +{ + SetNextThink( gpGlobals->curtime + 0.1f ); + + bool shouldMaterialize = false; + IPhysicsObject *pPhysics = VPhysicsGetObject(); + if ( pPhysics ) + { + shouldMaterialize = pPhysics->IsAsleep(); + } + else + { + shouldMaterialize = (GetFlags() & FL_ONGROUND) ? true : false; + } + + if ( shouldMaterialize ) + { + // clatter if we have an owner (i.e., dropped by someone) + // don't clatter if the gun is waiting to respawn (if it's waiting, it is invisible!) + if ( GetOwnerEntity() ) + { + EmitSound( "BaseCombatWeapon.WeaponDrop" ); + } + Materialize(); + } +} + +//==================================================================================== +// WEAPON SPAWNING +//==================================================================================== +//----------------------------------------------------------------------------- +// Purpose: Make a weapon visible and tangible +//-----------------------------------------------------------------------------// +void CBaseCombatWeapon::Materialize( void ) +{ + if ( IsEffectActive( EF_NODRAW ) ) + { + // changing from invisible state to visible. +#ifdef HL2MP + EmitSound( "AlyxEmp.Charge" ); +#else + EmitSound( "BaseCombatWeapon.WeaponMaterialize" ); +#endif + + RemoveEffects( EF_NODRAW ); + DoMuzzleFlash(); + } +#ifdef HL2MP + if ( HasSpawnFlags( SF_NORESPAWN ) == false ) + { + VPhysicsInitNormal( SOLID_BBOX, GetSolidFlags() | FSOLID_TRIGGER, false ); + SetMoveType( MOVETYPE_VPHYSICS ); + + HL2MPRules()->AddLevelDesignerPlacedObject( this ); + } +#else + SetSolid( SOLID_BBOX ); + AddSolidFlags( FSOLID_TRIGGER ); +#endif + + SetPickupTouch(); + + SetThink (NULL); +} + +//----------------------------------------------------------------------------- +// Purpose: See if the game rules will let this weapon respawn +//----------------------------------------------------------------------------- +void CBaseCombatWeapon::AttemptToMaterialize( void ) +{ + float time = g_pGameRules->FlWeaponTryRespawn( this ); + + if ( time == 0 ) + { + Materialize(); + return; + } + + SetNextThink( gpGlobals->curtime + time ); +} + +//----------------------------------------------------------------------------- +// Purpose: Weapon has been picked up, should it respawn? +//----------------------------------------------------------------------------- +void CBaseCombatWeapon::CheckRespawn( void ) +{ + switch ( g_pGameRules->WeaponShouldRespawn( this ) ) + { + case GR_WEAPON_RESPAWN_YES: + Respawn(); + break; + case GR_WEAPON_RESPAWN_NO: + return; + break; + } +} + +class CWeaponList : public CAutoGameSystem +{ +public: + CWeaponList( char const *name ) : CAutoGameSystem( name ) + { + } + + + virtual void LevelShutdownPostEntity() + { + m_list.Purge(); + } + + void AddWeapon( CBaseCombatWeapon *pWeapon ) + { + m_list.AddToTail( pWeapon ); + } + + void RemoveWeapon( CBaseCombatWeapon *pWeapon ) + { + m_list.FindAndRemove( pWeapon ); + } + CUtlLinkedList< CBaseCombatWeapon * > m_list; +}; + +CWeaponList g_WeaponList( "CWeaponList" ); + +void OnBaseCombatWeaponCreated( CBaseCombatWeapon *pWeapon ) +{ + g_WeaponList.AddWeapon( pWeapon ); +} + +void OnBaseCombatWeaponDestroyed( CBaseCombatWeapon *pWeapon ) +{ + g_WeaponList.RemoveWeapon( pWeapon ); +} + +int CBaseCombatWeapon::GetAvailableWeaponsInBox( CBaseCombatWeapon **pList, int listMax, const Vector &mins, const Vector &maxs ) +{ + // linear search all weapons + int count = 0; + int index = g_WeaponList.m_list.Head(); + while ( index != g_WeaponList.m_list.InvalidIndex() ) + { + CBaseCombatWeapon *pWeapon = g_WeaponList.m_list[index]; + // skip any held weapon + if ( !pWeapon->GetOwner() ) + { + // restrict to mins/maxs + if ( IsPointInBox( pWeapon->GetAbsOrigin(), mins, maxs ) ) + { + if ( count < listMax ) + { + pList[count] = pWeapon; + count++; + } + } + } + index = g_WeaponList.m_list.Next( index ); + } + + return count; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CBaseCombatWeapon::ObjectCaps( void ) +{ + int caps = BaseClass::ObjectCaps(); + if ( !IsFollowingEntity() && !HasSpawnFlags(SF_WEAPON_NO_PLAYER_PICKUP) ) + { + caps |= FCAP_IMPULSE_USE; + } + + return caps; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseCombatWeapon::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + CBasePlayer *pPlayer = ToBasePlayer( pActivator ); + + if ( pPlayer ) + { + m_OnPlayerUse.FireOutput( pActivator, pCaller ); + + // + // Bump the weapon to try equipping it before picking it up physically. This is + // important in a few spots in the game where the player could potentially +use pickup + // and then THROW AWAY a vital weapon, rendering them unable to continue the game. + // + if ( pPlayer->BumpWeapon( this ) ) + { + OnPickedUp( pPlayer ); + } + else + { + pPlayer->PickupObject( this ); + } + } +} + -- cgit v1.2.3