Merge
authorWuzzy <Wuzzy2@mail.ru>
Tue, 30 Oct 2018 23:57:57 +0100
changeset 14060 aeac678d7c79
parent 14059 1ac129df8e5b (diff)
parent 14054 bf77c4d2294f (current diff)
child 14061 128fbd36eee4
Merge
rust/landgen/src/outline.rs
--- a/ChangeLog.txt	Tue Oct 30 19:08:39 2018 +0300
+++ b/ChangeLog.txt	Tue Oct 30 23:57:57 2018 +0100
@@ -7,7 +7,7 @@
  + Rework team rankings
  + Tied teams now rank equally
  + Help button in main menu
- + 14 new hedgehog taunts
+ + 17 new hedgehog taunts
  + Many new Lua API features
  * Functionality of controllers restored
  * Fix at least 2 crashes
@@ -54,7 +54,8 @@
  + New default brown clan color for better contrast
  + Allow to change volume during pause
  + Add sounds: flamethrower, landspray, idle freezer, shorykuen hit
- + Add taunts: Amazing, Brilliant, Bugger, Drat, Excellent, Fire, Gonnagetyou, Grenade, Ohdear, Runaway, Solong, Thisoneismine, Whatthe, Watchthis
+ + Add taunts: Amazing, Brilliant, Bugger, Cutitout, Drat, Excellent, Fire, Gonnagetyou, Grenade,
+               Leavemealone, Ohdear, Revenge, Runaway, Solong, Thisoneismine, Whatthe, Watchthis
  * Fix extreme amounts of droplets when shooting with minigun into ocean world edge
  * Fix some flakes disappearing in world wrap worlds while moving camera
  * Fix invisible projectile timer, attack bar, target on other side of wrap world edge
--- a/hedgewars/uScript.pas	Tue Oct 30 19:08:39 2018 +0300
+++ b/hedgewars/uScript.pas	Tue Oct 30 23:57:57 2018 +0100
@@ -2187,7 +2187,7 @@
                     if instaVoice then
                         PlaySoundV(TSound(s), gear^.Hedgehog^.Team^.Voicepack, false, true)
                     else
-                        AddVoice(TSound(s), gear^.Hedgehog^.Team^.Voicepack, true);
+                        AddVoice(TSound(s), gear^.Hedgehog^.Team^.Voicepack, true, false);
                     end;
                 end;
             end;
--- a/hedgewars/uSound.pas	Tue Oct 30 19:08:39 2018 +0300
+++ b/hedgewars/uSound.pas	Tue Oct 30 23:57:57 2018 +0100
@@ -62,14 +62,15 @@
 // Plays the sound snd [from a given voicepack],
 // if keepPlaying is given and true,
 // then the sound's playback won't be interrupted if asked to play again.
-procedure PlaySound(snd: TSound);
-procedure PlaySound(snd: TSound; keepPlaying: boolean);
-procedure PlaySound(snd: TSound; keepPlaying: boolean; ignoreMask: boolean);
-procedure PlaySound(snd: TSound; keepPlaying, ignoreMask, soundAsMusic: boolean);
-procedure PlaySoundV(snd: TSound; voicepack: PVoicepack);
-procedure PlaySoundV(snd: TSound; voicepack: PVoicepack; keepPlaying: boolean);
-procedure PlaySoundV(snd: TSound; voicepack: PVoicepack; keepPlaying, ignoreMask: boolean);
-procedure PlaySoundV(snd: TSound; voicepack: PVoicepack; keepPlaying, ignoreMask, soundAsMusic: boolean);
+// Returns true if sound was found and is played, false otherwise.
+function PlaySound(snd: TSound): boolean;
+function PlaySound(snd: TSound; keepPlaying: boolean): boolean;
+function PlaySound(snd: TSound; keepPlaying: boolean; ignoreMask: boolean): boolean;
+function PlaySound(snd: TSound; keepPlaying, ignoreMask, soundAsMusic: boolean): boolean;
+function PlaySoundV(snd: TSound; voicepack: PVoicepack): boolean;
+function PlaySoundV(snd: TSound; voicepack: PVoicepack; keepPlaying: boolean): boolean;
+function PlaySoundV(snd: TSound; voicepack: PVoicepack; keepPlaying, ignoreMask: boolean): boolean;
+function PlaySoundV(snd: TSound; voicepack: PVoicepack; keepPlaying, ignoreMask, soundAsMusic: boolean): boolean;
 
 // Plays/stops a sound to replace the main background music temporarily.
 procedure PlayMusicSound(snd: TSound);
@@ -89,8 +90,11 @@
 procedure StopSoundChan(chn: LongInt);
 procedure StopSoundChan(chn, fadems: LongInt);
 
+// Add voice to the voice queue
 procedure AddVoice(snd: TSound; voicepack: PVoicepack);
-procedure AddVoice(snd: TSound; voicepack: PVoicepack; ignoreMask: boolean);
+procedure AddVoice(snd: TSound; voicepack: PVoicepack; ignoreMask, isFallback: boolean);
+
+// Actually play next voice
 procedure PlayNextVoice;
 
 
@@ -125,6 +129,7 @@
 
 var Volume: LongInt;
     SoundTimerTicks: Longword;
