/*** * * Copyright (c) 1996-2001, Valve LLC. All rights reserved. * * This product contains software technology licensed from Id * Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. * All Rights Reserved. * * Use, distribution, and modification of this source code and/or resulting * object code is restricted to non-commercial enhancements to products from * Valve LLC. All other use, distribution, or modification is prohibited * without written permission from Valve LLC. * ****/ // // Health.cpp // // implementation of CHudHealth class // #include #include #include #include "hud.h" #include "cl_util.h" #include "parsemsg.h" #include DECLARE_MESSAGE(m_Health, Health ) DECLARE_MESSAGE(m_Health, Damage ) #define PAIN_NAME "sprites/%d_pain.spr" #define DAMAGE_NAME "sprites/%d_dmg.spr" int giDmgHeight, giDmgWidth; int giDmgFlags[NUM_DMG_TYPES] = { DMG_POISON, DMG_ACID, DMG_FREEZE|DMG_SLOWFREEZE, DMG_DROWN, DMG_BURN|DMG_SLOWBURN, DMG_NERVEGAS, DMG_RADIATION, DMG_SHOCK, DMG_TRANQ, DMG_CONCUSS, }; int CHudHealth::Init(void) { HOOK_MESSAGE(Health); HOOK_MESSAGE(Damage); m_iFlags = HUD_ACTIVE; m_iHealth = m_iOldHealth = 100; m_bitsDamage = 0; m_fAttackFront = m_fAttackRear = m_fAttackRight = m_fAttackLeft = 0; giDmgHeight = 0; giDmgWidth = 0; memset(m_dmg, 0, sizeof(DAMAGE_IMAGE) * NUM_DMG_TYPES); gHUD.AddHudElem(this); return 1; } void CHudHealth::Reset( void ) { // make sure the pain compass is cleared when the player respawns m_fAttackFront = m_fAttackRear = m_fAttackRight = m_fAttackLeft = 0; // force all the flashing damage icons to expire m_bitsDamage = 0; for ( int i = 0; i < NUM_DMG_TYPES; i++ ) { m_dmg[i].fExpire = 0; } } int CHudHealth::VidInit(void) { m_hDamageSprite = 0; m_HUD_dmg_bio = gHUD.GetSpriteIndex( "dmg_bio" ) + 1; m_HUD_healthbar = gHUD.GetSpriteIndex("healthbar"); m_HUD_healthbg = gHUD.GetSpriteIndex("healthbg"); giDmgHeight = gHUD.GetSpriteRect(m_HUD_dmg_bio).right - gHUD.GetSpriteRect(m_HUD_dmg_bio).left; giDmgWidth = gHUD.GetSpriteRect(m_HUD_dmg_bio).bottom - gHUD.GetSpriteRect(m_HUD_dmg_bio).top; return 1; } int CHudHealth:: MsgFunc_Health(const char *pszName, int iSize, void *pbuf ) { BEGIN_READ( pbuf, iSize ); int x = READ_BYTE(); m_iHealth = x; return 1; } int CHudHealth:: MsgFunc_Damage(const char *pszName, int iSize, void *pbuf ) { BEGIN_READ( pbuf, iSize ); int armor = READ_BYTE(); // armor int damageTaken = READ_BYTE(); // health long bitsDamage = READ_LONG(); // damage bits vec3_t vecFrom; for ( int i = 0 ; i < 3 ; i++) vecFrom[i] = READ_COORD(); UpdateTiles(gHUD.m_flTime, bitsDamage); // Actually took damage? if ( damageTaken > 0 || armor > 0 ) { CalcDamageDirection(vecFrom); } return 1; } // Returns back a color from the // Green <-> Yellow <-> Red ramp void CHudHealth::GetPainColor( int &r, int &g, int &b ) { int iHealth = m_iHealth; if (iHealth > 25) iHealth -= 25; else if ( iHealth < 0 ) iHealth = 0; #if 0 g = iHealth * 255 / 100; r = 255 - g; b = 0; #else if (m_iHealth > 25) { UnpackRGB(r,g,b, RGB_WHITE); } else { r = 250; g = 0; b = 0; } #endif } int CHudHealth::Draw(float flTime) { if(gHUD.m_iHideHUDDisplay & (HIDEHUD_ALL|HIDEHUD_HEALTH)) return 1; int x,y,r,g,b,a; wrect_t *prc; // Draw background a = 255; UnpackRGB(r,g,b,RGB_WHITE); ScaleColors(r,g,b,a); prc = &gHUD.GetSpriteRect(m_HUD_healthbg); x = (ScreenWidth / 2) - (prc->right - prc->left) / 2; y = ScreenHeight - (prc->bottom - prc->top); SPR_Set(gHUD.GetSprite(m_HUD_healthbg),r,g,b); SPR_DrawHoles(0,x,y,prc); if(gHUD.m_PlayerState.ActiveState() == PS_BLEEDING && gHUD.m_Health.m_iHealth > 0 && gHUD.m_Health.m_iHealth < 100) UnpackRGB(r,g,b,RGB_REDISH); else UnpackRGB(r,g,b,RGB_GREENISH); ScaleColors(r,g,b,a); // Center of health bar x += (prc->right - prc->left) / 2; y += (prc->bottom - prc->top) / 2; prc = &gHUD.GetSpriteRect(m_HUD_healthbar); // Get new coordinates x -= (prc->right - prc->left) / 2; y -= (prc->bottom - prc->top) / 2; int iHealthWidth = (prc->right - prc->left) * m_iHealth / 100; // Draw old health if(m_iOldHealth > m_iHealth) { int ohr,ohg,ohb,oha; int iScissorX,iScissorWidth; oha = 225; UnpackRGB(ohr,ohg,ohb,RGB_YELLOWISH); ScaleColors(ohr,ohg,ohb,oha); iScissorX = x + iHealthWidth; iScissorWidth = ((prc->right-prc->left) * m_iOldHealth / 100) - iHealthWidth; SPR_EnableScissor(iScissorX,0,iScissorWidth,ScreenHeight); SPR_Set(gHUD.GetSprite(m_HUD_healthbar),ohr,ohg,ohb); SPR_DrawAdditive(0,x,y,prc); SPR_DisableScissor(); // Reduce old health value m_iOldHealth -= gHUD.m_flTimeDelta * 1.5; } else m_iOldHealth = m_iHealth; // Draw current health SPR_EnableScissor(x,0,iHealthWidth,ScreenHeight); SPR_Set(gHUD.GetSprite(m_HUD_healthbar),r,g,b); SPR_DrawAdditive(0,x,y,prc); SPR_DisableScissor(); DrawDamage(flTime); // return DrawPain(flTime); return 1; } void CHudHealth::CalcDamageDirection(vec3_t vecFrom) { vec3_t forward, right, up; float side, front; vec3_t vecOrigin, vecAngles; if (!vecFrom[0] && !vecFrom[1] && !vecFrom[2]) { m_fAttackFront = m_fAttackRear = m_fAttackRight = m_fAttackLeft = 0; return; } memcpy(vecOrigin, gHUD.m_vecOrigin, sizeof(vec3_t)); memcpy(vecAngles, gHUD.m_vecAngles, sizeof(vec3_t)); VectorSubtract (vecFrom, vecOrigin, vecFrom); float flDistToTarget = vecFrom.Length(); vecFrom = vecFrom.Normalize(); AngleVectors (vecAngles, forward, right, up); front = DotProduct (vecFrom, right); side = DotProduct (vecFrom, forward); if (flDistToTarget <= 50) { m_fAttackFront = m_fAttackRear = m_fAttackRight = m_fAttackLeft = 1; } else { if (side > 0) { if (side > 0.3) m_fAttackFront = Q_max(m_fAttackFront, side); } else { float f = fabs(side); if (f > 0.3) m_fAttackRear = Q_max(m_fAttackRear, f); } if (front > 0) { if (front > 0.3) m_fAttackRight = Q_max(m_fAttackRight, front); } else { float f = fabs(front); if (f > 0.3) m_fAttackLeft = Q_max(m_fAttackLeft, f); } } } int CHudHealth::DrawPain(float flTime) { if (!(m_fAttackFront || m_fAttackRear || m_fAttackLeft || m_fAttackRight)) return 1; int r, g, b; int x, y, a, shade; // TODO: get the shift value of the health a = 255; // max brightness until then float fFade = gHUD.m_flTimeDelta * 2; // SPR_Draw top if (m_fAttackFront > 0.4) { GetPainColor(r,g,b); shade = a * Q_max( m_fAttackFront, 0.5 ); ScaleColors(r, g, b, shade); SPR_Set(m_hDamageSprite, r, g, b ); x = ScreenWidth/2 - SPR_Width(m_hDamageSprite, 0)/2; y = ScreenHeight/2 - SPR_Height(m_hDamageSprite,0) * 3; SPR_DrawAdditive(0, x, y, NULL); m_fAttackFront = Q_max( 0, m_fAttackFront - fFade ); } else m_fAttackFront = 0; if (m_fAttackRight > 0.4) { GetPainColor(r,g,b); shade = a * Q_max( m_fAttackRight, 0.5 ); ScaleColors(r, g, b, shade); SPR_Set(m_hDamageSprite, r, g, b ); x = ScreenWidth/2 + SPR_Width(m_hDamageSprite, 1) * 2; y = ScreenHeight/2 - SPR_Height(m_hDamageSprite,1)/2; SPR_DrawAdditive(1, x, y, NULL); m_fAttackRight = Q_max( 0, m_fAttackRight - fFade ); } else m_fAttackRight = 0; if (m_fAttackRear > 0.4) { GetPainColor(r,g,b); shade = a * Q_max( m_fAttackRear, 0.5 ); ScaleColors(r, g, b, shade); SPR_Set(m_hDamageSprite, r, g, b ); x = ScreenWidth/2 - SPR_Width(m_hDamageSprite, 2)/2; y = ScreenHeight/2 + SPR_Height(m_hDamageSprite,2) * 2; SPR_DrawAdditive(2, x, y, NULL); m_fAttackRear = Q_max( 0, m_fAttackRear - fFade ); } else m_fAttackRear = 0; if (m_fAttackLeft > 0.4) { GetPainColor(r,g,b); shade = a * Q_max( m_fAttackLeft, 0.5 ); ScaleColors(r, g, b, shade); SPR_Set(m_hDamageSprite, r, g, b ); x = ScreenWidth/2 - SPR_Width(m_hDamageSprite, 3) * 3; y = ScreenHeight/2 - SPR_Height(m_hDamageSprite,3)/2; SPR_DrawAdditive(3, x, y, NULL); m_fAttackLeft = Q_max( 0, m_fAttackLeft - fFade ); } else m_fAttackLeft = 0; return 1; } int CHudHealth::DrawDamage(float flTime) { int r, g, b, a; DAMAGE_IMAGE *pdmg; if (!m_bitsDamage) return 1; UnpackRGB(r,g,b, RGB_WHITE); a = (int)( fabs(sin(flTime*2)) * 256.0); ScaleColors(r, g, b, a); // Draw all the items int i; for ( i = 0; i < NUM_DMG_TYPES; i++) { if (m_bitsDamage & giDmgFlags[i]) { pdmg = &m_dmg[i]; SPR_Set(gHUD.GetSprite(m_HUD_dmg_bio + i), r, g, b ); SPR_DrawAdditive(0, pdmg->x, pdmg->y, &gHUD.GetSpriteRect(m_HUD_dmg_bio + i)); } } // check for bits that should be expired for ( i = 0; i < NUM_DMG_TYPES; i++ ) { DAMAGE_IMAGE *pdmg = &m_dmg[i]; if ( m_bitsDamage & giDmgFlags[i] ) { pdmg->fExpire = Q_min( flTime + DMG_IMAGE_LIFE, pdmg->fExpire ); if ( pdmg->fExpire <= flTime // when the time has expired && a < 40 ) // and the flash is at the low point of the cycle { pdmg->fExpire = 0; int y = pdmg->y; pdmg->x = pdmg->y = 0; // move everyone above down for (int j = 0; j < NUM_DMG_TYPES; j++) { pdmg = &m_dmg[j]; if ((pdmg->y) && (pdmg->y < y)) pdmg->y += giDmgHeight; } m_bitsDamage &= ~giDmgFlags[i]; // clear the bits } } } return 1; } void CHudHealth::UpdateTiles(float flTime, long bitsDamage) { DAMAGE_IMAGE *pdmg; // Which types are new? long bitsOn = ~m_bitsDamage & bitsDamage; for (int i = 0; i < NUM_DMG_TYPES; i++) { pdmg = &m_dmg[i]; // Is this one already on? if (m_bitsDamage & giDmgFlags[i]) { pdmg->fExpire = flTime + DMG_IMAGE_LIFE; // extend the duration if (!pdmg->fBaseline) pdmg->fBaseline = flTime; } // Are we just turning it on? if (bitsOn & giDmgFlags[i]) { // put this one at the bottom pdmg->x = giDmgWidth/8 + ScreenWidth/4; pdmg->y = ScreenHeight - giDmgHeight * 2; pdmg->fExpire=flTime + DMG_IMAGE_LIFE; // move everyone else up for (int j = 0; j < NUM_DMG_TYPES; j++) { if (j == i) continue; pdmg = &m_dmg[j]; if (pdmg->y) pdmg->y -= giDmgHeight; } pdmg = &m_dmg[i]; } } // damage bits are only turned on here; they are turned off when the draw time has expired (in DrawDamage()) m_bitsDamage |= bitsDamage; }