Variable hitsound pitch based on damage, disabled by default (modified by Melanosuchus)
authorMario <zacjardine@y7mail.com>
Thu, 27 Nov 2014 07:51:15 +0000 (18:51 +1100)
committerMario <zacjardine@y7mail.com>
Thu, 27 Nov 2014 07:51:15 +0000 (18:51 +1100)
defaultXonotic.cfg
qcsrc/client/View.qc
qcsrc/client/autocvars.qh
qcsrc/common/stats.qh
qcsrc/server/defs.qh
qcsrc/server/g_damage.qc
qcsrc/server/g_world.qc
qcsrc/server/mutators/mutator_instagib.qc

index 86000b6..cb87529 100644 (file)
@@ -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"
index a069faa..166ea07 100644 (file)
@@ -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)
index 28ef157..9b0d683 100644 (file)
@@ -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;
index 793582e..f358069 100644 (file)
@@ -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;
index 7608bbe..b892e24 100644 (file)
@@ -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;
index 2e4c079..02488b8 100644 (file)
@@ -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;
index 44f60eb..7eda010 100644 (file)
@@ -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);
 }
 
 
index e014951..bd61023 100644 (file)
@@ -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;
        }