+    LastVoiceFailed: boolean;
 implementation
 uses uVariables, uConsole, uCommands, uDebug, uPhysFSLayer;
 
@@ -140,14 +145,14 @@
     isAutoDampening: boolean;
     isSEBackup: boolean;
     VoiceList : array[0..7] of TVoice =  (
-                    ( snd: sndNone; voicepack: nil),
-                    ( snd: sndNone; voicepack: nil),
-                    ( snd: sndNone; voicepack: nil),
-                    ( snd: sndNone; voicepack: nil),
-                    ( snd: sndNone; voicepack: nil),
-                    ( snd: sndNone; voicepack: nil),
-                    ( snd: sndNone; voicepack: nil),
-                    ( snd: sndNone; voicepack: nil));
+                    ( snd: sndNone; voicepack: nil; isFallback: false),
+                    ( snd: sndNone; voicepack: nil; isFallback: false),
+                    ( snd: sndNone; voicepack: nil; isFallback: false),
+                    ( snd: sndNone; voicepack: nil; isFallback: false),
+                    ( snd: sndNone; voicepack: nil; isFallback: false),
+                    ( snd: sndNone; voicepack: nil; isFallback: false),
+                    ( snd: sndNone; voicepack: nil; isFallback: false),
+                    ( snd: sndNone; voicepack: nil; isFallback: false));
     Soundz: array[TSound] of record
             FileName: string[31];
             Path, AltPath    : TPathType;
@@ -319,7 +324,10 @@
             (FileName:            'Excellent.ogg'; Path: ptVoices; AltPath: ptNone),// sndExcellent
             (FileName:                 'Fire.ogg'; Path: ptVoices; AltPath: ptNone),// sndFire
             (FileName:            'Watchthis.ogg'; Path: ptVoices; AltPath: ptNone),// sndWatchThis
-            (FileName:              'Runaway.ogg'; Path: ptVoices; AltPath: ptNone) // sndRunAway
+            (FileName:              'Runaway.ogg'; Path: ptVoices; AltPath: ptNone),// sndRunAway
+            (FileName:              'Revenge.ogg'; Path: ptVoices; AltPath: ptNone),// sndRevenge
+            (FileName:             'Cutitout.ogg'; Path: ptVoices; AltPath: ptNone),// sndCutItOut
+            (FileName:         'Leavemealone.ogg'; Path: ptVoices; AltPath: ptNone) // sndLeaveMeAlone
             );
 
 
@@ -462,45 +470,81 @@
         end;
 end;
 
-procedure PlaySound(snd: TSound);
-begin
-    PlaySoundV(snd, nil, false, false, false);
-end;
-
-procedure PlaySound(snd: TSound; keepPlaying: boolean);
+// Get a fallback voice, assuming that snd is not available. Returns sndNone if none is found.
+function GetFallbackV(snd: TSound): TSound;
 begin
-    PlaySoundV(snd, nil, keepPlaying, false, false);
-end;
-
-procedure PlaySound(snd: TSound; keepPlaying: boolean; ignoreMask: boolean);
-begin
-    PlaySoundV(snd, nil, keepPlaying, ignoreMask, false);
+    // Fallback to sndFirePunch1 / sndOw1 / sndOoff1 if a “higher-numbered” sound is missing
+    if (snd in [sndFirePunch2, sndFirePunch3, sndFirePunch4, sndFirePunch5, sndFirePunch6]) then
+        GetFallbackV := sndFirePunch1
+    else if (snd in [sndOw2, sndOw3, sndOw4]) then
+        GetFallbackV := sndOw1
+    else if (snd in [sndOoff2, sndOoff3]) then
+        GetFallbackV := sndOoff1
+    // Other fallback sounds
+    else if (snd = sndGrenade) then
+        if random(2) = 0 then
+            GetFallbackV := sndNooo
+        else
+            GetFallbackV := sndUhOh
+    else if (snd in [sndDrat, sndBugger]) then
+        GetFallbackV := sndStupid
+    else if (snd in [sndGonnaGetYou, sndCutItOut, sndLeaveMeAlone]) then
+        GetFallbackV := sndRegret
+    else if (snd in [sndOhDear, sndSoLong]) then
+        GetFallbackV := sndByeBye
+    else if (snd = sndWhatThe) then
+        GetFallbackV := sndNooo
+    else if (snd = sndRunAway) then
+        GetFallbackV := sndOops
+    else if (snd = sndThisOneIsMine) then
+        GetFallbackV := sndReinforce
+    else if (snd in [sndAmazing, sndBrilliant, sndExcellent]) then
+        GetFallbackV := sndEnemyDown
+    else
+        GetFallbackV := sndNone;
 end;
 
-procedure PlaySound(snd: TSound; keepPlaying: boolean; ignoreMask, soundAsMusic: boolean);
+function PlaySound(snd: TSound): boolean;
 begin
-    PlaySoundV(snd, nil, keepPlaying, ignoreMask, soundAsMusic);
+    PlaySound:= PlaySoundV(snd, nil, false, false, false);
 end;
 
-procedure PlaySoundV(snd: TSound; voicepack: PVoicepack);
+function PlaySound(snd: TSound; keepPlaying: boolean): boolean;
 begin
