From c15f8fc9049fd33a4795dd63567a9c4c9e5c1f40 Mon Sep 17 00:00:00 2001 From: terencehill Date: Sun, 28 Jan 2024 00:45:07 +0100 Subject: [PATCH] HUD: Show a 2d directional indicator around the screen center when a player hits you --- qcsrc/client/view.qc | 26 +++++++++++ qcsrc/client/view.qh | 6 +++ qcsrc/common/effects/qc/damageeffects.qc | 56 +++++++++++++++--------- xonotic-client.cfg | 4 ++ 4 files changed, 71 insertions(+), 21 deletions(-) diff --git a/qcsrc/client/view.qc b/qcsrc/client/view.qc index 689f4b4ce..a1e2c6f91 100644 --- a/qcsrc/client/view.qc +++ b/qcsrc/client/view.qc @@ -753,6 +753,30 @@ void View_EventChase(entity this) } } +void HitIndicatorShow() +{ + if (!autocvar_cl_hit_indicator || autocvar_cl_hit_indicator_size <= 0 + || time > HitIndicator_time + 1 + || !HitIndicator_attacker || !CSQCModel_server2csqc(HitIndicator_attacker - 1)) + { + return; + } + + entity attacker = CSQCModel_server2csqc(HitIndicator_attacker - 1); + 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_centersss + ofs; + drawspritearrow(org, ang, '1 0 0', 0.8, autocvar_cl_hit_indicator_size); +} + vector damage_blurpostprocess, content_blurpostprocess; void UpdateDamage() @@ -907,6 +931,8 @@ void HUD_Draw(entity this) } } + HitIndicatorShow(); + // crosshair goes VERY LAST UpdateDamage(); HUD_Crosshair(this); diff --git a/qcsrc/client/view.qh b/qcsrc/client/view.qh index cd33ebfb6..76bedbdab 100644 --- a/qcsrc/client/view.qh +++ b/qcsrc/client/view.qh @@ -94,6 +94,12 @@ int autocvar_cl_nade_timer; bool autocvar_r_drawviewmodel; vector autocvar_cl_gunoffset; +bool autocvar_cl_hit_indicator = 1; +float autocvar_cl_hit_indicator_radius = 0.15; +float autocvar_cl_hit_indicator_size = 1.1; +int HitIndicator_attacker; +int HitIndicator_time; + void calc_followmodel_ofs(entity view); vector project_3d_to_2d(vector vec); diff --git a/qcsrc/common/effects/qc/damageeffects.qc b/qcsrc/common/effects/qc/damageeffects.qc index dbc138f41..d77af71f3 100644 --- a/qcsrc/common/effects/qc/damageeffects.qc +++ b/qcsrc/common/effects/qc/damageeffects.qc @@ -9,6 +9,7 @@ bool Damage_DamageInfo_SendEntity(entity this, entity to, int sf) vector org = vec3(floor(this.origin.x), floor(this.origin.y), floor(this.origin.z)); WriteHeader(MSG_ENTITY, ENT_CLIENT_DAMAGEINFO); WriteShort(MSG_ENTITY, this.projectiledeathtype); + WriteByte(MSG_ENTITY, etof(this.owner)); // attacker WriteVector(MSG_ENTITY, org); WriteByte(MSG_ENTITY, bound(1, this.dmg, 255)); WriteByte(MSG_ENTITY, bound(0, this.dmg_radius, 255)); @@ -35,6 +36,7 @@ void Damage_DamageInfo(vector org, float coredamage, float edgedamage, float rad // origin is just data to be sent //setorigin(e, org); e.origin = org; + e.owner = dmgowner; e.projectiledeathtype = deathtype; e.dmg = coredamage; e.dmg_edge = edgedamage; @@ -191,7 +193,7 @@ 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; + bool hitplayer = false, hitme = false; int species, forcemul; vector force, thisforce; @@ -199,6 +201,8 @@ NET_HANDLE(ENT_CLIENT_DAMAGEINFO, bool isNew) w_issilent = (w_deathtype & 0x8000); w_deathtype = (w_deathtype & 0x7FFF); + int attacker = ReadByte(); + w_org = ReadVector(); thedamage = ReadByte(); @@ -263,7 +267,9 @@ NET_HANDLE(ENT_CLIENT_DAMAGEINFO, bool isNew) DamageEffect(it, w_org, thisdmg, w_deathtype, species); - if(it != csqcplayer && (it.isplayermodel & ISPLAYER_MODEL)) + if(it == csqcplayer) + hitme = true; // this impact damaged me + else if(it != csqcplayer && (it.isplayermodel & ISPLAYER_MODEL)) hitplayer = true; // this impact damaged another player }); @@ -400,29 +406,37 @@ NET_HANDLE(ENT_CLIENT_DAMAGEINFO, bool isNew) MUTATOR_CALLHOOK(DamageInfo, this, w_deathtype, w_org); - // TODO spawn particle effects and sounds based on w_deathtype if(!DEATH_ISSPECIAL(w_deathtype)) - if(!hitplayer || rad) // don't show ground impacts for hitscan weapons if a player was hit { - Weapon hitwep = DEATH_WEAPONOF(w_deathtype); - w_random = prandom(); - - vector force_dir = normalize(force); - // this traceline usually starts in solid when a hitscan shot hits a surface with a very small angle - // if so, try another traceline starting further back (may still start in solid but only with extremely small angles) - traceline(w_org - force_dir * 16, w_org + force_dir * 16, MOVE_NOMONSTERS, NULL); - if(trace_startsolid) - traceline(w_org - force_dir * 40, w_org + force_dir * 16, MOVE_NOMONSTERS, NULL); - if(trace_fraction < 1) - w_backoff = trace_plane_normal; - else - w_backoff = -force_dir; - setorigin(this, w_org + w_backoff * 2); // for sound() calls + if(hitme) + { + HitIndicator_attacker = attacker; + HitIndicator_time = time; + } - if(!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)) + // 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 { - if(!MUTATOR_CALLHOOK(Weapon_ImpactEffect, hitwep, this)) - hitwep.wr_impacteffect(hitwep, this); + Weapon hitwep = DEATH_WEAPONOF(w_deathtype); + w_random = prandom(); + + vector force_dir = normalize(force); + // this traceline usually starts in solid when a hitscan shot hits a surface with a very small angle + // if so, try another traceline starting further back (may still start in solid but only with extremely small angles) + traceline(w_org - force_dir * 16, w_org + force_dir * 16, MOVE_NOMONSTERS, NULL); + if(trace_startsolid) + traceline(w_org - force_dir * 40, w_org + force_dir * 16, MOVE_NOMONSTERS, NULL); + if(trace_fraction < 1) + w_backoff = trace_plane_normal; + else + w_backoff = -force_dir; + setorigin(this, w_org + w_backoff * 2); // for sound() calls + + if(!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)) + { + if(!MUTATOR_CALLHOOK(Weapon_ImpactEffect, hitwep, this)) + hitwep.wr_impacteffect(hitwep, this); + } } } } diff --git a/xonotic-client.cfg b/xonotic-client.cfg index d1b449f47..16f752b1f 100644 --- a/xonotic-client.cfg +++ b/xonotic-client.cfg @@ -201,6 +201,10 @@ 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_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" + 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" -- 2.39.2