//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ //=============================================================================// #include "cbase.h" #include "in_buttons.h" #include "takedamageinfo.h" #include "ammodef.h" #include "portal_gamerules.h" #ifdef CLIENT_DLL extern IVModelInfoClient* modelinfo; #else extern IVModelInfo* modelinfo; #endif #if defined( CLIENT_DLL ) #include "vgui/ISurface.h" #include "vgui_controls/Controls.h" #include "c_portal_player.h" #include "hud_crosshair.h" #include "PortalRender.h" #else #include "portal_player.h" #include "vphysics/constraints.h" #endif #include "weapon_portalbase.h" // ----------------------------------------------------------------------------- // // Global functions. // ----------------------------------------------------------------------------- // bool IsAmmoType( int iAmmoType, const char *pAmmoName ) { return GetAmmoDef()->Index( pAmmoName ) == iAmmoType; } static const char * s_WeaponAliasInfo[] = { "none", // WEAPON_NONE = 0, //Melee "shotgun", //WEAPON_AMERKNIFE, NULL, // end of list marker }; // ----------------------------------------------------------------------------- // // CWeaponPortalBase tables. // ----------------------------------------------------------------------------- // IMPLEMENT_NETWORKCLASS_ALIASED( WeaponPortalBase, DT_WeaponPortalBase ) BEGIN_NETWORK_TABLE( CWeaponPortalBase, DT_WeaponPortalBase ) #ifdef CLIENT_DLL #else // world weapon models have no aminations // SendPropExclude( "DT_AnimTimeMustBeFirst", "m_flAnimTime" ), // SendPropExclude( "DT_BaseAnimating", "m_nSequence" ), // SendPropExclude( "DT_LocalActiveWeaponData", "m_flTimeWeaponIdle" ), #endif END_NETWORK_TABLE() BEGIN_PREDICTION_DATA( CWeaponPortalBase ) END_PREDICTION_DATA() LINK_ENTITY_TO_CLASS( weapon_portal_base, CWeaponPortalBase ); #ifdef GAME_DLL BEGIN_DATADESC( CWeaponPortalBase ) END_DATADESC() #endif // ----------------------------------------------------------------------------- // // CWeaponPortalBase implementation. // ----------------------------------------------------------------------------- // CWeaponPortalBase::CWeaponPortalBase() { SetPredictionEligible( true ); AddSolidFlags( FSOLID_TRIGGER ); // Nothing collides with these but it gets touches. m_flNextResetCheckTime = 0.0f; } bool CWeaponPortalBase::IsPredicted() const { return false; } void CWeaponPortalBase::WeaponSound( WeaponSound_t sound_type, float soundtime /* = 0.0f */ ) { #ifdef CLIENT_DLL // If we have some sounds from the weapon classname.txt file, play a random one of them const char *shootsound = GetWpnData().aShootSounds[ sound_type ]; if ( !shootsound || !shootsound[0] ) return; CBroadcastRecipientFilter filter; // this is client side only if ( !te->CanPredict() ) return; CBaseEntity::EmitSound( filter, GetPlayerOwner()->entindex(), shootsound, &GetPlayerOwner()->GetAbsOrigin() ); #else BaseClass::WeaponSound( sound_type, soundtime ); #endif } CBasePlayer* CWeaponPortalBase::GetPlayerOwner() const { return dynamic_cast< CBasePlayer* >( GetOwner() ); } CPortal_Player* CWeaponPortalBase::GetPortalPlayerOwner() const { return dynamic_cast< CPortal_Player* >( GetOwner() ); } #ifdef CLIENT_DLL void CWeaponPortalBase::OnDataChanged( DataUpdateType_t type ) { BaseClass::OnDataChanged( type ); if ( GetPredictable() && !ShouldPredict() ) ShutdownPredictable(); } int CWeaponPortalBase::DrawModel( int flags ) { if ( !m_bReadyToDraw ) return 0; if ( GetOwner() && (GetOwner() == C_BasePlayer::GetLocalPlayer()) && !g_pPortalRender->IsRenderingPortal() && !C_BasePlayer::ShouldDrawLocalPlayer() ) return 0; //Sometimes the return value of ShouldDrawLocalPlayer() fluctuates too often to draw the correct model all the time, so this is a quick fix if it's changed too fast int iOriginalIndex = GetModelIndex(); bool bChangeModelBack = false; int iWorldModelIndex = GetWorldModelIndex(); if( iOriginalIndex != iWorldModelIndex ) { SetModelIndex( iWorldModelIndex ); bChangeModelBack = true; } int iRetVal = BaseClass::DrawModel( flags ); if( bChangeModelBack ) SetModelIndex( iOriginalIndex ); return iRetVal; } bool CWeaponPortalBase::ShouldDraw( void ) { if ( !GetOwner() || GetOwner() != C_BasePlayer::GetLocalPlayer() ) return true; if ( !IsActiveByLocalPlayer() ) return false; //if ( GetOwner() && GetOwner() == C_BasePlayer::GetLocalPlayer() && materials->GetRenderTarget() == 0 ) // return false; return true; } bool CWeaponPortalBase::ShouldPredict() { if ( GetOwner() && GetOwner() == C_BasePlayer::GetLocalPlayer() ) return true; return BaseClass::ShouldPredict(); } //----------------------------------------------------------------------------- // Purpose: Draw the weapon's crosshair //----------------------------------------------------------------------------- void CWeaponPortalBase::DrawCrosshair() { C_BasePlayer *player = C_BasePlayer::GetLocalPlayer(); if ( !player ) return; Color clr = gHUD.m_clrNormal; CHudCrosshair *crosshair = GET_HUDELEMENT( CHudCrosshair ); if ( !crosshair ) return; // Check to see if the player is in VGUI mode... if (player->IsInVGuiInputMode()) { CHudTexture *pArrow = gHUD.GetIcon( "arrow" ); crosshair->SetCrosshair( pArrow, gHUD.m_clrNormal ); return; } // Find out if this weapon's auto-aimed onto a target bool bOnTarget = ( m_iState == WEAPON_IS_ONTARGET ); if ( player->GetFOV() >= 90 ) { // normal crosshairs if ( bOnTarget && GetWpnData().iconAutoaim ) { clr[3] = 255; crosshair->SetCrosshair( GetWpnData().iconAutoaim, clr ); } else if ( GetWpnData().iconCrosshair ) { clr[3] = 255; crosshair->SetCrosshair( GetWpnData().iconCrosshair, clr ); } else { crosshair->ResetCrosshair(); } } else { Color white( 255, 255, 255, 255 ); // zoomed crosshairs if (bOnTarget && GetWpnData().iconZoomedAutoaim) crosshair->SetCrosshair(GetWpnData().iconZoomedAutoaim, white); else if ( GetWpnData().iconZoomedCrosshair ) crosshair->SetCrosshair( GetWpnData().iconZoomedCrosshair, white ); else crosshair->ResetCrosshair(); } } void CWeaponPortalBase::DoAnimationEvents( CStudioHdr *pStudioHdr ) { // HACK: Because this model renders view and world models in the same frame // it's using the wrong studio model when checking the sequences. C_BasePlayer *pPlayer = UTIL_PlayerByIndex( 1 ); if ( pPlayer && pPlayer->GetActiveWeapon() == this ) { C_BaseViewModel *pViewModel = pPlayer->GetViewModel(); if ( pViewModel ) { pStudioHdr = pViewModel->GetModelPtr(); } } if ( pStudioHdr ) { BaseClass::DoAnimationEvents( pStudioHdr ); } } void CWeaponPortalBase::GetRenderBounds( Vector& theMins, Vector& theMaxs ) { if ( IsRagdoll() ) { m_pRagdoll->GetRagdollBounds( theMins, theMaxs ); } else if ( GetModel() ) { CStudioHdr *pStudioHdr = NULL; // HACK: Because this model renders view and world models in the same frame // it's using the wrong studio model when checking the sequences. C_BasePlayer *pPlayer = UTIL_PlayerByIndex( 1 ); if ( pPlayer && pPlayer->GetActiveWeapon() == this ) { C_BaseViewModel *pViewModel = pPlayer->GetViewModel(); if ( pViewModel ) { pStudioHdr = pViewModel->GetModelPtr(); } } else { pStudioHdr = GetModelPtr(); } if ( !pStudioHdr || !pStudioHdr->SequencesAvailable() || GetSequence() == -1 ) { theMins = vec3_origin; theMaxs = vec3_origin; return; } if (!VectorCompare( vec3_origin, pStudioHdr->view_bbmin() ) || !VectorCompare( vec3_origin, pStudioHdr->view_bbmax() )) { // clipping bounding box VectorCopy ( pStudioHdr->view_bbmin(), theMins); VectorCopy ( pStudioHdr->view_bbmax(), theMaxs); } else { // movement bounding box VectorCopy ( pStudioHdr->hull_min(), theMins); VectorCopy ( pStudioHdr->hull_max(), theMaxs); } mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( GetSequence() ); VectorMin( seqdesc.bbmin, theMins, theMins ); VectorMax( seqdesc.bbmax, theMaxs, theMaxs ); } else { theMins = vec3_origin; theMaxs = vec3_origin; } } #else void CWeaponPortalBase::Spawn() { BaseClass::Spawn(); // Set this here to allow players to shoot dropped weapons SetCollisionGroup( COLLISION_GROUP_WEAPON ); // Use less bloat for the collision box for this weapon. (bug 43800) CollisionProp()->UseTriggerBounds( true, 20 ); } void CWeaponPortalBase:: Materialize( void ) { if ( IsEffectActive( EF_NODRAW ) ) { // changing from invisible state to visible. EmitSound( "AlyxEmp.Charge" ); RemoveEffects( EF_NODRAW ); DoMuzzleFlash(); } if ( HasSpawnFlags( SF_NORESPAWN ) == false ) { VPhysicsInitNormal( SOLID_BBOX, GetSolidFlags() | FSOLID_TRIGGER, false ); SetMoveType( MOVETYPE_VPHYSICS ); //PortalRules()->AddLevelDesignerPlacedObject( this ); } if ( HasSpawnFlags( SF_NORESPAWN ) == false ) { if ( GetOriginalSpawnOrigin() == vec3_origin ) { m_vOriginalSpawnOrigin = GetAbsOrigin(); m_vOriginalSpawnAngles = GetAbsAngles(); } } SetPickupTouch(); SetThink (NULL); } #endif const CPortalSWeaponInfo &CWeaponPortalBase::GetPortalWpnData() const { const FileWeaponInfo_t *pWeaponInfo = &GetWpnData(); const CPortalSWeaponInfo *pPortalInfo; #ifdef _DEBUG pPortalInfo = dynamic_cast< const CPortalSWeaponInfo* >( pWeaponInfo ); Assert( pPortalInfo ); #else pPortalInfo = static_cast< const CPortalSWeaponInfo* >( pWeaponInfo ); #endif return *pPortalInfo; } void CWeaponPortalBase::FireBullets( const FireBulletsInfo_t &info ) { FireBulletsInfo_t modinfo = info; modinfo.m_iPlayerDamage = GetPortalWpnData().m_iPlayerDamage; BaseClass::FireBullets( modinfo ); } #if defined( CLIENT_DLL ) #include "c_te_effect_dispatch.h" #define NUM_MUZZLE_FLASH_TYPES 4 bool CWeaponPortalBase::OnFireEvent( C_BaseViewModel *pViewModel, const Vector& origin, const QAngle& angles, int event, const char *options ) { return BaseClass::OnFireEvent( pViewModel, origin, angles, event, options ); } void UTIL_ClipPunchAngleOffset( QAngle &in, const QAngle &punch, const QAngle &clip ) { QAngle final = in + punch; //Clip each component for ( int i = 0; i < 3; i++ ) { if ( final[i] > clip[i] ) { final[i] = clip[i]; } else if ( final[i] < -clip[i] ) { final[i] = -clip[i]; } //Return the result in[i] = final[i] - punch[i]; } } #endif