-    PlaySoundV(snd, voicepack, false, false, false);
+    PlaySound:= PlaySoundV(snd, nil, keepPlaying, false, false);
+end;
+
+function PlaySound(snd: TSound; keepPlaying: boolean; ignoreMask: boolean): boolean;
+begin
+    PlaySound:= PlaySoundV(snd, nil, keepPlaying, ignoreMask, false);
 end;
 
-procedure PlaySoundV(snd: TSound; voicepack: PVoicepack; keepPlaying: boolean);
+function PlaySound(snd: TSound; keepPlaying: boolean; ignoreMask, soundAsMusic: boolean): boolean;
 begin
-    PlaySoundV(snd, voicepack, keepPlaying, false, false);
+    PlaySound:= PlaySoundV(snd, nil, keepPlaying, ignoreMask, soundAsMusic);
+end;
+
+function PlaySoundV(snd: TSound; voicepack: PVoicepack): boolean;
+begin
+    PlaySoundV:= PlaySoundV(snd, voicepack, false, false, false);
 end;
 
-procedure PlaySoundV(snd: TSound; voicepack: PVoicepack; keepPlaying, ignoreMask: boolean);
+function PlaySoundV(snd: TSound; voicepack: PVoicepack; keepPlaying: boolean): boolean;
 begin
-    PlaySoundV(snd, voicepack, keepPlaying, ignoreMask, false);
+    PlaySoundV:= PlaySoundV(snd, voicepack, keepPlaying, false, false);
 end;
 
-procedure PlaySoundV(snd: TSound; voicepack: PVoicepack; keepPlaying, ignoreMask, soundAsMusic: boolean);
+function PlaySoundV(snd: TSound; voicepack: PVoicepack; keepPlaying, ignoreMask: boolean): boolean;
+begin
+    PlaySoundV:= PlaySoundV(snd, voicepack, keepPlaying, ignoreMask, false);
+end;
+
+function PlaySoundV(snd: TSound; voicepack: PVoicepack; keepPlaying, ignoreMask, soundAsMusic: boolean): boolean;
 var s:shortstring;
 rwops: PSDL_RWops;
 begin
+    s:= cPathz[Soundz[snd].Path] + '/' + voicepack^.name + '/' + Soundz[snd].FileName;
+    PlaySoundV:= false;
     if ((not isSoundEnabled) and (not (soundAsMusic and isMusicEnabled))) or fastUntilLag then
         exit;
 
@@ -515,37 +559,12 @@
         if (voicepack^.chunks[snd] = nil) and (Soundz[snd].Path = ptVoices) and (Soundz[snd].FileName <> '') then
             begin
             s:= cPathz[Soundz[snd].Path] + '/' + voicepack^.name + '/' + Soundz[snd].FileName;
-            // Fallback sounds
+            // Fallback taunts
             if (not pfsExists(s)) then
                 begin
-                // Fallback to sndFirePunch1 / sndOw1 / sndOoff1 if a “higher-numbered” sound is missing
-                if (snd in [sndFirePunch2, sndFirePunch3, sndFirePunch4, sndFirePunch5, sndFirePunch6]) then
-                    snd := sndFirePunch1
-                else if (snd in [sndOw2, sndOw3, sndOw4]) then
-                    snd := sndOw1
-                else if (snd in [sndOoff2, sndOoff3]) then
-                    snd := sndOoff1
-                // Other fallback sounds
-                else if (snd = sndGrenade) then
-                    if random(2) = 0 then
-                        snd := sndNooo
-                    else
-                        snd := sndUhOh
-                else if (snd in [sndDrat, sndBugger]) then
-                    snd := sndStupid
-                else if (snd = sndGonnaGetYou) then
-                    snd := sndRegret
-                else if (snd in [sndOhDear, sndSoLong]) then
-                    snd := sndByeBye
-                else if (snd = sndWhatThe) then
-                    snd := sndNooo
-                else if (snd = sndRunAway) then
-                    snd := sndOops
-                else if (snd = sndThisOneIsMine) then
-                    snd := sndReinforce
-                else if (snd in [sndAmazing, sndBrilliant, sndExcellent]) then
-                    snd := sndEnemyDown;
-
+                snd := GetFallbackV(snd);
+                if snd = sndNone then
+                    exit;
                 s:= cPathz[Soundz[snd].Path] + '/' + voicepack^.name + '/' + Soundz[snd].FileName;
                 end;
             WriteToConsole(msgLoading + s + ' ');
@@ -564,7 +583,8 @@
             else
                 WriteLnToConsole(msgOK)
             end;
-        lastChan[snd]:= Mix_PlayChannelTimed(-1, voicepack^.chunks[snd], 0, -1)
+        lastChan[snd]:= Mix_PlayChannelTimed(-1, voicepack^.chunks[snd], 0, -1);
+        PlaySoundV:= true;
         end
     else
         begin
@@ -585,7 +605,8 @@
             if SDLCheck(defVoicepack^.chunks[snd] <> nil, 'Mix_LoadWAV_RW', true) then exit;
             WriteLnToConsole(msgOK);
             end;
