//========= Copyright © 1996-2005, 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" #include "vgui_int.h" #include "model_types.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_ALIASED( weapon_portal_base, WeaponPortalBase ); #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 g_pGameRules->IsMultiplayer(); } 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(); } // opt out of the model fast path for now. Since this model is "drawn" when in first person // and not looking through a portal and drawing is aborted in DrawModel() the fast path can // skip this and cause an extra copy of this entity to be visible IClientModelRenderable* CWeaponPortalBase::GetClientModelRenderable() { // NOTE: This should work but doesn't. It makes the object invisible in the portal pass // I suspect the IsRenderingPortal() test isn't getting re-entered while building the list for the frame // but I haven't tracked it down. For now I'll just have weapons opt out of the fast path and render // correctly at lower performance. #if 0 C_BasePlayer *pOwner = ToBasePlayer( GetOwner() ); if ( pOwner && C_BasePlayer::IsLocalPlayer( pOwner ) ) { ACTIVE_SPLITSCREEN_PLAYER_GUARD_ENT( pOwner ); if ( !g_pPortalRender->IsRenderingPortal() && !pOwner->ShouldDrawLocalPlayer() ) return 0; } return this; #else return NULL; #endif } int CWeaponPortalBase::DrawModel( int flags, const RenderableInstance_t &instance ) { if ( !m_bReadyToDraw ) return 0; C_BasePlayer *pOwner = ToBasePlayer( GetOwner() ); if ( pOwner && C_BasePlayer::IsLocalPlayer( pOwner ) ) { ACTIVE_SPLITSCREEN_PLAYER_GUARD_ENT( pOwner ); if ( !g_pPortalRender->IsRenderingPortal() && !pOwner->ShouldDrawLocalPlayer() && !VGui_IsSplitScreen() ) 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, instance ); if( bChangeModelBack ) SetModelIndex( iOriginalIndex ); return iRetVal; } bool CWeaponPortalBase::ShouldPredict() { if ( C_BasePlayer::IsLocalPlayer( GetOwner() ) ) 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 = GetHud().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 = HudIcons().GetIcon( "arrow" ); crosshair->SetCrosshair( pArrow, GetHud().m_clrNormal ); return; } // Find out if this weapon's auto-aimed onto a target bool bOnTarget = ( m_iState == WEAPON_IS_ACTIVE ) && player->m_fOnTarget; 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_flPlayerDamage = 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