//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: Hud element that indicates the direction of damage taken by the player // //=============================================================================// #include "cbase.h" #include "hud.h" #include "text_message.h" #include "hud_macros.h" #include "iclientmode.h" #include "view.h" #include #include #include #include "VGuiMatSurface/IMatSystemSurface.h" #include "materialsystem/imaterial.h" #include "materialsystem/imesh.h" #include "materialsystem/imaterialvar.h" #include "IEffects.h" #include "hudelement.h" #include "clienteffectprecachesystem.h" using namespace vgui; // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- class CHudDamageIndicator : public CHudElement, public vgui::Panel { DECLARE_CLASS_SIMPLE( CHudDamageIndicator, vgui::Panel ); public: CHudDamageIndicator( const char *pElementName ); void Init( void ); void VidInit( void ); void Reset( void ); virtual bool ShouldDraw( void ); // Handler for our message void MsgFunc_Damage( bf_read &msg ); private: virtual void OnThink(); virtual void Paint(); virtual void ApplySchemeSettings(vgui::IScheme *pScheme); // Painting void GetDamagePosition( const Vector &vecDelta, float flRadius, float *xpos, float *ypos, float *flRotation ); void DrawDamageIndicator(int x0, int y0, int x1, int y1, float alpha, float flRotation ); private: // Indication times CPanelAnimationVarAliasType( float, m_flMinimumWidth, "MinimumWidth", "10", "proportional_float" ); CPanelAnimationVarAliasType( float, m_flMaximumWidth, "MaximumWidth", "100", "proportional_float" ); CPanelAnimationVarAliasType( float, m_flMinimumHeight, "MinimumHeight", "20", "proportional_float" ); CPanelAnimationVarAliasType( float, m_flMaximumHeight, "MaximumHeight", "100", "proportional_float" ); CPanelAnimationVarAliasType( float, m_flStartRadius, "StartRadius", "140", "proportional_float" ); CPanelAnimationVarAliasType( float, m_flEndRadius, "EndRadius", "120", "proportional_float" ); CPanelAnimationVar( float, m_iMaximumDamage, "MaximumDamage", "100" ); CPanelAnimationVar( float, m_flMinimumTime, "MinimumTime", "1" ); CPanelAnimationVar( float, m_flMaximumTime, "MaximumTime", "2" ); CPanelAnimationVar( float, m_flTravelTime, "TravelTime", ".1" ); CPanelAnimationVar( float, m_flFadeOutPercentage, "FadeOutPercentage", "0.7" ); CPanelAnimationVar( float, m_flNoise, "Noise", "0.1" ); // List of damages we've taken struct damage_t { int iScale; float flLifeTime; float flStartTime; Vector vecDelta; // Damage origin relative to the player }; CUtlVector m_vecDamages; CMaterialReference m_WhiteAdditiveMaterial; }; DECLARE_HUDELEMENT( CHudDamageIndicator ); DECLARE_HUD_MESSAGE( CHudDamageIndicator, Damage ); //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CHudDamageIndicator::CHudDamageIndicator( const char *pElementName ) : CHudElement( pElementName ), BaseClass(NULL, "HudDamageIndicator") { vgui::Panel *pParent = g_pClientMode->GetViewport(); SetParent( pParent ); SetHiddenBits( HIDEHUD_HEALTH ); m_WhiteAdditiveMaterial.Init( "VGUI/damageindicator", TEXTURE_GROUP_VGUI ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CHudDamageIndicator::Init( void ) { HOOK_HUD_MESSAGE( CHudDamageIndicator, Damage ); Reset(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CHudDamageIndicator::Reset( void ) { m_vecDamages.Purge(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CHudDamageIndicator::VidInit( void ) { } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CHudDamageIndicator::OnThink() { } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CHudDamageIndicator::ShouldDraw( void ) { if ( !CHudElement::ShouldDraw() ) return false; // Don't draw if we don't have any damage to indicate if ( !m_vecDamages.Count() ) return false; return true; } //----------------------------------------------------------------------------- // Purpose: Convert a damage position in world units to the screen's units //----------------------------------------------------------------------------- void CHudDamageIndicator::GetDamagePosition( const Vector &vecDelta, float flRadius, float *xpos, float *ypos, float *flRotation ) { // Player Data Vector playerPosition = MainViewOrigin(); QAngle playerAngles = MainViewAngles(); Vector forward, right, up(0,0,1); AngleVectors (playerAngles, &forward, NULL, NULL ); forward.z = 0; VectorNormalize(forward); CrossProduct( up, forward, right ); float front = DotProduct(vecDelta, forward); float side = DotProduct(vecDelta, right); *xpos = flRadius * -side; *ypos = flRadius * -front; // Get the rotation (yaw) *flRotation = atan2(*xpos,*ypos) + M_PI; *flRotation *= 180 / M_PI; float yawRadians = -(*flRotation) * M_PI / 180.0f; float ca = cos( yawRadians ); float sa = sin( yawRadians ); // Rotate it around the circle *xpos = (int)((ScreenWidth() / 2) + (flRadius * sa)); *ypos = (int)((ScreenHeight() / 2) - (flRadius * ca)); } //----------------------------------------------------------------------------- // Purpose: Draw a single damage indicator //----------------------------------------------------------------------------- void CHudDamageIndicator::DrawDamageIndicator(int x0, int y0, int x1, int y1, float alpha, float flRotation ) { CMatRenderContextPtr pRenderContext( materials ); IMesh *pMesh = pRenderContext->GetDynamicMesh( true, NULL, NULL, m_WhiteAdditiveMaterial ); // Get the corners, since they're being rotated int wide = x1 - x0; int tall = y1 - y0; Vector2D vecCorners[4]; Vector2D center( x0 + (wide * 0.5f), y0 + (tall * 0.5f) ); float yawRadians = -flRotation * M_PI / 180.0f; Vector2D axis[2]; axis[0].x = cos(yawRadians); axis[0].y = sin(yawRadians); axis[1].x = -axis[0].y; axis[1].y = axis[0].x; Vector2DMA( center, -0.5f * wide, axis[0], vecCorners[0] ); Vector2DMA( vecCorners[0], -0.5f * tall, axis[1], vecCorners[0] ); Vector2DMA( vecCorners[0], wide, axis[0], vecCorners[1] ); Vector2DMA( vecCorners[1], tall, axis[1], vecCorners[2] ); Vector2DMA( vecCorners[0], tall, axis[1], vecCorners[3] ); // Draw the sucker CMeshBuilder meshBuilder; meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 ); int iAlpha = alpha * 255; meshBuilder.Color4ub( 255,255,255, iAlpha ); meshBuilder.TexCoord2f( 0,0,0 ); meshBuilder.Position3f( vecCorners[0].x,vecCorners[0].y,0 ); meshBuilder.AdvanceVertex(); meshBuilder.Color4ub( 255,255,255, iAlpha ); meshBuilder.TexCoord2f( 0,1,0 ); meshBuilder.Position3f( vecCorners[1].x,vecCorners[1].y,0 ); meshBuilder.AdvanceVertex(); meshBuilder.Color4ub( 255,255,255, iAlpha ); meshBuilder.TexCoord2f( 0,1,1 ); meshBuilder.Position3f( vecCorners[2].x,vecCorners[2].y,0 ); meshBuilder.AdvanceVertex(); meshBuilder.Color4ub( 255,255,255, iAlpha ); meshBuilder.TexCoord2f( 0,0,1 ); meshBuilder.Position3f( vecCorners[3].x,vecCorners[3].y,0 ); meshBuilder.AdvanceVertex(); meshBuilder.End(); pMesh->Draw(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CHudDamageIndicator::Paint() { // Iterate backwards, because we might remove them as we go int iSize = m_vecDamages.Count(); for (int i = iSize-1; i >= 0; i--) { // Scale size to the damage float clampedDamage = clamp( (float) m_vecDamages[i].iScale, 0.f, m_iMaximumDamage ); int iWidth = RemapVal(clampedDamage, 0, m_iMaximumDamage, m_flMinimumWidth, m_flMaximumWidth) * 0.5; int iHeight = RemapVal(clampedDamage, 0, m_iMaximumDamage, m_flMinimumHeight, m_flMaximumHeight) * 0.5; // Find the place to draw it float xpos, ypos; float flRotation; float flTimeSinceStart = ( gpGlobals->curtime - m_vecDamages[i].flStartTime ); float flRadius = RemapVal( MIN( flTimeSinceStart, m_flTravelTime ), 0, m_flTravelTime, m_flStartRadius, m_flEndRadius ); GetDamagePosition( m_vecDamages[i].vecDelta, flRadius, &xpos, &ypos, &flRotation ); // Calculate life left float flLifeLeft = ( m_vecDamages[i].flLifeTime - gpGlobals->curtime ); if ( flLifeLeft > 0 ) { float flPercent = flTimeSinceStart / (m_vecDamages[i].flLifeTime - m_vecDamages[i].flStartTime); float alpha; if ( flPercent <= m_flFadeOutPercentage ) { alpha = 1.0; } else { alpha = 1.0 - RemapVal( flPercent, m_flFadeOutPercentage, 1.0, 0.0, 1.0 ); } DrawDamageIndicator( xpos-iWidth, ypos-iHeight, xpos+iWidth, ypos+iHeight, alpha, flRotation ); } else { m_vecDamages.Remove(i); } } } //----------------------------------------------------------------------------- // Purpose: Message handler for Damage message //----------------------------------------------------------------------------- void CHudDamageIndicator::MsgFunc_Damage( bf_read &msg ) { damage_t damage; damage.iScale = msg.ReadShort(); msg.ReadLong(); // Read & ignored if ( !msg.ReadOneBit() ) return; if ( damage.iScale > m_iMaximumDamage ) { damage.iScale = m_iMaximumDamage; } Vector vecOrigin; msg.ReadBitVec3Coord( vecOrigin ); damage.flStartTime = gpGlobals->curtime; damage.flLifeTime = gpGlobals->curtime + RemapVal(damage.iScale, 0, m_iMaximumDamage, m_flMinimumTime, m_flMaximumTime); if ( vecOrigin == vec3_origin ) { vecOrigin = MainViewOrigin(); } damage.vecDelta = (vecOrigin - MainViewOrigin()); VectorNormalize( damage.vecDelta ); // Add some noise damage.vecDelta[0] += random->RandomFloat( -m_flNoise, m_flNoise ); damage.vecDelta[1] += random->RandomFloat( -m_flNoise, m_flNoise ); damage.vecDelta[2] += random->RandomFloat( -m_flNoise, m_flNoise ); VectorNormalize( damage.vecDelta ); m_vecDamages.AddToTail( damage ); } //----------------------------------------------------------------------------- // Purpose: hud scheme settings //----------------------------------------------------------------------------- void CHudDamageIndicator::ApplySchemeSettings(vgui::IScheme *pScheme) { BaseClass::ApplySchemeSettings(pScheme); SetPaintBackgroundEnabled(false); // set our size int screenWide, screenTall; int x, y; GetPos(x, y); GetHudSize(screenWide, screenTall); SetBounds(0, y, screenWide, screenTall - y); }