-        lastChan[snd]:= Mix_PlayChannelTimed(-1, defVoicepack^.chunks[snd], 0, -1)
+        lastChan[snd]:= Mix_PlayChannelTimed(-1, defVoicepack^.chunks[snd], 0, -1);
+        PlaySoundV:= true;
         end;
 end;
 
@@ -603,10 +624,23 @@
 
 procedure AddVoice(snd: TSound; voicepack: PVoicepack);
 begin
-    AddVoice(snd, voicepack, false);
+    AddVoice(snd, voicepack, false, false);
 end;
 
-procedure AddVoice(snd: TSound; voicepack: PVoicepack; ignoreMask: boolean);
+{
+AddVoice: Add a voice to the voice queue.
+* snd: Sound ID
+* voicepack: Hedgehog voicepack
+* ignoreMask: If true, the sound will be played anyway if masked by Lua
+* isFallback: If true, this sound is added as fallback if the sound previously added to the
+             queue was not found. Useful to make sure a voice is always played, even if
+             a voicepack is incomplete.
+             Example:
+                 AddVoice(sndRevenge, voiceAttacker);
+                 AddVoice(sndRegret, voiceVictim, false, true);
+             --> plays sndRegret if sndRevenge could not be played.
+}
+procedure AddVoice(snd: TSound; voicepack: PVoicepack; ignoreMask, isFallback: boolean);
 var i : LongInt;
 begin
 
@@ -634,11 +668,13 @@
         begin
         VoiceList[i].snd:= snd;
         VoiceList[i].voicepack:= voicepack;
+        VoiceList[i].isFallback:= isFallback;
         end
 end;
 
 procedure PlayNextVoice;
 var i : LongInt;
+    played : boolean;
 begin
     if (not isSoundEnabled) or fastUntilLag or ((LastVoice.snd <> sndNone) and (lastChan[LastVoice.snd] <> -1) and (Mix_Playing(lastChan[LastVoice.snd]) <> 0)) then
         exit;
@@ -646,14 +682,19 @@
     while (i<High(VoiceList)) and (VoiceList[i].snd = sndNone) do
         inc(i);
 
-    if (VoiceList[i].snd <> sndNone) then
+    played:= false;
+    if (VoiceList[i].snd <> sndNone) and ((not VoiceList[i].isFallback) or LastVoiceFailed) then
         begin
         LastVoice.snd:= VoiceList[i].snd;
         LastVoice.voicepack:= VoiceList[i].voicepack;
+        LastVoice.isFallback:= VoiceList[i].isFallback;
         VoiceList[i].snd:= sndNone;
-        PlaySoundV(LastVoice.snd, LastVoice.voicepack)
+        played:= PlaySoundV(LastVoice.snd, LastVoice.voicepack);
+        // Remember if sound was not played.
+        LastVoiceFailed:= (not played);
         end
-    else LastVoice.snd:= sndNone;
+    else
+        LastVoice.snd:= sndNone;
 end;
 
 function LoopSound(snd: TSound): LongInt;
@@ -953,6 +994,7 @@
     Volume:= 0;
     SoundTimerTicks:= 0;
     defVoicepack:= AskForVoicepack('Default');
+    LastVoiceFailed:= false;
 
     for i:= Low(TSound) to High(TSound) do
         lastChan[i]:= -1;
--- a/hedgewars/uStats.pas	Tue Oct 30 19:08:39 2018 +0300
+++ b/hedgewars/uStats.pas	Tue Oct 30 23:57:57 2018 +0100
@@ -53,17 +53,21 @@
 uses uSound, uLocale, uVariables, uUtils, uIO, uCaptions, uMisc, uConsole, uScript;
 
 var DamageClan  : Longword = 0;
+    DamageTeam  : Longword = 0;
     DamageTotal : Longword = 0;
     DamageTurn  : Longword = 0;
     PoisonTurn  : Longword = 0; // Poisoned enemies per turn
     PoisonClan  : Longword = 0; // Poisoned own clan members in turn
+    PoisonTeam  : Longword = 0; // Poisoned own team members in turn
     PoisonTotal : Longword = 0; // Poisoned hogs in whole round
     KillsClan   : LongWord = 0;
+    KillsTeam   : LongWord = 0;
     Kills       : LongWord = 0;
     KillsTotal  : LongWord = 0;
     HitTargets  : LongWord = 0; // Target (gtTarget) hits per turn
     AmmoUsedCount : Longword = 0;
     AmmoDamagingUsed : boolean = false;
+    LeaveMeAlone : boolean = false;
     SkippedTurns: LongWord = 0;
     isTurnSkipped: boolean = false;
     vpHurtSameClan: PVoicepack = nil;
@@ -71,10 +75,12 @@
 
 procedure HedgehogPoisoned(Gear: PGear; Attacker: PHedgehog);
 begin
-    if Attacker^.Team^.Clan = Gear^.HEdgehog^.Team^.Clan then
+    if Attacker^.Team^.Clan = Gear^.Hedgehog^.Team^.Clan then
         begin
-        vpHurtSameClan:= CurrentHedgehog^.Team^.voicepack;
-        inc(PoisonClan)
+        vpHurtSameClan:= Gear^.Hedgehog^.Team^.voicepack;
+        inc(PoisonClan);
+        if Attacker^.Team = Gear^.Hedgehog^.Team then
+            inc(PoisonTeam);
         end
     else
         begin
