//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: Combative shield weapon // // $NoKeywords: $ //=============================================================================// #include "cbase.h" #include "basetfplayer_shared.h" #include "weapon_twohandedcontainer.h" #include "weapon_combatshield.h" #include "engine/IEngineSound.h" #include "in_buttons.h" #include "weapon_combat_usedwithshieldbase.h" #if !defined( CLIENT_DLL ) #include "tf_shield.h" extern ConVar tf_knockdowntime; #else #include "iviewrender_beams.h" #include "c_team.h" #include "cdll_int.h" #include "hudelement.h" #include "bone_setup.h" #include "beamdraw.h" #include #endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" // Damage CVars ConVar weapon_combat_shield_rechargetime( "weapon_combat_shield_rechargetime","4", FCVAR_REPLICATED, "Time after taking damage before the shields starts recharging" ); ConVar weapon_combat_shield_rechargeamount( "weapon_combat_shield_rechargeamount","0.03", FCVAR_REPLICATED, "Amount shield recharges every 10th of a second (must be an int)" ); ConVar weapon_combat_shield_factor( "weapon_combat_shield_factor","0.4", FCVAR_REPLICATED, "Factor applied to damage the shield blocks" ); ConVar weapon_combat_shield_teslaspeed( "weapon_combat_shield_teslaspeed", "0.025f", FCVAR_REPLICATED, "Speed of the tesla effect on the view model." ); ConVar weapon_combat_shield_teslaskitter( "weapon_combat_shield_teslaskitter", "0.3f", FCVAR_REPLICATED, "Speed of the tesla skitter effect (percentage)." ); ConVar weapon_combat_shield_teslaeffect( "weapon_combat_shield_teslaeffect", "4", FCVAR_REPLICATED, "Experimenting with effects." ); ConVar weapon_combat_shield_health( "weapon_combat_shield_health", "100", FCVAR_REPLICATED, "Combat shield's maximum health." ); // HACK: If we don't set this then we get a pop when transitioning into / out of idle animation // for commando_test model because the origin is wrong // This can be removed once the model itself is fixed #define SHIELD_FADOUT_TIME 0.2f //----------------------------------------------------------------------------- // Constructor, destructor: //----------------------------------------------------------------------------- CWeaponCombatShield::CWeaponCombatShield() { m_bAllowPostFrame = true; m_bHasShieldParry = false; m_flShieldHealth = 1.0; #if defined( CLIENT_DLL ) m_flFlashTimeEnd = 0; m_flTeslaSpeed = weapon_combat_shield_teslaspeed.GetFloat(); m_flTeslaSkitter = weapon_combat_shield_teslaskitter.GetFloat(); m_flTeslaLeftInc = 0.0f; m_flTeslaRightInc = 0.0f; m_pTeslaBeam = NULL; m_pTeslaBeam2 = NULL; m_flShieldInc = 1.0f; m_pShieldBeam = NULL; m_pShieldBeam2 = NULL; m_pShieldBeam3 = NULL; #endif SetPredictionEligible( true ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CWeaponCombatShield::Precache( void ) { BaseClass::Precache(); PrecacheModel( "sprites/blueflare1.vmt" ); PrecacheModel( "sprites/physbeam.vmt" ); PrecacheScriptSound( "WeaponCombatShield.TakeBash" ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CWeaponCombatShield::Deploy( void ) { if ( BaseClass::Deploy() ) { GainedNewTechnology(NULL); SetShieldState( SS_DOWN ); return true; } return false; } //----------------------------------------------------------------------------- // Purpose: Get the activity the other weapon in our twohanded container should play for this activity //----------------------------------------------------------------------------- int CWeaponCombatShield::GetOtherWeaponsActivity( int iActivity ) { switch ( iActivity ) { case ACT_VM_HAULBACK: return ACT_VM_DRAW; case ACT_VM_SECONDARYATTACK: return ACT_VM_HOLSTER; default: break; }; return -1; } //----------------------------------------------------------------------------- // Purpose: Get the activity the other weapon in our twohanded container should // play instead of the one it's attempting to play. //----------------------------------------------------------------------------- int CWeaponCombatShield::ReplaceOtherWeaponsActivity( int iActivity ) { switch ( iActivity ) { case ACT_VM_IDLE: // If I'm active, don't let it idle if ( GetShieldState() != SS_DOWN ) return -1; default: break; }; return BaseClass::ReplaceOtherWeaponsActivity(iActivity); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CWeaponCombatShield::Holster( CBaseCombatWeapon *pSwitchingTo ) { CBaseTFPlayer *player = ToBaseTFPlayer( GetOwner() ); if ( player ) { player->SetBlocking( false ); player->SetParrying( false ); if ( m_iShieldState != SS_DOWN && m_iShieldState != SS_UNAVAILABLE ) { SetShieldState( SS_LOWERING ); } } return true; } //----------------------------------------------------------------------------- // Purpose: I've been bashed by another player's shield //----------------------------------------------------------------------------- bool CWeaponCombatShield::TakeShieldBash( CBaseTFPlayer *pBasher ) { CBaseTFPlayer *pOwner = ToBaseTFPlayer( GetOwner() ); if ( !pOwner ) return false; // If I'm blocking, drop my block and prevent me from doing anything if ( GetShieldState() == SS_UP || GetShieldState() == SS_RAISING || GetShieldState() == SS_LOWERING ) { // Make the shield unavailable SetShieldState( SS_UNAVAILABLE ); SendWeaponAnim( ACT_VM_HITCENTER ); m_flShieldUnavailableEndTime = gpGlobals->curtime + 2.0; // Play a sound EmitSound( "WeaponCombatShield.TakeBash" ); return true; } return false; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CWeaponCombatShield::SetShieldState( int iShieldState ) { CBaseTFPlayer *pOwner = ToBaseTFPlayer( GetOwner() ); if ( !pOwner ) return; switch (iShieldState ) { default: case SS_DOWN: pOwner->SetBlocking( false ); m_flShieldUpStartTime = 0; m_flShieldParryEndTime = 0; m_flShieldUnavailableEndTime = 0; m_flShieldRaisedTime = 0.0f; m_flShieldLoweredTime = 0.0f; break; case SS_UP: SendWeaponAnim( ACT_VM_FIDGET ); pOwner->SetBlocking( true ); m_flShieldDownStartTime = 0.0f; m_flShieldParryEndTime = 0; m_flShieldUnavailableEndTime = 0; m_flShieldRaisedTime = 0.0f; m_flShieldLoweredTime = 0.0f; break; case SS_PARRYING: pOwner->SetBlocking( false ); pOwner->SetParrying( true ); m_flShieldParryEndTime = gpGlobals->curtime + PARRY_OPPORTUNITY_LENGTH; m_flShieldParrySwingEndTime = gpGlobals->curtime + 1.0f; // a hack to make it look ok m_flShieldUnavailableEndTime = gpGlobals->curtime + SequenceDuration(); m_flNextPrimaryAttack = m_flShieldUnavailableEndTime; m_flShieldRaisedTime = 0.0f; m_flShieldLoweredTime = 0.0f; break; case SS_PARRYING_FINISH_SWING: pOwner->SetBlocking( false ); pOwner->SetParrying( false ); break; case SS_UNAVAILABLE: SendWeaponAnim( ACT_VM_HAULBACK ); pOwner->SetBlocking( false ); pOwner->SetParrying( false ); break; case SS_RAISING: { pOwner->SetBlocking( false ); pOwner->SetParrying( false ); m_flShieldRaisedTime = gpGlobals->curtime + SequenceDuration(); m_flShieldUpStartTime = gpGlobals->curtime; } break; case SS_LOWERING: { SendWeaponAnim( ACT_VM_HAULBACK ); pOwner->SetBlocking( false ); pOwner->SetParrying( false ); m_flShieldLoweredTime = gpGlobals->curtime + SequenceDuration(); m_flShieldDownStartTime = gpGlobals->curtime; } break; }; m_iShieldState = iShieldState; } //----------------------------------------------------------------------------- // Purpose: Check to see if the shield state should change //----------------------------------------------------------------------------- void CWeaponCombatShield::UpdateShieldState( void ) { CBaseTFPlayer *pOwner = ToBaseTFPlayer( GetOwner() ); if ( !pOwner ) return; // Check to see if I should move out of the current state switch ( m_iShieldState ) { default: case SS_DOWN: case SS_UP: break; case SS_RAISING: if ( gpGlobals->curtime > m_flShieldRaisedTime ) { SetShieldState( SS_UP ); } break; case SS_LOWERING: if ( gpGlobals->curtime > m_flShieldLoweredTime ) { SetShieldState( SS_DOWN ); } break; case SS_PARRYING: if ( gpGlobals->curtime > m_flShieldParryEndTime ) { SetShieldState( SS_PARRYING_FINISH_SWING ); } break; case SS_PARRYING_FINISH_SWING: if ( gpGlobals->curtime > m_flShieldParrySwingEndTime ) { SetShieldState( SS_UNAVAILABLE ); } break; case SS_UNAVAILABLE: if ( gpGlobals->curtime > m_flShieldUnavailableEndTime ) { SetShieldState( SS_DOWN ); } break; }; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int CWeaponCombatShield::GetShieldState( void ) { return m_iShieldState; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CWeaponCombatShield::SetShieldUsable( bool bUsable ) { // Shutting down! if ( !bUsable ) { if ( m_iShieldState == SS_UP || m_iShieldState == SS_RAISING ) { // We've got our shield up, so drop it (play sound & animation). SendWeaponAnim( ACT_VM_HAULBACK ); WeaponSound( SPECIAL2 ); SetShieldState( SS_LOWERING ); } } m_bUsable = bUsable; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CWeaponCombatShield::ShieldUsable( void ) { return m_bUsable; } //----------------------------------------------------------------------------- // Purpose: // Input : allow - //----------------------------------------------------------------------------- void CWeaponCombatShield::SetAllowPostFrame( bool allow ) { m_bAllowPostFrame = allow; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CWeaponCombatShield::ItemPostFrame( void ) { if ( m_bAllowPostFrame ) { ShieldPostFrame(); } } //----------------------------------------------------------------------------- // Purpose: Allow the shield to interrupt reloads, etc. //----------------------------------------------------------------------------- void CWeaponCombatShield::ItemBusyFrame( void ) { if ( m_bAllowPostFrame ) { ShieldPostFrame(); } } //----------------------------------------------------------------------------- // Purpose: The player holding this weapon has just gained new technology. //----------------------------------------------------------------------------- void CWeaponCombatShield::GainedNewTechnology( CBaseTechnology *pTechnology ) { BaseClass::GainedNewTechnology( pTechnology ); CBaseTFPlayer *pPlayer = ToBaseTFPlayer( GetOwner() ); if ( pPlayer ) { // Has a parry? if ( pPlayer->HasNamedTechnology( "com_comboshield_parry" ) ) { m_bHasShieldParry = true; } else { m_bHasShieldParry = false; } } } //----------------------------------------------------------------------------- // Purpose: Handle the shield input //----------------------------------------------------------------------------- void CWeaponCombatShield::ShieldPostFrame( void ) { CBaseTFPlayer *pOwner = ToBaseTFPlayer( GetOwner() ); if (!pOwner) return; UpdateShieldState(); CheckReload(); // Store off EMP state bool isEMPed = IsOwnerEMPed(); // If the shield's unavailable, just abort if ( GetShieldState() == SS_UNAVAILABLE ) return; if ( m_flNextPrimaryAttack > gpGlobals->curtime ) return; bool shieldRaised = ( GetShieldState() == SS_UP ); bool shieldRaising = ( GetShieldState() == SS_RAISING ); // GetShieldState() == SS_LOWERING ); // If my shield's out of power, I can't do anything with it if ( !GetShieldHealth() ) return; // Was the shield button just pressed? if ( GetShieldState() == SS_DOWN && !isEMPed && pOwner->m_nButtons & IN_ATTACK2 ) { // Play sound & anim WeaponSound( SPECIAL1 ); SendWeaponAnim( ACT_VM_SECONDARYATTACK ); SetShieldState( SS_RAISING ); // Abort any reloads in progess pOwner->AbortReload(); } else if ( ( shieldRaised || shieldRaising ) && !FBitSet( pOwner->m_nButtons, IN_ATTACK2 ) ) { // Shield button was just released, check to see if we were parrying bool shouldParry = (gpGlobals->curtime < (m_flShieldUpStartTime + PARRY_DETECTION_TIME )); if ( m_bHasShieldParry && shouldParry ) { // Parry! // Play sound & anim WeaponSound( SPECIAL2 ); SendWeaponAnim( ACT_VM_SWINGHIT ); SetShieldState( SS_PARRYING ); // Bash enemies in front of me ShieldBash(); } else { // Player's just lowered his shield // Play sound & anim WeaponSound( SPECIAL2 ); SendWeaponAnim( ACT_VM_HAULBACK ); SetShieldState( SS_LOWERING ); m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration(); } } else if ( GetShieldState() == SS_UP && ( pOwner->m_nButtons & IN_ATTACK2 ) && ( isEMPed ) ) { // We've got our shield up, and we were just EMPed, so drop it // Play sound & anim SendWeaponAnim( ACT_VM_HAULBACK ); WeaponSound( SPECIAL2 ); SetShieldState( SS_LOWERING ); } } //----------------------------------------------------------------------------- // Purpose: Bash enemies in front of me with my shield //----------------------------------------------------------------------------- void CWeaponCombatShield::ShieldBash( void ) { #if 0 // ROBIN: Disabled shield bash return; // Get any players in front of me CBaseTFPlayer *pOwner = ToBaseTFPlayer( GetOwner() ); if ( !pOwner ) return; // Get the target point and location Vector vecAiming; Vector vecSrc = pOwner->Weapon_ShootPosition( pOwner->GetOrigin() ); pOwner->EyeVectors( &vecAiming ); // Find a player in range of this player, and make sure they're healable trace_t tr; Vector vecEnd = vecSrc + (vecAiming * SHIELD_BASH_RANGE); UTIL_TraceLine( vecSrc, vecEnd, MASK_SHOT, pOwner->edict(), COLLISION_GROUP_NONE, &tr); if (tr.fraction != 1.0) { CBaseEntity *pEntity = CBaseEntity::Instance(tr.u.ent); if ( pEntity ) { CBaseTFPlayer *pPlayer = ToBaseTFPlayer( pEntity ); if ( pPlayer && (pPlayer != pOwner) ) { // Target needs to be on the eneny team if ( pPlayer->IsAlive() && !pPlayer->InSameTeam( pOwner ) ) { // Ok, we have an enemy player pPlayer->TakeShieldBash( pOwner ); } } } } #endif } //----------------------------------------------------------------------------- // Purpose: Attempt to block the incoming attack, and return the damage it // should do after the block, if any. //----------------------------------------------------------------------------- float CWeaponCombatShield::AttemptToBlock( float flDamage ) { CBaseTFPlayer *pPlayer = ToBaseTFPlayer( GetOwner() ); if ( !pPlayer || !weapon_combat_shield_factor.GetFloat() ) return 0; // Block as much of the damage as we can float flPowerNeeded = flDamage * weapon_combat_shield_factor.GetFloat(); flPowerNeeded = RemapVal( flPowerNeeded, 0, weapon_combat_shield_health.GetFloat(), 0, 1 ); float flPowerUsed = MIN( flPowerNeeded, GetShieldHealth() ); #ifndef CLIENT_DLL RemoveShieldHealth( flPowerUsed ); // Start recharging shortly after taking damage SetThink( ShieldRechargeThink ); SetNextThink( gpGlobals->curtime + weapon_combat_shield_rechargetime.GetFloat() ); #endif // Failed to block it all? if ( flPowerUsed < flPowerNeeded ) { #ifndef CLIENT_DLL // Force the shield to drop if it's up if ( GetShieldState() == SS_UP ) { // Play sound & anim SendWeaponAnim( ACT_VM_HAULBACK ); WeaponSound( SPECIAL2 ); SetShieldState( SS_LOWERING ); } #endif return ( flDamage - (flPowerUsed * (1.0 / weapon_combat_shield_factor.GetFloat())) ); } return 0; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- float CWeaponCombatShield::GetShieldHealth( void ) { return m_flShieldHealth; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CWeaponCombatShield::AddShieldHealth( float flHealth ) { m_flShieldHealth = MIN( 1.0, m_flShieldHealth + flHealth ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CWeaponCombatShield::RemoveShieldHealth( float flHealth ) { m_flShieldHealth = MAX( 0.0, m_flShieldHealth - flHealth ); } //----------------------------------------------------------------------------- // Purpose: Recharge the shield //----------------------------------------------------------------------------- void CWeaponCombatShield::ShieldRechargeThink( void ) { // FIXME: //xxx #if !defined( CLIENT_DLL ) if ( GetShieldHealth() >= 1.0 ) { SetThink( NULL ); return; } AddShieldHealth( weapon_combat_shield_rechargeamount.GetFloat() ); SetNextThink( gpGlobals->curtime + 0.1f ); #endif } //==================================================================================== // WEAPON CLIENT HANDLING //==================================================================================== int CWeaponCombatShield::UpdateClientData( CBasePlayer *pPlayer ) { if ( !pPlayer ) { return BaseClass::UpdateClientData( pPlayer ); } CWeaponTwoHandedContainer *pContainer = ( CWeaponTwoHandedContainer * )pPlayer->Weapon_OwnsThisType( "weapon_twohandedcontainer" ); if ( !pContainer || pContainer != pPlayer->GetActiveWeapon() ) return BaseClass::UpdateClientData( pPlayer ); // Make sure this weapon is one of the container's active weapons if ( pContainer->GetLeftWeapon() != this && pContainer->GetRightWeapon() != this ) return BaseClass::UpdateClientData( pPlayer ); int retval = pContainer->UpdateClientData( pPlayer ); m_iState = pContainer->m_iState; return retval; } //----------------------------------------------------------------------------- // Purpose: // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool CWeaponCombatShield::VisibleInWeaponSelection( void ) { return false; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ bool CWeaponCombatShield::IsUp( void ) { return ( GetShieldState() == SS_UP ); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ float CWeaponCombatShield::GetRaisingTime( void ) { if ((GetShieldState() != SS_UP ) && (GetShieldState() != SS_RAISING)) return 0.0f; return gpGlobals->curtime - m_flShieldUpStartTime; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ float CWeaponCombatShield::GetLoweringTime( void ) { if ((GetShieldState() != SS_DOWN ) && (GetShieldState() != SS_LOWERING)) return 0.0f; return gpGlobals->curtime - m_flShieldDownStartTime; } #if defined( CLIENT_DLL ) //----------------------------------------------------------------------------- // Purpose: Draw the ammo counts //----------------------------------------------------------------------------- void CWeaponCombatShield::DrawAmmo( void ) { // ROBIN: Removed this now that the shield colors itself to show health level return; int r, g, b, a; int x, y; // Get the shield power level float flPowerLevel = GetShieldHealth(); float flInverseFactor = 1.0 - flPowerLevel; // Set our color gHUD.m_clrNormal.GetColor( r, g, b, a ); int iWidth = XRES(12); int iHeight = YRES(64); x = XRES(548); y = ( ScreenHeight() - YRES(2) - iHeight ); // Flashing the power level? float flFlash = 0; if ( gpGlobals->curtime < m_flFlashTimeEnd && !GetPrimaryAmmo() ) { flFlash = fmod( gpGlobals->curtime, 0.25 ); flFlash *= 2 * M_PI; flFlash = cos( flFlash ); } // draw the exhausted portion of the bar. vgui::surface()->DrawSetColor( Color( r, g * flPowerLevel, b * flPowerLevel, 100 + (flFlash * 100) ) ); vgui::surface()->DrawFilledRect( x, y, x + iWidth, y + iHeight * flInverseFactor ); // draw the powerered portion of the bar vgui::surface()->DrawSetColor( Color( r, g * flPowerLevel, b * flPowerLevel, 190 ) ); vgui::surface()->DrawFilledRect( x, y + iHeight * flInverseFactor, x + iWidth, y + iHeight * flInverseFactor + iHeight * flPowerLevel); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CWeaponCombatShield::GetViewmodelBoneControllers( C_BaseViewModel *pViewModel, float controllers[MAXSTUDIOBONECTRLS]) { // Dial shows the shield power level float flPowerLevel = 1.0; if ( GetShieldState() == SS_UP ) { flPowerLevel = GetShieldHealth(); } else if ( GetShieldState() == SS_RAISING ) { // Bring the power up with the animation float flTotal = m_flShieldRaisedTime - m_flShieldUpStartTime; float flCurrent = (gpGlobals->curtime - m_flShieldUpStartTime); flPowerLevel = flCurrent / flTotal; } else if ( GetShieldState() == SS_LOWERING ) { // Bring the power down with the animation float flTotal = (m_flShieldLoweredTime - m_flShieldDownStartTime); float flCurrent = (gpGlobals->curtime - m_flShieldDownStartTime); flPowerLevel = 1.0 - (flCurrent / flTotal); } // Make the middle point be full power (right of the middle being powered up) // Adjust a little for the perspective. flPowerLevel *= 0.55; // Add some shake flPowerLevel += RandomFloat( -0.02, 0.02 ); controllers[0] = flPowerLevel; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CWeaponCombatShield::ViewModelDrawn( C_BaseViewModel *pViewModel ) { if ( m_iShieldState == SS_DOWN ) return; DrawBeams( pViewModel ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CWeaponCombatShield::InitShieldBeam( void ) { BeamInfo_t beamInfo; beamInfo.m_vecStart.Init(); beamInfo.m_vecEnd.Init(); beamInfo.m_pszModelName = "sprites/physbeam.vmt"; beamInfo.m_flHaloScale = 0.0f; beamInfo.m_flLife = 0.0f; beamInfo.m_flWidth = 2.0f; beamInfo.m_flEndWidth = 2.0f; beamInfo.m_flFadeLength = 0.0f; beamInfo.m_flAmplitude = 4.0f; beamInfo.m_flBrightness = 50.0f; beamInfo.m_flSpeed = 5.0f; beamInfo.m_nStartFrame = 0; beamInfo.m_flFrameRate = 0.0f; beamInfo.m_flRed = 255.0f; beamInfo.m_flGreen = 255.0f; beamInfo.m_flBlue = 128.0f; beamInfo.m_nSegments = 15; beamInfo.m_bRenderable = false; m_pShieldBeam = beams->CreateBeamPoints( beamInfo ); beamInfo.m_vecStart.Init(); beamInfo.m_vecEnd.Init(); beamInfo.m_pszModelName = "sprites/physbeam.vmt"; beamInfo.m_flHaloScale = 0.0f; beamInfo.m_flLife = 0.0f; beamInfo.m_flWidth = 1.5f; beamInfo.m_flEndWidth = 1.5f; beamInfo.m_flFadeLength = 0.0f; beamInfo.m_flAmplitude = 8.0f; beamInfo.m_flBrightness = 75.0f; beamInfo.m_flSpeed = 10.0f; beamInfo.m_nStartFrame = 0; beamInfo.m_flFrameRate = 0.0f; beamInfo.m_flRed = 255.0f; beamInfo.m_flGreen = 255.0f; beamInfo.m_flBlue = 128.0f; beamInfo.m_nSegments = 20; beamInfo.m_bRenderable = false; m_pShieldBeam2 = beams->CreateBeamPoints( beamInfo ); beamInfo.m_vecStart.Init(); beamInfo.m_vecEnd.Init(); beamInfo.m_pszModelName = "sprites/physbeam.vmt"; beamInfo.m_flHaloScale = 0.0f; beamInfo.m_flLife = 0.0f; beamInfo.m_flWidth = 3.0f; beamInfo.m_flEndWidth = 3.0f; beamInfo.m_flFadeLength = 0.0f; beamInfo.m_flAmplitude = 5.5f; beamInfo.m_flBrightness = 50.0f; beamInfo.m_flSpeed = 10.0f; beamInfo.m_nStartFrame = 0; beamInfo.m_flFrameRate = 0.0f; beamInfo.m_flRed = 255.0f; beamInfo.m_flGreen = 255.0f; beamInfo.m_flBlue = 128.0f; beamInfo.m_nSegments = 18; beamInfo.m_bRenderable = false; m_pShieldBeam3 = beams->CreateBeamPoints( beamInfo ); m_hShieldSpriteMaterial.Init( "sprites/blueflare1", TEXTURE_GROUP_CLIENT_EFFECTS ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CWeaponCombatShield::InitTeslaBeam( void ) { BeamInfo_t beamInfo; beamInfo.m_vecStart.Init(); beamInfo.m_vecEnd.Init(); beamInfo.m_pszModelName = "sprites/blueflare1.vmt"; beamInfo.m_flHaloScale = 0.0f; beamInfo.m_flLife = 0.0f; beamInfo.m_flWidth = 1.0f; beamInfo.m_flEndWidth = 1.0f; beamInfo.m_flFadeLength = 0.0f; beamInfo.m_flAmplitude = 16.0f; beamInfo.m_flBrightness = 255.0f; beamInfo.m_flSpeed = 25.0f; beamInfo.m_nStartFrame = 0; beamInfo.m_flFrameRate = 0.0f; beamInfo.m_flRed = 206.0f; beamInfo.m_flGreen = 181.0f; beamInfo.m_flBlue = 127.0f; beamInfo.m_nSegments = 15; beamInfo.m_bRenderable = false; m_pTeslaBeam = beams->CreateBeamPoints( beamInfo ); beamInfo.m_vecStart.Init(); beamInfo.m_vecEnd.Init(); beamInfo.m_pszModelName = "sprites/blueflare1.vmt"; beamInfo.m_flHaloScale = 0.0f; beamInfo.m_flLife = 0.0f; beamInfo.m_flWidth = 1.0f; beamInfo.m_flEndWidth = 1.0f; beamInfo.m_flFadeLength = 0.0f; beamInfo.m_flAmplitude = 27.0f; beamInfo.m_flBrightness = 100.0f; beamInfo.m_flSpeed = 15.0f; beamInfo.m_nStartFrame = 0; beamInfo.m_flFrameRate = 0.0f; beamInfo.m_flRed = 206.0f; beamInfo.m_flGreen = 181.0f; beamInfo.m_flBlue = 127.0f; beamInfo.m_nSegments = 8; beamInfo.m_bRenderable = false; m_pTeslaBeam2 = beams->CreateBeamPoints( beamInfo ); m_hTeslaSpriteMaterial.Init( "sprites/blueflare1", TEXTURE_GROUP_CLIENT_EFFECTS ); } //----------------------------------------------------------------------------- // Purpose: Render a tesla beam in the veiw model. // // NOTE: This is a big ugly mess that will get cleaned up when I nail down // the effect. //----------------------------------------------------------------------------- void CWeaponCombatShield::DrawBeams( C_BaseViewModel *pViewModel ) { // Verify data. if ( !pViewModel ) return; // Only humans have the tesla effects if ( GetTeamNumber() == TEAM_ALIENS ) return; // Init if ( !m_pTeslaBeam ) { InitTeslaBeam(); } if ( !m_pShieldBeam ) { InitShieldBeam(); } if ( !m_pShieldBeam || !m_pTeslaBeam ) return; // Variables BeamInfo_t beamInfo; QAngle vecAngle; int iAttachment; // Setup a color reflecting the health float flShieldHealth = GetShieldHealth(); color32 color; color.r = 206; color.g = flShieldHealth * 182; color.b = flShieldHealth * 127; color.a = 255; // Tesla Effect Vector vecRightTop, vecRightBottom; Vector vecLeftTop, vecLeftBottom; iAttachment = pViewModel->LookupAttachment( "LeftBottom" ); pViewModel->GetAttachment( iAttachment, vecLeftBottom, vecAngle ); pViewModel->UncorrectViewModelAttachment( vecLeftBottom ); iAttachment = pViewModel->LookupAttachment( "LeftTip" ); pViewModel->GetAttachment( iAttachment, vecLeftTop, vecAngle ); pViewModel->UncorrectViewModelAttachment( vecLeftTop ); iAttachment = pViewModel->LookupAttachment( "RightBottom" ); pViewModel->GetAttachment( iAttachment, vecRightBottom, vecAngle ); pViewModel->UncorrectViewModelAttachment( vecRightBottom ); iAttachment = pViewModel->LookupAttachment( "RightTip" ); pViewModel->GetAttachment( iAttachment, vecRightTop, vecAngle ); pViewModel->UncorrectViewModelAttachment( vecRightTop ); m_flTeslaLeftInc += weapon_combat_shield_teslaspeed.GetFloat(); m_flTeslaRightInc += weapon_combat_shield_teslaspeed.GetFloat(); if ( m_flTeslaLeftInc > 1.0f ) { m_flTeslaLeftInc = 0.0f; } if ( m_flTeslaRightInc > 1.0f ) { m_flTeslaRightInc = 0.0f; } Vector vecLeft = vecLeftTop - vecLeftBottom; Vector vecRight = vecRightTop - vecRightBottom; Vector vecStart = vecLeftBottom + ( m_flTeslaLeftInc * vecLeft ); Vector vecEnd = vecRightBottom + ( m_flTeslaRightInc * vecRight ); beamInfo.m_vecStart = vecStart; beamInfo.m_vecEnd = vecEnd; beamInfo.m_flRed = color.r; beamInfo.m_flGreen = color.g; beamInfo.m_flBlue = color.b; beams->UpdateBeamInfo( m_pTeslaBeam, beamInfo ); beams->UpdateBeamInfo( m_pTeslaBeam2, beamInfo ); beams->DrawBeam( m_pTeslaBeam ); beams->DrawBeam( m_pTeslaBeam2 ); // Draw a sprite at the tip of the tesla coil. float flSize = 4.0f; materials->Bind( m_hShieldSpriteMaterial, this ); DrawSprite( vecStart, flSize, flSize, color ); DrawSprite( vecEnd, flSize, flSize, color ); // Shield Effect float flPercentage = random->RandomFloat( 0.0f, 1.0f ); if ( flPercentage < weapon_combat_shield_teslaskitter.GetFloat() ) { char szShieldJoint[16]; int nJoint = random->RandomInt( 1, 8 ); Q_snprintf( szShieldJoint, sizeof( szShieldJoint ), "Shield%d", nJoint ); Vector vecJoint; int iAttachment = pViewModel->LookupAttachment( &szShieldJoint[0] ); pViewModel->GetAttachment( iAttachment, vecJoint, vecAngle ); pViewModel->UncorrectViewModelAttachment( vecJoint ); if ( nJoint < 5 ) { beamInfo.m_vecStart = vecLeftTop; } else { beamInfo.m_vecStart = vecRightTop; } beamInfo.m_vecEnd = vecJoint; beams->UpdateBeamInfo( m_pTeslaBeam, beamInfo ); beams->UpdateBeamInfo( m_pTeslaBeam2, beamInfo ); beams->DrawBeam( m_pTeslaBeam ); beams->DrawBeam( m_pTeslaBeam2 ); float flSize = 7.0f; color32 color = { 206, 181, 127, 255 }; materials->Bind( m_hShieldSpriteMaterial, this ); DrawSprite( beamInfo.m_vecStart, flSize, flSize, color ); } #if 0 // Shield Effect char szShieldJoint[16]; Vector vecShieldJoints[8]; for( int iJoint = 0; iJoint < 8; ++iJoint ) { Q_snprintf( szShieldJoint, sizeof( szShieldJoint ), "Shield%d", iJoint+1 ); iAttachment = pViewModel->LookupAttachment( &szShieldJoint[0] ); pViewModel->GetAttachment( iAttachment, vecShieldJoints[iJoint], vecAngle ); pViewModel->UncorrectViewModelAttachment( vecShieldJoints[iJoint] ); } // Shield Internal if ( m_flShieldInc < 1.0f ) { Vector vecEdge, vecEnd; if ( m_bLeftToRight ) { vecEdge = vecShieldJoints[((m_nShieldEdge+1)%8)] - vecShieldJoints[m_nShieldEdge]; vecEnd = vecShieldJoints[m_nShieldEdge] + ( m_flShieldInc * vecEdge ); } else { vecEdge = vecShieldJoints[m_nShieldEdge] - vecShieldJoints[((m_nShieldEdge+1)%8)]; vecEnd = vecShieldJoints[((m_nShieldEdge+1)%8)] + ( m_flShieldInc * vecEdge ); } if ( m_nShieldEdge < 5 ) { beamInfo.m_vecStart = vecLeftTop; } else { beamInfo.m_vecStart = vecRightTop; } beamInfo.m_vecEnd = vecEnd; beams->UpdateBeamPoints( m_pShieldBeam2, beamInfo ); beams->UpdateBeamPoints( m_pShieldBeam3, beamInfo ); beams->DrawBeam( m_pShieldBeam2 ); beams->DrawBeam( m_pShieldBeam3 ); m_flShieldInc += m_flShieldSpeed; } else { m_flShieldInc = 0.0f; m_flShieldSpeed = random->RandomFloat( 0.015f, 0.15f ); m_nShieldEdge = random->RandomInt( 0, 7 ); float flSide = random->RandomFloat( 0.0f, 1.0f ); m_bLeftToRight = ( flSide < 0.5f ); } // Shield Outline for( iJoint = 0; iJoint < 8; ++iJoint ) { beamInfo.m_vecStart = vecShieldJoints[iJoint]; beamInfo.m_vecEnd = vecShieldJoints[(iJoint+1)%8]; beams->UpdateBeamPoints( m_pShieldBeam, beamInfo ); beams->UpdateBeamPoints( m_pShieldBeam2, beamInfo ); beams->DrawBeam( m_pShieldBeam ); beams->DrawBeam( m_pShieldBeam2 ); // Draw a sprite at the tip of the tesla coil. float flSize = 3.0f; color32 color = { 255, 255, 0, 255 }; materials->Bind( m_hShieldSpriteMaterial, this ); DrawSprite( vecShieldJoints[iJoint], flSize, flSize, color ); } #endif } //----------------------------------------------------------------------------- // Purpose: If the shield has no health, and they're trying to raise it, flash the power level //----------------------------------------------------------------------------- void CWeaponCombatShield::HandleInput( void ) { // If the player's dead, ignore input C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); if ( !pPlayer || pPlayer->GetHealth() < 0 ) return; // Attempting to raise the shield? if ( !GetShieldHealth() && ( gHUD.m_iKeyBits & IN_ATTACK2 ) ) { m_flFlashTimeEnd = gpGlobals->curtime + 1.0; } } #endif LINK_ENTITY_TO_CLASS( weapon_combat_shield, CWeaponCombatShield ); LINK_ENTITY_TO_CLASS( weapon_combat_shield_alien, CWeaponCombatShieldAlien ); IMPLEMENT_NETWORKCLASS_ALIASED( WeaponCombatShield , DT_WeaponCombatShield ) IMPLEMENT_NETWORKCLASS_ALIASED( WeaponCombatShieldAlien, DT_WeaponCombatShieldAlien ) #if !defined( CLIENT_DLL ) //----------------------------------------------------------------------------- // Purpose: Only send the LocalWeaponData to the player carrying the weapon //----------------------------------------------------------------------------- void* SendProxy_SendCombatShieldLocalWeaponDataTable( const SendProp *pProp, const void *pStruct, const void *pVarData, CSendProxyRecipients *pRecipients, int objectID ) { // Get the weapon entity CBaseCombatWeapon *pWeapon = (CBaseCombatWeapon*)pVarData; if ( pWeapon ) { // Only send this chunk of data to the player carrying this weapon CBasePlayer *pPlayer = ToBasePlayer( pWeapon->GetOwner() ); if ( pPlayer ) { pRecipients->SetOnly( pPlayer->GetClientIndex() ); return (void*)pVarData; } } return NULL; } REGISTER_SEND_PROXY_NON_MODIFIED_POINTER( SendProxy_SendCombatShieldLocalWeaponDataTable ); #endif //----------------------------------------------------------------------------- // Purpose: Propagation data for weapons. Only sent when a player's holding it. //----------------------------------------------------------------------------- BEGIN_NETWORK_TABLE_NOBASE( CWeaponCombatShield, DT_WeaponCombatShieldLocal ) #if !defined( CLIENT_DLL ) SendPropTime( SENDINFO( m_flShieldParryEndTime ) ), SendPropTime( SENDINFO( m_flShieldParrySwingEndTime ) ), SendPropTime( SENDINFO( m_flShieldUnavailableEndTime ) ), SendPropTime( SENDINFO( m_flShieldRaisedTime ) ), SendPropTime( SENDINFO( m_flShieldLoweredTime ) ), #else RecvPropTime( RECVINFO( m_flShieldParryEndTime ) ), RecvPropTime( RECVINFO( m_flShieldParrySwingEndTime ) ), RecvPropTime( RECVINFO( m_flShieldUnavailableEndTime ) ), RecvPropTime( RECVINFO( m_flShieldRaisedTime ) ), RecvPropTime( RECVINFO( m_flShieldLoweredTime ) ), #endif END_NETWORK_TABLE() BEGIN_NETWORK_TABLE( CWeaponCombatShield , DT_WeaponCombatShield ) #if !defined( CLIENT_DLL ) SendPropDataTable("this", 0, &REFERENCE_SEND_TABLE(DT_WeaponCombatShieldLocal), SendProxy_SendCombatShieldLocalWeaponDataTable ), SendPropTime( SENDINFO( m_flShieldUpStartTime ) ), SendPropTime( SENDINFO( m_flShieldDownStartTime ) ), SendPropInt( SENDINFO( m_iShieldState ), 3, SPROP_UNSIGNED ), SendPropInt( SENDINFO( m_bAllowPostFrame ), 1, SPROP_UNSIGNED ), SendPropInt( SENDINFO( m_bHasShieldParry ), 1, SPROP_UNSIGNED ), SendPropFloat(SENDINFO( m_flShieldHealth ), 8, SPROP_ROUNDDOWN, 0.0f, 1.0f ), #else RecvPropDataTable("this", 0, 0, &REFERENCE_RECV_TABLE(DT_WeaponCombatShieldLocal)), RecvPropTime( RECVINFO( m_flShieldUpStartTime ) ), RecvPropTime( RECVINFO( m_flShieldDownStartTime ) ), RecvPropInt( RECVINFO( m_iShieldState ) ), RecvPropInt( RECVINFO( m_bAllowPostFrame ) ), RecvPropInt( RECVINFO( m_bHasShieldParry ) ), RecvPropFloat(RECVINFO( m_flShieldHealth ) ), #endif END_NETWORK_TABLE() BEGIN_NETWORK_TABLE( CWeaponCombatShieldAlien, DT_WeaponCombatShieldAlien ) END_NETWORK_TABLE() BEGIN_PREDICTION_DATA( CWeaponCombatShield ) DEFINE_PRED_FIELD( m_iShieldState, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ), DEFINE_PRED_FIELD( m_bAllowPostFrame, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ), DEFINE_PRED_FIELD( m_bHasShieldParry, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ), DEFINE_PRED_FIELD( m_flShieldHealth, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ), DEFINE_PRED_FIELD_TOL( m_flShieldUpStartTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, TD_MSECTOLERANCE ), DEFINE_PRED_FIELD_TOL( m_flShieldDownStartTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, TD_MSECTOLERANCE ), DEFINE_PRED_FIELD_TOL( m_flShieldParryEndTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, TD_MSECTOLERANCE ), DEFINE_PRED_FIELD_TOL( m_flShieldParrySwingEndTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, TD_MSECTOLERANCE ), DEFINE_PRED_FIELD_TOL( m_flShieldUnavailableEndTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, TD_MSECTOLERANCE ), DEFINE_PRED_FIELD_TOL( m_flShieldRaisedTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, TD_MSECTOLERANCE ), DEFINE_PRED_FIELD_TOL( m_flShieldLoweredTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, TD_MSECTOLERANCE ), END_PREDICTION_DATA() BEGIN_PREDICTION_DATA( CWeaponCombatShieldAlien ) END_PREDICTION_DATA() PRECACHE_WEAPON_REGISTER(weapon_combat_shield); PRECACHE_WEAPON_REGISTER(weapon_combat_shield_alien);