hedgewars/uStats.pas
branchqmlfrontend
changeset 12855 1b2b84315d27
parent 12568 494d3e1c4810
child 12918 6deb29364723
equal deleted inserted replaced
11843:01f88c3b7b66 12855:1b2b84315d27
    22 interface
    22 interface
    23 uses uConsts, uTypes;
    23 uses uConsts, uTypes;
    24 
    24 
    25 var TotalRounds: LongInt;
    25 var TotalRounds: LongInt;
    26     FinishedTurnsTotal: LongInt;
    26     FinishedTurnsTotal: LongInt;
       
    27     SendGameResultOn : boolean = true;
       
    28     SendRankingStatsOn : boolean = true;
       
    29     SendAchievementsStatsOn : boolean = true;
    27     SendHealthStatsOn : boolean = true;
    30     SendHealthStatsOn : boolean = true;
    28 
    31 
    29 procedure initModule;
    32 procedure initModule;
    30 procedure freeModule;
    33 procedure freeModule;
    31 
    34 
    32 procedure AmmoUsed(am: TAmmoType);
    35 procedure AmmoUsed(am: TAmmoType);
       
    36 procedure HedgehogPoisoned(Gear: PGear; Attacker: PHedgehog);
       
    37 procedure HedgehogSacrificed(Hedgehog: PHedgehog);
    33 procedure HedgehogDamaged(Gear: PGear; Attacker: PHedgehog; Damage: Longword; killed: boolean);
    38 procedure HedgehogDamaged(Gear: PGear; Attacker: PHedgehog; Damage: Longword; killed: boolean);
       
    39 procedure TargetHit;
    34 procedure Skipped;
    40 procedure Skipped;
    35 procedure TurnReaction;
    41 procedure TurnReaction;
    36 procedure SendStats;
    42 procedure SendStats;
    37 procedure hedgehogFlight(Gear: PGear; time: Longword);
    43 procedure hedgehogFlight(Gear: PGear; time: Longword);
    38 procedure declareAchievement(id, teamname, location: shortstring; value: LongInt);
    44 procedure declareAchievement(id, teamname, location: shortstring; value: LongInt);
    43 uses uSound, uLocale, uVariables, uUtils, uIO, uCaptions, uMisc, uConsole, uScript;
    49 uses uSound, uLocale, uVariables, uUtils, uIO, uCaptions, uMisc, uConsole, uScript;
    44 
    50 
    45 var DamageClan  : Longword = 0;
    51 var DamageClan  : Longword = 0;
    46     DamageTotal : Longword = 0;
    52     DamageTotal : Longword = 0;
    47     DamageTurn  : Longword = 0;
    53     DamageTurn  : Longword = 0;
       
    54     PoisonTurn  : Longword = 0; // Poisoned enemies per turn
       
    55     PoisonClan  : Longword = 0; // Poisoned own clan members in turn
       
    56     PoisonTotal : Longword = 0; // Poisoned hogs in whole round
    48     KillsClan   : LongWord = 0;
    57     KillsClan   : LongWord = 0;
    49     Kills       : LongWord = 0;
    58     Kills       : LongWord = 0;
    50     KillsTotal  : LongWord = 0;
    59     KillsTotal  : LongWord = 0;
       
    60     HitTargets  : LongWord = 0; // Target (gtTarget) hits per turn
    51     AmmoUsedCount : Longword = 0;
    61     AmmoUsedCount : Longword = 0;
    52     AmmoDamagingUsed : boolean = false;
    62     AmmoDamagingUsed : boolean = false;
    53     SkippedTurns: LongWord = 0;
    63     SkippedTurns: LongWord = 0;
    54     isTurnSkipped: boolean = false;
    64     isTurnSkipped: boolean = false;
    55     vpHurtSameClan: PVoicepack = nil;
    65     vpHurtSameClan: PVoicepack = nil;
    56     vpHurtEnemy: PVoicepack = nil;
    66     vpHurtEnemy: PVoicepack = nil;
    57 
    67 
       
    68 procedure HedgehogPoisoned(Gear: PGear; Attacker: PHedgehog);
       
    69 begin
       
    70     if Attacker^.Team^.Clan = Gear^.HEdgehog^.Team^.Clan then
       
    71         begin
       
    72         vpHurtSameClan:= CurrentHedgehog^.Team^.voicepack;
       
    73         inc(PoisonClan)
       
    74         end
       
    75     else
       
    76         begin
       
    77         vpHurtEnemy:= Gear^.Hedgehog^.Team^.voicepack;
       
    78         inc(PoisonTurn)
       
    79         end;
       
    80     Gear^.Hedgehog^.stats.StepPoisoned:= true;
       
    81     inc(PoisonTotal)
       
    82 end;
       
    83 
       
    84 procedure HedgehogSacrificed(Hedgehog: PHedgehog);
       
    85 begin
       
    86     Hedgehog^.stats.Sacrificed:= true
       
    87 end;
       
    88 
    58 procedure HedgehogDamaged(Gear: PGear; Attacker: PHedgehog; Damage: Longword; killed: boolean);
    89 procedure HedgehogDamaged(Gear: PGear; Attacker: PHedgehog; Damage: Longword; killed: boolean);
    59 begin
    90 begin
    60 if Attacker^.Team^.Clan = Gear^.Hedgehog^.Team^.Clan then
    91 if Attacker^.Team^.Clan = Gear^.Hedgehog^.Team^.Clan then
    61     vpHurtSameClan:= CurrentHedgehog^.Team^.voicepack
    92     vpHurtSameClan:= CurrentHedgehog^.Team^.voicepack
    62 else
    93 else
    70 
   101 
    71 if CurrentHedgehog^.Team^.Clan = Gear^.Hedgehog^.Team^.Clan then inc(DamageClan, Damage);
   102 if CurrentHedgehog^.Team^.Clan = Gear^.Hedgehog^.Team^.Clan then inc(DamageClan, Damage);
    72 
   103 
    73 if killed then
   104 if killed then
    74     begin
   105     begin
       
   106     Gear^.Hedgehog^.stats.StepDied:= true;
    75     inc(Attacker^.stats.StepKills);
   107     inc(Attacker^.stats.StepKills);
    76     inc(Kills);
   108     inc(Kills);
    77     inc(KillsTotal);
   109     inc(KillsTotal);
    78     inc(Attacker^.Team^.stats.Kills);
   110     inc(Attacker^.Team^.stats.Kills);
    79     if (Attacker^.Team^.TeamName = Gear^.Hedgehog^.Team^.TeamName) then
   111     if (Attacker^.Team^.TeamName = Gear^.Hedgehog^.Team^.TeamName) then
    89 
   121 
    90 inc(DamageTotal, Damage);
   122 inc(DamageTotal, Damage);
    91 inc(DamageTurn, Damage)
   123 inc(DamageTurn, Damage)
    92 end;
   124 end;
    93 
   125 
       
   126 procedure TargetHit();
       
   127 begin
       
   128    inc(HitTargets)
       
   129 end;
       
   130 
    94 procedure Skipped;
   131 procedure Skipped;
    95 begin
   132 begin
    96 inc(SkippedTurns);
   133 inc(SkippedTurns);
    97 isTurnSkipped:= true
   134 isTurnSkipped:= true
    98 end;
   135 end;
    99 
   136 
   100 procedure TurnReaction;
   137 procedure TurnReaction;
   101 var i, t: LongInt;
   138 var i, t: LongInt;
       
   139     killsCheck: LongInt;
   102     s: ansistring;
   140     s: ansistring;
   103 begin
   141 begin
   104 //TryDo(not bBetweenTurns, 'Engine bug: TurnReaction between turns', true);
   142 //TryDo(not bBetweenTurns, 'Engine bug: TurnReaction between turns', true);
   105 
   143 
   106 inc(FinishedTurnsTotal);
   144 inc(FinishedTurnsTotal);
   107 if FinishedTurnsTotal <> 0 then
   145 if FinishedTurnsTotal <> 0 then
   108     begin
   146     begin
   109     s:= ansistring(CurrentHedgehog^.Name);
   147     s:= ansistring(CurrentHedgehog^.Name);
   110     inc(CurrentHedgehog^.stats.FinishedTurns);
   148     inc(CurrentHedgehog^.stats.FinishedTurns);
   111 
   149     // If the hog sacrificed (=kamikaze/piano) itself, this needs to be taken into accounts for the reactions later
   112     if (CurrentHedgehog^.stats.DamageGiven = DamageTotal) and (DamageTotal > 0) then
   150     if (CurrentHedgehog^.stats.Sacrificed) then
       
   151         killsCheck:= 1
       
   152     else
       
   153         killsCheck:= 0;
       
   154 
       
   155     // First blood (first damage, poison or kill)
       
   156     if ((DamageTotal > 0) or (KillsTotal > 0) or (PoisonTotal > 0)) and ((CurrentHedgehog^.stats.DamageGiven = DamageTotal) and (CurrentHedgehog^.stats.StepKills = KillsTotal) and (PoisonTotal = PoisonTurn + PoisonClan)) then
   113         AddVoice(sndFirstBlood, CurrentTeam^.voicepack)
   157         AddVoice(sndFirstBlood, CurrentTeam^.voicepack)
   114 
   158 
   115     else if CurrentHedgehog^.stats.StepDamageRecv > 0 then
   159     // Hog hurts, poisons or kills itself (except sacrifice)
       
   160     else if (CurrentHedgehog^.stats.Sacrificed = false) and ((CurrentHedgehog^.stats.StepDamageRecv > 0) or (CurrentHedgehog^.stats.StepPoisoned) or (CurrentHedgehog^.stats.StepDied)) then
   116         begin
   161         begin
   117         AddVoice(sndStupid, PreviousTeam^.voicepack);
   162         AddVoice(sndStupid, PreviousTeam^.voicepack);
   118         if CurrentHedgehog^.stats.DamageGiven = CurrentHedgehog^.stats.StepDamageRecv then
   163         // Message for hurting itself only (not drowning)
       
   164         if (CurrentHedgehog^.stats.DamageGiven = CurrentHedgehog^.stats.StepDamageRecv) and (CurrentHedgehog^.stats.StepDamageRecv >= 1) then
   119             AddCaption(FormatA(GetEventString(eidHurtSelf), s), cWhiteColor, capgrpMessage);
   165             AddCaption(FormatA(GetEventString(eidHurtSelf), s), cWhiteColor, capgrpMessage);
   120         end
   166         end
   121 
   167 
   122     else if DamageClan <> 0 then
   168     // Hog hurts, poisons or kills own team/clan member. Sacrifice is taken into account
   123         if DamageTurn > DamageClan then
   169     else if (DamageClan <> 0) or (KillsClan > killsCheck) or (PoisonClan <> 0) then
       
   170         if (DamageTurn > DamageClan) or (Kills > KillsClan) then
   124             if random(2) = 0 then
   171             if random(2) = 0 then
   125                 AddVoice(sndNutter, CurrentTeam^.voicepack)
   172                 AddVoice(sndNutter, CurrentTeam^.voicepack)
   126             else
   173             else
   127                 AddVoice(sndWatchIt, vpHurtSameClan)
   174                 AddVoice(sndWatchIt, vpHurtSameClan)
   128         else
   175         else
   129             if random(2) = 0 then
   176             if random(2) = 0 then
   130                 AddVoice(sndSameTeam, vpHurtSameClan)
   177                 AddVoice(sndSameTeam, vpHurtSameClan)
   131             else
   178             else
   132                 AddVoice(sndTraitor, vpHurtSameClan)
   179                 AddVoice(sndTraitor, vpHurtSameClan)
   133 
   180 
   134     else if CurrentHedgehog^.stats.StepDamageGiven <> 0 then
   181     // Hog hurts, kills or poisons enemy
   135         if Kills > 0 then
   182     else if (CurrentHedgehog^.stats.StepDamageGiven <> 0) or (CurrentHedgehog^.stats.StepKills > killsCheck) or (PoisonTurn <> 0) then
       
   183         if Kills > killsCheck then
   136             AddVoice(sndEnemyDown, CurrentTeam^.voicepack)
   184             AddVoice(sndEnemyDown, CurrentTeam^.voicepack)
   137         else
   185         else
   138             AddVoice(sndRegret, vpHurtEnemy)
   186             AddVoice(sndRegret, vpHurtEnemy)
   139 
   187 
   140     else if AmmoDamagingUsed then
   188     // Missed shot
   141         AddVoice(sndMissed, PreviousTeam^.voicepack)
   189     // A miss is defined as a shot with a damaging weapon with 0 kills, 0 damage, 0 hogs poisoned and 0 targets hit
       
   190     else if AmmoDamagingUsed and (Kills <= killsCheck) and (PoisonTurn = 0) and (PoisonClan = 0) and (DamageTurn = 0) and (HitTargets = 0) then
       
   191         // Chance to call hedgehog stupid if sacrificed for nothing
       
   192         if CurrentHedgehog^.stats.Sacrificed then
       
   193             if random(2) = 0 then
       
   194                 AddVoice(sndMissed, PreviousTeam^.voicepack)
       
   195             else
       
   196                 AddVoice(sndStupid, PreviousTeam^.voicepack)
       
   197         else
       
   198             AddVoice(sndMissed, PreviousTeam^.voicepack)
       
   199 
       
   200     // Timeout
   142     else if (AmmoUsedCount > 0) and (not isTurnSkipped) then
   201     else if (AmmoUsedCount > 0) and (not isTurnSkipped) then
   143         begin end// nothing ?
   202         begin end// nothing ?
   144     else if isTurnSkipped then
   203 
   145         begin
   204     // Turn skipped
   146         AddVoice(sndBoring, PreviousTeam^.voicepack);
   205     else if isTurnSkipped and (not PlacingHogs) then
       
   206         begin
       
   207         AddVoice(sndCoward, PreviousTeam^.voicepack);
   147         AddCaption(FormatA(GetEventString(eidTurnSkipped), s), cWhiteColor, capgrpMessage);
   208         AddCaption(FormatA(GetEventString(eidTurnSkipped), s), cWhiteColor, capgrpMessage);
   148         end
   209         end
   149     else if not PlacingHogs then
       
   150         AddVoice(sndCoward, PreviousTeam^.voicepack);
       
   151     end;
   210     end;
   152 
   211 
   153 
   212 
   154 for t:= 0 to Pred(TeamsCount) do // send even on zero turn
   213 for t:= 0 to Pred(TeamsCount) do // send even on zero turn
   155     with TeamsArray[t]^ do
   214     with TeamsArray[t]^ do
   164                     MaxStepDamageGiven:= StepDamageGiven;
   223                     MaxStepDamageGiven:= StepDamageGiven;
   165                 if StepKills > MaxStepKills then
   224                 if StepKills > MaxStepKills then
   166                     MaxStepKills:= StepKills;
   225                     MaxStepKills:= StepKills;
   167                 StepKills:= 0;
   226                 StepKills:= 0;
   168                 StepDamageRecv:= 0;
   227                 StepDamageRecv:= 0;
   169                 StepDamageGiven:= 0
   228                 StepDamageGiven:= 0;
       
   229                 StepPoisoned:= false;
       
   230                 StepDied:= false;
   170                 end;
   231                 end;
   171 
   232 
   172 if SendHealthStatsOn then
   233 if SendHealthStatsOn then
   173     for t:= 0 to Pred(ClansCount) do
   234     for t:= 0 to Pred(ClansCount) do
   174         with ClansArray[t]^ do
   235         with ClansArray[t]^ do
   178 
   239 
   179 Kills:= 0;
   240 Kills:= 0;
   180 KillsClan:= 0;
   241 KillsClan:= 0;
   181 DamageClan:= 0;
   242 DamageClan:= 0;
   182 DamageTurn:= 0;
   243 DamageTurn:= 0;
       
   244 HitTargets:= 0;
       
   245 PoisonClan:= 0;
       
   246 PoisonTurn:= 0;
   183 AmmoUsedCount:= 0;
   247 AmmoUsedCount:= 0;
   184 AmmoDamagingUsed:= false;
   248 AmmoDamagingUsed:= false;
   185 isTurnSkipped:= false
   249 isTurnSkipped:= false
   186 end;
   250 end;
   187 
   251 
   224     winnersClan:= nil;
   288     winnersClan:= nil;
   225 
   289 
   226     for t:= 0 to Pred(TeamsCount) do
   290     for t:= 0 to Pred(TeamsCount) do
   227         with TeamsArray[t]^ do
   291         with TeamsArray[t]^ do
   228         begin
   292         begin
   229             if not ExtDriven then
   293             if (not ExtDriven) and SendRankingStatsOn then
   230                 SendStat(siTeamStats, GetTeamStatString(TeamsArray[t]));
   294                 SendStat(siTeamStats, GetTeamStatString(TeamsArray[t]));
   231             for i:= 0 to cMaxHHIndex do
   295             for i:= 0 to cMaxHHIndex do
   232                 begin
   296                 begin
   233                 if Hedgehogs[i].stats.MaxStepDamageGiven > msd then
   297                 if Hedgehogs[i].stats.MaxStepDamageGiven > msd then
   234                     begin
   298                     begin
   248 
   312 
   249             { send player stats for winner teams }
   313             { send player stats for winner teams }
   250             if Clan^.ClanHealth > 0 then
   314             if Clan^.ClanHealth > 0 then
   251                 begin
   315                 begin
   252                 winnersClan:= Clan;
   316                 winnersClan:= Clan;
   253                 SendStat(siPlayerKills, IntToStr(Clan^.Color) + ' ' +
   317                 if SendRankingStatsOn then
   254                     IntToStr(stats.Kills) + ' ' + TeamName);
   318                     SendStat(siPlayerKills, IntToStr(Clan^.Color) + ' ' +
       
   319                         IntToStr(stats.Kills) + ' ' + TeamName);
   255             end;
   320             end;
   256 
   321 
   257             { determine maximum values of TeamKills, TurnSkips, TeamDamage }
   322             { determine maximum values of TeamKills, TurnSkips, TeamDamage }
   258             if stats.TeamKills > maxTeamKills then
   323             if stats.TeamKills > maxTeamKills then
   259                 begin
   324                 begin
   272             end;
   337             end;
   273 
   338 
   274         end;
   339         end;
   275 
   340 
   276     { now send player stats for loser teams }
   341     { now send player stats for loser teams }
   277     for t:= 0 to Pred(TeamsCount) do
   342     if SendRankingStatsOn then
   278         begin
   343         for t:= 0 to Pred(TeamsCount) do
   279         with TeamsArray[t]^ do
       
   280             begin
   344             begin
   281             if Clan^.ClanHealth = 0 then
   345             with TeamsArray[t]^ do
   282                 begin
   346                 begin
   283                 SendStat(siPlayerKills, IntToStr(Clan^.Color) + ' ' +
   347                 if Clan^.ClanHealth = 0 then
   284                     IntToStr(stats.Kills) + ' ' + TeamName);
   348                     begin
       
   349                     SendStat(siPlayerKills, IntToStr(Clan^.Color) + ' ' +
       
   350                         IntToStr(stats.Kills) + ' ' + TeamName);
       
   351                 end;
   285             end;
   352             end;
   286         end;
   353         end;
   287     end;
   354 
   288 
   355     // “Achievements” / Details part of stats screen
   289     if msdhh <> nil then
   356     if SendAchievementsStatsOn then
   290         SendStat(siMaxStepDamage, IntToStr(msd) + ' ' + msdhh^.Name + ' (' + msdhh^.Team^.TeamName + ')');
   357         begin
   291     if mskcnt = 1 then
   358         if msdhh <> nil then
   292         SendStat(siMaxStepKills, IntToStr(msk) + ' ' + mskhh^.Name + ' (' + mskhh^.Team^.TeamName + ')');
   359             SendStat(siMaxStepDamage, IntToStr(msd) + ' ' + msdhh^.Name + ' (' + msdhh^.Team^.TeamName + ')');
   293 
   360         if mskcnt = 1 then
   294     if maxTeamKills > 1 then
   361             SendStat(siMaxStepKills, IntToStr(msk) + ' ' + mskhh^.Name + ' (' + mskhh^.Team^.TeamName + ')');
   295         SendStat(siMaxTeamKills, IntToStr(maxTeamKills) + ' ' + maxTeamKillsName);
   362 
   296     if maxTurnSkips > 2 then
   363         if maxTeamKills > 1 then
   297         SendStat(siMaxTurnSkips, IntToStr(maxTurnSkips) + ' ' + maxTurnSkipsName);
   364             SendStat(siMaxTeamKills, IntToStr(maxTeamKills) + ' ' + maxTeamKillsName);
   298     if maxTeamDamage > 30 then
   365         if maxTurnSkips > 2 then
   299         SendStat(siMaxTeamDamage, IntToStr(maxTeamDamage) + ' ' + maxTeamDamageName);
   366             SendStat(siMaxTurnSkips, IntToStr(maxTurnSkips) + ' ' + maxTurnSkipsName);
   300 
   367         if maxTeamDamage > 30 then
   301     if KilledHHs > 0 then
   368             SendStat(siMaxTeamDamage, IntToStr(maxTeamDamage) + ' ' + maxTeamDamageName);
   302         SendStat(siKilledHHs, IntToStr(KilledHHs));
   369 
       
   370         if KilledHHs > 0 then
       
   371             SendStat(siKilledHHs, IntToStr(KilledHHs));
       
   372         end;
   303 
   373 
   304     // now to console
   374     // now to console
   305     if winnersClan <> nil then
   375     if winnersClan <> nil then
   306         begin
   376         begin
   307         WriteLnToConsole('WINNERS');
   377         WriteLnToConsole('WINNERS');
   340 procedure initModule;
   410 procedure initModule;
   341 begin
   411 begin
   342     DamageClan  := 0;
   412     DamageClan  := 0;
   343     DamageTotal := 0;
   413     DamageTotal := 0;
   344     DamageTurn  := 0;
   414     DamageTurn  := 0;
       
   415     PoisonClan  := 0;
       
   416     PoisonTurn  := 0;
   345     KillsClan   := 0;
   417     KillsClan   := 0;
   346     Kills       := 0;
   418     Kills       := 0;
   347     KillsTotal  := 0;
   419     KillsTotal  := 0;
       
   420     HitTargets  := 0;
   348     AmmoUsedCount := 0;
   421     AmmoUsedCount := 0;
   349     AmmoDamagingUsed := false;
   422     AmmoDamagingUsed := false;
   350     SkippedTurns:= 0;
   423     SkippedTurns:= 0;
   351     isTurnSkipped:= false;
   424     isTurnSkipped:= false;
   352     vpHurtSameClan:= nil;
   425     vpHurtSameClan:= nil;