@@ -93,9 +99,28 @@
 procedure HedgehogDamaged(Gear: PGear; Attacker: PHedgehog; Damage: Longword; killed: boolean);
 begin
 if Attacker^.Team^.Clan = Gear^.Hedgehog^.Team^.Clan then
-    vpHurtSameClan:= CurrentHedgehog^.Team^.voicepack
+    vpHurtSameClan:= Gear^.Hedgehog^.Team^.voicepack
 else
+    begin
     vpHurtEnemy:= Gear^.Hedgehog^.Team^.voicepack;
+    if (not killed) then
+        begin
+        // Check if victim got attacked by RevengeHog again
+        if (Gear^.Hedgehog^.RevengeHog <> nil) and (Gear^.Hedgehog^.RevengeHog = Attacker) then
+            LeaveMeAlone:= true;
+        // Check if attacker got revenge
+        if (Attacker^.RevengeHog <> nil) and (Attacker^.RevengeHog = Gear^.Hedgehog) then
+            begin
+            Attacker^.stats.GotRevenge:= true;
+            // Also reset the "in-row" counter to restore LeaveMeAlone/CutItOut taunts
+            Attacker^.stats.StepDamageRecvInRow:= 0;
+            Attacker^.RevengeHog:= nil;
+            end
+        // If not, victim remembers their attacker to plan *their* revenge
+        else
+            Gear^.Hedgehog^.RevengeHog:= Attacker;
+        end
+    end;
 
 //////////////////////////
 
@@ -103,6 +128,7 @@
 inc(Gear^.Hedgehog^.stats.StepDamageRecv, Damage);
 
 if CurrentHedgehog^.Team^.Clan = Gear^.Hedgehog^.Team^.Clan then inc(DamageClan, Damage);
+if CurrentHedgehog^.Team = Gear^.Hedgehog^.Team then inc(DamageTeam, Damage);
 
 if killed then
     begin
@@ -119,7 +145,11 @@
     if Gear = Attacker^.Gear then
         inc(Attacker^.Team^.stats.Suicides);
     if Attacker^.Team^.Clan = Gear^.Hedgehog^.Team^.Clan then
+        begin
         inc(KillsClan);
+        if Attacker^.Team = Gear^.Hedgehog^.Team then
+            inc(KillsTeam);
+        end;
     end;
 
 inc(DamageTotal, Damage);
@@ -147,6 +177,7 @@
 for t:= 0 to Pred(TeamsCount) do // send even on zero turn
     with TeamsArray[t]^ do
         for i:= 0 to cMaxHHIndex do
+            begin
             with Hedgehogs[i].stats do
                 begin
                 inc(DamageRecv, StepDamageRecv);
@@ -157,7 +188,18 @@
                     MaxStepDamageGiven:= StepDamageGiven;
                 if StepKills > MaxStepKills then
                     MaxStepKills:= StepKills;
+                if (Hedgehogs[i].Team <> nil) and (Hedgehogs[i].Team^.Clan^.ClanIndex <> CurrentHedgehog^.Team^.Clan^.ClanIndex) then
+                    begin
+                    if StepDamageRecv > 0 then
+                        inc(StepDamageRecvInRow)
+                    else
+                        StepDamageRecvInRow:= 0;
+                    if StepDamageRecvInRow >= 3 then
+                        LeaveMeAlone:= true;
+                    end;
                 end;
+            end;
+
 
 // Write into the death log which clans died in this turn,
 // important for final rankings.
@@ -241,8 +283,10 @@
             else
                 AddVoice(sndWatchIt, vpHurtSameClan)
         else
-            if random(2) = 0 then
+            // Attacked same team
+            if (random(2) = 0) and ((DamageTeam <> 0) or (KillsTeam > killsCheck) or (PoisonTeam <> 0)) then
                 AddVoice(sndSameTeam, vpHurtSameClan)
+            // Attacked same team or a clan member
             else
                 AddVoice(sndTraitor, vpHurtSameClan)
 
@@ -262,12 +306,31 @@
             AddVoice(sndEnemyDown, CurrentTeam^.voicepack)
         // 0 kills, only damage or poison
         else
-            // TODO: Play sndExcellent for a high damage shot.
-            // Not done yet because the fallback is sndEnemyDown.
-            if random(2) = 0 then
-                AddVoice(sndRegret, vpHurtEnemy)
+            // possible reactions of victim, in the order of preference:
+            // 1. claiming revenge
+            // 2. complaining about getting attacked too often
+            // 3. threatening enemy with retaliation
+            if CurrentHedgehog^.stats.GotRevenge then
+                begin
+                AddVoice(sndRevenge, CurrentHedgehog^.Team^.voicepack);
+                // If revenge taunt was added, one of the following voices is
+                // added as fallback (4th param), in case of a missing Revenge sound file.
+                if random(2) = 0 then
+                    AddVoice(sndRegret, vpHurtEnemy, false, true)
+                else
+                    AddVoice(sndGonnaGetYou, vpHurtEnemy, false, true);
+                end
             else
