From 37675fbd82ecd04eb9f43de5acb3b480d5c2f3d0 Mon Sep 17 00:00:00 2001 From: terencehill Date: Mon, 29 Jan 2024 12:26:40 +0100 Subject: [PATCH] HUD: implement a 2d directional hit indicator around the screen center when a player hits you * only 1 indicator at a time * starts fading out as soon as it's shown * the higher the damage, the bigger the indicator size * damage is accumulated for a second (and indicator gets bigger) * not shown if the attacker is not visible * not shown when chase_active is enabled --- qcsrc/client/view.qc | 29 ++++++++++++++++--- qcsrc/client/view.qh | 3 ++ qcsrc/common/effects/qc/damageeffects.qc | 14 ++++----- .../mutator/waypoints/waypointsprites.qc | 6 ++-- .../mutator/waypoints/waypointsprites.qh | 2 +- qcsrc/common/turrets/cl_turrets.qc | 2 +- xonotic-client.cfg | 2 ++ 7 files changed, 41 insertions(+), 17 deletions(-) diff --git a/qcsrc/client/view.qc b/qcsrc/client/view.qc index a1e2c6f91..de1d10aab 100644 --- a/qcsrc/client/view.qc +++ b/qcsrc/client/view.qc @@ -753,16 +753,33 @@ void View_EventChase(entity this) } } +void HitIndicatorUpdate(float dmg, int attacker) +{ + if (time < HitIndicator_time + 1 && HitIndicator_attacker == attacker) + HitIndicator_damage += dmg; + else + HitIndicator_damage = dmg; + HitIndicator_attacker = attacker; + HitIndicator_time = time; +} + void HitIndicatorShow() { if (!autocvar_cl_hit_indicator || autocvar_cl_hit_indicator_size <= 0 - || time > HitIndicator_time + 1 - || !HitIndicator_attacker || !CSQCModel_server2csqc(HitIndicator_attacker - 1)) + || autocvar_chase_active || time > HitIndicator_time + autocvar_cl_hit_indicator_fade_time) { return; } entity attacker = CSQCModel_server2csqc(HitIndicator_attacker - 1); + if (!HitIndicator_attacker || !CSQCModel_server2csqc(HitIndicator_attacker - 1)) + return; + + // hide indicator if attacker is not visible (check ported from shownames.qc) + traceline(view_origin, attacker.origin, MOVE_NOMONSTERS, attacker); + if (trace_fraction < 1 && (trace_networkentity != attacker.sv_entnum && trace_ent.entnum != attacker.sv_entnum)) + return; + vector org = project_3d_to_2d(attacker.origin); vector scr_center = '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight; float radius = autocvar_cl_hit_indicator_radius * min(vid_conwidth, vid_conheight); @@ -773,8 +790,12 @@ void HitIndicatorShow() ang += M_PI; ofs = -ofs; } - org = scr_centersss + ofs; - drawspritearrow(org, ang, '1 0 0', 0.8, autocvar_cl_hit_indicator_size); + org = scr_center + ofs; + float alpha = autocvar_cl_hit_indicator_fade_alpha; + alpha *= 1 - (time - HitIndicator_time) / autocvar_cl_hit_indicator_fade_time; + float size = autocvar_cl_hit_indicator_size; + size = map_bound_ranges(HitIndicator_damage, 30, 90, size, size * 2); + drawspritearrow(org, ang, '1 0 0', alpha, size, true); } vector damage_blurpostprocess, content_blurpostprocess; diff --git a/qcsrc/client/view.qh b/qcsrc/client/view.qh index 76bedbdab..326706cef 100644 --- a/qcsrc/client/view.qh +++ b/qcsrc/client/view.qh @@ -95,9 +95,12 @@ bool autocvar_r_drawviewmodel; vector autocvar_cl_gunoffset; bool autocvar_cl_hit_indicator = 1; +float autocvar_cl_hit_indicator_fade_alpha = 0.8; +float autocvar_cl_hit_indicator_fade_time = 1; float autocvar_cl_hit_indicator_radius = 0.15; float autocvar_cl_hit_indicator_size = 1.1; int HitIndicator_attacker; +int HitIndicator_damage; int HitIndicator_time; void calc_followmodel_ofs(entity view); diff --git a/qcsrc/common/effects/qc/damageeffects.qc b/qcsrc/common/effects/qc/damageeffects.qc index d77af71f3..38ada5fec 100644 --- a/qcsrc/common/effects/qc/damageeffects.qc +++ b/qcsrc/common/effects/qc/damageeffects.qc @@ -193,9 +193,10 @@ void DamageEffect(entity this, vector hitorg, float thedamage, int type, int spe NET_HANDLE(ENT_CLIENT_DAMAGEINFO, bool isNew) { float thedamage, rad, edge, thisdmg; - bool hitplayer = false, hitme = false; + bool hitplayer = false; int species, forcemul; vector force, thisforce; + float hitme_damage = 0; w_deathtype = ReadShort(); w_issilent = (w_deathtype & 0x8000); @@ -267,8 +268,8 @@ NET_HANDLE(ENT_CLIENT_DAMAGEINFO, bool isNew) DamageEffect(it, w_org, thisdmg, w_deathtype, species); - if(it == csqcplayer) - hitme = true; // this impact damaged me + if(it == csqcplayer && attacker - 1 != player_localnum) + hitme_damage = thisdmg; // this impact damaged me else if(it != csqcplayer && (it.isplayermodel & ISPLAYER_MODEL)) hitplayer = true; // this impact damaged another player }); @@ -408,11 +409,8 @@ NET_HANDLE(ENT_CLIENT_DAMAGEINFO, bool isNew) if(!DEATH_ISSPECIAL(w_deathtype)) { - if(hitme) - { - HitIndicator_attacker = attacker; - HitIndicator_time = time; - } + if(hitme_damage > 0) + HitIndicatorUpdate(hitme_damage, attacker); // TODO spawn particle effects and sounds based on w_deathtype if(!hitplayer || rad) // don't show ground impacts for hitscan weapons if a player was hit diff --git a/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qc b/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qc index f19fc059e..c371778be 100644 --- a/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qc +++ b/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qc @@ -337,10 +337,10 @@ void drawhealthbar(vector org, float rot, float h, vector sz, vector hotspot, fl } // returns location of sprite text -vector drawspritearrow(vector o, float ang, vector rgb, float a, float t) +vector drawspritearrow(vector o, float ang, vector rgb, float a, float t, bool no_border) { float size = 9.0 * t; - float border = 1.5 * t; + float border = no_border ? 0 : 1.5 * t; float margin = 4.0 * t; float borderDiag = border * M_SQRT2; @@ -659,7 +659,7 @@ void Draw_WaypointSprite(entity this) SetResourceExplicit(this, RES_HEALTH, -1); } - o = drawspritearrow(o, ang, rgb, a, SPRITE_ARROW_SCALE * t); + o = drawspritearrow(o, ang, rgb, a, SPRITE_ARROW_SCALE * t, false); string pic = ""; bool is_text = true; diff --git a/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qh b/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qh index 2274da336..bc0241790 100644 --- a/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qh +++ b/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qh @@ -102,7 +102,7 @@ void drawquad(vector o, vector ri, vector up, string pic, vector rgb, float a, f void drawhealthbar(vector org, float rot, float h, vector sz, vector hotspot, float width, float height, float margin, float border, float align, vector rgb, float a, vector hrgb, float ha, float f); // returns location of sprite text -vector drawspritearrow(vector o, float ang, vector rgb, float a, float t); +vector drawspritearrow(vector o, float ang, vector rgb, float a, float t, bool no_border); // returns location of sprite healthbar vector drawsprite_TextOrIcon(bool is_text, vector o, float ang, float minwidth, vector rgb, float a, vector sz, string str); diff --git a/qcsrc/common/turrets/cl_turrets.qc b/qcsrc/common/turrets/cl_turrets.qc index 219b23318..e480dd49c 100644 --- a/qcsrc/common/turrets/cl_turrets.qc +++ b/qcsrc/common/turrets/cl_turrets.qc @@ -181,7 +181,7 @@ void turret_draw2d(entity this) if(draw_healthbar || draw_text) // make sure it's actually being drawn { - o = drawspritearrow(o, M_PI, rgb, a, SPRITE_ARROW_SCALE * t); + o = drawspritearrow(o, M_PI, rgb, a, SPRITE_ARROW_SCALE * t, false); } if(draw_text) { diff --git a/xonotic-client.cfg b/xonotic-client.cfg index 16f752b1f..1ecd52e50 100644 --- a/xonotic-client.cfg +++ b/xonotic-client.cfg @@ -202,6 +202,8 @@ seta cl_voice_directional 1 "0 = all voices are non-directional, 1 = all voices seta cl_voice_directional_taunt_attenuation 0.5 "this defines the distance from which taunts can be heard" seta cl_hit_indicator 1 "show a 2d directional indicator around the screen center when a player hits you" +seta cl_hit_indicator_fade_time 1 "how long hit indicator takes to fade away" +seta cl_hit_indicator_fade_alpha 1 "initial hit indicator alpha" seta cl_hit_indicator_radius 0.15 "show the directional indicator at this percentage of the screen from the center" seta cl_hit_indicator_size 1.1 "directional indicator size" -- 2.39.2