From: Mario Date: Thu, 27 Nov 2014 07:51:15 +0000 (+1100) Subject: Variable hitsound pitch based on damage, disabled by default (modified by Melanosuchus) X-Git-Tag: xonotic-v0.8.0~134^2~2 X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fxonotic-data.pk3dir.git;a=commitdiff_plain;h=4626abb5835aa634be556b43361c20ea6481be6f Variable hitsound pitch based on damage, disabled by default (modified by Melanosuchus) --- diff --git a/defaultXonotic.cfg b/defaultXonotic.cfg index 86000b66bf..cb8752959e 100644 --- a/defaultXonotic.cfg +++ b/defaultXonotic.cfg @@ -220,8 +220,11 @@ set sv_ready_restart 0 "if set to 1 allow a map to be restarted once all players set sv_ready_restart_after_countdown 0 "if set to 1 the players and map items are reset after the countdown ended, otherwise they're reset already at the beginning of the countdown" set sv_ready_restart_repeatable 0 "allows the players to restart the game as often as needed" -seta cl_hitsound 1 "play a hit notifier sound when you have hit an enemy" +seta cl_hitsound 1 "play a hit notifier sound when you have hit an enemy, 1: same pitch 2: increase pitch with more damage 3: decrease 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.95 "minimum pitch of hit sound" +seta cl_hitsound_max_pitch 2 "maximum pitch of hit sound" +seta cl_hitsound_nom_damage 80 "damage amount at which hitsound bases pitch off" seta cl_eventchase_death 1 "camera goes into 3rd person mode when the player is dead" seta cl_eventchase_nexball 1 "camera goes into 3rd person mode when in nexball game-mode" diff --git a/qcsrc/client/View.qc b/qcsrc/client/View.qc index a069faa465..166ea07f09 100644 --- a/qcsrc/client/View.qc +++ b/qcsrc/client/View.qc @@ -374,9 +374,11 @@ entity nightvision_noise, nightvision_noise2; #define MAX_TIME_DIFF 5 float pickup_crosshair_time, pickup_crosshair_size; -float hit_time, typehit_time; -float nextsound_hit_time, nextsound_typehit_time; -float hitindication_crosshair_time, hitindication_crosshair_size; +float hitsound_time_prev; +float spectatee_status_prev; // for preventing hitsound when switching spectatee +float damage_dealt_total, damage_dealt_total_prev; +float typehit_time, typehit_time_prev; +float hitindication_crosshair_size; float use_nex_chargepool; float myhealth, myhealth_prev; @@ -1136,21 +1138,87 @@ void CSQC_UpdateView(float w, float h) scoreboard_active = HUD_WouldDrawScoreboard(); - hit_time = getstatf(STAT_HIT_TIME); - if(hit_time > nextsound_hit_time && autocvar_cl_hitsound) + // varying sound pitch + damage_dealt_total = getstati(STAT_DAMAGE_DEALT_TOTAL); + + // detect overflow on server side + if (damage_dealt_total < damage_dealt_total_prev) { - if(time - hit_time < MAX_TIME_DIFF) // don't play the sound if it's too old. - sound(world, CH_INFO, "misc/hit.wav", VOL_BASE, ATTEN_NONE); + dprint("resetting dmg total: ", ftos(damage_dealt_total), " prev: ", ftos(damage_dealt_total_prev), "\n"); + damage_dealt_total_prev = 0; + } + + // prevent hitsound when switching spectatee + if (spectatee_status != spectatee_status_prev) + { + damage_dealt_total_prev = damage_dealt_total; + } + spectatee_status_prev = spectatee_status; - nextsound_hit_time = time + autocvar_cl_hitsound_antispam_time; + // amount of damage since last hit sound + float unaccounted_damage = damage_dealt_total - damage_dealt_total_prev; + + + if (autocvar_cl_hitsound == 1) + { + if ( time - hitsound_time_prev > autocvar_cl_hitsound_antispam_time ) + if ( damage_dealt_total > 0 ) + { + sound(world, CH_INFO, "misc/hit.wav", VOL_BASE, ATTEN_NONE); + hitsound_time_prev = time; + } } + else if (unaccounted_damage > 0 && autocvar_cl_hitsound > 0 && time - hitsound_time_prev > autocvar_cl_hitsound_antispam_time) + { + // customizable gradient function that crosses (0,a), (c,1) and asymptotically approaches b + float a, b, c, x; + a = autocvar_cl_hitsound_max_pitch; + b = autocvar_cl_hitsound_min_pitch; + c = autocvar_cl_hitsound_nom_damage; + x = unaccounted_damage; + float pitch_shift = (b*x*(a-1) + a*c*(1-b)) / (x*(a-1) + c*(1-b)); + + // if sound variation is disabled, set pitch_shift to 1 + if (autocvar_cl_hitsound == 1) + { + pitch_shift = 1; + } + + // if pitch shift is reversed, mirror in (max-min)/2 + min + if (autocvar_cl_hitsound == 3) + { + float mirror_value = (a-b)/2 + b; + pitch_shift = mirror_value + (mirror_value - pitch_shift); + } + + dprint("dmg total (dmg): ", ftos(damage_dealt_total), " (+", ftos(unaccounted_damage), "), pitch shift: ", ftos(pitch_shift), "\n"); + + // todo: avoid very long and very short sounds from wave stretching using different sound files? seems unnecessary + // todo: normalize sound pressure levels? seems unnecessary + + // scale to fit function interface + float param_pitch_shift = pitch_shift * 100; + + // play sound + sound7(world, CH_INFO, "misc/hit.wav", VOL_BASE, ATTN_NONE, param_pitch_shift, 0); + + // track damage accounted for + damage_dealt_total_prev = damage_dealt_total; + + // remember when this sound was played to prevent sound spam + hitsound_time_prev = time; + } + else if (autocvar_cl_hitsound == 0) + { + // forget the damage to prevent hitsound when enabling it + damage_dealt_total_prev = damage_dealt_total; + } + typehit_time = getstatf(STAT_TYPEHIT_TIME); - if(typehit_time > nextsound_typehit_time) + if(typehit_time - typehit_time_prev > autocvar_cl_hitsound_antispam_time) { - if(time - typehit_time < MAX_TIME_DIFF) // don't play the sound if it's too old. - sound(world, CH_INFO, "misc/typehit.wav", VOL_BASE, ATTEN_NONE); - - nextsound_typehit_time = time + autocvar_cl_hitsound_antispam_time; + sound(world, CH_INFO, "misc/typehit.wav", VOL_BASE, ATTN_NONE); + typehit_time_prev = typehit_time; } //else @@ -1350,16 +1418,14 @@ void CSQC_UpdateView(float w, float h) wcross_scale += sin(pickup_crosshair_size) * autocvar_crosshair_pickup; } + // todo: make crosshair hit indication dependent on damage dealt if(autocvar_crosshair_hitindication) { vector hitindication_color = ((autocvar_crosshair_color_special == 1) ? stov(autocvar_crosshair_hitindication_per_weapon_color) : stov(autocvar_crosshair_hitindication_color)); - if(hitindication_crosshair_time < hit_time) + if(unaccounted_damage) { - if(time - hit_time < MAX_TIME_DIFF) // don't trigger the animation if it's too old - hitindication_crosshair_size = 1; - - hitindication_crosshair_time = hit_time; + hitindication_crosshair_size = 1; } if(hitindication_crosshair_size > 0) diff --git a/qcsrc/client/autocvars.qh b/qcsrc/client/autocvars.qh index 28ef157e50..9b0d683656 100644 --- a/qcsrc/client/autocvars.qh +++ b/qcsrc/client/autocvars.qh @@ -411,6 +411,9 @@ float autocvar_vid_conwidth; float autocvar_vid_pixelheight; float autocvar_viewsize; float autocvar_cl_hitsound; +var float autocvar_cl_hitsound_min_pitch = 0.95; // minimal difference in minsta +var float autocvar_cl_hitsound_max_pitch = 2; +var float autocvar_cl_hitsound_nom_damage = 80; float autocvar_cl_hitsound_antispam_time; var float autocvar_cl_eventchase_death = 1; var float autocvar_cl_eventchase_nexball = 1; diff --git a/qcsrc/common/stats.qh b/qcsrc/common/stats.qh index 793582e126..f358069e21 100644 --- a/qcsrc/common/stats.qh +++ b/qcsrc/common/stats.qh @@ -56,7 +56,7 @@ const float STAT_NEX_CHARGE = 50; const float STAT_LAST_PICKUP = 51; const float STAT_HUD = 52; const float STAT_NEX_CHARGEPOOL = 53; -const float STAT_HIT_TIME = 54; +const float STAT_DAMAGE_DEALT_TOTAL = 54; const float STAT_TYPEHIT_TIME = 55; const float STAT_LAYED_MINES = 56; const float STAT_HAGAR_LOAD = 57; diff --git a/qcsrc/server/defs.qh b/qcsrc/server/defs.qh index 7608bbeb4b..b892e24f2b 100644 --- a/qcsrc/server/defs.qh +++ b/qcsrc/server/defs.qh @@ -155,7 +155,7 @@ void setanim(entity e, vector anim, float looping, float override, float restart .float dmgtime; .float killcount; -.float hitsound, typehitsound; +.float damage_dealt, typehitsound; .float watersound_finished; .float iscreature; @@ -519,6 +519,8 @@ string matchid; .float hit_time; .float typehit_time; +.float damage_dealt_total; + .float stat_leadlimit; float radar_showennemies; diff --git a/qcsrc/server/g_damage.qc b/qcsrc/server/g_damage.qc index 2e4c079ce7..02488b8b11 100644 --- a/qcsrc/server/g_damage.qc +++ b/qcsrc/server/g_damage.qc @@ -874,7 +874,7 @@ void Damage (entity targ, entity inflictor, entity attacker, float damage, float if(victim.BUTTON_CHAT) attacker.typehitsound += 1; else - attacker.hitsound += 1; + attacker.damage_dealt += damage; } damage_goodhits += 1; @@ -1337,12 +1337,12 @@ void Fire_ApplyDamage(entity e) t = min(frametime, e.fire_endtime - time); d = e.fire_damagepersec * t; - hi = e.fire_owner.hitsound; + hi = e.fire_owner.damage_dealt; ty = e.fire_owner.typehitsound; Damage(e, e, e.fire_owner, d, e.fire_deathtype, e.origin, '0 0 0'); if(e.fire_hitsound && e.fire_owner) { - e.fire_owner.hitsound = hi; + e.fire_owner.damage_dealt = hi; e.fire_owner.typehitsound = ty; } e.fire_hitsound = TRUE; diff --git a/qcsrc/server/g_world.qc b/qcsrc/server/g_world.qc index 44f60eb870..7eda0103c4 100644 --- a/qcsrc/server/g_world.qc +++ b/qcsrc/server/g_world.qc @@ -786,7 +786,7 @@ void spawnfunc_worldspawn (void) addstat(STAT_WEAPON_CLIPLOAD, AS_INT, clip_load); addstat(STAT_WEAPON_CLIPSIZE, AS_INT, clip_size); addstat(STAT_LAST_PICKUP, AS_FLOAT, last_pickup); - addstat(STAT_HIT_TIME, AS_FLOAT, hit_time); + addstat(STAT_DAMAGE_DEALT_TOTAL, AS_INT, damage_dealt_total); addstat(STAT_TYPEHIT_TIME, AS_FLOAT, typehit_time); addstat(STAT_LAYED_MINES, AS_INT, minelayer_mines); @@ -2236,19 +2236,21 @@ void EndFrame() float altime; FOR_EACH_REALCLIENT(self) { + self.damage_dealt_total = 0; + if(IS_SPEC(self)) { if(self.enemy.typehitsound) self.typehit_time = time; - else if(self.enemy.hitsound) - self.hit_time = time; + else if(self.enemy.damage_dealt) + self.damage_dealt_total = ceil(self.enemy.damage_dealt); } else { if(self.typehitsound) self.typehit_time = time; - else if(self.hitsound) - self.hit_time = time; + else if(self.damage_dealt) + self.damage_dealt_total = ceil(self.damage_dealt); } } altime = time + frametime * (1 + autocvar_g_antilag_nudge); @@ -2259,10 +2261,12 @@ void EndFrame() // needed! FOR_EACH_CLIENT(self) { - self.hitsound = FALSE; self.typehitsound = FALSE; + self.damage_dealt = 0; antilag_record(self, altime); } + FOR_EACH_MONSTER(self) + antilag_record(self, altime); } diff --git a/qcsrc/server/mutators/mutator_instagib.qc b/qcsrc/server/mutators/mutator_instagib.qc index e014951cea..bd61023a1e 100644 --- a/qcsrc/server/mutators/mutator_instagib.qc +++ b/qcsrc/server/mutators/mutator_instagib.qc @@ -251,8 +251,8 @@ MUTATOR_HOOKFUNCTION(instagib_PlayerDamage) frag_target.armorvalue -= 1; Send_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CENTER_MINSTA_LIVES_REMAINING, frag_target.armorvalue); frag_damage = 0; - frag_target.hitsound += 1; - frag_attacker.hitsound += 1; // TODO change this to a future specific hitsound for armor hit + frag_target.damage_dealt += 1; + frag_attacker.damage_dealt += 1; // TODO change this to a future specific hitsound for armor hit } if(IS_PLAYER(frag_attacker)) @@ -279,7 +279,7 @@ MUTATOR_HOOKFUNCTION(instagib_PlayerDamage) { frag_attacker.armorvalue -= 1; Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_MINSTA_LIVES_REMAINING, frag_attacker.armorvalue); - frag_attacker.hitsound += 1; + frag_attacker.damage_dealt += 1; } frag_mirrordamage = 0; }