-                AddVoice(sndGonnaGetYou, vpHurtEnemy)
+                if LeaveMeAlone then
+                    if random(2) = 0 then
+                        AddVoice(sndCutItOut, vpHurtEnemy)
+                    else
+                        AddVoice(sndLeaveMeAlone, vpHurtEnemy)
+                else
+                    if random(2) = 0 then
+                        AddVoice(sndRegret, vpHurtEnemy)
+                    else
+                        AddVoice(sndGonnaGetYou, vpHurtEnemy)
 
     // Missed shot
     // A miss is defined as a shot with a damaging weapon with 0 kills, 0 damage, 0 hogs poisoned and 0 targets hit
@@ -308,16 +371,21 @@
                 StepDamageGiven:= 0;
                 StepPoisoned:= false;
                 StepDied:= false;
+                GotRevenge:= false;
                 end;
 
 Kills:= 0;
 KillsClan:= 0;
+KillsTeam:= 0;
 DamageClan:= 0;
+DamageTeam:= 0;
 DamageTurn:= 0;
 HitTargets:= 0;
 PoisonClan:= 0;
+PoisonTeam:= 0;
 PoisonTurn:= 0;
 AmmoUsedCount:= 0;
+LeaveMeAlone:= false;
 AmmoDamagingUsed:= false;
 isTurnSkipped:= false;
 end;
@@ -513,16 +581,20 @@
 procedure initModule;
 begin
     DamageClan  := 0;
+    DamageTeam  := 0;
     DamageTotal := 0;
     DamageTurn  := 0;
     PoisonClan  := 0;
+    PoisonTeam  := 0;
     PoisonTurn  := 0;
     KillsClan   := 0;
+    KillsTeam   := 0;
     Kills       := 0;
     KillsTotal  := 0;
     HitTargets  := 0;
     AmmoUsedCount := 0;
     AmmoDamagingUsed := false;
+    LeaveMeAlone := false;
     SkippedTurns:= 0;
     isTurnSkipped:= false;
     vpHurtSameClan:= nil;
--- a/hedgewars/uTeams.pas	Tue Oct 30 19:08:39 2018 +0300
+++ b/hedgewars/uTeams.pas	Tue Oct 30 23:57:57 2018 +0100
@@ -708,6 +708,7 @@
     CurrentHedgehog^.Gear:= Gear;
     CurrentHedgehog^.Name:= id;
     CurrentHedgehog^.InitialHealth:= Gear^.Health;
+    CurrentHedgehog^.RevengeHog:= nil;
     CurrHedgehog:= HedgehogsNumber;
     inc(HedgehogsNumber)
     end
--- a/hedgewars/uTypes.pas	Tue Oct 30 19:08:39 2018 +0300
+++ b/hedgewars/uTypes.pas	Tue Oct 30 23:57:57 2018 +0100
@@ -154,7 +154,8 @@
             sndCustom5, sndCustom6, sndCustom7, sndCustom8, sndMinigun, sndFlamethrower, sndIceBeamIdle,
             sndLandGun, sndCaseImpact, sndExtraDamage, sndFirePunchHit, sndGrenade, sndThisOneIsMine,
             sndWhatThe, sndSoLong, sndOhDear, sndGonnaGetYou, sndDrat, sndBugger, sndAmazing,
