]> git.rm.cloudns.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
HUD hit indicator: implement a 2d directional hit indicator around the screen center...
authorterencehill <piuntn@gmail.com>
Wed, 14 Feb 2024 00:23:49 +0000 (01:23 +0100)
committerterencehill <piuntn@gmail.com>
Wed, 14 Feb 2024 21:53:38 +0000 (22:53 +0100)
* for now only 1 indicator at a time
* starts to fade out as soon as it appears
* the higher the damage, the bigger the indicator size
* damage is accumulated
* hidden when the attacker is not visible
* disabled if chase_active is enabled
* shown while spectating a player too
* implemented as an extension of damagetext (requires sv_damagetext >= 2)

Extra: added cl_damagetext_received (disabled by default) that allows displaying damage text when you receive damage too

qcsrc/client/view.qc
qcsrc/client/view.qh
qcsrc/common/mutators/mutator/damagetext/cl_damagetext.qc
qcsrc/common/mutators/mutator/damagetext/cl_damagetext.qh
qcsrc/common/mutators/mutator/damagetext/sv_damagetext.qc
qcsrc/common/mutators/mutator/waypoints/waypointsprites.qc
qcsrc/common/mutators/mutator/waypoints/waypointsprites.qh
qcsrc/common/turrets/cl_turrets.qc
xonotic-client.cfg

index 689f4b4cecbdd80ad0c119f2c42fce5be85977bc..900114588ece060a366b252b9303d6f38982051a 100644 (file)
@@ -753,6 +753,51 @@ void View_EventChase(entity this)
        }
 }
 
+void HitIndicatorUpdate(float dmg, entity 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
+               || autocvar_chase_active || time > HitIndicator_time + autocvar_cl_hit_indicator_fade_time)
+       {
+               return;
+       }
+
+       entity attacker = HitIndicator_attacker;
+       if (!attacker)
+               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);
+       vector ofs = normalize(org - scr_center - org.z * eZ) * radius;
+       float ang = atan2(-ofs.x, -ofs.y);
+       if (org.z < 0)
+       {
+               ang += M_PI;
+               ofs = -ofs;
+       }
+       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;
 
 void UpdateDamage()
@@ -907,6 +952,8 @@ void HUD_Draw(entity this)
                }
        }
 
+       HitIndicatorShow();
+
        // crosshair goes VERY LAST
        UpdateDamage();
        HUD_Crosshair(this);
index cd33ebfb6a1201e41f9caa706209c1cc2fcb3d92..0a54db106afa637f39e58d2ff47e7e6fad029a2a 100644 (file)
@@ -94,6 +94,15 @@ int autocvar_cl_nade_timer;
 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.5;
+float autocvar_cl_hit_indicator_radius = 0.15;
+float autocvar_cl_hit_indicator_size = 1.1;
+entity HitIndicator_attacker;
+float HitIndicator_damage;
+int HitIndicator_time;
+
 void calc_followmodel_ofs(entity view);
 
 vector project_3d_to_2d(vector vec);
