hlsdk-xash3d/cl_dll/health.cpp

472 lines
10 KiB
C++
Raw Normal View History

2016-06-04 15:24:23 +02:00
/***
*
* 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
//
2019-10-13 15:37:32 +02:00
#include <stdio.h>
#include <stdlib.h>
#include <cmath>
2016-06-04 15:24:23 +02:00
#include "hud.h"
#include "cl_util.h"
#include "parsemsg.h"
#include <string.h>
#include "mobility_int.h"
2016-07-03 15:39:55 +02:00
DECLARE_MESSAGE( m_Health, Health )
DECLARE_MESSAGE( m_Health, Damage )
2016-06-04 15:24:23 +02:00
#define PAIN_NAME "sprites/%d_pain.spr"
#define DAMAGE_NAME "sprites/%d_dmg.spr"
int giDmgHeight, giDmgWidth;
2016-07-03 15:39:55 +02:00
int giDmgFlags[NUM_DMG_TYPES] =
2016-06-04 15:24:23 +02:00
{
DMG_POISON,
DMG_ACID,
DMG_FREEZE|DMG_SLOWFREEZE,
DMG_DROWN,
DMG_BURN|DMG_SLOWBURN,
DMG_NERVEGAS,
DMG_RADIATION,
DMG_SHOCK,
DMG_CALTROP,
DMG_TRANQ,
DMG_CONCUSS,
DMG_HALLUC
};
2016-07-03 15:39:55 +02:00
int CHudHealth::Init( void )
2016-06-04 15:24:23 +02:00
{
2016-07-03 15:39:55 +02:00
HOOK_MESSAGE( Health );
HOOK_MESSAGE( Damage );
2016-06-04 15:24:23 +02:00
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;
2016-07-03 15:39:55 +02:00
memset( m_dmg, 0, sizeof(DAMAGE_IMAGE) * NUM_DMG_TYPES );
2016-06-04 15:24:23 +02:00
2016-07-03 15:39:55 +02:00
gHUD.AddHudElem( this );
2016-06-04 15:24:23 +02:00
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;
2016-07-03 15:39:55 +02:00
for( int i = 0; i < NUM_DMG_TYPES; i++ )
2016-06-04 15:24:23 +02:00
{
m_dmg[i].fExpire = 0;
}
}
2016-07-03 15:39:55 +02:00
int CHudHealth::VidInit( void )
2016-06-04 15:24:23 +02:00
{
m_hSprite = 0;
m_HUD_dmg_bio = gHUD.GetSpriteIndex( "dmg_bio" ) + 1;
m_HUD_cross = gHUD.GetSpriteIndex( "cross" );
2016-07-03 15:39:55 +02:00
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;
2016-06-04 15:24:23 +02:00
return 1;
}
2016-07-03 15:39:55 +02:00
int CHudHealth::MsgFunc_Health( const char *pszName, int iSize, void *pbuf )
2016-06-04 15:24:23 +02:00
{
// TODO: update local health data
BEGIN_READ( pbuf, iSize );
int x = READ_BYTE();
m_iFlags |= HUD_ACTIVE;
// Only update the fade if we've changed health
2016-07-03 15:39:55 +02:00
if( x != m_iHealth )
2016-06-04 15:24:23 +02:00
{
m_fFade = FADE_TIME;
m_iHealth = x;
}
return 1;
}
2016-07-03 15:39:55 +02:00
int CHudHealth::MsgFunc_Damage( const char *pszName, int iSize, void *pbuf )
2016-06-04 15:24:23 +02:00
{
BEGIN_READ( pbuf, iSize );
int armor = READ_BYTE(); // armor
int damageTaken = READ_BYTE(); // health
long bitsDamage = READ_LONG(); // damage bits
vec3_t vecFrom;
2016-08-02 23:05:15 +02:00
for( int i = 0; i < 3; i++ )
2016-06-04 15:24:23 +02:00
vecFrom[i] = READ_COORD();
2016-07-03 15:39:55 +02:00
UpdateTiles( gHUD.m_flTime, bitsDamage );
2016-06-04 15:24:23 +02:00
// Actually took damage?
2016-07-03 15:39:55 +02:00
if( damageTaken > 0 || armor > 0 )
2016-06-04 15:24:23 +02:00
{
2016-07-03 15:39:55 +02:00
CalcDamageDirection( vecFrom );
2016-06-04 15:24:23 +02:00
if( gMobileEngfuncs && damageTaken > 0 )
{
2016-07-03 15:39:55 +02:00
float time = damageTaken * 4.0f;
2016-06-04 15:24:23 +02:00
2016-07-03 15:39:55 +02:00
if( time > 200.0f )
time = 200.0f;
gMobileEngfuncs->pfnVibrate( time, 0 );
2016-06-04 15:24:23 +02:00
}
}
return 1;
}
// Returns back a color from the
// Green <-> Yellow <-> Red ramp
void CHudHealth::GetPainColor( int &r, int &g, int &b )
{
#if 0
2016-06-04 15:24:23 +02:00
int iHealth = m_iHealth;
2016-07-03 15:39:55 +02:00
if( iHealth > 25 )
2016-06-04 15:24:23 +02:00
iHealth -= 25;
2016-07-03 15:39:55 +02:00
else if( iHealth < 0 )
2016-06-04 15:24:23 +02:00
iHealth = 0;
2016-06-04 15:24:23 +02:00
g = iHealth * 255 / 100;
r = 255 - g;
b = 0;
#else
2016-07-03 15:39:55 +02:00
if( m_iHealth > 25 )
2016-06-04 15:24:23 +02:00
{
2016-07-03 15:39:55 +02:00
UnpackRGB( r, g, b, RGB_YELLOWISH );
2016-06-04 15:24:23 +02:00
}
else
{
r = 250;
g = 0;
b = 0;
}
2016-07-03 15:39:55 +02:00
#endif
2016-06-04 15:24:23 +02:00
}
2016-07-03 15:39:55 +02:00
int CHudHealth::Draw( float flTime )
2016-06-04 15:24:23 +02:00
{
int r, g, b;
int a = 0, x, y;
int HealthWidth;
2016-07-03 15:39:55 +02:00
if( ( gHUD.m_iHideHUDDisplay & HIDEHUD_HEALTH ) || gEngfuncs.IsSpectateOnly() )
2016-06-04 15:24:23 +02:00
return 1;
2016-07-03 15:39:55 +02:00
if( !m_hSprite )
m_hSprite = LoadSprite( PAIN_NAME );
2016-06-04 15:24:23 +02:00
// Has health changed? Flash the health #
2016-07-03 15:39:55 +02:00
if( m_fFade )
2016-06-04 15:24:23 +02:00
{
2019-10-13 14:12:41 +02:00
m_fFade -= ( (float)gHUD.m_flTimeDelta * 20.0f );
2016-07-03 15:39:55 +02:00
if( m_fFade <= 0 )
2016-06-04 15:24:23 +02:00
{
a = MIN_ALPHA;
m_fFade = 0;
}
// Fade the health number back to dim
2016-07-03 15:39:55 +02:00
a = MIN_ALPHA + ( m_fFade / FADE_TIME ) * 128;
2016-06-04 15:24:23 +02:00
}
else
a = MIN_ALPHA;
// If health is getting low, make it bright red
2016-07-03 15:39:55 +02:00
if( m_iHealth <= 15 )
2016-06-04 15:24:23 +02:00
a = 255;
2016-07-03 15:39:55 +02:00
2016-06-04 15:24:23 +02:00
GetPainColor( r, g, b );
2016-07-03 15:39:55 +02:00
ScaleColors( r, g, b, a );
2016-06-04 15:24:23 +02:00
// Only draw health if we have the suit.
2016-07-03 15:39:55 +02:00
if( gHUD.m_iWeaponBits & ( 1 << ( WEAPON_SUIT ) ) )
2016-06-04 15:24:23 +02:00
{
2016-07-03 15:39:55 +02:00
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;
2016-06-04 15:24:23 +02:00
y = ScreenHeight - gHUD.m_iFontHeight - gHUD.m_iFontHeight / 2;
2016-07-03 15:39:55 +02:00
x = CrossWidth / 2;
2016-06-04 15:24:23 +02:00
2016-07-03 15:39:55 +02:00
SPR_Set( gHUD.GetSprite( m_HUD_cross ), r, g, b );
SPR_DrawAdditive( 0, x, y, &gHUD.GetSpriteRect( m_HUD_cross ) );
2016-06-04 15:24:23 +02:00
x = CrossWidth + HealthWidth / 2;
2016-07-03 15:39:55 +02:00
x = gHUD.DrawHudNumber( x, y, DHN_3DIGITS | DHN_DRAWZERO, m_iHealth, r, g, b );
2016-06-04 15:24:23 +02:00
2016-07-03 15:39:55 +02:00
x += HealthWidth / 2;
2016-06-04 15:24:23 +02:00
int iHeight = gHUD.m_iFontHeight;
2016-07-03 15:39:55 +02:00
int iWidth = HealthWidth / 10;
UnpackRGB( r, g, b, RGB_YELLOWISH );
FillRGBA( x, y, iWidth, iHeight, r, g, b, a );
2016-06-04 15:24:23 +02:00
}
2016-07-03 15:39:55 +02:00
DrawDamage( flTime );
return DrawPain( flTime );
2016-06-04 15:24:23 +02:00
}
2016-07-03 15:39:55 +02:00
void CHudHealth::CalcDamageDirection( vec3_t vecFrom )
2016-06-04 15:24:23 +02:00
{
2016-08-02 23:05:15 +02:00
vec3_t forward, right, up;
float side, front;
2016-06-04 15:24:23 +02:00
vec3_t vecOrigin, vecAngles;
2016-07-03 15:39:55 +02:00
if( !vecFrom[0] && !vecFrom[1] && !vecFrom[2] )
2016-06-04 15:24:23 +02:00
{
m_fAttackFront = m_fAttackRear = m_fAttackRight = m_fAttackLeft = 0;
return;
}
2016-07-03 15:39:55 +02:00
memcpy( vecOrigin, gHUD.m_vecOrigin, sizeof(vec3_t) );
memcpy( vecAngles, gHUD.m_vecAngles, sizeof(vec3_t) );
2016-06-04 15:24:23 +02:00
2016-07-03 15:39:55 +02:00
VectorSubtract( vecFrom, vecOrigin, vecFrom );
2016-06-04 15:24:23 +02:00
float flDistToTarget = vecFrom.Length();
vecFrom = vecFrom.Normalize();
2016-07-03 15:39:55 +02:00
AngleVectors( vecAngles, forward, right, up );
2016-06-04 15:24:23 +02:00
2016-07-03 15:39:55 +02:00
front = DotProduct( vecFrom, right );
side = DotProduct( vecFrom, forward );
2016-06-04 15:24:23 +02:00
2016-07-03 15:39:55 +02:00
if( flDistToTarget <= 50 )
2016-06-04 15:24:23 +02:00
{
m_fAttackFront = m_fAttackRear = m_fAttackRight = m_fAttackLeft = 1;
}
else
{
2019-10-13 13:49:25 +02:00
if( side > 0.0f )
2016-06-04 15:24:23 +02:00
{
2019-10-13 13:49:25 +02:00
if( side > 0.3f )
2019-08-11 23:25:50 +02:00
m_fAttackFront = Q_max( m_fAttackFront, side );
2016-06-04 15:24:23 +02:00
}
else
{
2016-07-03 15:39:55 +02:00
float f = fabs( side );
2019-10-13 13:49:25 +02:00
if( f > 0.3f )
2019-08-11 23:25:50 +02:00
m_fAttackRear = Q_max( m_fAttackRear, f );
2016-06-04 15:24:23 +02:00
}
2019-10-13 13:49:25 +02:00
if( front > 0.0f )
2016-06-04 15:24:23 +02:00
{
2019-10-13 13:49:25 +02:00
if( front > 0.3f )
2019-08-11 23:25:50 +02:00
m_fAttackRight = Q_max( m_fAttackRight, front );
2016-06-04 15:24:23 +02:00
}
else
{
2016-07-03 15:39:55 +02:00
float f = fabs( front );
2019-10-13 13:49:25 +02:00
if( f > 0.3f )
2019-08-11 23:25:50 +02:00
m_fAttackLeft = Q_max( m_fAttackLeft, f );
2016-06-04 15:24:23 +02:00
}
}
}
2016-07-03 15:39:55 +02:00
int CHudHealth::DrawPain( float flTime )
2016-06-04 15:24:23 +02:00
{
2016-07-03 15:39:55 +02:00
if( !( m_fAttackFront || m_fAttackRear || m_fAttackLeft || m_fAttackRight) )
2016-06-04 15:24:23 +02:00
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;
2016-07-03 15:39:55 +02:00
2016-06-04 15:24:23 +02:00
// SPR_Draw top
2019-10-13 13:49:25 +02:00
if( m_fAttackFront > 0.4f )
2016-06-04 15:24:23 +02:00
{
2016-07-03 15:39:55 +02:00
GetPainColor( r, g, b );
2019-10-13 13:49:25 +02:00
shade = a * Q_max( m_fAttackFront, 0.5f );
2016-07-03 15:39:55 +02:00
ScaleColors( r, g, b, shade );
SPR_Set( m_hSprite, r, g, b );
2016-06-04 15:24:23 +02:00
2016-07-03 15:39:55 +02:00
x = ScreenWidth / 2 - SPR_Width( m_hSprite, 0 ) / 2;
y = ScreenHeight / 2 - SPR_Height( m_hSprite, 0 ) * 3;
SPR_DrawAdditive( 0, x, y, NULL );
2019-08-11 23:25:50 +02:00
m_fAttackFront = Q_max( 0, m_fAttackFront - fFade );
2016-06-04 15:24:23 +02:00
} else
m_fAttackFront = 0;
2019-10-13 13:49:25 +02:00
if( m_fAttackRight > 0.4f )
2016-06-04 15:24:23 +02:00
{
2016-07-03 15:39:55 +02:00
GetPainColor( r, g, b );
2019-10-13 13:49:25 +02:00
shade = a * Q_max( m_fAttackRight, 0.5f );
2016-07-03 15:39:55 +02:00
ScaleColors( r, g, b, shade );
SPR_Set( m_hSprite, r, g, b );
2016-06-04 15:24:23 +02:00
2016-07-03 15:39:55 +02:00
x = ScreenWidth / 2 + SPR_Width( m_hSprite, 1 ) * 2;
y = ScreenHeight / 2 - SPR_Height( m_hSprite,1 ) / 2;
SPR_DrawAdditive( 1, x, y, NULL );
2019-08-11 23:25:50 +02:00
m_fAttackRight = Q_max( 0, m_fAttackRight - fFade );
2016-07-03 15:39:55 +02:00
}
else
2016-06-04 15:24:23 +02:00
m_fAttackRight = 0;
2019-10-13 13:49:25 +02:00
if( m_fAttackRear > 0.4f )
2016-06-04 15:24:23 +02:00
{
2016-07-03 15:39:55 +02:00
GetPainColor( r, g, b );
2019-10-13 13:49:25 +02:00
shade = a * Q_max( m_fAttackRear, 0.5f );
2016-07-03 15:39:55 +02:00
ScaleColors( r, g, b, shade );
SPR_Set( m_hSprite, r, g, b );
2016-06-04 15:24:23 +02:00
2016-07-03 15:39:55 +02:00
x = ScreenWidth / 2 - SPR_Width( m_hSprite, 2 ) / 2;
y = ScreenHeight / 2 + SPR_Height( m_hSprite, 2 ) * 2;
SPR_DrawAdditive( 2, x, y, NULL );
2019-08-11 23:25:50 +02:00
m_fAttackRear = Q_max( 0, m_fAttackRear - fFade );
2016-07-03 15:39:55 +02:00
}
else
2016-06-04 15:24:23 +02:00
m_fAttackRear = 0;
2019-10-13 13:49:25 +02:00
if( m_fAttackLeft > 0.4f )
2016-06-04 15:24:23 +02:00
{
2016-07-03 15:39:55 +02:00
GetPainColor( r, g, b );
2019-10-13 13:49:25 +02:00
shade = a * Q_max( m_fAttackLeft, 0.5f );
2016-07-03 15:39:55 +02:00
ScaleColors( r, g, b, shade );
SPR_Set( m_hSprite, r, g, b );
2016-06-04 15:24:23 +02:00
2016-07-03 15:39:55 +02:00
x = ScreenWidth / 2 - SPR_Width( m_hSprite, 3 ) * 3;
y = ScreenHeight / 2 - SPR_Height( m_hSprite,3 ) / 2;
SPR_DrawAdditive( 3, x, y, NULL );
2016-06-04 15:24:23 +02:00
2019-08-11 23:25:50 +02:00
m_fAttackLeft = Q_max( 0, m_fAttackLeft - fFade );
2016-06-04 15:24:23 +02:00
} else
m_fAttackLeft = 0;
return 1;
}
2016-07-03 15:39:55 +02:00
int CHudHealth::DrawDamage( float flTime )
2016-06-04 15:24:23 +02:00
{
int i, r, g, b, a;
DAMAGE_IMAGE *pdmg;
2016-08-02 23:05:15 +02:00
if( !m_bitsDamage )
2016-06-04 15:24:23 +02:00
return 1;
2016-07-03 15:39:55 +02:00
UnpackRGB( r, g, b, RGB_YELLOWISH );
2019-10-13 13:49:25 +02:00
a = (int)( fabs( sin( flTime * 2.0f ) ) * 256.0f );
2016-06-04 15:24:23 +02:00
2016-07-03 15:39:55 +02:00
ScaleColors( r, g, b, a );
2016-06-04 15:24:23 +02:00
// check for bits that should be expired
2016-07-03 15:39:55 +02:00
for( i = 0; i < NUM_DMG_TYPES; i++ )
2016-06-04 15:24:23 +02:00
{
2016-07-03 15:39:55 +02:00
if( m_bitsDamage & giDmgFlags[i] )
2016-06-04 15:24:23 +02:00
{
2016-07-03 15:39:55 +02:00
pdmg = &m_dmg[i];
2016-08-02 23:05:15 +02:00
// Draw all the items
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 ) );
2019-08-11 23:25:50 +02:00
pdmg->fExpire = Q_min( flTime + DMG_IMAGE_LIFE, pdmg->fExpire );
2016-06-04 15:24:23 +02:00
2016-07-03 15:39:55 +02:00
if( pdmg->fExpire <= flTime // when the time has expired
2016-06-04 15:24:23 +02:00
&& 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
2016-07-03 15:39:55 +02:00
for( int j = 0; j < NUM_DMG_TYPES; j++ )
2016-06-04 15:24:23 +02:00
{
pdmg = &m_dmg[j];
2016-07-03 15:39:55 +02:00
if( ( pdmg->y ) && ( pdmg->y < y ) )
2016-06-04 15:24:23 +02:00
pdmg->y += giDmgHeight;
}
m_bitsDamage &= ~giDmgFlags[i]; // clear the bits
}
}
}
return 1;
}
2016-07-03 15:39:55 +02:00
void CHudHealth::UpdateTiles( float flTime, long bitsDamage )
2016-06-04 15:24:23 +02:00
{
DAMAGE_IMAGE *pdmg;
// Which types are new?
long bitsOn = ~m_bitsDamage & bitsDamage;
2016-07-03 15:39:55 +02:00
for( int i = 0; i < NUM_DMG_TYPES; i++ )
2016-06-04 15:24:23 +02:00
{
pdmg = &m_dmg[i];
// Is this one already on?
2016-07-03 15:39:55 +02:00
if( m_bitsDamage & giDmgFlags[i] )
2016-06-04 15:24:23 +02:00
{
pdmg->fExpire = flTime + DMG_IMAGE_LIFE; // extend the duration
2016-07-03 15:39:55 +02:00
if( !pdmg->fBaseline )
2016-06-04 15:24:23 +02:00
pdmg->fBaseline = flTime;
}
// Are we just turning it on?
2016-07-03 15:39:55 +02:00
if( bitsOn & giDmgFlags[i] )
2016-06-04 15:24:23 +02:00
{
// put this one at the bottom
2016-07-03 15:39:55 +02:00
pdmg->x = giDmgWidth / 8;
2016-06-04 15:24:23 +02:00
pdmg->y = ScreenHeight - giDmgHeight * 2;
pdmg->fExpire=flTime + DMG_IMAGE_LIFE;
2016-07-03 15:39:55 +02:00
2016-06-04 15:24:23 +02:00
// move everyone else up
2016-07-03 15:39:55 +02:00
for( int j = 0; j < NUM_DMG_TYPES; j++ )
2016-06-04 15:24:23 +02:00
{
2016-07-03 15:39:55 +02:00
if( j == i )
2016-06-04 15:24:23 +02:00
continue;
pdmg = &m_dmg[j];
2016-07-03 15:39:55 +02:00
if( pdmg->y )
2016-06-04 15:24:23 +02:00
pdmg->y -= giDmgHeight;
}
// pdmg = &m_dmg[i];
2016-07-03 15:39:55 +02:00
}
}
2016-06-04 15:24:23 +02:00
// damage bits are only turned on here; they are turned off when the draw time has expired (in DrawDamage())
m_bitsDamage |= bitsDamage;
}