(* * Hedgewars, a free turn based strategy game * Copyright (c) 2004-2015 Andrey Korotaev <unC0Rr@gmail.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *){$INCLUDE "options.inc"}unit uStats;interfaceuses uConsts, uTypes;var TotalRounds: LongInt; // Number of rounds played (-1 if game not started) FinishedTurnsTotal: LongInt; SendGameResultOn : boolean = true; SendRankingStatsOn : boolean = true; SendAchievementsStatsOn : boolean = true; SendHealthStatsOn : boolean = true;procedure initModule;procedure freeModule;procedure AmmoUsed(am: TAmmoType);procedure HedgehogPoisoned(Gear: PGear; Attacker: PHedgehog);procedure HedgehogSacrificed(Hedgehog: PHedgehog);procedure HedgehogDamaged(Gear: PGear; Attacker: PHedgehog; Damage: Longword; killed: boolean);procedure TargetHit;procedure Skipped;procedure TurnReaction;procedure SendStats;procedure hedgehogFlight(Gear: PGear; time: Longword);procedure declareAchievement(id, teamname, location: shortstring; value: LongInt);procedure startGhostPoints(n: LongInt);procedure dumpPoint(x, y: LongInt);implementationuses uSound, uLocale, uVariables, uUtils, uIO, uCaptions, uMisc, uConsole, uScript;var DamageClan : Longword = 0; DamageTotal : Longword = 0; DamageTurn : Longword = 0; PoisonTurn : Longword = 0; // Poisoned enemies per turn PoisonClan : Longword = 0; // Poisoned own clan members in turn PoisonTotal : Longword = 0; // Poisoned hogs in whole round KillsClan : LongWord = 0; Kills : LongWord = 0; KillsTotal : LongWord = 0; HitTargets : LongWord = 0; // Target (gtTarget) hits per turn AmmoUsedCount : Longword = 0; AmmoDamagingUsed : boolean = false; SkippedTurns: LongWord = 0; isTurnSkipped: boolean = false; vpHurtSameClan: PVoicepack = nil; vpHurtEnemy: PVoicepack = nil;procedure HedgehogPoisoned(Gear: PGear; Attacker: PHedgehog);begin if Attacker^.Team^.Clan = Gear^.HEdgehog^.Team^.Clan then begin vpHurtSameClan:= CurrentHedgehog^.Team^.voicepack; inc(PoisonClan) end else begin vpHurtEnemy:= Gear^.Hedgehog^.Team^.voicepack; inc(PoisonTurn) end; Gear^.Hedgehog^.stats.StepPoisoned:= true; inc(PoisonTotal)end;procedure HedgehogSacrificed(Hedgehog: PHedgehog);begin Hedgehog^.stats.Sacrificed:= trueend;procedure HedgehogDamaged(Gear: PGear; Attacker: PHedgehog; Damage: Longword; killed: boolean);beginif Attacker^.Team^.Clan = Gear^.Hedgehog^.Team^.Clan then vpHurtSameClan:= CurrentHedgehog^.Team^.voicepackelse vpHurtEnemy:= Gear^.Hedgehog^.Team^.voicepack;//////////////////////////inc(Attacker^.stats.StepDamageGiven, Damage);inc(Attacker^.stats.DamageGiven, Damage);inc(Gear^.Hedgehog^.stats.StepDamageRecv, Damage);if CurrentHedgehog^.Team^.Clan = Gear^.Hedgehog^.Team^.Clan then inc(DamageClan, Damage);if killed then begin Gear^.Hedgehog^.stats.StepDied:= true; inc(Attacker^.stats.StepKills); inc(Kills); inc(KillsTotal); inc(Attacker^.Team^.stats.Kills); if (Attacker^.Team^.TeamName = Gear^.Hedgehog^.Team^.TeamName) then begin inc(Attacker^.Team^.stats.TeamKills); inc(Attacker^.Team^.stats.TeamDamage, Gear^.Damage); end; if Gear = Attacker^.Gear then inc(Attacker^.Team^.stats.Suicides); if Attacker^.Team^.Clan = Gear^.Hedgehog^.Team^.Clan then inc(KillsClan); end;inc(DamageTotal, Damage);inc(DamageTurn, Damage)end;procedure TargetHit();begin inc(HitTargets)end;procedure Skipped;begininc(SkippedTurns);isTurnSkipped:= trueend;procedure TurnReaction;var i, t: LongInt; killsCheck: LongInt; s: ansistring;begin//TryDo(not bBetweenTurns, 'Engine bug: TurnReaction between turns', true);inc(FinishedTurnsTotal);if FinishedTurnsTotal <> 0 then begin s:= ansistring(CurrentHedgehog^.Name); inc(CurrentHedgehog^.stats.FinishedTurns); // If the hog sacrificed (=kamikaze/piano) itself, this needs to be taken into accounts for the reactions later if (CurrentHedgehog^.stats.Sacrificed) then killsCheck:= 1 else killsCheck:= 0; // First blood (first damage, poison or kill) if ((DamageTotal > 0) or (KillsTotal > 0) or (PoisonTotal > 0)) and ((CurrentHedgehog^.stats.DamageGiven = DamageTotal) and (CurrentHedgehog^.stats.StepKills = KillsTotal) and (PoisonTotal = PoisonTurn + PoisonClan)) then AddVoice(sndFirstBlood, CurrentTeam^.voicepack) // Hog hurts, poisons or kills itself (except sacrifice) else if (CurrentHedgehog^.stats.Sacrificed = false) and ((CurrentHedgehog^.stats.StepDamageRecv > 0) or (CurrentHedgehog^.stats.StepPoisoned) or (CurrentHedgehog^.stats.StepDied)) then begin AddVoice(sndStupid, PreviousTeam^.voicepack); // Message for hurting itself only (not drowning) if (CurrentHedgehog^.stats.DamageGiven = CurrentHedgehog^.stats.StepDamageRecv) and (CurrentHedgehog^.stats.StepDamageRecv >= 1) then AddCaption(FormatA(GetEventString(eidHurtSelf), s), cWhiteColor, capgrpMessage); end // Hog hurts, poisons or kills own team/clan member. Sacrifice is taken into account else if (DamageClan <> 0) or (KillsClan > killsCheck) or (PoisonClan <> 0) then if (DamageTurn > DamageClan) or (Kills > KillsClan) then if random(2) = 0 then AddVoice(sndNutter, CurrentTeam^.voicepack) else AddVoice(sndWatchIt, vpHurtSameClan) else if random(2) = 0 then AddVoice(sndSameTeam, vpHurtSameClan) else AddVoice(sndTraitor, vpHurtSameClan) // Hog hurts, kills or poisons enemy else if (CurrentHedgehog^.stats.StepDamageGiven <> 0) or (CurrentHedgehog^.stats.StepKills > killsCheck) or (PoisonTurn <> 0) then if Kills > killsCheck then AddVoice(sndEnemyDown, CurrentTeam^.voicepack) else AddVoice(sndRegret, 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 else if AmmoDamagingUsed and (Kills <= killsCheck) and (PoisonTurn = 0) and (PoisonClan = 0) and (DamageTurn = 0) and (HitTargets = 0) then // Chance to call hedgehog stupid if sacrificed for nothing if CurrentHedgehog^.stats.Sacrificed then if random(2) = 0 then AddVoice(sndMissed, PreviousTeam^.voicepack) else AddVoice(sndStupid, PreviousTeam^.voicepack) else AddVoice(sndMissed, PreviousTeam^.voicepack) // Timeout else if (AmmoUsedCount > 0) and (not isTurnSkipped) then begin end// nothing ? // Turn skipped else if isTurnSkipped and (not PlacingHogs) then begin AddVoice(sndCoward, PreviousTeam^.voicepack); AddCaption(FormatA(GetEventString(eidTurnSkipped), s), cWhiteColor, capgrpMessage); end end;for t:= 0 to Pred(TeamsCount) do // send even on zero turn with TeamsArray[t]^ do for i:= 0 to cMaxHHIndex do with Hedgehogs[i].stats do begin inc(DamageRecv, StepDamageRecv); inc(DamageGiven, StepDamageGiven); if StepDamageRecv > MaxStepDamageRecv then MaxStepDamageRecv:= StepDamageRecv; if StepDamageGiven > MaxStepDamageGiven then MaxStepDamageGiven:= StepDamageGiven; if StepKills > MaxStepKills then MaxStepKills:= StepKills; StepKills:= 0; StepDamageRecv:= 0; StepDamageGiven:= 0; StepPoisoned:= false; StepDied:= false; end;if SendHealthStatsOn then for t:= 0 to Pred(ClansCount) do with ClansArray[t]^ do begin SendStat(siClanHealth, IntToStr(Color) + ' ' + IntToStr(ClanHealth)); end;Kills:= 0;KillsClan:= 0;DamageClan:= 0;DamageTurn:= 0;HitTargets:= 0;PoisonClan:= 0;PoisonTurn:= 0;AmmoUsedCount:= 0;AmmoDamagingUsed:= false;isTurnSkipped:= falseend;procedure AmmoUsed(am: TAmmoType);begininc(AmmoUsedCount);AmmoDamagingUsed:= AmmoDamagingUsed or Ammoz[am].isDamagingend;procedure hedgehogFlight(Gear: PGear; time: Longword);beginif time > 4000 then begin WriteLnToConsole('FLIGHT'); WriteLnToConsole(Gear^.Hedgehog^.Team^.TeamName); WriteLnToConsole(inttostr(time)); WriteLnToConsole( ''); endend;procedure SendStats;var i, t: LongInt; msd, msk: Longword; msdhh, mskhh: PHedgehog; mskcnt: Longword; maxTeamKills : Longword; maxTeamKillsName : shortstring; maxTurnSkips : Longword; maxTurnSkipsName : shortstring; maxTeamDamage : Longword; maxTeamDamageName : shortstring; winnersClan : PClan;beginif SendHealthStatsOn then msd:= 0; msdhh:= nil; msk:= 0; mskhh:= nil; mskcnt:= 0; maxTeamKills := 0; maxTurnSkips := 0; maxTeamDamage := 0; winnersClan:= nil; for t:= 0 to Pred(TeamsCount) do with TeamsArray[t]^ do begin if (not ExtDriven) and SendRankingStatsOn then SendStat(siTeamStats, GetTeamStatString(TeamsArray[t])); for i:= 0 to cMaxHHIndex do begin if Hedgehogs[i].stats.MaxStepDamageGiven > msd then begin msdhh:= @Hedgehogs[i]; msd:= Hedgehogs[i].stats.MaxStepDamageGiven end; if Hedgehogs[i].stats.MaxStepKills >= msk then if Hedgehogs[i].stats.MaxStepKills = msk then inc(mskcnt) else begin mskcnt:= 1; mskhh:= @Hedgehogs[i]; msk:= Hedgehogs[i].stats.MaxStepKills end; end; { send player stats for winner teams } if Clan^.ClanHealth > 0 then begin winnersClan:= Clan; if SendRankingStatsOn then SendStat(siPlayerKills, IntToStr(Clan^.Color) + ' ' + IntToStr(stats.Kills) + ' ' + TeamName); end; { determine maximum values of TeamKills, TurnSkips, TeamDamage } if stats.TeamKills > maxTeamKills then begin maxTeamKills := stats.TeamKills; maxTeamKillsName := TeamName; end; if stats.TurnSkips > maxTurnSkips then begin maxTurnSkips := stats.TurnSkips; maxTurnSkipsName := TeamName; end; if stats.TeamDamage > maxTeamDamage then begin maxTeamDamage := stats.TeamDamage; maxTeamDamageName := TeamName; end; end; { now send player stats for loser teams } if SendRankingStatsOn then for t:= 0 to Pred(TeamsCount) do begin with TeamsArray[t]^ do begin if Clan^.ClanHealth = 0 then begin SendStat(siPlayerKills, IntToStr(Clan^.Color) + ' ' + IntToStr(stats.Kills) + ' ' + TeamName); end; end; end; // “Achievements” / Details part of stats screen if SendAchievementsStatsOn then begin if msdhh <> nil then SendStat(siMaxStepDamage, IntToStr(msd) + ' ' + msdhh^.Name + ' (' + msdhh^.Team^.TeamName + ')'); if mskcnt = 1 then SendStat(siMaxStepKills, IntToStr(msk) + ' ' + mskhh^.Name + ' (' + mskhh^.Team^.TeamName + ')'); if maxTeamKills > 1 then SendStat(siMaxTeamKills, IntToStr(maxTeamKills) + ' ' + maxTeamKillsName); if maxTurnSkips > 2 then SendStat(siMaxTurnSkips, IntToStr(maxTurnSkips) + ' ' + maxTurnSkipsName); if maxTeamDamage > 30 then SendStat(siMaxTeamDamage, IntToStr(maxTeamDamage) + ' ' + maxTeamDamageName); if KilledHHs > 0 then SendStat(siKilledHHs, IntToStr(KilledHHs)); end; // now to console if winnersClan <> nil then begin WriteLnToConsole('WINNERS'); WriteLnToConsole(inttostr(winnersClan^.TeamsNumber)); for t:= 0 to winnersClan^.TeamsNumber - 1 do WriteLnToConsole(winnersClan^.Teams[t]^.TeamName); end else WriteLnToConsole('DRAW'); ScriptCall('onAchievementsDeclaration');end;procedure declareAchievement(id, teamname, location: shortstring; value: LongInt);beginif (length(id) = 0) or (length(teamname) = 0) or (length(location) = 0) then exit; WriteLnToConsole('ACHIEVEMENT'); WriteLnToConsole(id); WriteLnToConsole(teamname); WriteLnToConsole(location); WriteLnToConsole(inttostr(value));end;procedure startGhostPoints(n: LongInt);begin WriteLnToConsole('GHOST_POINTS'); WriteLnToConsole(inttostr(n));end;procedure dumpPoint(x, y: LongInt);begin WriteLnToConsole(inttostr(x)); WriteLnToConsole(inttostr(y));end;procedure initModule;begin DamageClan := 0; DamageTotal := 0; DamageTurn := 0; PoisonClan := 0; PoisonTurn := 0; KillsClan := 0; Kills := 0; KillsTotal := 0; HitTargets := 0; AmmoUsedCount := 0; AmmoDamagingUsed := false; SkippedTurns:= 0; isTurnSkipped:= false; vpHurtSameClan:= nil; vpHurtEnemy:= nil; TotalRounds:= -1; FinishedTurnsTotal:= -1;end;procedure freeModule;beginend;end.