index 8383626fc718733f0cfc76f0e5daa69b1166df76..a4db479ecadbea508bbb1712eb02b4123f359dab 100644 (file)
@@ -219,6 +219,7 @@ float current_alpha(entity damage_text)
 NET_HANDLE(damagetext, bool isNew)
 {
        make_pure(this);
+       int attacker_server_entity_index = ReadByte();
        int server_entity_index = ReadByte();
        int deathtype = ReadInt24_t();
        int flags = ReadByte();
@@ -236,6 +237,24 @@ NET_HANDLE(damagetext, bool isNew)
 
        return = true;
        if (!isNew) return;
+
+       int attacker_client_entity_index = attacker_server_entity_index - 1;
+       entity attacker_entcs = entcs_receiver(attacker_client_entity_index);
+
+       int client_entity_index = server_entity_index - 1;
+       entity entcs = entcs_receiver(client_entity_index);
+
+       if (attacker_entcs && client_entity_index == current_player)
+       {
+               if (autocvar_cl_hit_indicator && autocvar_cl_hit_indicator_size > 0 && !autocvar_chase_active)
+               {
+                       float total = rint((health + armor) / DAMAGETEXT_PRECISION_MULTIPLIER);
+                       HitIndicatorUpdate(total, attacker_entcs);
+               }
+               if (!autocvar_cl_damagetext_received)
+                       return;
+       }
+
        if (autocvar_cl_damagetext == 0) return;
        if (friendlyfire)
        {
@@ -247,8 +266,6 @@ NET_HANDLE(damagetext, bool isNew)
                        return;
        }
 
-       int client_entity_index = server_entity_index - 1;
-       entity entcs = entcs_receiver(client_entity_index);
 
        bool can_use_3d = entcs && entcs.has_origin;
        bool too_close = vdist(entcs.origin - view_origin, <, autocvar_cl_damagetext_2d_close_range);
index f845a97cd225fd3d2fd3a9f1f62fd650db117617..0db74151579097f3211e68ad9afd7e38b7593c66 100644 (file)
@@ -17,6 +17,7 @@ vector autocvar_cl_damagetext_offset_world;
 float autocvar_cl_damagetext_accumulate_alpha_rel;
 int autocvar_cl_damagetext_friendlyfire;
 vector autocvar_cl_damagetext_friendlyfire_color;
+bool autocvar_cl_damagetext_received;
 
 bool autocvar_cl_damagetext_2d;
 vector autocvar_cl_damagetext_2d_pos;
index c244d170d259709ea485cc874e0a707e0c1294dc..d72bc973afd31181c35ca9949bbdf680e25db217 100644 (file)
@@ -26,12 +26,13 @@ bool write_damagetext(entity this, entity client, int sf)
        float potential_damage = this.dent_net_potential;
        if (!(
                (SV_DAMAGETEXT_ALL()) ||
-               (SV_DAMAGETEXT_PLAYERS() && client == attacker) ||
+               (SV_DAMAGETEXT_PLAYERS() && (client == attacker || client == hit || client.enemy == hit)) ||
                (SV_DAMAGETEXT_SPECTATORS_ONLY() && IS_SPEC(client) && client.enemy == attacker) ||
                (SV_DAMAGETEXT_SPECTATORS_ONLY() && IS_OBSERVER(client))
        )) return false;
 
        WriteHeader(MSG_ENTITY, damagetext);
+       WriteByte(MSG_ENTITY, etof(attacker));
        WriteByte(MSG_ENTITY, etof(hit));
        WriteInt24_t(MSG_ENTITY, deathtype);
        WriteByte(MSG_ENTITY, flags);
index f19fc059e393661839737c680b3089491e7bd4db..c371778beac63ca08b71e7ea676e5d1d86390763 100644 (file)
@@ -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;
index 2274da336e926e31baeeeb46d471653c793519f1..bc024179027b55055fa16fad6ef2eca251c427a4 100644 (file)
@@ -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);
index 219b23318c9ab6a68ae9be5f798de916b32659b9..e480dd49c399c70053b7454dc2f33165a6e8c54b 100644 (file)
@@ -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)
        {
index d1b449f475d8ac5608a069854660ec72626e739e..3815bdddaf176b90d349b6ebda0b8733a4676f21 100644 (file)
@@ -201,6 +201,12 @@ seta cl_autotaunt 0 "automatically taunt enemies when fragging them"
 seta cl_voice_directional 1    "0 = all voices are non-directional, 1 = all voices are directional, 2 = only taunts are directional"
 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.5 "how long hit indicator takes to fade away"
+seta cl_hit_indicator_fade_alpha 0.8 "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 "hit indicator size"
+
 seta cl_hitsound 1 "play a hit notifier sound when you have hit an enemy, 1: same pitch 2: decrease pitch with more damage 3: increase pitch with more damage"
 set cl_hitsound_antispam_time 0.05 "don't play the hitsound more often than this"
 seta cl_hitsound_min_pitch 0.75 "minimum pitch of hit sound"
@@ -465,6 +471,7 @@ seta cl_damagetext_accumulate_alpha_rel "0.65" "Only update existing damage text
 seta cl_damagetext_accumulate_lifetime "-1" "Only update existing damage text when it is younger than this many seconds, negative always updates"
 seta cl_damagetext_friendlyfire "1" "0: never show for friendly fire, 1: when more than 0 damage, 2: always"
 seta cl_damagetext_friendlyfire_color "1 0 0" "Damage text color for friendlyfire"
+seta cl_damagetext_received "0" "Draw damage received too"
 
 seta cl_damagetext_2d 1 "Show damagetext in 2D coordinates if the enemy's location is not known"
 seta cl_damagetext_2d_pos "0.47 0.53 0" "2D damage text initial position (X and Y between 0 and 1)"