449 lines
10 KiB
C++
449 lines
10 KiB
C++
/***
|
|
*
|
|
* Copyright (c) 1996-2002, 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 "extdll.h"
|
|
#include "hud_iface.h"
|
|
#include "hud.h"
|
|
|
|
DECLARE_MESSAGE( m_Health, Health )
|
|
DECLARE_MESSAGE( m_Health, Damage )
|
|
|
|
#define PAIN_NAME "hud_pain"
|
|
#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_NUCLEAR
|
|
};
|
|
|
|
int CHudHealth :: Init( void )
|
|
{
|
|
HOOK_MESSAGE( Health );
|
|
HOOK_MESSAGE( Damage );
|
|
m_iHealth = 100;
|
|
m_fFade = 0;
|
|
m_iFlags = 0;
|
|
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_hSprite = 0;
|
|
|
|
m_HUD_dmg_bio = gHUD.GetSpriteIndex( "dmg_bio" ) + 1;
|
|
m_HUD_cross = gHUD.GetSpriteIndex( "cross" );
|
|
|
|
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 )
|
|
{
|
|
// TODO: update local health data
|
|
BEGIN_READ( pszName, iSize, pbuf );
|
|
int x = READ_BYTE();
|
|
|
|
m_iFlags |= HUD_ACTIVE;
|
|
|
|
// only update the fade if we've changed health
|
|
if( x != m_iHealth )
|
|
{
|
|
m_fFade = FADE_TIME;
|
|
m_iHealth = x;
|
|
}
|
|
END_READ();
|
|
|
|
return 1;
|
|
}
|
|
|
|
int CHudHealth :: MsgFunc_Damage( const char *pszName, int iSize, void *pbuf )
|
|
{
|
|
BEGIN_READ( pszName, iSize, pbuf );
|
|
|
|
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);
|
|
END_READ();
|
|
|
|
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( m_iHealth > 25 )
|
|
{
|
|
UnpackRGB( r, g, b, gHUD.m_iHUDColor );
|
|
}
|
|
else
|
|
{
|
|
r = 250;
|
|
g = 0;
|
|
b = 0;
|
|
}
|
|
}
|
|
|
|
int CHudHealth :: Draw( float flTime )
|
|
{
|
|
int r, g, b;
|
|
int a = 0, x, y;
|
|
int HealthWidth;
|
|
|
|
if((gHUD.m_iHideHUDDisplay & HIDEHUD_HEALTH) || IsSpectateOnly())
|
|
return 1;
|
|
|
|
if( !m_hSprite )
|
|
m_hSprite = LOAD_SHADER( PAIN_NAME );
|
|
|
|
// has health changed? Flash the health #
|
|
if( m_fFade )
|
|
{
|
|
m_fFade -= (gHUD.m_flTimeDelta * 20);
|
|
if( m_fFade <= 0 )
|
|
{
|
|
a = MIN_ALPHA;
|
|
m_fFade = 0;
|
|
}
|
|
|
|
// fade the health number back to dim
|
|
a = MIN_ALPHA + (m_fFade/FADE_TIME) * 128;
|
|
|
|
}
|
|
else a = MIN_ALPHA;
|
|
|
|
// if health is getting low, make it bright red
|
|
if( m_iHealth <= 15 ) a = 255;
|
|
|
|
GetPainColor( r, g, b );
|
|
ScaleColors( r, g, b, a );
|
|
|
|
// only draw health if we have the suit.
|
|
if( gHUD.m_iHideHUDDisplay & ITEM_SUIT )
|
|
{
|
|
HealthWidth = gHUD.GetSpriteRect(gHUD.m_HUD_number_0).right - gHUD.GetSpriteRect(gHUD.m_HUD_number_0).left;
|
|
int CrossWidth = gHUD.GetSpriteRect(m_HUD_cross).right - gHUD.GetSpriteRect(m_HUD_cross).left;
|
|
|
|
y = SCREEN_HEIGHT - gHUD.m_iFontHeight - gHUD.m_iFontHeight / 2;
|
|
x = CrossWidth / 2;
|
|
|
|
SPR_Set( gHUD.GetSprite( m_HUD_cross ), r, g, b );
|
|
SPR_DrawAdditive( 0, x, y, &gHUD.GetSpriteRect( m_HUD_cross ));
|
|
|
|
x = CrossWidth + HealthWidth / 2;
|
|
|
|
x = gHUD.DrawHudNumber( x, y, DHN_3DIGITS | DHN_DRAWZERO, m_iHealth, r, g, b );
|
|
|
|
x += HealthWidth / 2;
|
|
|
|
int iHeight = gHUD.m_iFontHeight;
|
|
int iWidth = HealthWidth / 10;
|
|
|
|
UnpackRGB( r, g, b, gHUD.m_iHUDColor );
|
|
FillRGBA( x, y, iWidth, iHeight, r, g, b, a );
|
|
}
|
|
|
|
DrawDamage( flTime );
|
|
return DrawPain( flTime );
|
|
}
|
|
|
|
void CHudHealth :: CalcDamageDirection( Vector vecFrom )
|
|
{
|
|
Vector forward, right, up;
|
|
float side, front;
|
|
Vector vecOrigin, vecAngles;
|
|
|
|
if( vecFrom == Vector( 0, 0, 0 ))
|
|
{
|
|
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 ));
|
|
|
|
vecFrom -= vecOrigin;
|
|
|
|
float flDistToTarget = vecFrom.Length();
|
|
|
|
vecFrom = vecFrom.Normalize();
|
|
AngleVectors( vecAngles, forward, right, up );
|
|
|
|
front = vecFrom.Dot( right );
|
|
side = vecFrom.Dot( forward );
|
|
|
|
if( flDistToTarget <= 50 )
|
|
{
|
|
m_fAttackFront = m_fAttackRear = m_fAttackRight = m_fAttackLeft = 1;
|
|
}
|
|
else
|
|
{
|
|
if( side > 0 )
|
|
{
|
|
if( side > 0.3 )
|
|
m_fAttackFront = max( m_fAttackFront, side );
|
|
}
|
|
else
|
|
{
|
|
float f = fabs( side );
|
|
if( f > 0.3 )
|
|
m_fAttackRear = max( m_fAttackRear, f );
|
|
}
|
|
|
|
if( front > 0 )
|
|
{
|
|
if( front > 0.3 )
|
|
m_fAttackRight = max( m_fAttackRight, front );
|
|
}
|
|
else
|
|
{
|
|
float f = fabs( front );
|
|
if( f > 0.3 )
|
|
m_fAttackLeft = 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 * max( m_fAttackFront, 0.5 );
|
|
ScaleColors( r, g, b, shade );
|
|
SPR_Set( m_hSprite, r, g, b );
|
|
|
|
x = SCREEN_WIDTH / 2 - SPR_Width( m_hSprite, 0 ) / 2;
|
|
y = SCREEN_HEIGHT / 2 - SPR_Height( m_hSprite, 0 ) * 3;
|
|
SPR_DrawAdditive( 0, x, y, NULL );
|
|
m_fAttackFront = max( 0, m_fAttackFront - fFade );
|
|
}
|
|
else m_fAttackFront = 0;
|
|
|
|
if( m_fAttackRight > 0.4 )
|
|
{
|
|
GetPainColor( r, g, b );
|
|
shade = a * max( m_fAttackRight, 0.5 );
|
|
ScaleColors( r, g, b, shade );
|
|
SPR_Set( m_hSprite, r, g, b );
|
|
|
|
x = SCREEN_WIDTH / 2 + SPR_Width( m_hSprite, 1 ) * 2;
|
|
y = SCREEN_HEIGHT / 2 - SPR_Height( m_hSprite,1 ) / 2;
|
|
SPR_DrawAdditive( 1, x, y, NULL );
|
|
m_fAttackRight = max( 0, m_fAttackRight - fFade );
|
|
}
|
|
else m_fAttackRight = 0;
|
|
|
|
if( m_fAttackRear > 0.4 )
|
|
{
|
|
GetPainColor( r, g, b );
|
|
shade = a * max( m_fAttackRear, 0.5 );
|
|
ScaleColors(r, g, b, shade);
|
|
SPR_Set(m_hSprite, r, g, b );
|
|
|
|
x = SCREEN_WIDTH / 2 - SPR_Width( m_hSprite, 2 ) / 2;
|
|
y = SCREEN_HEIGHT / 2 + SPR_Height( m_hSprite, 2 ) * 2;
|
|
SPR_DrawAdditive( 2, x, y, NULL);
|
|
m_fAttackRear = max( 0, m_fAttackRear - fFade );
|
|
}
|
|
else m_fAttackRear = 0;
|
|
|
|
if( m_fAttackLeft > 0.4 )
|
|
{
|
|
GetPainColor( r, g, b );
|
|
shade = a * max( m_fAttackLeft, 0.5 );
|
|
ScaleColors( r, g, b, shade );
|
|
SPR_Set( m_hSprite, r, g, b );
|
|
|
|
x = SCREEN_WIDTH / 2 - SPR_Width( m_hSprite, 3 ) * 3;
|
|
y = SCREEN_HEIGHT / 2 - SPR_Height( m_hSprite,3 ) / 2;
|
|
SPR_DrawAdditive( 3, x, y, NULL );
|
|
|
|
m_fAttackLeft = 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, gHUD.m_iHUDColor );
|
|
|
|
a = (int)(fabs(sin( flTime * 2)) * 256.0 );
|
|
|
|
ScaleColors( r, g, b, a );
|
|
|
|
// draw all the items
|
|
for( int 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 = min( flTime + DMG_IMAGE_LIFE, pdmg->fExpire );
|
|
|
|
// when the time has expired and the flash is at the low point of the cycle
|
|
if( pdmg->fExpire <= flTime && a < 40 )
|
|
{
|
|
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;
|
|
pdmg->y = SCREEN_HEIGHT - 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;
|
|
}
|