-            sndBrilliant, sndExcellent, sndFire, sndWatchThis, sndRunAway);
+            sndBrilliant, sndExcellent, sndFire, sndWatchThis, sndRunAway, sndRevenge, sndCutItOut,
+            sndLeaveMeAlone);
 
     // Available ammo types to be used by hedgehogs
     TAmmoType  = (amNothing, amGrenade, amClusterBomb, amBazooka, amBee, amShotgun, amPickHammer, // 6
@@ -330,17 +331,19 @@
         end;
 
     TStatistics = record
-        DamageRecv,
-        DamageGiven: Longword;
-        StepDamageRecv,
-        StepDamageGiven,
-        StepKills: Longword;
-        StepPoisoned,
-        StepDied,
-        Sacrificed: boolean;
-        MaxStepDamageRecv,
-        MaxStepDamageGiven,
-        MaxStepKills: Longword;
+        DamageRecv,              // total damage received
+        DamageGiven: Longword;   // total damage dealt
+        StepDamageRecvInRow,     // number of enemy turns in row this hog received any damage
+        StepDamageRecv,          // damage received in this turn
+        StepDamageGiven,         // damage dealt in this turn
+        StepKills: Longword;     // kills in this turn
+        StepPoisoned,            // whether hog got poisoned this turn
+        StepDied,                // whether hog died this turn
+        Sacrificed,              // whether hog was sacrificed in suicide attack (kamikaze, piano)
+        GotRevenge: boolean;     // whether hog got revenge in this turn
+        MaxStepDamageRecv,       // most damage received in one turn
+        MaxStepDamageGiven,      // most damage dealt in one turn
+        MaxStepKills: Longword;  // most kills in one turn
         FinishedTurns: Longword;
         end;
 
@@ -381,6 +384,7 @@
     TVoice = record
         snd: TSound;
         voicepack: PVoicePack;
+        isFallback: boolean;
         end;
 
     THHAmmo = array[0..cMaxSlotIndex, 0..cMaxSlotAmmoIndex] of TAmmo;
@@ -412,6 +416,7 @@
             Timer: Longword;
             HealthBarHealth: LongInt;
             Effects: array[THogEffect] of LongInt;
+            RevengeHog: PHedgehog;   // For which hog this hog wants revenge most. For sndRevenge taunt
             end;
 
     TTeam = record
--- a/share/hedgewars/Data/Sounds/voices/British/CMakeLists.txt	Tue Oct 30 19:08:39 2018 +0300
+++ b/share/hedgewars/Data/Sounds/voices/British/CMakeLists.txt	Tue Oct 30 23:57:57 2018 +0100
@@ -6,6 +6,7 @@
 Byebye.ogg
 Comeonthen.ogg
 Coward.ogg
+Cutitout.ogg
 Drat.ogg
 Enemydown.ogg
 Excellent.ogg
@@ -23,6 +24,7 @@
 Justyouwait.ogg
 Kamikaze.ogg
 Laugh.ogg
+Leavemealone.ogg
 Melon.ogg
 Missed.ogg
 Nooo.ogg
@@ -34,6 +36,7 @@
 PoisonCough.ogg
 PoisonMoan.ogg
 Reinforcements.ogg
+Revenge.ogg
 Runaway.ogg
 Sameteam.ogg
 Solong.ogg
--- a/share/hedgewars/Data/Sounds/voices/Classic/CMakeLists.txt	Tue Oct 30 19:08:39 2018 +0300
+++ b/share/hedgewars/Data/Sounds/voices/Classic/CMakeLists.txt	Tue Oct 30 23:57:57 2018 +0100
@@ -6,6 +6,7 @@
 Byebye.ogg
 Comeonthen.ogg
 Coward.ogg
+Cutitout.ogg
 Drat.ogg
 Enemydown.ogg
 Excellent.ogg
@@ -23,6 +24,7 @@
 Justyouwait.ogg
 Kamikaze.ogg
 Laugh.ogg
+Leavemealone.ogg
 Melon.ogg
 Missed.ogg
 Nooo.ogg
@@ -34,6 +36,9 @@
 PoisonCough.ogg
 PoisonMoan.ogg
 Reinforcements.ogg
+# FIXME: Revenge.ogg is "I'll get you back" but this taunt is meant for
+# successful reveng, not the announcement thereof.
+#Revenge.ogg
 Runaway.ogg
 Sameteam.ogg
 Solong.ogg
--- a/share/hedgewars/Data/Sounds/voices/Default/CMakeLists.txt	Tue Oct 30 19:08:39 2018 +0300
+++ b/share/hedgewars/Data/Sounds/voices/Default/CMakeLists.txt	Tue Oct 30 23:57:57 2018 +0100
@@ -6,6 +6,7 @@
 Byebye.ogg
 Comeonthen.ogg
 Coward.ogg
+Cutitout.ogg
 Drat.ogg
 Enemydown.ogg
 Excellent.ogg
@@ -23,6 +24,7 @@
 Justyouwait.ogg
 Kamikaze.ogg
 Laugh.ogg
+Leavemealone.ogg
 Melon.ogg
 Missed.ogg
 Nooo.ogg
@@ -34,6 +36,7 @@
 PoisonCough.ogg
 PoisonMoan.ogg
 Reinforcements.ogg
+Revenge.ogg
 Runaway.ogg
 Sameteam.ogg
 Solong.ogg
--- a/share/hedgewars/Data/Sounds/voices/Default_uk/CMakeLists.txt	Tue Oct 30 19:08:39 2018 +0300
+++ b/share/hedgewars/Data/Sounds/voices/Default_uk/CMakeLists.txt	Tue Oct 30 23:57:57 2018 +0100
@@ -6,6 +6,7 @@
 Byebye.ogg
 Comeonthen.ogg
 Coward.ogg
+Cutitout.ogg
 Drat.ogg
 Enemydown.ogg
 Excellent.ogg
@@ -23,6 +24,7 @@
 Justyouwait.ogg
 Kamikaze.ogg
 Laugh.ogg
+Leavemealone.ogg
 Melon.ogg
 Missed.ogg
 Nooo.ogg
@@ -34,6 +36,7 @@
 PoisonCough.ogg
 PoisonMoan.ogg
 Reinforcements.ogg
+Revenge.ogg
 Runaway.ogg
 Sameteam.ogg
 Solong.ogg
--- a/share/hedgewars/Data/Sounds/voices/Mobster/CMakeLists.txt	Tue Oct 30 19:08:39 2018 +0300
+++ b/share/hedgewars/Data/Sounds/voices/Mobster/CMakeLists.txt	Tue Oct 30 23:57:57 2018 +0100
@@ -6,6 +6,7 @@
 Byebye.ogg
 Comeonthen.ogg
 Coward.ogg
+Cutitout.ogg
 Drat.ogg
 Enemydown.ogg
 Excellent.ogg
@@ -23,6 +24,7 @@
 Justyouwait.ogg
 Kamikaze.ogg
 Laugh.ogg
+Leavemealone.ogg
 Melon.ogg
 Missed.ogg
 Nooo.ogg
@@ -34,6 +36,7 @@
 PoisonCough.ogg
 PoisonMoan.ogg
 Reinforcements.ogg
+Revenge.ogg
 Runaway.ogg
 Sameteam.ogg
 Solong.ogg
--- a/share/hedgewars/Data/Sounds/voices/Pirate/CMakeLists.txt	Tue Oct 30 19:08:39 2018 +0300
+++ b/share/hedgewars/Data/Sounds/voices/Pirate/CMakeLists.txt	Tue Oct 30 23:57:57 2018 +0100
@@ -6,6 +6,7 @@
 Byebye.ogg
 Comeonthen.ogg
 Coward.ogg
+Cutitout.ogg
 Drat.ogg
 Enemydown.ogg
 Excellent.ogg
@@ -23,6 +24,7 @@
 Justyouwait.ogg
 Kamikaze.ogg
 Laugh.ogg
+Leavemealone.ogg
 Melon.ogg
 Missed.ogg
 Nooo.ogg
@@ -34,6 +36,7 @@
 PoisonCough.ogg
 PoisonMoan.ogg
 Reinforcements.ogg
+Revenge.ogg
 Runaway.ogg
 Sameteam.ogg
 Solong.ogg
--- a/share/hedgewars/Data/Sounds/voices/Robot/CMakeLists.txt	Tue Oct 30 19:08:39 2018 +0300
+++ b/share/hedgewars/Data/Sounds/voices/Robot/CMakeLists.txt	Tue Oct 30 23:57:57 2018 +0100
@@ -6,6 +6,7 @@
 Byebye.ogg
 Comeonthen.ogg
 Coward.ogg
+Cutitout.ogg
 Drat.ogg
 Enemydown.ogg
 Excellent.ogg
@@ -23,6 +24,7 @@
 Justyouwait.ogg
 Kamikaze.ogg
 Laugh.ogg
+Leavemealone.ogg
 Melon.ogg
 Missed.ogg
 Nooo.ogg
@@ -34,6 +36,7 @@
 PoisonCough.ogg
 PoisonMoan.ogg
 Reinforcements.ogg
+Revenge.ogg
 Runaway.ogg
 Sameteam.ogg
 Solong.ogg
--- a/share/hedgewars/Data/Sounds/voices/Russian/CMakeLists.txt	Tue Oct 30 19:08:39 2018 +0300
+++ b/share/hedgewars/Data/Sounds/voices/Russian/CMakeLists.txt	Tue Oct 30 23:57:57 2018 +0100
@@ -6,6 +6,7 @@
 Byebye.ogg
 Comeonthen.ogg
 Coward.ogg
+Cutitout.ogg
 Drat.ogg
 Enemydown.ogg
 Excellent.ogg
@@ -23,6 +24,7 @@
 Justyouwait.ogg
 Kamikaze.ogg
 Laugh.ogg
+Leavemealone.ogg
 Melon.ogg
 Missed.ogg
 Nooo.ogg
@@ -34,6 +36,7 @@
 PoisonCough.ogg
 PoisonMoan.ogg
 Reinforcements.ogg
+Revenge.ogg
 Runaway.ogg
 Sameteam.ogg
 Solong.ogg
--- a/share/hedgewars/Data/Sounds/voices/Singer/CMakeLists.txt	Tue Oct 30 19:08:39 2018 +0300
+++ b/share/hedgewars/Data/Sounds/voices/Singer/CMakeLists.txt	Tue Oct 30 23:57:57 2018 +0100
@@ -6,6 +6,7 @@
 Byebye.ogg
 Comeonthen.ogg
 Coward.ogg
+Cutitout.ogg
 Drat.ogg
 Enemydown.ogg
 Excellent.ogg
@@ -23,6 +24,7 @@
 Justyouwait.ogg
 Kamikaze.ogg
 Laugh.ogg
+Leavemealone.ogg
 Melon.ogg
 Missed.ogg
 Nooo.ogg
@@ -34,6 +36,7 @@
 PoisonCough.ogg
 PoisonMoan.ogg
 Reinforcements.ogg
+Revenge.ogg
 Runaway.ogg
 Sameteam.ogg
 Solong.ogg
--- a/share/hedgewars/Data/Sounds/voices/Surfer/CMakeLists.txt	Tue Oct 30 19:08:39 2018 +0300
+++ b/share/hedgewars/Data/Sounds/voices/Surfer/CMakeLists.txt	Tue Oct 30 23:57:57 2018 +0100
@@ -6,6 +6,7 @@
 Byebye.ogg
 Comeonthen.ogg
 Coward.ogg
+Cutitout.ogg
 Drat.ogg
 Enemydown.ogg
 Excellent.ogg
@@ -23,6 +24,7 @@
 Justyouwait.ogg
 Kamikaze.ogg
 Laugh.ogg
+Leavemealone.ogg
 Melon.ogg
 Missed.ogg
 Nooo.ogg
@@ -34,6 +36,7 @@
 PoisonCough.ogg
 PoisonMoan.ogg
 Reinforcements.ogg
+Revenge.ogg
 Runaway.ogg
 Sameteam.ogg
 Solong.ogg